
Description
LockStore, InMemoryLockStore, LocksCapability and withLocks for multi-instance coordination in TanStack AI. Ships in @tanstack/ai — NOT in @tanstack/ai-persistence. Separate from AIPersistence state stores — not a stores key, not composable. InMemoryLockStore vs a distributed (e.g. Cloudflare Durable Object) lock, lease recovery, AbortSignal in critical sections. Use when sandbox or other middleware needs cross-worker mutual exclusion — NOT for storing messages/runs (use withPersistence).
SKILL.md
Locks (coordination — not persistence)
Dependency note: This skill builds on ai-core and ai-core/middleware.
withLocksis a ChatMiddleware that provides a capability. Locks are not part ofAIPersistence.storesand are not composed withcomposePersistence— they ship in@tanstack/ai, independent of@tanstack/ai-persistence.
Why separate?
State stores answer "what is durable chat data?"
Locks answer "who may run this critical section right now?"
withPersistence does not automatically lock a whole turn. Take a
per-thread (or other) lock yourself when multi-writer races matter.
Wire locks
import { withLocks, InMemoryLockStore } from '@tanstack/ai/locks'
middleware: [
withLocks(new InMemoryLockStore()), // single process
]
Alongside persistence — optional, locks do not require it:
import { withLocks, InMemoryLockStore } from '@tanstack/ai/locks'
import { withPersistence } from '@tanstack/ai-persistence'
middleware: [withPersistence(persistence), withLocks(new InMemoryLockStore())]
withLocks provides LocksCapability for downstream middleware (e.g.
sandbox). Order: usually state first, locks alongside or after depending on
who consumes the capability.
The contract
interface LockStore {
withLock<T>(key: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T>
}
InMemoryLockStore ships in @tanstack/ai/locks: a per-key promise chain,
correct within a single process only. Multi-instance deployments need a
distributed implementation — you write it. The Cloudflare Durable Object recipe
is in ai-persistence/build-cloudflare-adapter (@tanstack/ai-persistence).
Type your own store with defineLock (autocomplete, no : LockStore
annotation), then hand it to withLocks. Acquire the key, run fn, release when
fn settles:
import { defineLock, withLocks } from '@tanstack/ai/locks'
import { acquire } from './my-lock-backend'
const locks = defineLock({
async withLock(key, fn) {
const { release, signal } = await acquire(key)
try {
return await fn(signal)
} finally {
release()
}
},
})
middleware: [withLocks(locks)]
Lease semantics
A good LockStore:
- Serializes owners per key,
- Uses leases (or equivalent) so a crashed owner cannot block forever,
- Passes an
AbortSignalinto the critical section viawithLock; when the lease is lost, abort so work stops starting external mutations.
Callbacks must honor the signal and pass it to cancellable dependencies.
InMemoryLockStore never aborts its signal — within one process, ownership
cannot be lost.
Capability identity
The 'locks' capability token lives in @tanstack/ai/locks. Capability identity
is by object reference, so one shared token means a withLocks in the chain
reaches withSandbox automatically.
Common mistakes
HIGH: Importing locks from @tanstack/ai-persistence
They are not exported there. Use @tanstack/ai.
HIGH: Putting locks on AIPersistence.stores
Not supported. stores accepts only messages, runs, interrupts,
metadata — never locks. Use withLocks.
HIGH: Passing locks to composePersistence overrides
Same rejection, at the override layer. Locks are not state.
HIGH: Passing 'locks' to the conformance testkit's skip
skip accepts only chat state store keys. The suite does not cover locks
at all — test lease expiry and abort separately.
HIGH: InMemoryLockStore across multiple processes
No mutual exclusion between machines — use a distributed lock store.
MEDIUM: Ignoring lease abort
Continuing work after losing the lease races other owners.
Cross-references
- See also: ai-core/middleware/SKILL.md -- the middleware chain and capability plumbing
- See also:
@tanstack/ai-persistenceskills (skills/ai-persistence/SKILL.mdin that package) --ai-persistence/server(state middleware) andai-persistence/build-cloudflare-adapter(Durable Object lock recipe)
More skills from the ai repository
View all 27 skillsai-code-mode
execute sandboxed TypeScript code with LLMs
Jul 16AI SDKCode ExecutionSandboxingTanStack +1ai-core
configure TanStack AI agent features
Jul 30AgentsAIMiddlewareai-core/adapter-configuration
configure AI provider adapters
Jul 16AI SDKConfigurationLLMTanStackai-core/ag-ui-protocol
implement TanStack AI streaming protocol
Jul 16API DevelopmentLLMTanStackai-core/chat-experience
implement chat experiences with TanStack AI
Jul 30API DevelopmentFrontendLLMTanStackai-core/client-persistence
implement browser chat persistence for TanStack AI
Jul 30AIFrontendPersistenceTanStack
More from TanStack
View publisheraggregation
perform data aggregation in TanStack Table
table
Jul 30Data AnalysisFrontendTanStackapi-not-found
diagnose TanStack Table API errors
table
Jul 30DebuggingFrontendTanStackcell-selection
select rectangular cell ranges in tables
table
Jul 30Data AnalysisTanStackUI Componentsclient-vs-server
manage TanStack Table data pipelines
table
Jul 30Data PipelineFrontendPerformanceTanStackcolumn-faceting
build faceted filter UIs
table
Jul 30Data VisualizationFrontendTanStackcolumn-filtering
implement column filtering in TanStack Table
table
Jul 30Data AnalysisFrontendTanStack