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;
}
OptionDefaultBehavior
adapterMemoryStorePersistence backend for SDK state.
statenoneStandard Schema that types and validates thread.state() and setState().
transcriptsnoneOptional retention and per-user cap, keyed by the application user from top-level identifyUser.
actionRetentionMs604_800_000Retention 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.
lockTtl60_000Per-conversation turn-lock TTL in milliseconds (used by drop / legacy lock paths).
dedupTtl300_000Inbound event deduplication window in milliseconds.

Turn concurrency

ModeBehavior
"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.