import { describe, expect, it } from "vitest"; import { findProseToolCalls, isOnlyUnspeakableToolProse, sanitizeBotReplyText, stripAssistantRolePrefix, stripProseToolCalls, } from "./bot-reply-sanitize.js"; describe("stripAssistantRolePrefix", () => { it("removes a leaked assistant role prefix", () => { expect(stripAssistantRolePrefix("assistant: Here is the plan.")).toBe("Here is the plan."); expect(stripAssistantRolePrefix("Assistant: Hi")).toBe("Hi"); }); it("leaves normal text alone", () => { expect(stripAssistantRolePrefix("The assistant noted it.")).toBe("The assistant noted it."); }); }); describe("stripProseToolCalls", () => { it("removes a pasted handoff_to_bot JSON blob from executive-board transcript shape", () => { const raw = `Since the handoff is given, I'll provide the JSON response for the handoff: {"name": "handoff_to_bot", "parameters": {"message": "Erstelle ein Konzept für mobile Voice-First UI", "bot_id": "boss-1", "confirm_name": "Boss"}}`; const { text, proseToolCalls } = stripProseToolCalls(raw); expect(proseToolCalls).toEqual([ { name: "handoff_to_bot", parameters: { message: "Erstelle ein Konzept für mobile Voice-First UI", bot_id: "boss-1", confirm_name: "Boss", }, }, ]); expect(text).not.toContain("handoff_to_bot"); expect(text).not.toContain("confirm_name"); expect(text).not.toContain("JSON response"); }); it("keeps legitimate prose around a removed blob", () => { const raw = 'Hier sind drei Richtungen.\n{"name": "handoff_to_bot", "parameters": {"message": "do it", "bot_id": "x"}}'; const { text, proseToolCalls } = stripProseToolCalls(raw); expect(proseToolCalls).toHaveLength(1); expect(text).toBe("Hier sind drei Richtungen."); }); it("accepts arguments instead of parameters", () => { const raw = '{"name": "message_bot", "arguments": {"message": "ping", "bot_id": "a"}}'; const { proseToolCalls } = stripProseToolCalls(raw); expect(proseToolCalls[0]?.name).toBe("message_bot"); expect(proseToolCalls[0]?.parameters).toEqual({ message: "ping", bot_id: "a" }); }); }); describe("sanitizeBotReplyText", () => { it("combines role-prefix and prose-tool stripping", () => { const raw = 'assistant: Since the handoff is given, I\'ll provide the JSON response for the handoff:\n{"name": "handoff_to_bot", "parameters": {"message": "same task", "confirm_name": "Boss"}}'; const result = sanitizeBotReplyText(raw); expect(result.strippedRolePrefix).toBe(true); expect(result.proseToolCalls[0]?.name).toBe("handoff_to_bot"); expect(result.text).toBe(""); }); }); describe("findProseToolCalls", () => { it("finds nested parameter objects", () => { const matches = findProseToolCalls( '{"name":"handoff_to_bot","parameters":{"message":"Build {mobile} UI","bot_id":"ux"}}', ); expect(matches).toHaveLength(1); expect(matches[0]?.call.parameters.message).toBe("Build {mobile} UI"); }); }); describe("isOnlyUnspeakableToolProse", () => { it("is true for tool-json-only replies", () => { expect( isOnlyUnspeakableToolProse( '{"name": "handoff_to_bot", "parameters": {"message": "x", "bot_id": "y"}}', ), ).toBe(true); }); it("is false when real user-facing prose remains", () => { expect(isOnlyUnspeakableToolProse("Three concept directions: calm, bold, playful.")).toBe( false, ); }); });