Files
ask262/src/agent-tools/evaluateInEngine262.ts
T

153 lines
5.0 KiB
TypeScript

/**
* Evaluate JavaScript code in engine262 and capture spec section marks.
* Executes code in the engine262 JavaScript engine and returns the captured
* ECMAScript spec section marks as JSON.
*/
import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";
const evaluateSchema = z.object({
code: z.string().describe("JavaScript code to execute in engine262"),
});
// Type definitions for engine262 module
interface MarkData {
readonly sectionIds: string[];
readonly fileRelativePath: string;
readonly lineNumber: number;
readonly important: boolean;
}
// biome-ignore lint/suspicious/noExplicitAny: engine262 is an external module without TypeScript types
let engine262Module: any = null;
/**
* Lazy load engine262 module
*/
async function loadEngine262() {
if (!engine262Module) {
// Dynamic import of engine262 from local path
engine262Module = await import("../../engine262/lib/engine262.mjs");
}
return engine262Module;
}
/**
* Creates the evaluateInEngine262 tool.
* Executes JavaScript code in engine262 and captures spec section marks.
* @returns The evaluate_in_engine262 tool instance
*/
export function createEvaluateInEngine262Tool() {
return new DynamicStructuredTool({
name: "ask262_evaluate_in_engine262",
description:
"Executes JavaScript code in the engine262 JavaScript engine and captures which ECMAScript specification sections are hit during execution. " +
"Returns the full marks array as JSON. Useful for understanding how specific JavaScript operations map to the ECMAScript spec. " +
"ask262Debug is available globally in the execution context (no import needed). " +
"Use ask262Debug.startImportant() and ask262Debug.stopImportant() to mark important sections. " +
"Example: ask262Debug.startImportant(); let x = 1 + 2; ask262Debug.stopImportant();",
schema: evaluateSchema,
func: async ({ code }) => {
try {
const engine = await loadEngine262();
const ask262Debug = engine.ask262Debug as {
marks: MarkData[];
startTrace: () => void;
stopTrace: () => void;
startImportant: () => void;
stopImportant: () => void;
};
const Agent = engine.Agent;
const ManagedRealm = engine.ManagedRealm;
const setSurroundingAgent = engine.setSurroundingAgent;
const OrdinaryObjectCreate = engine.OrdinaryObjectCreate;
const CreateBuiltinFunction = engine.CreateBuiltinFunction;
const CreateDataProperty = engine.CreateDataProperty;
const Value = engine.Value;
const skipDebugger = engine.skipDebugger;
// Reset marks from previous runs
ask262Debug.marks = [];
// Set up agent and realm
const agent = new Agent();
setSurroundingAgent(agent);
const realm = new ManagedRealm();
// Expose ask262Debug controls to the evaluated code
realm.scope(() => {
const debugObj = OrdinaryObjectCreate(
agent.intrinsic("%Object.prototype%"),
);
skipDebugger(
CreateDataProperty(
realm.GlobalObject,
Value("ask262Debug"),
debugObj,
),
);
const startImportant = CreateBuiltinFunction(
() => {
ask262Debug.startImportant();
return Value.undefined;
},
0,
Value("startImportant"),
[],
);
skipDebugger(
CreateDataProperty(
debugObj,
Value("startImportant"),
startImportant,
),
);
const stopImportant = CreateBuiltinFunction(
() => {
ask262Debug.stopImportant();
return Value.undefined;
},
0,
Value("stopImportant"),
[],
);
skipDebugger(
CreateDataProperty(debugObj, Value("stopImportant"), stopImportant),
);
});
// Start tracing
ask262Debug.startTrace();
// Execute the code
realm.evaluateScript(code);
// Stop tracing
ask262Debug.stopTrace();
// Get captured marks
const marks = ask262Debug.marks;
// Filter and group marks by important flag
const importantMarks = marks.filter((m) => m.important);
const otherMarks = marks.filter((m) => !m.important);
// Extract sectionIds, remove fileRelativePath and lineNumber
const result = {
importantSections: importantMarks.map((m) => m.sectionIds),
otherSections: otherMarks.map((m) => m.sectionIds),
};
// Return compressed JSON
return JSON.stringify(result);
} catch (error) {
console.error(`[Tool: ask262_evaluate_in_engine262] Error: ${error}`);
return `Error executing code in engine262: ${error instanceof Error ? error.message : String(error)}`;
}
},
});
}