# Events

> Render live output and replay durable session state.

## Connect

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

Set `event_deltas` to `true` for live text and reasoning deltas.

```bash title="Terminal"
curl -N -sS \
  "$PANTHEON_BASE_URL/v1/sessions/$SESSION_ID/events/stream?event_deltas=true" \
  -H "Authorization: Bearer $PANTHEON_API_KEY" \
  -H "Last-Event-ID: $LAST_EVENT_ID"
```

The server sends a keep-alive comment every 15 seconds.

## Durable events

Durable events have an SSE `id` and JSON `sequence`. Save the SSE id as your replay cursor.

```yaml title="SSE"
event: tool.started
id: evt_101
data: {"id":"evt_101","sequence":7,"type":"tool.started","workspace_id":"workspace_123","session_id":"session_123","run_id":"0123456789abcdef0123456789abcdef","created_at":"2026-09-03T12:00:01Z","content":{"tool_call_id":"call_123","name":"echo_tool","input":{"message":"hello"}},"data":{"tool_call_id":"call_123","name":"echo_tool"}}
```

Text event content is a list of blocks. Structured event content is a JSON object.

## Ephemeral events

Ephemeral events have no SSE id.

```yaml title="SSE"
event: message.delta
data: {"type":"message.delta","run_id":"0123456789abcdef0123456789abcdef","delta":{"text":"Hello"}}
```

Pantheon does not persist or replay these events.

## Event catalog

| Value                   | Meaning                                                         |
| ----------------------- | --------------------------------------------------------------- |
| `run.started`           | A worker started the run.                                       |
| `run.completed`         | The run finished successfully.                                  |
| `run.failed`            | The run stopped with an error.                                  |
| `run.cancelled`         | A client cancelled the run.                                     |
| `message.started`       | Assistant message generation started.                           |
| `message.delta`         | A live assistant text fragment arrived.                         |
| `message.completed`     | Pantheon stored the final assistant message.                    |
| `reasoning.delta`       | A live reasoning fragment arrived.                              |
| `reasoning.message`     | Pantheon stored a durable reasoning message.                    |
| `intermediate.delta`    | A live intermediate output fragment arrived.                    |
| `tool.policy`           | What the agent was granted and what was withheld, once per run. |
| `tool.started`          | The agent started a tool call.                                  |
| `agent.custom_tool_use` | Your application must execute an application tool.              |
| `tool.completed`        | A tool call completed.                                          |
| `tool.failed`           | A tool call failed.                                             |
| `tool.refused`          | The agent called a tool its revision does not grant.            |
| `session.status_idle`   | The session returned to idle.                                   |
| `session.error`         | A session error occurred.                                       |
| `sandbox.recreated`     | Pantheon replaced a missing sandbox.                            |
| `stream.resync`         | The client must replay and reconnect with backoff.              |

The application tool lifecycle events share one `tool_call_id`.

Read [Application tools](https://pantheon-todo.alethiconsulting.com/docs/application-tools) for correlation and resume steps.

Every run opens with one `tool.policy` event naming the agent revision that governed it, the built-in tools it granted, and each tool it withheld with the reason. A call to a tool the revision does not grant is never executed and arrives as `tool.refused`, not `tool.failed`, so a policy decision is never mistaken for a broken tool.

## Handle stream.resync

If live subscription or barrier startup fails, the connection stays open and delivers persisted events with ids. The response header `X-Pantheon-Live-Events: unavailable` identifies this mode; otherwise its value is `live`, including when deltas were not requested. Startup degradation sends no resync frame. A live delivery failure mid-stream sends one resync frame without an id, then closes the connection.

### Save the cursor

Keep the last durable SSE id.

### Reconnect with backoff

Send that id in `Last-Event-ID`. If a resync arrives again within seconds of connecting, wait before the next attempt (500 ms doubling to 30 s) and cap the attempts, so a server that keeps resyncing never sees a tight loop.

### Read the replay

Pantheon replays persisted events before live output resumes.

## Pitfalls

> **Warning**
>
> - **Deltas repeat after reconnect:** Use only the SSE id from durable events as the cursor.
> - **Live text is missing:** Include event_deltas=true. Final durable message events still arrive.
> - **Two tool pills appear:** Merge every tool lifecycle event by tool_call_id.
