import { Effect, Option, Predicate, Schema } from "effect";import type { SandboxToolInvoker } from "@executor-js/codemode-core";import { makeQuickJsExecutor } from "@executor-js/runtime-quickjs"; import { InputValidationError, OutputValidationError, ToolSandboxError, type CollectResult, type CollectRequest, type HandleBridge, type InvokeRequest, type InvokeResult, type ValidationIssue, type ToolSandbox,} from "../seams/tool-sandbox";import { stableStringify } from "../pipeline/descriptor"; // ---------------------------------------------------------------------------// QuickJS-backed ToolSandbox (self-hosted).//// The published bundle is a CJS string. The sandbox body prepends a `require`// shim providing `executor:app` (defineTool/integration),// then executes the bundle so `module.exports.default` is the artifact. A// driver appended after the bundle either collects the descriptor (nothing// bound) or runs one handler with injected clients.//// Injected clients are Proxies that turn `github.issues.listForRepo(args)` into// `await __invokeTool("__handle__", { root, path, args })` — the ONE bridge the// QuickJS runtime already provides (`tools`/`__invokeTool`). Our// `SandboxToolInvoker` decodes that and forwards to the host `HandleBridge`.// The old `db` handle is intentionally unavailable in v1; handlers that still// try it get a clear error before any host storage call exists.// Everything crossing is JSON (the cloud version is RPC), so the interface// stays honest.//// Determinism: `collect` runs the collection body twice and byte-compares the// descriptor JSON. Effectful top-levels (Math.random, Date.now) diverge and are// rejected. QuickJS denies `fetch` and enforces a deadline + memory cap.// --------------------------------------------------------------------------- const COLLECT_TIMEOUT_MS = 10_000;const INVOKE_TIMEOUT_MS = 30_000; // The shim + module system injected before the bundle. Kept as a plain string// (QuickJS evals a string). `defineTool` records its def so the driver can read// it back. Clients are built by `__mkHandle`.const runtimePrelude = `var __modules = {};var __defs = { tool: null };function __recordDefault(mod) { return mod; }var __handleBridge = function(root, path, args) { // Route every injected-client method call through the single tool bridge. return tools.__handle__({ root: root, path: path, args: args });};function __mkHandle(root, prefix) { var target = function(){}; return new Proxy(target, { get: function(_t, prop) { if (prop === 'then' || typeof prop === 'symbol') return undefined; return __mkHandle(root, prefix.concat([String(prop)])); }, apply: function(_t, _this, callArgs) { return __handleBridge(root, prefix, callArgs); } });}function __unavailableStorage() { return new Proxy({}, { get: function() { return function() { throw new Error('storage is not available yet'); }; }, apply: function() { throw new Error('storage is not available yet'); } });}var __executorApp = { integration: function(integration) { return { integration: integration }; }, defineTool: function(def) { __defs.tool = def; return def; },};function __require(id) { if (id === 'executor:app') return __executorApp; throw new Error('module not available in sandbox: ' + id);}`; // Run the CJS bundle. The bundle's virtual entry sets `globalThis.__artifact`// (the def object returned by defineTool). `require` is our shim; `defineTool`// also records into `__defs` as a fallback.const wrapBundle = (bundle: string): string => `var module = { exports: {} };var exports = module.exports;var require = __require;(function(module, exports, require){${bundle}})(module, exports, require);`; const decodeJsonMarker = Schema.decodeUnknownOption(Schema.UnknownFromJsonString); const isRecord = (value: unknown): value is Record<string, unknown> => value !== null && typeof value === "object" && !Array.isArray(value); const parseMarker = (message: string): unknown => { const parse = (text: string): unknown => Option.getOrNull(decodeJsonMarker(text)); const direct = parse(message); if (direct) return direct; const firstBrace = message.indexOf("{"); const lastBrace = message.lastIndexOf("}"); if (firstBrace !== -1 && lastBrace > firstBrace) { return parse(message.slice(firstBrace, lastBrace + 1)); } return null;}; const isMarker = (value: unknown, tag: string): value is Record<string, unknown> => { if (!isRecord(value)) return false; return Predicate.isTagged(tag)(value);}; const markerMessage = (marker: Record<string, unknown>, fallback: string): string => typeof marker.message === "string" ? marker.message : fallback; const markerIssues = (marker: Record<string, unknown>): readonly ValidationIssue[] => Array.isArray(marker.issues) ? marker.issues.filter(isValidationIssue) : []; const isValidationIssue = (value: unknown): value is ValidationIssue => isRecord(value) && typeof value.message === "string" && (value.path === undefined || Array.isArray(value.path)); // Collect driver: describe the artifact's integrations + input/output schema.// Deterministic JSON only — no handler execution.const collectDriver = (artifact: string): string => `return await (async () => { var artifactName = ${JSON.stringify(artifact)}; var fail = function(tag, data) { var payload = data || {}; payload._tag = tag; throw JSON.stringify(payload); }; var sanitizeJson = function(value) { if (value === undefined) return undefined; if (value === null || typeof value !== 'object') return value; if (Array.isArray(value)) return value.map(sanitizeJson); var out = {}; for (var key in value) { if (key === '~standard') continue; if (typeof value[key] === 'function' || value[key] === undefined) continue; out[key] = sanitizeJson(value[key]); } return out; }; var schemaToJson = function(field, schema, direction) { if (schema === undefined) return undefined; if (schema && typeof schema === 'object' && schema['~standard']) { var standard = schema['~standard']; var vendor = standard && standard.vendor ? String(standard.vendor) : 'unknown'; var jsonSchema = standard && standard.jsonSchema; if (!jsonSchema || typeof jsonSchema[direction] !== 'function') { fail('SchemaExportError', { tool: artifactName, field: field, vendor: vendor, message: 'tool "' + artifactName + '" ' + field + ' schema from "' + vendor + '" is not supported for schema export', }); } return sanitizeJson(jsonSchema[direction]({ target: 'draft-07' })); } if (schema && typeof schema === 'object' && !Array.isArray(schema)) { return sanitizeJson(schema); } fail('SchemaExportError', { tool: artifactName, field: field, vendor: 'raw', message: 'tool "' + artifactName + '" ' + field + ' schema must be a Standard Schema or raw JSON Schema object', }); }; var def = __defs.tool || (globalThis.__artifact && (globalThis.__artifact.default || globalThis.__artifact)); if (def && typeof def.handler === 'function') { var handlerSource = Function.prototype.toString.call(def.handler); if (/\\bdb\\b/.test(handlerSource)) { fail('StorageUnavailableError', { tool: artifactName, field: 'handler', message: 'tool "' + artifactName + '" uses storage, but storage is not available yet', }); } } var integrations = {}; if (def && def.integrations) { for (var k in def.integrations) { var c = def.integrations[k]; integrations[k] = { integration: c && c.integration ? String(c.integration) : '' }; } } return { artifact: artifactName, kind: 'tool', description: def && def.description, integrations: integrations, annotations: def && def.annotations, hasHandler: !!(def && def.handler), inputJsonSchema: def ? schemaToJson('input', def.input, 'input') : undefined, outputJsonSchema: def && def.output !== undefined ? schemaToJson('output', def.output, 'output') : undefined, };})()`; const buildCollectCode = (bundle: string, request?: CollectRequest): string => runtimePrelude + wrapBundle(bundle) + collectDriver(request?.artifact ?? "default"); // Invoke driver: build injected clients from the request roots, validate// Standard Schema input/output inside the sandbox, then call the artifact's// handler with (input, injected).const buildInvokeDriver = (request: InvokeRequest): string => { const rootsLiteral = JSON.stringify(request.roots); const inputLiteral = JSON.stringify(request.input ?? {}); return `return await (async () => { var def = __defs.tool || (globalThis.__artifact && (globalThis.__artifact.default || globalThis.__artifact)); if (!def || typeof def.handler !== 'function') throw new Error('artifact has no handler: ${request.artifact}'); var failValidation = function(tag, field, issues) { throw JSON.stringify({ _tag: tag, tool: '${request.artifact}', field: field, message: 'tool "${request.artifact}" ' + field + ' validation failed', issues: JSON.parse(JSON.stringify(issues || [])), }); }; var validateWithStandardSchema = async function(field, schema, value) { if (!schema || typeof schema !== 'object' || !schema['~standard']) return value; var standard = schema['~standard']; if (!standard || typeof standard.validate !== 'function') return value; var result = await standard.validate(value); if (result && result.issues) { failValidation(field === 'input' ? 'InputValidationError' : 'OutputValidationError', field, result.issues); } return result && Object.prototype.hasOwnProperty.call(result, 'value') ? result.value : value; }; var roots = ${rootsLiteral}; var injected = {}; for (var name in roots) { var spec = roots[name]; if (spec.kind !== 'single') throw new Error('unsupported handle root kind: ' + spec.kind); injected[name] = __mkHandle(name, []); } injected.db = __unavailableStorage(); var input = await validateWithStandardSchema('input', def.input, ${inputLiteral}); var out = await def.handler(input, injected); out = await validateWithStandardSchema('output', def.output, out); return out;})()`;}; const buildInvokeCode = (bundle: string, request: InvokeRequest): string => runtimePrelude + wrapBundle(bundle) + buildInvokeDriver(request); // A no-op invoker for collect: no handle calls should happen; if they do// (misbehaving describe path), fail loudly.const collectInvoker: SandboxToolInvoker = { invoke: () => Effect.fail( new ToolSandboxError({ kind: "collect", message: "collect must not make handle calls" }), ),}; export interface QuickjsToolSandboxOptions { readonly collectTimeoutMs?: number; readonly invokeTimeoutMs?: number;} export const makeQuickjsToolSandbox = (options: QuickjsToolSandboxOptions = {}): ToolSandbox => { const collectExecutor = makeQuickJsExecutor({ timeoutMs: options.collectTimeoutMs ?? COLLECT_TIMEOUT_MS, }); const invokeExecutor = makeQuickJsExecutor({ timeoutMs: options.invokeTimeoutMs ?? INVOKE_TIMEOUT_MS, }); const runCollect = (code: string): Effect.Effect<unknown, ToolSandboxError> => collectExecutor.execute(code, collectInvoker).pipe( Effect.mapError( (cause) => new ToolSandboxError({ kind: "collect", message: "collect run failed", cause }), ), Effect.flatMap((result) => { if (result.error) { const marker = parseMarker(result.error); if ( isMarker(marker, "SchemaExportError") || isMarker(marker, "StorageUnavailableError") ) { return Effect.fail( new ToolSandboxError({ kind: "collect", message: markerMessage(marker, result.error), cause: marker, }), ); } return Effect.fail(new ToolSandboxError({ kind: "collect", message: result.error })); } return Effect.succeed(result.result); }), ); return { collect: (bundle: string, request?: CollectRequest) => Effect.gen(function* () { // Run twice, byte-compare (determinism gate). Key-sorted stringify so a // false mismatch never comes from property-order luck — a real // divergence (Math.random / Date.now) still fails. const first = yield* runCollect(buildCollectCode(bundle, request)); const second = yield* runCollect(buildCollectCode(bundle, request)); const a = stableStringify(first); const b = stableStringify(second); if (a !== b) { return yield* new ToolSandboxError({ kind: "nondeterministic", message: "descriptor collection is non-deterministic (an artifact read Math.random/Date.now or otherwise diverged between runs)", }); } const descriptor = first as { artifact?: string }; const result: CollectResult = { artifacts: { [String(descriptor.artifact ?? "default")]: { kind: "tool", descriptor: first, }, }, }; return result; }), invoke: (bundle: string, request: InvokeRequest, bridge: HandleBridge) => Effect.gen(function* () { // The invoker decodes the routed handle call and forwards to the host // bridge. Path 0 is `__handle__`; the single arg is {root, path, args}. const invoker: SandboxToolInvoker = { invoke: (input: { path: string; args: unknown }) => { // Strictness (grafted from A): the ONLY reserved bridge path the // invoke phase accepts is `__handle__`. Anything else is a hard // error, never silently ignored — a handler must not reach the host // through an unexpected channel. if (input.path !== "__handle__") { return Effect.fail( new ToolSandboxError({ kind: "invoke", message: `unexpected sandbox bridge path: ${input.path}`, }), ); } const call = input.args as { root: string; path: readonly string[]; args: readonly unknown[]; }; if (!call || typeof call.root !== "string" || !Array.isArray(call.path)) { return Effect.fail( new ToolSandboxError({ kind: "invoke", message: "malformed sandbox bridge call" }), ); } return bridge.call({ root: call.root, path: call.path, args: call.args }) as never; }, }; const result = yield* invokeExecutor .execute(buildInvokeCode(bundle, request), invoker) .pipe( Effect.mapError( (cause) => new ToolSandboxError({ kind: "invoke", message: "invoke run failed", cause }), ), ); if (result.error) { const marker = parseMarker(result.error); if (isMarker(marker, "InputValidationError")) { return yield* new InputValidationError({ message: markerMessage(marker, "input validation failed"), issues: markerIssues(marker), }); } if (isMarker(marker, "OutputValidationError")) { return yield* new OutputValidationError({ message: markerMessage(marker, "output validation failed"), issues: markerIssues(marker), }); } return yield* new ToolSandboxError({ kind: "invoke", message: result.error }); } return { output: result.result, logs: result.logs ?? [] } satisfies InvokeResult; }), };};
import { Effect, Option, Predicate, Schema } from "effect";
import type { SandboxToolInvoker } from "@executor-js/codemode-core";
import { makeQuickJsExecutor } from "@executor-js/runtime-quickjs";
import {
InputValidationError,
OutputValidationError,
ToolSandboxError,
type CollectResult,
type CollectRequest,
type HandleBridge,
type InvokeRequest,
type InvokeResult,
type ValidationIssue,
type ToolSandbox,
} from "../seams/tool-sandbox";
import { stableStringify } from "../pipeline/descriptor";
// ---------------------------------------------------------------------------
// QuickJS-backed ToolSandbox (self-hosted).
//
// The published bundle is a CJS string. The sandbox body prepends a `require`
// shim providing `executor:app` (defineTool/integration),
// then executes the bundle so `module.exports.default` is the artifact. A
// driver appended after the bundle either collects the descriptor (nothing
// bound) or runs one handler with injected clients.
//
// Injected clients are Proxies that turn `github.issues.listForRepo(args)` into
// `await __invokeTool("__handle__", { root, path, args })` — the ONE bridge the
// QuickJS runtime already provides (`tools`/`__invokeTool`). Our
// `SandboxToolInvoker` decodes that and forwards to the host `HandleBridge`.
// The old `db` handle is intentionally unavailable in v1; handlers that still
// try it get a clear error before any host storage call exists.
// Everything crossing is JSON (the cloud version is RPC), so the interface
// stays honest.
//
// Determinism: `collect` runs the collection body twice and byte-compares the
// descriptor JSON. Effectful top-levels (Math.random, Date.now) diverge and are
// rejected. QuickJS denies `fetch` and enforces a deadline + memory cap.
// ---------------------------------------------------------------------------
const COLLECT_TIMEOUT_MS = 10_000;
const INVOKE_TIMEOUT_MS = 30_000;
// The shim + module system injected before the bundle. Kept as a plain string
// (QuickJS evals a string). `defineTool` records its def so the driver can read
// it back. Clients are built by `__mkHandle`.
const runtimePrelude = `
var __modules = {};
var __defs = { tool: null };
function __recordDefault(mod) { return mod; }
var __handleBridge = function(root, path, args) {
// Route every injected-client method call through the single tool bridge.
return tools.__handle__({ root: root, path: path, args: args });
};
function __mkHandle(root, prefix) {
var target = function(){};
return new Proxy(target, {
get: function(_t, prop) {
if (prop === 'then' || typeof prop === 'symbol') return undefined;
return __mkHandle(root, prefix.concat([String(prop)]));
},
apply: function(_t, _this, callArgs) {
return __handleBridge(root, prefix, callArgs);
}
});
}
function __unavailableStorage() {
return new Proxy({}, {
get: function() {
return function() { throw new Error('storage is not available yet'); };
},
apply: function() {
throw new Error('storage is not available yet');
}
});
}
var __executorApp = {
integration: function(integration) { return { integration: integration }; },
defineTool: function(def) { __defs.tool = def; return def; },
};
function __require(id) {
if (id === 'executor:app') return __executorApp;
throw new Error('module not available in sandbox: ' + id);
}
`;
// Run the CJS bundle. The bundle's virtual entry sets `globalThis.__artifact`
// (the def object returned by defineTool). `require` is our shim; `defineTool`
// also records into `__defs` as a fallback.
const wrapBundle = (bundle: string): string => `
var module = { exports: {} };
var exports = module.exports;
var require = __require;
(function(module, exports, require){
${bundle}
})(module, exports, require);
`;
const decodeJsonMarker = Schema.decodeUnknownOption(Schema.UnknownFromJsonString);
const isRecord = (value: unknown): value is Record<string, unknown> =>
value !== null && typeof value === "object" && !Array.isArray(value);
const parseMarker = (message: string): unknown => {
const parse = (text: string): unknown => Option.getOrNull(decodeJsonMarker(text));
const direct = parse(message);
if (direct) return direct;
const firstBrace = message.indexOf("{");
const lastBrace = message.lastIndexOf("}");
if (firstBrace !== -1 && lastBrace > firstBrace) {
return parse(message.slice(firstBrace, lastBrace + 1));
}
return null;
};
const isMarker = (value: unknown, tag: string): value is Record<string, unknown> => {
if (!isRecord(value)) return false;
return Predicate.isTagged(tag)(value);
};
const markerMessage = (marker: Record<string, unknown>, fallback: string): string =>
typeof marker.message === "string" ? marker.message : fallback;
const markerIssues = (marker: Record<string, unknown>): readonly ValidationIssue[] =>
Array.isArray(marker.issues) ? marker.issues.filter(isValidationIssue) : [];
const isValidationIssue = (value: unknown): value is ValidationIssue =>
isRecord(value) &&
typeof value.message === "string" &&
(value.path === undefined || Array.isArray(value.path));
// Collect driver: describe the artifact's integrations + input/output schema.
// Deterministic JSON only — no handler execution.
const collectDriver = (artifact: string): string => `
return await (async () => {
var artifactName = ${JSON.stringify(artifact)};
var fail = function(tag, data) {
var payload = data || {};
payload._tag = tag;
throw JSON.stringify(payload);
};
var sanitizeJson = function(value) {
if (value === undefined) return undefined;
if (value === null || typeof value !== 'object') return value;
if (Array.isArray(value)) return value.map(sanitizeJson);
var out = {};
for (var key in value) {
if (key === '~standard') continue;
if (typeof value[key] === 'function' || value[key] === undefined) continue;
out[key] = sanitizeJson(value[key]);
}
return out;
};
var schemaToJson = function(field, schema, direction) {
if (schema === undefined) return undefined;
if (schema && typeof schema === 'object' && schema['~standard']) {
var standard = schema['~standard'];
var vendor = standard && standard.vendor ? String(standard.vendor) : 'unknown';
var jsonSchema = standard && standard.jsonSchema;
if (!jsonSchema || typeof jsonSchema[direction] !== 'function') {
fail('SchemaExportError', {
tool: artifactName,
field: field,
vendor: vendor,
message: 'tool "' + artifactName + '" ' + field + ' schema from "' + vendor + '" is not supported for schema export',
});
}
return sanitizeJson(jsonSchema[direction]({ target: 'draft-07' }));
}
if (schema && typeof schema === 'object' && !Array.isArray(schema)) {
return sanitizeJson(schema);
}
fail('SchemaExportError', {
tool: artifactName,
field: field,
vendor: 'raw',
message: 'tool "' + artifactName + '" ' + field + ' schema must be a Standard Schema or raw JSON Schema object',
});
};
var def = __defs.tool || (globalThis.__artifact && (globalThis.__artifact.default || globalThis.__artifact));
if (def && typeof def.handler === 'function') {
var handlerSource = Function.prototype.toString.call(def.handler);
if (/\\bdb\\b/.test(handlerSource)) {
fail('StorageUnavailableError', {
tool: artifactName,
field: 'handler',
message: 'tool "' + artifactName + '" uses storage, but storage is not available yet',
});
}
}
var integrations = {};
if (def && def.integrations) {
for (var k in def.integrations) {
var c = def.integrations[k];
integrations[k] = { integration: c && c.integration ? String(c.integration) : '' };
}
}
return {
artifact: artifactName,
kind: 'tool',
description: def && def.description,
integrations: integrations,
annotations: def && def.annotations,
hasHandler: !!(def && def.handler),
inputJsonSchema: def ? schemaToJson('input', def.input, 'input') : undefined,
outputJsonSchema: def && def.output !== undefined ? schemaToJson('output', def.output, 'output') : undefined,
};
})()
`;
const buildCollectCode = (bundle: string, request?: CollectRequest): string =>
runtimePrelude + wrapBundle(bundle) + collectDriver(request?.artifact ?? "default");
// Invoke driver: build injected clients from the request roots, validate
// Standard Schema input/output inside the sandbox, then call the artifact's
// handler with (input, injected).
const buildInvokeDriver = (request: InvokeRequest): string => {
const rootsLiteral = JSON.stringify(request.roots);
const inputLiteral = JSON.stringify(request.input ?? {});
return `
return await (async () => {
var def = __defs.tool || (globalThis.__artifact && (globalThis.__artifact.default || globalThis.__artifact));
if (!def || typeof def.handler !== 'function') throw new Error('artifact has no handler: ${request.artifact}');
var failValidation = function(tag, field, issues) {
throw JSON.stringify({
_tag: tag,
tool: '${request.artifact}',
field: field,
message: 'tool "${request.artifact}" ' + field + ' validation failed',
issues: JSON.parse(JSON.stringify(issues || [])),
});
};
var validateWithStandardSchema = async function(field, schema, value) {
if (!schema || typeof schema !== 'object' || !schema['~standard']) return value;
var standard = schema['~standard'];
if (!standard || typeof standard.validate !== 'function') return value;
var result = await standard.validate(value);
if (result && result.issues) {
failValidation(field === 'input' ? 'InputValidationError' : 'OutputValidationError', field, result.issues);
}
return result && Object.prototype.hasOwnProperty.call(result, 'value') ? result.value : value;
};
var roots = ${rootsLiteral};
var injected = {};
for (var name in roots) {
var spec = roots[name];
if (spec.kind !== 'single') throw new Error('unsupported handle root kind: ' + spec.kind);
injected[name] = __mkHandle(name, []);
}
injected.db = __unavailableStorage();
var input = await validateWithStandardSchema('input', def.input, ${inputLiteral});
var out = await def.handler(input, injected);
out = await validateWithStandardSchema('output', def.output, out);
return out;
})()
`;
};
const buildInvokeCode = (bundle: string, request: InvokeRequest): string =>
runtimePrelude + wrapBundle(bundle) + buildInvokeDriver(request);
// A no-op invoker for collect: no handle calls should happen; if they do
// (misbehaving describe path), fail loudly.
const collectInvoker: SandboxToolInvoker = {
invoke: () =>
Effect.fail(
new ToolSandboxError({ kind: "collect", message: "collect must not make handle calls" }),
),
};
export interface QuickjsToolSandboxOptions {
readonly collectTimeoutMs?: number;
readonly invokeTimeoutMs?: number;
}
export const makeQuickjsToolSandbox = (options: QuickjsToolSandboxOptions = {}): ToolSandbox => {
const collectExecutor = makeQuickJsExecutor({
timeoutMs: options.collectTimeoutMs ?? COLLECT_TIMEOUT_MS,
});
const invokeExecutor = makeQuickJsExecutor({
timeoutMs: options.invokeTimeoutMs ?? INVOKE_TIMEOUT_MS,
});
const runCollect = (code: string): Effect.Effect<unknown, ToolSandboxError> =>
collectExecutor.execute(code, collectInvoker).pipe(
Effect.mapError(
(cause) => new ToolSandboxError({ kind: "collect", message: "collect run failed", cause }),
),
Effect.flatMap((result) => {
if (result.error) {
const marker = parseMarker(result.error);
if (
isMarker(marker, "SchemaExportError") ||
isMarker(marker, "StorageUnavailableError")
) {
return Effect.fail(
new ToolSandboxError({
kind: "collect",
message: markerMessage(marker, result.error),
cause: marker,
}),
);
}
return Effect.fail(new ToolSandboxError({ kind: "collect", message: result.error }));
}
return Effect.succeed(result.result);
}),
);
return {
collect: (bundle: string, request?: CollectRequest) =>
Effect.gen(function* () {
// Run twice, byte-compare (determinism gate). Key-sorted stringify so a
// false mismatch never comes from property-order luck — a real
// divergence (Math.random / Date.now) still fails.
const first = yield* runCollect(buildCollectCode(bundle, request));
const second = yield* runCollect(buildCollectCode(bundle, request));
const a = stableStringify(first);
const b = stableStringify(second);
if (a !== b) {
return yield* new ToolSandboxError({
kind: "nondeterministic",
message:
"descriptor collection is non-deterministic (an artifact read Math.random/Date.now or otherwise diverged between runs)",
});
}
const descriptor = first as { artifact?: string };
const result: CollectResult = {
artifacts: {
[String(descriptor.artifact ?? "default")]: {
kind: "tool",
descriptor: first,
},
},
};
return result;
}),
invoke: (bundle: string, request: InvokeRequest, bridge: HandleBridge) =>
Effect.gen(function* () {
// The invoker decodes the routed handle call and forwards to the host
// bridge. Path 0 is `__handle__`; the single arg is {root, path, args}.
const invoker: SandboxToolInvoker = {
invoke: (input: { path: string; args: unknown }) => {
// Strictness (grafted from A): the ONLY reserved bridge path the
// invoke phase accepts is `__handle__`. Anything else is a hard
// error, never silently ignored — a handler must not reach the host
// through an unexpected channel.
if (input.path !== "__handle__") {
return Effect.fail(
new ToolSandboxError({
kind: "invoke",
message: `unexpected sandbox bridge path: ${input.path}`,
}),
);
}
const call = input.args as {
root: string;
path: readonly string[];
args: readonly unknown[];
};
if (!call || typeof call.root !== "string" || !Array.isArray(call.path)) {
return Effect.fail(
new ToolSandboxError({ kind: "invoke", message: "malformed sandbox bridge call" }),
);
}
return bridge.call({ root: call.root, path: call.path, args: call.args }) as never;
},
};
const result = yield* invokeExecutor
.execute(buildInvokeCode(bundle, request), invoker)
.pipe(
Effect.mapError(
(cause) =>
new ToolSandboxError({ kind: "invoke", message: "invoke run failed", cause }),
),
);
if (result.error) {
const marker = parseMarker(result.error);
if (isMarker(marker, "InputValidationError")) {
return yield* new InputValidationError({
message: markerMessage(marker, "input validation failed"),
issues: markerIssues(marker),
});
}
if (isMarker(marker, "OutputValidationError")) {
return yield* new OutputValidationError({
message: markerMessage(marker, "output validation failed"),
issues: markerIssues(marker),
});
}
return yield* new ToolSandboxError({ kind: "invoke", message: result.error });
}
return { output: result.result, logs: result.logs ?? [] } satisfies InvokeResult;
}),
};
};