Files
pi-auto-reinject/src/kept.ts
T
grayhook a0e6d204a6 Phase 5: add filterSkillsNeedingReinject — kept-window dedup input for pendingReinject.
Returns tracked registered skills absent from kept user messages per SPEC §5.2 and §6.4.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 11:20:11 +07:00

73 lines
2.0 KiB
TypeScript

import type { SessionEntry } from "@earendil-works/pi-coding-agent";
import { parseSkillBlocksFromText } from "./detect.js";
import type { TrackedSkill } from "./state.js";
/** Branch slice from firstKeptEntryId through tail (SPEC §6.4). */
export function getKeptEntries(branch: SessionEntry[], firstKeptEntryId: string): SessionEntry[] {
const startIndex = branch.findIndex((entry) => entry.id === firstKeptEntryId);
if (startIndex < 0) {
return [];
}
return branch.slice(startIndex);
}
function extractUserMessageText(content: unknown): string {
if (typeof content === "string") {
return content;
}
if (!Array.isArray(content)) {
return "";
}
const parts: string[] = [];
for (const part of content) {
if (!part || typeof part !== "object") {
continue;
}
const block = part as { type?: string; text?: string };
if (block.type === "text" && typeof block.text === "string") {
parts.push(block.text);
}
}
return parts.join("\n");
}
/** Skill names already present as blocks in kept user messages (SPEC §6.4). */
export function skillsPresentInKeptWindow(
keptEntries: SessionEntry[],
skillNames: readonly string[],
): Set<string> {
const present = new Set<string>();
const namesToCheck = new Set(skillNames);
if (namesToCheck.size === 0) {
return present;
}
for (const entry of keptEntries) {
if (entry.type !== "message" || entry.message.role !== "user") {
continue;
}
const text = extractUserMessageText(entry.message.content);
for (const block of parseSkillBlocksFromText(text)) {
if (namesToCheck.has(block.name)) {
present.add(block.name);
}
}
}
return present;
}
/** Tracked skills missing from kept window but still registered (SPEC §5.2, §6.4). */
export function filterSkillsNeedingReinject(
tracked: readonly TrackedSkill[],
keptPresent: ReadonlySet<string>,
registeredNames: ReadonlySet<string>,
): string[] {
const needing: string[] = [];
for (const skill of tracked) {
if (registeredNames.has(skill.name) && !keptPresent.has(skill.name)) {
needing.push(skill.name);
}
}
return needing;
}