# Agent Sessions API

The public agent API uses a session, turn, and durable transcript model. One
Investibook workspace owns one stable agent session. Internal worker
leases and process transports are implementation details.

All endpoints require `Authorization: Bearer <token>`. The transcript SSE
endpoint also accepts `?token=<token>` for browser `EventSource` clients.
Investibook retains its user-scoped Bearer auth so workspace ownership is
enforced by the existing authentication layer.

## Create or fetch a session

```http
POST /api/sessions
Content-Type: application/json

{"workspace_id":"ws-123"}
```

The operation is idempotent. It returns the same opaque `ses_...` ID for the
workspace.

```json
{
  "id": "ses_...",
  "workspace_id": "ws-123",
  "created_at": "...",
  "updated_at": "..."
}
```

## Send a turn

```http
POST /api/sessions/{session_id}/turns
Content-Type: application/json
Idempotency-Key: client-turn-123

{"input":"Diagnose the incident"}
```

By default the request waits and returns the final Turn view as JSON. The
Investibook Web and CLI clients use `?wait=false`; that extension returns the
queued Turn immediately with HTTP `202`, then observes the durable transcript.

Optional fields are `client_turn_id`, `attachment_ids`, `model_tier`, and
`agent_type`. `client_turn_id` or `Idempotency-Key` prevents duplicate model
execution. Only one turn may be active in a session.

## Stop the active turn

```http
POST /api/sessions/{session_id}/turns/stop
```

Cancellation is explicit and idempotent. Closing an HTTP or SSE connection only
detaches the observer and does not stop the turn.

## Fetch session history

```http
GET /api/sessions/{session_id}?history_limit=50
```

The response includes `active_turn` and recent final or active Turn views.

## Fetch transcript records

```http
GET /api/sessions/{session_id}/transcript
GET /api/sessions/{session_id}/transcript?resume_token={token}
```

The response contains durable normalized records and an opaque resume token:

```json
{
  "session_id": "ses_...",
  "records": [
    {
      "sequence": 1,
      "turn_id": "turn_...",
      "data": {"type": "turn_started", "input": "Diagnose the incident"},
      "created_at": "..."
    }
  ],
  "resume_token": "...",
  "reset": false,
  "has_more": false
}
```

Records contain the existing normalized agent events, including `text`,
`thought`, `tool`, `usage`, `image`, `artifact`, `error`, and `complete`.

## Stream transcript records

```http
GET /api/sessions/{session_id}/transcript/events
Accept: text/event-stream
```

The stream emits `record`, `sync`, `reset`, and `error` events. SSE
IDs are opaque transcript resume tokens. Browser and CLI reconnects send
`Last-Event-ID`, so replay survives backend process restarts.

## Compatibility

`GET /api/chat/stream` remains as the compatibility endpoint for clients that
predate the session API. The temporary `/api/chat/runs` API was never merged and
is intentionally not exposed.

## Run9 mapping

| Investibook | Remote execution model |
|---|---|
| `agent_sessions` row | session |
| `agent_turns` row | turn |
| `agent_transcript_records` rows | durable transcript |
| `turns/stop` | stop active turn |

The future switch-controlled Run9 executor stores remote session and turn IDs
as metadata. Run9 owns execution and workspace files. File contents are not
copied into these tables; Studio reads authorized artifact data through the
Run9 file API when needed.
