Skip to content

add a built-in agent with personas, behind the harness experiment - #141

Merged
hkonsti merged 22 commits into
mainfrom
hkonsti/agent-harness-proxy-integration
Oct 27, 2026
Merged

hkonsti merged 22 commits into
mainfrom
hkonsti/agent-harness-proxy-integration

Conversation

@hkonsti

@hkonsti hkonsti commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Work in progress: an agent Personal Agent Proxy runs itself, behind the harness experiment, to get a feel for the harness design before building further on it.

Chat. A Chat page where a member talks with the organization's personas: agents Personal Agent Proxy runs itself, each named and given a head, made by anyone and changed or deleted by whoever made it or the organization's owner. A conversation is the member's alone. In it the persona acts as them, reaching the shared connections, their own, and their vault. The model is Claude Sonnet 5.5 over OpenRouter.

One app, many personas. Every persona acts through the organization's one built-in agent login, which is one row on the Agent apps page (the Agents tab, renamed), shared with everyone, as Claude or ChatGPT are one row each however many assistants run inside them. Its permissions are every persona's. It is neither revoked nor deleted, and nothing signs in with it. Which persona did a piece of work is on the task.

Sub-agents as tasks. The persona talking with the person has one tool, delegate, and makes no calls to the person's accounts itself. A delegation starts a sub-agent: a thread of its own with the gate tools and a sleep, and a task of its own, titled by the persona, with the brief as its goal and its report as its outcome. Every delegation runs in the background: the call is answered at once, and the report lands later as an event, which the model reads as a message. So the persona is free while sub-agents work, several at once, and a person's message is never held up by one. A job that is given up answers its call with the failure, so nothing waits forever.

The log and the loop. Everything lives in the database: a thread per conversation and per sub-agent, an append-only log of entries, and a job per unit of work. A worker in each api instance claims jobs with a lease, a few at a time, and finishes each in the same transaction as what it writes, so an instance that dies mid-job loses nothing. A long conversation is summarized for the model in place of what it covers.

Heads. A persona's head is the head lab's (design/head-lab): a look, six numbers kept as JSON, in one of eight colors. The web app draws it with three.js, loaded as its own chunk the first time a head is drawn, rendering a still head once per look, color, pose and size. In the dialog the head looks at the pointer, and in the sidebar the open conversation's head watches the pointer while it is over the sidebar.

Still to do before this leaves the experiment: an "ask" grant state with approvals, event triggers, and poses by context. The page shows the conversation alone, as a chat; what the sub-agents did is in the activity log, by persona.


Open workspace in Conductor

…s own gate

A Chat page, behind the `harness` experiment, talks to an agent login Personal Agent Proxy
runs itself. It has grants and an activity log like any agent, and every call it makes goes
through the same MCP tool dispatch as Claude's, for the conversation's task. The model is
Claude Sonnet 5.5 over OpenRouter.

