import { describe, expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema"; import { allowAllToolInteractions, makeToolInvokerFromTools, toExecutorTool,} from "@executor/codemode-core"; import { makeQuickJsExecutor } from "./index"; const numberPairInputSchema = Schema.standardSchemaV1( Schema.Struct({ a: Schema.Number, b: Schema.Number, }),); const messageInputSchema = Schema.standardSchemaV1( Schema.Struct({ message: Schema.String, }),); const tools = { "math.add": { description: "Add two numbers", inputSchema: numberPairInputSchema, execute: ({ a, b }: { a: number; b: number }) => ({ sum: a + b }), }, "notifications.send": toExecutorTool({ tool: { description: "Send a message", inputSchema: messageInputSchema, execute: ({ message }: { message: string }) => ({ delivered: true, message, }), }, metadata: { interaction: "required", }, }),}; describe("runtime-quickjs", () => { it.effect("executes simple code and returns result", () => Effect.gen(function* () { const executor = makeQuickJsExecutor(); const toolInvoker = makeToolInvokerFromTools({ tools }); const output = yield* executor.execute( "return 1 + 2;", toolInvoker, ); expect(output.result).toBe(3); expect(output.error).toBeUndefined(); }), ); it.effect("executes code with tool calls", () => Effect.gen(function* () { const executor = makeQuickJsExecutor(); const toolInvoker = makeToolInvokerFromTools({ tools, onToolInteraction: allowAllToolInteractions, }); const output = yield* executor.execute( [ "const math = await tools.math.add({ a: 19, b: 23 });", "await tools.notifications.send({ message: `sum is ${math.sum}` });", "return math;", ].join("\n"), toolInvoker, ); expect(output.result).toEqual({ sum: 42 }); expect(output.error).toBeUndefined(); }), ); it.effect("captures console output in logs", () => Effect.gen(function* () { const executor = makeQuickJsExecutor(); const toolInvoker = makeToolInvokerFromTools({ tools }); const output = yield* executor.execute( [ 'console.log("hello from quickjs");', 'console.warn("a warning");', 'console.error("an error");', "return 42;", ].join("\n"), toolInvoker, ); expect(output.result).toBe(42); expect(output.logs).toContain("[log] hello from quickjs"); expect(output.logs).toContain("[warn] a warning"); expect(output.logs).toContain("[error] an error"); }), ); it.effect("reports execution errors without crashing", () => Effect.gen(function* () { const executor = makeQuickJsExecutor(); const toolInvoker = makeToolInvokerFromTools({ tools }); const output = yield* executor.execute( 'throw new Error("boom");', toolInvoker, ); expect(output.result).toBeNull(); expect(output.error).toContain("boom"); }), ); it.effect("handles tool call errors gracefully", () => Effect.gen(function* () { const failingTools = { "broken.thing": { description: "Always fails", inputSchema: Schema.standardSchemaV1(Schema.Struct({})), execute: () => { throw new Error("tool is broken"); }, }, }; const executor = makeQuickJsExecutor(); const toolInvoker = makeToolInvokerFromTools({ tools: failingTools, }); const output = yield* executor.execute( "return await tools.broken.thing({});", toolInvoker, ); expect(output.result).toBeNull(); expect(output.error).toContain("tool is broken"); }), ); it.effect("respects timeout", () => Effect.gen(function* () { const executor = makeQuickJsExecutor({ timeoutMs: 250, }); const toolInvoker = makeToolInvokerFromTools({ tools }); const output = yield* executor.execute( "while (true) {}", toolInvoker, ); expect(output.result).toBeNull(); expect(output.error).toContain("timed out"); }), ); it.effect("network access is denied by default", () => Effect.gen(function* () { const executor = makeQuickJsExecutor(); const toolInvoker = makeToolInvokerFromTools({ tools }); const output = yield* executor.execute( 'await fetch("https://example.com"); return 1;', toolInvoker, ); expect(output.result).toBeNull(); expect(output.error).toContain("fetch is disabled in QuickJS executor"); }), ); it.effect("multiple sequential tool calls work correctly", () => Effect.gen(function* () { const executor = makeQuickJsExecutor(); const toolInvoker = makeToolInvokerFromTools({ tools }); const output = yield* executor.execute( [ "const r1 = await tools.math.add({ a: 1, b: 2 });", "const r2 = await tools.math.add({ a: r1.sum, b: 10 });", "const r3 = await tools.math.add({ a: r2.sum, b: 100 });", "return r3;", ].join("\n"), toolInvoker, ); expect(output.result).toEqual({ sum: 113 }); expect(output.error).toBeUndefined(); }), );});
import { describe, expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Schema from "effect/Schema";
import {
allowAllToolInteractions,
makeToolInvokerFromTools,
toExecutorTool,
} from "@executor/codemode-core";
import { makeQuickJsExecutor } from "./index";
const numberPairInputSchema = Schema.standardSchemaV1(
Schema.Struct({
a: Schema.Number,
b: Schema.Number,
}),
);
const messageInputSchema = Schema.standardSchemaV1(
Schema.Struct({
message: Schema.String,
}),
);
const tools = {
"math.add": {
description: "Add two numbers",
inputSchema: numberPairInputSchema,
execute: ({ a, b }: { a: number; b: number }) => ({ sum: a + b }),
},
"notifications.send": toExecutorTool({
tool: {
description: "Send a message",
inputSchema: messageInputSchema,
execute: ({ message }: { message: string }) => ({
delivered: true,
message,
}),
},
metadata: {
interaction: "required",
},
}),
};
describe("runtime-quickjs", () => {
it.effect("executes simple code and returns result", () =>
Effect.gen(function* () {
const executor = makeQuickJsExecutor();
const toolInvoker = makeToolInvokerFromTools({ tools });
const output = yield* executor.execute(
"return 1 + 2;",
toolInvoker,
);
expect(output.result).toBe(3);
expect(output.error).toBeUndefined();
}),
);
it.effect("executes code with tool calls", () =>
Effect.gen(function* () {
const executor = makeQuickJsExecutor();
const toolInvoker = makeToolInvokerFromTools({
tools,
onToolInteraction: allowAllToolInteractions,
});
const output = yield* executor.execute(
[
"const math = await tools.math.add({ a: 19, b: 23 });",
"await tools.notifications.send({ message: `sum is ${math.sum}` });",
"return math;",
].join("\n"),
toolInvoker,
);
expect(output.result).toEqual({ sum: 42 });
expect(output.error).toBeUndefined();
}),
);
it.effect("captures console output in logs", () =>
Effect.gen(function* () {
const executor = makeQuickJsExecutor();
const toolInvoker = makeToolInvokerFromTools({ tools });
const output = yield* executor.execute(
[
'console.log("hello from quickjs");',
'console.warn("a warning");',
'console.error("an error");',
"return 42;",
].join("\n"),
toolInvoker,
);
expect(output.result).toBe(42);
expect(output.logs).toContain("[log] hello from quickjs");
expect(output.logs).toContain("[warn] a warning");
expect(output.logs).toContain("[error] an error");
}),
);
it.effect("reports execution errors without crashing", () =>
Effect.gen(function* () {
const executor = makeQuickJsExecutor();
const toolInvoker = makeToolInvokerFromTools({ tools });
const output = yield* executor.execute(
'throw new Error("boom");',
toolInvoker,
);
expect(output.result).toBeNull();
expect(output.error).toContain("boom");
}),
);
it.effect("handles tool call errors gracefully", () =>
Effect.gen(function* () {
const failingTools = {
"broken.thing": {
description: "Always fails",
inputSchema: Schema.standardSchemaV1(Schema.Struct({})),
execute: () => {
throw new Error("tool is broken");
},
},
};
const executor = makeQuickJsExecutor();
const toolInvoker = makeToolInvokerFromTools({
tools: failingTools,
});
const output = yield* executor.execute(
"return await tools.broken.thing({});",
toolInvoker,
);
expect(output.result).toBeNull();
expect(output.error).toContain("tool is broken");
}),
);
it.effect("respects timeout", () =>
Effect.gen(function* () {
const executor = makeQuickJsExecutor({
timeoutMs: 250,
});
const toolInvoker = makeToolInvokerFromTools({ tools });
const output = yield* executor.execute(
"while (true) {}",
toolInvoker,
);
expect(output.result).toBeNull();
expect(output.error).toContain("timed out");
}),
);
it.effect("network access is denied by default", () =>
Effect.gen(function* () {
const executor = makeQuickJsExecutor();
const toolInvoker = makeToolInvokerFromTools({ tools });
const output = yield* executor.execute(
'await fetch("https://example.com"); return 1;',
toolInvoker,
);
expect(output.result).toBeNull();
expect(output.error).toContain("fetch is disabled in QuickJS executor");
}),
);
it.effect("multiple sequential tool calls work correctly", () =>
Effect.gen(function* () {
const executor = makeQuickJsExecutor();
const toolInvoker = makeToolInvokerFromTools({ tools });
const output = yield* executor.execute(
[
"const r1 = await tools.math.add({ a: 1, b: 2 });",
"const r2 = await tools.math.add({ a: r1.sum, b: 10 });",
"const r3 = await tools.math.add({ a: r2.sum, b: 100 });",
"return r3;",
].join("\n"),
toolInvoker,
);
expect(output.result).toEqual({ sum: 113 });
expect(output.error).toBeUndefined();
}),
);
});