Documentation menu
Documentation

State and SQLite

Use synced state for what interfaces need now, and SQLite for durable records and queries.

Synced state

Define initialState, read this.state, and replace it with this.setState(next). Updates persist and broadcast to connected clients.

validateStateChange() runs synchronously before persistence. onStateChanged() observes a successful update.

example
type State = { phase: "waiting" | "running"; progress: number };

initialState: State = { phase: "waiting", progress: 0 };

validateStateChange(next: State) {
  if (next.progress < 0 || next.progress > 1) {
    throw new Error("progress must be between 0 and 1");
  }
}

setProgress(progress: number) {
  this.setState({ ...this.state, phase: "running", progress });
}

Embedded SQL

Each agent instance has isolated SQLite. Use the tagged this.sql helper for history, indexes, queues, and data that should not be broadcast as one state object.

example
this.sql`CREATE TABLE IF NOT EXISTS turns (
  id TEXT PRIMARY KEY,
  role TEXT NOT NULL,
  content TEXT NOT NULL
)`;

this.sql`INSERT INTO turns (id, role, content)
  VALUES (${crypto.randomUUID()}, ${role}, ${content})`;

const turns = this.sql<{ role: string; content: string }>`
  SELECT role, content FROM turns ORDER BY rowid
`;