Phase 1: add loadStateFromBranch — restore last state entry.

Walk branch backwards for skill-reinject:state custom entries per SPEC §6.3;
return structuredClone of valid v1 snapshot or null for full rescan.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-17 10:04:11 +07:00
parent db04a1dd01
commit edcf5352c4
+32 -1
View File
@@ -1,4 +1,4 @@
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import type { ExtensionAPI, SessionEntry } from "@earendil-works/pi-coding-agent";
/** How a skill was first observed in the session (SPEC §6.2). */
export type SkillSource = "slash" | "skill-block" | "read";
@@ -39,6 +39,37 @@ export function saveState(pi: ExtensionAPI, state: ExtensionState): void {
pi.appendEntry<ExtensionState>(STATE_ENTRY_TYPE, state);
}
function isExtensionState(data: unknown): data is ExtensionState {
if (!data || typeof data !== "object") {
return false;
}
const candidate = data as ExtensionState;
return (
candidate.version === 1 &&
(candidate.sessionOverride === null || typeof candidate.sessionOverride === "boolean") &&
Array.isArray(candidate.skills) &&
(candidate.lastCompactionSource === null ||
candidate.lastCompactionSource === "auto" ||
candidate.lastCompactionSource === "manual") &&
Array.isArray(candidate.pendingReinject)
);
}
/** Latest persisted state on the branch, or null if none (SPEC §6.3). */
export function loadStateFromBranch(branch: SessionEntry[]): ExtensionState | null {
for (let i = branch.length - 1; i >= 0; i--) {
const entry = branch[i];
if (entry.type !== "custom" || entry.customType !== STATE_ENTRY_TYPE || entry.data === undefined) {
continue;
}
if (!isExtensionState(entry.data)) {
continue;
}
return structuredClone(entry.data);
}
return null;
}
export function createInitialState(): ExtensionState {
return {
version: 1,