StateStore
The pluggable persistence interface — five primitive groups, three shipped backends, and a conformance suite for custom implementations.
Overview
StateStore is the persistence contract that backs the entire bot runtime: action snapshots, per-thread state, cross-platform transcripts, turn locks, inbound-event deduplication, and optional queuing. You choose a backend by passing it as store.adapter to createBot.
The default is MemoryStore — built-in, zero-config, but lost on restart. Swap in createRedisStore or createPostgresStore for durability across restarts and horizontal scale.
For a full guide on configuring persistence, see Persistence. For cross-platform transcript storage built on top of StateStore, see Transcripts.
Interface
interface StateStore {
kv: {
get<T>(key: string): Promise<T | undefined>;
set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
delete(key: string): Promise<void>;
};
list: {
append<T>(key: string, value: T, opts?: { maxLen?: number; ttlMs?: number }): Promise<number>;
range<T>(key: string, start?: number, stop?: number): Promise<T[]>;
trim(key: string, maxLen: number): Promise<void>;
delete(key: string): Promise<void>;
};
lock: {
acquire(key: string, opts?: { ttlMs?: number }): Promise<{ token: string } | null>;
release(key: string, token: string): Promise<void>;
};
dedup: {
seen(key: string, ttlMs: number): Promise<boolean>;
};
queue: {
enqueue<T>(key: string, value: T, opts?: { maxSize?: number; onFull?: "drop-oldest" | "drop-newest" }): Promise<number>;
dequeue<T>(key: string): Promise<T | undefined>;
depth(key: string): Promise<number>;
};
}JSON-serialization contract: all values round-trip through JSON.stringify / JSON.parse on remote backends (Redis, Postgres), so T must be JSON-serializable. Non-JSON values (Date, Map, class instances) will not survive on those backends. MemoryStore preserves them by reference — a backend divergence to be aware of when writing tests that run against both.
Primitive Groups
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Shipped Backends
MemoryStore (default)
import { MemoryStore } from "@copilotkit/bot";
// createBot uses MemoryStore by default; explicit construction:
const store = new MemoryStore();Zero-config in-memory implementation. All data is lost on process restart. Suitable for development, demos, or stateless deployments where no cross-turn state is needed.
Redis (@copilotkit/bot-store-redis)
import { createRedisStore } from "@copilotkit/bot-store-redis";
const store = createRedisStore({ url: process.env.REDIS_URL });
const bot = createBot({
adapters: [slack({ botToken, appToken })],
store: { adapter: store },
});Durable, multi-process. Uses a node-redis client; locks and bounded-enqueue use Lua scripts for atomicity. All values are JSON-encoded.
Prop
Type
Prop
Type
Prop
Type
Postgres (@copilotkit/bot-store-postgres)
import { createPostgresStore } from "@copilotkit/bot-store-postgres";
const store = createPostgresStore({
connectionString: process.env.DATABASE_URL,
autoMigrate: true,
});
const bot = createBot({
adapters: [slack({ botToken, appToken })],
store: { adapter: store },
});Durable, multi-process. Uses a node-postgres pool; backs kv/list/lock/dedup/queue on three tables (cpk_state_kv, cpk_state_list, cpk_state_queue). All values stored as JSONB. Locks are TTL'd rows (not pg advisory locks) so token/TTL fencing matches the Redis backend.
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Custom Backends
Implement all five primitive groups of the StateStore interface and pass your instance as store.adapter. Run the conformance suite to verify correctness:
import { runStateStoreConformance } from "@copilotkit/bot";
runStateStoreConformance(
"MyCustomStore",
() => new MyCustomStore(),
async (s) => s.close(), // optional teardown
);runStateStoreConformance(name, make, teardown?) registers a full Vitest describe block covering all five primitive groups. Your backend passes when all tests green. The teardown callback receives the store instance after each test for cleanup (disconnect, flush, etc.).
Related
- createBot — the
store.adapteroption - Thread —
setState,state,subscribe,unsubscribe,isSubscribed - Persistence guide — choosing and configuring a backend
- Transcripts guide — cross-platform conversation history built on StateStore
- ActionStore — the older action-snapshot interface (deprecated)