🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@enclave-vm/core

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@enclave-vm/core - npm Package Compare versions

Comparing version
2.14.0
to
2.14.1
+682
esm/worker.mjs
// 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
};
"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",

@@ -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';