UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

69 lines (68 loc) • 3.59 kB
import vm from "node:vm"; import { splitWorkflowMeta, WorkflowScriptError } from "./workflow-meta.js"; import { thrownText } from "../core/thrown-value.js"; export const WORKFLOW_DATE_ERR = "Date.now() / new Date() are unavailable in workflow scripts (breaks resume). Stamp results after the workflow returns, or pass timestamps via args."; export const WORKFLOW_RANDOM_ERR = "Math.random() is unavailable in workflow scripts (breaks resume). For N independent samples, include the index in the agent label or prompt."; const DETERMINISM_SHIM = `(() => { const NOW_ERR = ${JSON.stringify(WORKFLOW_DATE_ERR)}; const RANDOM_ERR = ${JSON.stringify(WORKFLOW_RANDOM_ERR)}; Math.random = function random() { throw new Error(RANDOM_ERR) }; const RealDate = Date; RealDate.now = function now() { throw new Error(NOW_ERR) }; function ShimDate(...a) { if (!new.target) throw new Error(NOW_ERR); // bare Date() → now-string if (a.length === 0) throw new Error(NOW_ERR); return Reflect.construct(RealDate, a, new.target); } ShimDate.now = RealDate.now; ShimDate.parse = RealDate.parse; ShimDate.UTC = RealDate.UTC; ShimDate.prototype = RealDate.prototype; // Close the (new Date(x)).constructor backdoor to RealDate.now — point // .constructor at the shim, then freeze RealDate so it can't be undone. RealDate.prototype.constructor = ShimDate; Object.freeze(RealDate); globalThis.Date = ShimDate; })()`; export const devWorkflowScriptRunner = { safeForUntrustedScripts: false, async run({ scriptSource, primitives, scriptArgs, signal }) { if (signal?.aborted) throw new Error("workflow script aborted before start"); const { meta, body } = splitWorkflowMeta(scriptSource); const wrapped = `(async function(agent, parallel, pipeline, phase, log, budget, args) {\n${body}\n})`; const importThrow = () => { throw new WorkflowScriptError("import() is not available in workflow scripts."); }; let compiled; try { try { compiled = new vm.Script(wrapped, { filename: "workflow-script.js", importModuleDynamically: importThrow }); } catch (err) { if (err instanceof SyntaxError) throw err; compiled = new vm.Script(wrapped, { filename: "workflow-script.js" }); } } catch (err) { throw new WorkflowScriptError(`workflow script failed to compile: ${thrownText(err)}`); } const noHostApi = (name) => () => { throw new WorkflowScriptError(`${name} is not available in workflow scripts — there are no Node/host APIs here. Use agent()/log()/` + `budget for work, and schedule time-based behavior outside the workflow.`); }; const context = vm.createContext({ require: noHostApi("require"), setTimeout: noHostApi("setTimeout"), setInterval: noHostApi("setInterval"), setImmediate: noHostApi("setImmediate"), fetch: noHostApi("fetch"), queueMicrotask: noHostApi("queueMicrotask"), }, { codeGeneration: { strings: false, wasm: false } }); new vm.Script(DETERMINISM_SHIM, { filename: "workflow-determinism-shim.js" }).runInContext(context); const fn = compiled.runInContext(context); const result = await fn(primitives.agent, primitives.parallel, primitives.pipeline, primitives.phase, primitives.log, primitives.budget, scriptArgs); return { result, meta }; }, };