> ## Documentation Index
> Fetch the complete documentation index at: https://opensandbox-docs-ask-context.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Sessions and turns

> Understand durable conversations and multi-turn agent work

A session is a durable conversation with a deployed agent. Each turn appends
input and streamed runtime events to the session, allowing clients to resume a
conversation without rebuilding its history locally. The events are listed on
[Session events](/agents/events).

A session keeps the deployment it was created on, chosen by the platform
from the alias it was addressed to. Advancing Development or
promoting to Production changes which deployment new sessions get; turns sent
to an existing session keep running the code it started with, including its
declared tools and memory resources. To run new code, start a new session.
Memory documents are not pinned: they belong to the project and environment,
so a new session bound to the same document reads what the old one saved.

## Agent playground

Open a project in the dashboard and choose **Agent playground**. Select the
agent and environment independently, then create a new session or resume a
previous playground session.

Playground sessions stay in the playground list. Sessions started through an
API appear under **Sessions**, where their durable turn history can be
inspected.

See [Playground and debugging](/agents/playground) for target selection and the
debug inspector.

## Finding sessions

Give a session labels when you create it, such as the ticket, repository or
user it works for, and list sessions by project, environment, agent,
deployment, status, creation or update time and label instead of keeping
your own index of session ids. The list returns rows with the session's
status, its current turn activity and its latest result, and never the
prompts or messages; open a session for its turns and events. The query, the
row shape and the label bounds are on the [API
reference](/agents/api#get-and-list).

When one session stands for one record in your application, give it that
record's id as its `externalReference` at creation. The reference is stored
as given, returned on the session and its lifecycle events, and is an exact
list filter: after a lost create response, retry with the same request key
first, then `GET /sessions?externalReference=<ref>` finds the session
however many unrelated sessions have been created since. Retrying the key
with a different reference is refused, so a record cannot be attached to
the wrong session. Bounds and semantics are on the [API
reference](/agents/api#external-references).

The list is paged: at most 100 rows per request, 50 by default, newest
created first. Follow `nextCursor` with the same filters until it is
`null`; every session matching the filters appears exactly once, however
many were created in the same instant. The [TypeScript
client](/reference/typescript-sdk/agents#sessions) does this for you with
`sessions.iterate(filters)`.

## Execution, labels and results

These describe different parts of a session:

| Field | What it answers |
| - | - |
| `status` and `activity` | Is work queued, running, stopping or finished? |
| `labels` | How does your application organize the session? A ticket ID or `archived` label is your convention; changing it does not stop work. |
| `result` | What did the agent last report? Its `data` is validated against the [result tool's](/agents/tools#the-session-result) output schema; `turnId` and `callId` identify the report. |
| `question` | Is the agent waiting on a person? The open [question](#questions), or `null`. |

A report does not finish a turn, and each report replaces the whole result.
Later work can run, fail or be cancelled while an earlier result remains.
For a review queue, check current activity, the last settled turn's status,
and whether `result.turnId` matches that turn before treating the report as
ready. Your application decides what the reported data means.

## Questions

A turn can end by asking. When the agent calls the
[`ask`](/agents/tools#ask-a-question) tool, the turn completes with
`outcome: "question"` and the session's `question` is set:

```json theme={null}
{
  "question": {
    "id": "<question-id>",
    "text": "Plan first, or go ahead?",
    "options": [{ "label": "Go", "value": "go" }, { "label": "Plan first", "value": "plan" }],
    "askedAt": "2026-10-02T10:00:00.000Z"
  }
}
```

`options` is empty for a free-text question. `question` is `null` when
nothing is open, and it is independent of `result`: a session can hold a
result and an open question at the same time. A consumer that only knows
completed turns still reads an asking turn correctly, because its status is
`completed`.

Answer by sending a turn that names the question with `answers`:

```ts theme={null}
await oc.sessions.turns.send(sessionId, {
  input: "Go",
  answers: session.question!.id,
  idempotencyKey: "answer-1",
});
```

The agent reads the reply as [`useInput().answer`](/agents/inputs#answers-and-steering).
Naming a question that is not the open one is refused with
`409 question_stale`; read the session again and answer its current
question.

While a question is open, a turn sent without `answers` does not run on its
own. It is held, and delivered with the answer, in order, as `steering`, so
the turn that interprets the answer sees every constraint written in the
meantime. `steering` carries a bounded number of held inputs, up to 64 KiB;
the rest run as ordinary turns after the answer, in order. The API answers a
held input with `202 { status: "held", questionId }` and no `turnId`; see
[Turns](/agents/api#turns). A turn already queued behind the asking turn
settles as `cancelled` with reason `held` and keeps its turn id. Held inputs
are in the [event log](/agents/events#questions) as `message.held`, then
`message.delivered` or `message.discarded`.

One question is open at a time. The answering turn may ask again, which opens
a new question. A question closes without an answer with
`question.closed { reason }`:

| `reason` | Cause | Held inputs |
| - | - | - |
| `stopped` | The session was stopped from [Linear](/agents/linear#stop) | Discarded |
| `dismissed` | [Dismiss](/agents/api#questions) was called | Run as ordinary turns, in order |
| `ended` | The session was ended | Not run |
| `undeliverable` | The question could not be posted to Linear | Run as ordinary turns, in order |

Interrupting a turn does not close a question. In Linear, messages typed
while the agent works usually arrive after the answer; see
[Ask before acting](/agents/linear#ask-before-acting).

## Durability and recovery

Closing the browser or redeploying your application does not discard work
the API has accepted. Reopen the session and resume reading its durable
event log. If a submission's response was lost, retry with the same request
key and body; see [Starting work from an application](/agents/api#start-work-from-an-application).

Durable history does not make every command resumable. A lost computer can
leave a command's outcome unknown; OpenComputer does not automatically
replay that command or roll back its effects. The turn can continue with a
tool error or fail. Inspect the recorded outcome and verify external
effects before asking the agent to retry.

[Interrupt](/agents/api#end-and-interrupt) stops the current turn while
keeping the session available for more work. End closes the session and
revokes its memory writes. Neither operation undoes completed writes;
their different acknowledgement and cleanup guarantees are described in
the API reference.

## CLI

Start a session against the development agent:

```bash theme={null}
npm run session
```

The session command always uses the current project's bound Development
deployment. It does not select between local and remote runtimes.

Projects with multiple agents may select another member of the bound project.
The environment remains Development:

```bash theme={null}
npm run session -- --agent reviewer "Review this change"
```

Add `--verbose` to stream non-message lifecycle, runtime, tool, egress, usage,
and turn events. Assistant message text continues to stream normally:

```bash theme={null}
npm run session -- --verbose --agent reviewer "Review this change"
```

Or manage the durable session lifecycle explicitly:

```bash theme={null}
npm run session -- create "Draft a response"
npm run session -- list
npm run session -- inspect <session-id>
npm run session -- attach <session-id>
npm run session -- send <session-id> "Continue"
npm run session -- end <session-id>
```

`list` shows the newest sessions first, one page at a time; narrow it with
exact filters and continue from the cursor the previous page printed. With
`--json` the page is printed as `{ sessions, nextCursor }` for scripts:

```bash theme={null}
opencomputer session list --status suspended --agent reviewer --limit 20
opencomputer session list --external-reference order-42 --json
opencomputer session list --limit 2 --cursor <next-cursor> --json
```

`--agent` takes the agent's id, `--limit` is 1 to 100, and `--cursor`
continues only with the filters that produced it.

Bind [memory](/agents/memory) documents when creating a session; add
`--create-document` to create the ones that do not exist yet:

```bash theme={null}
npm run session -- create --memory notes=workshop --create-document "Plan the workshop"
```

Follow the durable event log directly. With `--json`, each event is one NDJSON
record suitable for a coding agent or log processor:

```bash theme={null}
opencomputer sessions tail <session-id> --after 0 --json
opencomputer sessions tail <session-id> --after 42 --no-follow --json
```

Project scripts resolve the `opencomputer` binary from the installed
`@opencomputer/cli` package. For a one-off invocation outside those scripts,
select the scoped package explicitly:

```bash theme={null}
npx --package @opencomputer/cli opencomputer session "Draft a response"
```

Use `--keep` on supported commands when the session should remain active after
the command exits.

### Workspace files

Files an agent writes under `/workspace` (reports, captures, screenshots, tool
output) can be listed and downloaded with `session files`. Newly written files
appear gradually and may take a few moments to finish syncing. Refresh or list
the workspace again if a file has just been created.

Downloads never pass through the model or the session transcript: OpenComputer
authorizes the exact versioned workspace object for a short-lived direct
download through its file-delivery edge. The CLI streams that object to disk,
checks its byte count, and computes SHA-256 locally before the file is saved.

```bash theme={null}
# List the files in the session's /workspace
opencomputer session files <session-id>
opencomputer session files ls <session-id>

# Download one file (dest may be a file path or an existing directory)
opencomputer session files download <session-id> report/summary.md
opencomputer session files download <session-id> /workspace/captures/run.har ./evidence/

# Download the whole workspace, mirroring its layout
opencomputer session files download <session-id> --all ./workspace-copy

# Machine-readable output with size and locally computed sha256 per file
opencomputer session files download <session-id> --all ./workspace-copy --json
```

`cp` is an alias for `download`. Workspace paths may be written relative to
`/workspace` or with the `/workspace/` prefix; paths that leave the workspace
are refused before any request is made. Each download is written to a
temporary file and renamed into place only after the expected byte count is
received; SHA-256 is reported from the completed local file. An interrupted
transfer never leaves a partial file behind. The versioned S3-backed workspace
remains the source of the file in the
CLI, the session page's **Files** tab, and the
[playground debug inspector](/agents/playground).

## From an application

Applications create sessions with the [management API](/agents/api): `POST
/api/managed-agents/sessions` with an API key, then turns and the event log
under that session. The
[TypeScript client](/reference/typescript-sdk/agents) wraps those
routes; its `sessions.startOnDocument` creates a memory document and a
session bound to it in one call. In the browser, the
[`useAgent`](/agents/react) hook attaches to a session your server created
and renders its messages.

## Input sources

Inside an agent, `useInput().source` identifies how the current work arrived.
This lets the same agent distinguish direct work from delegated subagent work
without defining another session type.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.