@herbertgao/pi-extensions 2026.9.20 → 2026.10.1

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 (357) hide show
  1. package/README.md +18 -13
  2. package/THIRD_PARTY_NOTICES.md +31 -5
  3. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +1 -1
  4. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +1 -1
  5. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
  6. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +1 -1
  7. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
  8. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/index.js +4 -0
  9. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +43 -3
  10. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +142 -59
  11. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
  12. package/node_modules/@dietrichgebert/ponytail/README.es.md +87 -204
  13. package/node_modules/@dietrichgebert/ponytail/README.ja.md +212 -0
  14. package/node_modules/@dietrichgebert/ponytail/README.ko.md +92 -226
  15. package/node_modules/@dietrichgebert/ponytail/README.md +58 -244
  16. package/node_modules/@dietrichgebert/ponytail/README.zh-CN.md +212 -0
  17. package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +3 -3
  18. package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +4 -4
  19. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +63 -16
  20. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +8 -7
  21. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +46 -23
  22. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +39 -7
  23. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +20 -3
  24. package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +3 -3
  25. package/node_modules/@dietrichgebert/ponytail/package.json +2 -2
  26. package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +47 -17
  27. package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +21 -7
  28. package/node_modules/@dietrichgebert/ponytail/skills/plugin.json +5 -0
  29. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +7 -2
  30. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +1 -1
  31. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +16 -14
  32. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +3 -3
  33. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +12 -7
  34. package/node_modules/@herbertgao/pi-bark/README.md +39 -3
  35. package/node_modules/@herbertgao/pi-bark/package.json +1 -1
  36. package/node_modules/@herbertgao/pi-bark/src/index.ts +275 -15
  37. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +50 -50
  38. package/node_modules/@herbertgao/pi-cc-extensions/README.md +7 -7
  39. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +25 -2
  40. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +24 -1
  41. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -4
  42. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +47 -5
  43. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/mcp-chip.ts +136 -0
  44. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +56 -11
  45. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +36 -4
  46. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +11 -2
  47. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +303 -0
  48. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +1 -1
  49. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +36 -0
  50. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
  51. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +2 -2
  52. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +88 -34
  53. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +6 -5
  54. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -1
  55. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +31 -6
  56. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/message-display.ts +3 -3
  57. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +3 -0
  58. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +173 -36
  59. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +1 -0
  60. package/node_modules/@herbertgao/pi-cc-extensions/package.json +6 -4
  61. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  62. package/node_modules/@herbertgao/pi-subagents/package.json +1 -1
  63. package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +3 -2
  64. package/node_modules/@herbertgao/sol-pi/CONTRIBUTING.md +22 -0
  65. package/node_modules/@herbertgao/sol-pi/README.md +36 -17
  66. package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +10 -6
  67. package/node_modules/@herbertgao/sol-pi/agents-install.md +15 -12
  68. package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +24 -8
  69. package/node_modules/@herbertgao/sol-pi/docs/configuration.md +2 -2
  70. package/node_modules/@herbertgao/sol-pi/docs/star-history.md +18 -0
  71. package/node_modules/@herbertgao/sol-pi/package.json +4 -2
  72. package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +1 -1
  73. package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +1 -1
  74. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +23 -4
  75. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +32 -1
  76. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +3 -2
  77. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +1 -1
  78. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +27 -7
  79. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +137 -41
  80. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +1 -0
  81. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +2 -1
  82. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/projection.ts +58 -0
  83. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +28 -6
  84. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +2 -1
  85. package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +15 -2
  86. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  87. package/node_modules/@narumitw/pi-caffeinate/package.json +4 -4
  88. package/node_modules/@pi-plugins/fast-mode/README.md +7 -1
  89. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  90. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +7 -1
  91. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  92. package/node_modules/@pi-plugins/fast-mode/package.json +5 -5
  93. package/node_modules/pi-antigravity/README.md +3 -0
  94. package/node_modules/pi-antigravity/package.json +8 -8
  95. package/node_modules/pi-antigravity/src/auth/oauth.ts +6 -3
  96. package/node_modules/pi-antigravity/src/index.ts +114 -110
  97. package/node_modules/pi-antigravity/src/stream/stream.ts +1 -1
  98. package/node_modules/pi-antigravity/src/utils/util.ts +18 -1
  99. package/node_modules/pi-multi-account/CHANGELOG.md +20 -0
  100. package/node_modules/pi-multi-account/README.md +46 -8
  101. package/node_modules/pi-multi-account/config-file-transaction.ts +42 -0
  102. package/node_modules/pi-multi-account/index.ts +255 -269
  103. package/node_modules/pi-multi-account/model-pins.ts +36 -0
  104. package/node_modules/pi-multi-account/package.json +9 -7
  105. package/node_modules/pi-multi-account/slot-proxy-auth.ts +12 -2
  106. package/node_modules/pi-multi-account/slot-proxy.ts +27 -5
  107. package/node_modules/pi-typesafe/README.md +1 -1
  108. package/node_modules/pi-typesafe/dist/backends.d.ts +15 -5
  109. package/node_modules/pi-typesafe/dist/backends.js +18 -0
  110. package/node_modules/pi-typesafe/dist/client.d.ts +4 -3
  111. package/node_modules/pi-typesafe/dist/client.js +22 -7
  112. package/node_modules/pi-typesafe/dist/index.d.ts +2 -2
  113. package/node_modules/pi-typesafe/dist/index.js +1 -1
  114. package/node_modules/pi-typesafe/dist/usage.d.ts +14 -4
  115. package/node_modules/pi-typesafe/dist/usage.js +147 -42
  116. package/node_modules/pi-typesafe/package.json +1 -1
  117. package/node_modules/pi-web-access/CHANGELOG.md +65 -0
  118. package/node_modules/pi-web-access/README.md +63 -14
  119. package/node_modules/pi-web-access/brave-rate-limit.ts +176 -0
  120. package/node_modules/pi-web-access/brave.ts +28 -11
  121. package/node_modules/pi-web-access/curator-page.ts +5 -1
  122. package/node_modules/pi-web-access/curator-server.ts +2 -0
  123. package/node_modules/pi-web-access/declared-web-links.ts +2 -2
  124. package/node_modules/pi-web-access/degoog.ts +323 -0
  125. package/node_modules/pi-web-access/dist/index.js +3647 -2740
  126. package/node_modules/pi-web-access/dist/mcp-cli.js +27443 -0
  127. package/node_modules/pi-web-access/exa.ts +5 -1
  128. package/node_modules/pi-web-access/extract.ts +41 -3
  129. package/node_modules/pi-web-access/firecrawl.ts +35 -4
  130. package/node_modules/pi-web-access/gemini-search.ts +36 -16
  131. package/node_modules/pi-web-access/gemini-web.ts +14 -9
  132. package/node_modules/pi-web-access/index.ts +164 -1207
  133. package/node_modules/pi-web-access/keenable.ts +221 -0
  134. package/node_modules/pi-web-access/mcp-cli.ts +40 -0
  135. package/node_modules/pi-web-access/mcp-server.ts +81 -0
  136. package/node_modules/pi-web-access/mcp-tool-definitions.ts +117 -0
  137. package/node_modules/pi-web-access/openai-search.ts +58 -11
  138. package/node_modules/pi-web-access/package.json +23 -5
  139. package/node_modules/pi-web-access/perplexity.ts +64 -56
  140. package/node_modules/pi-web-access/summary-review.ts +8 -2
  141. package/node_modules/pi-web-access/tool-arguments.ts +20 -0
  142. package/node_modules/pi-web-access/video-extract.ts +1 -1
  143. package/node_modules/pi-web-access/web-tool-contract.ts +86 -0
  144. package/node_modules/pi-web-access/web-tool-core.ts +1502 -0
  145. package/node_modules/pi-web-access/youtube-extract.ts +2 -5
  146. package/node_modules/{@herbertgao/resume-from → resume-from}/README.md +16 -12
  147. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/adapter.d.ts +0 -5
  148. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/adapter.js +2 -3
  149. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/contract.d.ts +2 -2
  150. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/reader.d.ts +3 -0
  151. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/reader.js +27 -15
  152. package/node_modules/{@herbertgao/resume-from/dist/adapters/codex → resume-from/dist/adapters/claude-code}/redaction.js +16 -3
  153. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/writer.d.ts +2 -4
  154. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/writer.js +1 -1
  155. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/contract.d.ts +2 -4
  156. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/index.js +1 -2
  157. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/read.d.ts +16 -0
  158. package/node_modules/resume-from/dist/adapters/codex/read.js +723 -0
  159. package/node_modules/{@herbertgao/resume-from/dist/adapters/pi → resume-from/dist/adapters/codex}/redaction.js +16 -3
  160. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/rollout.d.ts +44 -4
  161. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/rollout.js +88 -4
  162. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/write.d.ts +2 -2
  163. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/write.js +2 -2
  164. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/contract.d.ts +5 -1
  165. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/adapter.js +10 -6
  166. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/contract.d.ts +2 -2
  167. package/node_modules/{@herbertgao/resume-from/dist/adapters/claude-code → resume-from/dist/adapters/pi}/redaction.js +16 -3
  168. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/serialize.d.ts +2 -4
  169. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/serialize.js +2 -2
  170. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/render.d.ts +1 -1
  171. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/render.js +4 -4
  172. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/runner.js +2 -2
  173. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/contract.d.ts +2 -2
  174. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/command.js +31 -8
  175. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/wiring.d.ts +1 -1
  176. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/wiring.js +1 -2
  177. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/confirmation.d.ts +4 -1
  178. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/confirmation.js +2 -2
  179. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/contract.d.ts +8 -6
  180. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/contract.d.ts +10 -5
  181. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/finder.d.ts +2 -1
  182. package/node_modules/resume-from/dist/import/discovery/finder.js +269 -0
  183. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/contract.d.ts +3 -3
  184. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/lander.js +3 -3
  185. package/node_modules/resume-from/dist/import/pipeline.d.ts +28 -0
  186. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/pipeline.js +31 -11
  187. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/builder.d.ts +2 -2
  188. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/builder.js +9 -5
  189. package/node_modules/resume-from/dist/import/preview/warnings.d.ts +14 -0
  190. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/warnings.js +26 -20
  191. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/wiring.js +12 -2
  192. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/contract.d.ts +9 -3
  193. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/git.d.ts +4 -4
  194. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/git.js +7 -4
  195. package/node_modules/resume-from/dist/platform/repo/reader.d.ts +3 -0
  196. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/reader.js +60 -20
  197. package/node_modules/{@herbertgao/resume-from → resume-from}/dist/session/contract.d.ts +8 -1
  198. package/node_modules/{@herbertgao/resume-from → resume-from}/package.json +19 -32
  199. package/node_modules/{@herbertgao/resume-from → resume-from}/shims/pi/extensions/resume-from.js +2 -2
  200. package/package.json +21 -21
  201. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +0 -21
  202. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +0 -62
  203. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
  204. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +0 -115
  205. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +0 -27
  206. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +0 -27
  207. package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
  208. package/node_modules/@dietrichgebert/ponytail/assets/retriever-icon.png +0 -0
  209. package/node_modules/@dietrichgebert/ponytail/assets/retriever-logo-dark.svg +0 -4
  210. package/node_modules/@dietrichgebert/ponytail/assets/retriever-logo-light.svg +0 -4
  211. package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
  212. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
  213. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
  214. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
  215. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/read.js +0 -270
  216. package/node_modules/@herbertgao/resume-from/dist/import/discovery/finder.js +0 -129
  217. package/node_modules/@herbertgao/resume-from/dist/import/pipeline.d.ts +0 -17
  218. package/node_modules/@herbertgao/resume-from/dist/import/preview/warnings.d.ts +0 -11
  219. package/node_modules/@herbertgao/resume-from/dist/platform/repo/reader.d.ts +0 -8
  220. /package/node_modules/{@herbertgao/resume-from → resume-from}/LICENSE +0 -0
  221. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/contract.js +0 -0
  222. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/entries.d.ts +0 -0
  223. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/entries.js +0 -0
  224. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/index.d.ts +0 -0
  225. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/index.js +0 -0
  226. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/layout.d.ts +0 -0
  227. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/layout.js +0 -0
  228. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/readback.d.ts +0 -0
  229. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/readback.js +0 -0
  230. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/redaction.d.ts +0 -0
  231. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/validation.d.ts +0 -0
  232. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/validation.js +0 -0
  233. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/contract.js +0 -0
  234. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/index.d.ts +0 -0
  235. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/readback.d.ts +0 -0
  236. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/readback.js +0 -0
  237. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/redaction.d.ts +0 -0
  238. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/validation.d.ts +0 -0
  239. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/validation.js +0 -0
  240. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/contract.js +0 -0
  241. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/adapter.d.ts +0 -0
  242. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/contract.js +0 -0
  243. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/format.d.ts +0 -0
  244. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/format.js +0 -0
  245. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/index.d.ts +0 -0
  246. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/index.js +0 -0
  247. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/parse.d.ts +0 -0
  248. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/parse.js +0 -0
  249. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/redaction.d.ts +0 -0
  250. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/validate.d.ts +0 -0
  251. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/validate.js +0 -0
  252. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/bin.d.ts +0 -0
  253. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/bin.js +0 -0
  254. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/contract.d.ts +0 -0
  255. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/contract.js +0 -0
  256. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/agents.d.ts +0 -0
  257. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/agents.js +0 -0
  258. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/args.d.ts +0 -0
  259. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/args.js +0 -0
  260. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/contract.d.ts +0 -0
  261. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/contract.js +0 -0
  262. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/index.d.ts +0 -0
  263. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/index.js +0 -0
  264. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/presentation.d.ts +0 -0
  265. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/presentation.js +0 -0
  266. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/runner.d.ts +0 -0
  267. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/contract.js +0 -0
  268. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/entry.d.ts +0 -0
  269. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/entry.js +0 -0
  270. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/index.d.ts +0 -0
  271. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/index.js +0 -0
  272. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/command.d.ts +0 -0
  273. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/contract.d.ts +0 -0
  274. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/contract.js +0 -0
  275. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/index.d.ts +0 -0
  276. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/index.js +0 -0
  277. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/picker.d.ts +0 -0
  278. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/picker.js +0 -0
  279. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/presentation.d.ts +0 -0
  280. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/presentation.js +0 -0
  281. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/register.d.ts +0 -0
  282. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/register.js +0 -0
  283. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/ui.d.ts +0 -0
  284. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/ui.js +0 -0
  285. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/profile.d.ts +0 -0
  286. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/profile.js +0 -0
  287. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/registry.d.ts +0 -0
  288. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/registry.js +0 -0
  289. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/contract.js +0 -0
  290. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/contract.js +0 -0
  291. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/errors.d.ts +0 -0
  292. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/errors.js +0 -0
  293. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/homes.d.ts +0 -0
  294. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/homes.js +0 -0
  295. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/index.d.ts +0 -0
  296. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/index.js +0 -0
  297. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/ordering.d.ts +0 -0
  298. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/ordering.js +0 -0
  299. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/errors.d.ts +0 -0
  300. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/errors.js +0 -0
  301. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/index.d.ts +0 -0
  302. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/index.js +0 -0
  303. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/contract.js +0 -0
  304. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/errors.d.ts +0 -0
  305. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/errors.js +0 -0
  306. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/handover.d.ts +0 -0
  307. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/handover.js +0 -0
  308. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/index.d.ts +0 -0
  309. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/index.js +0 -0
  310. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/lander.d.ts +0 -0
  311. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/marker.d.ts +0 -0
  312. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/marker.js +0 -0
  313. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/contract.d.ts +0 -0
  314. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/contract.js +0 -0
  315. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/format.d.ts +0 -0
  316. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/format.js +0 -0
  317. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/index.d.ts +0 -0
  318. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/index.js +0 -0
  319. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/contract.d.ts +0 -0
  320. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/contract.js +0 -0
  321. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/index.d.ts +0 -0
  322. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/index.js +0 -0
  323. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/rules.d.ts +0 -0
  324. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/rules.js +0 -0
  325. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/wiring.d.ts +0 -0
  326. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/index.d.ts +0 -0
  327. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/index.js +0 -0
  328. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/contract.d.ts +0 -0
  329. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/contract.js +0 -0
  330. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/defaults.d.ts +0 -0
  331. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/defaults.js +0 -0
  332. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/errors.d.ts +0 -0
  333. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/errors.js +0 -0
  334. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/index.d.ts +0 -0
  335. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/index.js +0 -0
  336. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/loader.d.ts +0 -0
  337. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/loader.js +0 -0
  338. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/paths.d.ts +0 -0
  339. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/paths.js +0 -0
  340. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/validate.d.ts +0 -0
  341. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/validate.js +0 -0
  342. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/contract.js +0 -0
  343. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/index.d.ts +0 -0
  344. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/index.js +0 -0
  345. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/contract.d.ts +0 -0
  346. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/contract.js +0 -0
  347. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/file-committer.d.ts +0 -0
  348. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/file-committer.js +0 -0
  349. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/index.d.ts +0 -0
  350. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/index.js +0 -0
  351. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/contract.d.ts +0 -0
  352. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/contract.js +0 -0
  353. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/estimator.d.ts +0 -0
  354. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/estimator.js +0 -0
  355. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/index.d.ts +0 -0
  356. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/index.js +0 -0
  357. /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/session/contract.js +0 -0
