@mariozechner/pi-coding-agent 0.30.2 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. package/CHANGELOG.md +244 -1
  2. package/README.md +105 -84
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +5 -1
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/file-processor.d.ts +3 -3
  7. package/dist/cli/file-processor.d.ts.map +1 -1
  8. package/dist/cli/file-processor.js +7 -10
  9. package/dist/cli/file-processor.js.map +1 -1
  10. package/dist/config.d.ts +9 -0
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +18 -0
  13. package/dist/config.js.map +1 -1
  14. package/dist/core/agent-session.d.ts +73 -34
  15. package/dist/core/agent-session.d.ts.map +1 -1
  16. package/dist/core/agent-session.js +464 -210
  17. package/dist/core/agent-session.js.map +1 -1
  18. package/dist/core/auth-storage.d.ts +2 -2
  19. package/dist/core/auth-storage.d.ts.map +1 -1
  20. package/dist/core/auth-storage.js +2 -2
  21. package/dist/core/auth-storage.js.map +1 -1
  22. package/dist/core/bash-executor.d.ts +2 -2
  23. package/dist/core/bash-executor.d.ts.map +1 -1
  24. package/dist/core/bash-executor.js +2 -2
  25. package/dist/core/bash-executor.js.map +1 -1
  26. package/dist/core/compaction/branch-summarization.d.ts +84 -0
  27. package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
  28. package/dist/core/compaction/branch-summarization.js +233 -0
  29. package/dist/core/compaction/branch-summarization.js.map +1 -0
  30. package/dist/core/{compaction.d.ts → compaction/compaction.d.ts} +38 -19
  31. package/dist/core/compaction/compaction.d.ts.map +1 -0
  32. package/dist/core/compaction/compaction.js +558 -0
  33. package/dist/core/compaction/compaction.js.map +1 -0
  34. package/dist/core/compaction/index.d.ts +7 -0
  35. package/dist/core/compaction/index.d.ts.map +1 -0
  36. package/dist/core/compaction/index.js +7 -0
  37. package/dist/core/compaction/index.js.map +1 -0
  38. package/dist/core/compaction/utils.d.ts +35 -0
  39. package/dist/core/compaction/utils.d.ts.map +1 -0
  40. package/dist/core/compaction/utils.js +138 -0
  41. package/dist/core/compaction/utils.js.map +1 -0
  42. package/dist/core/custom-tools/index.d.ts +2 -1
  43. package/dist/core/custom-tools/index.d.ts.map +1 -1
  44. package/dist/core/custom-tools/index.js +1 -0
  45. package/dist/core/custom-tools/index.js.map +1 -1
  46. package/dist/core/custom-tools/loader.d.ts.map +1 -1
  47. package/dist/core/custom-tools/loader.js +13 -80
  48. package/dist/core/custom-tools/loader.js.map +1 -1
  49. package/dist/core/custom-tools/types.d.ts +84 -59
  50. package/dist/core/custom-tools/types.d.ts.map +1 -1
  51. package/dist/core/custom-tools/types.js.map +1 -1
  52. package/dist/core/custom-tools/wrapper.d.ts +15 -0
  53. package/dist/core/custom-tools/wrapper.d.ts.map +1 -0
  54. package/dist/core/custom-tools/wrapper.js +23 -0
  55. package/dist/core/custom-tools/wrapper.js.map +1 -0
  56. package/dist/core/exec.d.ts +29 -0
  57. package/dist/core/exec.d.ts.map +1 -0
  58. package/dist/core/exec.js +71 -0
  59. package/dist/core/exec.js.map +1 -0
  60. package/dist/core/export-html/index.d.ts +17 -0
  61. package/dist/core/export-html/index.d.ts.map +1 -0
  62. package/dist/core/export-html/index.js +171 -0
  63. package/dist/core/export-html/index.js.map +1 -0
  64. package/dist/core/export-html/template.css +781 -0
  65. package/dist/core/export-html/template.html +54 -0
  66. package/dist/core/export-html/template.js +1185 -0
  67. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  68. package/dist/core/export-html/vendor/marked.min.js +6 -0
  69. package/dist/core/hooks/index.d.ts +4 -4
  70. package/dist/core/hooks/index.d.ts.map +1 -1
  71. package/dist/core/hooks/index.js +3 -3
  72. package/dist/core/hooks/index.js.map +1 -1
  73. package/dist/core/hooks/loader.d.ts +40 -5
  74. package/dist/core/hooks/loader.d.ts.map +1 -1
  75. package/dist/core/hooks/loader.js +43 -10
  76. package/dist/core/hooks/loader.js.map +1 -1
  77. package/dist/core/hooks/runner.d.ts +94 -18
  78. package/dist/core/hooks/runner.d.ts.map +1 -1
  79. package/dist/core/hooks/runner.js +199 -120
  80. package/dist/core/hooks/runner.js.map +1 -1
  81. package/dist/core/hooks/tool-wrapper.d.ts +1 -1
  82. package/dist/core/hooks/tool-wrapper.d.ts.map +1 -1
  83. package/dist/core/hooks/tool-wrapper.js +36 -19
  84. package/dist/core/hooks/tool-wrapper.js.map +1 -1
  85. package/dist/core/hooks/types.d.ts +407 -96
  86. package/dist/core/hooks/types.d.ts.map +1 -1
  87. package/dist/core/hooks/types.js.map +1 -1
  88. package/dist/core/index.d.ts +4 -3
  89. package/dist/core/index.d.ts.map +1 -1
  90. package/dist/core/index.js.map +1 -1
  91. package/dist/core/messages.d.ts +44 -12
  92. package/dist/core/messages.d.ts.map +1 -1
  93. package/dist/core/messages.js +82 -34
  94. package/dist/core/messages.js.map +1 -1
  95. package/dist/core/model-registry.d.ts +5 -5
  96. package/dist/core/model-registry.d.ts.map +1 -1
  97. package/dist/core/model-registry.js +7 -7
  98. package/dist/core/model-registry.js.map +1 -1
  99. package/dist/core/model-resolver.d.ts +7 -7
  100. package/dist/core/model-resolver.d.ts.map +1 -1
  101. package/dist/core/model-resolver.js +45 -14
  102. package/dist/core/model-resolver.js.map +1 -1
  103. package/dist/core/sdk.d.ts +7 -10
  104. package/dist/core/sdk.d.ts.map +1 -1
  105. package/dist/core/sdk.js +88 -32
  106. package/dist/core/sdk.js.map +1 -1
  107. package/dist/core/session-manager.d.ts +202 -36
  108. package/dist/core/session-manager.d.ts.map +1 -1
  109. package/dist/core/session-manager.js +565 -133
  110. package/dist/core/session-manager.js.map +1 -1
  111. package/dist/core/settings-manager.d.ts +9 -3
  112. package/dist/core/settings-manager.d.ts.map +1 -1
  113. package/dist/core/settings-manager.js +13 -12
  114. package/dist/core/settings-manager.js.map +1 -1
  115. package/dist/core/system-prompt.d.ts.map +1 -1
  116. package/dist/core/system-prompt.js +6 -3
  117. package/dist/core/system-prompt.js.map +1 -1
  118. package/dist/core/tools/bash.d.ts +1 -1
  119. package/dist/core/tools/bash.d.ts.map +1 -1
  120. package/dist/core/tools/bash.js.map +1 -1
  121. package/dist/core/tools/edit-diff.d.ts +33 -0
  122. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  123. package/dist/core/tools/edit-diff.js +171 -0
  124. package/dist/core/tools/edit-diff.js.map +1 -0
  125. package/dist/core/tools/edit.d.ts +7 -1
  126. package/dist/core/tools/edit.d.ts.map +1 -1
  127. package/dist/core/tools/edit.js +20 -95
  128. package/dist/core/tools/edit.js.map +1 -1
  129. package/dist/core/tools/find.d.ts +1 -1
  130. package/dist/core/tools/find.d.ts.map +1 -1
  131. package/dist/core/tools/find.js.map +1 -1
  132. package/dist/core/tools/grep.d.ts +1 -1
  133. package/dist/core/tools/grep.d.ts.map +1 -1
  134. package/dist/core/tools/grep.js.map +1 -1
  135. package/dist/core/tools/index.d.ts +1 -1
  136. package/dist/core/tools/index.d.ts.map +1 -1
  137. package/dist/core/tools/index.js.map +1 -1
  138. package/dist/core/tools/ls.d.ts +1 -1
  139. package/dist/core/tools/ls.d.ts.map +1 -1
  140. package/dist/core/tools/ls.js.map +1 -1
  141. package/dist/core/tools/read.d.ts +1 -1
  142. package/dist/core/tools/read.d.ts.map +1 -1
  143. package/dist/core/tools/read.js.map +1 -1
  144. package/dist/core/tools/write.d.ts +1 -1
  145. package/dist/core/tools/write.d.ts.map +1 -1
  146. package/dist/core/tools/write.js.map +1 -1
  147. package/dist/index.d.ts +8 -7
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +5 -5
  150. package/dist/index.js.map +1 -1
  151. package/dist/main.d.ts.map +1 -1
  152. package/dist/main.js +22 -21
  153. package/dist/main.js.map +1 -1
  154. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  155. package/dist/modes/interactive/components/assistant-message.js +3 -4
  156. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  157. package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
  158. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/bash-execution.js +6 -2
  160. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  161. package/dist/modes/interactive/components/bordered-loader.d.ts +12 -0
  162. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
  163. package/dist/modes/interactive/components/bordered-loader.js +30 -0
  164. package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
  165. package/dist/modes/interactive/components/branch-summary-message.d.ts +14 -0
  166. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
  167. package/dist/modes/interactive/components/branch-summary-message.js +35 -0
  168. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
  169. package/dist/modes/interactive/components/compaction-summary-message.d.ts +14 -0
  170. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
  171. package/dist/modes/interactive/components/compaction-summary-message.js +36 -0
  172. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
  173. package/dist/modes/interactive/components/dynamic-border.d.ts +5 -1
  174. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  175. package/dist/modes/interactive/components/dynamic-border.js +5 -1
  176. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  177. package/dist/modes/interactive/components/footer.d.ts +12 -6
  178. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  179. package/dist/modes/interactive/components/footer.js +57 -25
  180. package/dist/modes/interactive/components/footer.js.map +1 -1
  181. package/dist/modes/interactive/components/hook-editor.d.ts +15 -0
  182. package/dist/modes/interactive/components/hook-editor.d.ts.map +1 -0
  183. package/dist/modes/interactive/components/hook-editor.js +95 -0
  184. package/dist/modes/interactive/components/hook-editor.js.map +1 -0
  185. package/dist/modes/interactive/components/hook-message.d.ts +18 -0
  186. package/dist/modes/interactive/components/hook-message.d.ts.map +1 -0
  187. package/dist/modes/interactive/components/hook-message.js +80 -0
  188. package/dist/modes/interactive/components/hook-message.js.map +1 -0
  189. package/dist/modes/interactive/components/model-selector.d.ts +3 -3
  190. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  191. package/dist/modes/interactive/components/model-selector.js +1 -1
  192. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  193. package/dist/modes/interactive/components/tool-execution.d.ts +15 -2
  194. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/tool-execution.js +70 -21
  196. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  197. package/dist/modes/interactive/components/tree-selector.d.ts +52 -0
  198. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
  199. package/dist/modes/interactive/components/tree-selector.js +745 -0
  200. package/dist/modes/interactive/components/tree-selector.js.map +1 -0
  201. package/dist/modes/interactive/components/user-message-selector.d.ts +3 -3
  202. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  203. package/dist/modes/interactive/components/user-message-selector.js +1 -1
  204. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  205. package/dist/modes/interactive/components/user-message.d.ts +1 -1
  206. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/user-message.js +2 -5
  208. package/dist/modes/interactive/components/user-message.js.map +1 -1
  209. package/dist/modes/interactive/interactive-mode.d.ts +29 -12
  210. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  211. package/dist/modes/interactive/interactive-mode.js +589 -208
  212. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  213. package/dist/modes/interactive/theme/dark.json +13 -1
  214. package/dist/modes/interactive/theme/light.json +13 -1
  215. package/dist/modes/interactive/theme/theme-schema.json +34 -0
  216. package/dist/modes/interactive/theme/theme.d.ts +20 -2
  217. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  218. package/dist/modes/interactive/theme/theme.js +135 -2
  219. package/dist/modes/interactive/theme/theme.js.map +1 -1
  220. package/dist/modes/print-mode.d.ts +3 -3
  221. package/dist/modes/print-mode.d.ts.map +1 -1
  222. package/dist/modes/print-mode.js +26 -20
  223. package/dist/modes/print-mode.js.map +1 -1
  224. package/dist/modes/rpc/rpc-client.d.ts +13 -10
  225. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  226. package/dist/modes/rpc/rpc-client.js +11 -10
  227. package/dist/modes/rpc/rpc-client.js.map +1 -1
  228. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  229. package/dist/modes/rpc/rpc-mode.js +88 -35
  230. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  231. package/dist/modes/rpc/rpc-types.d.ts +30 -11
  232. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  233. package/dist/modes/rpc/rpc-types.js.map +1 -1
  234. package/dist/utils/shell.d.ts +4 -2
  235. package/dist/utils/shell.d.ts.map +1 -1
  236. package/dist/utils/shell.js +36 -7
  237. package/dist/utils/shell.js.map +1 -1
  238. package/dist/utils/tools-manager.d.ts +1 -1
  239. package/dist/utils/tools-manager.d.ts.map +1 -1
  240. package/dist/utils/tools-manager.js +2 -2
  241. package/dist/utils/tools-manager.js.map +1 -1
  242. package/docs/compaction.md +388 -0
  243. package/docs/custom-tools.md +146 -43
  244. package/docs/extension-loading.md +1004 -0
  245. package/docs/hooks.md +562 -596
  246. package/docs/rpc.md +33 -19
  247. package/docs/sdk.md +93 -21
  248. package/docs/session-tree-plan.md +441 -0
  249. package/docs/session.md +172 -21
  250. package/docs/skills.md +2 -0
  251. package/docs/theme.md +31 -2
  252. package/docs/tree.md +197 -0
  253. package/docs/tui.md +343 -0
  254. package/examples/README.md +1 -9
  255. package/examples/custom-tools/hello/index.ts +4 -3
  256. package/examples/custom-tools/question/index.ts +4 -4
  257. package/examples/custom-tools/subagent/index.ts +7 -6
  258. package/examples/custom-tools/todo/index.ts +11 -5
  259. package/examples/hooks/README.md +29 -71
  260. package/examples/hooks/auto-commit-on-exit.ts +8 -9
  261. package/examples/hooks/confirm-destructive.ts +29 -30
  262. package/examples/hooks/custom-compaction.ts +20 -21
  263. package/examples/hooks/dirty-repo-guard.ts +41 -40
  264. package/examples/hooks/file-trigger.ts +10 -5
  265. package/examples/hooks/git-checkpoint.ts +16 -12
  266. package/examples/hooks/handoff.ts +150 -0
  267. package/examples/hooks/permission-gate.ts +1 -1
  268. package/examples/hooks/protected-paths.ts +1 -1
  269. package/examples/hooks/qna.ts +119 -0
  270. package/examples/hooks/snake.ts +343 -0
  271. package/examples/hooks/status-line.ts +40 -0
  272. package/examples/sdk/01-minimal.ts +1 -1
  273. package/examples/sdk/02-custom-model.ts +1 -1
  274. package/examples/sdk/03-custom-prompt.ts +1 -1
  275. package/examples/sdk/04-skills.ts +1 -1
  276. package/examples/sdk/05-tools.ts +4 -4
  277. package/examples/sdk/06-hooks.ts +1 -1
  278. package/examples/sdk/07-context-files.ts +1 -1
  279. package/examples/sdk/08-slash-commands.ts +6 -1
  280. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  281. package/examples/sdk/10-settings.ts +1 -1
  282. package/examples/sdk/11-sessions.ts +1 -1
  283. package/examples/sdk/12-full-control.ts +4 -7
  284. package/package.json +6 -6
  285. package/dist/core/compaction.d.ts.map +0 -1
  286. package/dist/core/compaction.js +0 -412
  287. package/dist/core/compaction.js.map +0 -1
  288. package/dist/core/export-html.d.ts +0 -23
  289. package/dist/core/export-html.d.ts.map +0 -1
  290. package/dist/core/export-html.js +0 -1185
  291. package/dist/core/export-html.js.map +0 -1
  292. package/dist/modes/interactive/components/compaction.d.ts +0 -15
  293. package/dist/modes/interactive/components/compaction.d.ts.map +0 -1
  294. package/dist/modes/interactive/components/compaction.js +0 -41
  295. package/dist/modes/interactive/components/compaction.js.map +0 -1
  296. package/docs/hooks-v2.md +0 -385
  297. package/docs/session-tree.md +0 -452
