peaks-loop 4.1.3 → 4.1.4

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 (402) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +56 -0
  3. package/README-en.md +1 -1
  4. package/README.md +1 -1
  5. package/dist/cli/commands/asset-command-shared.d.ts +12 -0
  6. package/dist/cli/commands/asset-command-shared.js +45 -0
  7. package/dist/cli/commands/asset-commands.d.ts +6 -1
  8. package/dist/cli/commands/asset-commands.js +11 -372
  9. package/dist/cli/commands/asset-crystallize-command.d.ts +3 -0
  10. package/dist/cli/commands/asset-crystallize-command.js +60 -0
  11. package/dist/cli/commands/asset-crystallize-payload.d.ts +47 -0
  12. package/dist/cli/commands/asset-crystallize-payload.js +85 -0
  13. package/dist/cli/commands/asset-crystallize-runner.d.ts +3 -0
  14. package/dist/cli/commands/asset-crystallize-runner.js +133 -0
  15. package/dist/cli/commands/asset-dispose-command.d.ts +3 -0
  16. package/dist/cli/commands/asset-dispose-command.js +99 -0
  17. package/dist/cli/commands/asset-status-command.d.ts +3 -0
  18. package/dist/cli/commands/asset-status-command.js +100 -0
  19. package/dist/cli/commands/baseline-audit-command.d.ts +3 -0
  20. package/dist/cli/commands/baseline-audit-command.js +64 -0
  21. package/dist/cli/commands/baseline-command-shared.d.ts +23 -0
  22. package/dist/cli/commands/baseline-command-shared.js +39 -0
  23. package/dist/cli/commands/baseline-commands.js +33 -313
  24. package/dist/cli/commands/baseline-freeze-commands.d.ts +4 -0
  25. package/dist/cli/commands/baseline-freeze-commands.js +81 -0
  26. package/dist/cli/commands/baseline-guard-command.d.ts +3 -0
  27. package/dist/cli/commands/baseline-guard-command.js +52 -0
  28. package/dist/cli/commands/baseline-read-commands.d.ts +5 -0
  29. package/dist/cli/commands/baseline-read-commands.js +72 -0
  30. package/dist/cli/commands/baseline-rollback-commands.d.ts +4 -0
  31. package/dist/cli/commands/baseline-rollback-commands.js +88 -0
  32. package/dist/cli/commands/classify-downgrade-command.d.ts +9 -0
  33. package/dist/cli/commands/classify-downgrade-command.js +21 -0
  34. package/dist/cli/commands/classify-run-command.d.ts +9 -0
  35. package/dist/cli/commands/classify-run-command.js +73 -0
  36. package/dist/cli/commands/classify-signals.d.ts +5 -0
  37. package/dist/cli/commands/classify-signals.js +104 -0
  38. package/dist/cli/commands/code-emit-handoff-command.js +40 -3
  39. package/dist/cli/commands/code-gate-step-08-command.js +25 -9
  40. package/dist/cli/commands/codegraph-command-options.d.ts +22 -0
  41. package/dist/cli/commands/codegraph-command-options.js +22 -0
  42. package/dist/cli/commands/codegraph-commands.d.ts +2 -2
  43. package/dist/cli/commands/codegraph-commands.js +14 -397
  44. package/dist/cli/commands/codegraph-config-restore-command.d.ts +3 -0
  45. package/dist/cli/commands/codegraph-config-restore-command.js +200 -0
  46. package/dist/cli/commands/codegraph-proxy-commands.d.ts +10 -0
  47. package/dist/cli/commands/codegraph-proxy-commands.js +159 -0
  48. package/dist/cli/commands/codegraph-repair-commands.d.ts +3 -0
  49. package/dist/cli/commands/codegraph-repair-commands.js +94 -0
  50. package/dist/cli/commands/codegraph-status-command.d.ts +1 -1
  51. package/dist/cli/commands/codegraph-status-command.js +27 -214
  52. package/dist/cli/commands/codegraph-status-integrity.d.ts +44 -0
  53. package/dist/cli/commands/codegraph-status-integrity.js +121 -0
  54. package/dist/cli/commands/codegraph-status-json.d.ts +5 -0
  55. package/dist/cli/commands/codegraph-status-json.js +127 -0
  56. package/dist/cli/commands/container-command-shared.d.ts +60 -0
  57. package/dist/cli/commands/container-command-shared.js +52 -0
  58. package/dist/cli/commands/container-commands.d.ts +1 -1
  59. package/dist/cli/commands/container-commands.js +4 -216
  60. package/dist/cli/commands/container-release-command.d.ts +3 -0
  61. package/dist/cli/commands/container-release-command.js +105 -0
  62. package/dist/cli/commands/container-spawn-command.d.ts +3 -0
  63. package/dist/cli/commands/container-spawn-command.js +148 -0
  64. package/dist/cli/commands/context-check-command.d.ts +3 -0
  65. package/dist/cli/commands/context-check-command.js +81 -0
  66. package/dist/cli/commands/context-commands.d.ts +2 -7
  67. package/dist/cli/commands/context-commands.js +6 -276
  68. package/dist/cli/commands/context-layer-command.d.ts +3 -0
  69. package/dist/cli/commands/context-layer-command.js +34 -0
  70. package/dist/cli/commands/context-layer-fetchers.d.ts +15 -0
  71. package/dist/cli/commands/context-layer-fetchers.js +162 -0
  72. package/dist/cli/commands/context-status-command.d.ts +17 -0
  73. package/dist/cli/commands/context-status-command.js +41 -0
  74. package/dist/cli/commands/contract-write-command.d.ts +3 -0
  75. package/dist/cli/commands/contract-write-command.js +144 -0
  76. package/dist/cli/commands/core/doctor-bindings.d.ts +17 -0
  77. package/dist/cli/commands/core/doctor-bindings.js +30 -0
  78. package/dist/cli/commands/core/doctor-command.d.ts +4 -18
  79. package/dist/cli/commands/core/doctor-command.js +2 -323
  80. package/dist/cli/commands/core/doctor-logs.d.ts +8 -0
  81. package/dist/cli/commands/core/doctor-logs.js +32 -0
  82. package/dist/cli/commands/core/doctor-probes.d.ts +3 -0
  83. package/dist/cli/commands/core/doctor-probes.js +55 -0
  84. package/dist/cli/commands/core/doctor-run-command.d.ts +12 -0
  85. package/dist/cli/commands/core/doctor-run-command.js +172 -0
  86. package/dist/cli/commands/core/doctor-summary.d.ts +7 -0
  87. package/dist/cli/commands/core/doctor-summary.js +39 -0
  88. package/dist/cli/commands/core/memory-command-shared.d.ts +25 -0
  89. package/dist/cli/commands/core/memory-command-shared.js +20 -0
  90. package/dist/cli/commands/core/memory-command.d.ts +1 -1
  91. package/dist/cli/commands/core/memory-command.js +6 -227
  92. package/dist/cli/commands/core/memory-delegated-verbs.d.ts +4 -0
  93. package/dist/cli/commands/core/memory-delegated-verbs.js +178 -0
  94. package/dist/cli/commands/core/memory-extract-command.d.ts +3 -0
  95. package/dist/cli/commands/core/memory-extract-command.js +90 -0
  96. package/dist/cli/commands/core/memory-sync-command.d.ts +3 -0
  97. package/dist/cli/commands/core/memory-sync-command.js +38 -0
  98. package/dist/cli/commands/core/session-command.d.ts +1 -1
  99. package/dist/cli/commands/core/session-command.js +12 -206
  100. package/dist/cli/commands/core/session-info-command.d.ts +3 -0
  101. package/dist/cli/commands/core/session-info-command.js +142 -0
  102. package/dist/cli/commands/core/session-verb-commands.d.ts +5 -0
  103. package/dist/cli/commands/core/session-verb-commands.js +58 -0
  104. package/dist/cli/commands/core/standards-command.d.ts +1 -24
  105. package/dist/cli/commands/core/standards-command.js +33 -201
  106. package/dist/cli/commands/core/standards-init-command.d.ts +3 -0
  107. package/dist/cli/commands/core/standards-init-command.js +87 -0
  108. package/dist/cli/commands/core/standards-lint.d.ts +24 -0
  109. package/dist/cli/commands/core/standards-lint.js +57 -0
  110. package/dist/cli/commands/core/standards-migrate-command.d.ts +3 -0
  111. package/dist/cli/commands/core/standards-migrate-command.js +57 -0
  112. package/dist/cli/commands/cron-scheduler-commands.d.ts +5 -1
  113. package/dist/cli/commands/cron-scheduler-commands.js +36 -206
  114. package/dist/cli/commands/cron-scheduler-run-once-command.d.ts +7 -0
  115. package/dist/cli/commands/cron-scheduler-run-once-command.js +35 -0
  116. package/dist/cli/commands/cron-scheduler-shared.d.ts +6 -0
  117. package/dist/cli/commands/cron-scheduler-shared.js +48 -0
  118. package/dist/cli/commands/cron-scheduler-start-command.d.ts +7 -0
  119. package/dist/cli/commands/cron-scheduler-start-command.js +65 -0
  120. package/dist/cli/commands/cron-scheduler-status-command.d.ts +7 -0
  121. package/dist/cli/commands/cron-scheduler-status-command.js +46 -0
  122. package/dist/cli/commands/cron-scheduler-stop-command.d.ts +7 -0
  123. package/dist/cli/commands/cron-scheduler-stop-command.js +61 -0
  124. package/dist/cli/commands/dispatch-dag-envelope.d.ts +41 -0
  125. package/dist/cli/commands/dispatch-dag-envelope.js +49 -0
  126. package/dist/cli/commands/dispatch-dag-failure.d.ts +18 -0
  127. package/dist/cli/commands/dispatch-dag-failure.js +16 -0
  128. package/dist/cli/commands/dispatch-dag-preflight.d.ts +51 -0
  129. package/dist/cli/commands/dispatch-dag-preflight.js +105 -0
  130. package/dist/cli/commands/dispatch-dag-runners.d.ts +48 -0
  131. package/dist/cli/commands/dispatch-dag-runners.js +73 -0
  132. package/dist/cli/commands/dispatch-from-dag.d.ts +27 -0
  133. package/dist/cli/commands/dispatch-from-dag.js +87 -257
  134. package/dist/cli/commands/final-review-command-envelopes.d.ts +44 -0
  135. package/dist/cli/commands/final-review-command-envelopes.js +100 -0
  136. package/dist/cli/commands/final-review-command-shared.d.ts +52 -0
  137. package/dist/cli/commands/final-review-command-shared.js +35 -0
  138. package/dist/cli/commands/final-review-commands.d.ts +7 -27
  139. package/dist/cli/commands/final-review-commands.js +13 -240
  140. package/dist/cli/commands/final-review-prepare-command.d.ts +3 -0
  141. package/dist/cli/commands/final-review-prepare-command.js +200 -0
  142. package/dist/cli/commands/governance-classify-contract-commands.d.ts +23 -9
  143. package/dist/cli/commands/governance-classify-contract-commands.js +26 -269
  144. package/dist/cli/commands/hooks-command-shared.d.ts +42 -0
  145. package/dist/cli/commands/hooks-command-shared.js +32 -0
  146. package/dist/cli/commands/hooks-commands.d.ts +20 -0
  147. package/dist/cli/commands/hooks-commands.js +75 -309
  148. package/dist/cli/commands/hooks-install-envelopes.d.ts +6 -0
  149. package/dist/cli/commands/hooks-install-envelopes.js +144 -0
  150. package/dist/cli/commands/hooks-status-command.d.ts +3 -0
  151. package/dist/cli/commands/hooks-status-command.js +93 -0
  152. package/dist/cli/commands/hooks-uninstall-command.d.ts +3 -0
  153. package/dist/cli/commands/hooks-uninstall-command.js +43 -0
  154. package/dist/cli/commands/loop-command-shared.d.ts +31 -0
  155. package/dist/cli/commands/loop-command-shared.js +15 -0
  156. package/dist/cli/commands/loop-commands.d.ts +12 -17
  157. package/dist/cli/commands/loop-commands.js +56 -238
  158. package/dist/cli/commands/loop-export-command.d.ts +3 -0
  159. package/dist/cli/commands/loop-export-command.js +73 -0
  160. package/dist/cli/commands/loop-goal-compose-command.d.ts +3 -0
  161. package/dist/cli/commands/loop-goal-compose-command.js +36 -0
  162. package/dist/cli/commands/loop-import-command.d.ts +3 -0
  163. package/dist/cli/commands/loop-import-command.js +86 -0
  164. package/dist/cli/commands/loop-placeholder-commands.d.ts +3 -0
  165. package/dist/cli/commands/loop-placeholder-commands.js +52 -0
  166. package/dist/cli/commands/memory-command-shared.d.ts +59 -0
  167. package/dist/cli/commands/memory-command-shared.js +39 -0
  168. package/dist/cli/commands/memory-commands.d.ts +6 -79
  169. package/dist/cli/commands/memory-commands.js +19 -309
  170. package/dist/cli/commands/memory-ingest-command.d.ts +3 -0
  171. package/dist/cli/commands/memory-ingest-command.js +50 -0
  172. package/dist/cli/commands/memory-list-command.d.ts +16 -0
  173. package/dist/cli/commands/memory-list-command.js +129 -0
  174. package/dist/cli/commands/memory-reindex-command.d.ts +9 -0
  175. package/dist/cli/commands/memory-reindex-command.js +78 -0
  176. package/dist/cli/commands/memory-rotate-command.d.ts +3 -0
  177. package/dist/cli/commands/memory-rotate-command.js +55 -0
  178. package/dist/cli/commands/memory-search-command.d.ts +7 -0
  179. package/dist/cli/commands/memory-search-command.js +46 -0
  180. package/dist/cli/commands/openspec-archive-command.d.ts +3 -0
  181. package/dist/cli/commands/openspec-archive-command.js +55 -0
  182. package/dist/cli/commands/openspec-command-shared.d.ts +62 -0
  183. package/dist/cli/commands/openspec-command-shared.js +135 -0
  184. package/dist/cli/commands/openspec-commands.d.ts +1 -1
  185. package/dist/cli/commands/openspec-commands.js +33 -306
  186. package/dist/cli/commands/openspec-from-doctor-command.d.ts +3 -0
  187. package/dist/cli/commands/openspec-from-doctor-command.js +66 -0
  188. package/dist/cli/commands/openspec-init-command.d.ts +3 -0
  189. package/dist/cli/commands/openspec-init-command.js +49 -0
  190. package/dist/cli/commands/openspec-list-command.d.ts +3 -0
  191. package/dist/cli/commands/openspec-list-command.js +27 -0
  192. package/dist/cli/commands/openspec-render-command.d.ts +3 -0
  193. package/dist/cli/commands/openspec-render-command.js +33 -0
  194. package/dist/cli/commands/openspec-show-command.d.ts +3 -0
  195. package/dist/cli/commands/openspec-show-command.js +33 -0
  196. package/dist/cli/commands/openspec-to-rd-command.d.ts +3 -0
  197. package/dist/cli/commands/openspec-to-rd-command.js +33 -0
  198. package/dist/cli/commands/openspec-validate-command.d.ts +3 -0
  199. package/dist/cli/commands/openspec-validate-command.js +45 -0
  200. package/dist/cli/commands/playwright-browser-command.d.ts +8 -0
  201. package/dist/cli/commands/playwright-browser-command.js +61 -0
  202. package/dist/cli/commands/playwright-commands.d.ts +10 -62
  203. package/dist/cli/commands/playwright-commands.js +77 -400
  204. package/dist/cli/commands/playwright-envelope.d.ts +8 -0
  205. package/dist/cli/commands/playwright-envelope.js +29 -0
  206. package/dist/cli/commands/playwright-ls-stop-commands.d.ts +4 -0
  207. package/dist/cli/commands/playwright-ls-stop-commands.js +74 -0
  208. package/dist/cli/commands/playwright-session-store.d.ts +65 -0
  209. package/dist/cli/commands/playwright-session-store.js +189 -0
  210. package/dist/cli/commands/project-commands.d.ts +19 -0
  211. package/dist/cli/commands/project-commands.js +98 -259
  212. package/dist/cli/commands/project-context-command.d.ts +3 -0
  213. package/dist/cli/commands/project-context-command.js +44 -0
  214. package/dist/cli/commands/project-dashboard-command.d.ts +3 -0
  215. package/dist/cli/commands/project-dashboard-command.js +74 -0
  216. package/dist/cli/commands/project-knowledge-command.d.ts +3 -0
  217. package/dist/cli/commands/project-knowledge-command.js +48 -0
  218. package/dist/cli/commands/project-memories-show-command.d.ts +3 -0
  219. package/dist/cli/commands/project-memories-show-command.js +87 -0
  220. package/dist/cli/commands/project-memory-index-command.d.ts +3 -0
  221. package/dist/cli/commands/project-memory-index-command.js +31 -0
  222. package/dist/cli/commands/qa-archive-screenshots-command.d.ts +3 -0
  223. package/dist/cli/commands/qa-archive-screenshots-command.js +53 -0
  224. package/dist/cli/commands/qa-commands.d.ts +14 -87
  225. package/dist/cli/commands/qa-commands.js +17 -315
  226. package/dist/cli/commands/qa-context-prestep.d.ts +1 -0
  227. package/dist/cli/commands/qa-context-prestep.js +34 -0
  228. package/dist/cli/commands/qa-run-command.d.ts +3 -0
  229. package/dist/cli/commands/qa-run-command.js +105 -0
  230. package/dist/cli/commands/qa-run-options.d.ts +23 -0
  231. package/dist/cli/commands/qa-run-options.js +31 -0
  232. package/dist/cli/commands/qa-run-slice.d.ts +41 -0
  233. package/dist/cli/commands/qa-run-slice.js +145 -0
  234. package/dist/cli/commands/release-canary-command.d.ts +28 -0
  235. package/dist/cli/commands/release-canary-command.js +129 -0
  236. package/dist/cli/commands/release-command-shared.d.ts +13 -0
  237. package/dist/cli/commands/release-command-shared.js +18 -0
  238. package/dist/cli/commands/release-commands.d.ts +10 -18
  239. package/dist/cli/commands/release-commands.js +25 -298
  240. package/dist/cli/commands/release-done-command.d.ts +3 -0
  241. package/dist/cli/commands/release-done-command.js +64 -0
  242. package/dist/cli/commands/release-hotfix-command.d.ts +3 -0
  243. package/dist/cli/commands/release-hotfix-command.js +50 -0
  244. package/dist/cli/commands/release-plan-command.d.ts +3 -0
  245. package/dist/cli/commands/release-plan-command.js +33 -0
  246. package/dist/cli/commands/release-precheck-command.d.ts +3 -0
  247. package/dist/cli/commands/release-precheck-command.js +28 -0
  248. package/dist/cli/commands/release-promote-command.d.ts +3 -0
  249. package/dist/cli/commands/release-promote-command.js +34 -0
  250. package/dist/cli/commands/release-rollback-command.d.ts +3 -0
  251. package/dist/cli/commands/release-rollback-command.js +33 -0
  252. package/dist/cli/commands/release-watch-command.d.ts +3 -0
  253. package/dist/cli/commands/release-watch-command.js +44 -0
  254. package/dist/cli/commands/sediment-argv-flags.d.ts +54 -0
  255. package/dist/cli/commands/sediment-argv-flags.js +116 -0
  256. package/dist/cli/commands/sediment-command-shared.d.ts +15 -0
  257. package/dist/cli/commands/sediment-command-shared.js +5 -0
  258. package/dist/cli/commands/sediment-commands.d.ts +35 -59
  259. package/dist/cli/commands/sediment-commands.js +36 -590
  260. package/dist/cli/commands/sediment-pool-commands.d.ts +9 -0
  261. package/dist/cli/commands/sediment-pool-commands.js +185 -0
  262. package/dist/cli/commands/sediment-query-commands.d.ts +4 -0
  263. package/dist/cli/commands/sediment-query-commands.js +94 -0
  264. package/dist/cli/commands/sediment-release-commands.d.ts +8 -0
  265. package/dist/cli/commands/sediment-release-commands.js +232 -0
  266. package/dist/cli/commands/sop-advance-command.d.ts +3 -0
  267. package/dist/cli/commands/sop-advance-command.js +160 -0
  268. package/dist/cli/commands/sop-check-command.d.ts +3 -0
  269. package/dist/cli/commands/sop-check-command.js +44 -0
  270. package/dist/cli/commands/sop-command-shared.d.ts +23 -0
  271. package/dist/cli/commands/sop-command-shared.js +18 -0
  272. package/dist/cli/commands/sop-commands.d.ts +15 -1
  273. package/dist/cli/commands/sop-commands.js +26 -240
  274. package/dist/cli/commands/sop-init-command.d.ts +3 -0
  275. package/dist/cli/commands/sop-init-command.js +51 -0
  276. package/dist/cli/commands/sop-lint-command.d.ts +3 -0
  277. package/dist/cli/commands/sop-lint-command.js +64 -0
  278. package/dist/cli/commands/sop-register-command.d.ts +3 -0
  279. package/dist/cli/commands/sop-register-command.js +48 -0
  280. package/dist/cli/commands/sop-registry-command.d.ts +3 -0
  281. package/dist/cli/commands/sop-registry-command.js +30 -0
  282. package/dist/cli/commands/statusline-command-shared.d.ts +73 -0
  283. package/dist/cli/commands/statusline-command-shared.js +90 -0
  284. package/dist/cli/commands/statusline-commands.d.ts +1 -19
  285. package/dist/cli/commands/statusline-commands.js +48 -287
  286. package/dist/cli/commands/statusline-compact-command.d.ts +4 -0
  287. package/dist/cli/commands/statusline-compact-command.js +54 -0
  288. package/dist/cli/commands/statusline-install-command.d.ts +3 -0
  289. package/dist/cli/commands/statusline-install-command.js +48 -0
  290. package/dist/cli/commands/statusline-render-command.d.ts +9 -0
  291. package/dist/cli/commands/statusline-render-command.js +55 -0
  292. package/dist/cli/commands/statusline-status-command.d.ts +3 -0
  293. package/dist/cli/commands/statusline-status-command.js +23 -0
  294. package/dist/cli/commands/statusline-uninstall-command.d.ts +3 -0
  295. package/dist/cli/commands/statusline-uninstall-command.js +23 -0
  296. package/dist/cli/commands/test-command-run-helpers.d.ts +27 -0
  297. package/dist/cli/commands/test-command-run-helpers.js +73 -0
  298. package/dist/cli/commands/test-command-runner-resolution.d.ts +57 -0
  299. package/dist/cli/commands/test-command-runner-resolution.js +183 -0
  300. package/dist/cli/commands/test-commands.d.ts +8 -56
  301. package/dist/cli/commands/test-commands.js +26 -249
  302. package/dist/cli/commands/vm-command-shared.d.ts +23 -0
  303. package/dist/cli/commands/vm-command-shared.js +23 -0
  304. package/dist/cli/commands/vm-commands.d.ts +1 -28
  305. package/dist/cli/commands/vm-commands.js +36 -270
  306. package/dist/cli/commands/vm-hypervisor-runtime.d.ts +15 -0
  307. package/dist/cli/commands/vm-hypervisor-runtime.js +64 -0
  308. package/dist/cli/commands/vm-release-command.d.ts +5 -0
  309. package/dist/cli/commands/vm-release-command.js +101 -0
  310. package/dist/cli/commands/vm-spawn-command.d.ts +5 -0
  311. package/dist/cli/commands/vm-spawn-command.js +177 -0
  312. package/dist/cli/commands/web-command-shared.d.ts +12 -0
  313. package/dist/cli/commands/web-command-shared.js +30 -0
  314. package/dist/cli/commands/web-commands.d.ts +7 -14
  315. package/dist/cli/commands/web-commands.js +6 -314
  316. package/dist/cli/commands/web-install-command.d.ts +10 -0
  317. package/dist/cli/commands/web-install-command.js +116 -0
  318. package/dist/cli/commands/web-lifecycle-commands.d.ts +9 -30
  319. package/dist/cli/commands/web-lifecycle-commands.js +14 -322
  320. package/dist/cli/commands/web-login-command.d.ts +14 -0
  321. package/dist/cli/commands/web-login-command.js +189 -0
  322. package/dist/cli/commands/web-op-payload.d.ts +22 -0
  323. package/dist/cli/commands/web-op-payload.js +89 -0
  324. package/dist/cli/commands/web-op-refusals.d.ts +37 -0
  325. package/dist/cli/commands/web-op-refusals.js +73 -0
  326. package/dist/cli/commands/web-op-runner.d.ts +14 -0
  327. package/dist/cli/commands/web-op-runner.js +166 -0
  328. package/dist/cli/commands/web-status-command.d.ts +4 -0
  329. package/dist/cli/commands/web-status-command.js +32 -0
  330. package/dist/cli/commands/web-stop-command.d.ts +10 -0
  331. package/dist/cli/commands/web-stop-command.js +59 -0
  332. package/dist/cli/commands/web-verb-specs.d.ts +18 -0
  333. package/dist/cli/commands/web-verb-specs.js +66 -0
  334. package/dist/cli/commands/workflow-command-shared.d.ts +102 -0
  335. package/dist/cli/commands/workflow-command-shared.js +74 -0
  336. package/dist/cli/commands/workflow-graph-commands.d.ts +3 -0
  337. package/dist/cli/commands/workflow-graph-commands.js +73 -0
  338. package/dist/cli/commands/workflow-init-command.d.ts +3 -0
  339. package/dist/cli/commands/workflow-init-command.js +68 -0
  340. package/dist/cli/commands/workflow-lifecycle-commands.d.ts +9 -59
  341. package/dist/cli/commands/workflow-lifecycle-commands.js +14 -348
  342. package/dist/cli/commands/workflow-node-commands.d.ts +3 -0
  343. package/dist/cli/commands/workflow-node-commands.js +154 -0
  344. package/dist/cli/commands/workflow-terminalize-command.d.ts +3 -0
  345. package/dist/cli/commands/workflow-terminalize-command.js +56 -0
  346. package/dist/services/code/auto-compact-artifacts.d.ts +54 -0
  347. package/dist/services/code/auto-compact-artifacts.js +135 -0
  348. package/dist/services/code/auto-compact-attempt.d.ts +46 -0
  349. package/dist/services/code/auto-compact-attempt.js +88 -0
  350. package/dist/services/code/auto-compact-decision.d.ts +74 -0
  351. package/dist/services/code/auto-compact-decision.js +149 -0
  352. package/dist/services/code/auto-compact-dispatch-gate.d.ts +36 -0
  353. package/dist/services/code/auto-compact-dispatch-gate.js +86 -0
  354. package/dist/services/code/auto-compact-envelopes.d.ts +33 -0
  355. package/dist/services/code/auto-compact-envelopes.js +134 -0
  356. package/dist/services/code/auto-compact-history.d.ts +46 -0
  357. package/dist/services/code/auto-compact-history.js +17 -0
  358. package/dist/services/code/auto-compact-orchestrator.d.ts +11 -240
  359. package/dist/services/code/auto-compact-orchestrator.js +165 -686
  360. package/dist/services/code/auto-compact-run-types.d.ts +123 -0
  361. package/dist/services/code/auto-compact-run-types.js +1 -0
  362. package/dist/services/code/emit-handoff.d.ts +35 -1
  363. package/dist/services/code/emit-handoff.js +128 -7
  364. package/dist/services/code/step-08-gate.d.ts +28 -19
  365. package/dist/services/code/step-08-gate.js +92 -35
  366. package/dist/services/config/config-coercion.d.ts +7 -0
  367. package/dist/services/config/config-coercion.js +156 -0
  368. package/dist/services/config/config-file-io.d.ts +11 -0
  369. package/dist/services/config/config-file-io.js +61 -0
  370. package/dist/services/config/config-get.d.ts +2 -0
  371. package/dist/services/config/config-get.js +36 -0
  372. package/dist/services/config/config-global-loader.d.ts +15 -0
  373. package/dist/services/config/config-global-loader.js +111 -0
  374. package/dist/services/config/config-nested-path.d.ts +3 -0
  375. package/dist/services/config/config-nested-path.js +53 -0
  376. package/dist/services/config/config-primitives.d.ts +16 -0
  377. package/dist/services/config/config-primitives.js +24 -0
  378. package/dist/services/config/config-service.d.ts +9 -33
  379. package/dist/services/config/config-service.js +16 -665
  380. package/dist/services/config/config-validation.d.ts +19 -0
  381. package/dist/services/config/config-validation.js +152 -0
  382. package/dist/services/config/config-workspace.d.ts +10 -0
  383. package/dist/services/config/config-workspace.js +158 -0
  384. package/dist/services/skills/hooks-settings-deny-list.d.ts +61 -0
  385. package/dist/services/skills/hooks-settings-deny-list.js +173 -0
  386. package/dist/services/skills/hooks-settings-envelopes.d.ts +20 -0
  387. package/dist/services/skills/hooks-settings-envelopes.js +79 -0
  388. package/dist/services/skills/hooks-settings-file-shape.d.ts +43 -0
  389. package/dist/services/skills/hooks-settings-file-shape.js +207 -0
  390. package/dist/services/skills/hooks-settings-paths.d.ts +34 -0
  391. package/dist/services/skills/hooks-settings-paths.js +75 -0
  392. package/dist/services/skills/hooks-settings-read-installed.d.ts +39 -0
  393. package/dist/services/skills/hooks-settings-read-installed.js +60 -0
  394. package/dist/services/skills/hooks-settings-service.d.ts +12 -212
  395. package/dist/services/skills/hooks-settings-service.js +71 -657
  396. package/dist/services/skills/hooks-settings-types.d.ts +139 -0
  397. package/dist/services/skills/hooks-settings-types.js +1 -0
  398. package/dist/services/workspace/claude-settings-template-merge.d.ts +89 -0
  399. package/dist/services/workspace/claude-settings-template-merge.js +243 -0
  400. package/dist/services/workspace/claude-settings-template.d.ts +29 -87
  401. package/dist/services/workspace/claude-settings-template.js +81 -236
  402. package/package.json +5 -5