Everything lives in the database: a thread per conversation, an append-only log of entries
(the person, the model with its tool calls, each call's result), and a job per unit of work,
inference on a log or one tool call. A worker in each api instance claims jobs with a lease
and finishes them in the same transaction as what they write, fenced by the lease, so an
instance that dies mid-job loses nothing: the next worker picks the job up. A write that was
interrupted is never made again; the model is told its outcome is unknown. One tool beyond
the connections, sleep_until, is a job due later.

The web app gets a second sidebar beside the icon rail for the conversations, and a page
mode that scrolls a part of itself so the header, sidebars and input stay put.
Message and Bubble for the turns, a Message Scroller that anchors the person's newest message
and follows the reply, Trace lines for the agent's calls that open to their details, and an
Input Group for the message field, added to the ui package with the shadcn CLI.
…y the person

A thread is a persona now: a name and an avatar, one of four shapes in one of eight colors,
made and changed in a dialog from the "+ New" row and the conversation's header. The sidebar
lists them with the last thing said in each, as a chat app does. The migration for the
threads, never deployed, is rewritten in place with the three columns.

The login, the grants and the system prompt stay the same for every persona.
… summary in place of what it covers

Once a request to the model has passed a size, the next inference first asks the model for a
summary of the log before the person's last two turns, written as a summary entry with how far
it reaches. The model reads the last summary, then what came since, with the tool results of
the kept turns before the current one cleared. The page shows the conversation as before: the
summaries are the model's, not the person's.
…h a conversation per member

Main made organizations of people, with connections and agents a member's own or shared, and
everything under the organization's slug. The built-in agents follow: each is an agent shared with
every member, named and given a face on the Chat page, where anyone makes one and whoever made it or
the organization's owner changes or deletes it. A member's conversation with an agent is theirs
alone, as a direct message: in it the agent acts as them, reaching the shared connections and their
own, both vaults included, and its task and activity are theirs. Leaving an organization takes a
member's conversations with it. The chat routes move under the organization, the migration becomes
v032 with the face on the agent and the member on the thread, and the Agents page shows the
built-in agents as shared, without a login to set up.
…to list a connection's tools rather than guess theirs
… and take a thread's task as abandoned by its last start
@hkonsti
hkonsti force-pushed the hkonsti/agent-harness-proxy-integration branch from ee4c104 to 3434ecb Compare October 26, 2026 22:19
Two spheres and painted eyes, drawn flat: color with the nose always the darker sphere, nose and eye size in narrow ranges, and a full turn. Opens with looks/2026-10-27-blue.json.
…work as a sub-agent that is a task

The agent talking with the person makes no calls to their accounts itself: it has one tool,
delegate, which starts a sub-agent on a title and a brief. A sub-agent is a thread of its own,
with the gate tools and a sleep, and a task of its own, titled as the agent titled it, with the
brief as its goal and the report as its outcome. The conversation has no task any more.

Every delegation runs in the background: the call is answered at once, that the sub-agent has
started, which keeps the chat form whole, and the report lands later as an event, a new kind of
entry the model reads as a message, as it reads the person. So the agent is free while sub-agents
work, several at once, and a person's message is never held up by one. A sub-agent's first answer
without a call is its report, which finishes its thread and its task and answers the call that
started it, in one transaction, the sub-agent's thread locked before its parent's.

A job that is given up answers its call with the failure, and a sub-agent whose turn failed
reports it, so nothing waits forever. The worker runs a few jobs at once. The page shows a
delegation as a line with its title, opening to the brief, the report and the sub-agent's
activity. The migration for the threads, never deployed, is rewritten in place.
The agent row and the agents api carry the face, null for any other agent, and the logo component
draws the avatar for one, so the Agents page, the activity log, a task's card and page, the
request dialog and the connection page tell personas apart as the Chat page does.
… made looking at the pointer

A persona is a color and a look now: the lab's six numbers for the nose and the eyes, kept as JSON
in place of the shape, shuffled for a new persona, with the lab's first look for one without. The
web app draws a head with three.js, loaded on its own the first time a head is drawn, on one
renderer for the page: a still head is rendered once per look, color, pose and size and copied
from then on, so lists and the activity log stay cheap.

In the dialog the head faces the person and turns toward the pointer wherever it is, from the
first frame, since the pointer's last place on the page is kept; the colors are round swatches in
a grid under the name, with a shuffle and the way back to the look it opened with. In the sidebar
the open conversation's head watches the pointer while it is over the sidebar, and settles back
to rest, drawn no more, when it leaves. The top bar shows the name alone.
…age, and say which persona a task was

A persona is a row of its own now, named and given a head, and every persona acts through the
organization's one built-in agent login, Chat, made the first time Chat is used and shared with
everyone: its permissions are every persona's, as an outside app's are every assistant's inside
it, since a login is the identity we can verify and a persona is not. The login is neither
revoked nor deleted, and nothing signs in with it, so its row says what it is instead of a
username. The Agents tab is Agent permissions.

Which persona did a piece of work is on the task: a sub-agent's task records the persona it ran
as, and the activity log, a task's card and its page show the persona's head and name for it, the
app's logo for any other task.
…ides, and nothing of the sub-agents

The Chat page shows what the person said and what the persona answered, each in a bubble, the
person's on the right and the persona's outlined on the left, turns in a row from one side close
together. The sub-agents the persona started, their briefs and their reports are in the activity
log, not on the page: the api gives the page the turns alone, and the sub-agent list, the trace
lines and the trace pieces that only served them are gone. The Chat app's badge on the Agent
permissions page sits in a row rather than with a margin.
…ottom fade at its end everywhere

What a persona answers is shown as Markdown: paragraphs, lists, tables, code and links, styled
to sit in its bubble, headings as bold lines, since a chat reply has none. The model is told so,
and to lay a reply out as a list or a table where that reads better. The sidebar's preview of the
last message drops the Markdown marks.

The message scroller's viewport fades its bottom while there is more below. A browser that can
drive the fade from the scroll position lifts it at the end by itself; one that can't got
shadcn's fallback, a fade that never lifts. The viewport now says whether there is more below,
from the scroller's own state, and the stylesheet lifts the fade where there isn't.
…on what went wrong

The model is run on a log only once it is ready, and nothing lands in the log while it runs: a
message or a sub-agent's report that comes meanwhile is pending, and takes its place after the
answer once the job ends, waking the agent again. The idle finish and its race go away with it.

A log to be summarized goes to the model as text, since a request carrying calls without the
tools is refused; a sub-agent's log, one brief and the work on it, is summarized before its last
steps. A model that turned the key down, or refused the request, is told apart from one that
can't be reached, and only the latter is tried again. A turn that fails for good in the
conversation answers with what went wrong, in a red bubble, instead of going quiet.
…Chat at once

The organization's row is locked while either is looked for and made, as a persona's is for a
member's thread, so two first uses can't make two. The first persona, General, is made by the
api on the first listing, by nobody in particular, instead of by whoever opened the page.

The open conversation's row in the list shows its last message cut to a line like every other
row's: the preview is shared with the web app.
…ng, and keep the built-in login without a password

A failed delegate tool job appended a second tool result for a call already answered as
started, which the model API refused on every later request, so the conversation was dead.
The failure now comes as an event, as the report would have. An answer of no words and no
calls is written as that said, so the person sees something and no empty turn is replayed.
The password route refuses the built-in login as revoke and delete do. A model retry due in
a moment reads as busy, not asleep. A delegation whose arguments aren't JSON no longer throws
out of the context builder. A poll of the log begun before a send can't put back the log
without the message. The thread poll and the readiness check read only what they need. One
cut helper replaces three copies, and the abandoned-task path copies the task's title and
goal instead of reloading the log. The unreferenced ui components and exports are gone, and
the Markdown components drop react-markdown's node prop before spreading.
@hkonsti
hkonsti force-pushed the hkonsti/agent-harness-proxy-integration branch from 54ddcc7 to ecc6f52 Compare October 27, 2026 22:45
…n reports after the stop, and let a live head rest
@hkonsti
hkonsti marked this pull request as ready for review October 27, 2026 23:27
@hkonsti
hkonsti merged commit 366a4ae into main Oct 27, 2026
3 checks passed
@hkonsti
hkonsti deleted the hkonsti/agent-harness-proxy-integration branch October 27, 2026 23:37
@hkonsti hkonsti changed the title wip: add a built-in agent with personas, behind the harness experiment add a built-in agent with personas, behind the harness experiment Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant