evOo-local/apps/web/src/lib/bot-persona.test.ts
cynarAI dd177f7ef5
Name 1:1 senders, reach the computer from a group, translate stage lines (#187)
* Name 1:1 senders after their bot and reach the computer from a group

In a 1:1 chat bot messages fell back to the label "evOo" because only group
members were looked up; they now use the bot's name. The main conversation
and project chats show the computer button and open their lead bot's computer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

* Translate the stage's collaboration lines

Subagent, handoff, delegation and peer-reply lines on the stage were English
in every language. They now go through the catalog, with German filled in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-09 18:31:49 +02:00

169 lines
4.1 KiB
TypeScript

import { beforeAll, describe, expect, it } from "vitest";
import {
extractVoiceAwareness,
narrateAwarenessForPersona,
narrateGroupSpark,
personaTextPose,
resolveBotPersona,
resolvePersonaArchetype,
resolveTextStagePhase,
} from "./bot-persona";
import { i18n } from "./i18n";
beforeAll(() => {
i18n.load("en", {});
i18n.activate("en");
});
describe("resolvePersonaArchetype", () => {
it("detects Steve Jobs CTO template alignment", () => {
expect(
resolvePersonaArchetype({
name: "Steve Jobs - Autonomous CTO",
title: "Executive CTO",
description: "Full system access",
}),
).toBe("cto-executive");
});
it("detects fixer / chief operator", () => {
expect(
resolvePersonaArchetype({
name: "Fixer",
title: "Chief of Staff",
description: "",
}),
).toBe("chief-operator");
});
it("falls back to specialist when no archetype rule matches", () => {
expect(
resolvePersonaArchetype({
name: "Maya",
title: "Compliance Review",
description: "Focused specialist",
}),
).toBe("specialist");
});
});
describe("resolveBotPersona", () => {
it("uses title as role line when present", () => {
const profile = resolveBotPersona({
name: "Apex Quant",
title: "Portfolio Lead",
description: "alpha signals",
});
expect(profile.archetype).toBe("quant");
expect(profile.roleLine).toBe("Portfolio Lead");
expect(profile.cadence).toBe("urgent");
});
});
describe("extractVoiceAwareness", () => {
it("surfaces active subagent collaboration", () => {
const event = extractVoiceAwareness(
{
threadId: "t1",
botId: "b1",
messages: [
{
id: "m1",
role: "bot",
runId: "r1",
createdAt: "",
blocks: [
{
kind: "subagent",
agentId: "a1",
name: "Code Architect",
task: "Refactor auth module",
status: "running",
},
],
},
],
run: { id: "r1", status: "running" },
} as never,
"Steve Jobs",
);
expect(event?.kind).toBe("subagent");
expect(event?.targetName).toBe("Code Architect");
});
});
describe("narrateAwarenessForPersona", () => {
it("uses executive casting language for CTO archetype", () => {
const profile = resolveBotPersona({
name: "Steve Jobs - Autonomous CTO",
title: "CTO",
description: "",
});
const line = narrateAwarenessForPersona(profile, {
kind: "subagent",
actorName: profile.roleLine,
targetName: "Code Architect",
detail: "Harden login flow",
});
expect(line).toContain("Code Architect");
expect(line.toLowerCase()).toContain("casting");
});
});
describe("narrateGroupSpark", () => {
it("uses playful peer-reply language for builder archetype", () => {
const line = narrateGroupSpark(
{
kind: "peer-reply",
actorName: "Eggbot",
actorBotId: "b2",
detail: "That API shape is wrong.",
},
"builder",
);
expect(line.toLowerCase()).toContain("geeked");
expect(line).toContain("Eggbot");
});
});
describe("resolveTextStagePhase", () => {
it("maps running work to working phase", () => {
expect(
resolveTextStagePhase({
running: true,
hasMessages: true,
}),
).toBe("working");
});
it("listens while a run waits for the user to take over", () => {
expect(
resolveTextStagePhase({
running: false,
botStatus: "waiting_takeover",
hasMessages: true,
}),
).toBe("listening");
});
it("uses idle for empty threads", () => {
expect(
resolveTextStagePhase({
running: false,
hasMessages: false,
}),
).toBe("idle");
});
});
describe("personaTextPose", () => {
it("returns working pose while agent runs", () => {
const profile = resolveBotPersona({
name: "Apex Quant",
title: "Quant",
description: "",
});
expect(personaTextPose(profile, "working")).toBe("working");
});
});