@@ -7,11 +7,11 @@ Installation and configuration are complete only when Pi remains unmodified, the
7
7
  ## Rules
8
8
 
9
9
  - Do not modify, patch, fork, or vendor upstream Pi. SoL-Pi must load as a standalone extension through Pi's public package interface.
10
- - Use Node.js 24 or newer and the tested Pi release `@earendil-works/pi-coding-agent@0.85.1`. Treat a different Pi version as a compatibility change and rerun the full suite before using it.
10
+ - Use Node.js 24 or newer and the tested Pi release `@earendil-works/pi-coding-agent@1.0.4`. Treat a different Pi version as a compatibility change and rerun the full suite before using it.
11
11
  - Do not clean, reset, switch, or overwrite unrelated repository changes.
12
12
  - Do not print, log, commit, upload, or include any secret in a command line. Check only whether a credential is present.
13
13
  - Keep SoL-Pi settings in `sol-pi.json`. The Evidence-Preserving Reducer provider/model route is a SoL-Pi setting; provider URLs, credentials, the main agent model, and shell behavior remain Pi settings.
14
- - Keep persistent artifacts under Pi's session-derived `sol-pi/<session-id>/` root; do not configure a separate storage path.
14
+ - Keep persistent artifacts under Pi's session-derived `sol-pi/<session-id>/` root. For `--no-session` and in-memory sessions, use the automatic private directory under the system temporary directory; do not configure a separate storage path. Temporary archives remain after worker exit for callers to read, with cleanup managed by the host or caller.
15
15
 