@@ -1,688 +1,208 @@
1
1
  /**
2
+ * Drives the whole compaction of a session's context.
2
3
  *
3
- * knows the current plan, open questions, recent decisions, in-flight
4
- * batches, todo state, git status, and active skills. That context is
5
- * strictly more valuable than what `/compact` can synthesize from raw
6
- * conversation history — so peaks-loop drives the entire compaction:
4
+ * peaks-loop knows the current plan, open questions, recent decisions, in-flight
5
+ * batches, todo state, git status, and active skills. That context is strictly more
6
+ * valuable than what `/compact` can synthesise from raw conversation history — so
7
+ * peaks-loop drives the entire compaction:
7
8
  *
8
- * 1. Read current context % (via IDE adapter's `readContextPercent`).
9
- * 2. If ratio ≥ 0.95 (RED LINE): peaks-loop ASKS the harness to
10
- * compact and reports that it is waiting. It does NOT block
11
- * sub-agent dispatch (slice
12
- * the *decision*, the harness owns the *capability*, and peaks-loop
13
- * has no way to compact a running session itself — so a "gate"
14
- * here gated nothing and deadlocked the runner at the worst
15
- * moment. If the ratio keeps rising and the harness has not
16
- * compacted, the honest move is to say so and hand control back.
17
- * 3. If 0.85 ≤ ratio < 0.95 (pre-compact zone): peaks-loop prepares
18
- * the convergence toolkit (checkpoint + auto-decisions log +
19
- * IDE-dispatch handle) and surfaces it to the LLM. The LLM
20
- * DECIDES when to fire `peaks code auto-compact`;
21
- * peaks-loop does NOT auto-fire. The toolkit is ready so the
22
- * LLM doesn't lose context to a last-second `/compact` panic.
23
- * 4. If ratio < 0.85: skip — return a one-line info row.
9
+ * 1. Read the current context % (via the IDE adapter's `readContextPercent`).
10
+ * 2. If the ratio ≥ the mode's red line: peaks-loop ASKS the harness to compact and
11
+ * reports that it is waiting. It does NOT block sub-agent dispatch — the harness
12
+ * owns the *capability*, peaks-loop only owns the *decision*, and peaks-loop has
13
+ * no way to compact a running session itself, so a "gate" here gated nothing and
14
+ * deadlocked the runner at the worst moment. If the ratio keeps rising and the
15
+ * harness has not compacted, the honest move is to say so and hand control back.
16
+ * 3. If the ratio ≥ the mode's auto-fire threshold: peaks-loop prepares the
17
+ * convergence toolkit (checkpoint + auto-decisions log + IDE-dispatch handle) and
18
+ * fires it without LLM confirmation.
19
+ * 4. Below that: skip — return a one-line info row.
24
20
  *
25
- * Why two tiers (vs. one): the LLM uses the 0.85–0.95 zone for
26
- * intelligent convergence — wait for in-flight sub-agents, finish
27
- * the current todo row, persist a checkpoint, then compact. At 0.95
28
- * peaks-loop stops negotiating and requests the compact outright. Net
29
- * effect: the LLM-runner keeps working at any ratio without human
30
- * intervention, and without a gate it cannot satisfy.
21
+ * The tiers exist so the ratio is never left to drift: peaks-loop preempts at the
22
+ * auto-fire threshold, and the LLM is not asked to guess when to compact.
23
+ *
24
+ * This file owns the run: one probe, one decision, one attempt. The decision table
25
+ * lives in `auto-compact-decision.js`, the artifacts in `auto-compact-artifacts.js`,
26
+ * and the envelopes in `auto-compact-envelopes.js`.
31
27
  */
