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.

466 lines (434 loc) 14.5 kB
// Capability tokens + accessors (sandbox-owned only). // LockStore / withLocks / defineLock: import from @tanstack/ai/locks. export { SandboxCapability, SandboxPolicyCapability, ToolBridgeProvisionerCapability, getSandbox, provideSandbox, getSandboxPolicy, provideSandboxPolicy, getToolBridgeProvisioner, provideToolBridgeProvisioner, } from './capabilities' // Durable instance map (resume-or-create across processes). // Pass a store to `withSandbox(sandbox, { instances })`; the capability is the // ambient alternative for platform-level wiring. export { SandboxInstanceStoreCapability, getSandboxInstanceStore, provideSandboxInstanceStore, InMemorySandboxInstanceStore, defineSandboxInstanceStore, } from './instance-store' export type { SandboxInstanceStore, SandboxInstanceRecord, } from './instance-store' // Portable immutable sandbox checkpoint metadata. export { SandboxCheckpointError, SandboxCheckpointConflictError, SandboxCheckpointDuplicateIdError, SandboxCheckpointInvalidIdError, SandboxCheckpointInvalidEntryError, SandboxCheckpointParentMismatchError, SandboxCheckpointNotHeadError, SandboxCheckpointWriterConflictError, SandboxCheckpointWriterLostError, isForkCapableSandboxCheckpointStore, InMemorySandboxCheckpointStore, defineSandboxCheckpointStore, } from './checkpoint-store' export type { SandboxCheckpoint, SandboxCheckpointStore, SandboxSnapshotEntry, SandboxSnapshotFileEntry, SandboxSnapshotDirectoryEntry, SandboxSnapshotArtifact, SandboxCheckpointErrorCode, SandboxCheckpointWriter, SandboxCheckpointWriterLease, SandboxCheckpointStoreOptions, SandboxCheckpointForkInput, SandboxCheckpointForkCapability, ForkCapableSandboxCheckpointStore, } from './checkpoint-store' // File snapshot policy used by provider snapshot create/restore inputs. export { SandboxSnapshotError, defaultSandboxSnapshotPolicy } from './snapshots' export type { SandboxSnapshotErrorCode, SandboxSnapshotPolicy, } from './snapshots' export { memorySandboxSnapshots } from './memory-snapshots' export type { MemorySandboxSnapshots, MemorySandboxSnapshotsOptions, } from './memory-snapshots' export { createSandboxSnapshots } from './snapshot-operations' export type { CreateSandboxSnapshotsInput, ForkSandboxSnapshotInput, ReadSandboxSnapshotArtifactInput, SandboxSnapshots, SaveSandboxSnapshotInput, SnapshotPersistence, } from './snapshot-operations' export { createSnapshotTools } from './snapshot-tools' export type { CreateSnapshotToolsOptions } from './snapshot-tools' // Workspace projection capability (provided by withSandbox, consumed by harness adapters) export { ProjectionCapability, getWorkspaceProjection, provideWorkspaceProjection, } from './projection' export type { WorkspaceProjection } from './projection' // Middleware export { withSandbox } from './middleware' export type { SandboxMiddlewareOptions } from './middleware' // Harness tool history: `withSandbox` records the tool calls a harness ran INSIDE the // sandbox into the transcript, so a finished run restores its tool cards. This is how // an app recognises them — e.g. in its own `MessageStore.saveThread`, to cap or drop // what it does not want to store. export { isSandboxToolCall } from './tool-history' // Sandbox definition + lifecycle export { defineSandbox } from './sandbox' export type { SandboxConfig, SandboxDefinition, SandboxEnsureContext, SandboxLifecycle, SandboxHooks, ReuseStrategy, SnapshotStrategy, } from './sandbox' // Workspace export { defineWorkspace, gitSource, githubRepo, localSource, fileSkill, agentSkill, mcpSkill, gitSkill, } from './workspace' export type { WorkspaceDefinition, WorkspaceSource, WorkspaceSkill, PackageManager, McpConfig, } from './workspace' // Secrets export { createSecrets, bearer, isSecretRef, resolveSecret, resolveBearer, resolveAllSecrets, } from './secrets' export type { SecretRef, Secrets, BearerRef } from './secrets' // Policy export { defineSandboxPolicy, evaluateCommand, commandAliases } from './policy' export type { SandboxPolicy, PolicyDecision, CommandRules, CapabilityRules, } from './policy' // Provider + handle contracts export type { SandboxProvider, SandboxHandle, SandboxCapabilities, SandboxFs, SandboxFsStat, SandboxGit, SandboxProcess, SandboxPorts, SandboxEnv, SandboxChannel, SpawnHandle, ExecResult, ProcessOptions, SnapshotRef, SandboxCreateInput, SandboxResumeInput, SandboxRestoreInput, SandboxDestroyInput, } from './contracts' // Bootstrap engine (exported for provider/adapter authors + tests) export { bootstrapWorkspace, detectPackageManager, DEFAULT_WORKSPACE_ROOT, } from './bootstrap' export { resolveHarnessCwd } from './harness-cwd' export type { BootstrapResult } from './bootstrap' // AGENTS.md writer + gitSkill path helper (used by bootstrap + harness adapters) export { writeAgentsFile, resolveGitSkillDir, discoverSkillDirs, formatWorkspaceScriptsSection, mergeAgentsContent, } from './agents-file' export type { DiscoveredSkillDir } from './agents-file' // Exec-backed git helper (for providers without native git) export { createExecBackedGit } from './git-exec' // Harness runner: spawn an agent CLI in a sandbox + stream NDJSON stdout export { spawnNdjson, toLines, startJournaledAgent, readJournalNdjson, } from './runner' export type { SpawnNdjsonOptions, JournalOptions } from './runner' // The agent output journal: the durability boundary for a sandboxed run. // // journalListCommand/journalMtimeListCommand/parseJournalMtimeListing back the // journal-directory sweep (`journal-sweep.ts`); journalExitProbeCommand/ // parseJournalExit/parseExitSentinel back the reaper's out-of-band exit probe // (`reap.ts`) and the streaming reader, which must agree on what "the run // ended" means; EXIT_SENTINEL_NONCE_KEY/exitSentinelLine are what make that // sentinel unforgeable by the agent's own stdout and are needed by anything // that seeds a journal by hand (a fake host, a test); the // mechanism that lets a sweep learn a detached run finished WITHOUT driving // it; decodeJournalRunId recovers the runId a listed filename encodes, fail // closed. `encodeRunId` is exported for adapters that derive their OWN // in-sandbox paths from a caller-supplied `runId` (prompt files, MCP bridge // configs) and must not hand-roll a second, divergent encoder. // `normalizeJournalDir` is intentionally NOT exported: it is a path // formatting detail of this module's own commands, not something a caller // composes with. export { DEFAULT_JOURNAL_DIR, encodeRunId, EXIT_SENTINEL_KEY, EXIT_SENTINEL_NONCE_KEY, exitSentinelLine, parseExitSentinel, journalPaths, journaledCommand, journalFollowCommand, journalReadCommand, journalExistsCommand, journalListCommand, journalMtimeListCommand, parseJournalMtimeListing, journalExitProbeCommand, parseJournalExit, decodeJournalRunId, } from './journal' export type { JournalPaths, JournalMtimeListing, JournalDirEntry, DecodedJournalRunId, } from './journal' // Journal-directory sweep: bound the journals a detached run's sentinel never // got OBSERVED for (see `journal-sweep.ts`'s module doc for why almost every // branch keeps rather than deletes — deleting a live run's journal makes it // unresumable, with no undo). export { pruneJournals, DEFAULT_ORPHAN_TTL_MS, DEFAULT_MAX_DELETES, } from './journal-sweep' export type { PruneJournalsOptions, PruneJournalsResult, KeptJournal, KeptJournalReason, PruneJournalsFailure, } from './journal-sweep' // Detached-run reaper: sweep a `RunStore`'s reclaimable runs, driving a run to // terminal ONLY once the out-of-band journal probe (`probeRunExit`) already // knows the agent exited, or once its TTL has expired — never to find out // whether it finished (see `reap.ts`'s module doc for why that design was // rejected). export { reapDetachedRuns, probeRunExit, DEFAULT_RUN_BUDGET_MS, DEFAULT_MAX_RUNS, DEFAULT_EXIT_PROBE_BYTES, } from './reap' export type { RunExitProbe, ReapRunOutcome, ReapRunEntry, ReapResult, ReapOptions, } from './reap' // Sandbox reclaim: tear down the sandbox behind a terminal run. // `sandboxReclaimer` adapts `reclaimSandbox` to `ReapOptions.reclaim`. export { reclaimSandbox, sandboxReclaimer, SandboxReclaimFailedError, } from './reclaim' export type { ReclaimOutcome, ReclaimSandboxOptions } from './reclaim' export { DEFAULT_JOURNAL_POLL_MS, journalReadStrategy, readJournal, } from './journal-reader' export type { ReadJournalOptions } from './journal-reader' export { decodeBase64Stream, toJournalLines } from './journal-bytes' export type { JournalLine } from './journal-bytes' export { createRunScopedIdGen, chunkFingerprint, chunkFingerprintIgnoringThreadId, chunkThreadId, } from './chunk-identity' export { alignToStoredLog, isBridgeCustomChunk, JournalReplayDivergedError, JournalReplayThreadIdMismatchError, DEFAULT_MAX_OUT_OF_BAND_SKIP, } from './align' export type { AlignToStoredLogOptions } from './align' // Attach preflight: the gate that makes a hopeless attach fail instead of // tailing an empty journal forever. `JournalAttachUnavailableError` and its // `reason` are the branchable surface (404 / 410 / 504 at an attach route), and // the bounded-wait default is exported because it bounds an attach REQUEST. export { awaitAttachableJournal, JournalAttachUnavailableError, DEFAULT_ATTACH_JOURNAL_WAIT_MS, DEFAULT_ATTACH_PROBE_INTERVAL_MS, } from './attach-preflight' export type { AttachUnavailableReason, AwaitAttachableJournalOptions, } from './attach-preflight' // Durability seam: the `withSandbox(sandbox, { runs, durability })` option // shape, the capability harness adapters read back via `getSandboxDurability`, // and the two helpers that turn a resolved durability into the pieces a // harness adapter actually drives with — a `journalOptionsFor` journal option // and an attach-only `alignedIfAttaching` alignment transform. // // `resolveSandboxDurability` is deliberately NOT exported: it is // `withSandbox`'s own path from raw options to the capability payload (see // `middleware.ts`), and a harness adapter only ever needs the ALREADY-RESOLVED // value read back off the capability bus, never to re-run that resolution // itself. export { SandboxDurabilityCapability, getSandboxDurability, provideSandboxDurability, DurableAttachNotSupportedError, DurableRunIdRequiredError, DurableThreadIdRequiredError, resolveDurableRunId, resolveDurableThreadId, journalOptionsFor, alignedIfAttaching, } from './durability' export type { SandboxDurabilityOptions, SandboxDurabilityLog, SandboxRunDurability, } from './durability' // Run driver: fills in core's injected takeover seams (`claim`/`pipe`) with // this package's single-writer claim (`claim.ts`) and run log (`run.ts`), so // an application wires `request`/`runs`/`locks`/`durability`/`drive` instead of // hand-rolling the claim/fence dance itself. // // `claim.ts`'s own primitives — `withRunClaim`, `fenceDurability`, // `awaitLogQuiescence`, `runDriverLockKey`, and their `RunClaim` / // `WithRunClaimOptions` types — are deliberately NOT exported. They are // exactly the "easy to get wrong" seam `sandboxRunDriver` exists to make // impossible (see `driver.ts`'s module doc, points 1-3), and publishing them // would invite the same hand-rolled fencing bugs as a supported path. The two // error classes below ARE exported despite that: both can surface through // `sandboxRunDriver` itself, so a caller needs `instanceof` to branch on them, // and `DEFAULT_FENCE_QUIET_MS` is exported because it is the documented // default for `sandboxRunDriver`'s own `fenceQuietMs` option. export { sandboxRunDriver, RunDriverPipeOutsideClaimError } from './driver' export type { SandboxRunDriverOptions } from './driver' export { RunClaimNotAcquiredError, RunClaimLostError, DEFAULT_FENCE_QUIET_MS, } from './claim' // MCP tool-proxy bridge (shared by harness adapters): transport-agnostic core // + the node:http host transport + a fetch-friendly JSON-RPC dispatcher. export { startHostToolBridge, hostForSandbox, createToolBridgeCore, handleBridgeJsonRpc, timingSafeBearerEqual, nodeHttpBridgeProvisioner, BRIDGED_MCP_SERVER_NAME, } from './tool-bridge' export type { HostToolBridge, StartBridgeOptions, ToolBridgeCore, ToolBridgeCoreOptions, ToolDescriptor, ToolCallResult, BridgePermission, PermissionToolResult, ToolBridgeProvisioner, ToolBridgeProvisionOptions, ProvisionedBridge, } from './tool-bridge' // Surface bridged-tool custom events (e.g. code mode console logs) on a harness // adapter's live output stream. export { createBridgeEventChannel, mergeChunkStreams } from './bridge-events' export type { BridgeEventChannel } from './bridge-events' // Host-tool delegation for the co-located ("combined") model: harness + bridge // run in-container; only chat()-tool EXECUTION crosses back to the orchestrator. export { remoteToolStubs, toolDescriptors, httpRemoteToolExecutor, executeHostTool, isToolExecRequest, } from './remote-tools' export type { RemoteToolExecutor, RemoteToolExecuteOptions, ToolExecRequest, } from './remote-tools' // Run driver — pumps a chat() stream into core's `StreamDurability` and // records run status/lifecycle in core's `RunStore`, so a trigger returns // immediately while a durable orchestrator drives the run and clients tail it. export { pipeToRunLog, RunController } from './run' export type { RunDeps, PipeToRunLogOptions, RunControllerStartInput, RunHandle, } from './run' // Interactive approvals (shared by harness adapters) export { resolveApproval, approvalId, buildApprovalRequestedEvent, APPROVAL_REQUESTED_EVENT, } from './approvals' export type { ResolveApprovalInput, ApprovalOutcome } from './approvals' // File-event watch (low-level workspace observer) export { watchWorkspace, diffSnapshots } from './watch' export type { SandboxFileEvent, FileEvent, FileEventType, WatchOptions, SandboxWatchHandle, } from './watch' // Keying export { computeSandboxKey, computeWorkspaceHash } from './key' export type { SandboxKeyInput } from './key' // Errors export { UnsupportedCapabilityError, MissingSandboxError } from './errors'