Skip to content

Latest commit

 

History

History
171 lines (123 loc) · 20.5 KB

sessions-events.md

File metadata and controls

171 lines (123 loc) · 20.5 KB
title 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 API 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:

status When
idle No Turn yet, or the latest Turn completed or was cancelled. Input reserved for a hosted Environment that is still provisioning also reads idle
in_progress The latest Turn is queued, running or waiting
requires_action The latest Turn waits for a function result, including after a cancellation request until the Turn ends, or input waits for a self_hosted machine to connect. required_actions lists function_call or environment_connection entries
failed The 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)
stateDiagram-v2
    [*] --> idle
    idle --> in_progress: Turn queued
    in_progress --> idle: completed or cancelled
    in_progress --> requires_action: awaits function result
    idle --> requires_action: awaits self_hosted machine
    requires_action --> in_progress: result or machine
    requires_action --> idle: Turn ended
    in_progress --> failed: Turn failed
    idle --> failed: input expired or Environment failed
    failed --> in_progress: new input
Loading

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 1–256 bytes identifies the whole ordered batch; an empty or longer key returns 400. 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 Turn that waits for a function result keeps the Session at requires_action with its required_actions until then, and the request emits no Session event. 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 OAC_EXECUTION_CONCURRENCY work slots is free. A Session stays bound to the Runtime that first ran it.
  • Execution availability. While Core shuts down, or after its Worker loses execution ownership, requests return 503 execution_unavailable.

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, and ends with 202 when the Turn starts or with one of the input errors. 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. Disconnecting the waiting request does not cancel the reservation or restart its deadline.

Input errors

Checks run in this order: request validation, whose paths start with events[i], Session lookup, the Harness input check, retry lookup, the Credential check for a message that starts a Turn, 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).

Case Status and code Message
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 Session 409 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 result 409 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 ends 409 conflict_error "The tool call already has a different result."
The same Idempotency-Key with a different batch 409 idempotency_conflict "This idempotency key was used with different input."
A result whose call_id names no function call of this Session 400 invalid_request_error, param null "Unknown pending tool call: {call_id}"
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 ID 400 invalid_request_error, param null "The tool call belongs to a different Turn."
A missing, malformed or foreign Session 404 not_found_error "Resource not found."
A reserved batch whose five-minute deadline passed before a Turn started 409 environment_input_expired "The environment input deadline elapsed before admission."
A reserved batch cancelled because an administrator archived the Session or reset its deployment 409 environment_input_cancelled "The environment input was cancelled before admission."
New input after an openai_hosted Environment failed to provision 409 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 Environment 409 environment_unavailable "The environment is no longer available for new input."
A message that would start a Turn on a Session whose bound MCP Credential, or its Vault, was deleted 400 invalid_request_error, param null "MCP credential_id {id} was not found in an attached vault"
A message the Session's Harness cannot take, such as whitespace-only text on MiniMax Code 400 unsupported_or_invalid_configuration See whitespace-only text

An empty turn_id or blank call_id is the generic 400 invalid_request. Apart from the unknown tool call message, which names its call_id as the official SDK expects, and the Credential message, which names the Credential as Session creation does, 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, except a Subagent's function_call Item, published once as item.added; 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; Environment events carry session_id and a null 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 reasoning Item follows one sequence: item.added in progress with an empty summary; for each summary_text part in order, reasoning_summary_part.added with empty text, reasoning_summary_text.delta events, then reasoning_summary_text.done and reasoning_summary_part.done once the next part starts or the Item ends; then item.done. reasoning_summary_part.done has status incomplete for a part a cancelled or failed Turn cut off, otherwise null.
  • 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 the Turn ends. A cancellation request does not remove it from a waiting Turn. 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's error has an official code and a fixed message for that code, never raw engine diagnostics; Differences from OpenAI lists the codes Core reports. Stream snapshots report the same error as reads. 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. GET /agents/sessions/{session_id}/turns/{turn_id}/items lists only the Items of that root Turn with the same paging, ordering and cursor rules; a Turn that does not exist in the Session returns 404.

Item type Content
message User or assistant content parts. phase is the Harness's phase when it reports one (commentary, final_answer), otherwise null
command_execution Command, reported output, exit code, duration and working directory
mcp_call Server and tool identity, arguments, structured result or error
function_call, function_call_output A 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. Codex file changes appear as an apply_patch function call with the changes as arguments and no invented result
web_search_call The supported action fields (search, open_page, find_in_page, other); Claude Code reports search for WebSearch and open_page for WebFetch
reasoning One native reasoning item, each native part one summary_text part, present only when the Agent sets reasoning.summary; with it null (the default) no reasoning Items or reasoning events appear for any Turn, root or child. OpenAI compatibility describes the text. Status can be null on a child Turn's Item
agent_message and the Subagent coordination calls See Subagents. agent_message has no status

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.

PostgreSQL cannot store U+0000, so Harness output that contains it, such as a command's output, is stored, streamed and listed with U+FFFD in its place, and the Turn continues.

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:

Event Payload
agent.session.environment.failed error: {type: "environment_error", code: "environment_connection_failed", message: "The environment failed to connect."}
error error: {type: "environment_error", code: "sandbox_error", message: <reason>, param: null}
agent.session.failed The 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.