16
16
  ## Inputs
17
17
 
@@ -35,30 +35,33 @@ node --version
35
35
  npm --version
36
36
  ```
37
37
 
38
- Require Node.js 24 or newer. Install from the lockfile and run the source checks:
38
+ Require Node.js 24 or newer. In this monorepo, run the dependency and full checks from the repository root, then return to `packages/sol-pi` for package validation:
39
39
 
40
40
  ```bash
41
- npm ci --ignore-scripts
42
- npm run check
43
- npm audit --audit-level=high
41
+ # Repository root
42
+ bun install --frozen-lockfile
43
+ bun run check
44
+ bun audit
45
+ # packages/sol-pi
44
46
  node scripts/check-pi-compat.mjs
45
- npx vitest run tests/all-mechanisms.test.ts
47
+ bun run test -- tests/all-mechanisms.test.ts
48
+ npm pack --dry-run
46
49
  ```
47
50
 
48
- `npm run check` covers type checking, the complete test suite, and package inspection. `tests/all-mechanisms.test.ts` confirms that one all-enabled configuration registers all four mechanisms against Pi's public extension API. The tests run without a model provider.
51
+ `bun run check` covers root formatting, linting, type checking, and the complete test suites; `npm pack --dry-run` inspects this package. `tests/all-mechanisms.test.ts` confirms that one all-enabled configuration registers all four mechanisms against Pi's public extension API. The tests run without a model provider.
49
52
 
50
- Stop if any command fails. Do not hide a failure with `|| true` or replace `npm ci` with an unlocked install.
53
+ Stop if any command fails. Do not hide a failure with `|| true` or replace the frozen-lockfile install with an unlocked install.
51
54
 
52
55
  ## Phase 2: install Pi and SoL-Pi
53
56
 
54
57
  Install the tested Pi release without changing its source:
55
58
 
56
59
  ```bash
57
- npm install --global --ignore-scripts @earendil-works/pi-coding-agent@0.85.1
60
+ npm install --global --ignore-scripts @earendil-works/pi-coding-agent@1.0.4
58
61
  pi --version
