UNPKG

@tanstack/ai-sandbox

Version:

Provider-agnostic sandbox layer for TanStack AI — run harness adapters inside isolated sandboxes (defineSandbox, defineWorkspace, withSandbox) with a uniform SandboxHandle, workspace bootstrap, policy, and resumable lifecycle.

86 lines (85 loc) 3.93 kB
import { SandboxCapability } from './capabilities.js'; import { ProjectionCapability } from './projection.js'; import { SandboxSnapshotPolicy } from './snapshots.js'; import { SandboxCheckpointStore } from './checkpoint-store.js'; import { LockStore } from '@tanstack/ai/locks'; import { DefinedChatMiddleware, ModelMessage, RunStore } from '@tanstack/ai'; import { SandboxDurabilityOptions } from './durability.js'; import { SandboxInstanceStore } from './instance-store.js'; import { SandboxDefinition } from './sandbox.js'; /** * Durability seams for a sandboxed run. Both are optional; each independently * falls back to a process-lifetime in-memory default, which is correct for a * single process but NOT across replicas. */ export interface SandboxMiddlewareOptions<TOffset extends string = string> { snapshots?: { persistence: { stores: { messages: { loadThread: (threadId: string) => Promise<ReadonlyArray<ModelMessage>>; }; artifacts: { listForThread: (threadId: string) => Promise<ReadonlyArray<{ artifactId: string; runId: string; threadId: string; blobKey?: string; name: string; mimeType: string; size: number; createdAt: number; }>>; }; blobs: { get: (key: string) => Promise<{ arrayBuffer: () => Promise<ArrayBuffer>; } | null>; head: (key: string) => Promise<unknown>; put: (key: string, body: Uint8Array) => Promise<unknown>; }; }; }; checkpoints: SandboxCheckpointStore; policy?: SandboxSnapshotPolicy; }; /** * Durable instance map (which provider sandbox to resume for a key). Pass * your own store to make resume survive across processes/replicas. * * Takes precedence over a store provided on the capability bus (see * `provideSandboxInstanceStore`), so the call site wins over ambient wiring. */ instances?: SandboxInstanceStore; /** * Distributed lock serializing resume-or-create for one key. Needed for * multi-replica correctness so two concurrent runs don't both create. * * Prefer `withLocks` from `@tanstack/ai/locks` when other middleware also * needs the lock; use this option to scope one to this sandbox. Takes * precedence over a bus-provided lock. */ locks?: LockStore; /** * Run lifecycle records. Pair with `durability.adapter` to make a run * DETACHABLE: a client disconnect then leaves the agent running and records * `detachedSince` instead of destroying the sandbox. * * Pass the SAME store chat persistence uses (`persistence.stores.runs`) so * one record describes the run instead of two that can disagree. * * Defaults to `undefined`: an app that passes neither this nor `durability` * keeps today's destroy-on-disconnect behavior exactly. */ runs?: RunStore; /** * Delivery durability for the run's event log, plus the journal and detach * knobs. Requires `runs`; either alone is not durable. * * `TOffset` is inferred from the adapter passed here, so a branded-cursor * backend (`durableStream`) wires without a cast and without the call site * ever naming the parameter. */ durability?: SandboxDurabilityOptions<TOffset>; } export declare function withSandbox<TOffset extends string = string>(definition: SandboxDefinition, options?: SandboxMiddlewareOptions<TOffset>): DefinedChatMiddleware<unknown, readonly [], readonly [typeof SandboxCapability, typeof ProjectionCapability]>;