Skip to content

Sessions

A session is a conversation between a user (or agent) and an LLM. It contains messages (user prompts and assistant responses), parts (tool calls, tool results, text blocks within a message), and token usage. Sessions originate in opencode, which owns the schema. arbe layers run correlation, sync, and querying on top.

See also: CLI, Control plane, Runs and sessions, Tasks.

arbe chat creates or resumes a session. arbe loop runs multiple iterations against the same session. arbe do dispatches a new session on a sandbox. Subagent tool calls spawn child sessions within the parent’s run.

Lifecycle

  1. A CLI command starts a run, which creates or resumes an opencode session.
  2. During execution, opencode writes messages and parts to its SQLite.
  3. On run completion, @arbe/observability copies the session data into .arbe/arbe.db — a local snapshot for offline access and correlation with runs.
  4. If logged in (arbe login), the run and session data sync to Supabase via POST /api/runs. If not, local-only.

A session can span multiple runs (arbe resume continues an existing session). A run references a primary session but may also spawn subagent sessions — run.session_ids is an array.

Storage

Three locations, same data at different durability levels:

opencode SQLite (~/.local/share/opencode/opencode.db) is the live store. Messages and parts are written here during execution. This is the only place subagent sessions exist until run completion.

Local arbe SQLite (.arbe/arbe.db) holds verbatim copies of session, message, and part rows — snapshotted on run completion by @arbe/observability. Tables: sessions, messages, parts. The runs table cross-references sessions by session_id. This store powers arbe sessions and arbe session <id> when querying local data.

Supabase (arbe_sessions, arbe_messages, arbe_parts) holds the same data, pushed on run completion when authenticated. arbe_sessions has a run_id foreign key to arbe_runs. This store powers arbe sessions --remote and the web UI at /runs.

Tasks do not reference sessions directly. The correlation goes through runs: a run has both a task_id and a session_id.

Querying

Local sessions come from two places: opencode’s SQLite (for the session list — id, title, timestamps) and arbe’s SQLite (for run correlation, status, task association via @arbe/observability).

Remote sessions come from Supabase via GET /api/sessions. Query params: limit (default 50, max 200), since (ISO timestamp cutoff), task (filter by associated run’s task_id).

The CLI merges local and sandbox sources by default. arbe sessions queries opencode’s local db plus any registered sandboxes via their API. --remote switches to querying Supabase instead — useful for cross-machine history.

CLI surface

arbe sessions list sessions (local + sandboxes)
arbe sessions --limit 10 most recent 10
arbe sessions --since 1h last hour (durations: 30m, 1h, 2d, 1w)
arbe sessions --remote from Supabase instead of local
arbe sessions -s <sandbox> scope to one sandbox
arbe sessions gc reap stale runs
arbe session <n|id> detail view (status, sandbox, runs, tasks)
arbe session <n|id> history full conversation
arbe result <n|id> last assistant output
arbe resume <n|id> continue the session

resume is the user-facing command name. attach is the lower-level opencode action arbe uses under the hood.

API

GET /api/sessions — list the authenticated user’s synced sessions. Returns rows from arbe_sessions with selected columns (id, run_id, project_id, title, directory, timestamps, summary stats). Auth via Bearer token or session cookie.

POST /api/runs — sync a completed run with optional session payload. This is the write path — sessions are upserted as a nested object within the run payload, not posted independently. Payload validated against ArbeRunSchema in packages/core/schemas/run.ts. Route handler: packages/www/src/routes/api/runs/+server.ts.

No dedicated session write endpoint exists. Sessions always arrive as part of a run sync.

Schemas

Run and session payload types are Zod schemas in packages/core/schemas/run.ts (@arbe/core/schemas/run). Record content schemas live in packages/core/schemas/record-content.ts. The schemas are the source of truth — TypeScript types are derived via z.infer<>, and the route validates against the same schemas the CLI builds payloads for.

ArbeRunSchema is the top-level POST body. It nests ArbeSessionSchema (with ArbeMessageSchema and ArbePartSchema arrays). Enum schemas RunOrigin, RunStatus, and RunExecution constrain the string unions.