Skill Tracer
Skills › Scientific Agent Skills › pi-agent › compaction.md

compaction.md

78 lines · skills/pi-agent/references/compaction.md at 5f2bc03

Open on GitHub
1# Compaction and Branch Summarization
2
3Source: https://pi.dev/docs/latest/compaction
4
5Reviewed against Pi 0.99.2 and the package versions listed in `../SKILL.md` on 2026-09-30.
6
7Pi has two summarization mechanisms that share the same structured summary format and track file operations cumulatively.
8
9| Mechanism | Trigger | Purpose |
10|---|---|---|
11| Compaction | context exceeds threshold, or `/compact` | Summarize old messages to free context |
12| Branch summarization | `/tree` navigation | Preserve context when switching branches |
14Both disable prompt-cache writes where supported because these one-off prompts are unlikely to be reused.

Compaction and Branch Summarization

Source: https://pi.dev/docs/latest/compaction

Reviewed against Pi 0.99.2 and the package versions listed in ../SKILL.md on 2026-09-30.

Pi has two summarization mechanisms that share the same structured summary format and track file operations cumulatively.

MechanismTriggerPurpose
Compactioncontext exceeds threshold, or /compactSummarize old messages to free context
Branch summarization/tree navigationPreserve context when switching branches

Both disable prompt-cache writes where supported because these one-off prompts are unlikely to be reused.

Raw lines 1 to 15
16## Auto-Compaction
18Triggers when `contextTokens > contextWindow - reserveTokens`. Defaults: `reserveTokens` 16384, `keepRecentTokens` 20000, configured under `compaction` in global or project settings. Exact-model `compaction.modelOverrides` can override either budget independently. `/compact [instructions]` works even with auto-compaction disabled.
20Steps: walk backwards from the newest message accumulating token estimates until `keepRecentTokens` is reached (the cut point) → collect messages from the previous kept boundary (or session start) to the cut point → summarize with the structured format, passing any previous summary as iterative context → append a `CompactionEntry` → rebuild the context for the next request as summary plus messages from `firstKeptEntryId`.
22On repeated compactions the summarized span starts at the previous compaction's kept boundary (`firstKeptEntryId`), not at the compaction entry, falling back to the entry after the previous compaction when that kept entry is not on the path. This re-includes messages that survived the earlier pass. `tokensBefore` is recalculated from the context-edited projection before writing the new entry.
24Valid cut points: user messages, assistant messages, bash execution messages, and custom messages (`custom_message`, `branch_summary`). Pi never cuts at tool results.

Auto-Compaction

Triggers when contextTokens > contextWindow - reserveTokens. Defaults: reserveTokens 16384, keepRecentTokens 20000, configured under compaction in global or project settings. Exact-model compaction.modelOverrides can override either budget independently. /compact [instructions] works even with auto-compaction disabled.

Steps: walk backwards from the newest message accumulating token estimates until keepRecentTokens is reached (the cut point) → collect messages from the previous kept boundary (or session start) to the cut point → summarize with the structured format, passing any previous summary as iterative context → append a CompactionEntry → rebuild the context for the next request as summary plus messages from firstKeptEntryId.

On repeated compactions the summarized span starts at the previous compaction's kept boundary (firstKeptEntryId), not at the compaction entry, falling back to the entry after the previous compaction when that kept entry is not on the path. This re-includes messages that survived the earlier pass. tokensBefore is recalculated from the context-edited projection before writing the new entry.

Valid cut points: user messages, assistant messages, bash execution messages, and custom messages (custom_message, branch_summary). Pi never cuts at tool results.

Raw lines 16 to 25
26## Split Turns
28A user-message span starts with a user message and includes assistant turns and tool calls until the next user message. A runtime `turn_start`/`turn_end` instead encloses one assistant response and its tools. When a single turn exceeds `keepRecentTokens`, the cut lands mid-turn at an assistant message (`isSplitTurn`). Pi then generates two summaries — a history summary for previous context and a turn-prefix summary for the early part of the split turn — and merges them.

Split Turns

A user-message span starts with a user message and includes assistant turns and tool calls until the next user message. A runtime turn_start/turn_end instead encloses one assistant response and its tools. When a single turn exceeds keepRecentTokens, the cut lands mid-turn at an assistant message (isSplitTurn). Pi then generates two summaries — a history summary for previous context and a turn-prefix summary for the early part of the split turn — and merges them.

Raw lines 26 to 29
30## Branch Summarization
32On `/tree` navigation to a different branch: find the deepest common ancestor, walk from the old leaf back to it, include messages up to the token budget newest-first, summarize, and append a `BranchSummaryEntry` at the navigation point — the summary lands on the destination branch's new leaf, not on the branch being left.
34Both mechanisms extract file operations from the tool calls being summarized **and** from previous compaction/branch-summary `details`, so read/modified file tracking accumulates across passes.

Branch Summarization

On /tree navigation to a different branch: find the deepest common ancestor, walk from the old leaf back to it, include messages up to the token budget newest-first, summarize, and append a BranchSummaryEntry at the navigation point — the summary lands on the destination branch's new leaf, not on the branch being left.

Both mechanisms extract file operations from the tool calls being summarized and from previous compaction/branch-summary details, so read/modified file tracking accumulates across passes.

