Phase 5: add kept-window tests — slice, presence, filter, empty kept.
Covers kept-window dedup helpers per SPEC §6.4 and §12.1. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
|||||||
|
import type { SessionEntry } from "@earendil-works/pi-coding-agent";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
filterSkillsNeedingReinject,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const skillBlock = (name: string) =>
|
||||||
|
`<skill name="${name}" location="/skills/${name}/SKILL.md">\nbody\n</skill>`;
|
||||||
|
|
||||||
|
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());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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<string>();
|
||||||
|
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([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user