@tanstack/ai-sandbox 0.5.11 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/bridge-events.d.ts +2 -2
- package/dist/esm/bridge-events.js +3 -2
- package/dist/esm/bridge-events.js.map +1 -1
- package/dist/esm/memory-snapshots.js +1 -0
- package/dist/esm/memory-snapshots.js.map +1 -1
- package/dist/esm/tool-bridge.d.ts +2 -2
- package/dist/esm/tool-bridge.js.map +1 -1
- package/package.json +6 -6
- package/src/bridge-events.ts +8 -3
- package/src/memory-snapshots.ts +7 -0
- package/src/tool-bridge.ts +6 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { StreamChunk } from '@tanstack/ai';
|
|
1
|
+
import { EmitCustomEventOptions, StreamChunk } from '@tanstack/ai';
|
|
2
2
|
export interface BridgeEventChannel {
|
|
3
3
|
/** Pass as the bridge's `emitCustomEvent`; buffers a CUSTOM chunk for the stream. */
|
|
4
|
-
emitCustomEvent: (eventName: string, value: Record<string, unknown
|
|
4
|
+
emitCustomEvent: (eventName: string, value: Record<string, unknown>, options?: EmitCustomEventOptions) => void;
|
|
5
5
|
/** Live CUSTOM-chunk stream; ends after {@link close} once drained. */
|
|
6
6
|
stream: AsyncIterable<StreamChunk>;
|
|
7
7
|
/** Stop the stream (call when the run's main output is done). */
|
|
@@ -32,7 +32,7 @@ function createBridgeEventChannel(meta) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
return {
|
|
35
|
-
emitCustomEvent(eventName, value) {
|
|
35
|
+
emitCustomEvent(eventName, value, options) {
|
|
36
36
|
if (closed) return;
|
|
37
37
|
buffer.push(withTanstackMetadata({
|
|
38
38
|
type: EventType.CUSTOM,
|
|
@@ -42,7 +42,8 @@ function createBridgeEventChannel(meta) {
|
|
|
42
42
|
}, {
|
|
43
43
|
model: meta.model,
|
|
44
44
|
...meta.threadId !== void 0 ? { threadId: meta.threadId } : {},
|
|
45
|
-
...meta.runId !== void 0 ? { runId: meta.runId } : {}
|
|
45
|
+
...meta.runId !== void 0 ? { runId: meta.runId } : {},
|
|
46
|
+
...options?.batch === true ? { batch: true } : {}
|
|
46
47
|
}));
|
|
47
48
|
notify?.();
|
|
48
49
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-events.js","names":[],"sources":["../../src/bridge-events.ts"],"sourcesContent":["/**\n * Helpers that let a harness adapter surface custom events emitted by BRIDGED\n * tools (via {@link ToolBridgeCoreOptions.emitCustomEvent}) on its live output\n * stream.\n *\n * The bridge runs out-of-band from the harness's own event stream, so a bridged\n * tool's progress/console events have no path to the client on their own. An\n * adapter creates a {@link BridgeEventChannel}, hands its `emitCustomEvent` to\n * the bridge provisioner, and {@link mergeChunkStreams | merges} the channel's\n * stream into its translated output — so events interleave live while the agent\n * runs (e.g. code mode's `code_mode:console` logs during a long execution).\n */\nimport { EventType, withTanstackMetadata } from '@tanstack/ai'\nimport type { StreamChunk } from '@tanstack/ai'\n\nexport interface BridgeEventChannel {\n /** Pass as the bridge's `emitCustomEvent`; buffers a CUSTOM chunk for the stream. */\n emitCustomEvent: (eventName: string
|
|
1
|
+
{"version":3,"file":"bridge-events.js","names":[],"sources":["../../src/bridge-events.ts"],"sourcesContent":["/**\n * Helpers that let a harness adapter surface custom events emitted by BRIDGED\n * tools (via {@link ToolBridgeCoreOptions.emitCustomEvent}) on its live output\n * stream.\n *\n * The bridge runs out-of-band from the harness's own event stream, so a bridged\n * tool's progress/console events have no path to the client on their own. An\n * adapter creates a {@link BridgeEventChannel}, hands its `emitCustomEvent` to\n * the bridge provisioner, and {@link mergeChunkStreams | merges} the channel's\n * stream into its translated output — so events interleave live while the agent\n * runs (e.g. code mode's `code_mode:console` logs during a long execution).\n */\nimport { EventType, withTanstackMetadata } from '@tanstack/ai'\nimport type { EmitCustomEventOptions, StreamChunk } from '@tanstack/ai'\n\nexport interface BridgeEventChannel {\n /** Pass as the bridge's `emitCustomEvent`; buffers a CUSTOM chunk for the stream. */\n emitCustomEvent: (\n eventName: string,\n value: Record<string, unknown>,\n options?: EmitCustomEventOptions,\n ) => void\n /** Live CUSTOM-chunk stream; ends after {@link close} once drained. */\n stream: AsyncIterable<StreamChunk>\n /** Stop the stream (call when the run's main output is done). */\n close: () => void\n}\n\n/** Create a channel whose emitted events become CUSTOM {@link StreamChunk}s. */\nexport function createBridgeEventChannel(meta: {\n model: string\n threadId?: string\n runId?: string\n}): BridgeEventChannel {\n const buffer: Array<StreamChunk> = []\n let notify: (() => void) | null = null\n let closed = false\n\n async function* stream(): AsyncIterable<StreamChunk> {\n for (;;) {\n const next = buffer.shift()\n if (next !== undefined) {\n yield next\n continue\n }\n if (closed) return\n await new Promise<void>((resolve) => {\n notify = resolve\n })\n notify = null\n }\n }\n\n return {\n emitCustomEvent(eventName, value, options) {\n if (closed) return\n buffer.push(\n withTanstackMetadata(\n {\n type: EventType.CUSTOM,\n name: eventName,\n value,\n timestamp: Date.now(),\n },\n {\n model: meta.model,\n ...(meta.threadId !== undefined ? { threadId: meta.threadId } : {}),\n ...(meta.runId !== undefined ? { runId: meta.runId } : {}),\n ...(options?.batch === true ? { batch: true } : {}),\n },\n ) as StreamChunk,\n )\n notify?.()\n },\n close() {\n closed = true\n notify?.()\n },\n stream: stream(),\n }\n}\n\n/**\n * Merge a `side` chunk stream into a `base` chunk stream, yielding from whichever\n * settles first. Terminates when `base` ends (the run is over), then releases the\n * side iterator — so a never-ending channel (until closed) doesn't hang the merge.\n */\nexport async function* mergeChunkStreams(\n base: AsyncIterable<StreamChunk>,\n side: AsyncIterable<StreamChunk>,\n): AsyncIterable<StreamChunk> {\n const baseIt = base[Symbol.asyncIterator]()\n const sideIt = side[Symbol.asyncIterator]()\n let baseNext = baseIt.next().then((r) => ({ from: 'base' as const, r }))\n let sideNext = sideIt.next().then((r) => ({ from: 'side' as const, r }))\n let sideLive = true\n try {\n for (;;) {\n const winner = await Promise.race(\n sideLive ? [baseNext, sideNext] : [baseNext],\n )\n if (winner.from === 'base') {\n if (winner.r.done) return\n yield winner.r.value\n baseNext = baseIt.next().then((r) => ({ from: 'base' as const, r }))\n } else if (winner.r.done) {\n sideLive = false\n } else {\n yield winner.r.value\n sideNext = sideIt.next().then((r) => ({ from: 'side' as const, r }))\n }\n }\n } finally {\n // Fire-and-forget: do NOT await the side return. The channel generator is\n // suspended on a promise that only `close()` resolves, and `close()` runs in\n // the adapter's `finally` AFTER this merge completes — awaiting here would\n // deadlock. The adapter's `close()` lets the generator unwind afterwards.\n const baseReturn = baseIt.return?.(undefined)\n if (baseReturn) void baseReturn.catch(() => {})\n const sideReturn = sideIt.return?.(undefined)\n if (sideReturn) void sideReturn.catch(() => {})\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AA6BA,SAAgB,yBAAyB,MAIlB;CACrB,MAAM,SAA6B,CAAC;CACpC,IAAI,SAA8B;CAClC,IAAI,SAAS;CAEb,gBAAgB,SAAqC;EACnD,SAAS;GACP,MAAM,OAAO,OAAO,MAAM;GAC1B,IAAI,SAAS,KAAA,GAAW;IACtB,MAAM;IACN;GACF;GACA,IAAI,QAAQ;GACZ,MAAM,IAAI,SAAe,YAAY;IACnC,SAAS;GACX,CAAC;GACD,SAAS;EACX;CACF;CAEA,OAAO;EACL,gBAAgB,WAAW,OAAO,SAAS;GACzC,IAAI,QAAQ;GACZ,OAAO,KACL,qBACE;IACE,MAAM,UAAU;IAChB,MAAM;IACN;IACA,WAAW,KAAK,IAAI;GACtB,GACA;IACE,OAAO,KAAK;IACZ,GAAI,KAAK,aAAa,KAAA,IAAY,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;IACjE,GAAI,KAAK,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;IACxD,GAAI,SAAS,UAAU,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;GACnD,CACF,CACF;GACA,SAAS;EACX;EACA,QAAQ;GACN,SAAS;GACT,SAAS;EACX;EACA,QAAQ,OAAO;CACjB;AACF;;;;;;AAOA,gBAAuB,kBACrB,MACA,MAC4B;CAC5B,MAAM,SAAS,KAAK,OAAO,cAAc,CAAC;CAC1C,MAAM,SAAS,KAAK,OAAO,cAAc,CAAC;CAC1C,IAAI,WAAW,OAAO,KAAK,CAAC,CAAC,MAAM,OAAO;EAAE,MAAM;EAAiB;CAAE,EAAE;CACvE,IAAI,WAAW,OAAO,KAAK,CAAC,CAAC,MAAM,OAAO;EAAE,MAAM;EAAiB;CAAE,EAAE;CACvE,IAAI,WAAW;CACf,IAAI;EACF,SAAS;GACP,MAAM,SAAS,MAAM,QAAQ,KAC3B,WAAW,CAAC,UAAU,QAAQ,IAAI,CAAC,QAAQ,CAC7C;GACA,IAAI,OAAO,SAAS,QAAQ;IAC1B,IAAI,OAAO,EAAE,MAAM;IACnB,MAAM,OAAO,EAAE;IACf,WAAW,OAAO,KAAK,CAAC,CAAC,MAAM,OAAO;KAAE,MAAM;KAAiB;IAAE,EAAE;GACrE,OAAO,IAAI,OAAO,EAAE,MAClB,WAAW;QACN;IACL,MAAM,OAAO,EAAE;IACf,WAAW,OAAO,KAAK,CAAC,CAAC,MAAM,OAAO;KAAE,MAAM;KAAiB;IAAE,EAAE;GACrE;EACF;CACF,UAAU;EAKR,MAAM,aAAa,OAAO,SAAS,KAAA,CAAS;EAC5C,IAAI,YAAY,WAAgB,YAAY,CAAC,CAAC;EAC9C,MAAM,aAAa,OAAO,SAAS,KAAA,CAAS;EAC5C,IAAI,YAAY,WAAgB,YAAY,CAAC,CAAC;CAChD;AACF"}
|
|
@@ -338,6 +338,7 @@ async function createMemorySandboxSnapshots() {
|
|
|
338
338
|
get: async (runId) => runs.get(runId) ?? null,
|
|
339
339
|
findActiveRun: async (threadId) => [...runs.values()].filter((run) => run.threadId === threadId && run.status === "running").sort((a, b) => b.startedAt - a.startedAt)[0] ?? null,
|
|
340
340
|
listByThread: async (threadId) => [...runs.values()].filter((run) => run.threadId === threadId).sort((a, b) => a.startedAt - b.startedAt),
|
|
341
|
+
listByParentRun: async (parentRunId) => [...runs.values()].filter((run) => run.parentRunId === parentRunId).sort((a, b) => a.startedAt - b.startedAt),
|
|
341
342
|
listReclaimable: async (input) => [...runs.values()].filter((run) => run.status === "running" && run.detachedSince !== void 0 && run.detachedSince <= input.now - input.ttlMs)
|
|
342
343
|
},
|
|
343
344
|
generationRuns: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-snapshots.js","names":[],"sources":["../../src/memory-snapshots.ts"],"sourcesContent":["import type { ModelMessage } from '@tanstack/ai'\nimport type {\n MemoryArtifactRecord as ArtifactRecord,\n MemoryBlobBody as BlobBody,\n MemoryBlobRecord as BlobRecord,\n MemoryGenerationRunRecord as GenerationRunRecord,\n MemoryInterruptRecord as InterruptRecord,\n MemoryRunRecord as RunRecord,\n MemorySnapshotPersistence,\n} from './memory-snapshot-types'\nimport {\n SandboxCheckpointConflictError,\n SandboxCheckpointDuplicateIdError,\n SandboxCheckpointError,\n SandboxCheckpointInvalidEntryError,\n SandboxCheckpointInvalidIdError,\n SandboxCheckpointNotHeadError,\n SandboxCheckpointParentMismatchError,\n SandboxCheckpointWriterConflictError,\n SandboxCheckpointWriterLostError,\n} from './checkpoint-store'\nimport type {\n ForkCapableSandboxCheckpointStore,\n SandboxCheckpoint,\n SandboxCheckpointWriter,\n SandboxCheckpointWriterLease,\n SandboxCheckpointForkInput,\n} from './checkpoint-store'\nimport { createSandboxSnapshots } from './snapshot-operations'\nimport type {\n CreateSandboxSnapshotsInput,\n SandboxSnapshots,\n} from './snapshot-operations'\n\ntype BlobGetOptions = { range?: { offset: number; length?: number } }\n\nfunction resolveBlobRange(\n size: number,\n range: { offset: number; length?: number },\n): { offset: number; length: number } {\n if (\n !Number.isInteger(range.offset) ||\n range.offset < 0 ||\n range.offset >= size\n ) {\n throw new RangeError(\n `Blob range offset ${range.offset} is outside the object (size ${size}).`,\n )\n }\n const remaining = size - range.offset\n if (range.length === undefined) {\n return { offset: range.offset, length: remaining }\n }\n if (!Number.isInteger(range.length) || range.length < 0) {\n throw new RangeError(`Blob range length ${range.length} is not valid.`)\n }\n return {\n offset: range.offset,\n length: Math.min(range.length, remaining),\n }\n}\n\nexport type MemorySandboxSnapshots = SandboxSnapshots<\n MemorySnapshotPersistence,\n ForkCapableSandboxCheckpointStore\n>\n\nexport type MemorySandboxSnapshotsOptions = Omit<\n CreateSandboxSnapshotsInput<\n MemorySnapshotPersistence,\n ForkCapableSandboxCheckpointStore\n >,\n 'persistence' | 'checkpoints'\n>\n\nconst encoder = new TextEncoder()\nconst compare = (a: string, b: string) => {\n const left = encoder.encode(a)\n const right = encoder.encode(b)\n for (let i = 0; i < Math.min(left.length, right.length); i++) {\n const leftByte = left[i]\n const rightByte = right[i]\n if (leftByte !== rightByte) return (leftByte ?? 0) - (rightByte ?? 0)\n }\n return left.length - right.length\n}\nconst clone = <T>(value: T): T => structuredClone(value)\n\ninterface MemoryCheckpointState {\n checkpoints: Map<string, SandboxCheckpoint>\n heads: Map<string, string>\n writers: Map<string, { ownerToken: string; fence: number; expiresAt: number }>\n fences: Map<string, number>\n references: Map<string, number>\n}\n\ninterface MemorySnapshotState extends MemoryCheckpointState {\n messages: Map<string, Array<ModelMessage>>\n runs: Map<string, RunRecord>\n generations: Map<string, GenerationRunRecord>\n interrupts: Map<string, InterruptRecord>\n metadata: Map<string, Map<string, unknown>>\n artifacts: Map<string, ArtifactRecord>\n blobs: Map<string, { record: BlobRecord; bytes: Uint8Array }>\n}\n\nfunction hasUnpairedSurrogate(value: string): boolean {\n for (let index = 0; index < value.length; index++) {\n const code = value.charCodeAt(index)\n if (code >= 0xd800 && code <= 0xdbff) {\n const next = value.charCodeAt(index + 1)\n if (Number.isNaN(next) || next < 0xdc00 || next > 0xdfff) return true\n index++\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n return true\n }\n }\n return false\n}\n\nfunction assertValidIdentifier(\n value: unknown,\n label: string,\n): asserts value is string {\n if (\n typeof value !== 'string' ||\n value.length === 0 ||\n hasUnpairedSurrogate(value)\n ) {\n throw new SandboxCheckpointInvalidIdError(\n `${label} must be a non-empty well-formed Unicode string`,\n )\n }\n}\n\nfunction hasOwn(value: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key)\n}\n\nfunction validateEntries(checkpoint: SandboxCheckpoint): void {\n if (!Array.isArray(checkpoint.files)) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint files must be an array',\n )\n }\n const paths = new Set<string>()\n const kinds = new Map<string, 'file' | 'dir'>()\n for (const entry of checkpoint.files as ReadonlyArray<unknown>) {\n if (entry === null || typeof entry !== 'object') {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint entry must be an object',\n )\n }\n const candidate = entry as Record<string, unknown>\n if (\n typeof candidate.path !== 'string' ||\n candidate.path.length === 0 ||\n candidate.path.includes('\\0') ||\n candidate.path.startsWith('/') ||\n candidate.path.startsWith('\\\\') ||\n /^[A-Za-z]:([\\\\/]|$)/.test(candidate.path) ||\n candidate.path.includes('\\\\') ||\n candidate.path\n .split('/')\n .some((part) => part.length === 0 || part === '.' || part === '..')\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint entry path must be a normalized workspace-relative path',\n )\n }\n const path = candidate.path\n if (paths.has(path)) {\n throw new SandboxCheckpointInvalidEntryError(\n `Checkpoint contains duplicate entry path '${path}'`,\n )\n }\n for (\n let separator = path.indexOf('/');\n separator !== -1;\n separator = path.indexOf('/', separator + 1)\n ) {\n const ancestor = path.slice(0, separator)\n if (kinds.get(ancestor) === 'file') {\n throw new SandboxCheckpointInvalidEntryError(\n `Checkpoint entry '${path}' is beneath file '${ancestor}'`,\n )\n }\n }\n if (\n candidate.kind === 'file' &&\n Array.from(kinds.keys()).some((other) => other.startsWith(`${path}/`))\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n `Checkpoint file '${path}' is an ancestor of another entry`,\n )\n }\n paths.add(path)\n if (candidate.kind === 'file') {\n if (\n typeof candidate.blobKey !== 'string' ||\n candidate.blobKey.length === 0 ||\n hasUnpairedSurrogate(candidate.blobKey) ||\n !/^sandbox-files\\/sha256\\/[0-9a-f]{64}$/.test(candidate.blobKey)\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n 'File entries require a valid content-addressed blobKey',\n )\n }\n if (\n !hasOwn(candidate, 'size') ||\n typeof candidate.size !== 'number' ||\n !Number.isSafeInteger(candidate.size) ||\n candidate.size < 0\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n 'File entry size must be a non-negative safe integer',\n )\n }\n } else if (candidate.kind === 'dir') {\n if (hasOwn(candidate, 'blobKey') || hasOwn(candidate, 'size')) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Directory entries cannot contain file fields',\n )\n }\n } else {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint entry kind must be file or dir',\n )\n }\n kinds.set(path, candidate.kind)\n }\n}\n\nfunction validateArtifacts(checkpoint: SandboxCheckpoint): void {\n if (!Array.isArray(checkpoint.artifacts)) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint artifacts must be an array',\n )\n }\n for (const artifact of checkpoint.artifacts as ReadonlyArray<unknown>) {\n if (artifact === null || typeof artifact !== 'object') {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint artifact must be an object',\n )\n }\n const candidate = artifact as Record<string, unknown>\n if (\n typeof candidate.artifactId !== 'string' ||\n candidate.artifactId.length === 0 ||\n hasUnpairedSurrogate(candidate.artifactId) ||\n typeof candidate.name !== 'string' ||\n candidate.name.length === 0 ||\n typeof candidate.mimeType !== 'string' ||\n candidate.mimeType.length === 0 ||\n typeof candidate.blobKey !== 'string' ||\n candidate.blobKey.length === 0 ||\n hasUnpairedSurrogate(candidate.blobKey) ||\n !/^sandbox-artifacts\\/sha256\\/[0-9a-f]{64}$/.test(candidate.blobKey) ||\n typeof candidate.size !== 'number' ||\n !Number.isSafeInteger(candidate.size) ||\n candidate.size < 0 ||\n typeof candidate.createdAt !== 'number' ||\n !Number.isFinite(candidate.createdAt)\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint artifact has invalid fields',\n )\n }\n }\n}\n\nfunction validateCheckpoint(checkpoint: SandboxCheckpoint): void {\n assertValidIdentifier(checkpoint.id, 'Checkpoint id')\n assertValidIdentifier(checkpoint.threadId, 'Checkpoint thread id')\n if (checkpoint.parentCheckpointId !== null) {\n assertValidIdentifier(checkpoint.parentCheckpointId, 'Parent checkpoint id')\n }\n if (!Number.isFinite(checkpoint.createdAt)) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint createdAt must be a finite number',\n )\n }\n validateEntries(checkpoint)\n validateArtifacts(checkpoint)\n}\n\nfunction blobKeys(checkpoint: SandboxCheckpoint): Set<string> {\n const keys = new Set<string>()\n for (const entry of checkpoint.files) {\n if (entry.kind === 'file') keys.add(entry.blobKey)\n }\n for (const artifact of checkpoint.artifacts) keys.add(artifact.blobKey)\n return keys\n}\n\nclass MemorySnapshotCheckpointStore implements ForkCapableSandboxCheckpointStore {\n private readonly now = () => Date.now()\n private readonly leaseDurationMs = 120_000\n private readonly renewAfterMs = 45_000\n\n constructor(private readonly state: MemorySnapshotState) {}\n\n async get(id: string): Promise<SandboxCheckpoint | null> {\n assertValidIdentifier(id, 'Checkpoint id')\n const checkpoint = this.state.checkpoints.get(id)\n return checkpoint ? clone(checkpoint) : null\n }\n\n async list(threadId: string): Promise<Array<SandboxCheckpoint>> {\n assertValidIdentifier(threadId, 'Thread id')\n return [...this.state.checkpoints.values()]\n .filter((checkpoint) => checkpoint.threadId === threadId)\n .sort((a, b) => a.createdAt - b.createdAt || compare(a.id, b.id))\n .map(clone)\n }\n\n async getHead(threadId: string): Promise<string | null> {\n assertValidIdentifier(threadId, 'Thread id')\n return this.state.heads.get(threadId) ?? null\n }\n\n async append(input: {\n checkpoint: SandboxCheckpoint\n expectedHeadId: string | null\n writer: SandboxCheckpointWriter\n }): Promise<{ headId: string }> {\n const checkpoint = clone(input.checkpoint)\n const { expectedHeadId, writer } = input\n assertValidIdentifier(checkpoint.id, 'Checkpoint id')\n assertValidIdentifier(checkpoint.threadId, 'Checkpoint thread id')\n assertValidIdentifier(writer.threadId, 'Writer thread id')\n if (expectedHeadId !== null) {\n assertValidIdentifier(expectedHeadId, 'Expected head id')\n }\n if (checkpoint.parentCheckpointId != null) {\n assertValidIdentifier(\n checkpoint.parentCheckpointId,\n 'Parent checkpoint id',\n )\n }\n if (writer.threadId !== checkpoint.threadId) {\n throw new SandboxCheckpointWriterLostError(\n 'Checkpoint writer thread does not match checkpoint thread',\n )\n }\n validateCheckpoint(checkpoint)\n\n // Re-check live state after staging because cloning caller data may\n // re-enter append and advance this thread's head.\n this.assertWriter(writer, checkpoint.threadId)\n if (this.state.checkpoints.has(checkpoint.id)) {\n throw new SandboxCheckpointDuplicateIdError(\n `Checkpoint '${checkpoint.id}' already exists`,\n )\n }\n const actualHeadId = this.state.heads.get(checkpoint.threadId) ?? null\n if (actualHeadId !== expectedHeadId) {\n throw new SandboxCheckpointConflictError(\n `Expected head '${expectedHeadId}', but thread '${checkpoint.threadId}' is at '${actualHeadId}'`,\n )\n }\n const parentCheckpointId = checkpoint.parentCheckpointId ?? null\n if (parentCheckpointId !== expectedHeadId) {\n throw new SandboxCheckpointParentMismatchError(\n `Checkpoint '${checkpoint.id}' parent does not match expected head`,\n )\n }\n const stored = { ...checkpoint, parentCheckpointId }\n const keys = blobKeys(stored)\n this.state.checkpoints.set(stored.id, stored)\n this.state.heads.set(stored.threadId, stored.id)\n for (const key of keys) {\n this.state.references.set(key, (this.state.references.get(key) ?? 0) + 1)\n }\n return { headId: stored.id }\n }\n\n async deleteHead(input: {\n threadId: string\n checkpointId: string\n writer: SandboxCheckpointWriter\n }): Promise<void> {\n const { threadId, checkpointId, writer } = input\n assertValidIdentifier(threadId, 'Thread id')\n assertValidIdentifier(checkpointId, 'Checkpoint id')\n assertValidIdentifier(writer.threadId, 'Writer thread id')\n if (writer.threadId !== threadId) {\n throw new SandboxCheckpointWriterLostError(\n 'Checkpoint writer thread does not match operation thread',\n )\n }\n this.assertWriter(writer, threadId)\n if ((this.state.heads.get(threadId) ?? null) !== checkpointId) {\n throw new SandboxCheckpointNotHeadError(\n `Checkpoint '${checkpointId}' is not the current head of thread '${threadId}'`,\n )\n }\n const checkpoint = this.state.checkpoints.get(checkpointId)\n if (!checkpoint) {\n throw new SandboxCheckpointNotHeadError(\n `Checkpoint '${checkpointId}' does not exist`,\n )\n }\n this.state.checkpoints.delete(checkpointId)\n if (checkpoint.parentCheckpointId) {\n this.state.heads.set(threadId, checkpoint.parentCheckpointId)\n } else {\n this.state.heads.delete(threadId)\n }\n for (const key of blobKeys(checkpoint)) {\n const references = (this.state.references.get(key) ?? 0) - 1\n if (references > 0) this.state.references.set(key, references)\n else this.state.references.delete(key)\n }\n }\n\n async acquireWriter(threadId: string): Promise<SandboxCheckpointWriterLease> {\n assertValidIdentifier(threadId, 'Thread id')\n const current = this.state.writers.get(threadId)\n if (current && current.expiresAt > this.now()) {\n throw new SandboxCheckpointWriterConflictError(\n `Thread '${threadId}' already has an active checkpoint writer`,\n )\n }\n const fence = (this.state.fences.get(threadId) ?? 0) + 1\n this.state.fences.set(threadId, fence)\n const ownerToken = globalThis.crypto.randomUUID()\n const lease = {\n threadId,\n ownerToken,\n fence,\n expiresAt: this.now() + this.leaseDurationMs,\n }\n this.state.writers.set(threadId, lease)\n return {\n ...lease,\n get expiresAt() {\n return lease.expiresAt\n },\n renewAfterMs: this.renewAfterMs,\n renew: async () => {\n this.assertWriter(lease, threadId)\n lease.expiresAt = this.now() + this.leaseDurationMs\n return { expiresAt: lease.expiresAt }\n },\n release: async () => {\n const currentLease = this.state.writers.get(threadId)\n if (\n currentLease?.ownerToken === ownerToken &&\n currentLease.fence === fence\n ) {\n this.state.writers.delete(threadId)\n }\n },\n }\n }\n\n async listBlobReferences(): Promise<\n Array<{ key: string; references: number }>\n > {\n return [...this.state.references.entries()]\n .sort(([a], [b]) => compare(a, b))\n .map(([key, references]) => ({ key, references }))\n }\n\n async forkFromCheckpoint(\n input: SandboxCheckpointForkInput,\n ): Promise<{ checkpoint: SandboxCheckpoint }> {\n const sourceThreadId = input.sourceThreadId\n const sourceCheckpointId = input.sourceCheckpointId\n const destinationThreadId = input.destinationThreadId\n const destinationCheckpointId = input.destinationCheckpointId\n const createdAt = input.createdAt\n const suppliedWriter = input.writer\n const writer: SandboxCheckpointWriter = {\n threadId: suppliedWriter.threadId,\n ownerToken: suppliedWriter.ownerToken,\n fence: suppliedWriter.fence,\n }\n\n assertValidIdentifier(sourceThreadId, 'Source thread id')\n assertValidIdentifier(sourceCheckpointId, 'Source checkpoint id')\n assertValidIdentifier(destinationThreadId, 'Destination thread id')\n assertValidIdentifier(destinationCheckpointId, 'Destination checkpoint id')\n assertValidIdentifier(writer.threadId, 'Writer thread id')\n if (!Number.isFinite(createdAt)) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Fork checkpoint createdAt must be a finite number',\n )\n }\n if (sourceThreadId === destinationThreadId) {\n throw new SandboxCheckpointError(\n 'SANDBOX_SNAPSHOT_FORK_SOURCE_THREAD_MISMATCH',\n 'Source and destination threads must differ',\n )\n }\n const source = this.state.checkpoints.get(sourceCheckpointId)\n if (!source) {\n throw new SandboxCheckpointError(\n 'SANDBOX_SNAPSHOT_FORK_SOURCE_NOT_FOUND',\n 'Source checkpoint was not found',\n )\n }\n if (source.threadId !== sourceThreadId) {\n throw new SandboxCheckpointError(\n 'SANDBOX_SNAPSHOT_FORK_SOURCE_THREAD_MISMATCH',\n 'Source checkpoint belongs to another thread',\n )\n }\n if (writer.threadId !== destinationThreadId) {\n throw new SandboxCheckpointWriterLostError(\n 'Checkpoint writer thread does not match destination thread',\n )\n }\n this.assertWriter(writer, destinationThreadId)\n this.assertDestinationEmpty(destinationThreadId, destinationCheckpointId)\n\n const stagedCheckpoint: SandboxCheckpoint = clone({\n id: destinationCheckpointId,\n threadId: destinationThreadId,\n parentCheckpointId: null,\n createdAt,\n reason: 'fork-root',\n files: source.files,\n conversation: source.conversation,\n artifacts: source.artifacts,\n })\n validateCheckpoint(stagedCheckpoint)\n const stagedTranscript = clone([...stagedCheckpoint.conversation])\n const result = { checkpoint: clone(stagedCheckpoint) }\n const stagedReferences = [...blobKeys(stagedCheckpoint)].map((key) => ({\n key,\n references: (this.state.references.get(key) ?? 0) + 1,\n }))\n\n // Cloning can invoke user-defined getters. Revalidate immediately before\n // the synchronous publication block so a reentrant save is never lost.\n this.assertWriter(writer, destinationThreadId)\n this.assertDestinationEmpty(destinationThreadId, destinationCheckpointId)\n\n this.state.messages.set(stagedCheckpoint.threadId, stagedTranscript)\n this.state.checkpoints.set(stagedCheckpoint.id, stagedCheckpoint)\n this.state.heads.set(stagedCheckpoint.threadId, stagedCheckpoint.id)\n for (const reference of stagedReferences) {\n this.state.references.set(reference.key, reference.references)\n }\n return result\n }\n\n private assertDestinationEmpty(\n destinationThreadId: string,\n destinationCheckpointId: string,\n ): void {\n if (\n this.state.messages.has(destinationThreadId) ||\n [...this.state.runs.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n [...this.state.generations.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n [...this.state.interrupts.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n [...this.state.artifacts.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n [...this.state.checkpoints.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n this.state.heads.has(destinationThreadId) ||\n this.state.checkpoints.has(destinationCheckpointId)\n ) {\n throw new SandboxCheckpointError(\n 'SANDBOX_SNAPSHOT_FORK_DESTINATION_NOT_EMPTY',\n 'Destination thread is not empty',\n )\n }\n }\n\n private assertWriter(\n writer: SandboxCheckpointWriter,\n threadId: string,\n ): void {\n const current = this.state.writers.get(threadId)\n if (\n !current ||\n current.ownerToken !== writer.ownerToken ||\n current.fence !== writer.fence ||\n current.expiresAt <= this.now()\n ) {\n throw new SandboxCheckpointWriterLostError(\n `Checkpoint writer lease for thread '${threadId}' is no longer current`,\n )\n }\n }\n}\n\nasync function bodyBytes(body: BlobBody): Promise<Uint8Array> {\n if (typeof body === 'string') return encoder.encode(body)\n if (body instanceof ArrayBuffer) return new Uint8Array(body.slice(0))\n if (ArrayBuffer.isView(body))\n return new Uint8Array(\n body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength),\n )\n if (typeof Blob !== 'undefined' && body instanceof Blob)\n return new Uint8Array(await body.arrayBuffer())\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n const reader = body.getReader()\n const parts: Array<Uint8Array> = []\n try {\n for (;;) {\n const next = await reader.read()\n if (next.done) break\n parts.push(new Uint8Array(next.value))\n }\n } finally {\n reader.releaseLock()\n }\n const result = new Uint8Array(\n parts.reduce((total, part) => total + part.byteLength, 0),\n )\n let offset = 0\n for (const part of parts) {\n result.set(part, offset)\n offset += part.byteLength\n }\n return result\n }\n throw new TypeError('Unsupported blob body.')\n}\n\nexport async function memorySandboxSnapshots(\n options: MemorySandboxSnapshotsOptions = {},\n): Promise<MemorySandboxSnapshots> {\n const { persistence, checkpoints } = await createMemorySandboxSnapshots()\n return createSandboxSnapshots({\n persistence,\n checkpoints,\n ...options,\n })\n}\n\nasync function createMemorySandboxSnapshots(): Promise<{\n persistence: MemorySnapshotPersistence\n checkpoints: ForkCapableSandboxCheckpointStore\n}> {\n const messages = new Map<string, Array<ModelMessage>>()\n const runs = new Map<string, RunRecord>()\n const generations = new Map<string, GenerationRunRecord>()\n const interrupts = new Map<string, InterruptRecord>()\n const metadata = new Map<string, Map<string, unknown>>()\n const artifacts = new Map<string, ArtifactRecord>()\n const blobs = new Map<string, { record: BlobRecord; bytes: Uint8Array }>()\n const state: MemorySnapshotState = {\n messages,\n runs,\n generations,\n interrupts,\n metadata,\n artifacts,\n blobs,\n checkpoints: new Map(),\n heads: new Map(),\n writers: new Map(),\n fences: new Map(),\n references: new Map(),\n }\n let etag = 0\n const persistence: MemorySnapshotPersistence = {\n stores: {\n messages: {\n loadThread: async (threadId: string) =>\n messages.get(threadId)?.slice() ?? [],\n saveThread: async (threadId: string, value: Array<ModelMessage>) => {\n messages.set(threadId, value.slice())\n },\n },\n runs: {\n createOrResume: async (input: {\n runId: string\n threadId: string\n status?: RunRecord['status']\n startedAt: number\n }) => {\n const existing = runs.get(input.runId)\n if (existing) return existing\n const record: RunRecord = {\n ...input,\n status: input.status ?? 'running',\n }\n runs.set(record.runId, record)\n return record\n },\n update: async (runId: string, patch: Partial<RunRecord>) => {\n const value = runs.get(runId)\n if (value) runs.set(runId, { ...value, ...patch })\n },\n get: async (runId: string) => runs.get(runId) ?? null,\n findActiveRun: async (threadId: string) =>\n [...runs.values()]\n .filter(\n (run) => run.threadId === threadId && run.status === 'running',\n )\n .sort((a, b) => b.startedAt - a.startedAt)[0] ?? null,\n listByThread: async (threadId: string) =>\n [...runs.values()]\n .filter((run) => run.threadId === threadId)\n .sort((a, b) => a.startedAt - b.startedAt),\n listReclaimable: async (input: { now: number; ttlMs: number }) =>\n [...runs.values()].filter(\n (run) =>\n run.status === 'running' &&\n run.detachedSince !== undefined &&\n run.detachedSince <= input.now - input.ttlMs,\n ),\n },\n generationRuns: {\n createOrResume: async (\n input: Pick<\n GenerationRunRecord,\n | 'runId'\n | 'threadId'\n | 'activity'\n | 'provider'\n | 'model'\n | 'startedAt'\n > & { status?: GenerationRunRecord['status'] },\n ) => {\n const value = generations.get(input.runId) ?? {\n ...input,\n status: input.status ?? 'running',\n }\n generations.set(input.runId, value)\n return value\n },\n update: async (runId: string, patch: Partial<GenerationRunRecord>) => {\n const value = generations.get(runId)\n if (value) generations.set(runId, { ...value, ...patch })\n },\n get: async (runId: string) => generations.get(runId) ?? null,\n findLatestForThread: async (threadId: string) =>\n [...generations.values()]\n .filter((run) => run.threadId === threadId)\n .sort((a, b) => b.startedAt - a.startedAt)[0] ?? null,\n },\n interrupts: {\n create: async (\n record: Omit<InterruptRecord, 'status' | 'resolvedAt'>,\n ) => {\n if (!interrupts.has(record.interruptId))\n interrupts.set(record.interruptId, { ...record, status: 'pending' })\n },\n resolve: async (id: string, response?: unknown) => {\n const value = interrupts.get(id)\n if (value)\n interrupts.set(id, {\n ...value,\n status: 'resolved',\n resolvedAt: Date.now(),\n response,\n })\n },\n cancel: async (id: string) => {\n const value = interrupts.get(id)\n if (value)\n interrupts.set(id, {\n ...value,\n status: 'cancelled',\n resolvedAt: Date.now(),\n })\n },\n get: async (id: string) => interrupts.get(id) ?? null,\n list: async (threadId: string) =>\n [...interrupts.values()]\n .filter((value) => value.threadId === threadId)\n .sort((a, b) => a.requestedAt - b.requestedAt),\n listPending: async (threadId: string) =>\n [...interrupts.values()]\n .filter(\n (value) =>\n value.threadId === threadId && value.status === 'pending',\n )\n .sort((a, b) => a.requestedAt - b.requestedAt),\n listByRun: async (runId: string) =>\n [...interrupts.values()]\n .filter((value) => value.runId === runId)\n .sort((a, b) => a.requestedAt - b.requestedAt),\n listPendingByRun: async (runId: string) =>\n [...interrupts.values()]\n .filter(\n (value) => value.runId === runId && value.status === 'pending',\n )\n .sort((a, b) => a.requestedAt - b.requestedAt),\n },\n metadata: {\n get: async (namespace: string, key: string) => {\n const bucket = metadata.get(namespace)\n return bucket?.has(key) ? bucket.get(key) : null\n },\n set: async (namespace: string, key: string, value: unknown) => {\n let bucket = metadata.get(namespace)\n if (!bucket) {\n bucket = new Map()\n metadata.set(namespace, bucket)\n }\n bucket.set(key, value)\n },\n delete: async (namespace: string, key: string) => {\n metadata.get(namespace)?.delete(key)\n },\n },\n artifacts: {\n save: async (value: ArtifactRecord) => {\n artifacts.set(value.artifactId, { ...value })\n },\n get: async (id: string) => artifacts.get(id) ?? null,\n list: async (runId: string) =>\n [...artifacts.values()]\n .filter((value) => value.runId === runId)\n .sort(\n (a, b) =>\n a.createdAt - b.createdAt ||\n compare(a.artifactId, b.artifactId),\n ),\n\n listForThread: async (threadId: string) =>\n [...artifacts.values()]\n .filter((value) => value.threadId === threadId)\n .sort(\n (a, b) =>\n a.createdAt - b.createdAt ||\n compare(a.artifactId, b.artifactId),\n ),\n\n delete: async (id: string) => {\n artifacts.delete(id)\n },\n deleteForRun: async (runId: string) => {\n for (const [id, value] of artifacts)\n if (value.runId === runId) artifacts.delete(id)\n },\n },\n blobs: {\n put: async (\n key: string,\n body: BlobBody,\n putOptions?: {\n contentType?: string\n customMetadata?: Record<string, string>\n },\n ) => {\n const bytes = await bodyBytes(body)\n const now = Date.now()\n const record: BlobRecord = {\n key,\n size: bytes.byteLength,\n etag: String(++etag),\n contentType:\n putOptions?.contentType ??\n (typeof Blob !== 'undefined' && body instanceof Blob\n ? body.type || undefined\n : undefined),\n customMetadata: putOptions?.customMetadata\n ? { ...putOptions.customMetadata }\n : undefined,\n createdAt: blobs.get(key)?.record.createdAt ?? now,\n updatedAt: now,\n }\n blobs.set(key, { record, bytes: new Uint8Array(bytes) })\n return clone(record)\n },\n get: async (key: string, getOptions?: BlobGetOptions) => {\n const value = blobs.get(key)\n if (!value) return null\n const range = getOptions?.range\n ? resolveBlobRange(value.bytes.byteLength, getOptions.range)\n : { offset: 0, length: value.bytes.byteLength }\n const bytes = value.bytes.slice(\n range.offset,\n range.offset + range.length,\n )\n return {\n ...clone(value.record),\n ...(getOptions?.range ? { range } : {}),\n body: new Blob([bytes]).stream(),\n arrayBuffer: async () =>\n bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ),\n text: async () => new TextDecoder().decode(bytes),\n }\n },\n head: async (key: string) => clone(blobs.get(key)?.record ?? null),\n delete: async (key: string) => {\n blobs.delete(key)\n },\n list: async (listOptions?: {\n prefix?: string\n cursor?: string\n limit?: number\n }) => {\n const keys = [...blobs.keys()]\n .filter((key) => key.startsWith(listOptions?.prefix ?? ''))\n .filter(\n (key) =>\n listOptions?.cursor === undefined || key > listOptions.cursor,\n )\n .sort()\n if (listOptions?.limit === 0) return { objects: [], truncated: false }\n const page =\n listOptions?.limit === undefined\n ? keys\n : keys.slice(0, listOptions.limit)\n const truncated =\n listOptions?.limit !== undefined && keys.length > page.length\n const objects = page.map((key) => {\n const value = blobs.get(key)\n if (!value) throw new Error(`Missing blob for listed key: ${key}`)\n return value.record\n })\n return {\n objects: clone(objects),\n ...(truncated ? { cursor: page.at(-1), truncated: true } : {}),\n }\n },\n },\n },\n }\n const checkpointStore = new MemorySnapshotCheckpointStore(state)\n return {\n persistence,\n checkpoints: checkpointStore,\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory-snapshots.js","names":[],"sources":["../../src/memory-snapshots.ts"],"sourcesContent":["import type { ModelMessage } from '@tanstack/ai'\nimport type {\n MemoryArtifactRecord as ArtifactRecord,\n MemoryBlobBody as BlobBody,\n MemoryBlobRecord as BlobRecord,\n MemoryGenerationRunRecord as GenerationRunRecord,\n MemoryInterruptRecord as InterruptRecord,\n MemoryRunRecord as RunRecord,\n MemorySnapshotPersistence,\n} from './memory-snapshot-types'\nimport {\n SandboxCheckpointConflictError,\n SandboxCheckpointDuplicateIdError,\n SandboxCheckpointError,\n SandboxCheckpointInvalidEntryError,\n SandboxCheckpointInvalidIdError,\n SandboxCheckpointNotHeadError,\n SandboxCheckpointParentMismatchError,\n SandboxCheckpointWriterConflictError,\n SandboxCheckpointWriterLostError,\n} from './checkpoint-store'\nimport type {\n ForkCapableSandboxCheckpointStore,\n SandboxCheckpoint,\n SandboxCheckpointWriter,\n SandboxCheckpointWriterLease,\n SandboxCheckpointForkInput,\n} from './checkpoint-store'\nimport { createSandboxSnapshots } from './snapshot-operations'\nimport type {\n CreateSandboxSnapshotsInput,\n SandboxSnapshots,\n} from './snapshot-operations'\n\ntype BlobGetOptions = { range?: { offset: number; length?: number } }\n\nfunction resolveBlobRange(\n size: number,\n range: { offset: number; length?: number },\n): { offset: number; length: number } {\n if (\n !Number.isInteger(range.offset) ||\n range.offset < 0 ||\n range.offset >= size\n ) {\n throw new RangeError(\n `Blob range offset ${range.offset} is outside the object (size ${size}).`,\n )\n }\n const remaining = size - range.offset\n if (range.length === undefined) {\n return { offset: range.offset, length: remaining }\n }\n if (!Number.isInteger(range.length) || range.length < 0) {\n throw new RangeError(`Blob range length ${range.length} is not valid.`)\n }\n return {\n offset: range.offset,\n length: Math.min(range.length, remaining),\n }\n}\n\nexport type MemorySandboxSnapshots = SandboxSnapshots<\n MemorySnapshotPersistence,\n ForkCapableSandboxCheckpointStore\n>\n\nexport type MemorySandboxSnapshotsOptions = Omit<\n CreateSandboxSnapshotsInput<\n MemorySnapshotPersistence,\n ForkCapableSandboxCheckpointStore\n >,\n 'persistence' | 'checkpoints'\n>\n\nconst encoder = new TextEncoder()\nconst compare = (a: string, b: string) => {\n const left = encoder.encode(a)\n const right = encoder.encode(b)\n for (let i = 0; i < Math.min(left.length, right.length); i++) {\n const leftByte = left[i]\n const rightByte = right[i]\n if (leftByte !== rightByte) return (leftByte ?? 0) - (rightByte ?? 0)\n }\n return left.length - right.length\n}\nconst clone = <T>(value: T): T => structuredClone(value)\n\ninterface MemoryCheckpointState {\n checkpoints: Map<string, SandboxCheckpoint>\n heads: Map<string, string>\n writers: Map<string, { ownerToken: string; fence: number; expiresAt: number }>\n fences: Map<string, number>\n references: Map<string, number>\n}\n\ninterface MemorySnapshotState extends MemoryCheckpointState {\n messages: Map<string, Array<ModelMessage>>\n runs: Map<string, RunRecord>\n generations: Map<string, GenerationRunRecord>\n interrupts: Map<string, InterruptRecord>\n metadata: Map<string, Map<string, unknown>>\n artifacts: Map<string, ArtifactRecord>\n blobs: Map<string, { record: BlobRecord; bytes: Uint8Array }>\n}\n\nfunction hasUnpairedSurrogate(value: string): boolean {\n for (let index = 0; index < value.length; index++) {\n const code = value.charCodeAt(index)\n if (code >= 0xd800 && code <= 0xdbff) {\n const next = value.charCodeAt(index + 1)\n if (Number.isNaN(next) || next < 0xdc00 || next > 0xdfff) return true\n index++\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n return true\n }\n }\n return false\n}\n\nfunction assertValidIdentifier(\n value: unknown,\n label: string,\n): asserts value is string {\n if (\n typeof value !== 'string' ||\n value.length === 0 ||\n hasUnpairedSurrogate(value)\n ) {\n throw new SandboxCheckpointInvalidIdError(\n `${label} must be a non-empty well-formed Unicode string`,\n )\n }\n}\n\nfunction hasOwn(value: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key)\n}\n\nfunction validateEntries(checkpoint: SandboxCheckpoint): void {\n if (!Array.isArray(checkpoint.files)) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint files must be an array',\n )\n }\n const paths = new Set<string>()\n const kinds = new Map<string, 'file' | 'dir'>()\n for (const entry of checkpoint.files as ReadonlyArray<unknown>) {\n if (entry === null || typeof entry !== 'object') {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint entry must be an object',\n )\n }\n const candidate = entry as Record<string, unknown>\n if (\n typeof candidate.path !== 'string' ||\n candidate.path.length === 0 ||\n candidate.path.includes('\\0') ||\n candidate.path.startsWith('/') ||\n candidate.path.startsWith('\\\\') ||\n /^[A-Za-z]:([\\\\/]|$)/.test(candidate.path) ||\n candidate.path.includes('\\\\') ||\n candidate.path\n .split('/')\n .some((part) => part.length === 0 || part === '.' || part === '..')\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint entry path must be a normalized workspace-relative path',\n )\n }\n const path = candidate.path\n if (paths.has(path)) {\n throw new SandboxCheckpointInvalidEntryError(\n `Checkpoint contains duplicate entry path '${path}'`,\n )\n }\n for (\n let separator = path.indexOf('/');\n separator !== -1;\n separator = path.indexOf('/', separator + 1)\n ) {\n const ancestor = path.slice(0, separator)\n if (kinds.get(ancestor) === 'file') {\n throw new SandboxCheckpointInvalidEntryError(\n `Checkpoint entry '${path}' is beneath file '${ancestor}'`,\n )\n }\n }\n if (\n candidate.kind === 'file' &&\n Array.from(kinds.keys()).some((other) => other.startsWith(`${path}/`))\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n `Checkpoint file '${path}' is an ancestor of another entry`,\n )\n }\n paths.add(path)\n if (candidate.kind === 'file') {\n if (\n typeof candidate.blobKey !== 'string' ||\n candidate.blobKey.length === 0 ||\n hasUnpairedSurrogate(candidate.blobKey) ||\n !/^sandbox-files\\/sha256\\/[0-9a-f]{64}$/.test(candidate.blobKey)\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n 'File entries require a valid content-addressed blobKey',\n )\n }\n if (\n !hasOwn(candidate, 'size') ||\n typeof candidate.size !== 'number' ||\n !Number.isSafeInteger(candidate.size) ||\n candidate.size < 0\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n 'File entry size must be a non-negative safe integer',\n )\n }\n } else if (candidate.kind === 'dir') {\n if (hasOwn(candidate, 'blobKey') || hasOwn(candidate, 'size')) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Directory entries cannot contain file fields',\n )\n }\n } else {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint entry kind must be file or dir',\n )\n }\n kinds.set(path, candidate.kind)\n }\n}\n\nfunction validateArtifacts(checkpoint: SandboxCheckpoint): void {\n if (!Array.isArray(checkpoint.artifacts)) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint artifacts must be an array',\n )\n }\n for (const artifact of checkpoint.artifacts as ReadonlyArray<unknown>) {\n if (artifact === null || typeof artifact !== 'object') {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint artifact must be an object',\n )\n }\n const candidate = artifact as Record<string, unknown>\n if (\n typeof candidate.artifactId !== 'string' ||\n candidate.artifactId.length === 0 ||\n hasUnpairedSurrogate(candidate.artifactId) ||\n typeof candidate.name !== 'string' ||\n candidate.name.length === 0 ||\n typeof candidate.mimeType !== 'string' ||\n candidate.mimeType.length === 0 ||\n typeof candidate.blobKey !== 'string' ||\n candidate.blobKey.length === 0 ||\n hasUnpairedSurrogate(candidate.blobKey) ||\n !/^sandbox-artifacts\\/sha256\\/[0-9a-f]{64}$/.test(candidate.blobKey) ||\n typeof candidate.size !== 'number' ||\n !Number.isSafeInteger(candidate.size) ||\n candidate.size < 0 ||\n typeof candidate.createdAt !== 'number' ||\n !Number.isFinite(candidate.createdAt)\n ) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint artifact has invalid fields',\n )\n }\n }\n}\n\nfunction validateCheckpoint(checkpoint: SandboxCheckpoint): void {\n assertValidIdentifier(checkpoint.id, 'Checkpoint id')\n assertValidIdentifier(checkpoint.threadId, 'Checkpoint thread id')\n if (checkpoint.parentCheckpointId !== null) {\n assertValidIdentifier(checkpoint.parentCheckpointId, 'Parent checkpoint id')\n }\n if (!Number.isFinite(checkpoint.createdAt)) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Checkpoint createdAt must be a finite number',\n )\n }\n validateEntries(checkpoint)\n validateArtifacts(checkpoint)\n}\n\nfunction blobKeys(checkpoint: SandboxCheckpoint): Set<string> {\n const keys = new Set<string>()\n for (const entry of checkpoint.files) {\n if (entry.kind === 'file') keys.add(entry.blobKey)\n }\n for (const artifact of checkpoint.artifacts) keys.add(artifact.blobKey)\n return keys\n}\n\nclass MemorySnapshotCheckpointStore implements ForkCapableSandboxCheckpointStore {\n private readonly now = () => Date.now()\n private readonly leaseDurationMs = 120_000\n private readonly renewAfterMs = 45_000\n\n constructor(private readonly state: MemorySnapshotState) {}\n\n async get(id: string): Promise<SandboxCheckpoint | null> {\n assertValidIdentifier(id, 'Checkpoint id')\n const checkpoint = this.state.checkpoints.get(id)\n return checkpoint ? clone(checkpoint) : null\n }\n\n async list(threadId: string): Promise<Array<SandboxCheckpoint>> {\n assertValidIdentifier(threadId, 'Thread id')\n return [...this.state.checkpoints.values()]\n .filter((checkpoint) => checkpoint.threadId === threadId)\n .sort((a, b) => a.createdAt - b.createdAt || compare(a.id, b.id))\n .map(clone)\n }\n\n async getHead(threadId: string): Promise<string | null> {\n assertValidIdentifier(threadId, 'Thread id')\n return this.state.heads.get(threadId) ?? null\n }\n\n async append(input: {\n checkpoint: SandboxCheckpoint\n expectedHeadId: string | null\n writer: SandboxCheckpointWriter\n }): Promise<{ headId: string }> {\n const checkpoint = clone(input.checkpoint)\n const { expectedHeadId, writer } = input\n assertValidIdentifier(checkpoint.id, 'Checkpoint id')\n assertValidIdentifier(checkpoint.threadId, 'Checkpoint thread id')\n assertValidIdentifier(writer.threadId, 'Writer thread id')\n if (expectedHeadId !== null) {\n assertValidIdentifier(expectedHeadId, 'Expected head id')\n }\n if (checkpoint.parentCheckpointId != null) {\n assertValidIdentifier(\n checkpoint.parentCheckpointId,\n 'Parent checkpoint id',\n )\n }\n if (writer.threadId !== checkpoint.threadId) {\n throw new SandboxCheckpointWriterLostError(\n 'Checkpoint writer thread does not match checkpoint thread',\n )\n }\n validateCheckpoint(checkpoint)\n\n // Re-check live state after staging because cloning caller data may\n // re-enter append and advance this thread's head.\n this.assertWriter(writer, checkpoint.threadId)\n if (this.state.checkpoints.has(checkpoint.id)) {\n throw new SandboxCheckpointDuplicateIdError(\n `Checkpoint '${checkpoint.id}' already exists`,\n )\n }\n const actualHeadId = this.state.heads.get(checkpoint.threadId) ?? null\n if (actualHeadId !== expectedHeadId) {\n throw new SandboxCheckpointConflictError(\n `Expected head '${expectedHeadId}', but thread '${checkpoint.threadId}' is at '${actualHeadId}'`,\n )\n }\n const parentCheckpointId = checkpoint.parentCheckpointId ?? null\n if (parentCheckpointId !== expectedHeadId) {\n throw new SandboxCheckpointParentMismatchError(\n `Checkpoint '${checkpoint.id}' parent does not match expected head`,\n )\n }\n const stored = { ...checkpoint, parentCheckpointId }\n const keys = blobKeys(stored)\n this.state.checkpoints.set(stored.id, stored)\n this.state.heads.set(stored.threadId, stored.id)\n for (const key of keys) {\n this.state.references.set(key, (this.state.references.get(key) ?? 0) + 1)\n }\n return { headId: stored.id }\n }\n\n async deleteHead(input: {\n threadId: string\n checkpointId: string\n writer: SandboxCheckpointWriter\n }): Promise<void> {\n const { threadId, checkpointId, writer } = input\n assertValidIdentifier(threadId, 'Thread id')\n assertValidIdentifier(checkpointId, 'Checkpoint id')\n assertValidIdentifier(writer.threadId, 'Writer thread id')\n if (writer.threadId !== threadId) {\n throw new SandboxCheckpointWriterLostError(\n 'Checkpoint writer thread does not match operation thread',\n )\n }\n this.assertWriter(writer, threadId)\n if ((this.state.heads.get(threadId) ?? null) !== checkpointId) {\n throw new SandboxCheckpointNotHeadError(\n `Checkpoint '${checkpointId}' is not the current head of thread '${threadId}'`,\n )\n }\n const checkpoint = this.state.checkpoints.get(checkpointId)\n if (!checkpoint) {\n throw new SandboxCheckpointNotHeadError(\n `Checkpoint '${checkpointId}' does not exist`,\n )\n }\n this.state.checkpoints.delete(checkpointId)\n if (checkpoint.parentCheckpointId) {\n this.state.heads.set(threadId, checkpoint.parentCheckpointId)\n } else {\n this.state.heads.delete(threadId)\n }\n for (const key of blobKeys(checkpoint)) {\n const references = (this.state.references.get(key) ?? 0) - 1\n if (references > 0) this.state.references.set(key, references)\n else this.state.references.delete(key)\n }\n }\n\n async acquireWriter(threadId: string): Promise<SandboxCheckpointWriterLease> {\n assertValidIdentifier(threadId, 'Thread id')\n const current = this.state.writers.get(threadId)\n if (current && current.expiresAt > this.now()) {\n throw new SandboxCheckpointWriterConflictError(\n `Thread '${threadId}' already has an active checkpoint writer`,\n )\n }\n const fence = (this.state.fences.get(threadId) ?? 0) + 1\n this.state.fences.set(threadId, fence)\n const ownerToken = globalThis.crypto.randomUUID()\n const lease = {\n threadId,\n ownerToken,\n fence,\n expiresAt: this.now() + this.leaseDurationMs,\n }\n this.state.writers.set(threadId, lease)\n return {\n ...lease,\n get expiresAt() {\n return lease.expiresAt\n },\n renewAfterMs: this.renewAfterMs,\n renew: async () => {\n this.assertWriter(lease, threadId)\n lease.expiresAt = this.now() + this.leaseDurationMs\n return { expiresAt: lease.expiresAt }\n },\n release: async () => {\n const currentLease = this.state.writers.get(threadId)\n if (\n currentLease?.ownerToken === ownerToken &&\n currentLease.fence === fence\n ) {\n this.state.writers.delete(threadId)\n }\n },\n }\n }\n\n async listBlobReferences(): Promise<\n Array<{ key: string; references: number }>\n > {\n return [...this.state.references.entries()]\n .sort(([a], [b]) => compare(a, b))\n .map(([key, references]) => ({ key, references }))\n }\n\n async forkFromCheckpoint(\n input: SandboxCheckpointForkInput,\n ): Promise<{ checkpoint: SandboxCheckpoint }> {\n const sourceThreadId = input.sourceThreadId\n const sourceCheckpointId = input.sourceCheckpointId\n const destinationThreadId = input.destinationThreadId\n const destinationCheckpointId = input.destinationCheckpointId\n const createdAt = input.createdAt\n const suppliedWriter = input.writer\n const writer: SandboxCheckpointWriter = {\n threadId: suppliedWriter.threadId,\n ownerToken: suppliedWriter.ownerToken,\n fence: suppliedWriter.fence,\n }\n\n assertValidIdentifier(sourceThreadId, 'Source thread id')\n assertValidIdentifier(sourceCheckpointId, 'Source checkpoint id')\n assertValidIdentifier(destinationThreadId, 'Destination thread id')\n assertValidIdentifier(destinationCheckpointId, 'Destination checkpoint id')\n assertValidIdentifier(writer.threadId, 'Writer thread id')\n if (!Number.isFinite(createdAt)) {\n throw new SandboxCheckpointInvalidEntryError(\n 'Fork checkpoint createdAt must be a finite number',\n )\n }\n if (sourceThreadId === destinationThreadId) {\n throw new SandboxCheckpointError(\n 'SANDBOX_SNAPSHOT_FORK_SOURCE_THREAD_MISMATCH',\n 'Source and destination threads must differ',\n )\n }\n const source = this.state.checkpoints.get(sourceCheckpointId)\n if (!source) {\n throw new SandboxCheckpointError(\n 'SANDBOX_SNAPSHOT_FORK_SOURCE_NOT_FOUND',\n 'Source checkpoint was not found',\n )\n }\n if (source.threadId !== sourceThreadId) {\n throw new SandboxCheckpointError(\n 'SANDBOX_SNAPSHOT_FORK_SOURCE_THREAD_MISMATCH',\n 'Source checkpoint belongs to another thread',\n )\n }\n if (writer.threadId !== destinationThreadId) {\n throw new SandboxCheckpointWriterLostError(\n 'Checkpoint writer thread does not match destination thread',\n )\n }\n this.assertWriter(writer, destinationThreadId)\n this.assertDestinationEmpty(destinationThreadId, destinationCheckpointId)\n\n const stagedCheckpoint: SandboxCheckpoint = clone({\n id: destinationCheckpointId,\n threadId: destinationThreadId,\n parentCheckpointId: null,\n createdAt,\n reason: 'fork-root',\n files: source.files,\n conversation: source.conversation,\n artifacts: source.artifacts,\n })\n validateCheckpoint(stagedCheckpoint)\n const stagedTranscript = clone([...stagedCheckpoint.conversation])\n const result = { checkpoint: clone(stagedCheckpoint) }\n const stagedReferences = [...blobKeys(stagedCheckpoint)].map((key) => ({\n key,\n references: (this.state.references.get(key) ?? 0) + 1,\n }))\n\n // Cloning can invoke user-defined getters. Revalidate immediately before\n // the synchronous publication block so a reentrant save is never lost.\n this.assertWriter(writer, destinationThreadId)\n this.assertDestinationEmpty(destinationThreadId, destinationCheckpointId)\n\n this.state.messages.set(stagedCheckpoint.threadId, stagedTranscript)\n this.state.checkpoints.set(stagedCheckpoint.id, stagedCheckpoint)\n this.state.heads.set(stagedCheckpoint.threadId, stagedCheckpoint.id)\n for (const reference of stagedReferences) {\n this.state.references.set(reference.key, reference.references)\n }\n return result\n }\n\n private assertDestinationEmpty(\n destinationThreadId: string,\n destinationCheckpointId: string,\n ): void {\n if (\n this.state.messages.has(destinationThreadId) ||\n [...this.state.runs.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n [...this.state.generations.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n [...this.state.interrupts.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n [...this.state.artifacts.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n [...this.state.checkpoints.values()].some(\n (value) => value.threadId === destinationThreadId,\n ) ||\n this.state.heads.has(destinationThreadId) ||\n this.state.checkpoints.has(destinationCheckpointId)\n ) {\n throw new SandboxCheckpointError(\n 'SANDBOX_SNAPSHOT_FORK_DESTINATION_NOT_EMPTY',\n 'Destination thread is not empty',\n )\n }\n }\n\n private assertWriter(\n writer: SandboxCheckpointWriter,\n threadId: string,\n ): void {\n const current = this.state.writers.get(threadId)\n if (\n !current ||\n current.ownerToken !== writer.ownerToken ||\n current.fence !== writer.fence ||\n current.expiresAt <= this.now()\n ) {\n throw new SandboxCheckpointWriterLostError(\n `Checkpoint writer lease for thread '${threadId}' is no longer current`,\n )\n }\n }\n}\n\nasync function bodyBytes(body: BlobBody): Promise<Uint8Array> {\n if (typeof body === 'string') return encoder.encode(body)\n if (body instanceof ArrayBuffer) return new Uint8Array(body.slice(0))\n if (ArrayBuffer.isView(body))\n return new Uint8Array(\n body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength),\n )\n if (typeof Blob !== 'undefined' && body instanceof Blob)\n return new Uint8Array(await body.arrayBuffer())\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n const reader = body.getReader()\n const parts: Array<Uint8Array> = []\n try {\n for (;;) {\n const next = await reader.read()\n if (next.done) break\n parts.push(new Uint8Array(next.value))\n }\n } finally {\n reader.releaseLock()\n }\n const result = new Uint8Array(\n parts.reduce((total, part) => total + part.byteLength, 0),\n )\n let offset = 0\n for (const part of parts) {\n result.set(part, offset)\n offset += part.byteLength\n }\n return result\n }\n throw new TypeError('Unsupported blob body.')\n}\n\nexport async function memorySandboxSnapshots(\n options: MemorySandboxSnapshotsOptions = {},\n): Promise<MemorySandboxSnapshots> {\n const { persistence, checkpoints } = await createMemorySandboxSnapshots()\n return createSandboxSnapshots({\n persistence,\n checkpoints,\n ...options,\n })\n}\n\nasync function createMemorySandboxSnapshots(): Promise<{\n persistence: MemorySnapshotPersistence\n checkpoints: ForkCapableSandboxCheckpointStore\n}> {\n const messages = new Map<string, Array<ModelMessage>>()\n const runs = new Map<string, RunRecord>()\n const generations = new Map<string, GenerationRunRecord>()\n const interrupts = new Map<string, InterruptRecord>()\n const metadata = new Map<string, Map<string, unknown>>()\n const artifacts = new Map<string, ArtifactRecord>()\n const blobs = new Map<string, { record: BlobRecord; bytes: Uint8Array }>()\n const state: MemorySnapshotState = {\n messages,\n runs,\n generations,\n interrupts,\n metadata,\n artifacts,\n blobs,\n checkpoints: new Map(),\n heads: new Map(),\n writers: new Map(),\n fences: new Map(),\n references: new Map(),\n }\n let etag = 0\n const persistence: MemorySnapshotPersistence = {\n stores: {\n messages: {\n loadThread: async (threadId: string) =>\n messages.get(threadId)?.slice() ?? [],\n saveThread: async (threadId: string, value: Array<ModelMessage>) => {\n messages.set(threadId, value.slice())\n },\n },\n runs: {\n createOrResume: async (input: {\n runId: string\n threadId: string\n status?: RunRecord['status']\n startedAt: number\n parentRunId?: string\n subagentRunId?: string\n name?: string\n }) => {\n const existing = runs.get(input.runId)\n if (existing) return existing\n const record: RunRecord = {\n ...input,\n status: input.status ?? 'running',\n }\n runs.set(record.runId, record)\n return record\n },\n update: async (runId: string, patch: Partial<RunRecord>) => {\n const value = runs.get(runId)\n if (value) runs.set(runId, { ...value, ...patch })\n },\n get: async (runId: string) => runs.get(runId) ?? null,\n findActiveRun: async (threadId: string) =>\n [...runs.values()]\n .filter(\n (run) => run.threadId === threadId && run.status === 'running',\n )\n .sort((a, b) => b.startedAt - a.startedAt)[0] ?? null,\n listByThread: async (threadId: string) =>\n [...runs.values()]\n .filter((run) => run.threadId === threadId)\n .sort((a, b) => a.startedAt - b.startedAt),\n listByParentRun: async (parentRunId: string) =>\n [...runs.values()]\n .filter((run) => run.parentRunId === parentRunId)\n .sort((a, b) => a.startedAt - b.startedAt),\n listReclaimable: async (input: { now: number; ttlMs: number }) =>\n [...runs.values()].filter(\n (run) =>\n run.status === 'running' &&\n run.detachedSince !== undefined &&\n run.detachedSince <= input.now - input.ttlMs,\n ),\n },\n generationRuns: {\n createOrResume: async (\n input: Pick<\n GenerationRunRecord,\n | 'runId'\n | 'threadId'\n | 'activity'\n | 'provider'\n | 'model'\n | 'startedAt'\n > & { status?: GenerationRunRecord['status'] },\n ) => {\n const value = generations.get(input.runId) ?? {\n ...input,\n status: input.status ?? 'running',\n }\n generations.set(input.runId, value)\n return value\n },\n update: async (runId: string, patch: Partial<GenerationRunRecord>) => {\n const value = generations.get(runId)\n if (value) generations.set(runId, { ...value, ...patch })\n },\n get: async (runId: string) => generations.get(runId) ?? null,\n findLatestForThread: async (threadId: string) =>\n [...generations.values()]\n .filter((run) => run.threadId === threadId)\n .sort((a, b) => b.startedAt - a.startedAt)[0] ?? null,\n },\n interrupts: {\n create: async (\n record: Omit<InterruptRecord, 'status' | 'resolvedAt'>,\n ) => {\n if (!interrupts.has(record.interruptId))\n interrupts.set(record.interruptId, { ...record, status: 'pending' })\n },\n resolve: async (id: string, response?: unknown) => {\n const value = interrupts.get(id)\n if (value)\n interrupts.set(id, {\n ...value,\n status: 'resolved',\n resolvedAt: Date.now(),\n response,\n })\n },\n cancel: async (id: string) => {\n const value = interrupts.get(id)\n if (value)\n interrupts.set(id, {\n ...value,\n status: 'cancelled',\n resolvedAt: Date.now(),\n })\n },\n get: async (id: string) => interrupts.get(id) ?? null,\n list: async (threadId: string) =>\n [...interrupts.values()]\n .filter((value) => value.threadId === threadId)\n .sort((a, b) => a.requestedAt - b.requestedAt),\n listPending: async (threadId: string) =>\n [...interrupts.values()]\n .filter(\n (value) =>\n value.threadId === threadId && value.status === 'pending',\n )\n .sort((a, b) => a.requestedAt - b.requestedAt),\n listByRun: async (runId: string) =>\n [...interrupts.values()]\n .filter((value) => value.runId === runId)\n .sort((a, b) => a.requestedAt - b.requestedAt),\n listPendingByRun: async (runId: string) =>\n [...interrupts.values()]\n .filter(\n (value) => value.runId === runId && value.status === 'pending',\n )\n .sort((a, b) => a.requestedAt - b.requestedAt),\n },\n metadata: {\n get: async (namespace: string, key: string) => {\n const bucket = metadata.get(namespace)\n return bucket?.has(key) ? bucket.get(key) : null\n },\n set: async (namespace: string, key: string, value: unknown) => {\n let bucket = metadata.get(namespace)\n if (!bucket) {\n bucket = new Map()\n metadata.set(namespace, bucket)\n }\n bucket.set(key, value)\n },\n delete: async (namespace: string, key: string) => {\n metadata.get(namespace)?.delete(key)\n },\n },\n artifacts: {\n save: async (value: ArtifactRecord) => {\n artifacts.set(value.artifactId, { ...value })\n },\n get: async (id: string) => artifacts.get(id) ?? null,\n list: async (runId: string) =>\n [...artifacts.values()]\n .filter((value) => value.runId === runId)\n .sort(\n (a, b) =>\n a.createdAt - b.createdAt ||\n compare(a.artifactId, b.artifactId),\n ),\n\n listForThread: async (threadId: string) =>\n [...artifacts.values()]\n .filter((value) => value.threadId === threadId)\n .sort(\n (a, b) =>\n a.createdAt - b.createdAt ||\n compare(a.artifactId, b.artifactId),\n ),\n\n delete: async (id: string) => {\n artifacts.delete(id)\n },\n deleteForRun: async (runId: string) => {\n for (const [id, value] of artifacts)\n if (value.runId === runId) artifacts.delete(id)\n },\n },\n blobs: {\n put: async (\n key: string,\n body: BlobBody,\n putOptions?: {\n contentType?: string\n customMetadata?: Record<string, string>\n },\n ) => {\n const bytes = await bodyBytes(body)\n const now = Date.now()\n const record: BlobRecord = {\n key,\n size: bytes.byteLength,\n etag: String(++etag),\n contentType:\n putOptions?.contentType ??\n (typeof Blob !== 'undefined' && body instanceof Blob\n ? body.type || undefined\n : undefined),\n customMetadata: putOptions?.customMetadata\n ? { ...putOptions.customMetadata }\n : undefined,\n createdAt: blobs.get(key)?.record.createdAt ?? now,\n updatedAt: now,\n }\n blobs.set(key, { record, bytes: new Uint8Array(bytes) })\n return clone(record)\n },\n get: async (key: string, getOptions?: BlobGetOptions) => {\n const value = blobs.get(key)\n if (!value) return null\n const range = getOptions?.range\n ? resolveBlobRange(value.bytes.byteLength, getOptions.range)\n : { offset: 0, length: value.bytes.byteLength }\n const bytes = value.bytes.slice(\n range.offset,\n range.offset + range.length,\n )\n return {\n ...clone(value.record),\n ...(getOptions?.range ? { range } : {}),\n body: new Blob([bytes]).stream(),\n arrayBuffer: async () =>\n bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ),\n text: async () => new TextDecoder().decode(bytes),\n }\n },\n head: async (key: string) => clone(blobs.get(key)?.record ?? null),\n delete: async (key: string) => {\n blobs.delete(key)\n },\n list: async (listOptions?: {\n prefix?: string\n cursor?: string\n limit?: number\n }) => {\n const keys = [...blobs.keys()]\n .filter((key) => key.startsWith(listOptions?.prefix ?? ''))\n .filter(\n (key) =>\n listOptions?.cursor === undefined || key > listOptions.cursor,\n )\n .sort()\n if (listOptions?.limit === 0) return { objects: [], truncated: false }\n const page =\n listOptions?.limit === undefined\n ? keys\n : keys.slice(0, listOptions.limit)\n const truncated =\n listOptions?.limit !== undefined && keys.length > page.length\n const objects = page.map((key) => {\n const value = blobs.get(key)\n if (!value) throw new Error(`Missing blob for listed key: ${key}`)\n return value.record\n })\n return {\n objects: clone(objects),\n ...(truncated ? { cursor: page.at(-1), truncated: true } : {}),\n }\n },\n },\n },\n }\n const checkpointStore = new MemorySnapshotCheckpointStore(state)\n return {\n persistence,\n checkpoints: checkpointStore,\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyTool } from '@tanstack/ai';
|
|
1
|
+
import { AnyTool, EmitCustomEventOptions } from '@tanstack/ai';
|
|
2
2
|
/**
|
|
3
3
|
* Name of the bridged MCP server. The agent sees tools as
|
|
4
4
|
* `mcp__tanstack__<tool>`; each adapter's stream translator strips this prefix
|
|
@@ -33,7 +33,7 @@ export interface ToolBridgeCoreOptions {
|
|
|
33
33
|
* `emitCustomEvent` never reaches a bridged tool. The harness adapter supplies
|
|
34
34
|
* one that injects a CUSTOM chunk into its live output stream.
|
|
35
35
|
*/
|
|
36
|
-
emitCustomEvent?: (eventName: string, value: Record<string, unknown
|
|
36
|
+
emitCustomEvent?: (eventName: string, value: Record<string, unknown>, options?: EmitCustomEventOptions) => void;
|
|
37
37
|
/**
|
|
38
38
|
* Optional permission-prompt tool (e.g. for Claude Code's
|
|
39
39
|
* `--permission-prompt-tool`). When set, the bridge exposes an extra MCP tool
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-bridge.js","names":[],"sources":["../../src/tool-bridge.ts"],"sourcesContent":["/**\n * MCP tool-proxy bridge, shared by all harness adapters.\n *\n * Exposes chat()-provided server tools to an in-sandbox agent as an MCP server.\n * The agent (inside the sandbox) calls `mcp__tanstack__<tool>`; the call is\n * proxied OUT to a bridge endpoint, where the tool's `execute()` runs in the\n * orchestrator process (with its closures / DB / secrets), and the result is\n * returned into the sandbox.\n *\n * The bridge is split into a transport-agnostic CORE and a TRANSPORT:\n * - {@link createToolBridgeCore} owns tool dispatch + the permission resolver\n * (no I/O). It is what makes the bridge portable.\n * - {@link startHostToolBridge} is the `node:http` transport for a long-running\n * host (laptop / CI / Docker orchestrator). It binds loopback unless the\n * sandbox must reach it via `host.docker.internal`, and authenticates with a\n * constant-time bearer check.\n * - A serverless/edge orchestrator (e.g. a Durable Object) instead serves the\n * SAME core from its own `fetch` handler — no raw TCP listener — see\n * {@link handleBridgeJsonRpc} and the Cloudflare example.\n */\nimport { createServer } from 'node:http'\nimport { randomBytes, timingSafeEqual } from 'node:crypto'\nimport { once } from 'node:events'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js'\nimport type { AddressInfo } from 'node:net'\nimport type { AnyTool } from '@tanstack/ai'\n\n/**\n * Name of the bridged MCP server. The agent sees tools as\n * `mcp__tanstack__<tool>`; each adapter's stream translator strips this prefix\n * so tool-call events match the names the application registered.\n */\nexport const BRIDGED_MCP_SERVER_NAME = 'tanstack'\n\n/** Hostname the sandbox uses to reach the bridge endpoint, per provider. */\nexport function hostForSandbox(provider: string): string {\n return provider === 'docker' || provider === 'sbx'\n ? 'host.docker.internal'\n : '127.0.0.1'\n}\n\n/** Result of a permission decision returned to the harness's prompt tool. */\nexport interface PermissionToolResult {\n behavior: 'allow' | 'deny'\n message?: string\n updatedInput?: unknown\n}\n\nexport interface BridgePermission {\n toolName: string\n resolve: (input: {\n tool_name?: string\n input?: unknown\n }) => PermissionToolResult | Promise<PermissionToolResult>\n}\n\nexport interface ToolBridgeCoreOptions {\n /** Runtime context forwarded to each tool's `execute()`. */\n context?: unknown\n /** Abort signal forwarded to each tool's `execute()`. */\n signal?: AbortSignal\n /**\n * Forwarded to each tool's `execute()` so a bridged tool can stream progress /\n * custom events back to the client mid-execution (e.g. code mode's\n * `code_mode:console` logs). Without it those events are silently dropped — the\n * bridge runs out-of-band from the main tool executor, so the executor's own\n * `emitCustomEvent` never reaches a bridged tool. The harness adapter supplies\n * one that injects a CUSTOM chunk into its live output stream.\n */\n emitCustomEvent?: (eventName: string, value: Record<string, unknown>) => void\n /**\n * Optional permission-prompt tool (e.g. for Claude Code's\n * `--permission-prompt-tool`). When set, the bridge exposes an extra MCP tool\n * `<name>` whose handler returns the orchestrator's allow/deny decision.\n */\n permission?: BridgePermission\n}\n\n/** An MCP tool descriptor as advertised to the in-sandbox agent. */\nexport interface ToolDescriptor {\n name: string\n description?: string\n inputSchema: { type: 'object'; [key: string]: unknown }\n}\n\n/**\n * Coerce a tool's `inputSchema` into the object-schema shape MCP advertises,\n * substituting an empty object schema when it isn't already a JSON-schema object\n * (project rule: a guard, not an `as` cast).\n */\nfunction toObjectSchema(schema: unknown): {\n type: 'object'\n [key: string]: unknown\n} {\n if (\n schema !== null &&\n typeof schema === 'object' &&\n 'type' in schema &&\n schema.type === 'object'\n ) {\n return { ...schema, type: 'object' }\n }\n return { type: 'object', properties: {} }\n}\n\n/** MCP `tools/call` result shape. */\nexport interface ToolCallResult {\n content: Array<{ type: 'text'; text: string }>\n isError?: boolean\n}\n\n/**\n * Transport-agnostic bridge logic: list tools, and dispatch a tool/permission\n * call. No sockets, no auth — a transport ({@link startHostToolBridge} or a\n * `fetch` handler) wraps this and owns I/O + the bearer check.\n */\nexport interface ToolBridgeCore {\n listTools: () => Array<ToolDescriptor>\n callTool: (name: string, args: unknown) => Promise<ToolCallResult>\n}\n\n/** Build the transport-agnostic bridge core for the given tools. */\nexport function createToolBridgeCore(\n tools: Array<AnyTool>,\n options: ToolBridgeCoreOptions = {},\n): ToolBridgeCore {\n const toolsByName = new Map(tools.map((tool) => [tool.name, tool]))\n const permission = options.permission\n\n const permissionDescriptor: ToolDescriptor | undefined = permission\n ? {\n name: permission.toolName,\n description:\n 'Permission prompt: returns {behavior:\"allow\"|\"deny\"} for a requested action.',\n inputSchema: { type: 'object', properties: {} },\n }\n : undefined\n\n return {\n listTools() {\n return [\n ...tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: toObjectSchema(tool.inputSchema),\n })),\n ...(permissionDescriptor ? [permissionDescriptor] : []),\n ]\n },\n\n async callTool(name, args) {\n if (permission && name === permission.toolName) {\n const result = await permission.resolve(args ?? {})\n return { content: [{ type: 'text', text: JSON.stringify(result) }] }\n }\n const tool = toolsByName.get(name)\n if (!tool?.execute) throw new Error(`Unknown tool: ${name}`)\n try {\n const result: unknown = await tool.execute(args ?? {}, {\n context: options.context,\n abortSignal: options.signal,\n // No-op default so tools that always call it (e.g. code mode) don't\n // crash when the transport didn't wire a sink.\n emitCustomEvent: options.emitCustomEvent ?? (() => {}),\n })\n const text =\n typeof result === 'string' ? result : JSON.stringify(result)\n return { content: [{ type: 'text', text }] }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n isError: true,\n content: [\n { type: 'text', text: `Tool execution failed: ${message}` },\n ],\n }\n }\n },\n }\n}\n\n/**\n * Minimal JSON-RPC dispatcher over a {@link ToolBridgeCore}, so a `fetch`-based\n * transport (Worker / Durable Object) can serve MCP `initialize` / `tools/list`\n * / `tools/call` without the node-specific HTTP transport. Returns the JSON-RPC\n * response object, or `null` for a notification (no `id`).\n */\nexport async function handleBridgeJsonRpc(\n core: ToolBridgeCore,\n message: unknown,\n): Promise<unknown> {\n if (message === null || typeof message !== 'object') {\n return {\n jsonrpc: '2.0',\n id: null,\n error: { code: -32600, message: 'Invalid Request' },\n }\n }\n const rpc = message as { id?: unknown; method?: unknown; params?: unknown }\n const id = rpc.id ?? null\n const respond = (result: unknown): unknown => ({ jsonrpc: '2.0', id, result })\n switch (rpc.method) {\n case 'initialize':\n return respond({\n protocolVersion: '2024-11-05',\n capabilities: { tools: {} },\n serverInfo: { name: BRIDGED_MCP_SERVER_NAME, version: '1.0.0' },\n })\n case 'notifications/initialized':\n return null\n case 'tools/list':\n return respond({ tools: core.listTools() })\n case 'tools/call': {\n const params = (rpc.params ?? {}) as {\n name?: unknown\n arguments?: unknown\n }\n if (typeof params.name !== 'string') {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: -32602, message: 'Invalid params: name' },\n }\n }\n return respond(await core.callTool(params.name, params.arguments ?? {}))\n }\n default:\n return {\n jsonrpc: '2.0',\n id,\n error: { code: -32601, message: 'Method not found' },\n }\n }\n}\n\n/**\n * Constant-time check of an `Authorization: Bearer <token>` header against the\n * expected token. Length mismatch returns false early (token length is not\n * secret); equal-length comparison is timing-safe.\n */\nexport function timingSafeBearerEqual(\n header: string | undefined,\n token: string,\n): boolean {\n if (header === undefined) return false\n const a = Buffer.from(header)\n const b = Buffer.from(`Bearer ${token}`)\n if (a.length !== b.length) return false\n return timingSafeEqual(a, b)\n}\n\nexport interface HostToolBridge {\n /** MCP server name; tools appear to the agent as `mcp__<name>__<tool>`. */\n name: string\n /** URL the SANDBOX uses to reach this bridge. */\n url: string\n /** Per-run bearer token gating the endpoint. */\n token: string\n close: () => Promise<void>\n}\n\nexport interface StartBridgeOptions extends ToolBridgeCoreOptions {\n /** Hostname the sandbox uses to reach the host (e.g. `host.docker.internal`). */\n hostForSandbox: string\n /**\n * Address to bind the listener to. Defaults to `127.0.0.1` (loopback) and is\n * widened to `0.0.0.0` only when the sandbox reaches the host via\n * `host.docker.internal` (a container can't reach the host's loopback).\n */\n bindAddress?: string\n}\n\nfunction buildMcpServer(core: ToolBridgeCore): McpServer {\n const server = new McpServer(\n { name: BRIDGED_MCP_SERVER_NAME, version: '1.0.0' },\n { capabilities: { tools: {} } },\n )\n server.server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: core.listTools(),\n }))\n server.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const result = await core.callTool(\n request.params.name,\n request.params.arguments ?? {},\n )\n return {\n content: result.content,\n ...(result.isError ? { isError: true } : {}),\n }\n })\n return server\n}\n\n/**\n * Start the `node:http` MCP tool-proxy bridge for the given tools. For a\n * long-running host (laptop / CI / Docker orchestrator). Serverless/edge\n * orchestrators serve {@link createToolBridgeCore} from their own `fetch`\n * handler instead.\n */\nexport async function startHostToolBridge(\n tools: Array<AnyTool>,\n options: StartBridgeOptions,\n): Promise<HostToolBridge> {\n const token = randomBytes(24).toString('hex')\n const core = createToolBridgeCore(tools, options)\n // Loopback by default; widen to all interfaces only for the Docker bridge,\n // which a container reaches via host.docker.internal (host gateway).\n const bindAddress =\n options.bindAddress ??\n (options.hostForSandbox === 'host.docker.internal'\n ? '0.0.0.0'\n : '127.0.0.1')\n\n const httpServer = createServer((req, res) => {\n void (async () => {\n if (!timingSafeBearerEqual(req.headers['authorization'], token)) {\n res.writeHead(401).end('unauthorized')\n return\n }\n const server = buildMcpServer(core)\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n })\n res.on('close', () => {\n void transport.close()\n void server.close()\n })\n await server.connect(transport)\n\n let body = ''\n for await (const chunk of req) body += chunk\n let parsed: unknown\n try {\n parsed = body ? JSON.parse(body) : undefined\n } catch {\n // Malformed agent request → 400, distinct from an internal 500.\n if (!res.headersSent) res.writeHead(400).end('invalid JSON body')\n return\n }\n await transport.handleRequest(req, res, parsed)\n })().catch((error: unknown) => {\n // Log the underlying fault — on the host/Docker path there is no run-log\n // capturing it, so swallowing it leaves an operator with nothing.\n console.error('[tool-bridge] request handler failed:', error)\n if (!res.headersSent) res.writeHead(500).end('bridge error')\n })\n })\n\n await once(httpServer.listen(0, bindAddress), 'listening')\n const port = (httpServer.address() as AddressInfo).port\n const url = `http://${options.hostForSandbox}:${port}/mcp`\n\n return {\n name: BRIDGED_MCP_SERVER_NAME,\n url,\n token,\n close: () =>\n new Promise<void>((resolve) => httpServer.close(() => resolve())),\n }\n}\n\n/** A provisioned, reachable bridge endpoint (same shape as {@link HostToolBridge}). */\nexport type ProvisionedBridge = HostToolBridge\n\nexport interface ToolBridgeProvisionOptions extends ToolBridgeCoreOptions {\n /** Sandbox provider name, to derive how the sandbox reaches the bridge. */\n provider: string\n}\n\n/**\n * Stands up the tool-bridge endpoint for a run. The seam that makes the bridge\n * portable across runtimes: a harness adapter asks its capability context for a\n * provisioner and uses {@link nodeHttpBridgeProvisioner} as the default (host /\n * Docker). A serverless/edge orchestrator PROVIDES its own — e.g. a Durable\n * Object that mounts {@link createToolBridgeCore} / {@link handleBridgeJsonRpc}\n * on its `fetch` handler and returns a sandbox-reachable URL — so no raw TCP\n * listener is needed.\n */\nexport interface ToolBridgeProvisioner {\n provision: (\n tools: Array<AnyTool>,\n options: ToolBridgeProvisionOptions,\n ) => Promise<ProvisionedBridge>\n}\n\n/** Default provisioner: a `node:http` listener on the host. */\nexport const nodeHttpBridgeProvisioner: ToolBridgeProvisioner = {\n provision(tools, options) {\n const { provider, ...core } = options\n return startHostToolBridge(tools, {\n hostForSandbox: hostForSandbox(provider),\n ...core,\n })\n },\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-bridge.js","names":[],"sources":["../../src/tool-bridge.ts"],"sourcesContent":["/**\n * MCP tool-proxy bridge, shared by all harness adapters.\n *\n * Exposes chat()-provided server tools to an in-sandbox agent as an MCP server.\n * The agent (inside the sandbox) calls `mcp__tanstack__<tool>`; the call is\n * proxied OUT to a bridge endpoint, where the tool's `execute()` runs in the\n * orchestrator process (with its closures / DB / secrets), and the result is\n * returned into the sandbox.\n *\n * The bridge is split into a transport-agnostic CORE and a TRANSPORT:\n * - {@link createToolBridgeCore} owns tool dispatch + the permission resolver\n * (no I/O). It is what makes the bridge portable.\n * - {@link startHostToolBridge} is the `node:http` transport for a long-running\n * host (laptop / CI / Docker orchestrator). It binds loopback unless the\n * sandbox must reach it via `host.docker.internal`, and authenticates with a\n * constant-time bearer check.\n * - A serverless/edge orchestrator (e.g. a Durable Object) instead serves the\n * SAME core from its own `fetch` handler — no raw TCP listener — see\n * {@link handleBridgeJsonRpc} and the Cloudflare example.\n */\nimport { createServer } from 'node:http'\nimport { randomBytes, timingSafeEqual } from 'node:crypto'\nimport { once } from 'node:events'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js'\nimport type { AddressInfo } from 'node:net'\nimport type { AnyTool, EmitCustomEventOptions } from '@tanstack/ai'\n\n/**\n * Name of the bridged MCP server. The agent sees tools as\n * `mcp__tanstack__<tool>`; each adapter's stream translator strips this prefix\n * so tool-call events match the names the application registered.\n */\nexport const BRIDGED_MCP_SERVER_NAME = 'tanstack'\n\n/** Hostname the sandbox uses to reach the bridge endpoint, per provider. */\nexport function hostForSandbox(provider: string): string {\n return provider === 'docker' || provider === 'sbx'\n ? 'host.docker.internal'\n : '127.0.0.1'\n}\n\n/** Result of a permission decision returned to the harness's prompt tool. */\nexport interface PermissionToolResult {\n behavior: 'allow' | 'deny'\n message?: string\n updatedInput?: unknown\n}\n\nexport interface BridgePermission {\n toolName: string\n resolve: (input: {\n tool_name?: string\n input?: unknown\n }) => PermissionToolResult | Promise<PermissionToolResult>\n}\n\nexport interface ToolBridgeCoreOptions {\n /** Runtime context forwarded to each tool's `execute()`. */\n context?: unknown\n /** Abort signal forwarded to each tool's `execute()`. */\n signal?: AbortSignal\n /**\n * Forwarded to each tool's `execute()` so a bridged tool can stream progress /\n * custom events back to the client mid-execution (e.g. code mode's\n * `code_mode:console` logs). Without it those events are silently dropped — the\n * bridge runs out-of-band from the main tool executor, so the executor's own\n * `emitCustomEvent` never reaches a bridged tool. The harness adapter supplies\n * one that injects a CUSTOM chunk into its live output stream.\n */\n emitCustomEvent?: (\n eventName: string,\n value: Record<string, unknown>,\n options?: EmitCustomEventOptions,\n ) => void\n /**\n * Optional permission-prompt tool (e.g. for Claude Code's\n * `--permission-prompt-tool`). When set, the bridge exposes an extra MCP tool\n * `<name>` whose handler returns the orchestrator's allow/deny decision.\n */\n permission?: BridgePermission\n}\n\n/** An MCP tool descriptor as advertised to the in-sandbox agent. */\nexport interface ToolDescriptor {\n name: string\n description?: string\n inputSchema: { type: 'object'; [key: string]: unknown }\n}\n\n/**\n * Coerce a tool's `inputSchema` into the object-schema shape MCP advertises,\n * substituting an empty object schema when it isn't already a JSON-schema object\n * (project rule: a guard, not an `as` cast).\n */\nfunction toObjectSchema(schema: unknown): {\n type: 'object'\n [key: string]: unknown\n} {\n if (\n schema !== null &&\n typeof schema === 'object' &&\n 'type' in schema &&\n schema.type === 'object'\n ) {\n return { ...schema, type: 'object' }\n }\n return { type: 'object', properties: {} }\n}\n\n/** MCP `tools/call` result shape. */\nexport interface ToolCallResult {\n content: Array<{ type: 'text'; text: string }>\n isError?: boolean\n}\n\n/**\n * Transport-agnostic bridge logic: list tools, and dispatch a tool/permission\n * call. No sockets, no auth — a transport ({@link startHostToolBridge} or a\n * `fetch` handler) wraps this and owns I/O + the bearer check.\n */\nexport interface ToolBridgeCore {\n listTools: () => Array<ToolDescriptor>\n callTool: (name: string, args: unknown) => Promise<ToolCallResult>\n}\n\n/** Build the transport-agnostic bridge core for the given tools. */\nexport function createToolBridgeCore(\n tools: Array<AnyTool>,\n options: ToolBridgeCoreOptions = {},\n): ToolBridgeCore {\n const toolsByName = new Map(tools.map((tool) => [tool.name, tool]))\n const permission = options.permission\n\n const permissionDescriptor: ToolDescriptor | undefined = permission\n ? {\n name: permission.toolName,\n description:\n 'Permission prompt: returns {behavior:\"allow\"|\"deny\"} for a requested action.',\n inputSchema: { type: 'object', properties: {} },\n }\n : undefined\n\n return {\n listTools() {\n return [\n ...tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: toObjectSchema(tool.inputSchema),\n })),\n ...(permissionDescriptor ? [permissionDescriptor] : []),\n ]\n },\n\n async callTool(name, args) {\n if (permission && name === permission.toolName) {\n const result = await permission.resolve(args ?? {})\n return { content: [{ type: 'text', text: JSON.stringify(result) }] }\n }\n const tool = toolsByName.get(name)\n if (!tool?.execute) throw new Error(`Unknown tool: ${name}`)\n try {\n const result: unknown = await tool.execute(args ?? {}, {\n context: options.context,\n abortSignal: options.signal,\n // No-op default so tools that always call it (e.g. code mode) don't\n // crash when the transport didn't wire a sink.\n emitCustomEvent: options.emitCustomEvent ?? (() => {}),\n })\n const text =\n typeof result === 'string' ? result : JSON.stringify(result)\n return { content: [{ type: 'text', text }] }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n isError: true,\n content: [\n { type: 'text', text: `Tool execution failed: ${message}` },\n ],\n }\n }\n },\n }\n}\n\n/**\n * Minimal JSON-RPC dispatcher over a {@link ToolBridgeCore}, so a `fetch`-based\n * transport (Worker / Durable Object) can serve MCP `initialize` / `tools/list`\n * / `tools/call` without the node-specific HTTP transport. Returns the JSON-RPC\n * response object, or `null` for a notification (no `id`).\n */\nexport async function handleBridgeJsonRpc(\n core: ToolBridgeCore,\n message: unknown,\n): Promise<unknown> {\n if (message === null || typeof message !== 'object') {\n return {\n jsonrpc: '2.0',\n id: null,\n error: { code: -32600, message: 'Invalid Request' },\n }\n }\n const rpc = message as { id?: unknown; method?: unknown; params?: unknown }\n const id = rpc.id ?? null\n const respond = (result: unknown): unknown => ({ jsonrpc: '2.0', id, result })\n switch (rpc.method) {\n case 'initialize':\n return respond({\n protocolVersion: '2024-11-05',\n capabilities: { tools: {} },\n serverInfo: { name: BRIDGED_MCP_SERVER_NAME, version: '1.0.0' },\n })\n case 'notifications/initialized':\n return null\n case 'tools/list':\n return respond({ tools: core.listTools() })\n case 'tools/call': {\n const params = (rpc.params ?? {}) as {\n name?: unknown\n arguments?: unknown\n }\n if (typeof params.name !== 'string') {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: -32602, message: 'Invalid params: name' },\n }\n }\n return respond(await core.callTool(params.name, params.arguments ?? {}))\n }\n default:\n return {\n jsonrpc: '2.0',\n id,\n error: { code: -32601, message: 'Method not found' },\n }\n }\n}\n\n/**\n * Constant-time check of an `Authorization: Bearer <token>` header against the\n * expected token. Length mismatch returns false early (token length is not\n * secret); equal-length comparison is timing-safe.\n */\nexport function timingSafeBearerEqual(\n header: string | undefined,\n token: string,\n): boolean {\n if (header === undefined) return false\n const a = Buffer.from(header)\n const b = Buffer.from(`Bearer ${token}`)\n if (a.length !== b.length) return false\n return timingSafeEqual(a, b)\n}\n\nexport interface HostToolBridge {\n /** MCP server name; tools appear to the agent as `mcp__<name>__<tool>`. */\n name: string\n /** URL the SANDBOX uses to reach this bridge. */\n url: string\n /** Per-run bearer token gating the endpoint. */\n token: string\n close: () => Promise<void>\n}\n\nexport interface StartBridgeOptions extends ToolBridgeCoreOptions {\n /** Hostname the sandbox uses to reach the host (e.g. `host.docker.internal`). */\n hostForSandbox: string\n /**\n * Address to bind the listener to. Defaults to `127.0.0.1` (loopback) and is\n * widened to `0.0.0.0` only when the sandbox reaches the host via\n * `host.docker.internal` (a container can't reach the host's loopback).\n */\n bindAddress?: string\n}\n\nfunction buildMcpServer(core: ToolBridgeCore): McpServer {\n const server = new McpServer(\n { name: BRIDGED_MCP_SERVER_NAME, version: '1.0.0' },\n { capabilities: { tools: {} } },\n )\n server.server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: core.listTools(),\n }))\n server.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const result = await core.callTool(\n request.params.name,\n request.params.arguments ?? {},\n )\n return {\n content: result.content,\n ...(result.isError ? { isError: true } : {}),\n }\n })\n return server\n}\n\n/**\n * Start the `node:http` MCP tool-proxy bridge for the given tools. For a\n * long-running host (laptop / CI / Docker orchestrator). Serverless/edge\n * orchestrators serve {@link createToolBridgeCore} from their own `fetch`\n * handler instead.\n */\nexport async function startHostToolBridge(\n tools: Array<AnyTool>,\n options: StartBridgeOptions,\n): Promise<HostToolBridge> {\n const token = randomBytes(24).toString('hex')\n const core = createToolBridgeCore(tools, options)\n // Loopback by default; widen to all interfaces only for the Docker bridge,\n // which a container reaches via host.docker.internal (host gateway).\n const bindAddress =\n options.bindAddress ??\n (options.hostForSandbox === 'host.docker.internal'\n ? '0.0.0.0'\n : '127.0.0.1')\n\n const httpServer = createServer((req, res) => {\n void (async () => {\n if (!timingSafeBearerEqual(req.headers['authorization'], token)) {\n res.writeHead(401).end('unauthorized')\n return\n }\n const server = buildMcpServer(core)\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n })\n res.on('close', () => {\n void transport.close()\n void server.close()\n })\n await server.connect(transport)\n\n let body = ''\n for await (const chunk of req) body += chunk\n let parsed: unknown\n try {\n parsed = body ? JSON.parse(body) : undefined\n } catch {\n // Malformed agent request → 400, distinct from an internal 500.\n if (!res.headersSent) res.writeHead(400).end('invalid JSON body')\n return\n }\n await transport.handleRequest(req, res, parsed)\n })().catch((error: unknown) => {\n // Log the underlying fault — on the host/Docker path there is no run-log\n // capturing it, so swallowing it leaves an operator with nothing.\n console.error('[tool-bridge] request handler failed:', error)\n if (!res.headersSent) res.writeHead(500).end('bridge error')\n })\n })\n\n await once(httpServer.listen(0, bindAddress), 'listening')\n const port = (httpServer.address() as AddressInfo).port\n const url = `http://${options.hostForSandbox}:${port}/mcp`\n\n return {\n name: BRIDGED_MCP_SERVER_NAME,\n url,\n token,\n close: () =>\n new Promise<void>((resolve) => httpServer.close(() => resolve())),\n }\n}\n\n/** A provisioned, reachable bridge endpoint (same shape as {@link HostToolBridge}). */\nexport type ProvisionedBridge = HostToolBridge\n\nexport interface ToolBridgeProvisionOptions extends ToolBridgeCoreOptions {\n /** Sandbox provider name, to derive how the sandbox reaches the bridge. */\n provider: string\n}\n\n/**\n * Stands up the tool-bridge endpoint for a run. The seam that makes the bridge\n * portable across runtimes: a harness adapter asks its capability context for a\n * provisioner and uses {@link nodeHttpBridgeProvisioner} as the default (host /\n * Docker). A serverless/edge orchestrator PROVIDES its own — e.g. a Durable\n * Object that mounts {@link createToolBridgeCore} / {@link handleBridgeJsonRpc}\n * on its `fetch` handler and returns a sandbox-reachable URL — so no raw TCP\n * listener is needed.\n */\nexport interface ToolBridgeProvisioner {\n provision: (\n tools: Array<AnyTool>,\n options: ToolBridgeProvisionOptions,\n ) => Promise<ProvisionedBridge>\n}\n\n/** Default provisioner: a `node:http` listener on the host. */\nexport const nodeHttpBridgeProvisioner: ToolBridgeProvisioner = {\n provision(tools, options) {\n const { provider, ...core } = options\n return startHostToolBridge(tools, {\n hostForSandbox: hostForSandbox(provider),\n ...core,\n })\n },\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-sandbox",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.13",
|
|
4
4
|
"description": "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.",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,13 +56,13 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
59
|
-
"@tanstack/ai-skills": "^0.1.
|
|
59
|
+
"@tanstack/ai-skills": "^0.1.8"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@ngrok/ngrok": "^1.0.0",
|
|
63
63
|
"vitest": "^4.1.10",
|
|
64
|
-
"@tanstack/ai": "^0.
|
|
65
|
-
"@tanstack/ai-persistence": "^0.6.
|
|
64
|
+
"@tanstack/ai": "^0.59.0",
|
|
65
|
+
"@tanstack/ai-persistence": "^0.6.5"
|
|
66
66
|
},
|
|
67
67
|
"peerDependenciesMeta": {
|
|
68
68
|
"@ngrok/ngrok": {
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"@ngrok/ngrok": "^1.7.0",
|
|
80
80
|
"@vitest/coverage-v8": "4.1.10",
|
|
81
81
|
"vitest": "^4.1.10",
|
|
82
|
-
"@tanstack/ai": "0.
|
|
83
|
-
"@tanstack/ai-persistence": "0.6.
|
|
82
|
+
"@tanstack/ai": "0.59.0",
|
|
83
|
+
"@tanstack/ai-persistence": "0.6.5"
|
|
84
84
|
},
|
|
85
85
|
"scripts": {
|
|
86
86
|
"build": "vite build",
|
package/src/bridge-events.ts
CHANGED
|
@@ -11,11 +11,15 @@
|
|
|
11
11
|
* runs (e.g. code mode's `code_mode:console` logs during a long execution).
|
|
12
12
|
*/
|
|
13
13
|
import { EventType, withTanstackMetadata } from '@tanstack/ai'
|
|
14
|
-
import type { StreamChunk } from '@tanstack/ai'
|
|
14
|
+
import type { EmitCustomEventOptions, StreamChunk } from '@tanstack/ai'
|
|
15
15
|
|
|
16
16
|
export interface BridgeEventChannel {
|
|
17
17
|
/** Pass as the bridge's `emitCustomEvent`; buffers a CUSTOM chunk for the stream. */
|
|
18
|
-
emitCustomEvent: (
|
|
18
|
+
emitCustomEvent: (
|
|
19
|
+
eventName: string,
|
|
20
|
+
value: Record<string, unknown>,
|
|
21
|
+
options?: EmitCustomEventOptions,
|
|
22
|
+
) => void
|
|
19
23
|
/** Live CUSTOM-chunk stream; ends after {@link close} once drained. */
|
|
20
24
|
stream: AsyncIterable<StreamChunk>
|
|
21
25
|
/** Stop the stream (call when the run's main output is done). */
|
|
@@ -48,7 +52,7 @@ export function createBridgeEventChannel(meta: {
|
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
return {
|
|
51
|
-
emitCustomEvent(eventName, value) {
|
|
55
|
+
emitCustomEvent(eventName, value, options) {
|
|
52
56
|
if (closed) return
|
|
53
57
|
buffer.push(
|
|
54
58
|
withTanstackMetadata(
|
|
@@ -62,6 +66,7 @@ export function createBridgeEventChannel(meta: {
|
|
|
62
66
|
model: meta.model,
|
|
63
67
|
...(meta.threadId !== undefined ? { threadId: meta.threadId } : {}),
|
|
64
68
|
...(meta.runId !== undefined ? { runId: meta.runId } : {}),
|
|
69
|
+
...(options?.batch === true ? { batch: true } : {}),
|
|
65
70
|
},
|
|
66
71
|
) as StreamChunk,
|
|
67
72
|
)
|
package/src/memory-snapshots.ts
CHANGED
|
@@ -682,6 +682,9 @@ async function createMemorySandboxSnapshots(): Promise<{
|
|
|
682
682
|
threadId: string
|
|
683
683
|
status?: RunRecord['status']
|
|
684
684
|
startedAt: number
|
|
685
|
+
parentRunId?: string
|
|
686
|
+
subagentRunId?: string
|
|
687
|
+
name?: string
|
|
685
688
|
}) => {
|
|
686
689
|
const existing = runs.get(input.runId)
|
|
687
690
|
if (existing) return existing
|
|
@@ -707,6 +710,10 @@ async function createMemorySandboxSnapshots(): Promise<{
|
|
|
707
710
|
[...runs.values()]
|
|
708
711
|
.filter((run) => run.threadId === threadId)
|
|
709
712
|
.sort((a, b) => a.startedAt - b.startedAt),
|
|
713
|
+
listByParentRun: async (parentRunId: string) =>
|
|
714
|
+
[...runs.values()]
|
|
715
|
+
.filter((run) => run.parentRunId === parentRunId)
|
|
716
|
+
.sort((a, b) => a.startedAt - b.startedAt),
|
|
710
717
|
listReclaimable: async (input: { now: number; ttlMs: number }) =>
|
|
711
718
|
[...runs.values()].filter(
|
|
712
719
|
(run) =>
|
package/src/tool-bridge.ts
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
ListToolsRequestSchema,
|
|
29
29
|
} from '@modelcontextprotocol/sdk/types.js'
|
|
30
30
|
import type { AddressInfo } from 'node:net'
|
|
31
|
-
import type { AnyTool } from '@tanstack/ai'
|
|
31
|
+
import type { AnyTool, EmitCustomEventOptions } from '@tanstack/ai'
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Name of the bridged MCP server. The agent sees tools as
|
|
@@ -72,7 +72,11 @@ export interface ToolBridgeCoreOptions {
|
|
|
72
72
|
* `emitCustomEvent` never reaches a bridged tool. The harness adapter supplies
|
|
73
73
|
* one that injects a CUSTOM chunk into its live output stream.
|
|
74
74
|
*/
|
|
75
|
-
emitCustomEvent?: (
|
|
75
|
+
emitCustomEvent?: (
|
|
76
|
+
eventName: string,
|
|
77
|
+
value: Record<string, unknown>,
|
|
78
|
+
options?: EmitCustomEventOptions,
|
|
79
|
+
) => void
|
|
76
80
|
/**
|
|
77
81
|
* Optional permission-prompt tool (e.g. for Claude Code's
|
|
78
82
|
* `--permission-prompt-tool`). When set, the bridge exposes an extra MCP tool
|