Implementation: DESIGN (server) · Exposure: PUBLIC PREVIEW (contract). The Agency API is not live yet. This page describes the published v0.3 contract so you can review and build against it; details may change before alpha.
Every material transition in a run is published as a typed event. Events are durable, ordered by a gap-free sequence number, and can be read as a live Server-Sent Events (SSE) stream or as pages of JSON. Work-runtime runs have no event stream today; this is one of the items the alpha gate (restart, resume, verify, replay, receipt-backed completion) depends on.

Stream or list events

One endpoint serves both modes. The Accept header selects which.

Parameters

types takes a comma-separated list, for example ?types=run.status_changed,attention.requested. debug.trace events are delivered only with view=debug (scope debug:read).

Responses

410 is returned when the cursor or Last-Event-ID is older than the retention window (ERR_CURSOR_EXPIRED).

SSE format

Each message has three fields:
  • The server sends a retry: hint when the connection opens.
  • The server sends a comment heartbeat (: ping) at least every 15 seconds. Comment lines carry no event; ignore them.
  • Messages are separated by a blank line.
  • The stream closes after the terminal run.status_changed event.
Example stream:
The retry value and the IDs are illustrative, and the hash is shortened.

Resume

Events are durable for the life of the run plus the retention window. A client that was offline, including across a server restart, can replay what it missed.
  1. Remember the id of the last event you processed.
  2. Reconnect with the Last-Event-ID header set to that id. You can use ?cursor= instead; if both are sent, Last-Event-ID takes precedence.
  3. Delivery restarts strictly after that event, in seq order, with no gaps.
seq starts at 1 and increases by one per event within a run. If you ever see a gap, reconnect from your last processed id. If the resume point is older than the retention window the server returns 410 with ERR_CURSOR_EXPIRED. Fetch the run with GET /v1/runs/{run_id} to get current state, then start a new stream.

Polling

Pass next_cursor as ?cursor= to get the next page.

Event envelope

Every event has the same base fields (RunEventBase) plus a data object whose shape depends on type. RunEvent is the union of all event types, discriminated on type. Clients must ignore event types they do not recognize.

Event types

run.status_changed

RunStatusChangedEvent. The event with data.terminal: true is the last event of the stream. data.run_receipt_id is set on the terminal event.

run.progress

RunProgressEvent

run.message

RunMessageEvent. There are no Thread endpoints; messages arrive here. When delta is true, append text to the previous message.

attention.requested

AttentionRequestedEvent. data is an AttentionRequest.

attention.resolved

AttentionResolvedEvent. data is the AttentionRequest with status and resolution set.

input.accepted

InputAcceptedEvent. data is an InputAccepted.

artifact.created

ArtifactCreatedEvent. data is an Artifact.

receipt.appended

ReceiptAppendedEvent. receipt_type is a ReceiptType.

verification.completed

VerificationCompletedEvent. result is a VerificationResult: pending, pass, fail, inconclusive or error.

budget.threshold

BudgetThresholdEvent. Emitted at fractions 0.5, 0.8 and 1.0 of a budget dimension.

debug.trace

DebugTraceEvent. Only delivered with view=debug. data is an open object and may change without a major version.

Client checklist

  • Track the last id and resume with Last-Event-ID.
  • Process events in seq order and tolerate redelivery by deduplicating on id.
  • Ignore comment lines and unknown event types.
  • Treat the stream as finished only after a run.status_changed event with data.terminal: true.