Skip to content

Sessions, events and history

This contract covers what happens inside a Session: sending input, the live event stream, and reading the durable history of Turns, Items and usage. The Session resource itself (creation configuration, retry identity, update, list and deletion) is in Core wire behavior. Message and function-result content is in message content. The Agents API guide shows the calls with the SDK and HTTP.

Recovery model ​

The event stream is live only; Turns and Items are durable. A client that needs every result:

  1. Opens GET /v1/agents/sessions/{session_id}/events before sending input.
  2. After a disconnect, subscribes again and buffers new events.
  3. Reads the Session, its Turns and its Items, deduplicates Items by ID and keeps finalized Items when it applies the buffered updates.

A stream is an observer. Closing it never cancels admitted work, and a stream never replays events it did not send. After a lost input response, retry with the same Idempotency-Key, then read the Session, Turns and Items.

Session status ​

A Session's status and last_active_at derive from its latest root Turn and from input still waiting for its Environment:

statusWhen
idleNo Turn yet, or the latest Turn completed or was cancelled. Input reserved for a hosted Environment that is still provisioning also reads idle
in_progressThe latest Turn is queued, running or waiting
requires_actionThe latest Turn waits for a function result and no cancellation was requested, or input waits for a self_hosted machine to connect. required_actions lists function_call or environment_connection entries
failedThe latest Turn failed (error is "The execution could not complete."), input reserved for the Environment failed, initial input expired before admission, or the Environment failed to initialize (see Environment initialization failure)

A Session stays usable after a Turn fails: new input starts a new Turn. Later reserved input that expires leaves the Session idle. An Environment initialization failure or expiry prevents new work.

Send input ​

POST /v1/agents/sessions/{session_id}/events takes an ordered array of 1 to 64 events: agent.session.input.message, agent.session.input.cancel and agent.session.input.tool_result. The whole batch is admitted atomically, and the response is 202 with no body once the batch is stored, before any harness reads it. Admission never confirms native application.

  • Limits. The request body is at most 1 MiB, and the stored input of one request at most 512 KiB.
  • Null batch. An explicit null for events is invalid.
  • Empty batch. {"events": []} checks that the Session exists and returns 202. It creates no Turn, Item or retry identity.
  • Retries. An Idempotency-Key of up to 128 bytes identifies the whole ordered batch. The same key and batch return 202 again without admitting anything twice; the same key with another batch returns 409 idempotency_conflict. A request without a key is always new.
  • Messages. On an idle Session a message batch starts a queued Turn. While a Turn runs, messages join it (steering); they never start a parallel Turn. Each message stays its own user Item, even when the harness receives several as one prompt.
  • Cancellation. A queued Turn is cancelled without a live Runtime. A running Turn is cancelled when the Runtime confirms it; completion can win that race. The Turn has stopped when it reads cancelled, not when the request returns. A cancellation on an idle Session with no pending input is accepted and has no effect; while an input reservation is pending, it returns 409.
  • Function results. turn_id, call_id and success are required; output and error are optional and nullable (content rules). An identical repeated result returns 202 without another application or event. The result Item appears when the harness applies the result; a result that cancellation prevents from being applied stays stored but produces no Item.
  • Queueing. A queued Turn starts when a Runtime that supports the Session's harness and configuration is connected and one of Core's core.execution_concurrency work slots is free. A Session stays bound to the Runtime that first ran it.
  • Execution availability. A service without execution returns 503 execution_unavailable, and a Worker that loses execution ownership returns 503. A Session created without a model provider rejects new messages with 400 model_provider_required (model execution).

Sessions with an Environment ​

On openai_hosted and self_hosted Sessions, messages sent while a Turn runs join it at once. Messages sent to an idle Session reserve the batch for the Environment: the request waits until a Turn starts, for at most five minutes from the reservation. While the reservation waits for a self_hosted machine, the Session reads requires_action with an environment_connection action. A batch that carries messages on these placements may contain only messages. Cancellation-only and result-only batches are admitted at once and create no Turn.

The waiting request ends with 202 when the Turn starts, or with 409 environment_input_expired when the deadline passes, 409 environment_input_cancelled when an administrator archives the Session or resets its deployment and cancels the reservation, or 409 environment_unavailable when the Environment fails or expires. Disconnecting the waiting request does not cancel the reservation or restart its deadline.

Input errors ​

Checks run in this order: request validation, Session lookup, retry lookup, the Environment file-write gate for batches with a message, the pending-input gate, then each event in batch order. A rejected batch writes nothing and leaves any pending action unchanged. Every 409 has type conflict_error (error envelope).