59
62
  ```
60
63
 
61
- Require `pi --version` to report `0.85.1`.
64
+ Require `pi --version` to report `1.0.4`. Runtime peer ranges remain `*`; other hosts need their own validation.
62
65
 
63
66
  For a project-local registration, run this from `target_project` and substitute the resolved absolute `sol_pi_root`:
64
67
 
@@ -124,7 +127,7 @@ Require exit status 0 and retain its JSON output. The check applies SoL-Pi's def
124
127
 
125
128
  1. Run `pi list --approve` from `target_project` and confirm the expected SoL-Pi source and scope.
126
129
  2. Run `check-sol-pi-config.mjs --require-all-enabled` against the effective `sol-pi.json`.
127
- 3. Re-run `npx vitest run tests/all-mechanisms.test.ts` from `sol_pi_root`.
130
+ 3. Re-run `bun run test -- tests/all-mechanisms.test.ts` from `sol_pi_root`.
128
131
  4. Start Pi with `--offline --approve`, send no prompt, confirm there is no extension load error, and exit.
129
132
  5. Confirm that upstream Pi was not patched and that the SoL-Pi checkout contains no vendored Pi monorepo source.
130
133
 
@@ -1,6 +1,6 @@
1
1
  # Pi Compatibility
2
2
 
3
- SoL-Pi is developed and tested against `@earendil-works/pi-coding-agent` 0.85.1. Its public API surface is also type-checked and exercised against Pi 0.81.1, the base used by the original Pi fork. The runtime range is deliberately expressed as a peer dependency because Pi owns installation and upgrade of its packages.
3
+ This monorepo tests `@herbertgao/sol-pi` against the root development host Pi 1.0.4. Pi runtime peers remain `*`, so Pi owns installation and upgrades; that range is not a guarantee for every host. Upstream reports a 19-file/140-test suite and offline startup passing on 0.85.1 and 0.84.2, with earlier public-API checks on the 0.81.1 fork. Those are historical upstream results, not results for the locally adapted package. Its tests use Pi 1.x `ExtensionToolContext` for tool execution.
4
4
 
5
5
  SoL-Pi imports only public package exports:
6
6
 
@@ -19,10 +19,16 @@ The built-in edit/write definitions capture their working directory, so SoL-Pi c
19
19
 
20
20
  Action Fusion decodes `file://` targets with Node's `fileURLToPath()` before resolving the queue and hash-check path. This keeps file URLs, including percent-encoded filenames and Pi's optional `@` prefix, aligned with the file handled by the built-in mutation tool.
21
21
 
22
+ On Windows it applies the same drive-path conversion Pi's own resolver applies, so Git Bash, MSYS, Cygwin, and WSL targets such as `/c/src/app.ts` and home-relative `~\` paths resolve to the file the built-in mutation tool wrote. On other platforms those inputs keep their POSIX meaning.
23
+
22
24
  The queue covers only fused operations registered by this SoL-Pi instance. External processes, direct built-in-tool calls outside the replacement, and unrelated extensions are not globally locked. SoL-Pi hashes the target immediately before launching `then_run` and skips the command if it observes an intervening content change.
23
25
 
24
26
  When another extension already owns `write`, Action Fusion leaves that tool in place and only registers its `edit` replacement.
25
27
 
28
+ A native local `write` exposes the runtime-only `Symbol.for("herbertgao.pi.writeMetadataObserver")` annotation. CC-style rendering can register bounded previous-file capture inside Pi's native `WriteOperations.writeFile` mutation queue, rather than racing a `tool_call` hook. Custom write operations reject registration because they may not target the local filesystem. Cancellation during capture prevents the actual write. This annotation adds no runtime dependency or global observer state.
29
+
30
+ Pi 1.x may return `Bash.isError` instead of throwing on a failed follow-up. Action Fusion reports that failure while retaining the successful mutation.
31
+
26
32
  ## ObservationPack
27
33
 
28
34
  ObservationPack changes only the messages projected through the public `context` event. Stored session history remains intact. Original bytes and the JSONL ledger live under the session-derived SoL-Pi directory.
@@ -31,9 +37,9 @@ ObservationPack changes only the messages projected through the public `context`
31
37
 
32
38
  The reducer handles public `tool_result` events and resolves the configured reducer provider/model through Pi's model registry before calling `ExtensionContext.modelRegistry.complete()` when available. For the Pi 0.81.1 fork, which exposes no registry `complete()` method, it resolves authentication for that reducer model through `getApiKeyAndHeaders()` and calls the shared `@earendil-works/pi-ai/compat` completion API. The reducer preserves the original result whenever the configured reducer model is unavailable or eligibility, model-call, schema, source-hash, exact-quote, size, or likely-secret checks fail.
33
39
 
34
- All persistent paths use `SessionManager.getSessionDir()` and `getSessionId()`, which are present in both the fork and Pi 0.85.1. SoL-Pi creates no configurable storage-path surface.
40
+ All persistent paths use `SessionManager.getSessionDir()` and `getSessionId()`, which are present in both the fork and Pi 0.85.1. If the session directory is empty (`--no-session` or `SessionManager.inMemory()`), SoL-Pi lazily creates a private `sol-pi-<session-id>-<random>/` directory under `os.tmpdir()`. Its path is reused by session ID across contexts and both archiving mechanisms while the extension is loaded. These files remain available after worker shutdown for callers that need to read evidence; cleanup is left to the host or caller. SoL-Pi creates no configurable storage-path surface.
35
41
 
36
- The unpublished shared artifact layout is not read or migrated. Each session starts from its own `<sessionDir>/sol-pi/<sessionId>/` directory.
42
+ The unpublished shared artifact layout is not read or migrated. Each persistent session starts from its own `<sessionDir>/sol-pi/<sessionId>/` directory; ephemeral sessions use the temporary fallback above.
37
43
 
38
44
  ## Online Context Compact
39
45
 
