chat / src /lib /utils /metacognitiveLogic.spec.ts
Andrew
feat(tree): Add ELK port-based layout and persona-specific branching
cb5990d
import { describe, test, expect } from "vitest";
import type { Message } from "$lib/types/Message";
import type { MetacognitiveConfig } from "$lib/types/Metacognitive";
import { determineMetacognitivePrompt } from "./metacognitiveLogic";
const enabledConfig: MetacognitiveConfig = {
enabled: true,
frequencies: [2],
comprehensionPrompts: ["C1"],
perspectivePrompts: ["P1 {{personaName}}"],
};
function msg(partial: Partial<Message> & Pick<Message, "id" | "from" | "content">): Message {
// The caller type guarantees id/from/content are present.
return { ...partial } as Message;
}
describe("determineMetacognitivePrompt", () => {
test("returns null when config is disabled", () => {
const out = determineMetacognitivePrompt(
[msg({ id: "a1", from: "assistant", content: "x" })],
undefined,
{},
{}
);
expect(out).toBeNull();
});
test("respects dismissedForMessageId", () => {
const messages = [msg({ id: "a1", from: "assistant", content: "x" })];
const out = determineMetacognitivePrompt(
messages,
enabledConfig,
{ targetFrequency: 1, dismissedForMessageId: "a1" },
{}
);
expect(out).toBeNull();
});
test("gates by targetFrequency using assistant messages since last shown event", () => {
const messages = [
msg({
id: "a0",
from: "assistant",
content: "old",
metacognitiveEvents: [
{
type: "comprehension",
promptText: "C",
triggerFrequency: 1,
timestamp: new Date(),
accepted: false,
},
],
}),
msg({ id: "u1", from: "user", content: "q" }),
msg({ id: "a1", from: "assistant", content: "x" }),
];
expect(
determineMetacognitivePrompt(messages, enabledConfig, { targetFrequency: 2 }, {})
).toBeNull();
expect(
determineMetacognitivePrompt(messages, enabledConfig, { targetFrequency: 1 }, {})
).not.toBeNull();
});
test("prefers perspective when it can suggest a different active persona", () => {
const messages = [
msg({
id: "a1",
from: "assistant",
content: "x",
personaResponses: [{ personaId: "p1", personaName: "P1", content: "x" }],
}),
];
const out = determineMetacognitivePrompt(
messages,
enabledConfig,
{ targetFrequency: 1 },
{
activePersonas: ["p1", "p2"],
personas: [
{ id: "p1", name: "Alpha" },
{ id: "p2", name: "Beta" },
],
}
);
expect(out?.type).toBe("perspective");
expect(out?.suggestedPersonaId).toBe("p2");
expect(out?.promptText).toContain("Beta");
});
test("inserts personaName literally (no $ replacement expansions)", () => {
const config: MetacognitiveConfig = {
enabled: true,
frequencies: [1],
comprehensionPrompts: ["C1"],
perspectivePrompts: ["Hello {{personaName}}"],
};
const messages = [
msg({
id: "a1",
from: "assistant",
content: "x",
personaResponses: [{ personaId: "p1", personaName: "P1", content: "x" }],
}),
];
// `$&` would normally expand to the matched substring in String.replace replacement strings.
const out = determineMetacognitivePrompt(
messages,
config,
{ targetFrequency: 1 },
{
activePersonas: ["p1", "p2"],
personas: [
{ id: "p1", name: "Alpha" },
{ id: "p2", name: "$&" },
],
}
);
expect(out?.type).toBe("perspective");
expect(out?.suggestedPersonaName).toBe("$&");
expect(out?.promptText).toBe("Hello $&");
});
test("falls back to comprehension when there is no alternative persona", () => {
const messages = [
msg({
id: "a1",
from: "assistant",
content: "x",
personaResponses: [{ personaId: "p1", personaName: "P1", content: "x" }],
}),
];
const out = determineMetacognitivePrompt(
messages,
enabledConfig,
{ targetFrequency: 1 },
{ activePersonas: ["p1"], personas: [{ id: "p1", name: "Alpha" }] }
);
expect(out?.type).toBe("comprehension");
});
});