cf60e6df51
- 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.
88 lines
2.5 KiB
TypeScript
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");
|
|
});
|
|
});
|