CaseStatus and codeMessage
Earlier input to the Session still waits for admission, such as the reserved initial input of a provisioning hosted Session or of an offline self_hosted Session409 conflict_error"Earlier input to this Session is still pending."
Input the Turn cannot accept in its state, such as a result after cancellation or after its Turn ended without a stored result409 conflict_error"The Turn cannot accept this input in its current state."
A result that differs from the call's stored result, before or after its Turn ends409 conflict_error"The tool call already has a different result."
The same Idempotency-Key with a different batch409 idempotency_conflict"This idempotency key was used with different input."
A result whose call_id names no function call of this Session400 invalid_request_error, param null"Unknown pending tool call."
A result for a call of this Session whose turn_id names another Turn, an unknown Turn ID or a value that is not a Turn ID400 invalid_request_error, param null"The tool call belongs to a different Turn."
A missing, malformed or foreign Session404 not_found_error"Resource not found."
New input after an openai_hosted Environment failed to provision409 conflict_error"the hosted environment failed to provision"
New input after a self_hosted Environment failed, input already waiting when the Environment failed, or an expired Environment409 environment_unavailable"The environment is no longer available for new input."
A message the Session's harness cannot take, such as whitespace-only text on Claude Code400 unsupported_or_invalid_configurationSee whitespace-only text

An empty turn_id or blank call_id is the generic 400 invalid_request. Error messages never repeat caller input or internal identifiers.

Initial input at Session creation ​

POST /v1/agents/sessions accepts input as a string (one text message) or an array of user messages, with the same validation and admission as the events endpoint.

  • Initial input is required on none (400 invalid_request_error, "conversation-only sessions currently require initial input") and for stream: true on every placement except self_hosted (400, "streaming session creation requires initial input"). These checks run before the creation retry lookup.
  • The Session and its initial work commit in one transaction. On none that includes the first Turn and the input Items. On openai_hosted the input is reserved while the Environment provisions. On self_hosted it is reserved with an environment_connection action, and creation returns while the machine is offline.
  • Reserved initial input has the same five-minute deadline as later input. When it passes before a Turn starts, the Session reads failed without a Turn.
  • A creation retry returns the original Session and never admits its input again, including after later Turns (creation retries).

Creation streaming ​

stream: true on Session creation returns 201 with an event stream instead of JSON.

  1. The first event is agent.session.created with the same committed Session as the JSON 201 body, read after the commit. With initial input on none it already reads in_progress; on self_hosted it already shows the environment_connection action.
  2. The stream then sends every committed event of the creation exactly once, starting at the creation's own position, so fast execution cannot skip its first events.
  3. It ends right after the first agent.session.idle recorded when a Turn ends or an input reservation stops waiting (expired, cancelled or failed), or after any agent.session.failed, and never sends later events. requires_action, function results, resumed work and a self_hosted connection keep it open. A reservation keeps it open until a Turn settles or the reservation ends.
  4. A creation that admitted nothing ends right after agent.session.created. When a settlement records no event, the stream ends after the events committed up to the settled state; another client's work committed before that point can still be sent.

Input reserved while the ending Turn captures Artifacts can start a later Turn that the creation stream does not follow.

A retry with the same Idempotency-Key and stream: true returns 201 with only the connection comment and ends at once: it admits nothing and follows no work. To recover a lost Session ID, repeat the request with the same key and stream: false, then read the Session, Turns and Items. Disconnecting stops only the observer. Observe later Turns with the GET stream.

Live event stream ​

GET /v1/agents/sessions/{session_id}/events starts at the latest committed event and sends only events committed after that. Last-Event-ID is ignored. Events publish after their transaction commits.

  • Lifetime. The stream stays open across Turns and after a Turn fails. It ends when the Session is deleted or after the terminal agent.session.failed of an Environment initialization failure; a stream opened after that failure stays open. A keepalive comment is sent every 15 seconds.
  • Buffer. Core keeps at most 256 events and 64 MiB of events per Session, plus one larger event when needed. A reader that falls behind the buffer receives an error event with type server_error and code stream_interrupted, and the stream closes. A socket write that blocks for five seconds also closes it. Execution never waits for a reader.
  • Key recheck. An open stream checks the original Project key at most once per second while idle and before sending output. Revoking the key or archiving the Project closes the stream, and so does an authentication failure. A recheck uses the normal five-second authentication timeout and sends no Session data while it waits. Bytes already sent cannot be recalled.
  • Root work only. Child Turns and child Items publish no Session events; agent.session.subagent.* events and root coordination Items do. Read child work through the Subagent resources.

