import { describe, expect, it } from "vitest"; import { ASSISTANT_CONFIRMATION_PREFIX, buildApprovalSummary, canExecuteMutationTool, formatApprovalValue, isAffirmativeConfirmationReply, isCancellationReply, parseToolArguments, } from "../router/assistant-confirmation.js"; describe("assistant confirmation", () => { it("parses tool arguments defensively", () => { expect(parseToolArguments("{\"name\":\"Apollo\",\"status\":\"DRAFT\"}")).toEqual({ name: "Apollo", status: "DRAFT", }); expect(parseToolArguments("[]")).toEqual({}); expect(parseToolArguments("not-json")).toEqual({}); }); it("formats approval values compactly", () => { expect(formatApprovalValue("Apollo")).toBe("Apollo"); expect(formatApprovalValue("x".repeat(60))).toBe(`${"x".repeat(45)}...`); expect(formatApprovalValue([1, "two", true, "four"])).toBe("[1, two, true, ...]"); expect(formatApprovalValue({ nested: true })).toBe("{...}"); }); it("builds compact approval summaries from tool calls", () => { expect(buildApprovalSummary( "create_project", JSON.stringify({ name: "Apollo", status: "DRAFT", budgetCents: 1200000 }), )).toBe("create project (name=Apollo, status=DRAFT, budgetCents=1200000)"); expect(buildApprovalSummary("delete_project", "{}")).toBe("delete project"); }); it("recognizes affirmative and cancellation replies in multiple phrasings", () => { expect(isAffirmativeConfirmationReply("ja, bitte ausführen")).toBe(true); expect(isAffirmativeConfirmationReply("go ahead")).toBe(true); expect(isAffirmativeConfirmationReply("vielleicht")).toBe(false); expect(isCancellationReply("nein, abbrechen")).toBe(true); expect(isCancellationReply("stop")).toBe(true); expect(isCancellationReply("später")).toBe(false); }); it("blocks mutation tools until the user confirms a prior assistant summary", () => { expect(canExecuteMutationTool([ { role: "user", content: "Lege bitte ein Projekt an" }, ], "create_project")).toBe(false); expect(canExecuteMutationTool([ { role: "user", content: "Lege bitte ein Projekt an" }, { role: "assistant", content: "Ich werde jetzt das Projekt erstellen." }, { role: "user", content: "ja" }, ], "create_project")).toBe(false); expect(canExecuteMutationTool([ { role: "user", content: "Lege bitte ein Projekt an" }, { role: "assistant", content: `${ASSISTANT_CONFIRMATION_PREFIX} Ich werde das Projekt \"Apollo\" in DRAFT anlegen. Bitte bestätigen.` }, { role: "user", content: "ja, bitte ausführen" }, ], "create_project")).toBe(true); }); it("requires a matching server-side pending approval for mutation execution when provided", () => { const pendingApproval = { id: "approval-1", userId: "user-1", conversationId: "conversation-1", toolName: "create_project", toolArguments: JSON.stringify({ name: "Apollo", status: "DRAFT" }), summary: "create project (name=Apollo, status=DRAFT)", createdAt: Date.now(), expiresAt: Date.now() + 60_000, }; expect(canExecuteMutationTool([ { role: "assistant", content: `${ASSISTANT_CONFIRMATION_PREFIX} create project (name=Apollo). Bitte bestätigen.` }, { role: "user", content: "ja" }, ], "create_project", pendingApproval)).toBe(true); expect(canExecuteMutationTool([ { role: "assistant", content: `${ASSISTANT_CONFIRMATION_PREFIX} create project (name=Apollo). Bitte bestätigen.` }, { role: "user", content: "ja" }, ], "delete_project", pendingApproval)).toBe(false); }); it("does not require confirmation for read-only assistant tools", () => { expect(canExecuteMutationTool([ { role: "user", content: "Zeig mir meine Notifications" }, ], "list_notifications")).toBe(true); }); });