Defined in: packages/ai/src/activities/chat/middleware/run-store.ts:197
Durable store for run lifecycle records.
REQUIRED: createOrResume, update, get, findActiveRun. Every backend must implement all four — they are what the persistence middleware calls unconditionally. findActiveRun is required rather than feature-detected because a backend that has not implemented it is indistinguishable from one whose answer is legitimately null, so reconnect would silently do nothing instead of failing at build time. It was optional for exactly one release cycle and cost precisely that.
OPTIONAL: listByThread, listByParentRun, listReclaimable. Each serves one higher-level feature (thread history, subagent card reload, reclaim reaping) and callers feature-detect them, degrading when a backend omits them.
createOrResume: (input) => Promise<RunRecord>;Defined in: packages/ai/src/activities/chat/middleware/run-store.ts:208
Create a run record, or return the existing one unchanged if runId is already present.
INVARIANT (idempotency): an existing record is returned unchanged and the passed threadId, startedAt, status, parentRunId, subagentRunId, and name are ignored. This is what makes resuming a run safe. status defaults to 'running' on first creation. The three link fields are copied only on the first insert.
Pick<RunRecord, "threadId" | "runId" | "startedAt"> & object
Promise<RunRecord>
findActiveRun: (threadId) => Promise<RunRecord | null>;Defined in: packages/ai/src/activities/chat/middleware/run-store.ts:287
The most recent 'running' run for threadId, or null if none is active.
REQUIRED. This resolves "does this thread have a live run to attach to?" from the STABLE thread id, which is the durable basis for reconnecting a client (a reload, or the same thread opened on another device) — independent of the ephemeral run id, which a single turn may mint several of. When more than one run is 'running', the one with the greatest startedAt wins.
A backend that stubs this to null turns reconnect off silently, because null is also the correct answer for an idle thread. A backend with no run lifecycle at all should omit the whole runs store instead — capability tiers belong at the store level, not the method level.
string
Promise<RunRecord | null>
get: (runId) => Promise<RunRecord | null>;Defined in: packages/ai/src/activities/chat/middleware/run-store.ts:239
Current record, or null when unknown.
string
Promise<RunRecord | null>
optional listByParentRun?: (parentRunId) => Promise<RunRecord[]>;Defined in: packages/ai/src/activities/chat/middleware/run-store.ts:253
Child runs started by parentRunId, ascending by startedAt. OPTIONAL. reconstructChat uses this to put subagent cards back on the parent assistant message. A store that omits it reloads the text and not the cards.
string
Promise<RunRecord[]>
optional listByThread?: (threadId) => Promise<RunRecord[]>;Defined in: packages/ai/src/activities/chat/middleware/run-store.ts:246
Every run in a conversation, ascending by startedAt. OPTIONAL. reconstructChat calls it to find the parent runs of children that a tool call started. Without it those cards stay absent on reload. Consumers feature-detect.
string
Promise<RunRecord[]>
optional listReclaimable?: (opts) => Promise<RunRecord[]>;Defined in: packages/ai/src/activities/chat/middleware/run-store.ts:269
Runs that may be reclaimed: ALL THREE of status === 'running', detachedSince is set, and detachedSince <= now - ttlMs. The cutoff is inclusive — a run detached at exactly now - ttlMs IS reclaimable.
OPTIONAL: only needed by a reaper. Consumers feature-detect.
detachedSince is populated by withSandbox's detach path (see RunRecord.detachedSince). The sweep over the candidates this surfaces is @tanstack/ai-sandbox's reapDetachedRuns: it finalizes a run whose agent already finished, expires one past its TTL, and reclaims the sandbox. That is a function, not a scheduler — the application invokes it (cron, queue, alarm(), waitUntil) — and a backend that omits this method cannot be reaped at all.
number
number
Promise<RunRecord[]>
update: (runId, patch) => Promise<void>;Defined in: packages/ai/src/activities/chat/middleware/run-store.ts:222
Patch a record's mutable fields.
INVARIANT: updating an unknown runId is a no-op — it must not throw and must not create a record.
string
Partial<Pick<RunRecord, | "status" | "finishedAt" | "error" | "usage" | "sandboxKey" | "detachedSince" | "cancelRequested" | "driverEpoch">>
Promise<void>