continue-after-compaction.ts
2630 bytes
1import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
3const buildContinuationPrompt = (sessionFile: string | undefined, compactionEntryId: string): string => {
4 const sessionSource =
5 sessionFile === undefined
6 ? "This session is ephemeral, so no persisted session file is available."
7 : [
8 `The persisted session JSONL is ${JSON.stringify(sessionFile)}.`,
9 "Inspect it directly with the read and bash tools.",
10 "Do not launch a nested Pi process or open the session with `pi --session`.",
11 ].join(" ");
12
13 return `Compaction has just completed. Resume the existing task rather than waiting for another user prompt.
14
15${sessionSource}
16The new compaction entry ID is ${JSON.stringify(compactionEntryId)}.
17
18Before continuing:
19
201. Review the active session branch leading to the compaction entry. Focus first on messages and tool calls immediately before compaction, searching earlier history only as needed. Remember that JSONL append order can include abandoned branches, so follow parentId links rather than blindly treating every entry as active.
212. Reconstruct the original goal, user constraints, decisions made, files changed, commands and tests run, unresolved issues, and intended next action.
223. Reconcile the recovered history with the compaction summary and current repository state. Treat the current worktree as authoritative for file state and the original session history as authoritative for user intent.
234. Briefly state the context you recovered.
245. Immediately perform the next unfinished step. Do not stop after the recap and do not ask the user to repeat prior context unless the session data is genuinely unavailable or ambiguous.`;
25};
26
27/**
28 * Automatically resumes work after every successful Pi compaction.
29 *
30 * The continuation is deferred by one event-loop turn so manual compaction can
31 * finish reconnecting the agent runtime before a new prompt begins. During an
32 * active automatic-compaction recovery, it is delivered as a follow-up.
33 */
34export default function continueAfterCompaction(pi: ExtensionAPI): void {
35 const pendingTimers = new Set<ReturnType<typeof setTimeout>>();
36
37 pi.on("session_compact", (event, ctx) => {
38 const sessionFile = ctx.sessionManager.getSessionFile();
39 const prompt = buildContinuationPrompt(sessionFile, event.compactionEntry.id);
40
41 const timer = setTimeout(() => {
42 pendingTimers.delete(timer);
43 pi.sendUserMessage(prompt, { deliverAs: "followUp" });
44 }, 0);
45
46 pendingTimers.add(timer);
47 });
48
49 pi.on("session_shutdown", () => {
50 for (const timer of pendingTimers) {
51 clearTimeout(timer);
52 }
53 pendingTimers.clear();
54 });
55}