Repository navigation
add a built-in agent with personas, behind the harness experiment - #141
Merged
Merged
Conversation
…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
…-proxy-integration
…atalog's modules to read
hkonsti
force-pushed
the
hkonsti/agent-harness-proxy-integration
branch
from
October 26, 2026 22:19
ee4c104 to
3434ecb
Compare
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
force-pushed
the
hkonsti/agent-harness-proxy-integration
branch
from
October 27, 2026 22:45
54ddcc7 to
ecc6f52
Compare
…n reports after the stop, and let a live head rest
…nd note what happens to a job as it happens
hkonsti
marked this pull request as ready for review
October 27, 2026 23:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Work in progress: an agent Personal Agent Proxy runs itself, behind the
harnessexperiment, 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