# Sessions and runs

> Manage deployment pinning, sandbox state, concurrency, and cleanup.

## Sessions

Create a session with `agent_slug` or `deployment_id`.

The session stays pinned to its selected deployment snapshot.

Pass `user_id` when you know which of your end users is on the other end. It decides whose agent memory the conversation reads, so read [Agent memory](https://pantheon-todo.alethiconsulting.com/docs/sessions-and-runs#memory) before you leave it out.

| deployment_source | Meaning                                                                                          |
| ----------------- | ------------------------------------------------------------------------------------------------ |
| `published`       | The slug selected the active deployment, or the newest published deployment when none is active. |
| `legacy`          | Pantheon used a legacy deployment snapshot.                                                      |

## Sandbox status

| Value     | Meaning                                    |
| --------- | ------------------------------------------ |
| `ready`   | The sandbox is available.                  |
| `stopped` | The sandbox exists but is stopped.         |
| `missing` | The recorded sandbox no longer exists.     |
| `none`    | This session does not require a sandbox.   |
| `unknown` | Pantheon could not determine availability. |

## Runs

From this release, `run_id` is 32 hex characters in responses and stream events alike.

Start one run at a time. Stream until the run becomes terminal or waits for input.

| Value               | Meaning                                   |
| ------------------- | ----------------------------------------- |
| `queued`            | The run is waiting for a worker.          |
| `running`           | The agent is processing the turn.         |
| `waiting_for_input` | The run needs an application tool result. |
| `completed`         | The run finished successfully.            |
| `failed`            | The run stopped with an error.            |
| `cancelled`         | A client cancelled the run.               |
| `expired`           | The run passed its wait deadline.         |

### Record integrity

The `record_integrity` field describes whether the run's history was recorded, separately from its `status`.

| record_integrity | Meaning                                                                                                            |
| ---------------- | ------------------------------------------------------------------------------------------------------------------ |
| `complete`       | The run's history was recorded. This is the normal answer, including for a turn that finished with nothing to say. |
| `lost`           | The run executed but its history was not recorded. An empty timeline must not be read as a silent turn.            |

## Get one run

Send `GET` to `/v1/sessions/{session_id}/runs/{run_id}` to read a run without streaming. Requires `sessions:read` and `runs:read`.

```bash title="Terminal"
curl -sS "$PANTHEON_BASE_URL/v1/sessions/$SESSION_ID/runs/$RUN_ID" -H "Authorization: Bearer $PANTHEON_API_KEY"
```

The response contains id, session_id, status, record_integrity, model_used, usage, error, created_at and completed_at, using the same shape as cancel. Nullable fields are returned as null when unavailable.

Use the run_id returned by create or wait. Both compact and hyphenated UUIDs are accepted; the response id is compact. Missing sessions, missing runs and resources in another workspace return HTTP 404.

## Recover from SESSION_BUSY

A second run returns HTTP 409 with `SESSION_BUSY`.

```json title="JSON response"
{
  "error": {
    "code": "SESSION_BUSY",
    "message": "Session 'session_123' has an active run '0123456789abcdef0123456789abcdef' in status 'waiting_for_input'.",
    "request_id": "request_123",
    "active_run_id": "0123456789abcdef0123456789abcdef",
    "active_run_status": "waiting_for_input",
    "recovery": [
      "cancel the active run via POST /v1/sessions/session_123/runs/0123456789abcdef0123456789abcdef/cancel",
      "or resume it via POST /v1/sessions/session_123/runs/0123456789abcdef0123456789abcdef/resume"
    ]
  }
}
```

Resume the active run when it waits for a tool. Otherwise wait or cancel it.

```bash title="Terminal"
curl -sS -X POST \
  "$PANTHEON_BASE_URL/v1/sessions/$SESSION_ID/runs/$ACTIVE_RUN_ID/cancel" \
  -H "Authorization: Bearer $PANTHEON_API_KEY"
```

## Agent memory

An agent has a small store of notes, separate from the conversation transcript. It is a folder at `/memories/` that the model can read and edit with its own file tools, seeded with a template that asks it to keep preferences such as a preferred name, language, tone or time zone, and the task in progress.

> **Memory does not yet carry between sessions**
>
> On the hosted runtime an agent's memory files live with the session sandbox, so they last for the conversation and are lost when the sandbox is recycled. Durable memory is wired but not switched on: the durable store exists and is written to, but only by the seeder, which copies the stock template once per scope. No note an agent wrote has ever been placed in it. Do not build on memory persisting across sessions yet. The scope rule and the routes below describe the durable store and already hold, so nothing an agent records can reach another end user when it is switched on.

### How it is scoped

Memory belongs to one end user. Which one depends on how the session was created.

| scope     | What it covers                                                                   |
| --------- | -------------------------------------------------------------------------------- |
| `user`    | The session named a user_id. Memory follows that end user across their sessions. |
| `session` | The session named no user_id. Memory stays inside that one conversation.         |

A session created without `user_id` gets a memory of its own, not a shared one. Two of your end users can never read each other's notes, and an unidentified visitor cannot read an identified user's notes. The cost of that default is that memory does not carry from one of that visitor's conversations to the next. Send `user_id` at session creation and it will, once durable memory is switched on.

Memory never crosses a workspace.

### What is stored and for how long

Whatever the agent chose to write, as text, in PostgreSQL on Alethi infrastructure. It is not an encrypted or hidden store: your workspace can read its shape through the routes below, and Alethi operators can access it for support like any other stored data. Today the durable store holds only the seeded template for each scope, because agents write to the session sandbox instead.

There is no expiry. Memory lives until you delete it, until the session or the end user it belongs to is deleted, or until the workspace is deleted. Tell your end users what your agent remembers about them, and instruct your agent through its system prompt if you want it to keep less.

A long conversation does not thin memory out. Pantheon compacts a conversation that grows past its token threshold, replacing older turns with a summary, but memory is not part of that history: it is re-sent in full on every model call. A fact the agent wrote down is still in front of the model after a compaction, even when the exchange that produced it is no longer in the transcript. Erasing is the way to remove it.

An agent's declared `tools` list does not govern memory. It governs the built-in tools named in [Core concepts](https://pantheon-todo.alethiconsulting.com/docs/concepts); memory is read and written with the agent harness file tools, alongside the sandbox, and follows `requires_sandbox`. An agent that declares no tools still keeps notes.

### See what an agent holds

Send `GET` to `/v1/users/{user_id}/memory` for one end user, or `/v1/sessions/{session_id}/memory` for one conversation. The response reports the scope, whether anything is stored, and the path, size and timestamps of each entry. It does not return the stored text.

```bash title="Terminal"
curl -sS "$PANTHEON_BASE_URL/v1/users/$USER_ID/memory" \
  -H "Authorization: Bearer $PANTHEON_API_KEY"
```

The session route reports the memory that session actually reads. For a session bound to a `user_id` that is the user's memory, and the response says so.

### Erase it

Send `DELETE` to the same path. The entries are removed, not hidden, and the count of removed entries comes back. The next session in that scope starts from the stock template with nothing learned about the person.

```bash title="Terminal"
curl -sS -X DELETE "$PANTHEON_BASE_URL/v1/users/$USER_ID/memory" \
  -H "Authorization: Bearer $PANTHEON_API_KEY"
```

Deleting a session erases that session's memory. Deleting an end user erases theirs. Neither touches the other scope.

Every erase that removed something is recorded in your audit log as `memory.erased`, whichever of the three routes reached it. The record names the scope and how many entries went, never what they said.

## Archive a session

Archive finished sessions. Pantheon then destroys the attached sandbox.

```bash title="Terminal"
curl -sS -X POST "$PANTHEON_BASE_URL/v1/sessions/$SESSION_ID/archive" \
  -H "Authorization: Bearer $PANTHEON_API_KEY"
```

## Publish updates

### Update the agent

Changing an agent creates a new version.

### Publish again

Create a deployment for that version and its tool definitions.

### Create a session

New slug sessions select the active deployment, or the newest published deployment when none is active. Existing sessions stay pinned.

## Roll back a deployment

Send `POST` to `/v1/deployments/{id}/activate` with the earlier published deployment id.

This makes that deployment active for new sessions created by agent slug. Existing pinned sessions stay unchanged.

Only published deployments can be activated. Legacy snapshots return HTTP 409.

An active deployment takes precedence over newer publications until you activate another deployment.

Use `agent_id` to filter `GET` `/v1/deployments`.

Each returned deployment includes an `active` boolean. Activation returns the deployment with this flag set to `true`.

## Pitfalls

> **Warning**
>
> - **Session creation can take about 2.5 seconds:** Sandbox provisioning happens during creation when required.
> - **Run creation can take about 2.7 seconds:** Show a pending state before streaming begins.
> - **Pantheon recreates a missing sandbox:** Treat sandbox.recreated as data loss for unpublished workspace files.
> - **Memory does not survive a session:** Agent memory currently lives with the session sandbox. Durable cross-session memory is not switched on yet.
> - **Memory looks empty for a returning visitor:** Sessions created without user_id each get their own memory. Send user_id to carry memory between a person's sessions once durable memory is on. See [Agent memory](https://pantheon-todo.alethiconsulting.com/docs/sessions-and-runs#memory).