32
- import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from 'node:fs';
33
- import { dirname, join } from 'node:path';
34
28
  import { getSessionIdCanonical } from '../session/session-manager.js';
35
- import { getSessionDir } from '../session/getSessionDir.js';
36
29
  import { resolveOuterSessionId } from '../session/binding-status-service.js';
37
30
  import { resolveCanonicalProjectRoot } from '../config/config-service.js';
38
- import { describeHarnessWindowSync, harnessWindowSyncWarning } from '../context/harness-window-config.js';
39
- import { AUTO_COMPACT_PRE_COMPACT_RATIO, DEPRECATED_ENVELOPE_FIELDS } from '../context/auto-compact-types.js';
40
- import { describeMode, thresholdFor } from './auto-compact-modes.js';
31
+ import { thresholdFor } from './auto-compact-modes.js';
41
32
  import { resolveAutoCompactProfile } from '../mode/mode-status-service.js';
42
- import { CompactLifecyclePublisher, fillEventSettledMeasurement, newCompactRunId, readOpenDispatchRun, resolveDispatchedStage, settleOpenLifecycleRun, summarizeLifecycleError } from './auto-compact-lifecycle.js';
43
- const PRE_COMPACT_REASON = 'pre-compact-auto';
44
- /**
45
- * Map a context ratio to a `CompactTrigger` action. Pure; the side
46
- * effects (checkpoint + IDE dispatch) live in `runAutoCompact`. Two
47
- * tiers (standard mode; partial mode shifts both thresholds):
48
- *
49
- * - ratio < preCompact → 'none' or 'soft-warn'
50
- * - ratio ≥ preCompact → 'pre-compact' (async-friendly path)
51
- * - ratio ≥ redLine → 'red-line' (ask the harness; dispatch continues)
52
- *
53
- * Default `'standard'` (0.85/0.95). `'partial'` (0.70/0.85) is used
54
- * when 24h long-run mode is active or `--mode partial` is passed.
55
- */
56
- export function evaluateCompactTrigger(ratio, mode = 'standard') {
57
- const autoFire = thresholdFor(mode, 'autoFire');
58
- const preCompact = thresholdFor(mode, 'preCompact');
59
- const redLine = thresholdFor(mode, 'redLine');
60
- if (ratio < autoFire) {
61
- return ratio < 0.5
62
- ? { kind: 'none' }
63
- : {
64
- kind: 'soft-warn',
65
- ratio,
66
- message: `Context at ${(ratio * 100).toFixed(1)}%; below the ${(autoFire * 100).toFixed(0)}% auto-fire threshold (mode=${mode}).`
67
- };
68
- }
69
- if (ratio >= redLine) {
70
- // longer claims it blocks anything. It cannot: peaks-loop has no
71
- // executor for a running session (no `/compact` the model may invoke, no
72
- // hook-initiated compact, no `--compact` flag), so "refuse dispatch until
73
- // ratio < 0.85" was a gate with no key — a constructive deadlock at the
74
- // exact moment the runner most needed to keep working. What peaks-loop
75
- // CAN do is ask the harness (whose own trigger is armed) and say so.
76
- return {
77
- kind: 'red-line',
78
- ratio,
79
- message: `Context at ${(ratio * 100).toFixed(1)}% ≥ ${(redLine * 100).toFixed(0)}% red line (mode=${mode}). peaks-loop has asked the harness to compact and is WAITING for it — sub-agent dispatch is NOT blocked; carry on and re-probe with \`peaks code context-now\`.`
80
- };
81
- }
82
- if (ratio < preCompact) {
83
- // Part 22: auto-fire zone (0.80 ≤ ratio < 0.85). peaks-loop
84
- // preempts and runs `peaks code auto-compact` itself
85
- // without LLM involvement. The LLM is not asked to "decide";
86
- // the toolkit is applied synchronously. Closes the
87
- // LLM-misjudges-context window that previously let the
88
- // ratio drift to 0.95 before the auto-fire kicked in.
89
- return {
90
- kind: 'auto-fire',
91
- ratio,
92
- message: `Context at ${(ratio * 100).toFixed(1)}% in auto-fire zone (≥${(autoFire * 100).toFixed(0)}% / <${(preCompact * 100).toFixed(0)}%, mode=${mode}). peaks-loop will fire compact without LLM confirmation.`
93
- };
94
- }
95
- // pre-compact zone (0.85 ≤ ratio < 0.95): kept for backward
96
- // compat with operators who configured the higher threshold.
97
- // In practice peaks-loop already auto-fired at the lower
98
- // threshold; the pre-compact zone today is the "already fired"
99
- // zone.
100
- return {
101
- kind: 'pre-compact',
102
- ratio,
103
- toolkitReady: true,
104
- message: `Context at ${(ratio * 100).toFixed(1)}% in pre-compact zone (≥${(preCompact * 100).toFixed(0)}% / <${(redLine * 100).toFixed(0)}%, mode=${mode}). peaks-loop already fired the auto-compact pathway at the auto-fire threshold; the LLM does not need to act.`
105
- };
106
- }
33
+ import { fillEventSettledMeasurement, resolveDispatchedStage, settleOpenLifecycleRun } from './auto-compact-lifecycle.js';
34
+ import { evaluateAutoCompactDecision } from './auto-compact-decision.js';
35
+ import { appendCompactHistoryEvent } from './auto-compact-history.js';
36
+ import { readOpenRunGate } from './auto-compact-dispatch-gate.js';
37
+ import { dispatchAttempt, prepareCompactAttempt } from './auto-compact-attempt.js';
38
+ import { compactResult, noSessionResult, skipResult } from './auto-compact-envelopes.js';
39
+ export { evaluateCompactTrigger, evaluateAutoCompactDecision, buildConvergencePlan } from './auto-compact-decision.js';
40
+ export { appendCompactHistoryEvent } from './auto-compact-history.js';
107
41
  /**
108
- * Decide whether to run the auto-compact flow. Pure function for the
109
- * decision; side effects (checkpoint + IDE dispatch) live in
110
- * `runAutoCompact` below. Zero human / zero LLM intervention:
111
- *
112
- * - ratio < 0.85 → skip (LLM keeps working; no action)
113
- * - 0.85 ≤ ratio < 0.95 → pre-compact; if in-flight batch
114
- * present, defer (D6.e); else dispatch
115
- * IDE compact asynchronously.
116
- * - ratio ≥ 0.95 → red-line; ask the harness to compact
117
- * regardless of in-flight batch. Nothing is
118
- * gated — dispatch is NOT blocked (slice
119
- * peaks-loop cannot compact a running session,
120
- * so a "block" gated nothing and deadlocked the
121
- * runner).
42
+ * The below-threshold path still has work to do: a ratio that has fallen back under
43
+ * the auto-fire threshold is the real, measured proof that a previously-dispatched
44
+ * compact landed, so any run still open at `compacting` is settled here. Nothing is
45
+ * written when there is no open run, when the ratio is still high, or when the probe
46
+ * could not measure at all.
122
47
  */
