@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.
267 lines (266 loc) • 14.8 kB
JavaScript
//#region src/checkpoint-store.ts
var utf8Encoder = new TextEncoder();
var compareUtf8Bytes = (left, right) => {
const leftBytes = utf8Encoder.encode(left);
const rightBytes = utf8Encoder.encode(right);
const length = Math.min(leftBytes.length, rightBytes.length);
for (let index = 0; index < length; index++) {
const leftByte = leftBytes[index];
const rightByte = rightBytes[index];
if (leftByte !== rightByte) return (leftByte ?? 0) - (rightByte ?? 0);
}
return leftBytes.length - rightBytes.length;
};
function isForkCapableSandboxCheckpointStore(store) {
return typeof store.forkFromCheckpoint === "function";
}
var SandboxCheckpointError = class extends Error {
code;
constructor(code, message) {
super(message);
this.name = "SandboxCheckpointError";
this.code = code;
}
};
var SandboxCheckpointConflictError = class extends SandboxCheckpointError {
constructor(message) {
super("SANDBOX_SNAPSHOT_STALE_HEAD", message);
this.name = "SandboxCheckpointConflictError";
}
};
var SandboxCheckpointDuplicateIdError = class extends SandboxCheckpointError {
constructor(message) {
super("SANDBOX_SNAPSHOT_DUPLICATE_ID", message);
this.name = "SandboxCheckpointDuplicateIdError";
}
};
var SandboxCheckpointInvalidIdError = class extends SandboxCheckpointError {
constructor(message) {
super("SANDBOX_SNAPSHOT_INVALID_ID", message);
this.name = "SandboxCheckpointInvalidIdError";
}
};
var SandboxCheckpointInvalidEntryError = class extends SandboxCheckpointError {
constructor(message) {
super("SANDBOX_SNAPSHOT_INVALID_ENTRY", message);
this.name = "SandboxCheckpointInvalidEntryError";
}
};
var SandboxCheckpointParentMismatchError = class extends SandboxCheckpointError {
constructor(message) {
super("SANDBOX_SNAPSHOT_PARENT_MISMATCH", message);
this.name = "SandboxCheckpointParentMismatchError";
}
};
var SandboxCheckpointNotHeadError = class extends SandboxCheckpointError {
constructor(message) {
super("SANDBOX_SNAPSHOT_NOT_HEAD", message);
this.name = "SandboxCheckpointNotHeadError";
}
};
var SandboxCheckpointWriterConflictError = class extends SandboxCheckpointError {
constructor(message) {
super("SANDBOX_SNAPSHOT_WRITER_CONFLICT", message);
this.name = "SandboxCheckpointWriterConflictError";
}
};
var SandboxCheckpointWriterLostError = class extends SandboxCheckpointError {
constructor(message) {
super("SANDBOX_SNAPSHOT_WRITER_LOST", message);
this.name = "SandboxCheckpointWriterLostError";
}
};
function defineSandboxCheckpointStore(store) {
return store;
}
function copy(value) {
return structuredClone(value);
}
function blobKeys(checkpoint) {
const keys = /* @__PURE__ */ new Set();
for (const entry of checkpoint.files) if (entry.kind === "file") keys.add(entry.blobKey);
for (const artifact of checkpoint.artifacts) keys.add(artifact.blobKey);
return keys;
}
function hasUnpairedSurrogate(value) {
for (let index = 0; index < value.length; index++) {
const code = value.charCodeAt(index);
if (code >= 55296 && code <= 56319) {
const next = value.charCodeAt(index + 1);
if (Number.isNaN(next) || next < 56320 || next > 57343) return true;
index++;
} else if (code >= 56320 && code <= 57343) return true;
}
return false;
}
function assertValidIdentifier(value, label) {
if (typeof value !== "string" || value.length === 0 || hasUnpairedSurrogate(value)) throw new SandboxCheckpointInvalidIdError(`${label} must be a non-empty well-formed Unicode string`);
}
function hasOwn(value, key) {
return Object.prototype.hasOwnProperty.call(value, key);
}
function validateEntries(checkpoint) {
if (!Array.isArray(checkpoint.files)) throw new SandboxCheckpointInvalidEntryError("Checkpoint files must be an array");
const paths = /* @__PURE__ */ new Set();
const kinds = /* @__PURE__ */ new Map();
for (const entry of checkpoint.files) {
if (entry === null || typeof entry !== "object") throw new SandboxCheckpointInvalidEntryError("Checkpoint entry must be an object");
const candidate = entry;
if (typeof candidate.path !== "string" || candidate.path.length === 0 || candidate.path.includes("\0") || candidate.path.startsWith("/") || candidate.path.startsWith("\\") || /^[A-Za-z]:([\\/]|$)/.test(candidate.path) || candidate.path.includes("\\") || candidate.path.split("/").some((part) => part.length === 0 || part === "." || part === "..")) throw new SandboxCheckpointInvalidEntryError("Checkpoint entry path must be a normalized workspace-relative path");
const path = candidate.path;
if (paths.has(path)) throw new SandboxCheckpointInvalidEntryError(`Checkpoint contains duplicate entry path '${path}'`);
for (let separator = path.indexOf("/"); separator !== -1; separator = path.indexOf("/", separator + 1)) {
const ancestor = path.slice(0, separator);
if (kinds.get(ancestor) === "file") throw new SandboxCheckpointInvalidEntryError(`Checkpoint entry '${path}' is beneath file '${ancestor}'`);
}
if (candidate.kind === "file" && Array.from(kinds.keys()).some((other) => other.startsWith(`${path}/`))) throw new SandboxCheckpointInvalidEntryError(`Checkpoint file '${path}' is an ancestor of another entry`);
paths.add(path);
if (candidate.kind === "file") {
if (typeof candidate.blobKey !== "string" || candidate.blobKey.length === 0 || hasUnpairedSurrogate(candidate.blobKey) || !/^sandbox-files\/sha256\/[0-9a-f]{64}$/.test(candidate.blobKey)) throw new SandboxCheckpointInvalidEntryError("File entries require a non-empty blobKey");
if (!hasOwn(candidate, "size") || typeof candidate.size !== "number" || !Number.isSafeInteger(candidate.size) || candidate.size < 0) throw new SandboxCheckpointInvalidEntryError("File entry size must be a non-negative safe integer");
} else if (candidate.kind === "dir") {
if (hasOwn(candidate, "blobKey") || hasOwn(candidate, "size")) throw new SandboxCheckpointInvalidEntryError("Directory entries cannot contain file fields");
} else throw new SandboxCheckpointInvalidEntryError("Checkpoint entry kind must be file or dir");
kinds.set(path, candidate.kind);
}
}
function validateArtifacts(checkpoint) {
if (!Array.isArray(checkpoint.artifacts)) throw new SandboxCheckpointInvalidEntryError("Checkpoint artifacts must be an array");
for (const artifact of checkpoint.artifacts) {
if (artifact === null || typeof artifact !== "object") throw new SandboxCheckpointInvalidEntryError("Checkpoint artifact must be an object");
const candidate = artifact;
if (typeof candidate.artifactId !== "string" || candidate.artifactId.length === 0 || hasUnpairedSurrogate(candidate.artifactId) || typeof candidate.name !== "string" || candidate.name.length === 0 || typeof candidate.mimeType !== "string" || candidate.mimeType.length === 0 || typeof candidate.blobKey !== "string" || candidate.blobKey.length === 0 || hasUnpairedSurrogate(candidate.blobKey) || !/^sandbox-artifacts\/sha256\/[0-9a-f]{64}$/.test(candidate.blobKey) || typeof candidate.size !== "number" || !Number.isSafeInteger(candidate.size) || candidate.size < 0 || typeof candidate.createdAt !== "number" || !Number.isFinite(candidate.createdAt)) throw new SandboxCheckpointInvalidEntryError("Checkpoint artifact has invalid fields");
}
}
var InMemorySandboxCheckpointStore = class {
state;
now;
leaseDurationMs;
renewAfterMs;
constructor(options = {}) {
const state = {
checkpoints: /* @__PURE__ */ new Map(),
heads: /* @__PURE__ */ new Map(),
writers: /* @__PURE__ */ new Map(),
fences: /* @__PURE__ */ new Map(),
references: /* @__PURE__ */ new Map()
};
this.state = state;
this.now = options.now ?? (() => Date.now());
this.leaseDurationMs = options.leaseDurationMs ?? 12e4;
this.renewAfterMs = options.renewAfterMs ?? 45e3;
if (!Number.isFinite(this.leaseDurationMs) || this.leaseDurationMs <= 0) throw new Error("leaseDurationMs must be finite and positive");
if (!Number.isFinite(this.renewAfterMs) || this.renewAfterMs <= 0 || this.renewAfterMs >= this.leaseDurationMs) throw new Error("renewAfterMs must be finite, positive, and less than leaseDurationMs");
}
async get(id) {
assertValidIdentifier(id, "Checkpoint id");
const checkpoint = this.state.checkpoints.get(id);
return checkpoint ? copy(checkpoint) : null;
}
async list(threadId) {
assertValidIdentifier(threadId, "Thread id");
return Array.from(this.state.checkpoints.values()).filter((checkpoint) => checkpoint.threadId === threadId).sort((a, b) => a.createdAt - b.createdAt || compareUtf8Bytes(a.id, b.id)).map(copy);
}
async getHead(threadId) {
assertValidIdentifier(threadId, "Thread id");
return this.state.heads.get(threadId) ?? null;
}
async append(input) {
const checkpoint = copy(input.checkpoint);
const { expectedHeadId, writer } = input;
assertValidIdentifier(checkpoint.threadId, "Checkpoint thread id");
assertValidIdentifier(writer.threadId, "Writer thread id");
assertValidIdentifier(checkpoint.id, "Checkpoint id");
if (expectedHeadId !== null) assertValidIdentifier(expectedHeadId, "Expected head id");
if (checkpoint.parentCheckpointId != null) assertValidIdentifier(checkpoint.parentCheckpointId, "Parent checkpoint id");
if (writer.threadId !== checkpoint.threadId) throw new SandboxCheckpointWriterLostError("Checkpoint writer thread does not match checkpoint thread");
if (typeof checkpoint.id !== "string" || checkpoint.id.length === 0) throw new SandboxCheckpointInvalidIdError("Checkpoint id must be non-empty");
if (hasUnpairedSurrogate(checkpoint.id)) throw new SandboxCheckpointInvalidIdError("Checkpoint id must contain valid Unicode");
if (hasUnpairedSurrogate(checkpoint.threadId)) throw new SandboxCheckpointInvalidIdError("Checkpoint thread id must contain valid Unicode");
if (typeof checkpoint.createdAt !== "number" || !Number.isFinite(checkpoint.createdAt)) throw new SandboxCheckpointInvalidEntryError("Checkpoint createdAt must be a finite number");
if (expectedHeadId === "") throw new SandboxCheckpointInvalidIdError("Expected head id must be null or non-empty");
const parentCheckpointId = checkpoint.parentCheckpointId ?? null;
if (parentCheckpointId === "") throw new SandboxCheckpointInvalidIdError("Parent checkpoint id must be null or non-empty");
if (expectedHeadId !== null && hasUnpairedSurrogate(expectedHeadId)) throw new SandboxCheckpointInvalidIdError("Expected head id must contain valid Unicode");
if (parentCheckpointId !== null && hasUnpairedSurrogate(parentCheckpointId)) throw new SandboxCheckpointInvalidIdError("Parent checkpoint id must contain valid Unicode");
validateEntries(checkpoint);
validateArtifacts(checkpoint);
this.assertWriter(writer, checkpoint.threadId);
if (this.state.checkpoints.has(checkpoint.id)) throw new SandboxCheckpointDuplicateIdError(`Checkpoint '${checkpoint.id}' already exists`);
const actualHeadId = this.state.heads.get(checkpoint.threadId) ?? null;
if (actualHeadId !== expectedHeadId) throw new SandboxCheckpointConflictError(`Expected head '${expectedHeadId}', but thread '${checkpoint.threadId}' is at '${actualHeadId}'`);
if (parentCheckpointId !== expectedHeadId) throw new SandboxCheckpointParentMismatchError(`Checkpoint '${checkpoint.id}' parent does not match expected head`);
const stored = {
...checkpoint,
parentCheckpointId
};
const keys = blobKeys(stored);
this.state.checkpoints.set(stored.id, stored);
this.state.heads.set(stored.threadId, stored.id);
for (const key of keys) this.state.references.set(key, (this.state.references.get(key) ?? 0) + 1);
return { headId: stored.id };
}
async deleteHead(input) {
const { threadId, checkpointId, writer } = input;
assertValidIdentifier(threadId, "Thread id");
assertValidIdentifier(checkpointId, "Checkpoint id");
assertValidIdentifier(writer.threadId, "Writer thread id");
if (writer.threadId !== threadId) throw new SandboxCheckpointWriterLostError("Checkpoint writer thread does not match operation thread");
this.assertWriter(writer, threadId);
if ((this.state.heads.get(threadId) ?? null) !== checkpointId) throw new SandboxCheckpointNotHeadError(`Checkpoint '${checkpointId}' is not the current head of thread '${threadId}'`);
const checkpoint = this.state.checkpoints.get(checkpointId);
if (!checkpoint) throw new SandboxCheckpointNotHeadError(`Checkpoint '${checkpointId}' does not exist`);
this.state.checkpoints.delete(checkpointId);
if (checkpoint.parentCheckpointId) this.state.heads.set(threadId, checkpoint.parentCheckpointId);
else this.state.heads.delete(threadId);
for (const key of blobKeys(checkpoint)) {
const references = (this.state.references.get(key) ?? 0) - 1;
if (references > 0) this.state.references.set(key, references);
else this.state.references.delete(key);
}
}
async acquireWriter(threadId) {
assertValidIdentifier(threadId, "Thread id");
const current = this.state.writers.get(threadId);
if (current && current.expiresAt > this.now()) throw new SandboxCheckpointWriterConflictError(`Thread '${threadId}' already has an active checkpoint writer`);
const fence = (this.state.fences.get(threadId) ?? 0) + 1;
this.state.fences.set(threadId, fence);
const ownerToken = globalThis.crypto.randomUUID();
const lease = {
threadId,
ownerToken,
fence,
expiresAt: this.now() + this.leaseDurationMs
};
this.state.writers.set(threadId, lease);
return {
...lease,
get expiresAt() {
return lease.expiresAt;
},
renewAfterMs: this.renewAfterMs,
renew: async () => {
this.assertWriter(lease, threadId);
lease.expiresAt = this.now() + this.leaseDurationMs;
return { expiresAt: lease.expiresAt };
},
release: async () => {
const currentLease = this.state.writers.get(threadId);
if (currentLease?.ownerToken === ownerToken && currentLease.fence === fence) this.state.writers.delete(threadId);
}
};
}
assertWriter(writer, threadId) {
const current = this.state.writers.get(threadId);
if (!current || current.ownerToken !== writer.ownerToken || current.fence !== writer.fence || current.expiresAt <= this.now()) throw new SandboxCheckpointWriterLostError(`Checkpoint writer lease for thread '${threadId}' is no longer current`);
}
async listBlobReferences() {
return Array.from(this.state.references.entries()).sort(([a], [b]) => compareUtf8Bytes(a, b)).map(([key, references]) => ({
key,
references
}));
}
};
//#endregion
export { InMemorySandboxCheckpointStore, SandboxCheckpointConflictError, SandboxCheckpointDuplicateIdError, SandboxCheckpointError, SandboxCheckpointInvalidEntryError, SandboxCheckpointInvalidIdError, SandboxCheckpointNotHeadError, SandboxCheckpointParentMismatchError, SandboxCheckpointWriterConflictError, SandboxCheckpointWriterLostError, defineSandboxCheckpointStore, isForkCapableSandboxCheckpointStore };
//# sourceMappingURL=checkpoint-store.js.map