Raw lines 30 to 35
36## Entry Shapes
38`CompactionEntry`: `type`, `id`, `parentId`, `timestamp`, `summary`, `firstKeptEntryId`, `tokensBefore`, optional `usage` (LLM usage that generated the summary; counted in session totals), optional `fromHook` (legacy name for "provided by extension"), optional `details`.
40`BranchSummaryEntry`: same plus `fromId` instead of `firstKeptEntryId`.
42Default `details` is `{ readFiles: string[], modifiedFiles: string[] }`; extensions may store any JSON-serializable structure. Current entries use `firstKeptEntryId` plus an optional complete `systemMessage` checkpoint; `retainedTail` is not the Pi 0.99.2 contract. Context edits affect the projected input, while raw history and costs remain unchanged. See `references/session-format.md`.

Entry Shapes

CompactionEntry: type, id, parentId, timestamp, summary, firstKeptEntryId, tokensBefore, optional usage (LLM usage that generated the summary; counted in session totals), optional fromHook (legacy name for "provided by extension"), optional details.

BranchSummaryEntry: same plus fromId instead of firstKeptEntryId.

Default details is { readFiles: string[], modifiedFiles: string[] }; extensions may store any JSON-serializable structure. Current entries use firstKeptEntryId plus an optional complete systemMessage checkpoint; retainedTail is not the Pi 0.99.2 contract. Context edits affect the projected input, while raw history and costs remain unchanged. See references/session-format.md.

Raw lines 36 to 43
44## Summary Format
46Sections: `## Goal`, `## Constraints & Preferences`, `## Progress` (Done / In Progress / Blocked), `## Key Decisions`, `## Next Steps`, `## Critical Context`, then `<read-files>` and `<modified-files>` blocks.

Summary Format

Sections: ## Goal, ## Constraints & Preferences, ## Progress (Done / In Progress / Blocked), ## Key Decisions, ## Next Steps, ## Critical Context, then <read-files> and <modified-files> blocks.

Raw lines 44 to 47
48## Message Serialization
50`serializeConversation()` renders messages as `[User]:`, `[Assistant thinking]:`, `[Assistant]:`, `[Assistant tool calls]:`, `[Tool result]:` lines so the model does not treat the input as a conversation to continue. Tool results are truncated to 2000 characters during serialization, with a marker showing how many characters were dropped — `read` and `bash` results are usually the largest contributors to context.

Message Serialization

serializeConversation() renders messages as [User]:, [Assistant thinking]:, [Assistant]:, [Assistant tool calls]:, [Tool result]: lines so the model does not treat the input as a conversation to continue. Tool results are truncated to 2000 characters during serialization, with a marker showing how many characters were dropped — read and bash results are usually the largest contributors to context.

Raw lines 48 to 51
52## Extension Hooks
54`session_before_compact` receives `{ preparation, branchEntries, customInstructions, reason, willRetry, signal }`. `preparation` exposes `messagesToSummarize`, `turnPrefixMessages`, `previousSummary`, `fileOps`, `tokensBefore`, `firstKeptEntryId`, and `settings`; `reason` is `"manual"`, `"threshold"`, or `"overflow"`; `willRetry` indicates overflow recovery. Return `{ cancel: true }` or `{ compaction: { summary, firstKeptEntryId, tokensBefore, usage?, details? } }`.
56To summarize with your own model, convert messages first:
58```ts
59import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent";
61const text = serializeConversation(convertToLlm(preparation.messagesToSummarize));
62```
64`session_before_tree` receives `{ preparation, signal }` with `targetId`, `oldLeafId`, `commonAncestorId`, `entriesToSummarize`, and `userWantsSummary`. It always fires, whether or not the user chose to summarize. Return `{ cancel: true }` to cancel navigation, or `{ summary: { summary, usage?, details? } }` (used only when `userWantsSummary`).
66For direct programmatic summarization, `generateSummary()` returns the text and `generateSummaryWithUsage()` returns `{ text, usage }`.

Extension Hooks

session_before_compact receives { preparation, branchEntries, customInstructions, reason, willRetry, signal }. preparation exposes messagesToSummarize, turnPrefixMessages, previousSummary, fileOps, tokensBefore, firstKeptEntryId, and settings; reason is "manual", "threshold", or "overflow"; willRetry indicates overflow recovery. Return { cancel: true } or { compaction: { summary, firstKeptEntryId, tokensBefore, usage?, details? } }.

To summarize with your own model, convert messages first:

import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent";

const text = serializeConversation(convertToLlm(preparation.messagesToSummarize));

session_before_tree receives { preparation, signal } with targetId, oldLeafId, commonAncestorId, entriesToSummarize, and userWantsSummary. It always fires, whether or not the user chose to summarize. Return { cancel: true } to cancel navigation, or { summary: { summary, usage?, details? } } (used only when userWantsSummary).

For direct programmatic summarization, generateSummary() returns the text and generateSummaryWithUsage() returns { text, usage }.

Raw lines 52 to 67
68## Settings
70```json
72 "compaction": {
73 "enabled": true,
74 "reserveTokens": 16384,
75 "keepRecentTokens": 20000
76 }
78```

Settings

{
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  }
}
Raw lines 68 to 78