StateStore
The pluggable persistence interface — five primitive groups, the built-in MemoryStore default, 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. Implement the StateStore interface yourself to back it with a durable store (Redis, Postgres, etc.) for persistence 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/custom backends, 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/channels";
// 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.
Custom backends
To use a durable store (Redis, Postgres, or any other persistence layer), implement the StateStore interface and pass your instance as store.adapter. This gives you full control over the persistence strategy without depending on a specific adapter package.
Custom Backend Conformance
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/channels/testing";
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 — configuring a backend (in-memory default; bring your own durable backend)
- Transcripts guide — cross-platform conversation history built on StateStore
- ActionStore — the older action-snapshot interface (deprecated)