> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auto.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Sessions

> A session is a durable, observable execution of an agent: how sessions spawn, move through their lifecycle, receive commands and events, park, hand off, and end.

A session is one durable execution of an [agent](/concepts/agents): a sandbox running the agent's harness, a transcript, and a command queue, all backed by a Temporal workflow so nothing is lost when processes restart. Sessions are the thing you watch — every one is observable live from the web app and through agents' own introspection tools. This page covers the full arc: spawn, lifecycle, turns and commands, event delivery, parking, ending, and handoff.

## 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's `message` template 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.spawn` tool, passing the kickoff message and optionally the human requester on whose behalf the work runs.
* **The platform replaces a pool member** — for `replace: auto` agents, a failed singleton session, or one stopped with respawn intent, gets a successor spawned by the pool reconciler (see [runtime controls](/reference/runtime-controls)).

Creation pins everything the session will run with, in one transaction:

* 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 `maxPerSessionUsd` spend cap is frozen into the session record, so editing the agent cannot rewrite a live session's spend contract.
* For a `concurrency: 1` agent, the creation transaction claims the agent's singleton slot. If another live session already holds it, the spawn is refused with `409` and the live session's ID (`AgentSlotConflictError`), so you always know which session absorbed the work.
* Git mounts are validated up front: a payload-templated `ref` that 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:

| Status     | Meaning                                                                                                                       |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `queued`   | Created; runtime not yet provisioned or first turn not yet accepted.                                                          |
| `running`  | A turn is executing in the sandbox.                                                                                           |
| `awaiting` | Idle and resumable: the last turn completed, asked a question, or failed turn-scoped. New commands and routed events wake it. |
| `failed`   | Terminal. A turn failed with session scope, or provisioning failed. The record carries the error.                             |
| `stopped`  | Terminal. A stop command ended it.                                                                                            |

`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](#ending-a-session-stop-fail-archive).

## 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:

| Kind                          | Payload                                                          | Effect                                                                                                                                                                                |
| ----------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message`                     | `{ message, model?, reasoningEffort?, metadata?, attachments? }` | Deliver text (and uploaded files) into the session; opens a turn.                                                                                                                     |
| `answer`                      | `{ toolCallId, answers, response? }`                             | Answer a pending structured question (e.g. Claude Code's AskUserQuestion): map each question to a selected option, or reply freeform.                                                 |
| `interrupt`                   | `{ reason? }`                                                    | Abort the in-flight turn.                                                                                                                                                             |
| `stop`                        | `{ reason?, respawn?, handoff? }`                                | End the session terminally; optionally request a pool replacement and persist a handoff.                                                                                              |
| `pause` / `resume` / `cancel` | `{ reason? }`                                                    | Accepted and persisted, but not part of the runtime dispatch set today — only `start`, `startWithMessage`, `message`, `answer`, `interrupt`, and `stop` are delivered to the runtime. |

`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:

| Turn status         | Meaning                                                                                                                             |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `accepted`          | The runtime acknowledged the command.                                                                                               |
| `in_progress`       | The harness is working; output streams into the transcript.                                                                         |
| `waiting_for_input` | The agent asked a structured question and is blocked on an `answer` command.                                                        |
| `completed`         | Finished normally.                                                                                                                  |
| `failed`            | Failed, with `failureScope` deciding whether the session survives (`turn`) or fails with it (`session`).                            |
| `stale`             | A watchdog concluded the turn after its durable-progress clock — and, on runtimes that emit them, its liveness beats — went silent. |

## 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.

Commands sent while the session is still `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 at `awaiting`), a held recovery command is recorded, and a park row bounds how long the pause may last.

| Park reason                                                        | Resumes when                                                         | Bounded by                         |
| ------------------------------------------------------------------ | -------------------------------------------------------------------- | ---------------------------------- |
| `billing_insufficient_credits` / `billing_spending_limit_exceeded` | Credits are added to the organization                                | 7-day TTL, then expires terminally |
| `billing_spend_cap_exceeded`                                       | The [spend cap](/reference/runtime-controls) is raised or overridden | Persists until explicitly resolved |
| `provider_outage`                                                  | Automatic re-probe every 5 minutes                                   | 6-hour TTL                         |

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_current` tool when its work is complete. The self-archive path is rejected while the session holds active `held`-policy bindings — an agent bound to an open PR cannot declare itself done; the closed-PR trigger's `release: true` must free the binding first.
* An operator archives it from the web app.
* The auto-archive sweeper runs every 10 minutes: it archives `awaiting` sessions whose agent declares `session.archiveAfterInactive` and whose inactivity exceeds it (skipping sessions with active commands, turns, or checks), and repairs stale `queued`/`running` rows with no active work back to `awaiting`. 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](/reference/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 an `ambientStatus` line 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](/runtime/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](/runtime/chat-tools).

Two display fields do the heavy lifting in lists: `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

<CardGroup cols={2}>
  <Card title="Agents" href="/concepts/agents">
    The YAML that defines what a session runs.
  </Card>

  <Card title="Triggers and events" href="/concepts/triggers-and-events">
    How events spawn sessions and route into live ones.
  </Card>

  <Card title="Concurrency" href="/concepts/concurrency">
    concurrency: 1, replace: auto, onReplace, and onUnmatched: spawn for orchestrators.
  </Card>

  <Card title="Runtime controls" href="/reference/runtime-controls">
    Concurrency slots, replacement, spend caps, and model fallback.
  </Card>

  <Card title="auto.sessions.* tools" href="/runtime/auto-tools">
    How agents themselves spawn, message, and introspect sessions.
  </Card>
</CardGroup>
