import { describe, expect, it } from "vitest"; import { createConfirmTools } from "../src/agent/confirm_tools.js"; import { ToolConfirmationManager } from "../src/agent/confirmations.js"; import type { AgentStreamEvent } from "../src/agent/service.js"; import { makeCatalog, silentLogger } from "./helpers.js"; interface TestTool { name: string; execute: ( toolCallId: string, params: Record, signal?: AbortSignal, ) => Promise<{ content: Array<{ type: string; text: string }>; details: Record; isError?: boolean; }>; } async function setup() { const catalog = await makeCatalog([]); const manager = new ToolConfirmationManager(1_000); const events: AgentStreamEvent[] = []; const tools = createConfirmTools({ catalog, confirmations: manager, sessionId: "s1", logger: silentLogger, emit: (event) => events.push(event), hasStream: true, }) as unknown as TestTool[]; return { manager, events, tools }; } function resultText(result: { content: Array<{ type: string; text: string }> }): unknown { return JSON.parse(result.content[0]?.text ?? "{}"); } describe("confirm tools", () => { it("select mode emits options and returns the selected id", async () => { const { manager, events, tools } = await setup(); const tool = tools.find((item) => item.name === "confirm_user"); expect(tool).toBeDefined(); const promise = tool!.execute("call-1", { message: "请选择风格", mode: "select", options: [ { id: "a", label: "A" }, { id: "b", label: "B" }, ], }); expect(events[0]).toMatchObject({ type: "content_confirmation_required", confirm_type: "user_confirm", payload: { message: "请选择风格", mode: "select" }, }); expect(manager.resolve("s1", "call-1", { approved: true, content: "b" })).toBe(true); expect(resultText(await promise)).toEqual({ approved: true, content: "b" }); expect(events[1]).toMatchObject({ type: "content_confirmation_resolved", confirm_type: "user_confirm", approved: true, content: "b", }); }); it("treats provided options as select mode", async () => { const { manager, events, tools } = await setup(); const tool = tools.find((item) => item.name === "confirm_user")!; const promise = tool.execute("call-options", { message: "请选择风格", mode: "confirm", options: ["a", "b"], }); expect(events[0]).toMatchObject({ type: "content_confirmation_required", confirm_type: "user_confirm", payload: { mode: "select" }, }); manager.resolve("s1", "call-options", { approved: true, content: "a" }); expect(resultText(await promise)).toEqual({ approved: true, content: "a" }); }); it("edit mode returns edited content", async () => { const { manager, tools } = await setup(); const tool = tools.find((item) => item.name === "confirm_user")!; const promise = tool.execute("call-edit", { message: "请补充信息", mode: "edit", default_value: "默认内容", }); manager.resolve("s1", "call-edit", { approved: true, content: "用户修改后的内容" }); expect(resultText(await promise)).toEqual({ approved: true, content: "用户修改后的内容" }); }); it("confirm mode clears content", async () => { const { manager, tools } = await setup(); const tool = tools.find((item) => item.name === "confirm_user")!; const promise = tool.execute("call-confirm", { message: "确认继续?", mode: "confirm" }); manager.resolve("s1", "call-confirm", { approved: true, content: "忽略内容" }); expect(resultText(await promise)).toEqual({ approved: true, content: "" }); }); it("rejects an option that was not offered", async () => { const { manager, tools } = await setup(); const tool = tools.find((item) => item.name === "confirm_user")!; const promise = tool.execute("call-invalid", { message: "请选择", mode: "select", options: ["a", "b"], }); manager.resolve("s1", "call-invalid", { approved: true, content: "c" }); expect(resultText(await promise)).toEqual({ approved: false, content: "" }); }); });