123
- export function evaluateAutoCompactDecision(input) {
124
- const trigger = evaluateCompactTrigger(input.ratio, input.mode ?? 'standard');
125
- // Normalize both `inFlightBatch` and `inflightBatch` (boolean | probe)
126
- // into a single `InFlightBatchProbe` shape. The probe reads
127
- // `hasInFlightBatch`; the boolean reads truthiness.
128
- const rawProbe = input.inFlightBatch ?? input.inflightBatch;
129
- const probe = typeof rawProbe === 'boolean' ? { hasInFlightBatch: rawProbe } : rawProbe;
130
- if (trigger.kind === 'none') {
131
- return { shouldCompact: false, reason: 'below-threshold', trigger, action: 'ok' };
132
- }
133
- if (trigger.kind === 'soft-warn') {
134
- return { shouldCompact: false, reason: 'below-threshold', trigger, action: 'soft-warn' };
135
- }
136
- if (trigger.kind === 'red-line') {
137
- // Red line: ignore in-flight batch — the harness is asked NOW rather than
138
- // waiting for the batch to drain. Not "synchronous dispatch": peaks-loop
139
- // has no synchronous compact to run, it can only request one and report
140
- // that it is waiting.
141
- return { shouldCompact: true, reason: 'red-line', trigger, action: 'red-line' };
142
- }
143
- // pre-compact zone (0.85 ≤ ratio < 0.95): honor D6.e in-flight deferral.
144
- if (probe?.hasInFlightBatch === true) {
145
- return { shouldCompact: false, reason: 'in-flight-batch', trigger, action: 'defer' };
146
- }
147
- if (input.force) {
148
- return { shouldCompact: true, reason: 'pre-compact', trigger, action: 'auto-compact-now' };
149
- }
150
- // is the ONLY signal available on Mac Claude Code (no env-var, no statusline
151
- // poll). The gate above already returns shouldCompact: true at ratio ≥ 0.85,
152
- // but this forward-compat carve-out makes the source-aware rule explicit so
153
- // any future source-aware downgrading cannot silently re-introduce the
154
- // Mac auto-compact silent-failure mode without an audit. No higher-priority
155
- // source is present (`claude-code-env` would have been P1, `statusline-poll`
156
- // P2, `user-overridden` P4) — Mac's only signal is `transcript-estimate`.
157
- if (input.source === 'transcript-estimate' && input.ratio >= AUTO_COMPACT_PRE_COMPACT_RATIO)
158
- return { shouldCompact: true, reason: 'pre-compact', trigger, action: 'auto-compact-now' };
159
- // Default: peaks-loop drives pre-compact autonomously.
160
- return { shouldCompact: true, reason: 'pre-compact', trigger, action: 'auto-compact-now' };
161
- }
162
- /**
163
- * Build the convergence plan that D7's post-compact-detect will read
164
- * back. Includes the current plan, open questions, recent decisions,
165
- * todo state, and recent artifact paths — strictly more than what a
166
- * raw `/compact` would preserve.
167
- */
168
- export function buildConvergencePlan(input) {
169
- return {
170
- schemaVersion: 1,
171
- sessionId: input.sessionId,
48
+ function settleBelowThreshold(ctx) {
49
+ const { input, sessionId, probe, mode } = ctx;
50
+ const settleRead = settleOpenLifecycleRun({
172
51
  projectRoot: input.projectRoot,
173
- createdAt: new Date().toISOString(),
174
- ratio: input.ratio,
175
- checkpointPath: input.checkpointPath,
176
- nextActions: [...input.nextActions],
177
- resumeHint: input.redLine === true
178
- ? 'RED-LINE compact requested from the harness; work CONTINUES (nothing is blocked). Re-probe with `peaks code context-now`; if the ratio is still ≥ 0.95 and the harness has not compacted, report it and hand control back to the user.'
179
- : 'post-compact-detect shouldAutoResume → resume pre-compact plan from checkpoint'
180
- };
181
- }
182
- /**
183
- * Append a one-row convergence decision to the LLM-readable log.
184
- * The LLM reads this on the post-compact turn to pick up exactly
185
- * where it left off (vs. blindly trusting the IDE's compressed
186
- * transcript).
187
- */
188
- function appendAutoDecisionLog(input) {
189
- // Repair R6: same hand-rolled join as `writePreCompactCheckpoint`. All four
190
- // session-scoped paths in this file now go through the axis builder; none
191
- // composes the join itself, so none can resolve outside the project root.
192
- const dir = join(getSessionDir(input.projectRoot, input.sessionId), 'txt');
193
- if (!existsSync(dir))
194
- mkdirSync(dir, { recursive: true });
195
- const logPath = join(dir, 'auto-decisions.md');
196
- const row = [
197
- '',
198
- `## Auto-compact decision — ${input.plan.createdAt}`,
199
- `- ratio: ${(input.plan.ratio * 100).toFixed(1)}%`,
200
- `- checkpoint: ${input.plan.checkpointPath}`,
201
- `- next-actions: ${input.plan.nextActions.join(' | ')}`,
202
- `- resume-hint: ${input.plan.resumeHint}`,
203
- ''
204
- ].join('\n');
205
- if (!existsSync(logPath)) {
206
- writeFileSync(logPath, `# peaks-code auto-decisions log\n${row}`, 'utf8');
52
+ sessionId,
53
+ measuredRatio: probe.ratio,
54
+ source: probe.source,
55
+ autoFireThreshold: thresholdFor(mode, 'autoFire'),
56
+ onLifecycleStage: input.onLifecycleStage,
57
+ failLifecycleWrite: input.testHooks?.failLifecycleWrite
58
+ });
59
+ // A settle whose lifecycle write FAILED has not settled the run — the record still
60
+ // rests at `armed` / `compacting`, so the next probe finds the same run open and
61
+ // retries. Appending the "observed compaction point" row anyway would write one row
62
+ // per probe for a compaction the store never recorded: an unbounded append driven by
63
+ // a write that did not happen. Nothing is lost by deferring — `probe.ratio` is
64
+ // re-measured on every probe, so the retry carries a fresh number.
65
+ const settled = settleRead !== null && !settleRead.lifecycleWritten
66
+ ? null
67
+ : (settleRead ??
68
+ // The event path may have closed this run WITHOUT an honest post-compact
69
+ // number — the call above then finds nothing open, and without this the
70
+ // calibration pair stays blank for exactly the compactions the event path
71
+ // exists to witness. Fills the number the event owed.
72
+ fillEventSettledMeasurement({
73
+ projectRoot: input.projectRoot,
74
+ sessionId,
75
+ measuredRatio: probe.ratio,
76
+ source: probe.source
77
+ }));
78
+ // A dispatched compact demonstrably landed: append an `observed` row carrying the
79
+ // measured ratio, so `peaks compact history` can show "asked at R (intent) / landed
80
+ // by R' (observed)" once a real session has run. Without this row the intent has
81
+ // nothing to be compared against.
82
+ if (settled === null)
207
83
  return;
208
- }
209
- const existing = readFileSync(logPath, 'utf8');
210
- writeFileSync(logPath, `${existing}${row}`, 'utf8');
211
- }
212
- /**
213
- * main-session compact intent so the main-session LLM picks it up on
214
- * its next turn and fires `/compact` in-band. Without this file the
215
- * orchestrator's "main-session compact" request is invisible to the
216
- * main Claude Code window (defeats the whole point of auto-compact
217
- * for the main context).
218
- *
219
- * The file is gitignored under `.peaks/_runtime/<sessionId>/txt/` and
220
- * is one-shot: the LLM should `mv` it to `.consumed` after firing
221
- * `/compact`. A re-run will overwrite.
222
- *
223
- * write survives, but nothing consumes the file, and its `nextAction` asks
224
- * for a `/compact` the model cannot invoke (the Skill tool exposes only
225
- * `/init` and `/security-review`; hooks can observe or veto, never initiate).
226
- * Retained deliberately for the sibling A2 slice, which owns harness-side
227
- * state re-injection. Not a capability peaks-loop has today.
228
- */
229
- function writeMainSessionCompactIntent(input) {
230
- const dir = join(getSessionDir(input.projectRoot, input.sessionId), 'txt');
231
- if (!existsSync(dir))
232
- mkdirSync(dir, { recursive: true });
233
- const path = join(dir, 'auto-compact-pending.json');
234
- const payload = {
235
- schemaVersion: 1,
236
- pending: true,
237
- target: 'main',
238
- requestedAt: input.now.toISOString(),
239
- ratio: input.ratio,
240
- redLine: input.redLine,
241
- nextAction: 'next LLM turn MUST fire `/compact` then `mv .peaks/_runtime/<sid>/txt/auto-compact-pending.json .peaks/_runtime/<sid>/txt/auto-compact-pending.consumed.json`'
242
- };
243
- writeFileSync(path, JSON.stringify(payload, null, 2), 'utf8');
244
- }
245
- /**
246
- * Write a pre-compact checkpoint. The shape mirrors `peaks session
247
- * checkpoint` so D7's post-compact-detect picks it up unchanged.
248
- */
249
- function writePreCompactCheckpoint(input) {
250
- // Repair R6: this used to hand-roll the session join, which is the one shape
251
- // `getSessionDir`'s own header records as NOT covered by the guard — an
252
- // unsafe id resolved outside the project root and the checkpoint was written
253
- // there. The gate above now refuses an unresolvable id before this point, but
254
- // `--force` outranks that gate, so this join has to hold on its own.
255
- const dir = join(getSessionDir(input.projectRoot, input.sessionId), 'checkpoints');
256
- if (!existsSync(dir))
257
- mkdirSync(dir, { recursive: true });
258
- const prefix = input.redLine === true ? 'red-line-' : 'pre-compact-';
259
- const filename = `${prefix}${input.now.toISOString().replace(/[:.]/g, '-')}.json`;
260
- const path = join(dir, filename);
261
- const content = {
262
- schemaVersion: 1,
263
- reason: input.redLine === true ? 'pre-compact-red-line' : PRE_COMPACT_REASON,
264
- sessionId: input.sessionId,
265
- createdAt: input.now.toISOString(),
266
- // D7 reads `mode`, `currentPlan`, `openQuestions`, `recentDecisions`
267
- // out of this JSON. We seed empty arrays; the post-compact LLM
268
- // rehydrates from the auto-decisions log + open question list.
269
- mode: 'full-auto',
270
- currentPlan: input.redLine === true
271
- ? 'RED-LINE compact REQUESTED from the harness (not executed by peaks-loop); work continues'
272
- : 'auto-compact in progress; resume from auto-decisions.md',
273
- openQuestions: [],
274
- recentDecisions: [],
275
- recentArtifactPaths: [],
276
- gitStatus: '',
277
- skillsActive: ['peaks-code'],
278
- todoState: []
279
- };
280
- writeFileSync(path, JSON.stringify(content, null, 2), 'utf8');
281
- return path;
282
- }
283
- /**
284
- * the PRESENCE MODE, not the 24h state machine. Returns `'partial'`
285
- * when the mode is `24h`, otherwise `'standard'`. The CLI flag
286
- * `--mode` takes precedence (caller passes `input.mode` directly), so
287
- * this helper is only consulted when the flag is absent.
288
- */
289
- function resolveAutoCompactMode(projectRoot) {
290
- return resolveAutoCompactProfile(projectRoot);
84
+ appendObservedCompactEvent({
85
+ projectRoot: input.projectRoot,
86
+ sessionId,
87
+ event: {
88
+ schemaVersion: 1,
89
+ kind: 'observed',
90
+ ts: new Date().toISOString(),
91
+ target: input.target ?? 'main',
92
+ mode,
93
+ ide: probe.ide,
94
+ pathway: 'post-compact-probe',
95
+ beforeRatio: settled.triggerRatio,
96
+ afterRatio: probe.ratio,
97
+ redLine: false,
98
+ ok: true,
99
+ checkpointPath: '',
100
+ dispatchMessage: `post-compact probe measured ratio ${(probe.ratio * 100).toFixed(1)}% (source=${probe.source}) after the compact dispatched at ${(settled.triggerRatio * 100).toFixed(1)}%`,
101
+ windowTokens: probe.capacityTokens ?? null,
102
+ windowSource: probe.capacitySource ?? null
103
+ }
104
+ });
291
105
  }
292
106
  /**
293
- * Execute the auto-compact flow.
294
- *
295
- * Steps (orchestration):
296
- * 1. Resolve session id.
297
- * 3. Evaluate trigger via `evaluateCompactTrigger`.
298
- * 4. If trigger.kind === 'none' / 'soft-warn' → return skip.
299
- * 5. If trigger.kind === 'pre-compact' AND in-flight batch → wait.
300
- * 6. If trigger.kind === 'pre-compact' → async dispatch (write
301
- * checkpoint + IDE compact; orchestrator returns immediately).
302
- * 7. If trigger.kind === 'red-line' → dispatch the IDE compact, report
303
- * that the harness has been asked and that dispatch continues.
304
- *
305
- * The caller (CLI or skill body) handles the actual return — D7's
306
- * post-compact-detect will pick up the checkpoint on the next turn. For
307
- * red-line, the caller keeps working; it re-probes and, if the ratio is
308
- * still rising with no compact from the harness, reports that instead of
309
- * stalling.
107
+ * Execute the auto-compact flow. The caller handles the return — the post-compact
108
+ * detector picks the checkpoint up on the next turn. For the red line the caller keeps
109
+ * working: it re-probes and, if the ratio is still rising with no compact from the
110
+ * harness, reports that instead of stalling.
310
111
  */
311
112
  export async function runAutoCompact(input) {
312
113
  const sessionId = input.sessionId ?? getSessionIdCanonical(input.projectRoot);
313
- if (sessionId === null) {
314
- return {
315
- ok: false,
316
- code: 'AUTO_COMPACT_NO_SESSION',
317
- message: 'No active session; cannot run auto-compact. Run `peaks workspace init` first.',
318
- nextActions: ['Run `peaks workspace init --change-id <id>` to bind a session']
319
- };
114
+ if (sessionId === null)
115
+ return noSessionResult();
116
+ const mode = input.mode ?? resolveAutoCompactProfile(input.projectRoot);
117
+ const ctx = await openRun(input, sessionId, mode);
118
+ if (!ctx.decision.shouldCompact) {
119
+ settleBelowThreshold(ctx);
120
+ return skipResult(ctx);
121
+ }
122
+ const armed = readOpenRunGate(ctx);
123
+ if (armed !== null)
124
+ return armed;
125
+ const attempt = prepareCompactAttempt(ctx);
126
+ if (!attempt.ok)
127
+ return attempt.envelope;
128
+ const { lifecycle, checkpointPath, plan } = attempt;
129
+ const dispatched = await dispatchAttempt({ ctx, lifecycle, checkpointPath, plan });
130
+ if (!dispatched.ok)
131
+ return dispatched.envelope;
132
+ // The stage is chosen from what the dispatch ACTUALLY did, never from the hope that it
133
+ // compacted. `ide-native` on claude-code only installs a PreToolUse hook that fires at
134
+ // the red line — below that nothing is in flight and the honest stage is `armed`.
135
+ // Writing `compacting` there published a heartbeat that would never arrive, which is
136
+ // exactly what pinned the statusline at `stalled` for 92 minutes in the field. A
137
+ // dispatcher that returns `ok: false` compacted nothing either, so it is recorded as a
138
+ // failure at `compacting` rather than left looking in progress.
139
+ if (dispatched.dispatch.ok) {
140
+ lifecycle.advance(resolveDispatchedStage({ pathway: dispatched.dispatch.pathway, ratio: ctx.probe.ratio }));
320
141
  }
321
- // presence MODE ('24h' → 'partial'); 'standard' preserves the
322
- // v2.13.0 zero-pause contract.
323
- const mode = input.mode ?? resolveAutoCompactMode(input.projectRoot);
142
+ else {
143
+ lifecycle.fail(new Error(dispatched.dispatch.message), 'compacting');
144
+ }
145
+ appendDispatchHistory(ctx, checkpointPath, dispatched.dispatch, dispatched.target);
146
+ return compactResult(ctx, {
147
+ checkpointPath,
148
+ plan,
149
+ target: dispatched.target,
150
+ dispatch: dispatched.dispatch
151
+ });
152
+ }
153
+ /** One probe, one window sync, one decision — the state every phase below reads. */
154
+ async function openRun(input, sessionId, mode) {
324
155
  const { readContextPercent, syncHarnessWindowForProject } = await import('../context/auto-compact-reader.js');
325
- const outerSessionId = resolveOuterSessionId(input.projectRoot, sessionId, input.env ?? process.env);
326
156
  const probe = readContextPercent({
327
157
  projectRoot: input.projectRoot,
328
158
  sessionId,
329
- outerSessionId,
159
+ outerSessionId: resolveOuterSessionId(input.projectRoot, sessionId, input.env ?? process.env),
330
160
  env: input.env
331
161
  });
332
- // denominator this probe divided by into the harness's own machine-local
333
- // settings, so "peaks-loop's 85%" and "the harness's trigger" are the same
334
- // point on one scale. Idempotent — an unchanged value performs no write, so
335
- // a hook firing this on every Bash call cannot churn the file.
162
+ // The probe's denominator is materialized into the harness's own machine-local
163
+ // settings, so "peaks-loop's 85%" and "the harness's trigger" are the same point on one
164
+ // scale. Idempotent — an unchanged value performs no write. The project root is
165
+ // promoted to the git root first (`--project .` is what the PreToolUse hook passes):
166
+ // the harness's settings live at the project root, and the envelope must report an
167
+ // absolute path for the write it claims to have made. Fail-open.
336
168
  const harnessWindow = syncHarnessWindowForProject({
337
- // Promoted to the git root first (`--project .` is what the PreToolUse
338
- // hook passes): the harness's settings live at the project root, and the
339
- // envelope must report an absolute path for the write it claims to have
340
- // made. Fail-open — `resolveCanonicalProjectRoot` returns its input when
341
- // nothing resolves.
342
169
  projectRoot: resolveCanonicalProjectRoot(input.projectRoot),
343
170
  env: input.env,
344
171
  tokens: probe.capacityTokens ?? null
345
172
  });
346
173
  const decision = evaluateAutoCompactDecision({
347
174
  ratio: probe.ratio,
348
- // Slice 4.0.8 (D4d): production `inFlightBatch` MUST come from
349
- // the graph probe (see `workflow-inflight-probe.ts`). The
350
- // legacy boolean is preserved as a TEST-ONLY seam: when
351
- // `probeInflightBatch` is supplied, we call it and pass the
352
- // result as a `InFlightBatchProbe` so the pure decision function
353
- // sees a graph-backed value. When only the boolean is
354
- // supplied, we treat it as the test seam (the CLI gates it
355
- // behind `PEAKS_TEST_SEAM === '1'`).
175
+ // Production `inFlightBatch` MUST come from the graph probe. The legacy boolean is
176
+ // preserved as a TEST-ONLY seam: when `probeInflightBatch` is supplied we call it and
177
+ // pass an `InFlightBatchProbe`, so the pure decision sees a graph-backed value. The
178
+ // CLI gates the boolean behind `PEAKS_TEST_SEAM === '1'`.
356
179
  inFlightBatch: input.probeInflightBatch !== undefined
357
180
  ? { hasInFlightBatch: input.probeInflightBatch() }
358
181
  : input.inFlightBatch,
359
182
  force: input.force,
360
183
  bypassRedLine: input.bypassRedLine,
361
184
  mode,
362
- // tag through so `evaluateAutoCompactDecision` can apply the
363
- // source-aware carve-out for Mac's `transcript-estimate` signal.
185
+ // Tagged through so the decision can apply the source-aware carve-out for Mac's
186
+ // `transcript-estimate` signal.
364
187
  source: probe.source
365
188
  });
366
- if (!decision.shouldCompact) {
367
- // adapter's `postCompactDetectCommand`. A ratio that has fallen
368
- // back below the auto-fire threshold is the real, measured proof
369
- // that a previously-dispatched compact landed — so settle any run
370
- // still open at `compacting`. Nothing is written when there is no
371
- // open run, when the ratio is still high, or when the probe could
372
- // not measure at all.
373
- const settleRead = settleOpenLifecycleRun({
374
- projectRoot: input.projectRoot,
375
- sessionId,
376
- measuredRatio: probe.ratio,
377
- source: probe.source,
378
- autoFireThreshold: thresholdFor(mode, 'autoFire'),
379
- onLifecycleStage: input.onLifecycleStage,
380
- failLifecycleWrite: input.testHooks?.failLifecycleWrite
381
- });
382
- // Repair R6 (AC3): a settle whose lifecycle write FAILED has not settled the
383
- // run — the record is still resting at `armed` / `compacting`, so the next
384
- // probe finds the same run open and settles it again. Appending the
385
- // "observed compaction point" row anyway writes one row per probe for a
386
- // compaction the lifecycle store never recorded: an unbounded append driven
387
- // by a write that did not happen, and a history row whose paired lifecycle
388
- // record does not exist. Nothing is lost by deferring the row — `probe.ratio`
389
- // is re-measured on every probe, so the retry carries a fresh number.
390
- const settled = settleRead !== null && !settleRead.lifecycleWritten
391
- ? null
392
- : (settleRead ??
393
- // already have closed this run WITHOUT an honest post-compact number —
394
- // in which case the call above finds nothing open, and without this the
395
- // calibration pair stays blank for exactly the compactions the event path
396
- // exists to witness. Fills the number the event owed.
397
- fillEventSettledMeasurement({
398
- projectRoot: input.projectRoot,
399
- sessionId,
400
- measuredRatio: probe.ratio,
401
- source: probe.source
402
- }));
403
- // dispatched compact demonstrably landed. Append an `observed` row
404
- // carrying the measured ratio, so `peaks compact history` can show
405
- // "asked at R (intent) / landed by R' (observed)" once a real session has
406
- // run. Without this row the intent has nothing to be compared against.
407
- if (settled !== null) {
408
- appendObservedCompactEvent({
409
- projectRoot: input.projectRoot,
410
- sessionId,
411
- event: {
412
- schemaVersion: 1,
413
- kind: 'observed',
414
- ts: new Date().toISOString(),
415
- target: input.target ?? 'main',
416
- mode,
417
- ide: probe.ide,
418
- pathway: 'post-compact-probe',
419
- beforeRatio: settled.triggerRatio,
420
- afterRatio: probe.ratio,
421
- redLine: false,
422
- ok: true,
423
- checkpointPath: '',
424
- dispatchMessage: `post-compact probe measured ratio ${(probe.ratio * 100).toFixed(1)}% (source=${probe.source}) after the compact dispatched at ${(settled.triggerRatio * 100).toFixed(1)}%`,
425
- windowTokens: probe.capacityTokens ?? null,
426
- windowSource: probe.capacitySource ?? null
427
- }
428
- });
429
- }
430
- return {
431
- ok: true,
432
- code: decision.reason === 'in-flight-batch' ? 'AUTO_COMPACT_WAIT' : 'AUTO_COMPACT_SKIP',
433
- message: `${decision.trigger.kind === 'soft-warn'
434
- ? decision.trigger.message
435
- : decision.reason === 'in-flight-batch'
436
- ? `In-flight batch detected; deferring pre-compact (ratio=${(probe.ratio * 100).toFixed(1)}%); next probe will re-evaluate.`
437
- : `Context at ${(probe.ratio * 100).toFixed(1)}%; below the ${(thresholdFor(mode, 'autoFire') * 100).toFixed(0)}% auto-fire threshold (mode=${mode}).`}${
438
- // No compact was needed, but the sync may still have rewritten the
439
- // harness's settings (the first probe of a project always does). The
440
- // notice is appended ONLY for an actual write — the other actions'
441
- // sentences would be noise on every quiet probe — PLUS the one refusal
442
- // that is not quiet: a refused write that leaves peaks-loop's number and
443
- // the harness's pinned window disagreeing. That is the moment the ratio
444
- // stops describing the harness's trigger, so staying silent is exactly
445
- // the failure 要告知 exists to prevent.
446
- harnessWindow !== null &&
447
- harnessWindow !== undefined &&
448
- (harnessWindow.action === 'written' || harnessWindowSyncWarning(harnessWindow) !== null)
449
- ? ` ${describeHarnessWindowSync(harnessWindow)}`
450
- : ''}`,
451
- data: {
452
- sessionId,
453
- ratio: probe.ratio,
454
- source: probe.source,
455
- decision: decision.reason === 'in-flight-batch' ? 'in-flight-batch' : 'below-threshold',
456
- harnessWindow
457
- }
458
- };
459
- }
460
- const isRedLine = decision.reason === 'red-line';
461
- const now = input.now ?? new Date();
462
- // attempt.
463
- //
464
- // The decision above fires whenever the ratio is at or over the auto-fire
465
- // threshold, and the ratio does not come back down on its own — nothing in
466
- // peaks-loop can compact a running session, and the harness fires only at
467
- // its own red line. So "shouldCompact" was true on every probe, forever: one
468
- // real session's `compact-history.jsonl` holds 1075 dispatch rows, 444
469
- // checkpoints and ZERO compactions over 15.5 h — and the file was still
470
- // growing while this slice ran. Re-dispatching bought nothing, because the
471
- // `ide-native` dispatch only installs a PreToolUse hook and installing it
472
- // again is a documented no-op: 1074 of those 1075 rows say `already
473
- // installed` in their own `dispatchMessage`. It produced rows, not
474
- // compactions.
475
- //
476
- // The lifecycle store already knows whether an attempt is outstanding (see
477
- // `readOpenDispatchRun` — `armed`/`compacting` = dispatched, `completed`/
478
- // `failed` = over), so the gate needs no new state and no threshold: it is
479
- // keyed on the STAGE of the open run, never on a ratio, which is why it
480
- // survives any future realignment of the 0.65/0.70/0.85/0.95 table.
481
- //
482
- // What is NOT suppressed: the probe still measures and still reports. The
483
- // envelope carries the LIVE ratio plus the ratio the open ask was made at,
484
- // so "it crossed and it is still high, unanswered" stays legible on every
485
- // turn — the difference between a quiet signal and a silenced one.
486
- //
487
- // `force` (the `--force` test seam, "force compact at any ratio") outranks
488
- // the inference: an explicit instruction must not be silently reduced to a
489
- // no-op, which would make the published flag a lie.
490
- // Repair R6: `readOpenDispatchRun` answers in THREE states, not two. `none`
491
- // admits the dispatch; `unresolvable` means the backoff's question could not
492
- // be asked — the session id names no session directory, so no lifecycle
493
- // outstanding". Measured on ONE directory before this: a legal sid found the
494
- // open `armed` run and suppressed the dispatch; `./<legal sid>`, which joins
495
- // to the identical path, returned `null` and dispatched. A gate that reads a
496
- // string instead of the artifact the string names is the defect the whole
497
- // id axis exists to close, so an unanswerable question is NOT an admit: the
498
- // conservative direction is to leave the dispatch undone and say why.
499
- const openRun = readOpenDispatchRun({ projectRoot: input.projectRoot, sessionId });
500
- if (openRun.kind === 'unresolvable' && input.force !== true) {
501
- return {
502
- ok: true,
503
- code: 'AUTO_COMPACT_UNRESOLVED_SESSION',
504
- message: `Context at ${(probe.ratio * 100).toFixed(1)}%; a compact may be warranted, but this session's ` +
505
- `directory could not be resolved (${openRun.reason}), so whether a compact was already dispatched ` +
506
- `for this crossing cannot be answered. Not dispatching: an unanswered question is not a 'no'. ` +
507
- `The session id comes from the active binding or from the caller.`,
508
- data: {
509
- sessionId,
510
- ratio: probe.ratio,
511
- source: probe.source,
512
- decision: 'unresolved-session',
513
- harnessWindow
514
- }
515
- };
516
- }
517
- if (openRun.kind === 'open' && input.force !== true) {
518
- return {
519
- ok: true,
520
- code: 'AUTO_COMPACT_ALREADY_ARMED',
521
- message: `Context at ${(probe.ratio * 100).toFixed(1)}% — still above the ` +
522
- `${(thresholdFor(mode, 'autoFire') * 100).toFixed(0)}% auto-fire threshold (mode=${mode}); ` +
523
- `a compact was already dispatched for this crossing at ${(openRun.triggerRatio * 100).toFixed(1)}% ` +
524
- `(run ${openRun.runId}, resting at '${openRun.stage}') and nothing has compacted since. ` +
525
- `Not dispatching again: the trigger is already registered, so a second dispatch would install the ` +
526
- `same hook and add a checkpoint and a history row without adding a capability. ` +
527
- `Re-probe with \`peaks code context-now\`.`,
528
- data: {
529
- sessionId,
530
- ratio: probe.ratio,
531
- source: probe.source,
532
- decision: 'already-armed',
533
- armedAtRatio: openRun.triggerRatio,
534
- armedRunId: openRun.runId,
535
- harnessWindow
536
- }
537
- };
538
- }
539
- // committed to compacting, so the run is `queued`. One runId per
540
- // attempt; every later transition carries it forward.
541
- const lifecycle = new CompactLifecyclePublisher({
542
- projectRoot: input.projectRoot,
189
+ return {
190
+ input,
543
191
  sessionId,
544
- runId: newCompactRunId(now),
545
- triggerRatio: probe.ratio,
546
- redLine: isRedLine,
547
- onLifecycleStage: input.onLifecycleStage,
548
- failLifecycleWrite: input.testHooks?.failLifecycleWrite
549
- });
550
- lifecycle.advance('queued');
551
- let checkpointPath;
552
- let plan;
553
- let nextActions;
554
- try {
555
- // `preparing` covers checkpoint + convergence-plan + recovery writes.
556
- lifecycle.advance('preparing');
557
- if (input.testHooks?.failPreparing)
558
- throw new Error('disk full while writing checkpoint');
559
- checkpointPath = writePreCompactCheckpoint({
560
- projectRoot: input.projectRoot,
561
- sessionId,
562
- now,
563
- redLine: isRedLine
564
- });
565
- nextActions = isRedLine
566
- ? [
567
- 'RED-LINE: harness compact requested — sub-agent dispatch is NOT blocked; keep working',
568
- 'Post-compact resume picks up the convergence plan from auto-decisions.md',
569
- 'Next `peaks code context-now` probe will confirm the ratio dropped; if it keeps climbing and the harness has not compacted, report that to the user and hand control back',
570
- // also rewrites the harness's own settings, so it says so too. The
571
- // user accepted that write on the condition 要告知 — a write only
572
- // one of the two syncing commands reports is not a notice.
573
- describeHarnessWindowSync(harnessWindow)
574
- ]
575
- : [
576
- 'Pre-compact dispatched — IDE compact in progress (async)',
577
- 'Post-compact resume picks up the convergence plan from auto-decisions.md',
578
- 'Next `peaks code auto-compact` probe will confirm ratio dropped below 0.85',
579
- describeHarnessWindowSync(harnessWindow)
580
- ];
581
- plan = buildConvergencePlan({
582
- sessionId,
583
- projectRoot: input.projectRoot,
584
- ratio: probe.ratio,
585
- checkpointPath,
586
- nextActions,
587
- redLine: isRedLine
588
- });
589
- appendAutoDecisionLog({ projectRoot: input.projectRoot, sessionId, plan });
590
- }
591
- catch (error) {
592
- lifecycle.fail(error);
593
- // Preserve the original error contract: the caller gets the same
594
- // `AUTO_COMPACT_DISPATCH_FAILED` envelope shape it already handles,
595
- // not a thrown exception and not a widened type.
596
- return {
597
- ok: false,
598
- code: 'AUTO_COMPACT_DISPATCH_FAILED',
599
- message: `Auto-compact preparation failed before IDE dispatch: ${summarizeLifecycleError(error)}`,
600
- data: {
601
- sessionId,
602
- ratio: probe.ratio,
603
- source: probe.source,
604
- target: input.target ?? 'main',
605
- mode,
606
- redLineRequested: isRedLine,
607
- // Compatibility alias + its record. `redLineGated` shipped from
608
- // 2.13.0 to 4.0.46, so a consumer outside this repo reads it and must
609
- // not start receiving `undefined`; the record is what makes the
610
- // deprecation visible to that consumer. See the type.
611
- redLineGated: isRedLine,
612
- deprecatedFields: DEPRECATED_ENVELOPE_FIELDS,
613
- harnessWindow
614
- }
615
- };
616
- }
617
- const { dispatchIdeCompact } = await import('../context/auto-compact-dispatcher.js');
618
- const target = input.target ?? 'main';
619
- let dispatch;
620
- try {
621
- if (input.testHooks?.failCompacting)
622
- throw new Error('IDE dispatch exploded');
623
- // intent record so the next main-session LLM turn fires `/compact`
624
- // in-band. Without this record the LLM has no signal that the
625
- // orchestrator asked for compact; the dispatcher alone would have
626
- // been a no-op against the main Claude Code window.
627
- if (target === 'main') {
628
- writeMainSessionCompactIntent({
629
- projectRoot: input.projectRoot,
630
- sessionId,
631
- ratio: probe.ratio,
632
- redLine: isRedLine,
633
- now
634
- });
635
- }
636
- dispatch = await dispatchIdeCompact({
637
- projectRoot: input.projectRoot,
638
- sessionId,
639
- env: input.env,
640
- target
641
- });
642
- }
643
- catch (error) {
644
- // `compacting` is the phase this failure died in — a phase label,
645
- // not a claim that a compaction was in flight.
646
- lifecycle.fail(error, 'compacting');
647
- return {
648
- ok: false,
649
- code: 'AUTO_COMPACT_DISPATCH_FAILED',
650
- message: `Auto-compact checkpoint written but IDE dispatch threw: ${summarizeLifecycleError(error)}`,
651
- data: {
652
- sessionId,
653
- ratio: probe.ratio,
654
- source: probe.source,
655
- checkpointPath,
656
- convergencePlan: plan,
657
- target,
658
- mode,
659
- redLineRequested: isRedLine,
660
- redLineGated: isRedLine,
661
- deprecatedFields: DEPRECATED_ENVELOPE_FIELDS,
662
- harnessWindow
663
- }
664
- };
665
- }
666
- // chosen from what the dispatch ACTUALLY did, never from the hope
667
- // that it compacted. `ide-native` on claude-code only installs a
668
- // PreToolUse hook that fires at ratio ≥ 0.95 — below that nothing is
669
- // in flight and the honest stage is `armed` (see
670
- // `resolveDispatchedStage`). Writing `compacting` there published a
671
- // heartbeat that would never arrive, which is exactly what pinned
672
- // the statusline at `stalled` for 92 minutes in the field.
673
- if (dispatch.ok) {
674
- lifecycle.advance(resolveDispatchedStage({ pathway: dispatch.pathway, ratio: probe.ratio }));
675
- }
676
- else {
677
- // A dispatcher that returns `ok: false` did not compact anything —
678
- // record that as a failure at `compacting` rather than leaving the
679
- // run looking like it is still in progress.
680
- lifecycle.fail(new Error(dispatch.message), 'compacting');
681
- }
682
- // event so the new 'peaks compact history' CLI and the
683
- // 'peaks statusline compact' indicator have a record. The
684
- // append is best-effort: a write failure must NOT block the
685
- // compact return envelope.
192
+ mode,
193
+ probe,
194
+ harnessWindow,
195
+ decision,
196
+ isRedLine: decision.reason === 'red-line',
197
+ now: input.now ?? new Date()
198
+ };
199
+ }
200
+ /**
201
+ * Append the dispatch row the history CLI and the statusline indicator read. Best
202
+ * effort: a write failure must NOT block the compact return.
203
+ */
204
+ function appendDispatchHistory(ctx, checkpointPath, dispatch, target) {
205
+ const { input, sessionId, probe, mode, isRedLine, now } = ctx;
686
206
  try {
687
207
  appendCompactHistoryEvent({
688
208
  projectRoot: input.projectRoot,
@@ -710,46 +230,8 @@ export async function runAutoCompact(input) {
710
230
  catch {
711
231
  /* best-effort; do not fail the compact return */
712
232
  }
713
- return {
714
- ok: dispatch.ok,
715
- code: dispatch.ok
716
- ? isRedLine
717
- ? 'AUTO_COMPACT_RED_LINE'
718
- : 'AUTO_COMPACT_DISPATCHED'
719
- : 'AUTO_COMPACT_DISPATCH_FAILED',
720
- message: dispatch.ok
721
- ? isRedLine
722
- ? `RED-LINE: harness compact REQUESTED (${dispatch.ide} / ${dispatch.pathway} / target=${target} / mode=${mode} — ${describeMode(mode)}); checkpoint at ${checkpointPath}. Sub-agent dispatch is NOT blocked — keep working and re-probe with \`peaks code context-now\`.`
723
- : `Auto-compact dispatched (${dispatch.ide} / ${dispatch.pathway} / target=${target} / mode=${mode} — ${describeMode(mode)}); checkpoint at ${checkpointPath}.`
724
- : `Auto-compact checkpoint written but IDE dispatch failed: ${dispatch.message}`,
725
- data: {
726
- sessionId,
727
- ratio: probe.ratio,
728
- source: probe.source,
729
- checkpointPath,
730
- convergencePlan: plan,
731
- dispatch,
732
- target,
733
- mode,
734
- redLineRequested: isRedLine,
735
- redLineGated: isRedLine,
736
- deprecatedFields: DEPRECATED_ENVELOPE_FIELDS,
737
- harnessWindow
738
- }
739
- };
740
233
  }
741
- export function appendCompactHistoryEvent(input) {
742
- const dir = getSessionDir(input.projectRoot, input.sessionId);
743
- if (!existsSync(dir))
744
- mkdirSync(dir, { recursive: true });
745
- const path = join(dir, 'compact-history.jsonl');
746
- appendFileSync(path, JSON.stringify(input.event) + '\n', 'utf8');
747
- }
748
- /**
749
- * Best-effort variant for the post-compact measurement row. Telemetry must
750
- * never change the probe's return envelope, so an append failure here is
751
- * swallowed — same discipline as the dispatch-path append.
752
- */
234
+ /** Best-effort variant for the post-compact measurement row. Telemetry must never change the probe's return envelope. */
753
235
  function appendObservedCompactEvent(input) {
754
236
  try {
755
237
  appendCompactHistoryEvent(input);
@@ -758,6 +240,3 @@ function appendObservedCompactEvent(input) {
758
240
  /* best-effort; the probe result is already settled */
759
241
  }
760
242
  }
761
- // Keep dirname import live for symmetry with sibling services that
762
- // use it for path joins; tree-shaking removes it in builds.
763
- void dirname;