Files
CapaKraken/packages/api/src/__tests__/assistant-confirmation.test.ts
T

94 lines
3.8 KiB
TypeScript

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);
});
});