Skip to content

feat(agents): serve the agents service to screens over a MessagePort - #1725

Closed
loganj wants to merge 1 commit into
agent-host-agents-clientfrom
agent-host-agents-port
Closed

loganj wants to merge 1 commit into
agent-host-agents-clientfrom
agent-host-agents-port

Conversation

@loganj

@loganj loganj commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

🤖

Summary

Details

  • serveAgents(service, port) pushes what screens read synchronously:
    • the snapshot;
    • each shown agent's memberships;
    • the joinable agents;
    • which hosted agents can be interrupted;
    • the creation and update requests.
      It answers the screens' async calls (create, save, join, retype and the rest) and their fire-and-forget actions (resumeInterrupted and the dismissals).
  • Before each answer it pushes any pending state. So when a call such as create or join resolves, the client already shows its result. Several changes in one task are one push.
  • connectAgents(port) keeps the screens' synchronous methods (find, memberships, joinable, interruptible, creationRequests and the rest) and answers them from the pushed state, so screens do not change.
  • shared(prior, next) keeps every part of the prior state that the new push equals, matching array items by key. An agent that did not change keeps its object, which useSyncExternalStore and the huddle composer's !== checks need.
  • Handler-style calls (onClick={agents.resumeInterrupted}) drop the click event before it crosses.
  • Closing fails the calls still waiting.
  • Not carried yet: types(), sessionState and previewRetype. They call plugin code (type functions, session controls). A later PR gives the screens' page what it needs for those.
  • Known gap: state is pushed when the service notifies. A community connecting can change a membership's connected without a notify. Screens read it at their next render today, and they have the same timing with this client.

Testing

  • The new service.test.ts case runs the real service through a MessageChannel. create and join resolve with the client already showing the new agent and its two memberships. join again rejects with the service's message. An unrelated push keeps the agent's identity.
  • port.test.ts covers structural sharing, the loading state before the first push, refusals, click events being dropped, and closing.
  • The ordering tests fail when the push before an answer is removed: I checked by mutating it.
  • tsc --noEmit passes, and port.test.ts and service.test.ts pass.

🤖 Generated with Claude Code

serveAgents pushes what screens read synchronously (the snapshot, each
shown agent's memberships, joinable agents, which hosted agents can be
interrupted, and the creation and update requests) and answers their
calls. It pushes state before each answer, so a call's result never
arrives before what it changed. connectAgents keeps the pushed state
with structural sharing, so an unchanged agent keeps its identity.

Nothing uses it yet. Types, session state and the retype preview call
plugin code and come in a later change.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj

loganj commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Closing: the Manager/Runner split replaces this design (#1748 → #1839, with more to come). The branch stays in place because #1737, #1743 and #1744 are still stacked on it. They will move onto the new stack in the hidden-page step.

@loganj loganj closed this Oct 28, 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