@@ -0,0 +1,388 @@
1
+ # Compaction & Branch Summarization
2
+
3
+ LLMs have limited context windows. When conversations grow too long, pi uses compaction to summarize older content while preserving recent work. This page covers both auto-compaction and branch summarization.
4
+
5
+ **Source files:**
6
+ - [`src/core/compaction/compaction.ts`](../src/core/compaction/compaction.ts) - Auto-compaction logic
7
+ - [`src/core/compaction/branch-summarization.ts`](../src/core/compaction/branch-summarization.ts) - Branch summarization
8
+ - [`src/core/compaction/utils.ts`](../src/core/compaction/utils.ts) - Shared utilities (file tracking, serialization)
9
+ - [`src/core/session-manager.ts`](../src/core/session-manager.ts) - Entry types (`CompactionEntry`, `BranchSummaryEntry`)
10
+ - [`src/core/hooks/types.ts`](../src/core/hooks/types.ts) - Hook event types
11
+
12
+ ## Overview
13
+
14
+ Pi has two summarization mechanisms:
15
+
16
+ | Mechanism | Trigger | Purpose |
17
+ |-----------|---------|---------|
18
+ | Compaction | Context exceeds threshold, or `/compact` | Summarize old messages to free up context |
19
+ | Branch summarization | `/tree` navigation | Preserve context when switching branches |
20
+
21
+ Both use the same structured summary format and track file operations cumulatively.
22
+
23
+ ## Compaction
24
+
25
+ ### When It Triggers
26
+
27
+ Auto-compaction triggers when:
28
+
29
+ ```
30
+ contextTokens > contextWindow - reserveTokens
31
+ ```
32
+
33
+ By default, `reserveTokens` is 16384 tokens (configurable in `~/.pi/agent/settings.json` or `<project-dir>/.pi/settings.json`). This leaves room for the LLM's response.
34
+
35
+ You can also trigger manually with `/compact [instructions]`, where optional instructions focus the summary.
36
+
37
+ ### How It Works
38
+
39
+ 1. **Find cut point**: Walk backwards from newest message, accumulating token estimates until `keepRecentTokens` (default 20k, configurable in `~/.pi/agent/settings.json` or `<project-dir>/.pi/settings.json`) is reached
40
+ 2. **Extract messages**: Collect messages from previous compaction (or start) up to cut point
41
+ 3. **Generate summary**: Call LLM to summarize with structured format
42
+ 4. **Append entry**: Save `CompactionEntry` with summary and `firstKeptEntryId`
43
+ 5. **Reload**: Session reloads, using summary + messages from `firstKeptEntryId` onwards
44
+
45
+ ```
46
+ Before compaction:
47
+
48
+ entry: 0 1 2 3 4 5 6 7 8 9
49
+ ┌─────┬─────┬─────┬─────┬──────┬─────┬─────┬──────┬──────┬─────┐
50
+ │ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool│
51
+ └─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴─────┘
52
+ └────────┬───────┘ └──────────────┬──────────────┘
53
+ messagesToSummarize kept messages
54
+
55
+ firstKeptEntryId (entry 4)
56
+
57
+ After compaction (new entry appended):
58
+
59
+ entry: 0 1 2 3 4 5 6 7 8 9 10
60
+ ┌─────┬─────┬─────┬─────┬──────┬─────┬─────┬──────┬──────┬─────┬─────┐
61
+ │ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool│ cmp │
62
+ └─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴─────┴─────┘
63
+ └──────────┬──────┘ └──────────────────────┬───────────────────┘
64
+ not sent to LLM sent to LLM
65
+
66
+ starts from firstKeptEntryId
67
+
68
+ What the LLM sees:
69
+
70
+ ┌────────┬─────────┬─────┬─────┬──────┬──────┬─────┬──────┐
71
+ │ system │ summary │ usr │ ass │ tool │ tool │ ass │ tool │
72
+ └────────┴─────────┴─────┴─────┴──────┴──────┴─────┴──────┘
73
+ ↑ ↑ └─────────────────┬────────────────┘
74
+ prompt from cmp messages from firstKeptEntryId
75
+ ```
76
+
77
+ ### Split Turns
78
+
79
+ A "turn" starts with a user message and includes all assistant responses and tool calls until the next user message. Normally, compaction cuts at turn boundaries.
80
+
81
+ When a single turn exceeds `keepRecentTokens`, the cut point lands mid-turn at an assistant message. This is a "split turn":
82
+
83
+ ```
84
+ Split turn (one huge turn exceeds budget):
85
+
86
+ entry: 0 1 2 3 4 5 6 7 8
87
+ ┌─────┬─────┬─────┬──────┬─────┬──────┬──────┬─────┬──────┐
88
+ │ hdr │ usr │ ass │ tool │ ass │ tool │ tool │ ass │ tool │
89
+ └─────┴─────┴─────┴──────┴─────┴──────┴──────┴─────┴──────┘
90
+ ↑ ↑
91
+ turnStartIndex = 1 firstKeptEntryId = 7
92
+ │ │
93
+ └──── turnPrefixMessages (1-6) ───────┘
94
+ └── kept (7-8)
95
+
96
+ isSplitTurn = true
97
+ messagesToSummarize = [] (no complete turns before)
98
+ turnPrefixMessages = [usr, ass, tool, ass, tool, tool]
99
+ ```
100
+
101
+ For split turns, pi generates two summaries and merges them:
102
+ 1. **History summary**: Previous context (if any)
103
+ 2. **Turn prefix summary**: The early part of the split turn
104
+
105
+ ### Cut Point Rules
106
+
107
+ Valid cut points are:
108
+ - User messages
109
+ - Assistant messages
110
+ - BashExecution messages
111
+ - Hook messages (custom_message, branch_summary)
112
+
113
+ Never cut at tool results (they must stay with their tool call).
114
+
115
+ ### CompactionEntry Structure
116
+
117
+ Defined in [`src/core/session-manager.ts`](../src/core/session-manager.ts):
118
+
119
+ ```typescript
120
+ interface CompactionEntry<T = unknown> {
121
+ type: "compaction";
122
+ id: string;
123
+ parentId: string;
124
+ timestamp: number;
125
+ summary: string;
126
+ firstKeptEntryId: string;
127
+ tokensBefore: number;
128
+ fromHook?: boolean; // true if hook provided the compaction
129
+ details?: T; // hook-specific data
130
+ }
131
+
132
+ // Default compaction uses this for details (from compaction.ts):
133
+ interface CompactionDetails {
134
+ readFiles: string[];
135
+ modifiedFiles: string[];
136
+ }
137
+ ```
138
+
139
+ Hooks can store any JSON-serializable data in `details`. The default compaction tracks file operations, but custom compaction hooks can use their own structure.
140
+
141
+ See [`prepareCompaction()`](../src/core/compaction/compaction.ts) and [`compact()`](../src/core/compaction/compaction.ts) for the implementation.
142
+
143
+ ## Branch Summarization
144
+
145
+ ### When It Triggers
146
+
147
+ When you use `/tree` to navigate to a different branch, pi offers to summarize the work you're leaving. This injects context from the left branch into the new branch.
148
+
149
+ ### How It Works
150
+
151
+ 1. **Find common ancestor**: Deepest node shared by old and new positions
152
+ 2. **Collect entries**: Walk from old leaf back to common ancestor
153
+ 3. **Prepare with budget**: Include messages up to token budget (newest first)
154
+ 4. **Generate summary**: Call LLM with structured format
155
+ 5. **Append entry**: Save `BranchSummaryEntry` at navigation point
156
+
157
+ ```
158
+ Tree before navigation:
159
+
160
+ ┌─ B ─ C ─ D (old leaf, being abandoned)
161
+ A ───┤
162
+ └─ E ─ F (target)
163
+
164
+ Common ancestor: A
165
+ Entries to summarize: B, C, D
166
+
167
+ After navigation with summary:
168
+
169
+ ┌─ B ─ C ─ D ─ [summary of B,C,D]
170
+ A ───┤
171
+ └─ E ─ F (new leaf)
172
+ ```
173
+
174
+ ### Cumulative File Tracking
175
+
176
+ Both compaction and branch summarization track files cumulatively. When generating a summary, pi extracts file operations from:
177
+ - Tool calls in the messages being summarized
178
+ - Previous compaction or branch summary `details` (if any)
179
+
180
+ This means file tracking accumulates across multiple compactions or nested branch summaries, preserving the full history of read and modified files.
181
+
182
+ ### BranchSummaryEntry Structure
183
+
184
+ Defined in [`src/core/session-manager.ts`](../src/core/session-manager.ts):
185
+
186
+ ```typescript
187
+ interface BranchSummaryEntry<T = unknown> {
188
+ type: "branch_summary";
189
+ id: string;
190
+ parentId: string;
191
+ timestamp: number;
192
+ summary: string;
193
+ fromId: string; // Entry we navigated from
194
+ fromHook?: boolean; // true if hook provided the summary
195
+ details?: T; // hook-specific data
196
+ }
197
+
198
+ // Default branch summarization uses this for details (from branch-summarization.ts):
199
+ interface BranchSummaryDetails {
200
+ readFiles: string[];
201
+ modifiedFiles: string[];
202
+ }
203
+ ```
204
+
205
+ Same as compaction, hooks can store custom data in `details`.
206
+
207
+ See [`collectEntriesForBranchSummary()`](../src/core/compaction/branch-summarization.ts), [`prepareBranchEntries()`](../src/core/compaction/branch-summarization.ts), and [`generateBranchSummary()`](../src/core/compaction/branch-summarization.ts) for the implementation.
208
+
209
+ ## Summary Format
210
+
211
+ Both compaction and branch summarization use the same structured format:
212
+
213
+ ```markdown
214
+ ## Goal
215
+ [What the user is trying to accomplish]
216
+
217
+ ## Constraints & Preferences
218
+ - [Requirements mentioned by user]
219
+
220
+ ## Progress
221
+ ### Done
222
+ - [x] [Completed tasks]
223
+
224
+ ### In Progress
225
+ - [ ] [Current work]
226
+
227
+ ### Blocked
228
+ - [Issues, if any]
229
+
230
+ ## Key Decisions
231
+ - **[Decision]**: [Rationale]
232
+
233
+ ## Next Steps
234
+ 1. [What should happen next]
235
+
236
+ ## Critical Context
237
+ - [Data needed to continue]
238
+
239
+ <read-files>
240
+ path/to/file1.ts
241
+ path/to/file2.ts
242
+ </read-files>
243
+
244
+ <modified-files>
245
+ path/to/changed.ts
246
+ </modified-files>
247
+ ```
248
+
249
+ ### Message Serialization
250
+
251
+ Before summarization, messages are serialized to text via [`serializeConversation()`](../src/core/compaction/utils.ts):
252
+
253
+ ```
254
+ [User]: What they said
255
+ [Assistant thinking]: Internal reasoning
256
+ [Assistant]: Response text
257
+ [Assistant tool calls]: read(path="foo.ts"); edit(path="bar.ts", ...)
258
+ [Tool result]: Output from tool
259
+ ```
260
+
261
+ This prevents the model from treating it as a conversation to continue.
262
+
263
+ ## Custom Summarization via Hooks
264
+
265
+ Hooks can intercept and customize both compaction and branch summarization. See [`src/core/hooks/types.ts`](../src/core/hooks/types.ts) for event type definitions.
266
+
267
+ ### session_before_compact
268
+
269
+ Fired before auto-compaction or `/compact`. Can cancel or provide custom summary. See `SessionBeforeCompactEvent` and `CompactionPreparation` in the types file.
270
+
271
+ ```typescript
272
+ pi.on("session_before_compact", async (event, ctx) => {
273
+ const { preparation, branchEntries, customInstructions, signal } = event;
274
+
275
+ // preparation.messagesToSummarize - messages to summarize
276
+ // preparation.turnPrefixMessages - split turn prefix (if isSplitTurn)
277
+ // preparation.previousSummary - previous compaction summary
278
+ // preparation.fileOps - extracted file operations
279
+ // preparation.tokensBefore - context tokens before compaction
280
+ // preparation.firstKeptEntryId - where kept messages start
281
+ // preparation.settings - compaction settings
282
+
283
+ // branchEntries - all entries on current branch (for custom state)
284
+ // signal - AbortSignal (pass to LLM calls)
285
+
286
+ // Cancel:
287
+ return { cancel: true };
288
+
289
+ // Custom summary:
290
+ return {
291
+ compaction: {
292
+ summary: "Your summary...",
293
+ firstKeptEntryId: preparation.firstKeptEntryId,
294
+ tokensBefore: preparation.tokensBefore,
295
+ details: { /* custom data */ },
296
+ }
297
+ };
298
+ });
299
+ ```
300
+
301
+ #### Converting Messages to Text
302
+
303
+ To generate a summary with your own model, convert messages to text using `serializeConversation`:
304
+
305
+ ```typescript
306
+ import { convertToLlm, serializeConversation } from "@mariozechner/pi-coding-agent";
307
+
308
+ pi.on("session_before_compact", async (event, ctx) => {
309
+ const { preparation } = event;
310
+
311
+ // Convert AgentMessage[] to Message[], then serialize to text
312
+ const conversationText = serializeConversation(
313
+ convertToLlm(preparation.messagesToSummarize)
314
+ );
315
+ // Returns:
316
+ // [User]: message text
317
+ // [Assistant thinking]: thinking content
318
+ // [Assistant]: response text
319
+ // [Assistant tool calls]: read(path="..."); bash(command="...")
320
+ // [Tool result]: output text
321
+
322
+ // Now send to your model for summarization
323
+ const summary = await myModel.summarize(conversationText);
324
+
325
+ return {
326
+ compaction: {
327
+ summary,
328
+ firstKeptEntryId: preparation.firstKeptEntryId,
329
+ tokensBefore: preparation.tokensBefore,
330
+ }
331
+ };
332
+ });
333
+ ```
334
+
335
+ See [examples/hooks/custom-compaction.ts](../examples/hooks/custom-compaction.ts) for a complete example using a different model.
336
+
337
+ ### session_before_tree
338
+
339
+ Fired before `/tree` navigation. Always fires regardless of whether user chose to summarize. Can cancel navigation or provide custom summary.
340
+
341
+ ```typescript
342
+ pi.on("session_before_tree", async (event, ctx) => {
343
+ const { preparation, signal } = event;
344
+
345
+ // preparation.targetId - where we're navigating to
346
+ // preparation.oldLeafId - current position (being abandoned)
347
+ // preparation.commonAncestorId - shared ancestor
348
+ // preparation.entriesToSummarize - entries that would be summarized
349
+ // preparation.userWantsSummary - whether user chose to summarize
350
+
351
+ // Cancel navigation entirely:
352
+ return { cancel: true };
353
+
354
+ // Provide custom summary (only used if userWantsSummary is true):
355
+ if (preparation.userWantsSummary) {
356
+ return {
357
+ summary: {
358
+ summary: "Your summary...",
359
+ details: { /* custom data */ },
360
+ }
361
+ };
362
+ }
363
+ });
364
+ ```
365
+
366
+ See `SessionBeforeTreeEvent` and `TreePreparation` in the types file.
367
+
368
+ ## Settings
369
+
370
+ Configure compaction in `~/.pi/agent/settings.json` or `<project-dir>/.pi/settings.json`:
371
+
372
+ ```json
373
+ {
374
+ "compaction": {
375
+ "enabled": true,
376
+ "reserveTokens": 16384,
377
+ "keepRecentTokens": 20000
378
+ }
379
+ }
380
+ ```
381
+
382
+ | Setting | Default | Description |
383
+ |---------|---------|-------------|
384
+ | `enabled` | `true` | Enable auto-compaction |
385
+ | `reserveTokens` | `16384` | Tokens to reserve for LLM response |
386
+ | `keepRecentTokens` | `20000` | Recent tokens to keep (not summarized) |
387
+
388
+ Disable auto-compaction with `"enabled": false`. You can still compact manually with `/compact`.