@tanstack/ai-sandbox
Version:
Provider-agnostic sandbox layer for TanStack AI — run harness adapters inside isolated sandboxes (defineSandbox, defineWorkspace, withSandbox) with a uniform SandboxHandle, workspace bootstrap, policy, and resumable lifecycle.
191 lines (190 loc) • 8.04 kB
JavaScript
import { InMemorySandboxCheckpointStore } from "../checkpoint-store.js";
import { describe, expect, it } from "vitest";
//#region src/testkit/checkpoint-fork-conformance.ts
function sourceCheckpoint() {
return {
id: "source-root",
threadId: "source",
parentCheckpointId: null,
createdAt: 10,
reason: "named",
label: "source label",
sourceRunId: "run-1",
files: [{
path: "a.txt",
kind: "file",
blobKey: `sandbox-files/sha256/${"a".repeat(64)}`,
size: 1
}],
conversation: [{
role: "user",
content: "checkpoint conversation"
}],
artifacts: []
};
}
async function appendSource(checkpoints) {
const sourceWriter = await checkpoints.acquireWriter("source");
const source = sourceCheckpoint();
await checkpoints.append({
checkpoint: source,
expectedHeadId: null,
writer: sourceWriter
});
return {
source,
sourceWriter
};
}
function forkInput(writer, overrides = {}) {
return {
sourceThreadId: "source",
sourceCheckpointId: "source-root",
destinationThreadId: "destination",
destinationCheckpointId: "fork-root",
createdAt: 20,
writer,
...overrides
};
}
async function destinationState(persistence, checkpoints) {
return {
transcript: await persistence.stores.messages.loadThread("destination"),
head: await checkpoints.getHead("destination"),
checkpoints: await checkpoints.list("destination"),
references: await checkpoints.listBlobReferences()
};
}
async function expectRejectedWithoutDestinationChanges(persistence, checkpoints, operation, code) {
const before = await destinationState(persistence, checkpoints);
await expect(operation).rejects.toMatchObject({ code });
expect(await destinationState(persistence, checkpoints)).toEqual(before);
}
function runSandboxCheckpointForkConformance(name, makeSnapshots) {
describe(`Sandbox checkpoint fork conformance: ${name}`, () => {
it("copies a selected historical checkpoint and creates an exact fork root", async () => {
const { persistence, checkpoints } = await makeSnapshots();
const { source, sourceWriter } = await appendSource(checkpoints);
await checkpoints.append({
checkpoint: {
id: "source-head",
threadId: "source",
parentCheckpointId: source.id,
createdAt: 11,
reason: "automatic",
files: [],
conversation: [{
role: "user",
content: "newer conversation"
}],
artifacts: []
},
expectedHeadId: source.id,
writer: sourceWriter
});
await persistence.stores.messages.saveThread("source", [{
role: "user",
content: "current source message"
}]);
const destinationWriter = await checkpoints.acquireWriter("destination");
const result = await checkpoints.forkFromCheckpoint(forkInput(destinationWriter));
expect(result.checkpoint).toEqual({
id: "fork-root",
threadId: "destination",
parentCheckpointId: null,
createdAt: 20,
reason: "fork-root",
files: source.files,
conversation: source.conversation,
artifacts: []
});
expect(await persistence.stores.messages.loadThread("destination")).toEqual(source.conversation);
expect(await checkpoints.getHead("source")).toBe("source-head");
});
it("rejects a plain checkpoint store because it has no fork capability", () => {
const store = new InMemorySandboxCheckpointStore();
expect("forkFromCheckpoint" in store).toBe(false);
});
it("rejects a missing source without changing the destination", async () => {
const { persistence, checkpoints } = await makeSnapshots();
const writer = await checkpoints.acquireWriter("destination");
await expectRejectedWithoutDestinationChanges(persistence, checkpoints, checkpoints.forkFromCheckpoint(forkInput(writer, { sourceCheckpointId: "missing" })), "SANDBOX_SNAPSHOT_FORK_SOURCE_NOT_FOUND");
});
it("rejects a source thread mismatch without changing the destination", async () => {
const { persistence, checkpoints } = await makeSnapshots();
await appendSource(checkpoints);
const writer = await checkpoints.acquireWriter("destination");
await expectRejectedWithoutDestinationChanges(persistence, checkpoints, checkpoints.forkFromCheckpoint(forkInput(writer, { sourceThreadId: "another-source" })), "SANDBOX_SNAPSHOT_FORK_SOURCE_THREAD_MISMATCH");
});
it("rejects equal source and destination threads without changing the source", async () => {
const { persistence, checkpoints } = await makeSnapshots();
const { source, sourceWriter } = await appendSource(checkpoints);
const before = {
transcript: await persistence.stores.messages.loadThread("source"),
head: await checkpoints.getHead("source"),
checkpoints: await checkpoints.list("source"),
references: await checkpoints.listBlobReferences()
};
await expect(checkpoints.forkFromCheckpoint(forkInput(sourceWriter, { destinationThreadId: source.threadId }))).rejects.toMatchObject({ code: "SANDBOX_SNAPSHOT_FORK_SOURCE_THREAD_MISMATCH" });
expect({
transcript: await persistence.stores.messages.loadThread("source"),
head: await checkpoints.getHead("source"),
checkpoints: await checkpoints.list("source"),
references: await checkpoints.listBlobReferences()
}).toEqual(before);
});
it("rejects a missing destination writer without changing the destination", async () => {
const { persistence, checkpoints } = await makeSnapshots();
await appendSource(checkpoints);
await expectRejectedWithoutDestinationChanges(persistence, checkpoints, checkpoints.forkFromCheckpoint(forkInput({
threadId: "destination",
ownerToken: "missing-owner",
fence: 1
})), "SANDBOX_SNAPSHOT_WRITER_LOST");
});
it("rejects a wrong destination writer without changing the destination", async () => {
const { persistence, checkpoints } = await makeSnapshots();
await appendSource(checkpoints);
const wrongWriter = await checkpoints.acquireWriter("another-thread");
await expectRejectedWithoutDestinationChanges(persistence, checkpoints, checkpoints.forkFromCheckpoint(forkInput(wrongWriter)), "SANDBOX_SNAPSHOT_WRITER_LOST");
});
it("rejects a stale destination writer without changing the destination", async () => {
const { persistence, checkpoints } = await makeSnapshots();
await appendSource(checkpoints);
const staleWriter = await checkpoints.acquireWriter("destination");
await staleWriter.release();
await expectRejectedWithoutDestinationChanges(persistence, checkpoints, checkpoints.forkFromCheckpoint(forkInput(staleWriter)), "SANDBOX_SNAPSHOT_WRITER_LOST");
});
it("rejects a destination transcript without changing its checkpoint state", async () => {
const { persistence, checkpoints } = await makeSnapshots();
await appendSource(checkpoints);
await persistence.stores.messages.saveThread("destination", [{
role: "user",
content: "already here"
}]);
const writer = await checkpoints.acquireWriter("destination");
await expectRejectedWithoutDestinationChanges(persistence, checkpoints, checkpoints.forkFromCheckpoint(forkInput(writer)), "SANDBOX_SNAPSHOT_FORK_DESTINATION_NOT_EMPTY");
});
it("rejects an orphaned checkpoint id without changing the destination", async () => {
const { persistence, checkpoints } = await makeSnapshots();
const { source } = await appendSource(checkpoints);
const orphanWriter = await checkpoints.acquireWriter("orphan-thread");
await checkpoints.append({
checkpoint: {
...source,
id: "orphaned-id",
threadId: "orphan-thread",
parentCheckpointId: null,
createdAt: 11
},
expectedHeadId: null,
writer: orphanWriter
});
const destinationWriter = await checkpoints.acquireWriter("destination");
await expectRejectedWithoutDestinationChanges(persistence, checkpoints, checkpoints.forkFromCheckpoint(forkInput(destinationWriter, { destinationCheckpointId: "orphaned-id" })), "SANDBOX_SNAPSHOT_FORK_DESTINATION_NOT_EMPTY");
});
});
}
//#endregion
export { runSandboxCheckpointForkConformance };
//# sourceMappingURL=checkpoint-fork-conformance.js.map