import { describe, expect, it, vi } from "vitest"; vi.mock("./group-handoff.js", () => ({ handoffToGroupBot: vi.fn(), })); import { handoffToGroupBot } from "./group-handoff.js"; import { INBOUND_HANDOFF_WORKSHOP_FALLBACK, proseHandoffArgs, sanitizeTextBlocks, tryRecoverProseHandoff, workshopFallbackAfterBlockedHandoff, } from "./prose-handoff-recovery.js"; const handoffMock = vi.mocked(handoffToGroupBot); const run = { id: "run-ux", spaceId: "space-1", threadId: "thread-1", botId: "bot-ux", userId: "user-1", }; const deps = { prisma: {}, events: { notify: vi.fn(async () => undefined) }, jobs: { enqueue: vi.fn(async () => undefined) }, }; describe("sanitizeTextBlocks", () => { it("strips pasted handoff JSON from text blocks", () => { const blocks = sanitizeTextBlocks([ { kind: "text", text: 'Since the handoff is given:\n{"name": "handoff_to_bot", "parameters": {"message": "same", "bot_id": "boss"}}', }, ]); expect(blocks).toEqual([]); }); }); describe("proseHandoffArgs", () => { it("maps parameters to handoff input", () => { expect( proseHandoffArgs({ name: "handoff_to_bot", parameters: { message: "Erstelle ein Konzept", bot_id: "boss-1", confirm_name: "Boss", }, }), ).toEqual({ message: "Erstelle ein Konzept", bot_id: "boss-1", confirm_name: "Boss", }); }); }); describe("tryRecoverProseHandoff", () => { it("executes a pasted downward handoff instead of showing JSON", async () => { handoffMock.mockResolvedValueOnce({ ok: true, botId: "bot-dev", runId: "run-dev", note: "ok", }); const raw = '{"name": "handoff_to_bot", "parameters": {"message": "Build API", "bot_id": "bot-dev"}}'; const result = await tryRecoverProseHandoff(deps as never, run, "group-1", raw, false); expect(result.handedOff).toBe(true); expect(result.sanitizedText).toBe(""); expect(handoffMock).toHaveBeenCalledWith(deps, run, "group-1", { bot_id: "bot-dev", confirm_name: undefined, message: "Build API", }); }); it("flags an upward bounce without handing off", async () => { handoffMock.mockResolvedValueOnce({ error: "do not hand this stage back to its sender; post the result in the shared thread instead", }); const raw = '{"name": "handoff_to_bot", "parameters": {"message": "Erstelle ein Konzept", "confirm_name": "Boss"}}'; const result = await tryRecoverProseHandoff(deps as never, run, "group-1", raw, false); expect(result.handedOff).toBe(false); expect(result.blockedUpward).toBe(true); }); }); describe("workshopFallbackAfterBlockedHandoff", () => { it("returns a workshop line when only blocked handoff prose remains", () => { expect( workshopFallbackAfterBlockedHandoff("", true, false), ).toBe(INBOUND_HANDOFF_WORKSHOP_FALLBACK); }); it("does not override real specialist prose", () => { expect( workshopFallbackAfterBlockedHandoff("Drei Richtungen: ruhig, mutig, verspielt.", true, false), ).toBeUndefined(); }); });