Files
oh-my-pi/packages/coding-agent/test/core/js-tool-bridge.test.ts
T
can1357 cf60e6df51 feat(coding-agent): implemented eval framework and replaced python tool
- Added a unified eval framework with parser grammar, backend interfaces, and JS/Python execution result types.
- Added eval tool docs and updated prompts for fenced cells, `eval.py`/`eval.js`, and fallback behavior.
- Replaced the built-in `python` tool with `eval` across registry, rendering, interactive modes, and tool settings.
- Migrated Python execution runtime from `src/ipy` to `src/eval/py`, renamed state fields, and removed legacy introspection.
- Refactored browser tooling from in-process VM helpers to worker-managed tab supervisors and protocol transport.
- Added eval parser fallback and JS tool-bridge tests, updated imports, and removed obsolete python-mode suites.
2026-04-30 18:08:37 +02:00

88 lines
2.5 KiB
TypeScript

import { describe, expect, it, vi } from "bun:test";
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
import { Type } from "@sinclair/typebox";
import { callSessionTool } from "../../src/eval/js/tool-bridge";
function createTool(
name: string,
execute: (toolCallId: string, args: unknown, signal?: AbortSignal) => Promise<AgentToolResult>,
): AgentTool {
return {
name,
label: name,
description: `${name} tool`,
parameters: Type.Object({}),
concurrency: "parallel",
execute,
} as unknown as AgentTool;
}
function createSession(tools: AgentTool[]): ToolSession {
const registry = new Map(tools.map(tool => [tool.name, tool]));
return {
cwd: "/tmp/test",
hasUI: false,
getSessionFile: () => null,
getSessionSpawns: () => null,
settings: Settings.isolated(),
getToolByName: name => registry.get(name),
};
}
describe("callSessionTool", () => {
it("injects js intent and summarizes text results", async () => {
const execute = vi.fn().mockResolvedValue({
content: [{ type: "text", text: "hello" }],
});
const session = createSession([createTool("read", execute)]);
const statuses: Array<Record<string, unknown>> = [];
const result = await callSessionTool(
"read",
{ path: "/tmp/demo.txt" },
{
session,
emitStatus: event => {
statuses.push(event);
},
},
);
expect(result).toBe("hello");
expect(execute).toHaveBeenCalledWith(
expect.stringMatching(/^js-read-/),
{ path: "/tmp/demo.txt", _i: "js prelude" },
undefined,
);
expect(statuses).toEqual([expect.objectContaining({ op: "read", path: "/tmp/demo.txt", chars: 5 })]);
});
it("returns structured tool results when details or images are present", async () => {
const session = createSession([
createTool("custom", async () => ({
content: [
{ type: "text", text: "done" },
{ type: "image", mimeType: "image/png", data: "abc123" },
],
details: { ok: true },
})),
]);
const result = await callSessionTool("custom", {}, { session });
expect(result).toEqual({
text: "done",
details: { ok: true },
images: [{ mimeType: "image/png", data: "abc123" }],
});
});
it("throws when the requested tool is not available in the session registry", async () => {
const session = createSession([]);
await expect(callSessionTool("missing", {}, { session })).rejects.toThrow("Unknown tool from js runtime");
});
});