StoreConfig
Configure per-thread state, the StateStore backend, transcripts, and turn concurrency controls.
StoreConfig is the createChannel({ store }) value.
interface StoreConfig<TStateSchema> {
adapter?: StateStore;
state?: TStateSchema;
transcripts?: TranscriptsConfig;
actionRetentionMs?: number;
concurrency?: "parallel" | "serial" | "drop";
/** @deprecated Prefer `concurrency`. */
onLockConflict?:
| "drop"
| "force"
| ((
conversationKey: string,
message: IncomingMessage,
) => "drop" | "force" | Promise<"drop" | "force">);
lockTtl?: number;
dedupTtl?: number;
}| Option | Default | Behavior |
|---|---|---|
adapter | MemoryStore | Persistence backend for SDK state. |
state | none | Standard Schema that types and validates thread.state() and setState(). |
transcripts | none | Optional retention and per-user cap, keyed by the application user from top-level identifyUser. |
actionRetentionMs | 604_800_000 | Retention for durable callbacks and one-use HITL continuations. |
concurrency | "parallel" | How overlapping turns on the same conversation are handled (see below). |
onLockConflict | (maps into concurrency) | Deprecated. Prefer concurrency. Static "drop" / "force" map to "drop" / "parallel". A callback keeps the legacy exclusive-lock path. |
lockTtl | 60_000 | Per-conversation turn-lock TTL in milliseconds (used by drop / legacy lock paths). |
dedupTtl | 300_000 | Inbound event deduplication window in milliseconds. |
Turn concurrency
| Mode | Behavior |
|---|---|
"parallel" (default) | Overlapping turns on the same conversation run together. Multiple people asking questions in one Slack thread each get a concurrent reply. |
"serial" | Later turns on the same conversation wait until the in-flight turn finishes (per-conversation queue). |
"drop" | Later turns are discarded while a turn is already in flight. |
A configured singleton agent (agent: sharedInstance) is isolated per run via agent.clone() under the hood, so parallel mode does not mutate one shared agent object. Prefer an agent factory when you want explicit control:
agent: (threadId) => {
const a = new HttpAgent({ url });
a.threadId = threadId;
return a;
};const stateSchema = z.object({
stage: z.enum(["draft", "review", "done"]),
});
const channel = createChannel({
name: "support-slack",
identifyUser: "platform",
agent: makeAgent,
store: {
adapter: durableStateStore,
state: stateSchema,
// Opt into one-at-a-time processing for stateful workflows:
// concurrency: "serial",
},
});thread.setState(value) replaces the entire stored value and validates it
against state. It does not merge patches. Under "parallel", concurrent
turns that both call setState can race — use "serial" for workflows that
depend on exclusive state updates.
On the managed path, Intelligence owns cross-instance delivery claims and
ordered provider effects. Managed admission also keeps one canonical Thread
delivery active at a time on a Runtime, so "parallel" does not raise that
delivery limit. The SDK store still owns application state and callback
snapshots.