Event rules ​

  • Session events carry event_id, type and the session snapshot at that transition. Turn events carry session_id and turn_id. There is no Turn waiting event.
  • A new Turn publishes agent.session.turn.created, the user item.added, agent.session.in_progress, then agent.session.turn.in_progress, all from one transaction. When a batch holds several messages, the later messages follow the Session activity.
  • Terminal Turn events (completed, failed, cancelled) carry top-level usage copied from the Turn snapshot at that moment, null when unknown. Other events omit it.
  • item.added and item.done always carry output_index, null for input Items. Function results emit item.added only; item.done is for agent output.
  • An assistant message follows one sequence: item.added in progress with empty content, content_part.added with empty text, output_text.delta events, output_text.done, content_part.done and item.done. A message first observed complete, such as structured output, sends its whole text in one output_text.delta, byte for byte. The completed text replaces the accumulated deltas.
  • Codex command output streams as agent.output.command_execution_output.delta with the command's Item ID and output index. Native output quotas and text conversion apply, so the deltas are not a byte-exact capture; the completed Item is authoritative.
  • A cancelled or failed Turn marks its unfinished Items incomplete and keeps their partial content.
  • A function call stays in required_actions until the harness applies its result, or cancellation or the Turn's end removes it. A repeated notification emits no new state.

Turns and Items ​

Turn and Item lists take after, limit and order (list rules). Cursors are IDs within the same Session.

Turns. Session Turn routes hold root Turns only, ordered by creation time then ID; a child Turn ID returns 404 there. A failed Turn has error: {code: "internal_error", message: "The execution could not complete."} and never raw engine diagnostics. Administrators read the failure category through Session diagnostics.

Items. Items are ordered by the time they were first observed, then by their position in the Session, then by ID. Updates and retries never move an Item or change its output_index, a zero-based position among the Turn's output Items; input Items have none. Reads use the stored history index and never rebuild it from native journals. The Items list includes Items that are still in progress or incomplete.

Item typeContent
messageUser or assistant content parts. phase is the harness's phase when it reports one (commentary, final_answer), otherwise null
command_executionCommand, reported output, exit code, duration and working directory
mcp_callServer and tool identity, arguments, structured result or error
function_call, function_call_outputA linked call and its result. The result always carries output and error, null when the submission omitted them; stored results keep the submitted field presence. Native file changes appear as an apply_patch function call with the changes as arguments and no invented result
web_search_callThe supported action fields (search, open_page, find_in_page, other)
reasoning, agent_message and the Subagent coordination callsSee Subagents. agent_message has no status; reasoning status can be absent or null

A failed tool does not fail its Turn. Tool output is readable by the Session's Project and can contain the tool's own diagnostic text.

Usage ​

Turn and Session usage uses the pinned TokenUsage fields: input, cached input, output, reasoning output and total tokens. Null means unknown, never zero.

  • Turn usage is the latest complete snapshot the harness reported for that Turn. A new snapshot replaces the previous one; repeats never add. Stored snapshots survive cancellation and Worker restarts.
  • Session usage is the sum of root Turn usage when every root Turn has ended (completed, failed or cancelled) with known usage. It is null while any root Turn is queued, running or waiting, and stays null once a root Turn ends without usage. Subagent Turns do not count.
  • By harness. Codex reports measured snapshots while a Turn runs and at its end; a Turn interrupted before any usage report stays null. Claude Code and MiniMax Code report no complete public breakdown, so their usage is null.

Usage is best-effort accounting of reported measurements. It is not an invoice, and Core never estimates missing usage.

Environment initialization failure ​

When an Environment fails to initialize, whether an openai_hosted sandbox or a self_hosted machine, one transaction records the failure and three events, in this order:

EventPayload
agent.session.environment.failederror: {type: "environment_error", code: "environment_connection_failed", message: "The environment failed to connect."}
errorerror: {type: "environment_error", code: "sandbox_error", message: <reason>, param: null}
agent.session.failedThe Session: status: "failed", the reason as error, required_actions: [] and the failure time as last_active_at

Session reads and lists return the same Session, and input that was waiting for the Environment settles as failed in the same snapshot. The GET stream and the creation stream end after agent.session.failed. New input returns 409 (input errors); the Session can be deleted.

The Environment initialization contract defines the fixed failure reasons.

Core's own stream_interrupted error event carries type, code and message without param; error events shaped like the official ones carry param: null.

Released under the MIT License.