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
- A CLI command starts a run, which creates or resumes an opencode session.
- During execution, opencode writes messages and parts to its SQLite.
- On run completion,
@arbe/observabilitycopies the session data into.arbe/arbe.db— a local snapshot for offline access and correlation with runs. - If logged in (
arbe login), the run and session data sync to Supabase viaPOST /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 10arbe sessions --since 1h last hour (durations: 30m, 1h, 2d, 1w)arbe sessions --remote from Supabase instead of localarbe sessions -s <sandbox> scope to one sandboxarbe sessions gc reap stale runs
arbe session <n|id> detail view (status, sandbox, runs, tasks)arbe session <n|id> history full conversationarbe result <n|id> last assistant outputarbe resume <n|id> continue the sessionresume 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.