@@ -41,13 +47,19 @@ Online Context Compact uses ordinary public `context` and `before_provider_reque
41
47
 
42
48
  Before selecting or executing a boundary compaction, it also checks the optional `Symbol.for("pi-subagents:manager")` registry exposed by `pi-subagents`. A running top-level subagent, or an unreadable registry, suppresses that compaction rather than risking a context rewrite during delegated work. When the registry is absent, the check is a no-op.
43
49
 
44
- Pi does not expose its active retained-tail compaction setting through the public extension context. The standalone extension therefore uses the documented Pi 0.85.1 default of 20,000 tokens for its economic estimate. Its programmatic factory accepts an explicit matching value for a non-default Pi setting.
50
+ The native cut is selected from session entries, while removable-token savings are measured from the corresponding messages in the latest observed projection. ObservationPack placeholders are therefore priced at their projected size. Dropped messages, unseen additions, and ambiguous matches contribute no proven savings; the estimator never substitutes their raw byte counts or reruns context hooks to estimate them.
51
+
52
+ Compaction preserves the current plan, request-horizon samples, and context-growth history. Every plan update uses the same step-ID/status comparison, including the first update after compaction. A boundary requires a previously registered unfinished step to become completed. Newly introduced completed IDs establish history without creating a boundary, so re-keyed completed snapshots do not retrigger compaction. An assistant must register a step before completing it to make that completion eligible for OCC. Economic compaction has a two-provider-request cooldown; context-window protection can override it. A new user task following a fully completed plan resets the request-horizon samples while preserving unpaid cache debt.
45
53
 
46
- Pi 0.85.1's `ExtensionContext.compact()` aborts the active agent before it summarizes, and `agent_settled` fires only once a whole run has drained every turn, retry, auto-compaction, and queued continuation. A plan boundary that selects compaction therefore saves its plan and progress state, calls `ExtensionContext.abort()` to stop the run, and runs compaction from the `agent_settled` that stop produces. The handler awaits the compaction's own `onComplete`/`onError` callbacks. On success, the extension sends a hidden reminder through public `ExtensionAPI.sendMessage()` with `triggerTurn: true`, so Pi starts a new turn against the compacted context and rebuilds the plan even when the native summary omits that instruction.
54
+ Pi does not expose its active retained-tail compaction setting through the public extension context. The standalone extension therefore uses the Pi 0.85.1 default of 20,000 tokens for its economic estimate. Its programmatic factory accepts an explicit matching value for a non-default Pi setting.
47
55
 
48
- A settlement barrier keeps the original `agent_settled` dispatch open until the triggered continuation settles. Print- and JSON-mode processes therefore complete the compact-and-continue sequence within the same Pi invocation; an outer driver does not need to resume the session or send `Continue working`. This continuation is armed only by a successful boundary compaction. Cancelling or exiting does not schedule one. A Pi build that never emits `agent_settled` starts no boundary compaction.
56
+ Pi 0.85.1's `ExtensionContext.compact()` aborts the active agent before it summarizes, and `agent_settled` fires only once a whole run has drained every turn, retry, auto-compaction, and queued continuation. A plan boundary that selects compaction therefore saves its plan and progress state, calls `ExtensionContext.abort()` to stop the run, and runs compaction from the `agent_settled` that stop produces. The handler awaits the compaction's own `onComplete`/`onError` callbacks. On success, the extension sends a hidden reminder through public `ExtensionAPI.sendMessage()` with `triggerTurn: true`, so Pi starts a new turn against the compacted context and continues the current plan. The reminder asks it to preserve existing step IDs when updating progress; an immediate plan update is not required.
49
57
 
50
- Pi 0.85.1 does not return a promise from `ExtensionAPI.sendMessage()`. The barrier is therefore verified for standalone SoL-Pi and depends on Pi starting the requested turn synchronously. A later-loaded third-party extension that performs long asynchronous work in its own `agent_settled` handler is outside this guarantee and needs an integration test with that extension set.
58
+ A settlement barrier keeps the original `agent_settled` dispatch open until a synchronously triggered continuation settles. Print- and JSON-mode processes therefore complete the compact-and-continue sequence within the same Pi invocation; an outer driver does not need to resume the session or send `Continue working`. Successful compaction schedules a continuation. The exact native refusals `Nothing to compact (session too small)` and `Already compacted`, and Pi's explicit incomplete-summary token-cap error, also resume the interrupted task with the existing context. These skips are recorded as `sol-pi-online-context-compact-skipped` entries and shown as TUI warnings; they do not record a compaction or charge new cache debt. Plan-only turns cannot retry compaction until successful non-plan tool work or new user input occurs. Cancelling or exiting does not schedule a continuation. Other compaction errors still propagate.
59
+
60
+ Pi 0.85.1 does not return a promise from `ExtensionAPI.sendMessage()` and starts the requested turn synchronously, so it uses the extension's settlement barrier. Newer hosts can keep the context idle and defer the turn until settled handlers return; OCC returns control to such a host instead of waiting on the deferred turn or reporting a false start failure. A later-loaded third-party extension that performs long asynchronous work in its own `agent_settled` handler needs an integration test with that extension set.
61
+
62
+ For reasoning models, native compaction temporarily lowers high/xhigh/max thinking to medium to leave output budget for summary text, then restores it if no one else changed it.
51
63
 
52
64
  Pi reports the session as idle while an extension-requested manual compaction is running. SoL-Pi cancels `session_before_tree` during that interval to prevent tree navigation from moving the active leaf underneath the compaction. Navigation works normally after the compaction callback settles.
53
65
 
@@ -66,4 +78,8 @@ events, print output, or RPC UI requests.
66
78
 
67
79
  ## Test doubles
68
80
 
69
- The test suite drives every extension through the same public `ExtensionAPI` and `ExtensionContext` surface Pi provides, over a real public `SessionManager`, with no provider registered. That keeps the suite zero-spend and independent of the deleted Pi monorepo test harness. Suites that need a genuine session tree — branch order, compaction entries, custom entries, resume — use `SessionManager.inMemory()` or `SessionManager.create()` rather than reimplementing them.
81
+ The test suite drives every extension through the same public `ExtensionAPI` and `ExtensionContext` surface Pi provides, over a real public `SessionManager`, without calling a remote model provider. That keeps the suite zero-spend and independent of the deleted Pi monorepo test harness. Suites that need a genuine session tree — branch order, compaction entries, custom entries, resume — use `SessionManager.inMemory()` or `SessionManager.create()` rather than reimplementing them.
82
+
83
+ `tests/pi-package-integration.test.ts` loads the actual TypeScript entrypoint through Pi's `DefaultResourceLoader`, reads a trusted all-enabled project configuration, and executes a fused write/command and a plan update in a real `AgentSession`. `tests/online-context-compact-agent-session.test.ts` verifies one and two consecutive native compactions and waits for automatic continuation before the original prompt returns. These integration tests use Pi's deterministic faux provider; they verify runtime compatibility, not live provider authentication or token savings.
84
+
85
+ Upstream's historical 0.84.2 backward-compatibility run used an isolated copy of its source and tests, separate dependencies, and an empty Pi agent directory. Only the copy's four Pi development dependency versions, lockfile, and installation-guide version mentions changed. No source or test changes were needed. The run included all four mechanisms and the native compaction/continuation integration tests; it did not repeat live-provider benchmarks on 0.84.2.
@@ -41,7 +41,7 @@ This preflight does not make every valid SoL-Pi configuration all-enabled. Witho
41
41
 
42
42
  ## Feature behavior
43
43
 
44
- - `actionFusion`: registers SoL-Pi replacements for Pi's `edit` and `write` tools.
44
+ - `actionFusion`: registers SoL-Pi replacements for Pi's `edit` and `write` tools. With `@herbertgao/pi-cc-extensions` installed, local writes also provide the execution metadata needed for ccstyle's rich diff without replacing the fused tool. Custom write operations still defer to their owner because a local file snapshot may not describe a remote write.
45
45
  - `observationPack`: registers `obs_recall` and a provider-context projection handler.
46
46
  - `evidencePreservingReducer`: registers a `tool_result` handler and delegates long diagnostic-log reduction to the configured reducer provider/model.
47
47
  - `evidencePreservingReducerProvider`: provider namespace used to resolve the reducer model through Pi's model registry.
@@ -62,7 +62,7 @@ The release entry uses two runtime inputs:
62
62
  - **Context window** — from `ExtensionContext.getContextUsage()`, used for window-pressure protection.
63
63
  - **Cache write/read ratio** — from `cacheWriteReadRatio` in the effective `sol-pi.json`. The value remains fixed for the session and is not recomputed when the model changes. It drives one runtime decision and is not a cost report.
64
64
 
65
- The configured ratio stays fixed for the loaded extension. The mechanism stores its current plan, progress summaries, request horizon, context growth, and compaction debt as versioned custom entries in Pi's session log. After a successful compaction it sends one hidden, generic message with `triggerTurn: true`, which starts a new turn and instructs the assistant to rebuild its plan. A settlement barrier keeps print and JSON modes in the same Pi invocation until that continuation settles, so callers do not need to resume the session or inject `Continue working`. Cancelling or exiting does not schedule an automatic continuation. The mechanism creates no separate Online Context Compact files. The programmatic factory exposes only a matching retained-tail value for installations whose Pi compaction setting differs from the default.
65
+ The configured ratio stays fixed for the loaded extension. The mechanism stores its current plan, progress summaries, request horizon, context growth, and compaction debt as versioned custom entries in Pi's session log. After a successful compaction it sends one hidden, generic message with `triggerTurn: true`, which starts a new turn and asks the assistant to continue the current plan, preserving existing step IDs when updating progress. A settlement barrier keeps print and JSON modes in the same Pi invocation until that continuation settles, so callers do not need to resume the session or inject `Continue working`. On Pi 1.x, OCC returns to the host to let its deferred continuation run after settled handlers finish. Recoverable native refusals (session too small, already compacted, or an incomplete summary at the token cap) resume with the unchanged context and no new cache debt; successful non-plan tool work or new user input is required before another plan-only attempt. Economic compaction has a two-provider-request cooldown, but window protection overrides it. Cancelling or exiting does not schedule an automatic continuation. The mechanism creates no separate Online Context Compact files. The programmatic factory exposes only a matching retained-tail value for installations whose Pi compaction setting differs from the default.
66
66
 
67
67
  ## Pi integration
68
68
 
@@ -0,0 +1,18 @@
1
+ # Star history chart
2
+
3
+ The README chart is generated by [Update star history](https://github.com/NVlabs/SoL-Pi/actions/workflows/star-history.yml). It runs hourly at minute 17, when the generator or workflow changes on `main`, and on manual dispatch. GitHub may delay scheduled runs; the image shows the actual successful fetch time in UTC.
4
+
5
+ The workflow uses its built-in `GITHUB_TOKEN`, Node.js, and the [GitHub aggregate star history and count endpoints](https://docs.github.com/en/rest/activity/starring). It fetches every history page and renders locally with the original [Star History renderer](https://github.com/star-history/star-history/tree/c326eac651bc5afb4cd40d354223dd419e1e2ae6), pinned to a specific commit and installed from its lockfiles. No additional secrets or cached third-party chart API are used.
6
+
7
+ The chart retains Star History's original layout, typography, legend, and axes, with only an updated timestamp added below it. The line sums GitHub's daily star additions. The current total is retained in the source JSON and can differ from that sum because it excludes removed stars. Daily boundaries follow GitHub's reported week timestamps and are not guaranteed to align with UTC. No stargazer identities are collected.
8
+
9
+ The light SVG, dark SVG, source JSON, and renderer's MIT license are committed to the dedicated `star-history` branch. The README loads the SVGs from `raw.githubusercontent.com`; GitHub's image cache may add a short delay. The workflow does not change `main` or the website's `gh-pages` branch. Fetching, validation, and rendering finish before publication, so a failed run leaves the previous published chart available.
10
+
11
+ To refresh manually, open the workflow, select **Run workflow**, and run it on `main`. For a local preview, check out the pinned Star History revision separately, install its root and backend dependencies using pnpm 9.15.9 with `--frozen-lockfile --ignore-scripts`, then run:
12
+
13
+ ```bash
14
+ export STAR_HISTORY_RENDERER=/absolute/path/to/star-history-checkout
15
+ "$STAR_HISTORY_RENDERER/backend/node_modules/.bin/tsx" scripts/update-star-history.mjs NVlabs/SoL-Pi /absolute/path/to/star-history-preview
16
+ ```
17
+
18
+ For a higher API rate limit, supply `GITHUB_TOKEN` through the environment. Never put the token in a URL or command argument. Run generator tests with `npx vitest run tests/star-history.test.js`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/sol-pi",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "Token-efficient context and tool extensions for Pi, maintained from NVlabs/SoL-Pi.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -27,11 +27,13 @@
27
27
  "assets",
28
28
  "docs/compatibility.md",
29
29
  "docs/configuration.md",
30
+ "docs/star-history.md",
30
31
  "scripts/check-pi-compat.mjs",
31
32
  "scripts/check-sol-pi-config.mjs",
32
33
  "agents-install.md",
33
34
  "README.md",
34
35
  "LICENSE",
36
+ "CONTRIBUTING.md",
35
37
  "SECURITY.md",
36
38
  "THIRD_PARTY_NOTICES.md",
37
39
  "sol-pi.example.json"
@@ -71,6 +73,6 @@
71
73
  "version": "0.1.0",
72
74
  "reviewedVersion": "0.1.0",
73
75
  "repository": "https://github.com/NVlabs/SoL-Pi",
74
- "commit": "22277b7e0c3c46ba1259a6687f31fe39ade421a5"
76
+ "commit": "e1a586af0ad8956f42ae5b26bba20e48fbf30e00"
75
77
  }
76
78
  }
@@ -106,7 +106,7 @@ function validateConfig(value, requireAllEnabled) {
106
106
  function stringConfigValue(value, key, defaultValue) {
107
107
  const configured = Object.hasOwn(value, key) ? value[key] : defaultValue;
108
108
  if (typeof configured !== "string" || configured.trim().length === 0) fail(`${key} must be a non-empty string`);
109
- return configured;
109
+ return configured.trim();
110
110
  }
111
111
 
112
112
  try {
@@ -131,5 +131,5 @@ function stringConfigValue(
131
131
  if (typeof value !== "string" || value.trim().length === 0) {
132
132
  throw new Error(`SoL-Pi config ${key} must be a non-empty string: ${path}`);
133
133
  }
134
- return value;
134
+ return value.trim();
135
135
  }
@@ -9,17 +9,36 @@ import { basename, dirname, resolve } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
10
 
11
11
  const queueTails = new Map<string, Promise<void>>();
12
+ const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/gu;
13
+ const WINDOWS_SHELL_DRIVE = /^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/i;
12
14
 
13
- function stripToolPathPrefix(filePath: string): string {
14
- return filePath.startsWith("@") ? filePath.slice(1) : filePath;
15
+ function normalizeToolPath(filePath: string): string {
16
+ const normalized = filePath.replace(UNICODE_SPACES, " ");
17
+ return normalized.startsWith("@") ? normalized.slice(1) : normalized;
18
+ }
19
+
20
+ /**
21
+ * Git Bash, MSYS, Cygwin, and WSL hand Pi paths like `/c/src/app.ts`. On
22
+ * Windows, Pi's built-in mutation tools convert those to a native drive path
23
+ * before touching the filesystem, so the queue and hash guard must convert them
24
+ * the same way or they address a file the mutation never wrote.
25
+ */
26
+ export function normalizeWindowsShellPath(filePath: string): string {
27
+ if (process.platform !== "win32") return filePath;
28
+ if (!filePath.startsWith("/") || filePath.startsWith("//") || filePath.includes("\\")) return filePath;
29
+ const match = WINDOWS_SHELL_DRIVE.exec(filePath);
30
+ if (!match?.[1]) return filePath;
31
+ return `${match[1].toUpperCase()}:\\${match[2]?.replaceAll("/", "\\") ?? ""}`;
15
32
  }
16
33
 
17
34
  export function resolveToolPath(cwd: string, filePath: string): string {
18
- const stripped = stripToolPathPrefix(filePath);
35
+ const stripped = normalizeWindowsShellPath(normalizeToolPath(filePath));
19
36
  // Pi accepts file URLs; the queue and hash guard must use the same target.
20
37
  const expanded = stripped.startsWith("file://") ? fileURLToPath(stripped) : stripped;
21
38
  if (expanded === "~") return homedir();
22
- if (expanded.startsWith("~/")) return resolve(homedir(), expanded.slice(2));
39
+ if (expanded.startsWith("~/") || (process.platform === "win32" && expanded.startsWith("~\\"))) {
40
+ return resolve(homedir(), expanded.slice(2));
41
+ }
23
42
  return resolve(cwd, expanded);
24
43
  }
25
44
 
@@ -17,6 +17,7 @@
17
17
  * This standalone version composes only Pi's public tool definitions.
18
18
  */
19
19
 
20
+ import { mkdir, writeFile } from "node:fs/promises";
20
21
  import {
21
22
  type BashToolOptions,
22
23
  createEditToolDefinition,
@@ -42,6 +43,10 @@ const EDIT_THEN_RUN_DESCRIPTION =
42
43
  const WRITE_THEN_RUN_DESCRIPTION =
43
44
  "Command to run next on this file after the write succeeds — e.g. run, build, start/restart, install, or check it; optional timeout in seconds. Skipped if the write fails; a non-zero exit is reported but keeps the write.";
44
45
 
46
+ // Runtime-only annotation: Pi preserves symbol keys in getAllTools(), but JSON omits them.
47
+ const WRITE_METADATA_OBSERVER = Symbol.for("herbertgao.pi.writeMetadataObserver");
48
+ type WriteMetadataObserver = (toolCallId: string, path: string, write: () => Promise<void>) => Promise<void>;
49
+
45
50
  export interface ActionFusionOptions {
46
51
  /** Optional programmatic bash overrides, primarily for tests and embedded runtimes. */
47
52
  readonly bashOptions?: BashToolOptions;
@@ -82,6 +87,7 @@ export function createActionFusionExtension(options: ActionFusionOptions = {}):
82
87
  return (pi: ExtensionAPI) => {
83
88
  const editTemplate = baseEdit(process.cwd());
84
89
  const writeTemplate = baseWrite(process.cwd());
90
+ let observeWrite: WriteMetadataObserver | undefined;
85
91
 
86
92
  const editParameters = Type.Object({
87
93
  ...editTemplate.parameters.properties,
@@ -131,6 +137,14 @@ export function createActionFusionExtension(options: ActionFusionOptions = {}):
131
137
  pi.registerTool<typeof writeParameters, undefined>({
132
138
  ...writeTemplate,
133
139
  parameters: writeParameters,
140
+ annotations: Object.assign({}, writeTemplate.annotations, {
141
+ [WRITE_METADATA_OBSERVER]: (observer: WriteMetadataObserver) => {
142
+ // Custom operations may write remotely; local snapshots would be misleading.
143
+ if (options.writeOptions?.operations) return false;
144
+ observeWrite = observer;
145
+ return true;
146
+ },
147
+ }),
134
148
  async execute(toolCallId, input, signal, onUpdate, ctx) {
135
149
  const { then_run, ...writeInput } = input as typeof input & { then_run?: ThenRunInput };
136
150
  const result = await executeMutationThenRun({
@@ -140,7 +154,24 @@ export function createActionFusionExtension(options: ActionFusionOptions = {}):
140
154
  bashOptions: options.bashOptions,
141
155
  signal,
142
156
  ctx,
143
- mutate: () => baseWrite(ctx.cwd).execute(toolCallId, writeInput, signal, onUpdate, ctx),
157
+ mutate: () => {
158
+ const observer = observeWrite;
159
+ const write = observer
160
+ ? createWriteToolDefinition(ctx.cwd, {
161
+ operations: {
162
+ mkdir: async (dir) => {
163
+ await mkdir(dir, { recursive: true });
164
+ },
165
+ // Pi invokes this inside its mutation queue, so the snapshot is atomic with the write.
166
+ writeFile: (path, content) => observer(toolCallId, path, () => {
167
+ if (signal?.aborted) throw new Error("Operation aborted");
168
+ return writeFile(path, content, "utf8");
169
+ }),
170
+ },
171
+ })
172
+ : baseWrite(ctx.cwd);
173
+ return write.execute(toolCallId, writeInput, signal, onUpdate, ctx);
174
+ },
144
175
  });
145
176
  if (
146
177
  then_run &&
@@ -5,7 +5,7 @@
5
5
  import { createHash } from "node:crypto";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
8
- import { type BashToolOptions, createBashToolDefinition, type ExtensionContext } from "@earendil-works/pi-coding-agent";
8
+ import { type BashToolOptions, createBashToolDefinition, type ExtensionToolContext } from "@earendil-works/pi-coding-agent";
9
9
  import { Type } from "typebox";
10
10
  import { withFusedFileQueue } from "./file-queue.ts";
11
11
 
@@ -90,7 +90,7 @@ export async function executeMutationThenRun<TDetails>({
90
90
  mutate: () => Promise<AgentToolResult<TDetails>>;
91
91
  bashOptions: BashToolOptions | undefined;
92
92
  signal: AbortSignal | undefined;
93
- ctx: ExtensionContext;
93
+ ctx: ExtensionToolContext;
94
94
  }): Promise<AgentToolResult<TDetails>> {
95
95
  return withFusedFileQueue(absolutePath, async () => {
96
96
  let mutationResult: AgentToolResult<TDetails>;
@@ -113,6 +113,7 @@ export async function executeMutationThenRun<TDetails>({
113
113
  // pi-lens-ignore: sql-injection
114
114
  const bashResult = await bash.execute(`${toolCallId}:then_run`, thenRun, signal, undefined, ctx);
115
115
  const output = resultText(bashResult);
116
+ if (bashResult.isError) throw new Error(output);
116
117
  return {
117
118
  ...mutationResult,
118
119
  content: [
@@ -100,7 +100,7 @@ export function createObservation(message: ToolResultMessage, runtimeRoot: strin
100
100
  if (containsReducerReceipt(text)) return undefined;
101
101
  const bytes = Buffer.byteLength(text, "utf8");
102
102
  if (bytes <= THRESHOLD_BYTES) return undefined;
103
- if (!runtimeRoot) throw new Error("Persistent SoL-Pi runtime directory is unavailable");
103
+ if (!runtimeRoot) throw new Error("SoL-Pi runtime directory is unavailable");
104
104
 
105
105
  const contentHash = hash(text);
106
106
  const id = `obs_${hash(`${message.toolName}\0${message.toolCallId}\0${contentHash}`).slice(0, 24)}`;
@@ -9,6 +9,8 @@ export type CompactionEconomics = {
9
9
  readonly windowReserveTokens: number;
10
10
  readonly firstCompactionRequestScale: number;
11
11
  readonly subsequentCompactionMargin: number;
12
+ /** Minimum provider requests since the last compaction before economics may compact again. */
13
+ readonly minimumRequestsSinceCompaction: number;
12
14
  };
13
15
 
14
16
  export const DEFAULT_COMPACTION_ECONOMICS: CompactionEconomics = Object.freeze({
@@ -17,6 +19,7 @@ export const DEFAULT_COMPACTION_ECONOMICS: CompactionEconomics = Object.freeze({
17
19
  windowReserveTokens: 16_384,
18
20
  firstCompactionRequestScale: 2,
19
21
  subsequentCompactionMargin: 1.5,
22
+ minimumRequestsSinceCompaction: 2,
20
23
  });
21
24
 
22
25
  export type CompactionReason =
@@ -25,6 +28,7 @@ export type CompactionReason =
25
28
  | "deferred_economic"
26
29
  | "deferred_subsequent_margin"
27
30
  | "deferred_carried_debt"
31
+ | "deferred_post_compaction_cooldown"
28
32
  | "horizon_unavailable"
29
33
  | "cache_ratio_unavailable"
30
34
  | "native_not_compactable"
@@ -44,6 +48,8 @@ export type CompactionDecision = {
44
48
  readonly writeTokens: number;
45
49
  readonly archiveTokens: number;
46
50
  readonly memoTokens: number;
51
+ /** Estimated context written after compaction, including the replacement memo. */
52
+ readonly postCompactionTokens: number;
47
53
  readonly contextTokens: number;
48
54
  readonly completedBoundaryRequestCounts: readonly number[] | null;
49
55
  readonly requestsPerBoundaryMean: number | null;
@@ -58,6 +64,7 @@ export type CompactionDecision = {
58
64
  readonly cacheWriteReadRatio: number | null;
59
65
  readonly incrementalCacheCostRatio: number | null;
60
66
  readonly priorCompactionCount: number;
67
+ readonly requestsSinceLastCompaction: number | null;
61
68
  readonly carriedDebtTokens: number;
62
69
  readonly cacheDebtRepaymentTokens: number;
63
70
  readonly compact: boolean;
@@ -129,6 +136,8 @@ export function decideCompaction(input: {
129
136
  readonly averageContextTokenIncrement: number | null;
130
137
  readonly contextWindowTokens: number | null;
131
138
  readonly priorCompactionCount: number;
139
+ /** Provider requests since the last compaction; null when never compacted. */
140
+ readonly requestsSinceLastCompaction?: number | null;
132
141
  readonly carriedDebtTokens: number;
133
142
  readonly cacheDebtRepaymentTokens: number;
134
143
  readonly cacheWriteReadRatio: number | null;
@@ -147,15 +156,16 @@ export function decideCompaction(input: {
147
156
  averageContextTokenIncrement: input.averageContextTokenIncrement,
148
157
  });
149
158
  const savingTokens = input.archiveTokens - input.memoTokens;
159
+ const postCompactionTokens = Math.max(0, input.writeTokens - savingTokens);
150
160
  const incrementalCacheCostRatio =
151
161
  input.cacheWriteReadRatio === null ? null : Math.max(0, input.cacheWriteReadRatio - 1);
162
+ const newDebtTokens = postCompactionTokens * (incrementalCacheCostRatio ?? 0);
152
163
  const breakevenRequests =
153
- savingTokens > 0 && incrementalCacheCostRatio !== null
154
- ? (input.writeTokens * incrementalCacheCostRatio) / savingTokens
155
- : null;
164
+ savingTokens > 0 && incrementalCacheCostRatio !== null ? newDebtTokens / savingTokens : null;
165
+ const combinedRepaymentTokens = input.cacheDebtRepaymentTokens + savingTokens;
156
166
  const combinedBreakevenRequests =
157
- savingTokens > 0 && incrementalCacheCostRatio !== null
158
- ? (input.carriedDebtTokens + input.writeTokens * incrementalCacheCostRatio) / savingTokens
167
+ savingTokens > 0 && incrementalCacheCostRatio !== null && combinedRepaymentTokens > 0
168
+ ? (input.carriedDebtTokens + newDebtTokens) / combinedRepaymentTokens
159
169
  : null;
160
170
  const firstCompaction = input.priorCompactionCount === 0;
161
171
  const effectiveHorizonRequests =
@@ -193,12 +203,19 @@ export function decideCompaction(input: {
193
203
  combinedBreakevenRequests <= horizon.expectedRemainingRequests;
194
204
  const economic = firstCompaction ? firstEconomic : baseEconomic && subsequentMarginOpen && carriedDebtGateOpen;
195
205
  const compressible = savingTokens > 0;
196
- const compact = compressible && (windowProtection || economic);
206
+ const cooldownActive =
207
+ input.requestsSinceLastCompaction !== null &&
208
+ input.requestsSinceLastCompaction !== undefined &&
209
+ input.requestsSinceLastCompaction < input.economics.minimumRequestsSinceCompaction;
210
+ // Window protection stays absolute: near the context limit, compact even in cooldown.
211
+ const economicAllowed = economic && !cooldownActive;
212
+ const compact = compressible && (windowProtection || economicAllowed);
197
213
 
198
214
  return {
199
215
  writeTokens: input.writeTokens,
200
216
  archiveTokens: input.archiveTokens,
201
217
  memoTokens: input.memoTokens,
218
+ postCompactionTokens,
202
219
  contextTokens: input.contextTokens,
203
220
  ...(horizon ?? {
204
221
  completedBoundaryRequestCounts: null,
@@ -215,13 +232,16 @@ export function decideCompaction(input: {
215
232
  cacheWriteReadRatio: input.cacheWriteReadRatio,
216
233
  incrementalCacheCostRatio,
217
234
  priorCompactionCount: input.priorCompactionCount,
235
+ requestsSinceLastCompaction: input.requestsSinceLastCompaction ?? null,
218
236
  carriedDebtTokens: input.carriedDebtTokens,
219
237
  cacheDebtRepaymentTokens: input.cacheDebtRepaymentTokens,
220
238
  compact,
221
239
  reason: compressible
222
240
  ? windowProtection
223
241
  ? "window_protection"
224
- : economic
242
+ : economic && cooldownActive
243
+ ? "deferred_post_compaction_cooldown"
244
+ : economic
225
245
  ? "economic"
226
246
  : horizon === null
227
247
  ? "horizon_unavailable"