import { Effect } from "effect";import type { SandboxToolInvoker } from "@executor-js/codemode-core";import { makeQuickJsExecutor } from "@executor-js/runtime-quickjs"; import { ToolSandboxError, type CollectResult, type HandleBridge, type InvokeRequest, type InvokeResult, 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/defineWorkflow/connection/// connections/catalog) and `executor:ui` stubs, then executes the bundle so// `module.exports.default` is the artifact. A driver appended after the bundle// either collects descriptors (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`.// 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`/`defineWorkflow` record their def so// the driver can read it back. Clients are built by `__mkHandle`.const runtimePrelude = `var __modules = {};var __defs = { tool: null, workflow: null, ui: 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); } });}var __executorApp = { connection: function(integration, opts) { return { __decl: 'single', integration: integration, description: opts && opts.description }; }, connections: function(integration, opts) { return { __decl: 'array', integration: integration, description: opts && opts.description }; }, catalog: function() { return { __decl: 'catalog' }; }, defineTool: function(def) { __defs.tool = def; return def; }, defineWorkflow: function(def) { __defs.workflow = def; return def; },};var __executorUi = { config: function(){ return undefined; }, useQuery: function(){ return { data: [], isLoading: false, refetch: function(){} }; }, useTool: function(){ return { run: function(){ return Promise.resolve(); }, isRunning: false }; },};function __require(id) { if (id === 'executor:app') return __executorApp; if (id === 'executor:ui') return __executorUi; if (id === 'executor:ui/components') return {}; 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 define*), `globalThis.__zodToJson` and// `__zodToJsonOutput` (bound to the author's inlined zod). `require` is our// shim; `defineTool`/`defineWorkflow` also record 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);`; // Collect driver: describe the artifact's connections + input/output schema.// Deterministic JSON only — no handler execution.const collectDriver = `return await (async () => { var def = __defs.tool || __defs.workflow || (globalThis.__artifact && (globalThis.__artifact.default || globalThis.__artifact)); var kind = __defs.workflow ? 'workflow' : 'tool'; var conns = {}; if (def && def.connections) { for (var k in def.connections) { var c = def.connections[k]; conns[k] = { decl: c && c.__decl ? c.__decl : 'single', integration: c && c.integration, description: c && c.description }; } } var toJson = (typeof globalThis.__zodToJson === 'function') ? globalThis.__zodToJson : function(){ return undefined; }; var toJsonOut = (typeof globalThis.__zodToJsonOutput === 'function') ? globalThis.__zodToJsonOutput : function(){ return undefined; }; return { kind: kind, description: def && def.description, connections: conns, annotations: def && def.annotations, schedule: def && def.schedule, hasHandler: !!(def && (def.handler || def.run)), inputJsonSchema: def ? toJson(def.input) : undefined, outputJsonSchema: def ? toJsonOut(def.output) : undefined, };})()`; const buildCollectCode = (bundle: string): string => runtimePrelude + wrapBundle(bundle) + collectDriver; // Invoke driver: build injected clients from the request roots, then call the// artifact's handler with (input, injected). Fan-out arrays become arrays of// element handles.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 roots = ${rootsLiteral}; var injected = {}; for (var name in roots) { var spec = roots[name]; if (spec.kind === 'array') { var arr = []; for (var i = 0; i < spec.count; i++) arr.push(__mkHandle(name + '#' + i, [])); injected[name] = arr; } else { injected[name] = __mkHandle(name, []); } } var out = await def.handler(${inputLiteral}, injected); 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" }), ) as never,}; 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 = (bundle: string): Effect.Effect<unknown, ToolSandboxError> => collectExecutor.execute(buildCollectCode(bundle), collectInvoker).pipe( Effect.mapError( (cause) => new ToolSandboxError({ kind: "collect", message: "collect run failed", cause }), ), Effect.flatMap((result) => { if (result.error) { return Effect.fail(new ToolSandboxError({ kind: "collect", message: result.error })); } return Effect.succeed(result.result); }), ); return { collect: (bundle: string) => 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(bundle); const second = yield* runCollect(bundle); 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 { kind?: string }; const kind = descriptor?.kind === "workflow" ? "workflow" : "tool"; const result: CollectResult = { artifacts: { [String((descriptor as { artifact?: string }).artifact ?? "default")]: { kind, 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}`, }), ) as never; } 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" }), ) as never; } 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) { return yield* new ToolSandboxError({ kind: "invoke", message: result.error }); } return { output: result.result, logs: result.logs ?? [] } satisfies InvokeResult; }), };};
import { Effect } from "effect";
import type { SandboxToolInvoker } from "@executor-js/codemode-core";
import { makeQuickJsExecutor } from "@executor-js/runtime-quickjs";
import {
ToolSandboxError,
type CollectResult,
type HandleBridge,
type InvokeRequest,
type InvokeResult,
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/defineWorkflow/connection/
// connections/catalog) and `executor:ui` stubs, then executes the bundle so
// `module.exports.default` is the artifact. A driver appended after the bundle
// either collects descriptors (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`.
// 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`/`defineWorkflow` record their def so
// the driver can read it back. Clients are built by `__mkHandle`.
const runtimePrelude = `
var __modules = {};
var __defs = { tool: null, workflow: null, ui: 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);
}
});
}
var __executorApp = {
connection: function(integration, opts) { return { __decl: 'single', integration: integration, description: opts && opts.description }; },
connections: function(integration, opts) { return { __decl: 'array', integration: integration, description: opts && opts.description }; },
catalog: function() { return { __decl: 'catalog' }; },
defineTool: function(def) { __defs.tool = def; return def; },
defineWorkflow: function(def) { __defs.workflow = def; return def; },
};
var __executorUi = {
config: function(){ return undefined; },
useQuery: function(){ return { data: [], isLoading: false, refetch: function(){} }; },
useTool: function(){ return { run: function(){ return Promise.resolve(); }, isRunning: false }; },
};
function __require(id) {
if (id === 'executor:app') return __executorApp;
if (id === 'executor:ui') return __executorUi;
if (id === 'executor:ui/components') return {};
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 define*), `globalThis.__zodToJson` and
// `__zodToJsonOutput` (bound to the author's inlined zod). `require` is our
// shim; `defineTool`/`defineWorkflow` also record 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);
`;
// Collect driver: describe the artifact's connections + input/output schema.
// Deterministic JSON only — no handler execution.
const collectDriver = `
return await (async () => {
var def = __defs.tool || __defs.workflow || (globalThis.__artifact && (globalThis.__artifact.default || globalThis.__artifact));
var kind = __defs.workflow ? 'workflow' : 'tool';
var conns = {};
if (def && def.connections) {
for (var k in def.connections) {
var c = def.connections[k];
conns[k] = { decl: c && c.__decl ? c.__decl : 'single', integration: c && c.integration, description: c && c.description };
}
}
var toJson = (typeof globalThis.__zodToJson === 'function') ? globalThis.__zodToJson : function(){ return undefined; };
var toJsonOut = (typeof globalThis.__zodToJsonOutput === 'function') ? globalThis.__zodToJsonOutput : function(){ return undefined; };
return {
kind: kind,
description: def && def.description,
connections: conns,
annotations: def && def.annotations,
schedule: def && def.schedule,
hasHandler: !!(def && (def.handler || def.run)),
inputJsonSchema: def ? toJson(def.input) : undefined,
outputJsonSchema: def ? toJsonOut(def.output) : undefined,
};
})()
`;
const buildCollectCode = (bundle: string): string =>
runtimePrelude + wrapBundle(bundle) + collectDriver;
// Invoke driver: build injected clients from the request roots, then call the
// artifact's handler with (input, injected). Fan-out arrays become arrays of
// element handles.
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 roots = ${rootsLiteral};
var injected = {};
for (var name in roots) {
var spec = roots[name];
if (spec.kind === 'array') {
var arr = [];
for (var i = 0; i < spec.count; i++) arr.push(__mkHandle(name + '#' + i, []));
injected[name] = arr;
} else {
injected[name] = __mkHandle(name, []);
}
}
var out = await def.handler(${inputLiteral}, injected);
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" }),
) as never,
};
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 = (bundle: string): Effect.Effect<unknown, ToolSandboxError> =>
collectExecutor.execute(buildCollectCode(bundle), collectInvoker).pipe(
Effect.mapError(
(cause) => new ToolSandboxError({ kind: "collect", message: "collect run failed", cause }),
),
Effect.flatMap((result) => {
if (result.error) {
return Effect.fail(new ToolSandboxError({ kind: "collect", message: result.error }));
}
return Effect.succeed(result.result);
}),
);
return {
collect: (bundle: string) =>
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(bundle);
const second = yield* runCollect(bundle);
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 { kind?: string };
const kind = descriptor?.kind === "workflow" ? "workflow" : "tool";
const result: CollectResult = {
artifacts: {
[String((descriptor as { artifact?: string }).artifact ?? "default")]: {
kind,
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}`,
}),
) as never;
}
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" }),
) as never;
}
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) {
return yield* new ToolSandboxError({ kind: "invoke", message: result.error });
}
return { output: result.result, logs: result.logs ?? [] } satisfies InvokeResult;
}),
};
};