Thread stores¶
A ThreadStore is where a thread's full AG-UI event log lives. kaboo-runtime
ships two implementations and lets you write your own
(Custom store).
The ThreadStore contract¶
Every store implements seven methods:
| Method | Purpose |
|---|---|
appendEvents(threadId, agentId, events) |
Append a completed run's events, in order. |
readEvents(threadId) |
Read the full event log, verbatim and in order. |
readState(threadId) |
Latest state from the last STATE_SNAPSHOT, or null. |
saveMessages(threadId, messages) |
Persist the derived message snapshot. |
readMessages(threadId) |
Read the derived message snapshot. |
listThreads() |
Every thread, most recently updated first. |
clear(threadId?) |
Delete one thread, or all when omitted. |
Events are stored verbatim — there is no compaction — so ACTIVITY_SNAPSHOT
and CUSTOM events survive the round-trip and the full UI can be replayed.
InMemoryThreadStore¶
Keeps everything in a Map for the life of the process. Ideal for local
development, tests, and the demo. Data is lost on restart.
import { createKabooRunner, InMemoryThreadStore } from "@pgege/kaboo-runtime";
const runner = createKabooRunner(new InMemoryThreadStore());
PostgresThreadStore¶
Durable persistence in Postgres, using its own tables (independent of your
application schema). pg is an optional peer dependency — install it to use this
store.
import { createKabooRunner, PostgresThreadStore } from "@pgege/kaboo-runtime";
const runner = createKabooRunner(
new PostgresThreadStore({ dsn: process.env.DATABASE_URL }),
);
Postgres schema¶
The store creates these tables (and index) on first use — no migration step:
CREATE TABLE IF NOT EXISTS kaboo_threads (
id text PRIMARY KEY,
agent_id text NOT NULL,
created_at bigint NOT NULL,
updated_at bigint NOT NULL
);
CREATE TABLE IF NOT EXISTS kaboo_thread_events (
seq bigserial PRIMARY KEY,
thread_id text NOT NULL,
event jsonb NOT NULL
);
CREATE INDEX IF NOT EXISTS kaboo_thread_events_thread_idx
ON kaboo_thread_events (thread_id, seq);
CREATE TABLE IF NOT EXISTS kaboo_thread_messages (
thread_id text PRIMARY KEY,
messages jsonb NOT NULL,
updated_at bigint NOT NULL
);
dsn vs pool¶
Pass a dsn connection string and the store creates and owns its own pg.Pool.
To share an existing pool (e.g. your app already has one), pass pool instead:
import { Pool } from "pg";
import { PostgresThreadStore } from "@pgege/kaboo-runtime";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const store = new PostgresThreadStore({ pool });
Passing neither dsn nor pool throws.
Choosing a store¶
- Dev / tests / demo:
InMemoryThreadStore. - Production / durable:
PostgresThreadStore. - Anything else: a custom store.