Before you start
A token and a model, as in the quickstart. The stream is plain Server-Sent Events over HTTP — every language has a client, and the one below is thirty lines of standard library.The whole thing
What arrives
The run above, counted frame by frame:
Forty-seven of the seventy-five frames were text deltas, and not one of them
was durable. That is the shape of every run: most of the traffic is
animation, and all of the facts are in the rest.
A given run shows a subset of the full vocabulary, depending on what the agent
does and which model you are on.
Which frames you can build on
One rule: look forid:.
payload.delta, the settled
message carries payload.text. Rendering a delta by reading payload.text
prints nothing at all, silently — which is a mistake this page made until it
was run.
A durable frame has an integer seq and an id: line. It is stored, it is
replayed on reconnect, and it is a fact. A transient frame has seq: null and
no id:. It is not stored and not replayed.
Drive your state machine from durable frames and treat transient ones as
animation. Printing agent.text deltas as they arrive is exactly right.
Deciding “this step finished” from them is not: after a reconnect those deltas
are gone, while the agent.message they settled into is replayed.
Reconnect without losing anything
resume from: 23781 — so remembering one integer
is the whole of crash recovery. No reconciliation pass, no diffing against
/items.
What you do not get back are the text deltas: the animation from a
disconnected period is gone, and the agent.message it settled into is
replayed in its place. Which is why the state machine runs on durable frames.
The full vocabulary
Twenty-six types, grouped by the question they answer on screen:
Three worth singling out:
file.changedcovers creation, modification and deletion; the payload says which. For a live diff view this is more useful than the assistant’s prose.preview.readymeans the agent started a service and a port answered. It is the signal that there is something to click.agent.todosis the agent’s own checklist. Rendered as a task list, your users can see how many steps remain.
You have it working when
user.messageis the first frame you receive, because you subscribed firstturn.started,agent.tool_call,agent.tool_resultandturn.endedall arrive- Every frame either has an integer
seqand anid:, orseq: nulland neither — there is no third shape - After reconnecting mid-run, durable events are strictly increasing, all
greater than your cursor, and agree with
GET /items - No
agent.textis replayed - Your interface never polls
/turns
What to know
Terminal output is not on this stream. You see what a command returned throughagent.tool_result, but not the output arriving line by line.
Transient frames are never replayed. The animation from a disconnected
period is gone. The facts are not.
Payload shapes vary by type and are not individually described in the OpenAPI
document, which covers the envelope — type, seq, created_at, payload.
Branch on the type rather than expecting one parser to handle all of them.
There is a ceiling on concurrent streams per organization — see
limits.md. In a multi-tenant interface, hold one stream per
user rather than one per component.