Where sessions come from
- A trigger fires with
routing: { kind: spawn }— the normal case in a running factory. The session’s input is the triggering event, and the trigger’smessagetemplate becomes its first instruction. - A person starts one — from the agent’s page in the web app, with an optional kickoff message.
- Another agent spawns it with the
auto.sessions.spawntool, passing the kickoff message and optionally the human requester on whose behalf the work runs. - The platform replaces a pool member — for
replace: autoagents, a failed singleton session, or one stopped with respawn intent, gets a successor spawned by the pool reconciler (see runtime controls).
- Status starts at
queued, and the session’s dispatch workflow ID is derived from its session ID. - The agent spec, its environment, and its tool wiring are snapshotted onto the session. A later apply changes future sessions only; an in-flight session never silently gains or loses tools.
- The agent’s
maxPerSessionUsdspend cap is frozen into the session record, so editing the agent cannot rewrite a live session’s spend contract. - For a
concurrency: 1agent, the creation transaction claims the agent’s singleton slot. If another live session already holds it, the spawn is refused with409and the live session’s ID (AgentSlotConflictError), so you always know which session absorbed the work. - Git mounts are validated up front: a payload-templated
refthat cannot resolve, or a repository with no active GitHub connection grant, fails the spawn immediately instead of queueing a session that can never stage.
Lifecycle
A session is always in exactly one status:queued, running, and awaiting are the resumable statuses; failed and stopped are terminal, and commands sent to a terminal session are rejected.
While a session is being provisioned, a runtimePhase field narrates progress beneath the status: building_image → booting_container → preparing_environment → starting_runtime → running. The phases are written by the sandbox provisioning path and cleared once the session settles.
Status is derived from turn facts, not set by hand: a turn reaching waiting_for_input, completed, or stale puts the session at awaiting; a turn failing with failureScope: "turn" also lands at awaiting (the session survives its turn’s failure), while failureScope: "session" fails the whole session. A concurrent stopped always wins — the transition is guarded so a stop cannot be overwritten.
Archived is orthogonal to status. archivedAt hides a session from the default list and marks it semantically done, but an archived session in a resumable status is not dead — see Ending a session.
Turns and commands
All work enters a session as a command and executes as a turn.Commands
A command is a durable row: it is created (pending), claimed by the session’s dispatch workflow (dispatching), and settles accepted, failed, or canceled. Creation is idempotent, and the sender is stamped server-side so operator attribution cannot be spoofed — whether the command came from the web app composer, a chat thread, or another agent.
The command vocabulary:
start and startWithMessage are platform-created: every session gets exactly one idempotent start command that boots the runtime. When a kickoff message resolves — the manual start’s message, the trigger’s rendered message, or the agent’s initialPrompt — the start carries it and opens the first turn; when none resolves, the bare start boots the runtime and the session awaits its first message.
Message senders are typed — operator (a person, with identity), agent (another session), or system — and the transcript shows each message’s sender and, for trigger deliveries, the originating event key and a link to the external artifact behind it.
Turns
Each dispatched command opens a turn with its own status ladder:How events reach a live session
Each session has a long-lived dispatch workflow that drains its command queue in order. Three delivery behaviors matter when events and people talk to the same session:- Interrupt vs. defer. Delivered trigger messages interrupt the in-flight turn immediately so steering lands right away — except GitHub check and workflow-run completions (
github.check_run.completed,github.workflow_run.completed), which defer until the session is idle so a CI verdict never aborts a turn mid-tool-call. - Trigger debounce. Trigger deliveries wait in a held lane and flush as one aggregated command once 5 seconds have passed since the oldest held delivery or 2.5 seconds since the newest. A burst of related events (a push plus its check runs) becomes one coherent update instead of five interruptions. Human messages and lifecycle commands never wait on this window. The transcript still renders each absorbed delivery as its own entry inside the bundled message.
- Message batching. When several message commands are pending at delivery time, up to 32 are combined into one runtime turn. The batch stops at the first non-message command, so answers and stops keep their own turns.
queued (runtime provisioning) simply wait in the queue and deliver in order once the runtime is up. And a work-injecting command aimed at an archived session in a resumable status revives it: the same transaction that records the command clears archivedAt, so routed events can wake a session that had archived itself as done.
Parked sessions
Some failures should pause a session, not kill it. A park keeps the session resumable without introducing a new status: the failed turn settles turn-scoped (session atawaiting), a held recovery command is recorded, and a park row bounds how long the pause may last.
While parked, the sandbox may be paused to stop the meter. When the resume condition arrives, the held recovery command becomes due and the session picks up where it stopped; if the TTL expires first, the park resolves as
expired and the session settles terminally.
Ending a session: stop, fail, archive
Stop is the terminal command.{ reason? } is recorded; respawn asks the pool reconciler to spawn a replacement on the latest agent spec (for replace: auto agents); handoff persists wind-down state on the stopped session. Stop is the only lifecycle command that accepts these options.
Failure happens when a turn fails session-scoped or provisioning cannot complete. The session record carries the structured error, and the sessions list surfaces a distilled failureReason. A failed session can be retried — a retry creates a fresh session for the same input, idempotently.
Archive is softer than both: it marks the session done-for-now while leaving it revivable. Three paths archive a session:
- The agent archives itself with the
auto.sessions.archive_currenttool when its work is complete. The self-archive path is rejected while the session holds activeheld-policy bindings — an agent bound to an open PR cannot declare itself done; the closed-PR trigger’srelease: truemust free the binding first. - An operator archives it from the web app.
- The auto-archive sweeper runs every 10 minutes: it archives
awaitingsessions whose agent declaressession.archiveAfterInactiveand whose inactivity exceeds it (skipping sessions with active commands, turns, or checks), and repairs stalequeued/runningrows with no active work back toawaiting. Archiving releases the session’s bindings in the same transaction.
Handoff
Sessions are disposable; their knowledge should not be. A handoff is a trimmed note of up to 20,000 characters persisted on the session record (archiveHandoff) when the session winds down — passed by the agent to auto.sessions.archive_current { handoff }, or carried on a stop command’s handoff option.
For concurrency: 1 agents the handoff completes the replacement loop: when a successor session’s kickoff command is created, the platform attaches the predecessor’s persisted handoff to it, so the new session starts knowing what the old one knew. Combined with the agent’s onReplace prompt and onUnmatched: spawn routing, this is how singleton agents survive replacement without shared memory — see runtime controls.
Observing sessions
Every session is observable live, from wherever you already are:- Web app — the session page streams the transcript over SSE, shows queued messages, structured questions, bindings, and per-session usage; the sessions list live-updates with each session’s
displayTitle, status, and anambientStatusline summarizing what the agent is doing right now. - Agents themselves — the
auto.sessions.*introspection tools (summary,conversation,search,tools,triggers,bindings,commands) give any agent read access to any project session, which is what makes supervisor patterns work. See auto tools. - Chat — a session bound to a Slack thread converses where your team already is; the thread is an observation surface and a steering channel at once. See chat tools.
displayTitle (at most 64 characters, rendered from the agent’s displayTitle template against the event payload, or inferred when the agent declares displayTitle: infer) and ambientStatus, a periodically refreshed one-line description of current activity maintained by the dispatch workflow while the session runs.
Where to go next
Agents
The YAML that defines what a session runs.
Triggers and events
How events spawn sessions and route into live ones.
Concurrency
concurrency: 1, replace: auto, onReplace, and onUnmatched: spawn for orchestrators.
Runtime controls
Concurrency slots, replacement, spend caps, and model fallback.
auto.sessions.* tools
How agents themselves spawn, message, and introspect sessions.