chat command is a two-hundred-line example of doing it.
Reference components — a message list, a tool card, an approval card, a composer — are on
the roadmap, to be extracted from an example app rather than designed ahead of one.
The events
Every event is an envelope —seq, ts, type, workspace_id, session_id, turn_id —
around a typed payload. The ones a UI renders:
token and output are the only events a live consumer gets that a replay might not — and a
replay carries their content inside hop and tool_result respectively, so nothing is lost
either way.
Persist, then emit
An event reaches a subscriber only after it is in the workspace’s log. This is one line of code to get backwards, and everything about reconnecting rests on it: a client that saw an event can always fetch it again, and one that missed some can ask for everything after the lastseq it has.
The HTTP binding
@nativeharness/protocol provides SessionService, which drives turns server-side, and a
Node http handler over it:
Last-Event-ID and receives exactly the events it
missed. A turn that is still running when the page reloads is still running.
The handler is a regex and a switch on purpose: it is a reference binding. A deployment on
Express, Hono or anything else calls SessionService directly.
From the browser
fetch to /turns with the user’s text, to /resume with an approval decision or
answers, and to /cancel.
Model output is text
token text, ask_user questions and options, and hop output are model output. Render
them as text, never as markup. A model that writes <script> must produce a message that
says <script>.
The full contract — envelope, every payload, reconnect and replay, versioning — is
protocol.md.