event is the product-event kind, such as
run.state_changed or tool_call.requested. Its SSE id is an opaque cursor
for the committed project position; send it back unchanged rather than
constructing one. Its JSON body has this shape:
Connection lifecycle
- Opening: the server writes a
: connectedcomment line as soon as the stream opens, so proxies commit the response before the first event exists. - Keepalive: after 10 seconds without other output, the server writes a
: keepalivecomment line. Standards-compliant SSE parsers ignore lines that start with:. A custom parser must skip them and never treat them as events. - Normal close: a healthy stream closes after about 25 seconds. Reconnect
with
Last-Event-ID; the close is not data loss. - Backlog: after a reconnect, the server sends missed events in pages of 200 without pausing between full pages.
- One page:
?once=truereturns a single page of up to 200 events with no comment lines and closes immediately.
Cache-Control: no-store, no-transform and
X-Accel-Buffering: no. If OAO runs behind your own reverse proxy or load
balancer, disable response buffering and compression for text/event-stream,
and allow at least 15 seconds between reads.
Resume correctly
- Persist the last event ID only after your application has applied that event successfully.
- Reconnect with
Last-Event-IDafter a network error or process restart. - Treat duplicate events as normal and deduplicate by event
idor by(aggregateType, aggregateId, aggregateSequence). - Refresh the affected resource with a normal API read when your application does not understand a new event kind.
LISTEN/NOTIFY is only a
latency optimization inside OAO, so missed notifications do not create gaps.
Each API process shares one dedicated LISTEN connection across all of its open
streams and wakes only the streams of the project that committed the event. If
that connection drops, streams keep polling committed events every second while
the listener reconnects.
Model usage events
model.invocation_completed and model.invocation_failed payloads include
inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, and
costMicrounits, plus OAO’s normalized finishReason. Cache reads represent
prompt tokens reused from the provider cache; cache writes represent prompt
tokens newly stored there. A provider that does not report cache usage
contributes zero for those fields.
When a model call fails because of a provider finish condition, the failed
event also includes the provider’s exact bounded providerFinishReason when it
is available, plus a safe errorExplanation describing why OAO classified the
response as failed. For example, providerFinishReason: "content_filter"
explains that the provider’s content filter stopped the response and OAO could
not treat the partial output as a completed reply. These payloads never include
prompt or response content, raw provider error messages, or credentials.
Harness Operation events
The runtime emitsharness.operation_started,
harness.operation_completed, harness.operation_failed, and the applicable
cancellation or timeout event for focused scratch-loop work. It also emits one
harness.operation_step event for each completed inner model or tool step. Safe
payloads can
include operationKey, toolCallId, taskCharacters, timeoutMs,
durationMs, and resultValidated. Step payloads use harnessToolCallId,
stepKind, stepId, stepIndex, status, duration, bounded token counts, and a
safe summary. Model-step summaries are derived only from bounded tool names—for
example, Requested activate_skill. or Returned the structured result for validation. Tool steps can include a safe tool label or path summary.
These events are correlated to the aggregate run and tool call. They never
contain the detailed task, scratch conversation, structured result, Skill
instructions/resources, tool payloads, authorization material, or raw shared
documents. The authorized console groups the start and terminal lifecycle
events by toolCallId into one Harness transcript row. Opening that row shows a
details modal with safe, sequential inner-turn metadata. New events carry the
owning Harness tool-call ID, so concurrent scratch loops are attributed exactly
instead of by timing. Lifecycle windows that overlap are shown as one parallel
group with a shared colored rail and count in the console. Legacy activity that
does not contain explicit correlation is folded only when its timing identifies
one unambiguous invocation; ambiguous legacy activity remains in the parent
timeline and the affected Harness modals are marked as partial.
Filter in the client
The server feed is project-wide. A session UI normally stores its cursor, then filters frames whoseaggregateId equals a known run, thread, tool call,
approval, or sandbox ID. On relevant events, re-fetch
GET /sessions/{sessionId} to obtain the current transcript and debug view.
Event bodies contain public state and identifiers, not raw model reasoning,
credentials, authorization headers, unredacted tool payloads, or attached file
bytes. A user
message.created event may include only the safe fileCount;
re-fetch the session for filename, content type, size, and SHA-256 metadata.Delegation events
Payloads contain only delegate/version/session/run identifiers, ordinal,
state, and bounded status metadata. They never contain the delegated prompt,
child response, workspace contents, credentials, or authorization headers.
Skill events
Publication events may include Skill/version identifiers, version number, and
content hash. Runtime events contain only safe identifiers, the requested
resource path, outcome, and duration. They never contain instructions,
resource contents, decoded bytes, model text, credentials, or authorization
headers. Audit records follow the same boundary.
MCP events
MCP configuration emitsmcp.server_created,
mcp.server_version_published, mcp.discovery_completed,
mcp.discovery_failed, mcp.toolset_published,
mcp.credential_created, mcp.credential_rotated, and
mcp.credential_revoked. Runtime calls emit mcp.call_started,
mcp.call_completed, mcp.call_failed, or mcp.call_cancelled.
Payloads contain only tenant resource IDs, remote tool name, safe outcome/error
code, response byte count, and timing/state metadata. They exclude credentials,
headers, tool arguments, tool results, model content, and sandbox state.
TypeScript SDK
The workspace SDK reconnects automatically unless disabled:credentials: "include"; for a
server-side integration, provide apiKey.
