Showing the changes to packages/kernel/runtime-quickjs/src (2 of 865 files). Show all
Unified · Split
| @@ -1,314 +1,186 @@ | |||
| 1 | import { describe, expect, it } from "@effect/vitest"; | 1 | import { describe, expect, it } from "@effect/vitest"; |
| 2 | import * as Data from "effect/Data"; | ||
| 2 | import * as Effect from "effect/Effect"; | 3 | import * as Effect from "effect/Effect"; |
| 3 | import * as Schema from "effect/Schema"; | ||
| 4 | |||
| 5 | import { | ||
| 6 | allowAllToolInteractions, | ||
| 7 | makeToolInvokerFromTools, | ||
| 8 | toExecutorTool, | ||
| 9 | } from "@executor/codemode-core"; | ||
| 10 | 4 | ||
| 5 | import type { SandboxToolInvoker } from "@executor/codemode-core"; | ||
| 11 | import { makeQuickJsExecutor } from "./index"; | 6 | import { makeQuickJsExecutor } from "./index"; |
| 12 | 7 | ||
| 13 | const numberPairInputSchema = Schema.standardSchemaV1( | 8 | class UnknownToolError extends Data.TaggedError("UnknownToolError")<{ |
| 14 | Schema.Struct({ | 9 | readonly path: string; |
| 15 | a: Schema.Number, | 10 | }> {} |
| 16 | b: Schema.Number, | 11 | |
| 17 | }), | 12 | const makeTestInvoker = ( |
| 18 | ); | 13 | handlers: Record<string, (args: unknown) => unknown>, |
| 19 | 14 | ): SandboxToolInvoker => ({ | |
| 20 | const messageInputSchema = Schema.standardSchemaV1( | 15 | invoke: ({ path, args }) => { |
| 21 | Schema.Struct({ | 16 | const handler = handlers[path]; |
| 22 | message: Schema.String, | 17 | if (!handler) { |
| 23 | }), | 18 | return Effect.fail(new UnknownToolError({ path })); |
| 24 | ); | 19 | } |
| 25 | 20 | return Effect.try(() => handler(args)); | |
| 26 | const discoverInputSchema = Schema.standardSchemaV1( | ||
| 27 | Schema.Struct({ | ||
| 28 | query: Schema.String, | ||
| 29 | limit: Schema.optional(Schema.Number), | ||
| 30 | }), | ||
| 31 | ); | ||
| 32 | |||
| 33 | const describeToolInputSchema = Schema.standardSchemaV1( | ||
| 34 | Schema.Struct({ | ||
| 35 | path: Schema.String, | ||
| 36 | includeSchemas: Schema.optional(Schema.Boolean), | ||
| 37 | }), | ||
| 38 | ); | ||
| 39 | |||
| 40 | const repoInputSchema = Schema.standardSchemaV1( | ||
| 41 | Schema.Struct({ | ||
| 42 | owner: Schema.String, | ||
| 43 | repo: Schema.String, | ||
| 44 | }), | ||
| 45 | ); | ||
| 46 | |||
| 47 | const tools = { | ||
| 48 | "math.add": { | ||
| 49 | description: "Add two numbers", | ||
| 50 | inputSchema: numberPairInputSchema, | ||
| 51 | execute: ({ a, b }: { a: number; b: number }) => ({ sum: a + b }), | ||
| 52 | }, | 21 | }, |
| 53 | "notifications.send": toExecutorTool({ | 22 | }); |
| 54 | tool: { | ||
| 55 | description: "Send a message", | ||
| 56 | inputSchema: messageInputSchema, | ||
| 57 | execute: ({ message }: { message: string }) => ({ | ||
| 58 | delivered: true, | ||
| 59 | message, | ||
| 60 | }), | ||
| 61 | }, | ||
| 62 | metadata: { | ||
| 63 | interaction: "required", | ||
| 64 | }, | ||
| 65 | }), | ||
| 66 | }; | ||
| 67 | |||
| 68 | describe("runtime-quickjs", () => { | ||
| 69 | it.effect("executes simple code and returns result", () => | ||
| 70 | Effect.gen(function* () { | ||
| 71 | const executor = makeQuickJsExecutor(); | ||
| 72 | const toolInvoker = makeToolInvokerFromTools({ tools }); | ||
| 73 | |||
| 74 | const output = yield* executor.execute( | ||
| 75 | "return 1 + 2;", | ||
| 76 | toolInvoker, | ||
| 77 | ); | ||
| 78 | |||
| 79 | expect(output.result).toBe(3); | ||
| 80 | expect(output.error).toBeUndefined(); | ||
| 81 | }), | ||
| 82 | ); | ||
| 83 | |||
| 84 | it.effect("executes code with tool calls", () => | ||
| 85 | Effect.gen(function* () { | ||
| 86 | const executor = makeQuickJsExecutor(); | ||
| 87 | const toolInvoker = makeToolInvokerFromTools({ | ||
| 88 | tools, | ||
| 89 | onToolInteraction: allowAllToolInteractions, | ||
| 90 | }); | ||
| 91 | |||
| 92 | const output = yield* executor.execute( | ||
| 93 | [ | ||
| 94 | "const math = await tools.math.add({ a: 19, b: 23 });", | ||
| 95 | "await tools.notifications.send({ message: `sum is ${math.sum}` });", | ||
| 96 | "return math;", | ||
| 97 | ].join("\n"), | ||
| 98 | toolInvoker, | ||
| 99 | ); | ||
| 100 | |||
| 101 | expect(output.result).toEqual({ sum: 42 }); | ||
| 102 | expect(output.error).toBeUndefined(); | ||
| 103 | }), | ||
| 104 | ); | ||
| 105 | |||
| 106 | it.effect("captures console output in logs", () => | ||
| 107 | Effect.gen(function* () { | ||
| 108 | const executor = makeQuickJsExecutor(); | ||
| 109 | const toolInvoker = makeToolInvokerFromTools({ tools }); | ||
| 110 | |||
| 111 | const output = yield* executor.execute( | ||
| 112 | [ | ||
| 113 | 'console.log("hello from quickjs");', | ||
| 114 | 'console.warn("a warning");', | ||
| 115 | 'console.error("an error");', | ||
| 116 | "return 42;", | ||
| 117 | ].join("\n"), | ||
| 118 | toolInvoker, | ||
| 119 | ); | ||
| 120 | 23 | ||
| 121 | expect(output.result).toBe(42); | 24 | const executor = makeQuickJsExecutor({ timeoutMs: 5_000 }); |
| 122 | expect(output.logs).toContain("[log] hello from quickjs"); | ||
| 123 | expect(output.logs).toContain("[warn] a warning"); | ||
| 124 | expect(output.logs).toContain("[error] an error"); | ||
| 125 | }), | ||
| 126 | ); | ||
| 127 | 25 | ||
| 128 | it.effect("reports execution errors without crashing", () => | 26 | describe("quickjs executor", () => { |
| 27 | it.effect("runs plain code", () => | ||
| 129 | Effect.gen(function* () { | 28 | Effect.gen(function* () { |
| 130 | const executor = makeQuickJsExecutor(); | 29 | const result = yield* executor.execute( |
| 131 | const toolInvoker = makeToolInvokerFromTools({ tools }); | 30 | `return 1 + 2`, |
| 132 | 31 | makeTestInvoker({}), | |
| 133 | const output = yield* executor.execute( | ||
| 134 | 'throw new Error("boom");', | ||
| 135 | toolInvoker, | ||
| 136 | ); | 32 | ); |
| 137 | 33 | expect(result.result).toBe(3); | |
| 138 | expect(output.result).toBeNull(); | 34 | expect(result.error).toBeUndefined(); |
| 139 | expect(output.error).toContain("boom"); | ||
| 140 | }), | 35 | }), |
| 141 | ); | 36 | ); |
| 142 | 37 | ||
| 143 | it.effect("handles tool call errors gracefully", () => | 38 | it.effect("invokes a tool and returns its result", () => |
| 144 | Effect.gen(function* () { | 39 | Effect.gen(function* () { |
| 145 | const failingTools = { | 40 | const invoker = makeTestInvoker({ |
| 146 | "broken.thing": { | 41 | "math.add": (args) => { |
| 147 | description: "Always fails", | 42 | const { a, b } = args as { a: number; b: number }; |
| 148 | inputSchema: Schema.standardSchemaV1(Schema.Struct({})), | 43 | return { sum: a + b }; |
| 149 | execute: () => { | ||
| 150 | throw new Error("tool is broken"); | ||
| 151 | }, | ||
| 152 | }, | 44 | }, |
| 153 | }; | ||
| 154 | |||
| 155 | const executor = makeQuickJsExecutor(); | ||
| 156 | const toolInvoker = makeToolInvokerFromTools({ | ||
| 157 | tools: failingTools, | ||
| 158 | }); | 45 | }); |
| 159 | 46 | ||
| 160 | const output = yield* executor.execute( | 47 | const result = yield* executor.execute( |
| 161 | "return await tools.broken.thing({});", | 48 | ` |
| 162 | toolInvoker, | 49 | const res = await tools.math.add({ a: 5, b: 3 }); |
| 50 | return res.sum; | ||
| 51 | `, | ||
| 52 | invoker, | ||
| 163 | ); | 53 | ); |
| 164 | 54 | ||
| 165 | expect(output.result).toBeNull(); | 55 | expect(result.error).toBeUndefined(); |
| 166 | expect(output.error).toContain("tool is broken"); | 56 | expect(result.result).toBe(8); |
| 167 | }), | 57 | }), |
| 168 | ); | 58 | ); |
| 169 | 59 | ||
| 170 | it.effect("respects timeout", () => | 60 | it.effect("invokes multiple tools in sequence", () => |
| 171 | Effect.gen(function* () { | 61 | Effect.gen(function* () { |
| 172 | const executor = makeQuickJsExecutor({ | 62 | const invoker = makeTestInvoker({ |
| 173 | timeoutMs: 250, | 63 | "users.get": (args) => { |
| 64 | const { id } = args as { id: number }; | ||
| 65 | return { id, name: `User ${id}` }; | ||
| 66 | }, | ||
| 67 | "users.greet": (args) => { | ||
| 68 | const { name } = args as { name: string }; | ||
| 69 | return { message: `Hello, ${name}!` }; | ||
| 70 | }, | ||
| 174 | }); | 71 | }); |
| 175 | const toolInvoker = makeToolInvokerFromTools({ tools }); | ||
| 176 | 72 | ||
| 177 | const output = yield* executor.execute( | 73 | const result = yield* executor.execute( |
| 178 | "while (true) {}", | 74 | ` |
| 179 | toolInvoker, | 75 | const user = await tools.users.get({ id: 42 }); |
| 76 | const greeting = await tools.users.greet({ name: user.name }); | ||
| 77 | return greeting.message; | ||
| 78 | `, | ||
| 79 | invoker, | ||
| 180 | ); | 80 | ); |
| 181 | 81 | ||
| 182 | expect(output.result).toBeNull(); | 82 | expect(result.error).toBeUndefined(); |
| 183 | expect(output.error).toContain("timed out"); | 83 | expect(result.result).toBe("Hello, User 42!"); |
| 184 | }), | 84 | }), |
| 185 | ); | 85 | ); |
| 186 | 86 | ||
| 187 | it.effect("network access is denied by default", () => | 87 | it.effect("handles tool errors", () => |
| 188 | Effect.gen(function* () { | 88 | Effect.gen(function* () { |
| 189 | const executor = makeQuickJsExecutor(); | 89 | const invoker = makeTestInvoker({ |
| 190 | const toolInvoker = makeToolInvokerFromTools({ tools }); | 90 | "db.query": () => { |
| 91 | throw new Error("connection refused"); | ||
| 92 | }, | ||
| 93 | }); | ||
| 191 | 94 | ||
| 192 | const output = yield* executor.execute( | 95 | const result = yield* executor.execute( |
| 193 | 'await fetch("https://example.com"); return 1;', | 96 | ` |
| 194 | toolInvoker, | 97 | try { |
| 98 | await tools.db.query({ sql: "SELECT 1" }); | ||
| 99 | return "should not reach"; | ||
| 100 | } catch (e) { | ||
| 101 | return "caught: " + e.message; | ||
| 102 | } | ||
| 103 | `, | ||
| 104 | invoker, | ||
| 195 | ); | 105 | ); |
| 196 | 106 | ||
| 197 | expect(output.result).toBeNull(); | 107 | expect(result.error).toBeUndefined(); |
| 198 | expect(output.error).toContain("fetch is disabled in QuickJS executor"); | 108 | expect(result.result).toContain("caught:"); |
| 199 | }), | 109 | }), |
| 200 | ); | 110 | ); |
| 201 | 111 | ||
| 202 | it.effect("multiple sequential tool calls work correctly", () => | 112 | it.effect("handles unknown tool path", () => |
| 203 | Effect.gen(function* () { | 113 | Effect.gen(function* () { |
| 204 | const executor = makeQuickJsExecutor(); | 114 | const invoker = makeTestInvoker({}); |
| 205 | const toolInvoker = makeToolInvokerFromTools({ tools }); | 115 | |
| 206 | 116 | const result = yield* executor.execute( | |
| 207 | const output = yield* executor.execute( | 117 | ` |
| 208 | [ | 118 | try { |
| 209 | "const r1 = await tools.math.add({ a: 1, b: 2 });", | 119 | await tools.nonexistent.thing({ x: 1 }); |
| 210 | "const r2 = await tools.math.add({ a: r1.sum, b: 10 });", | 120 | return "should not reach"; |
| 211 | "const r3 = await tools.math.add({ a: r2.sum, b: 100 });", | 121 | } catch (e) { |
| 212 | "return r3;", | 122 | return "caught: " + e.message; |
| 213 | ].join("\n"), | 123 | } |
| 214 | toolInvoker, | 124 | `, |
| 125 | invoker, | ||
| 215 | ); | 126 | ); |
| 216 | 127 | ||
| 217 | expect(output.result).toEqual({ sum: 113 }); | 128 | expect(result.error).toBeUndefined(); |
| 218 | expect(output.error).toBeUndefined(); | 129 | expect(result.result).toContain("caught:"); |
| 219 | }), | 130 | }), |
| 220 | ); | 131 | ); |
| 221 | 132 | ||
| 222 | it.effect("does not expose internal executor bridge globals to user code", () => | 133 | it.effect("captures console.log output", () => |
| 223 | Effect.gen(function* () { | 134 | Effect.gen(function* () { |
| 224 | const executor = makeQuickJsExecutor(); | 135 | const result = yield* executor.execute( |
| 225 | const toolInvoker = makeToolInvokerFromTools({ tools }); | 136 | ` |
| 226 | 137 | console.log("hello from sandbox"); | |
| 227 | const output = yield* executor.execute( | 138 | console.warn("a warning"); |
| 228 | [ | 139 | return "done"; |
| 229 | "return {", | 140 | `, |
| 230 | " hasInvokeTool: typeof globalThis.__executor_invokeTool !== 'undefined',", | 141 | makeTestInvoker({}), |
| 231 | " hasLogBridge: typeof globalThis.__executor_log !== 'undefined',", | ||
| 232 | " globalKeys: Object.keys(globalThis).sort(),", | ||
| 233 | "};", | ||
| 234 | ].join("\n"), | ||
| 235 | toolInvoker, | ||
| 236 | ); | 142 | ); |
| 237 | 143 | ||
| 238 | expect(output.result).toEqual({ | 144 | expect(result.error).toBeUndefined(); |
| 239 | hasInvokeTool: false, | 145 | expect(result.result).toBe("done"); |
| 240 | hasLogBridge: false, | 146 | expect(result.logs).toContainEqual("[log] hello from sandbox"); |
| 241 | globalKeys: [], | 147 | expect(result.logs).toContainEqual("[warn] a warning"); |
| 242 | }); | ||
| 243 | expect(output.error).toBeUndefined(); | ||
| 244 | }), | 148 | }), |
| 245 | ); | 149 | ); |
| 246 | 150 | ||
| 247 | it.effect("supports the documented discovery workflow shape", () => | 151 | it.effect("passes tool result into next tool call", () => |
| 248 | Effect.gen(function* () { | 152 | Effect.gen(function* () { |
| 249 | const executor = makeQuickJsExecutor(); | 153 | const invoker = makeTestInvoker({ |
| 250 | const toolInvoker = makeToolInvokerFromTools({ | 154 | "stripe.customers.list": () => ({ |
| 251 | tools: { | 155 | data: [ |
| 252 | discover: { | 156 | { id: "cus_1", email: "alice@example.com" }, |
| 253 | inputSchema: discoverInputSchema, | 157 | { id: "cus_2", email: "bob@example.com" }, |
| 254 | execute: () => ({ | 158 | ], |
| 255 | bestPath: "github.issues.list", | 159 | }), |
| 256 | results: [ | 160 | "stripe.invoices.create": (args) => { |
| 257 | { | 161 | const { customer, amount } = args as { customer: string; amount: number }; |
| 258 | path: "github.issues.list", | 162 | return { id: "inv_1", customer, amount }; |
| 259 | score: 0.99, | ||
| 260 | }, | ||
| 261 | ], | ||
| 262 | total: 1, | ||
| 263 | }), | ||
| 264 | }, | ||
| 265 | "describe.tool": { | ||
| 266 | inputSchema: describeToolInputSchema, | ||
| 267 | execute: ({ path }: { path: string; includeSchemas?: boolean }) => ({ | ||
| 268 | path, | ||
| 269 | contract: { | ||
| 270 | inputTypePreview: "{ owner: string; repo: string }", | ||
| 271 | }, | ||
| 272 | }), | ||
| 273 | }, | ||
| 274 | "github.issues.list": { | ||
| 275 | inputSchema: repoInputSchema, | ||
| 276 | execute: ({ owner, repo }: { owner: string; repo: string }) => ({ | ||
| 277 | owner, | ||
| 278 | repo, | ||
| 279 | issues: [{ id: "issue_1" }], | ||
| 280 | }), | ||
| 281 | }, | ||
| 282 | }, | 163 | }, |
| 283 | }); | 164 | }); |
| 284 | 165 | ||
| 285 | const output = yield* executor.execute( | 166 | const result = yield* executor.execute( |
| 286 | [ | 167 | ` |
| 287 | 'const { results, bestPath } = await tools.discover({ query: "github issues", limit: 5 });', | 168 | const customers = await tools.stripe.customers.list(); |
| 288 | "const path = bestPath ?? results[0]?.path;", | 169 | const invoice = await tools.stripe.invoices.create({ |
| 289 | 'if (!path) throw new Error("No matching tools found.");', | 170 | customer: customers.data[0].id, |
| 290 | "const detail = await tools.describe.tool({ path, includeSchemas: true });", | 171 | amount: 5000, |
| 291 | 'const issues = await tools.github.issues.list({ owner: "openai", repo: "codex" });', | 172 | }); |
| 292 | "return { path, detail, issues };", | 173 | return invoice; |
| 293 | ].join("\n"), | 174 | `, |
| 294 | toolInvoker, | 175 | invoker, |
| 295 | ); | 176 | ); |
| 296 | 177 | ||
| 297 | expect(output.result).toEqual({ | 178 | expect(result.error).toBeUndefined(); |
| 298 | path: "github.issues.list", | 179 | expect(result.result).toEqual({ |
| 299 | detail: { | 180 | id: "inv_1", |
| 300 | path: "github.issues.list", | 181 | customer: "cus_1", |
| 301 | contract: { | 182 | amount: 5000, |
| 302 | inputTypePreview: "{ owner: string; repo: string }", | ||
| 303 | }, | ||
| 304 | }, | ||
| 305 | issues: { | ||
| 306 | owner: "openai", | ||
| 307 | repo: "codex", | ||
| 308 | issues: [{ id: "issue_1" }], | ||
| 309 | }, | ||
| 310 | }); | 183 | }); |
| 311 | expect(output.error).toBeUndefined(); | ||
| 312 | }), | 184 | }), |
| 313 | ); | 185 | ); |
| 314 | }); | 186 | }); |
| @@ -1,8 +1,9 @@ | |||
| 1 | import type { | 1 | import type { |
| 2 | CodeExecutor, | 2 | CodeExecutor, |
| 3 | ExecuteResult, | 3 | ExecuteResult, |
| 4 | ToolInvoker, | 4 | SandboxToolInvoker, |
| 5 | } from "@executor/codemode-core"; | 5 | } from "@executor/codemode-core"; |
| 6 | import * as Data from "effect/Data"; | ||
| 6 | import * as Effect from "effect/Effect"; | 7 | import * as Effect from "effect/Effect"; |
| 7 | import { | 8 | import { |
| 8 | getQuickJS, | 9 | getQuickJS, |
| @@ -11,6 +12,7 @@ import { · 2 unmodified lines | |||
| 11 | type QuickJSDeferredPromise, | 12 | type QuickJSDeferredPromise, |
| 12 | type QuickJSHandle, | 13 | type QuickJSHandle, |
| 13 | type QuickJSRuntime, | 14 | type QuickJSRuntime, |
| 15 | type QuickJSWASMModule, | ||
| 14 | } from "quickjs-emscripten"; | 16 | } from "quickjs-emscripten"; |
| 15 | 17 | ||
| 16 | export type QuickJsExecutorOptions = { | 18 | export type QuickJsExecutorOptions = { |
| @@ -19,6 +21,20 @@ export type QuickJsExecutorOptions = { · 2 unmodified lines | |||
| 19 | maxStackSizeBytes?: number; | 21 | maxStackSizeBytes?: number; |
| 20 | }; | 22 | }; |
| 21 | 23 | ||
| 24 | // Allow pre-loading a QuickJS module (e.g. with custom WASM bytes for compiled binaries) | ||
| 25 | let preloadedModule: QuickJSWASMModule | null = null; | ||
| 26 | |||
| 27 | export const setQuickJSModule = (mod: QuickJSWASMModule) => { | ||
| 28 | preloadedModule = mod; | ||
| 29 | }; | ||
| 30 | |||
| 31 | const resolveQuickJS = (): Promise<QuickJSWASMModule> => | ||
| 32 | preloadedModule ? Promise.resolve(preloadedModule) : getQuickJS(); | ||
| 33 | |||
| 34 | class QuickJsExecutionError extends Data.TaggedError("QuickJsExecutionError")<{ | ||
| 35 | readonly message: string; | ||
| 36 | }> {} | ||
| 37 | |||
| 22 | const DEFAULT_TIMEOUT_MS = 5 * 60_000; | 38 | const DEFAULT_TIMEOUT_MS = 5 * 60_000; |
| 23 | const EXECUTION_FILENAME = "executor-quickjs-runtime.js"; | 39 | const EXECUTION_FILENAME = "executor-quickjs-runtime.js"; |
| 24 | 40 | ||
| @@ -178,7 +194,7 @@ const createLogBridge = ( · 153 unmodified lines | |||
| 178 | 194 | ||
| 179 | const createToolBridge = ( | 195 | const createToolBridge = ( |
| 180 | context: QuickJSContext, | 196 | context: QuickJSContext, |
| 181 | toolInvoker: ToolInvoker, | 197 | toolInvoker: SandboxToolInvoker, |
| 182 | pendingDeferreds: Set<QuickJSDeferredPromise>, | 198 | pendingDeferreds: Set<QuickJSDeferredPromise>, |
| 183 | ): QuickJSHandle => | 199 | ): QuickJSHandle => |
| 184 | context.newFunction("__executor_invokeTool", (pathHandle, argsHandle) => { | 200 | context.newFunction("__executor_invokeTool", (pathHandle, argsHandle) => { |
| @@ -288,13 +304,13 @@ const drainAsync = async ( · 103 unmodified lines | |||
| 288 | const evaluateInQuickJs = async ( | 304 | const evaluateInQuickJs = async ( |
| 289 | options: QuickJsExecutorOptions, | 305 | options: QuickJsExecutorOptions, |
| 290 | code: string, | 306 | code: string, |
| 291 | toolInvoker: ToolInvoker, | 307 | toolInvoker: SandboxToolInvoker, |
| 292 | ): Promise<ExecuteResult> => { | 308 | ): Promise<ExecuteResult> => { |
| 293 | const timeoutMs = Math.max(100, options.timeoutMs ?? DEFAULT_TIMEOUT_MS); | 309 | const timeoutMs = Math.max(100, options.timeoutMs ?? DEFAULT_TIMEOUT_MS); |
| 294 | const deadlineMs = Date.now() + timeoutMs; | 310 | const deadlineMs = Date.now() + timeoutMs; |
| 295 | const logs: string[] = []; | 311 | const logs: string[] = []; |
| 296 | const pendingDeferreds = new Set<QuickJSDeferredPromise>(); | 312 | const pendingDeferreds = new Set<QuickJSDeferredPromise>(); |
| 297 | const QuickJS = await getQuickJS(); | 313 | const QuickJS = await resolveQuickJS(); |
| 298 | const runtime = QuickJS.newRuntime(); | 314 | const runtime = QuickJS.newRuntime(); |
| 299 | 315 | ||
| 300 | try { | 316 | try { |
| @@ -399,16 +415,16 @@ const evaluateInQuickJs = async ( · 98 unmodified lines | |||
| 399 | const runInQuickJs = ( | 415 | const runInQuickJs = ( |
| 400 | options: QuickJsExecutorOptions, | 416 | options: QuickJsExecutorOptions, |
| 401 | code: string, | 417 | code: string, |
| 402 | toolInvoker: ToolInvoker, | 418 | toolInvoker: SandboxToolInvoker, |
| 403 | ): Effect.Effect<ExecuteResult, Error> => | 419 | ): Effect.Effect<ExecuteResult, QuickJsExecutionError> => |
| 404 | Effect.tryPromise({ | 420 | Effect.tryPromise({ |
| 405 | try: () => evaluateInQuickJs(options, code, toolInvoker), | 421 | try: () => evaluateInQuickJs(options, code, toolInvoker), |
| 406 | catch: toError, | 422 | catch: (cause) => new QuickJsExecutionError({ message: String(cause) }), |
| 407 | }); | 423 | }); |
| 408 | 424 | ||
| 409 | export const makeQuickJsExecutor = ( | 425 | export const makeQuickJsExecutor = ( |
| 410 | options: QuickJsExecutorOptions = {}, | 426 | options: QuickJsExecutorOptions = {}, |
| 411 | ): CodeExecutor => ({ | 427 | ): CodeExecutor => ({ |
| 412 | execute: (code: string, toolInvoker: ToolInvoker) => | 428 | execute: (code: string, toolInvoker: SandboxToolInvoker) => |
| 413 | runInQuickJs(options, code, toolInvoker), | 429 | runInQuickJs(options, code, toolInvoker), |
| 414 | }); | 430 | }); |