* 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>
169 lines
4.1 KiB
TypeScript
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");
|
|
});
|
|
});
|