Event catalogue
Protocol version 2. Both transports carry byte-identical bodies — see Streaming a run for the guarantees and the resumption rules.
Adding a type is a protocol change and bumps the version. Adding a field to a
type’s data is additive, which is why the types are a closed set and the payloads
are mappings: read an unknown key rather than refusing the frame.
Envelope
Section titled “Envelope”Every frame carries exactly these keys:
{ "protocol": "…", "seq": "…", "run_id": "…", "ts": "…", "type": "…", "segment_id": "…", "data": "…"}Frame types
Section titled “Frame types”segment_id says whether the envelope’s segment_id may be null for that type:
required on a content frame you could not otherwise attribute, optional on
run-level, notice and error frames.
| Type | segment_id | data keys | Notes |
|---|---|---|---|
run.start | optional | agent_id, agent_revision_id, session_id, lane, trigger_kind, attempt | First event of an attempt. |
run.status | optional | status, previous | Every transition, including resumption. |
run.end | optional | status, usage, cost, duration_ms, failure_class?, failure_detail? | Terminal for the attempt. Not emitted for a failure with a retry scheduled (D-514): telling a subscriber to close on a run that is about to produce more frames is the worse of the two errors. |
segment.start | required | kind, name, parent_segment_id?, group_key?, model_ref?, tool_slug?, child_run_id? | Emitted before any content of the segment, by construction (annex 15 §3.2). ‘kind’ is the step vocabulary, not a fifth translation of it (D-158, D-517). |
segment.end | required | status, duration_ms, usage?, cost?, finish_reason?, error? | Usage rides here. There is no separate usage event (annex 15 §3.2). |
text.delta | required | text | Incremental output. THE ONLY CHANNEL FOR OUTPUT TEXT (annex 15 §3.1). |
thinking.delta | required | text | Reasoning traces, distinct from output. |
tool.call | required | tool_call_id, tool_slug, arguments_delta?, arguments? | |
tool.result | required | tool_call_id, ok, output_preview, truncated, bytes | Preview only; the full value is in the step payload. |
suspend | optional | kind, resume_contract, prompt, deadline_at? | |
resume | optional | kind, resolved_by | |
notice | optional | level, code, message, attributes? | Compaction applied, model fallback used, cache skipped, guardrail warned. |
error | optional | code, message, retryable | ‘code’ is from error_codes below; a client never string-matches ‘message’. |
run.end is terminal for the attempt; nothing follows it and a
client may close on it.
These types are coalesced before delivery, so a client sees fewer, larger frames
than the executor produced: text.delta, thinking.delta.
Error frame codes
Section titled “Error frame codes”An error frame carries one of these, and a client never string-matches the
message. They are the stream’s own vocabulary and are spelled differently from
the HTTP error catalogue.
AUTH_FAILED, MALFORMED_FRAME, PROTOCOL_UNSUPPORTED, RUN_NOT_FOUND, BUDGET_EXCEEDED, RATE_LIMITED, MODEL_ERROR, TOOL_ERROR, GUARDRAIL_BLOCKED, CONTEXT_OVERFLOW, TIMEOUT, CANCELLED, SANDBOX_ERROR, CHANNEL_ERROR, INTERNAL_ERROR.
WebSocket close codes
Section titled “WebSocket close codes”| Code | Meaning |
|---|---|
1000 | Normal close after run.end |
4401 | Authentication failed or timed out (5 s to send the first frame) |
4404 | Run not found or out of scope - deliberately indistinguishable |
4409 | Protocol version unsupported |