evOo-local/packages/adapters/src/prose-handoff-recovery.test.ts
2026-09-22 06:57:55 +00:00

111 lines
3.1 KiB
TypeScript

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