@enclave-vm/core
Advanced tools
+682
| // libs/core/src/adapters/interpreter-adapter.ts | ||
| import * as acorn from "acorn"; | ||
| // libs/core/src/interpreter/interpreter.ts | ||
| var BLOCKED_KEYS = /* @__PURE__ */ new Set([ | ||
| "__proto__", | ||
| "prototype", | ||
| "constructor", | ||
| "__defineGetter__", | ||
| "__defineSetter__", | ||
| "__lookupGetter__", | ||
| "__lookupSetter__" | ||
| ]); | ||
| var MAX_STRING_OP_LENGTH = 1e6; | ||
| var HIGHER_ORDER_ARRAY_METHODS = /* @__PURE__ */ new Set([ | ||
| "map", | ||
| "flatMap", | ||
| "filter", | ||
| "forEach", | ||
| "some", | ||
| "every", | ||
| "find", | ||
| "findIndex", | ||
| "reduce", | ||
| "sort" | ||
| ]); | ||
| var StepLimitError = class extends Error { | ||
| constructor(limit) { | ||
| super(`Execution step limit exceeded (${limit})`); | ||
| this.name = "StepLimitError"; | ||
| } | ||
| }; | ||
| var InterpreterError = class extends Error { | ||
| constructor(message) { | ||
| super(message); | ||
| this.name = "InterpreterError"; | ||
| } | ||
| }; | ||
| var ReturnSignal = class { | ||
| constructor(value) { | ||
| this.value = value; | ||
| } | ||
| }; | ||
| var BreakSignal = class { | ||
| }; | ||
| var ContinueSignal = class { | ||
| }; | ||
| var Scope = class _Scope { | ||
| constructor(parent) { | ||
| this.parent = parent; | ||
| } | ||
| vars = /* @__PURE__ */ new Map(); | ||
| child() { | ||
| return new _Scope(this); | ||
| } | ||
| declare(name, value, mutable) { | ||
| this.vars.set(name, { value, mutable }); | ||
| } | ||
| has(name) { | ||
| return this.vars.has(name) || (this.parent?.has(name) ?? false); | ||
| } | ||
| get(name) { | ||
| const slot = this.vars.get(name); | ||
| if (slot) return slot.value; | ||
| if (this.parent) return this.parent.get(name); | ||
| throw new InterpreterError(`'${name}' is not defined`); | ||
| } | ||
| set(name, value) { | ||
| const slot = this.vars.get(name); | ||
| if (slot) { | ||
| if (!slot.mutable) throw new InterpreterError(`Assignment to constant '${name}'`); | ||
| slot.value = value; | ||
| return; | ||
| } | ||
| if (this.parent) { | ||
| this.parent.set(name, value); | ||
| return; | ||
| } | ||
| throw new InterpreterError(`'${name}' is not defined`); | ||
| } | ||
| }; | ||
| var Interpreter = class { | ||
| constructor(options) { | ||
| this.options = options; | ||
| this.globals = options.globals; | ||
| } | ||
| steps = 0; | ||
| callDepth = 0; | ||
| globals; | ||
| /** Number of AST nodes evaluated so far (for execution stats). */ | ||
| get stepCount() { | ||
| return this.steps; | ||
| } | ||
| async run(program) { | ||
| const root = new Scope(); | ||
| for (const stmt of program.body) { | ||
| if (stmt.type === "FunctionDeclaration" && stmt.id) { | ||
| root.declare(stmt.id.name, this.makeFunction(stmt, root), false); | ||
| } | ||
| } | ||
| let last; | ||
| for (const stmt of program.body) { | ||
| if (stmt.type === "FunctionDeclaration") continue; | ||
| last = await this.execStatement(stmt, root); | ||
| } | ||
| if (root.has("__ag_main")) { | ||
| const main = root.get("__ag_main"); | ||
| if (typeof main === "function") return main(); | ||
| } | ||
| return last; | ||
| } | ||
| tick() { | ||
| if (this.options.signal?.aborted) throw new InterpreterError("Execution aborted"); | ||
| if (++this.steps > this.options.maxSteps) throw new StepLimitError(this.options.maxSteps); | ||
| } | ||
| // ── Statements ───────────────────────────────────────────────────────────── | ||
| async execStatement(node, scope) { | ||
| this.tick(); | ||
| switch (node.type) { | ||
| case "VariableDeclaration": { | ||
| for (const d of node.declarations) { | ||
| const value = d.init ? await this.evalExpr(d.init, scope) : void 0; | ||
| this.bindPattern(d.id, value, scope, node.kind !== "const"); | ||
| } | ||
| return void 0; | ||
| } | ||
| case "FunctionDeclaration": | ||
| if (node.id) scope.declare(node.id.name, this.makeFunction(node, scope), false); | ||
| return void 0; | ||
| case "ExpressionStatement": | ||
| return this.evalExpr(node.expression, scope); | ||
| case "BlockStatement": | ||
| return this.execBlock(node.body, scope.child()); | ||
| case "IfStatement": | ||
| if (truthy(await this.evalExpr(node.test, scope))) return this.execStatement(node.consequent, scope); | ||
| if (node.alternate) return this.execStatement(node.alternate, scope); | ||
| return void 0; | ||
| case "ForOfStatement": | ||
| return this.execForOf(node, scope); | ||
| case "ForStatement": | ||
| return this.execFor(node, scope); | ||
| case "WhileStatement": | ||
| return this.execWhile(node, scope); | ||
| case "ReturnStatement": | ||
| throw new ReturnSignal(node.argument ? await this.evalExpr(node.argument, scope) : void 0); | ||
| case "BreakStatement": | ||
| throw new BreakSignal(); | ||
| case "ContinueStatement": | ||
| throw new ContinueSignal(); | ||
| case "EmptyStatement": | ||
| return void 0; | ||
| default: | ||
| throw new InterpreterError(`Unsupported statement: ${node.type}`); | ||
| } | ||
| } | ||
| async execBlock(body, scope) { | ||
| for (const stmt of body) { | ||
| if (stmt.type === "FunctionDeclaration" && stmt.id) { | ||
| scope.declare(stmt.id.name, this.makeFunction(stmt, scope), false); | ||
| } | ||
| } | ||
| for (const stmt of body) { | ||
| if (stmt.type === "FunctionDeclaration") continue; | ||
| await this.execStatement(stmt, scope); | ||
| } | ||
| return void 0; | ||
| } | ||
| async execForOf(node, scope) { | ||
| const iterable = await this.evalExpr(node.right, scope); | ||
| if (!isIterable(iterable)) throw new InterpreterError("for-of target is not iterable"); | ||
| for (const item of iterable) { | ||
| this.tick(); | ||
| const inner = scope.child(); | ||
| const decl = node.left; | ||
| if (decl.type === "VariableDeclaration") { | ||
| this.bindPattern(decl.declarations[0].id, item, inner, decl.kind !== "const"); | ||
| } else { | ||
| this.assignTo(decl, item, inner); | ||
| } | ||
| try { | ||
| await this.execStatement(node.body, inner); | ||
| } catch (e) { | ||
| if (e instanceof BreakSignal) break; | ||
| if (e instanceof ContinueSignal) continue; | ||
| throw e; | ||
| } | ||
| } | ||
| } | ||
| async execFor(node, scope) { | ||
| const inner = scope.child(); | ||
| if (node.init) { | ||
| if (node.init.type === "VariableDeclaration") await this.execStatement(node.init, inner); | ||
| else await this.evalExpr(node.init, inner); | ||
| } | ||
| while (node.test ? truthy(await this.evalExpr(node.test, inner)) : true) { | ||
| this.tick(); | ||
| try { | ||
| await this.execStatement(node.body, inner.child()); | ||
| } catch (e) { | ||
| if (e instanceof BreakSignal) break; | ||
| if (!(e instanceof ContinueSignal)) throw e; | ||
| } | ||
| if (node.update) await this.evalExpr(node.update, inner); | ||
| } | ||
| } | ||
| async execWhile(node, scope) { | ||
| while (truthy(await this.evalExpr(node.test, scope))) { | ||
| this.tick(); | ||
| try { | ||
| await this.execStatement(node.body, scope.child()); | ||
| } catch (e) { | ||
| if (e instanceof BreakSignal) break; | ||
| if (!(e instanceof ContinueSignal)) throw e; | ||
| } | ||
| } | ||
| } | ||
| // ── Expressions ──────────────────────────────────────────────────────────-- | ||
| async evalExpr(node, scope) { | ||
| this.tick(); | ||
| switch (node.type) { | ||
| case "Literal": | ||
| return node.value; | ||
| case "Identifier": | ||
| return scope.has(node.name) ? scope.get(node.name) : this.readGlobal(node.name); | ||
| case "TemplateLiteral": | ||
| return this.evalTemplate(node, scope); | ||
| case "ArrayExpression": { | ||
| const arr = []; | ||
| for (const el of node.elements) { | ||
| if (el == null) { | ||
| arr.push(void 0); | ||
| } else if (el.type === "SpreadElement") { | ||
| const spread = await this.evalExpr(el.argument, scope); | ||
| if (!isIterable(spread)) throw new InterpreterError("Spread target is not iterable"); | ||
| for (const v of spread) arr.push(v); | ||
| } else { | ||
| arr.push(await this.evalExpr(el, scope)); | ||
| } | ||
| } | ||
| return arr; | ||
| } | ||
| case "ObjectExpression": { | ||
| const obj = {}; | ||
| for (const prop of node.properties) { | ||
| if (prop.type === "SpreadElement") { | ||
| const spread = await this.evalExpr(prop.argument, scope); | ||
| if (spread && typeof spread === "object") { | ||
| for (const [k, v] of Object.entries(spread)) { | ||
| if (!BLOCKED_KEYS.has(k)) obj[k] = v; | ||
| } | ||
| } | ||
| continue; | ||
| } | ||
| const key = await this.propKey(prop, scope); | ||
| if (BLOCKED_KEYS.has(key)) throw new InterpreterError(`Forbidden property key: ${key}`); | ||
| obj[key] = await this.evalExpr(prop.value, scope); | ||
| } | ||
| return obj; | ||
| } | ||
| case "UnaryExpression": { | ||
| const v = await this.evalExpr(node.argument, scope); | ||
| switch (node.operator) { | ||
| case "!": | ||
| return !truthy(v); | ||
| case "-": | ||
| return -v; | ||
| case "+": | ||
| return +v; | ||
| case "typeof": | ||
| return typeof v; | ||
| case "~": | ||
| return ~v; | ||
| default: | ||
| throw new InterpreterError(`Unsupported unary operator: ${node.operator}`); | ||
| } | ||
| } | ||
| case "BinaryExpression": | ||
| return this.evalBinary(node, scope); | ||
| case "LogicalExpression": { | ||
| const left = await this.evalExpr(node.left, scope); | ||
| if (node.operator === "&&") return truthy(left) ? this.evalExpr(node.right, scope) : left; | ||
| if (node.operator === "||") return truthy(left) ? left : this.evalExpr(node.right, scope); | ||
| return left ?? await this.evalExpr(node.right, scope); | ||
| } | ||
| case "ConditionalExpression": | ||
| return truthy(await this.evalExpr(node.test, scope)) ? this.evalExpr(node.consequent, scope) : this.evalExpr(node.alternate, scope); | ||
| case "MemberExpression": | ||
| return (await this.evalMember(node, scope)).value; | ||
| case "CallExpression": | ||
| return this.evalCall(node, scope); | ||
| case "AwaitExpression": | ||
| return await this.evalExpr(node.argument, scope); | ||
| case "AssignmentExpression": | ||
| return this.evalAssignment(node, scope); | ||
| case "ArrowFunctionExpression": | ||
| case "FunctionExpression": | ||
| return this.makeFunction(node, scope); | ||
| default: | ||
| throw new InterpreterError(`Unsupported expression: ${node.type}`); | ||
| } | ||
| } | ||
| async evalTemplate(node, scope) { | ||
| let out = ""; | ||
| for (let i = 0; i < node.quasis.length; i++) { | ||
| out += node.quasis[i].value.cooked ?? ""; | ||
| if (i < node.expressions.length) out += String(await this.evalExpr(node.expressions[i], scope)); | ||
| } | ||
| return out; | ||
| } | ||
| async evalBinary(node, scope) { | ||
| const l = await this.evalExpr(node.left, scope); | ||
| const r = await this.evalExpr(node.right, scope); | ||
| switch (node.operator) { | ||
| case "+": | ||
| return l + r; | ||
| case "-": | ||
| return l - r; | ||
| case "*": | ||
| return l * r; | ||
| case "/": | ||
| return l / r; | ||
| case "%": | ||
| return l % r; | ||
| case "**": | ||
| return l ** r; | ||
| case "==": | ||
| return l == r; | ||
| case "!=": | ||
| return l != r; | ||
| case "===": | ||
| return l === r; | ||
| case "!==": | ||
| return l !== r; | ||
| case "<": | ||
| return l < r; | ||
| case "<=": | ||
| return l <= r; | ||
| case ">": | ||
| return l > r; | ||
| case ">=": | ||
| return l >= r; | ||
| case "&": | ||
| return l & r; | ||
| case "|": | ||
| return l | r; | ||
| case "^": | ||
| return l ^ r; | ||
| case "<<": | ||
| return l << r; | ||
| case ">>": | ||
| return l >> r; | ||
| case ">>>": | ||
| return l >>> r; | ||
| default: | ||
| throw new InterpreterError(`Unsupported binary operator: ${node.operator}`); | ||
| } | ||
| } | ||
| /** Resolve a member access to `{ object, key, value }`, blocking escape keys. */ | ||
| async evalMember(node, scope) { | ||
| const object = await this.evalExpr(node.object, scope); | ||
| const key = node.computed ? String(await this.evalExpr(node.property, scope)) : node.property.name; | ||
| if (BLOCKED_KEYS.has(key)) throw new InterpreterError(`Forbidden property access: ${key}`); | ||
| if (object == null) throw new InterpreterError(`Cannot read '${key}' of ${String(object)}`); | ||
| const value = object[key]; | ||
| return { object, key, value }; | ||
| } | ||
| async evalCall(node, scope) { | ||
| const args = []; | ||
| for (const a of node.arguments) { | ||
| if (a.type === "SpreadElement") { | ||
| const spread = await this.evalExpr(a.argument, scope); | ||
| if (!isIterable(spread)) throw new InterpreterError("Spread target is not iterable"); | ||
| for (const v of spread) args.push(v); | ||
| } else { | ||
| args.push(await this.evalExpr(a, scope)); | ||
| } | ||
| } | ||
| let fn; | ||
| let thisArg; | ||
| if (node.callee.type === "MemberExpression") { | ||
| const m = await this.evalMember(node.callee, scope); | ||
| if (Array.isArray(m.object) && HIGHER_ORDER_ARRAY_METHODS.has(m.key) && typeof args[0] === "function") { | ||
| return this.callArrayMethod(m.object, m.key, args); | ||
| } | ||
| if (typeof m.object === "string" && (m.key === "repeat" || m.key === "padStart" || m.key === "padEnd")) { | ||
| const n = Number(args[0]); | ||
| const produced = m.key === "repeat" ? m.object.length * (n > 0 ? n : 0) : Math.max(m.object.length, n > 0 ? n : 0); | ||
| if (produced > MAX_STRING_OP_LENGTH) { | ||
| throw new InterpreterError( | ||
| `String '${m.key}' would produce ${produced} chars, exceeding the ${MAX_STRING_OP_LENGTH} limit` | ||
| ); | ||
| } | ||
| } | ||
| fn = m.value; | ||
| thisArg = m.object; | ||
| } else { | ||
| fn = await this.evalExpr(node.callee, scope); | ||
| thisArg = void 0; | ||
| } | ||
| if (typeof fn !== "function") throw new InterpreterError("Attempted to call a non-function"); | ||
| if (++this.callDepth > this.options.maxCallDepth) { | ||
| this.callDepth--; | ||
| throw new InterpreterError(`Max call depth exceeded (${this.options.maxCallDepth})`); | ||
| } | ||
| try { | ||
| return await fn.apply(thisArg, args); | ||
| } finally { | ||
| this.callDepth--; | ||
| } | ||
| } | ||
| /** | ||
| * Async implementations of the higher-order array methods, so interpreter | ||
| * callbacks (which are async — they may `await` tool calls) are awaited. Each | ||
| * iteration ticks the step budget. | ||
| */ | ||
| async callArrayMethod(arr, method, args) { | ||
| const cb = args[0]; | ||
| const each = (i) => { | ||
| this.tick(); | ||
| return cb(arr[i], i, arr); | ||
| }; | ||
| switch (method) { | ||
| case "map": { | ||
| const out = []; | ||
| for (let i = 0; i < arr.length; i++) out.push(await each(i)); | ||
| return out; | ||
| } | ||
| case "flatMap": { | ||
| const out = []; | ||
| for (let i = 0; i < arr.length; i++) { | ||
| const v = await each(i); | ||
| if (Array.isArray(v)) out.push(...v); | ||
| else out.push(v); | ||
| } | ||
| return out; | ||
| } | ||
| case "filter": { | ||
| const out = []; | ||
| for (let i = 0; i < arr.length; i++) if (truthy(await each(i))) out.push(arr[i]); | ||
| return out; | ||
| } | ||
| case "forEach": | ||
| for (let i = 0; i < arr.length; i++) await each(i); | ||
| return void 0; | ||
| case "some": | ||
| for (let i = 0; i < arr.length; i++) if (truthy(await each(i))) return true; | ||
| return false; | ||
| case "every": | ||
| for (let i = 0; i < arr.length; i++) if (!truthy(await each(i))) return false; | ||
| return true; | ||
| case "find": | ||
| for (let i = 0; i < arr.length; i++) if (truthy(await each(i))) return arr[i]; | ||
| return void 0; | ||
| case "findIndex": | ||
| for (let i = 0; i < arr.length; i++) if (truthy(await each(i))) return i; | ||
| return -1; | ||
| case "reduce": { | ||
| const reducer = cb; | ||
| let acc; | ||
| let start = 0; | ||
| if (args.length > 1) acc = args[1]; | ||
| else { | ||
| if (arr.length === 0) throw new InterpreterError("Reduce of empty array with no initial value"); | ||
| acc = arr[0]; | ||
| start = 1; | ||
| } | ||
| for (let i = start; i < arr.length; i++) { | ||
| this.tick(); | ||
| acc = await reducer(acc, arr[i], i, arr); | ||
| } | ||
| return acc; | ||
| } | ||
| case "sort": { | ||
| const cmp = cb; | ||
| for (let i = 1; i < arr.length; i++) { | ||
| let j = i; | ||
| while (j > 0) { | ||
| this.tick(); | ||
| if (await cmp(arr[j - 1], arr[j]) <= 0) break; | ||
| [arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]; | ||
| j--; | ||
| } | ||
| } | ||
| return arr; | ||
| } | ||
| default: | ||
| throw new InterpreterError(`Unsupported array method: ${method}`); | ||
| } | ||
| } | ||
| async evalAssignment(node, scope) { | ||
| const value = await this.evalExpr(node.right, scope); | ||
| if (node.operator === "=") { | ||
| this.assignTo(node.left, value, scope); | ||
| return value; | ||
| } | ||
| const current = await this.evalExpr(node.left, scope); | ||
| const op = node.operator.slice(0, -1); | ||
| const next = applyBinary(op, current, value); | ||
| this.assignTo(node.left, next, scope); | ||
| return next; | ||
| } | ||
| /** Assign to an Identifier or MemberExpression target (blocking escape keys). */ | ||
| assignTo(target, value, scope) { | ||
| if (target.type === "Identifier") { | ||
| scope.set(target.name, value); | ||
| return; | ||
| } | ||
| if (target.type === "MemberExpression") { | ||
| throw new InterpreterError("Member assignment is not supported in AgentScript"); | ||
| } | ||
| throw new InterpreterError(`Unsupported assignment target: ${target.type}`); | ||
| } | ||
| bindPattern(pattern, value, scope, mutable) { | ||
| if (pattern.type === "Identifier") { | ||
| scope.declare(pattern.name, value, mutable); | ||
| return; | ||
| } | ||
| if (pattern.type === "ArrayPattern") { | ||
| const arr = isIterable(value) ? [...value] : []; | ||
| pattern.elements.forEach((el, i) => { | ||
| if (el) this.bindPattern(el, arr[i], scope, mutable); | ||
| }); | ||
| return; | ||
| } | ||
| if (pattern.type === "ObjectPattern") { | ||
| const obj = value ?? {}; | ||
| for (const prop of pattern.properties) { | ||
| if (prop.type === "RestElement") continue; | ||
| const key = prop.key.name; | ||
| if (BLOCKED_KEYS.has(key)) throw new InterpreterError(`Forbidden destructured key: ${key}`); | ||
| this.bindPattern(prop.value, obj[key], scope, mutable); | ||
| } | ||
| return; | ||
| } | ||
| throw new InterpreterError(`Unsupported binding pattern: ${pattern.type}`); | ||
| } | ||
| /** Build an interpreter-backed function (declaration / arrow / expression). */ | ||
| makeFunction(node, closure) { | ||
| const self = this; | ||
| return async function interpreted(...args) { | ||
| const fnScope = closure.child(); | ||
| node.params.forEach((p, i) => self.bindPattern(p, args[i], fnScope, true)); | ||
| try { | ||
| if (node.body.type === "BlockStatement") { | ||
| await self.execBlock(node.body.body, fnScope); | ||
| return void 0; | ||
| } | ||
| return await self.evalExpr(node.body, fnScope); | ||
| } catch (e) { | ||
| if (e instanceof ReturnSignal) return e.value; | ||
| throw e; | ||
| } | ||
| }; | ||
| } | ||
| async propKey(prop, scope) { | ||
| if (prop.computed) return String(await this.evalExpr(prop.key, scope)); | ||
| if (prop.key.type === "Identifier") return prop.key.name; | ||
| if (prop.key.type === "Literal") return String(prop.key.value); | ||
| throw new InterpreterError("Unsupported property key"); | ||
| } | ||
| readGlobal(name) { | ||
| if (Object.prototype.hasOwnProperty.call(this.globals, name)) return this.globals[name]; | ||
| throw new InterpreterError(`'${name}' is not defined`); | ||
| } | ||
| }; | ||
| function truthy(v) { | ||
| return Boolean(v); | ||
| } | ||
| function isIterable(v) { | ||
| return v != null && typeof v[Symbol.iterator] === "function"; | ||
| } | ||
| function applyBinary(op, l, r) { | ||
| switch (op) { | ||
| case "+": | ||
| return l + r; | ||
| case "-": | ||
| return l - r; | ||
| case "*": | ||
| return l * r; | ||
| case "/": | ||
| return l / r; | ||
| case "%": | ||
| return l % r; | ||
| case "**": | ||
| return l ** r; | ||
| default: | ||
| throw new InterpreterError(`Unsupported compound operator: ${op}=`); | ||
| } | ||
| } | ||
| // libs/core/src/adapters/interpreter-adapter.ts | ||
| var SAFE_GLOBALS = Object.freeze({ Math, JSON }); | ||
| var DEFAULT_MAX_STEPS = 5e6; | ||
| var DEFAULT_MAX_CALL_DEPTH = 256; | ||
| var InterpreterAdapter = class { | ||
| constructor(options = {}) { | ||
| this.options = options; | ||
| } | ||
| async execute(code, context) { | ||
| const startTime = Date.now(); | ||
| let toolCallCount = 0; | ||
| const maxToolCalls = context.config.maxToolCalls ?? Number.POSITIVE_INFINITY; | ||
| const toolHandler = context.toolHandler ?? context.config.toolHandler; | ||
| const __safe_callTool = async (name, args) => { | ||
| if (++toolCallCount > maxToolCalls) { | ||
| throw new Error(`Tool call limit exceeded (${maxToolCalls})`); | ||
| } | ||
| if (typeof toolHandler !== "function") { | ||
| throw new Error("No toolHandler configured for the sandbox"); | ||
| } | ||
| return toolHandler(name, args); | ||
| }; | ||
| const globals = { ...SAFE_GLOBALS, __safe_callTool }; | ||
| let program; | ||
| try { | ||
| program = acorn.parse(code, { ecmaVersion: "latest", sourceType: "script" }); | ||
| } catch (error) { | ||
| return this.fail(error, startTime, toolCallCount, 0, "SyntaxError"); | ||
| } | ||
| const timeout = context.config.timeout; | ||
| const timer = timeout && timeout > 0 ? setTimeout(() => context.abortController.abort(), timeout) : void 0; | ||
| const interpreter = new Interpreter({ | ||
| globals, | ||
| maxSteps: this.options.maxSteps ?? DEFAULT_MAX_STEPS, | ||
| maxCallDepth: this.options.maxCallDepth ?? DEFAULT_MAX_CALL_DEPTH, | ||
| signal: context.abortController.signal | ||
| }); | ||
| let timeoutHandle; | ||
| const TIMED_OUT = /* @__PURE__ */ Symbol("timeout"); | ||
| const deadline = timeout && timeout > 0 ? new Promise((resolve) => { | ||
| timeoutHandle = setTimeout(() => resolve(TIMED_OUT), timeout); | ||
| }) : void 0; | ||
| try { | ||
| const run = interpreter.run(program); | ||
| run.catch(() => void 0); | ||
| const outcome = deadline ? await Promise.race([run, deadline]) : await run; | ||
| if (outcome === TIMED_OUT) { | ||
| return this.fail( | ||
| new Error(`Execution timed out after ${timeout}ms`), | ||
| startTime, | ||
| toolCallCount, | ||
| interpreter.stepCount, | ||
| "TimeoutError", | ||
| "EXECUTION_TIMEOUT" | ||
| ); | ||
| } | ||
| return { | ||
| success: true, | ||
| value: outcome, | ||
| stats: this.stats(startTime, toolCallCount, interpreter.stepCount) | ||
| }; | ||
| } catch (error) { | ||
| const code2 = error instanceof StepLimitError ? "STEP_LIMIT_EXCEEDED" : void 0; | ||
| return this.fail(error, startTime, toolCallCount, interpreter.stepCount, void 0, code2); | ||
| } finally { | ||
| if (timer) clearTimeout(timer); | ||
| if (timeoutHandle) clearTimeout(timeoutHandle); | ||
| } | ||
| } | ||
| dispose() { | ||
| } | ||
| stats(startTime, toolCallCount, iterationCount) { | ||
| const endTime = Date.now(); | ||
| return { duration: endTime - startTime, toolCallCount, iterationCount, startTime, endTime }; | ||
| } | ||
| fail(error, startTime, toolCallCount, iterationCount, name, code) { | ||
| const err = error instanceof Error ? error : new Error(String(error)); | ||
| const execError = { | ||
| message: err.message, | ||
| name: name ?? err.name, | ||
| ...err.stack ? { stack: err.stack } : {}, | ||
| ...code ? { code } : {} | ||
| }; | ||
| return { success: false, error: execError, stats: this.stats(startTime, toolCallCount, iterationCount) }; | ||
| } | ||
| }; | ||
| export { | ||
| Interpreter, | ||
| InterpreterAdapter, | ||
| InterpreterError, | ||
| StepLimitError | ||
| }; |
+722
| "use strict"; | ||
| var __create = Object.create; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __getProtoOf = Object.getPrototypeOf; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
| // If the importer is in node compatibility mode or this is not an ESM | ||
| // file that has been converted to a CommonJS file using a Babel- | ||
| // compatible transform (i.e. "__esModule" has not been set), then set | ||
| // "default" to the CommonJS "module.exports" for node compatibility. | ||
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
| mod | ||
| )); | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // libs/core/src/worker.ts | ||
| var worker_exports = {}; | ||
| __export(worker_exports, { | ||
| Interpreter: () => Interpreter, | ||
| InterpreterAdapter: () => InterpreterAdapter, | ||
| InterpreterError: () => InterpreterError, | ||
| StepLimitError: () => StepLimitError | ||
| }); | ||
| module.exports = __toCommonJS(worker_exports); | ||
| // libs/core/src/adapters/interpreter-adapter.ts | ||
| var acorn = __toESM(require("acorn")); | ||
| // libs/core/src/interpreter/interpreter.ts | ||
| var BLOCKED_KEYS = /* @__PURE__ */ new Set([ | ||
| "__proto__", | ||
| "prototype", | ||
| "constructor", | ||
| "__defineGetter__", | ||
| "__defineSetter__", | ||
| "__lookupGetter__", | ||
| "__lookupSetter__" | ||
| ]); | ||
| var MAX_STRING_OP_LENGTH = 1e6; | ||
| var HIGHER_ORDER_ARRAY_METHODS = /* @__PURE__ */ new Set([ | ||
| "map", | ||
| "flatMap", | ||
| "filter", | ||
| "forEach", | ||
| "some", | ||
| "every", | ||
| "find", | ||
| "findIndex", | ||
| "reduce", | ||
| "sort" | ||
| ]); | ||
| var StepLimitError = class extends Error { | ||
| constructor(limit) { | ||
| super(`Execution step limit exceeded (${limit})`); | ||
| this.name = "StepLimitError"; | ||
| } | ||
| }; | ||
| var InterpreterError = class extends Error { | ||
| constructor(message) { | ||
| super(message); | ||
| this.name = "InterpreterError"; | ||
| } | ||
| }; | ||
| var ReturnSignal = class { | ||
| constructor(value) { | ||
| this.value = value; | ||
| } | ||
| }; | ||
| var BreakSignal = class { | ||
| }; | ||
| var ContinueSignal = class { | ||
| }; | ||
| var Scope = class _Scope { | ||
| constructor(parent) { | ||
| this.parent = parent; | ||
| } | ||
| vars = /* @__PURE__ */ new Map(); | ||
| child() { | ||
| return new _Scope(this); | ||
| } | ||
| declare(name, value, mutable) { | ||
| this.vars.set(name, { value, mutable }); | ||
| } | ||
| has(name) { | ||
| return this.vars.has(name) || (this.parent?.has(name) ?? false); | ||
| } | ||
| get(name) { | ||
| const slot = this.vars.get(name); | ||
| if (slot) return slot.value; | ||
| if (this.parent) return this.parent.get(name); | ||
| throw new InterpreterError(`'${name}' is not defined`); | ||
| } | ||
| set(name, value) { | ||
| const slot = this.vars.get(name); | ||
| if (slot) { | ||
| if (!slot.mutable) throw new InterpreterError(`Assignment to constant '${name}'`); | ||
| slot.value = value; | ||
| return; | ||
| } | ||
| if (this.parent) { | ||
| this.parent.set(name, value); | ||
| return; | ||
| } | ||
| throw new InterpreterError(`'${name}' is not defined`); | ||
| } | ||
| }; | ||
| var Interpreter = class { | ||
| constructor(options) { | ||
| this.options = options; | ||
| this.globals = options.globals; | ||
| } | ||
| steps = 0; | ||
| callDepth = 0; | ||
| globals; | ||
| /** Number of AST nodes evaluated so far (for execution stats). */ | ||
| get stepCount() { | ||
| return this.steps; | ||
| } | ||
| async run(program) { | ||
| const root = new Scope(); | ||
| for (const stmt of program.body) { | ||
| if (stmt.type === "FunctionDeclaration" && stmt.id) { | ||
| root.declare(stmt.id.name, this.makeFunction(stmt, root), false); | ||
| } | ||
| } | ||
| let last; | ||
| for (const stmt of program.body) { | ||
| if (stmt.type === "FunctionDeclaration") continue; | ||
| last = await this.execStatement(stmt, root); | ||
| } | ||
| if (root.has("__ag_main")) { | ||
| const main = root.get("__ag_main"); | ||
| if (typeof main === "function") return main(); | ||
| } | ||
| return last; | ||
| } | ||
| tick() { | ||
| if (this.options.signal?.aborted) throw new InterpreterError("Execution aborted"); | ||
| if (++this.steps > this.options.maxSteps) throw new StepLimitError(this.options.maxSteps); | ||
| } | ||
| // ── Statements ───────────────────────────────────────────────────────────── | ||
| async execStatement(node, scope) { | ||
| this.tick(); | ||
| switch (node.type) { | ||
| case "VariableDeclaration": { | ||
| for (const d of node.declarations) { | ||
| const value = d.init ? await this.evalExpr(d.init, scope) : void 0; | ||
| this.bindPattern(d.id, value, scope, node.kind !== "const"); | ||
| } | ||
| return void 0; | ||
| } | ||
| case "FunctionDeclaration": | ||
| if (node.id) scope.declare(node.id.name, this.makeFunction(node, scope), false); | ||
| return void 0; | ||
| case "ExpressionStatement": | ||
| return this.evalExpr(node.expression, scope); | ||
| case "BlockStatement": | ||
| return this.execBlock(node.body, scope.child()); | ||
| case "IfStatement": | ||
| if (truthy(await this.evalExpr(node.test, scope))) return this.execStatement(node.consequent, scope); | ||
| if (node.alternate) return this.execStatement(node.alternate, scope); | ||
| return void 0; | ||
| case "ForOfStatement": | ||
| return this.execForOf(node, scope); | ||
| case "ForStatement": | ||
| return this.execFor(node, scope); | ||
| case "WhileStatement": | ||
| return this.execWhile(node, scope); | ||
| case "ReturnStatement": | ||
| throw new ReturnSignal(node.argument ? await this.evalExpr(node.argument, scope) : void 0); | ||
| case "BreakStatement": | ||
| throw new BreakSignal(); | ||
| case "ContinueStatement": | ||
| throw new ContinueSignal(); | ||
| case "EmptyStatement": | ||
| return void 0; | ||
| default: | ||
| throw new InterpreterError(`Unsupported statement: ${node.type}`); | ||
| } | ||
| } | ||
| async execBlock(body, scope) { | ||
| for (const stmt of body) { | ||
| if (stmt.type === "FunctionDeclaration" && stmt.id) { | ||
| scope.declare(stmt.id.name, this.makeFunction(stmt, scope), false); | ||
| } | ||
| } | ||
| for (const stmt of body) { | ||
| if (stmt.type === "FunctionDeclaration") continue; | ||
| await this.execStatement(stmt, scope); | ||
| } | ||
| return void 0; | ||
| } | ||
| async execForOf(node, scope) { | ||
| const iterable = await this.evalExpr(node.right, scope); | ||
| if (!isIterable(iterable)) throw new InterpreterError("for-of target is not iterable"); | ||
| for (const item of iterable) { | ||
| this.tick(); | ||
| const inner = scope.child(); | ||
| const decl = node.left; | ||
| if (decl.type === "VariableDeclaration") { | ||
| this.bindPattern(decl.declarations[0].id, item, inner, decl.kind !== "const"); | ||
| } else { | ||
| this.assignTo(decl, item, inner); | ||
| } | ||
| try { | ||
| await this.execStatement(node.body, inner); | ||
| } catch (e) { | ||
| if (e instanceof BreakSignal) break; | ||
| if (e instanceof ContinueSignal) continue; | ||
| throw e; | ||
| } | ||
| } | ||
| } | ||
| async execFor(node, scope) { | ||
| const inner = scope.child(); | ||
| if (node.init) { | ||
| if (node.init.type === "VariableDeclaration") await this.execStatement(node.init, inner); | ||
| else await this.evalExpr(node.init, inner); | ||
| } | ||
| while (node.test ? truthy(await this.evalExpr(node.test, inner)) : true) { | ||
| this.tick(); | ||
| try { | ||
| await this.execStatement(node.body, inner.child()); | ||
| } catch (e) { | ||
| if (e instanceof BreakSignal) break; | ||
| if (!(e instanceof ContinueSignal)) throw e; | ||
| } | ||
| if (node.update) await this.evalExpr(node.update, inner); | ||
| } | ||
| } | ||
| async execWhile(node, scope) { | ||
| while (truthy(await this.evalExpr(node.test, scope))) { | ||
| this.tick(); | ||
| try { | ||
| await this.execStatement(node.body, scope.child()); | ||
| } catch (e) { | ||
| if (e instanceof BreakSignal) break; | ||
| if (!(e instanceof ContinueSignal)) throw e; | ||
| } | ||
| } | ||
| } | ||
| // ── Expressions ──────────────────────────────────────────────────────────-- | ||
| async evalExpr(node, scope) { | ||
| this.tick(); | ||
| switch (node.type) { | ||
| case "Literal": | ||
| return node.value; | ||
| case "Identifier": | ||
| return scope.has(node.name) ? scope.get(node.name) : this.readGlobal(node.name); | ||
| case "TemplateLiteral": | ||
| return this.evalTemplate(node, scope); | ||
| case "ArrayExpression": { | ||
| const arr = []; | ||
| for (const el of node.elements) { | ||
| if (el == null) { | ||
| arr.push(void 0); | ||
| } else if (el.type === "SpreadElement") { | ||
| const spread = await this.evalExpr(el.argument, scope); | ||
| if (!isIterable(spread)) throw new InterpreterError("Spread target is not iterable"); | ||
| for (const v of spread) arr.push(v); | ||
| } else { | ||
| arr.push(await this.evalExpr(el, scope)); | ||
| } | ||
| } | ||
| return arr; | ||
| } | ||
| case "ObjectExpression": { | ||
| const obj = {}; | ||
| for (const prop of node.properties) { | ||
| if (prop.type === "SpreadElement") { | ||
| const spread = await this.evalExpr(prop.argument, scope); | ||
| if (spread && typeof spread === "object") { | ||
| for (const [k, v] of Object.entries(spread)) { | ||
| if (!BLOCKED_KEYS.has(k)) obj[k] = v; | ||
| } | ||
| } | ||
| continue; | ||
| } | ||
| const key = await this.propKey(prop, scope); | ||
| if (BLOCKED_KEYS.has(key)) throw new InterpreterError(`Forbidden property key: ${key}`); | ||
| obj[key] = await this.evalExpr(prop.value, scope); | ||
| } | ||
| return obj; | ||
| } | ||
| case "UnaryExpression": { | ||
| const v = await this.evalExpr(node.argument, scope); | ||
| switch (node.operator) { | ||
| case "!": | ||
| return !truthy(v); | ||
| case "-": | ||
| return -v; | ||
| case "+": | ||
| return +v; | ||
| case "typeof": | ||
| return typeof v; | ||
| case "~": | ||
| return ~v; | ||
| default: | ||
| throw new InterpreterError(`Unsupported unary operator: ${node.operator}`); | ||
| } | ||
| } | ||
| case "BinaryExpression": | ||
| return this.evalBinary(node, scope); | ||
| case "LogicalExpression": { | ||
| const left = await this.evalExpr(node.left, scope); | ||
| if (node.operator === "&&") return truthy(left) ? this.evalExpr(node.right, scope) : left; | ||
| if (node.operator === "||") return truthy(left) ? left : this.evalExpr(node.right, scope); | ||
| return left ?? await this.evalExpr(node.right, scope); | ||
| } | ||
| case "ConditionalExpression": | ||
| return truthy(await this.evalExpr(node.test, scope)) ? this.evalExpr(node.consequent, scope) : this.evalExpr(node.alternate, scope); | ||
| case "MemberExpression": | ||
| return (await this.evalMember(node, scope)).value; | ||
| case "CallExpression": | ||
| return this.evalCall(node, scope); | ||
| case "AwaitExpression": | ||
| return await this.evalExpr(node.argument, scope); | ||
| case "AssignmentExpression": | ||
| return this.evalAssignment(node, scope); | ||
| case "ArrowFunctionExpression": | ||
| case "FunctionExpression": | ||
| return this.makeFunction(node, scope); | ||
| default: | ||
| throw new InterpreterError(`Unsupported expression: ${node.type}`); | ||
| } | ||
| } | ||
| async evalTemplate(node, scope) { | ||
| let out = ""; | ||
| for (let i = 0; i < node.quasis.length; i++) { | ||
| out += node.quasis[i].value.cooked ?? ""; | ||
| if (i < node.expressions.length) out += String(await this.evalExpr(node.expressions[i], scope)); | ||
| } | ||
| return out; | ||
| } | ||
| async evalBinary(node, scope) { | ||
| const l = await this.evalExpr(node.left, scope); | ||
| const r = await this.evalExpr(node.right, scope); | ||
| switch (node.operator) { | ||
| case "+": | ||
| return l + r; | ||
| case "-": | ||
| return l - r; | ||
| case "*": | ||
| return l * r; | ||
| case "/": | ||
| return l / r; | ||
| case "%": | ||
| return l % r; | ||
| case "**": | ||
| return l ** r; | ||
| case "==": | ||
| return l == r; | ||
| case "!=": | ||
| return l != r; | ||
| case "===": | ||
| return l === r; | ||
| case "!==": | ||
| return l !== r; | ||
| case "<": | ||
| return l < r; | ||
| case "<=": | ||
| return l <= r; | ||
| case ">": | ||
| return l > r; | ||
| case ">=": | ||
| return l >= r; | ||
| case "&": | ||
| return l & r; | ||
| case "|": | ||
| return l | r; | ||
| case "^": | ||
| return l ^ r; | ||
| case "<<": | ||
| return l << r; | ||
| case ">>": | ||
| return l >> r; | ||
| case ">>>": | ||
| return l >>> r; | ||
| default: | ||
| throw new InterpreterError(`Unsupported binary operator: ${node.operator}`); | ||
| } | ||
| } | ||
| /** Resolve a member access to `{ object, key, value }`, blocking escape keys. */ | ||
| async evalMember(node, scope) { | ||
| const object = await this.evalExpr(node.object, scope); | ||
| const key = node.computed ? String(await this.evalExpr(node.property, scope)) : node.property.name; | ||
| if (BLOCKED_KEYS.has(key)) throw new InterpreterError(`Forbidden property access: ${key}`); | ||
| if (object == null) throw new InterpreterError(`Cannot read '${key}' of ${String(object)}`); | ||
| const value = object[key]; | ||
| return { object, key, value }; | ||
| } | ||
| async evalCall(node, scope) { | ||
| const args = []; | ||
| for (const a of node.arguments) { | ||
| if (a.type === "SpreadElement") { | ||
| const spread = await this.evalExpr(a.argument, scope); | ||
| if (!isIterable(spread)) throw new InterpreterError("Spread target is not iterable"); | ||
| for (const v of spread) args.push(v); | ||
| } else { | ||
| args.push(await this.evalExpr(a, scope)); | ||
| } | ||
| } | ||
| let fn; | ||
| let thisArg; | ||
| if (node.callee.type === "MemberExpression") { | ||
| const m = await this.evalMember(node.callee, scope); | ||
| if (Array.isArray(m.object) && HIGHER_ORDER_ARRAY_METHODS.has(m.key) && typeof args[0] === "function") { | ||
| return this.callArrayMethod(m.object, m.key, args); | ||
| } | ||
| if (typeof m.object === "string" && (m.key === "repeat" || m.key === "padStart" || m.key === "padEnd")) { | ||
| const n = Number(args[0]); | ||
| const produced = m.key === "repeat" ? m.object.length * (n > 0 ? n : 0) : Math.max(m.object.length, n > 0 ? n : 0); | ||
| if (produced > MAX_STRING_OP_LENGTH) { | ||
| throw new InterpreterError( | ||
| `String '${m.key}' would produce ${produced} chars, exceeding the ${MAX_STRING_OP_LENGTH} limit` | ||
| ); | ||
| } | ||
| } | ||
| fn = m.value; | ||
| thisArg = m.object; | ||
| } else { | ||
| fn = await this.evalExpr(node.callee, scope); | ||
| thisArg = void 0; | ||
| } | ||
| if (typeof fn !== "function") throw new InterpreterError("Attempted to call a non-function"); | ||
| if (++this.callDepth > this.options.maxCallDepth) { | ||
| this.callDepth--; | ||
| throw new InterpreterError(`Max call depth exceeded (${this.options.maxCallDepth})`); | ||
| } | ||
| try { | ||
| return await fn.apply(thisArg, args); | ||
| } finally { | ||
| this.callDepth--; | ||
| } | ||
| } | ||
| /** | ||
| * Async implementations of the higher-order array methods, so interpreter | ||
| * callbacks (which are async — they may `await` tool calls) are awaited. Each | ||
| * iteration ticks the step budget. | ||
| */ | ||
| async callArrayMethod(arr, method, args) { | ||
| const cb = args[0]; | ||
| const each = (i) => { | ||
| this.tick(); | ||
| return cb(arr[i], i, arr); | ||
| }; | ||
| switch (method) { | ||
| case "map": { | ||
| const out = []; | ||
| for (let i = 0; i < arr.length; i++) out.push(await each(i)); | ||
| return out; | ||
| } | ||
| case "flatMap": { | ||
| const out = []; | ||
| for (let i = 0; i < arr.length; i++) { | ||
| const v = await each(i); | ||
| if (Array.isArray(v)) out.push(...v); | ||
| else out.push(v); | ||
| } | ||
| return out; | ||
| } | ||
| case "filter": { | ||
| const out = []; | ||
| for (let i = 0; i < arr.length; i++) if (truthy(await each(i))) out.push(arr[i]); | ||
| return out; | ||
| } | ||
| case "forEach": | ||
| for (let i = 0; i < arr.length; i++) await each(i); | ||
| return void 0; | ||
| case "some": | ||
| for (let i = 0; i < arr.length; i++) if (truthy(await each(i))) return true; | ||
| return false; | ||
| case "every": | ||
| for (let i = 0; i < arr.length; i++) if (!truthy(await each(i))) return false; | ||
| return true; | ||
| case "find": | ||
| for (let i = 0; i < arr.length; i++) if (truthy(await each(i))) return arr[i]; | ||
| return void 0; | ||
| case "findIndex": | ||
| for (let i = 0; i < arr.length; i++) if (truthy(await each(i))) return i; | ||
| return -1; | ||
| case "reduce": { | ||
| const reducer = cb; | ||
| let acc; | ||
| let start = 0; | ||
| if (args.length > 1) acc = args[1]; | ||
| else { | ||
| if (arr.length === 0) throw new InterpreterError("Reduce of empty array with no initial value"); | ||
| acc = arr[0]; | ||
| start = 1; | ||
| } | ||
| for (let i = start; i < arr.length; i++) { | ||
| this.tick(); | ||
| acc = await reducer(acc, arr[i], i, arr); | ||
| } | ||
| return acc; | ||
| } | ||
| case "sort": { | ||
| const cmp = cb; | ||
| for (let i = 1; i < arr.length; i++) { | ||
| let j = i; | ||
| while (j > 0) { | ||
| this.tick(); | ||
| if (await cmp(arr[j - 1], arr[j]) <= 0) break; | ||
| [arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]; | ||
| j--; | ||
| } | ||
| } | ||
| return arr; | ||
| } | ||
| default: | ||
| throw new InterpreterError(`Unsupported array method: ${method}`); | ||
| } | ||
| } | ||
| async evalAssignment(node, scope) { | ||
| const value = await this.evalExpr(node.right, scope); | ||
| if (node.operator === "=") { | ||
| this.assignTo(node.left, value, scope); | ||
| return value; | ||
| } | ||
| const current = await this.evalExpr(node.left, scope); | ||
| const op = node.operator.slice(0, -1); | ||
| const next = applyBinary(op, current, value); | ||
| this.assignTo(node.left, next, scope); | ||
| return next; | ||
| } | ||
| /** Assign to an Identifier or MemberExpression target (blocking escape keys). */ | ||
| assignTo(target, value, scope) { | ||
| if (target.type === "Identifier") { | ||
| scope.set(target.name, value); | ||
| return; | ||
| } | ||
| if (target.type === "MemberExpression") { | ||
| throw new InterpreterError("Member assignment is not supported in AgentScript"); | ||
| } | ||
| throw new InterpreterError(`Unsupported assignment target: ${target.type}`); | ||
| } | ||
| bindPattern(pattern, value, scope, mutable) { | ||
| if (pattern.type === "Identifier") { | ||
| scope.declare(pattern.name, value, mutable); | ||
| return; | ||
| } | ||
| if (pattern.type === "ArrayPattern") { | ||
| const arr = isIterable(value) ? [...value] : []; | ||
| pattern.elements.forEach((el, i) => { | ||
| if (el) this.bindPattern(el, arr[i], scope, mutable); | ||
| }); | ||
| return; | ||
| } | ||
| if (pattern.type === "ObjectPattern") { | ||
| const obj = value ?? {}; | ||
| for (const prop of pattern.properties) { | ||
| if (prop.type === "RestElement") continue; | ||
| const key = prop.key.name; | ||
| if (BLOCKED_KEYS.has(key)) throw new InterpreterError(`Forbidden destructured key: ${key}`); | ||
| this.bindPattern(prop.value, obj[key], scope, mutable); | ||
| } | ||
| return; | ||
| } | ||
| throw new InterpreterError(`Unsupported binding pattern: ${pattern.type}`); | ||
| } | ||
| /** Build an interpreter-backed function (declaration / arrow / expression). */ | ||
| makeFunction(node, closure) { | ||
| const self = this; | ||
| return async function interpreted(...args) { | ||
| const fnScope = closure.child(); | ||
| node.params.forEach((p, i) => self.bindPattern(p, args[i], fnScope, true)); | ||
| try { | ||
| if (node.body.type === "BlockStatement") { | ||
| await self.execBlock(node.body.body, fnScope); | ||
| return void 0; | ||
| } | ||
| return await self.evalExpr(node.body, fnScope); | ||
| } catch (e) { | ||
| if (e instanceof ReturnSignal) return e.value; | ||
| throw e; | ||
| } | ||
| }; | ||
| } | ||
| async propKey(prop, scope) { | ||
| if (prop.computed) return String(await this.evalExpr(prop.key, scope)); | ||
| if (prop.key.type === "Identifier") return prop.key.name; | ||
| if (prop.key.type === "Literal") return String(prop.key.value); | ||
| throw new InterpreterError("Unsupported property key"); | ||
| } | ||
| readGlobal(name) { | ||
| if (Object.prototype.hasOwnProperty.call(this.globals, name)) return this.globals[name]; | ||
| throw new InterpreterError(`'${name}' is not defined`); | ||
| } | ||
| }; | ||
| function truthy(v) { | ||
| return Boolean(v); | ||
| } | ||
| function isIterable(v) { | ||
| return v != null && typeof v[Symbol.iterator] === "function"; | ||
| } | ||
| function applyBinary(op, l, r) { | ||
| switch (op) { | ||
| case "+": | ||
| return l + r; | ||
| case "-": | ||
| return l - r; | ||
| case "*": | ||
| return l * r; | ||
| case "/": | ||
| return l / r; | ||
| case "%": | ||
| return l % r; | ||
| case "**": | ||
| return l ** r; | ||
| default: | ||
| throw new InterpreterError(`Unsupported compound operator: ${op}=`); | ||
| } | ||
| } | ||
| // libs/core/src/adapters/interpreter-adapter.ts | ||
| var SAFE_GLOBALS = Object.freeze({ Math, JSON }); | ||
| var DEFAULT_MAX_STEPS = 5e6; | ||
| var DEFAULT_MAX_CALL_DEPTH = 256; | ||
| var InterpreterAdapter = class { | ||
| constructor(options = {}) { | ||
| this.options = options; | ||
| } | ||
| async execute(code, context) { | ||
| const startTime = Date.now(); | ||
| let toolCallCount = 0; | ||
| const maxToolCalls = context.config.maxToolCalls ?? Number.POSITIVE_INFINITY; | ||
| const toolHandler = context.toolHandler ?? context.config.toolHandler; | ||
| const __safe_callTool = async (name, args) => { | ||
| if (++toolCallCount > maxToolCalls) { | ||
| throw new Error(`Tool call limit exceeded (${maxToolCalls})`); | ||
| } | ||
| if (typeof toolHandler !== "function") { | ||
| throw new Error("No toolHandler configured for the sandbox"); | ||
| } | ||
| return toolHandler(name, args); | ||
| }; | ||
| const globals = { ...SAFE_GLOBALS, __safe_callTool }; | ||
| let program; | ||
| try { | ||
| program = acorn.parse(code, { ecmaVersion: "latest", sourceType: "script" }); | ||
| } catch (error) { | ||
| return this.fail(error, startTime, toolCallCount, 0, "SyntaxError"); | ||
| } | ||
| const timeout = context.config.timeout; | ||
| const timer = timeout && timeout > 0 ? setTimeout(() => context.abortController.abort(), timeout) : void 0; | ||
| const interpreter = new Interpreter({ | ||
| globals, | ||
| maxSteps: this.options.maxSteps ?? DEFAULT_MAX_STEPS, | ||
| maxCallDepth: this.options.maxCallDepth ?? DEFAULT_MAX_CALL_DEPTH, | ||
| signal: context.abortController.signal | ||
| }); | ||
| let timeoutHandle; | ||
| const TIMED_OUT = /* @__PURE__ */ Symbol("timeout"); | ||
| const deadline = timeout && timeout > 0 ? new Promise((resolve) => { | ||
| timeoutHandle = setTimeout(() => resolve(TIMED_OUT), timeout); | ||
| }) : void 0; | ||
| try { | ||
| const run = interpreter.run(program); | ||
| run.catch(() => void 0); | ||
| const outcome = deadline ? await Promise.race([run, deadline]) : await run; | ||
| if (outcome === TIMED_OUT) { | ||
| return this.fail( | ||
| new Error(`Execution timed out after ${timeout}ms`), | ||
| startTime, | ||
| toolCallCount, | ||
| interpreter.stepCount, | ||
| "TimeoutError", | ||
| "EXECUTION_TIMEOUT" | ||
| ); | ||
| } | ||
| return { | ||
| success: true, | ||
| value: outcome, | ||
| stats: this.stats(startTime, toolCallCount, interpreter.stepCount) | ||
| }; | ||
| } catch (error) { | ||
| const code2 = error instanceof StepLimitError ? "STEP_LIMIT_EXCEEDED" : void 0; | ||
| return this.fail(error, startTime, toolCallCount, interpreter.stepCount, void 0, code2); | ||
| } finally { | ||
| if (timer) clearTimeout(timer); | ||
| if (timeoutHandle) clearTimeout(timeoutHandle); | ||
| } | ||
| } | ||
| dispose() { | ||
| } | ||
| stats(startTime, toolCallCount, iterationCount) { | ||
| const endTime = Date.now(); | ||
| return { duration: endTime - startTime, toolCallCount, iterationCount, startTime, endTime }; | ||
| } | ||
| fail(error, startTime, toolCallCount, iterationCount, name, code) { | ||
| const err = error instanceof Error ? error : new Error(String(error)); | ||
| const execError = { | ||
| message: err.message, | ||
| name: name ?? err.name, | ||
| ...err.stack ? { stack: err.stack } : {}, | ||
| ...code ? { code } : {} | ||
| }; | ||
| return { success: false, error: execError, stats: this.stats(startTime, toolCallCount, iterationCount) }; | ||
| } | ||
| }; | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| Interpreter, | ||
| InterpreterAdapter, | ||
| InterpreterError, | ||
| StepLimitError | ||
| }); |
+4
-3
| { | ||
| "name": "@enclave-vm/core", | ||
| "version": "2.14.0", | ||
| "version": "2.14.1", | ||
| "description": "Sandbox runtime for secure JavaScript code execution", | ||
@@ -47,4 +47,5 @@ "author": "AgentFront <info@agentfront.dev>", | ||
| "@babel/standalone": "^7.29.0", | ||
| "@enclave-vm/ast": "2.14.0", | ||
| "@enclave-vm/types": "2.14.0", | ||
| "@enclave-vm/ast": "2.14.1", | ||
| "@enclave-vm/types": "2.14.1", | ||
| "@types/estree": "1.0.8", | ||
| "acorn": "8.15.0", | ||
@@ -51,0 +52,0 @@ "acorn-walk": "8.3.4", |
| { | ||
| "name": "@enclave-vm/core", | ||
| "version": "2.14.0", | ||
| "version": "2.14.1", | ||
| "description": "Sandbox runtime for secure JavaScript code execution", | ||
@@ -49,4 +49,5 @@ "author": "AgentFront <info@agentfront.dev>", | ||
| "@babel/standalone": "^7.29.0", | ||
| "@enclave-vm/ast": "2.14.0", | ||
| "@enclave-vm/types": "2.14.0", | ||
| "@enclave-vm/ast": "2.14.1", | ||
| "@enclave-vm/types": "2.14.1", | ||
| "@types/estree": "1.0.8", | ||
| "acorn": "8.15.0", | ||
@@ -53,0 +54,0 @@ "acorn-walk": "8.3.4", |
+1
-1
@@ -5,2 +5,2 @@ export { InterpreterAdapter } from './adapters/interpreter-adapter'; | ||
| export type { InterpreterOptions } from './interpreter/interpreter'; | ||
| export type { ExecutionContext, ExecutionResult, ExecutionError, ExecutionStats, ToolHandler, } from './types'; | ||
| export type { ExecutionContext, ExecutionResult, ExecutionError, ExecutionStats, ToolHandler } from './types'; |
1159704
4.28%69
2.99%31960
4.57%10
11.11%+ Added
+ Added
+ Added
- Removed
- Removed
Updated
Updated