import type { SessionEntry } from "@earendil-works/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { filterSkillsNeedingReinject, filterSkillsNeedingReinjectByKept, getKeptEntries, skillsPresentInKeptWindow, } from "../src/kept"; import type { TrackedSkill } from "../src/state"; const ts = "2024-12-03T14:00:00.000Z"; function userMessage(id: string, content: string): SessionEntry { return { type: "message", id, parentId: null, timestamp: ts, message: { role: "user", content }, } as SessionEntry; } function assistantMessage(id: string, content: string): SessionEntry { return { type: "message", id, parentId: null, timestamp: ts, message: { role: "assistant", content: [{ type: "text", text: content }] }, } as SessionEntry; } function compactionEntry(id: string, firstKeptEntryId: string): SessionEntry { return { type: "compaction", id, parentId: null, timestamp: ts, summary: "summary", firstKeptEntryId, tokensBefore: 1000, } as SessionEntry; } function reinjectCustomMessage(id: string, content: string): SessionEntry { return { type: "custom_message", id, parentId: null, timestamp: ts, customType: "skill-reinject:inject", content, display: true, } as SessionEntry; } const skillBlock = (name: string) => `\nbody\n`; const trackedSkills: TrackedSkill[] = [ { name: "alpha", filePath: "/skills/alpha/SKILL.md", baseDir: "/skills/alpha", firstSeenAt: 1, lastSeenAt: 1, sources: ["slash"], }, { name: "beta", filePath: "/skills/beta/SKILL.md", baseDir: "/skills/beta", firstSeenAt: 2, lastSeenAt: 2, sources: ["skill-block"], }, ]; describe("getKeptEntries", () => { const branch: SessionEntry[] = [ userMessage("e1", "old"), userMessage("e2", "kept start"), assistantMessage("e3", "reply"), compactionEntry("e4", "e2"), userMessage("e5", "after compact"), ]; it("slices from firstKeptEntryId through tail", () => { expect(getKeptEntries(branch, "e2").map((entry) => entry.id)).toEqual(["e2", "e3", "e4", "e5"]); }); it("returns empty when firstKeptEntryId is missing", () => { expect(getKeptEntries(branch, "missing")).toEqual([]); expect(getKeptEntries([], "e1")).toEqual([]); }); }); describe("skillsPresentInKeptWindow", () => { it("detects skill blocks in kept user messages", () => { const kept = [userMessage("k1", skillBlock("alpha")), assistantMessage("k2", "ignored")]; expect(skillsPresentInKeptWindow(kept, ["alpha", "beta"])).toEqual(new Set(["alpha"])); }); it("returns empty when no tracked skills or no blocks", () => { expect(skillsPresentInKeptWindow([userMessage("k1", "plain")], ["alpha"])).toEqual(new Set()); expect(skillsPresentInKeptWindow([userMessage("k1", skillBlock("alpha"))], [])).toEqual(new Set()); expect(skillsPresentInKeptWindow([], ["alpha", "beta"])).toEqual(new Set()); }); it("detects skill blocks in skill-reinject:inject custom messages", () => { const kept = [reinjectCustomMessage("inj1", skillBlock("beta"))]; expect(skillsPresentInKeptWindow(kept, ["alpha", "beta"])).toEqual(new Set(["beta"])); }); it("ignores reinject custom outside kept slice when slicing branch", () => { const branch: SessionEntry[] = [ reinjectCustomMessage("inj0", skillBlock("alpha")), userMessage("e2", "kept start"), compactionEntry("e4", "e2"), ]; const kept = getKeptEntries(branch, "e2"); expect(skillsPresentInKeptWindow(kept, ["alpha", "beta"])).toEqual(new Set()); expect(skillsPresentInKeptWindow(kept, ["alpha"])).toEqual(new Set()); }); it("counts reinject custom inside kept slice", () => { const branch: SessionEntry[] = [ userMessage("e2", "kept start"), reinjectCustomMessage("inj1", skillBlock("alpha")), compactionEntry("e4", "e2"), ]; const kept = getKeptEntries(branch, "e2"); expect(skillsPresentInKeptWindow(kept, ["alpha", "beta"])).toEqual(new Set(["alpha"])); }); }); describe("filterSkillsNeedingReinjectByKept", () => { it("returns tracked skills absent from kept window regardless of registration", () => { const keptPresent = new Set(["alpha"]); expect(filterSkillsNeedingReinjectByKept(trackedSkills, keptPresent)).toEqual(["beta"]); }); it("preserves tracked order including unregistered names", () => { const keptPresent = new Set(); expect(filterSkillsNeedingReinjectByKept(trackedSkills, keptPresent)).toEqual(["alpha", "beta"]); }); it("returns empty when all tracked skills are in kept window", () => { const keptPresent = new Set(["alpha", "beta"]); expect(filterSkillsNeedingReinjectByKept(trackedSkills, keptPresent)).toEqual([]); }); }); describe("filterSkillsNeedingReinject", () => { it("returns registered tracked skills absent from kept window", () => { const keptPresent = new Set(["alpha"]); const registered = new Set(["alpha", "beta"]); expect(filterSkillsNeedingReinject(trackedSkills, keptPresent, registered)).toEqual(["beta"]); }); it("excludes unregistered skills and preserves tracked order", () => { const keptPresent = new Set(); const registered = new Set(["alpha", "beta"]); expect(filterSkillsNeedingReinject(trackedSkills, keptPresent, registered)).toEqual(["alpha", "beta"]); expect(filterSkillsNeedingReinject(trackedSkills, keptPresent, new Set(["beta"]))).toEqual(["beta"]); }); it("returns empty when all tracked skills are in kept window", () => { const keptPresent = new Set(["alpha", "beta"]); const registered = new Set(["alpha", "beta"]); expect(filterSkillsNeedingReinject(trackedSkills, keptPresent, registered)).toEqual([]); }); });