mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
Adds a built-in, opt-in codemode tool that runs model-written JavaScript in the codemode sandbox. Scripts call the other active tools as tools.<name>(args), including concurrently with Promise.all; only the return value, console output, and images attached with image() reach the model. Nested calls run through the agent loop via a new AgentToolContext (5th execute argument, ctx.executeTool for extension tools), so argument validation and the beforeToolCall/afterToolCall hooks apply. Hooks and tool_call/tool_result events carry the parent call id. Tools can declare outputSchema and return structuredContent; codemode passes that to scripts and falls back to text content otherwise. The codemode package gains globals, declaration rendering from JSON Schema, and optional deadlines.
89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { renderDeclarations, schemaToType } from "../src/index.ts";
|
|
|
|
const execute = () => undefined;
|
|
|
|
describe("schemaToType", () => {
|
|
it("renders primitives, literals, and unions", () => {
|
|
expect(schemaToType({ type: "string" })).toBe("string");
|
|
expect(schemaToType({ type: "integer" })).toBe("number");
|
|
expect(schemaToType({ type: ["string", "null"] })).toBe("string | null");
|
|
expect(schemaToType({ const: "a" })).toBe('"a"');
|
|
expect(schemaToType({ enum: ["a", 1, null] })).toBe('"a" | 1 | null');
|
|
expect(schemaToType({ anyOf: [{ type: "string" }, { type: "number" }] })).toBe("string | number");
|
|
expect(schemaToType({ anyOf: [{ type: "string" }, {}] })).toBe("unknown");
|
|
expect(schemaToType({ $ref: "#/defs/x" })).toBe("unknown");
|
|
expect(schemaToType(true)).toBe("unknown");
|
|
expect(schemaToType(false)).toBe("never");
|
|
});
|
|
|
|
it("renders arrays and tuples", () => {
|
|
expect(schemaToType({ type: "array", items: { type: "string" } })).toBe("string[]");
|
|
expect(schemaToType({ type: "array", items: { anyOf: [{ type: "string" }, { type: "number" }] } })).toBe(
|
|
"(string | number)[]",
|
|
);
|
|
expect(schemaToType({ type: "array", prefixItems: [{ type: "string" }, { type: "number" }] })).toBe(
|
|
"[string, number]",
|
|
);
|
|
expect(schemaToType({ type: "array" })).toBe("unknown[]");
|
|
});
|
|
|
|
it("renders objects with optional members, doc comments, and index signatures", () => {
|
|
const type = schemaToType({
|
|
type: "object",
|
|
properties: {
|
|
path: { type: "string", description: "Path to read" },
|
|
"max-lines": { type: "number" },
|
|
},
|
|
required: ["path"],
|
|
});
|
|
expect(type).toBe('{\n /** Path to read */\n path: string;\n "max-lines"?: number;\n}');
|
|
expect(schemaToType({ type: "object", additionalProperties: { type: "number" } })).toBe(
|
|
"{\n [key: string]: number;\n}",
|
|
);
|
|
expect(schemaToType({ type: "object" })).toBe("Record<string, unknown>");
|
|
expect(schemaToType({ type: "object", properties: {}, additionalProperties: false })).toBe("{}");
|
|
});
|
|
});
|
|
|
|
describe("renderDeclarations", () => {
|
|
it("renders tools and globals", () => {
|
|
const text = renderDeclarations({
|
|
tools: [
|
|
{
|
|
name: "read",
|
|
description: "Read a file.\nSecond line.",
|
|
inputSchema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] },
|
|
outputSchema: { type: "string" },
|
|
execute,
|
|
},
|
|
{ name: "remote-api", execute },
|
|
{ name: "noargs", inputSchema: { type: "object", properties: {} }, execute },
|
|
],
|
|
globals: [{ name: "image", description: "Attach an image.", inputSchema: { type: "string" }, execute }],
|
|
});
|
|
expect(text).toBe(
|
|
[
|
|
"declare const tools: {",
|
|
" /**",
|
|
" * Read a file.",
|
|
" * Second line.",
|
|
" */",
|
|
" read(args: {",
|
|
" path: string;",
|
|
" }): Promise<string>;",
|
|
' "remote-api"(args?: unknown): Promise<unknown>;',
|
|
" noargs(args?: Record<string, unknown>): Promise<unknown>;",
|
|
"};",
|
|
"",
|
|
"/** Attach an image. */",
|
|
"declare function image(args: string): Promise<unknown>;",
|
|
].join("\n"),
|
|
);
|
|
});
|
|
|
|
it("escapes comment terminators in descriptions", () => {
|
|
const text = renderDeclarations({ tools: [{ name: "x", description: "a */ b", execute }] });
|
|
expect(text).toContain("/** a *\\/ b */");
|
|
});
|
|
});
|