@iowarp/clio-coder 0.5.0 → 0.5.2

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 (312) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/CHANGELOG.md +35 -0
  3. package/CONTRIBUTING.md +9 -0
  4. package/README.md +12 -9
  5. package/ROADMAP.md +30 -5
  6. package/dist/{acp-KS7ARGV6.js → acp-F6PGOFZ4.js} +2 -2
  7. package/dist/{agents-YOGJ2YEJ.js → agents-ZHP4R6EX.js} +22 -21
  8. package/dist/assets/codewiki.json +1 -1
  9. package/dist/{auth-CEDCO6O6.js → auth-N75U2H3Z.js} +4 -4
  10. package/dist/{chunk-D3KWFOUT.js → chunk-23UIXH6Z.js} +3 -3
  11. package/dist/{chunk-ZR5YA6ZF.js → chunk-36XVK5P5.js} +5 -5
  12. package/dist/{chunk-AVSR2HKI.js → chunk-3DZ4LPLS.js} +6 -6
  13. package/dist/{chunk-LYJJ6546.js → chunk-3GSPCWFX.js} +12 -12
  14. package/dist/{chunk-CRHPCPZP.js → chunk-4KC4CHKJ.js} +2 -2
  15. package/dist/{chunk-4VDHLCF5.js → chunk-4WJBWKMI.js} +2 -2
  16. package/dist/{chunk-KBUTSJSR.js → chunk-4XBYHXGK.js} +2 -2
  17. package/dist/{chunk-6W75D37F.js → chunk-53P775GE.js} +6 -3
  18. package/dist/{chunk-XZWSP67B.js → chunk-5APYAXCB.js} +2 -2
  19. package/dist/{chunk-D65E25JG.js → chunk-67HPYNTJ.js} +2 -2
  20. package/dist/{chunk-5JQR5B4D.js → chunk-6KV4KCQZ.js} +4 -4
  21. package/dist/{chunk-V4XJWDHR.js → chunk-6UKFRTXI.js} +2 -1
  22. package/dist/{chunk-NDPF2N2L.js → chunk-7DW7CJW2.js} +2 -2
  23. package/dist/{chunk-5KQEUIWM.js → chunk-7LUEQODV.js} +4 -4
  24. package/dist/{chunk-LNOXCGN2.js → chunk-7UFDIOBT.js} +3 -3
  25. package/dist/{chunk-DXSETN72.js → chunk-7WUGNN43.js} +2 -2
  26. package/dist/{chunk-V35KZDUZ.js → chunk-AA3GRZWS.js} +2 -2
  27. package/dist/{chunk-X4CEFOSG.js → chunk-ADO3OQIC.js} +4 -4
  28. package/dist/{chunk-UX2L5CUD.js → chunk-AI36V3ON.js} +3 -3
  29. package/dist/{chunk-B7GY6QER.js → chunk-B26OUMJW.js} +2 -2
  30. package/dist/{chunk-LAR7DX5E.js → chunk-B5TASTCK.js} +153 -62
  31. package/dist/{chunk-TGWZXJ7L.js → chunk-BEMQK5Q7.js} +92 -84
  32. package/dist/{chunk-RNDCROSG.js → chunk-CNXU7TR2.js} +3 -3
  33. package/dist/{chunk-3JLTRPN7.js → chunk-CVMUXT64.js} +35 -66
  34. package/dist/{chunk-733ZBT5P.js → chunk-E74MJF6G.js} +4 -4
  35. package/dist/{chunk-T2MDZES3.js → chunk-EBMTBE76.js} +3 -3
  36. package/dist/{chunk-OZ76TLGU.js → chunk-EEITU7S6.js} +2 -2
  37. package/dist/{chunk-GRHOXDKB.js → chunk-ELDJWJKM.js} +97 -14
  38. package/dist/{chunk-GCQFN7YI.js → chunk-EMYEQLWO.js} +2 -2
  39. package/dist/{chunk-LTWH2ACR.js → chunk-EQPFEDFV.js} +222 -127
  40. package/dist/{chunk-TDGYTWM6.js → chunk-F4I3H56H.js} +2 -2
  41. package/dist/chunk-F6KCJO3U.js +523 -0
  42. package/dist/{chunk-FGSQHUCK.js → chunk-F6VKWTTY.js} +5 -5
  43. package/dist/{chunk-UJD5MPP6.js → chunk-FLNXQQ5B.js} +2 -2
  44. package/dist/{chunk-EX57VVWW.js → chunk-FOSRJ2VZ.js} +4 -4
  45. package/dist/{chunk-ZARZ4POR.js → chunk-G5ZDQQE3.js} +3 -3
  46. package/dist/{chunk-HQVN7G4F.js → chunk-G62JBPXN.js} +7 -7
  47. package/dist/{chunk-ZJPORZTC.js → chunk-GIAI7A2K.js} +2 -2
  48. package/dist/chunk-GPTR6OTU.js +2551 -0
  49. package/dist/{chunk-EZD2BXGP.js → chunk-GUFQPTQK.js} +2 -2
  50. package/dist/{chunk-OPD7GL6F.js → chunk-H6FQVNZC.js} +8 -7
  51. package/dist/chunk-H7REJ7L5.js +80 -0
  52. package/dist/{chunk-EYTLV3W3.js → chunk-HTWTORF7.js} +2 -2
  53. package/dist/{chunk-DF6PI6GN.js → chunk-I6UKQPGS.js} +3 -3
  54. package/dist/{chunk-ISHUS7HC.js → chunk-JRW236JT.js} +3 -3
  55. package/dist/{chunk-DE2LU267.js → chunk-KKAJGT3B.js} +3 -2
  56. package/dist/{chunk-OV62D6KO.js → chunk-KQMYL5GR.js} +3 -3
  57. package/dist/{chunk-QDUUOZ3S.js → chunk-KWMFC7FN.js} +3 -3
  58. package/dist/{chunk-R7FGRZNF.js → chunk-LXYHCCIZ.js} +9 -9
  59. package/dist/{chunk-NFSK2VNU.js → chunk-LZMTVVN4.js} +2 -2
  60. package/dist/{chunk-62UK7AGW.js → chunk-M5PMRZSR.js} +397 -44
  61. package/dist/chunk-MMZMM6SW.js +131 -0
  62. package/dist/{chunk-FFYTC2KQ.js → chunk-MNABNEBX.js} +2 -2
  63. package/dist/{chunk-XMDBTYCH.js → chunk-N3CWOGVF.js} +3 -3
  64. package/dist/{chunk-VYRQHORQ.js → chunk-NMQSO6Z6.js} +4 -3
  65. package/dist/{chunk-R2UUNSNQ.js → chunk-O3ZAF7UY.js} +18 -5
  66. package/dist/{chunk-4SYHGVDE.js → chunk-OQNCLZI4.js} +4 -4
  67. package/dist/{chunk-EG4ARXPC.js → chunk-OX6QTA4N.js} +3 -3
  68. package/dist/{chunk-P4GNZPXO.js → chunk-OYUS3UZP.js} +2 -2
  69. package/dist/{chunk-OQ2EOAHA.js → chunk-PF36KOGI.js} +291 -112
  70. package/dist/{chunk-55RMDDFM.js → chunk-PGQG7ZMW.js} +2 -2
  71. package/dist/{chunk-CBAQPTDU.js → chunk-PIU6BXXW.js} +2 -2
  72. package/dist/{chunk-EKV4UBCM.js → chunk-PZBQUJ2F.js} +2 -2
  73. package/dist/{chunk-7RET77VB.js → chunk-Q2HKY32Y.js} +2 -2
  74. package/dist/{chunk-U6YTUVOX.js → chunk-Q4RMNWMZ.js} +4 -4
  75. package/dist/chunk-Q6FRR3AQ.js +48 -0
  76. package/dist/{chunk-6RJMYYIE.js → chunk-QJSIAJBY.js} +3 -3
  77. package/dist/{chunk-DOUISWEE.js → chunk-QOJ4ERMU.js} +5 -5
  78. package/dist/{chunk-IFPDAD7R.js → chunk-RWSRKXLT.js} +3 -3
  79. package/dist/{chunk-T33IYTZM.js → chunk-SAASVMIY.js} +3 -3
  80. package/dist/{chunk-2DTIWSAW.js → chunk-SEQDH6JC.js} +3 -3
  81. package/dist/{chunk-BRCOSE7O.js → chunk-SZUO5BSE.js} +2 -2
  82. package/dist/{chunk-HOEDA42N.js → chunk-T7HZ7KJF.js} +2 -2
  83. package/dist/{chunk-OJXOA6YU.js → chunk-TL6LNERH.js} +2 -2
  84. package/dist/{chunk-UCINIRHW.js → chunk-TPCZXWLT.js} +3 -3
  85. package/dist/{chunk-ZUKUCZYZ.js → chunk-TQ2KTH4A.js} +2 -2
  86. package/dist/{chunk-4WOOTKFD.js → chunk-U3FETNQB.js} +341 -20
  87. package/dist/{chunk-5B3RTYAK.js → chunk-UGDSB4AN.js} +2 -2
  88. package/dist/{chunk-VKHLUZNO.js → chunk-UZ7YBL43.js} +3 -3
  89. package/dist/{chunk-QNUQ7K7D.js → chunk-VKMSEO7Y.js} +4 -4
  90. package/dist/{chunk-IN7DGBVS.js → chunk-VLX5VZ35.js} +5 -5
  91. package/dist/{chunk-SJGS3GDI.js → chunk-WAGBMMNX.js} +5 -5
  92. package/dist/{chunk-BFOSV5EZ.js → chunk-WX2YCH7F.js} +2 -2
  93. package/dist/{chunk-3LT34CAM.js → chunk-XBIGUILU.js} +32 -17
  94. package/dist/{chunk-XDHUDE5K.js → chunk-XKYBFRWR.js} +5 -5
  95. package/dist/{chunk-U5QU5ZOD.js → chunk-XYPWFSU5.js} +3 -3
  96. package/dist/{chunk-YBUECLAF.js → chunk-YEJQDODA.js} +2 -2
  97. package/dist/{chunk-YQ6XEFVK.js → chunk-Z74OGONW.js} +39 -7
  98. package/dist/{chunk-I4ELN5BX.js → chunk-ZTNOEJRI.js} +2 -2
  99. package/dist/cli/index.js +26 -26
  100. package/dist/{clio-K2PBVCEM.js → clio-4IKFE25F.js} +2 -2
  101. package/dist/{clio-context-tools-GR4OHFSO.js → clio-context-tools-OHJXYX4R.js} +25 -23
  102. package/dist/{code-nav-4X3OI6DK.js → code-nav-XPM3MY5I.js} +7 -7
  103. package/dist/{config-Q5J7UBAB.js → config-343YS25T.js} +43 -41
  104. package/dist/{config-graph-P3555574.js → config-graph-WI263KDX.js} +43 -41
  105. package/dist/{configure-KIJ32ZO6.js → configure-GZDG4HER.js} +21 -21
  106. package/dist/{context-LFBQOSXT.js → context-4OFO7N7Y.js} +12 -12
  107. package/dist/{context-WBH3KSVR.js → context-JLK2RLOG.js} +42 -40
  108. package/dist/{context-NNLBES2Y.js → context-O6DIWCF6.js} +23 -21
  109. package/dist/{context-clear-YOHMQ6SQ.js → context-clear-MR7KOGQI.js} +42 -40
  110. package/dist/{context-working-set-MRAEVDNV.js → context-working-set-YZAD3KWO.js} +14 -13
  111. package/dist/{data-tool-JJPBGMQJ.js → data-tool-EKD7DFHG.js} +7 -7
  112. package/dist/{detail-RHJOTGN4.js → detail-YZPHFEVJ.js} +43 -41
  113. package/dist/{dispatch-runner-NC7PYNIO.js → dispatch-runner-VKVXVTWJ.js} +44 -42
  114. package/dist/{doctor-S5KZ2GTN.js → doctor-53AVQXAO.js} +17 -16
  115. package/dist/{doctor-deep-2EJQSHIZ.js → doctor-deep-BLRB2NCG.js} +5 -5
  116. package/dist/{eval-3D6M36N7.js → eval-P3BQSLQE.js} +21 -20
  117. package/dist/{evidence-XNVPPLVK.js → evidence-6LLI6SZH.js} +44 -42
  118. package/dist/{evidence-6IYN52ET.js → evidence-HTN3JLUO.js} +42 -40
  119. package/dist/{evidence-JVECPI3F.js → evidence-NJRE3R5H.js} +42 -40
  120. package/dist/{evolve-TLPUDJDM.js → evolve-WWZHJVJP.js} +42 -40
  121. package/dist/{fleet-WKVBF5SZ.js → fleet-GGZ4BVJE.js} +64 -62
  122. package/dist/{fleet-6RJWIOEO.js → fleet-QG3IIWWM.js} +44 -42
  123. package/dist/{fleet-commands-L6WFT7KZ.js → fleet-commands-J2ZIKISQ.js} +7 -7
  124. package/dist/{fleet-decisions-BTQMWU5A.js → fleet-decisions-Y2Q7375Q.js} +8 -8
  125. package/dist/{fleet-graph-553DZ67Q.js → fleet-graph-TCBCCCBP.js} +11 -11
  126. package/dist/{fleet-inspect-236FZPRB.js → fleet-inspect-KOLKBC56.js} +44 -42
  127. package/dist/{fleet-preflight-NZQHABWO.js → fleet-preflight-44UNCC4Y.js} +27 -25
  128. package/dist/{fleet-validate-7NLAE26R.js → fleet-validate-K2FXRZVA.js} +13 -13
  129. package/dist/{fleet-verify-E3RO6A5W.js → fleet-verify-AS444WDW.js} +42 -40
  130. package/dist/{fleet-view-KMZQY7YJ.js → fleet-view-X4JU4J6O.js} +43 -41
  131. package/dist/gui/ops-worker.js +9 -9
  132. package/dist/gui/reads-worker.js +9 -8
  133. package/dist/{init-DV5B6HQG.js → init-VSOWFHVL.js} +54 -52
  134. package/dist/{interactive-OXGYZYAS.js → interactive-JRCBENLZ.js} +1733 -577
  135. package/dist/{interop-J7UOBOSF.js → interop-JR6BCY7R.js} +10 -10
  136. package/dist/{inventory-OEGIL6PY.js → inventory-3JUGLVJI.js} +43 -41
  137. package/dist/{library-ROZAMUIL.js → library-7BOH3Y7N.js} +15 -15
  138. package/dist/{library-B3UPGY4V.js → library-7PFONXUI.js} +9 -9
  139. package/dist/{library-MXQA5DLT.js → library-TUYZMFIA.js} +7 -7
  140. package/dist/{library-import-2LTWOVBD.js → library-import-TJVXKLDK.js} +10 -10
  141. package/dist/{library-inventory-LJ3XHLWO.js → library-inventory-MD3AYTP4.js} +9 -9
  142. package/dist/{library-validation-DARHHUNW.js → library-validation-ZDLITEA3.js} +7 -7
  143. package/dist/{mcp-2GSPYQ54.js → mcp-EFCWUPUR.js} +3 -3
  144. package/dist/{memory-J3STUUIK.js → memory-Q2YVOKK5.js} +42 -40
  145. package/dist/{models-Y3GMFJ2V.js → models-M7BOCW7F.js} +17 -16
  146. package/dist/{monitor-LHNH577J.js → monitor-2HD3MC67.js} +47 -45
  147. package/dist/{orchestrator-DTAPXULA.js → orchestrator-OUW3ZN6Y.js} +1894 -226
  148. package/dist/{panes-A743ONZ5.js → panes-TTPPSYCL.js} +3 -3
  149. package/dist/{preload-OJBMAIXF.js → preload-A47C2NUF.js} +42 -40
  150. package/dist/{providers-CMETPZNS.js → providers-T43W7EL4.js} +4 -4
  151. package/dist/{resources-FY6XYFN2.js → resources-TP6X2V6W.js} +20 -11
  152. package/dist/{run-H64MEVTA.js → run-LDSBPMCS.js} +61 -59
  153. package/dist/{share-CAFMMQLM.js → share-URHKKMHR.js} +9 -9
  154. package/dist/{skills-ERKQUC7L.js → skills-E477MOOO.js} +13 -11
  155. package/dist/{skills-eval-CQ23MYQK.js → skills-eval-AIBR2L7H.js} +583 -93
  156. package/dist/{skills-inventory-TVUYJDVN.js → skills-inventory-HLMKY3R5.js} +13 -11
  157. package/dist/{slash-commands-ROG3KIFJ.js → slash-commands-LIIZKA5Y.js} +28 -26
  158. package/dist/{startup-background-KDWCQMWT.js → startup-background-JR2KDK6R.js} +43 -41
  159. package/dist/{steer-KKYXJSH4.js → steer-D7RTAPCM.js} +3 -3
  160. package/dist/{system-ZDW44ZGX.js → system-X2P5N4IY.js} +14 -13
  161. package/dist/{targets-AJWA6E7Z.js → targets-VFFAKUFM.js} +25 -24
  162. package/dist/{tasks-6L2JJRC5.js → tasks-ERAWZA22.js} +6 -6
  163. package/dist/{terminal-lease-YFUSDPAD.js → terminal-lease-V2N2BIKQ.js} +2 -2
  164. package/dist/{trace-LA5MVR4Y.js → trace-MA5BT7XY.js} +3 -3
  165. package/dist/{usage-XSILDHKF.js → usage-WYFY2L6M.js} +50 -48
  166. package/dist/{verifiers-ECA5GIHL.js → verifiers-TYIW2XKF.js} +7 -7
  167. package/dist/{verify-R6QQCCEB.js → verify-242UFYTP.js} +6 -6
  168. package/dist/{web-fetch-PVZNMBOB.js → web-fetch-LRAGYU6Y.js} +3 -3
  169. package/dist/{wiki-generate-RP22WP5K.js → wiki-generate-OFKOX2T6.js} +53 -51
  170. package/dist/worker/entry.js +31 -29
  171. package/docs/architecture/architecture.md +1 -0
  172. package/docs/architecture/context-engine.md +4 -2
  173. package/docs/architecture/observability.md +6 -4
  174. package/docs/architecture/prompt-envelope-and-tools.md +1 -1
  175. package/docs/architecture/session-lifecycle.md +1 -1
  176. package/docs/architecture/tui-design.md +12 -9
  177. package/docs/gui/parity/02-slash-and-surfaces.md +1 -1
  178. package/docs/guide/commands-and-modes.md +91 -5
  179. package/docs/guide/configuration-and-targets.md +2 -2
  180. package/docs/guide/context-continuity.md +49 -0
  181. package/docs/guide/proactive-memory.md +6 -3
  182. package/library/registry.yaml +292 -32
  183. package/library/skills/README.md +27 -2
  184. package/library/skills/context/context-handoff/SKILL.md +10 -4
  185. package/library/skills/meta/clio-coder-dev/SKILL.md +92 -88
  186. package/library/skills/meta/clio-coder-dev/evals.md +51 -46
  187. package/library/skills/meta/clio-coder-dev/plugin.json +2 -2
  188. package/library/skills/meta/clio-coder-dev/references/change-map.md +49 -0
  189. package/library/skills/meta/clio-coder-test/SKILL.md +89 -142
  190. package/library/skills/meta/clio-coder-test/evals.md +58 -62
  191. package/library/skills/meta/clio-coder-test/plugin.json +2 -2
  192. package/library/skills/meta/clio-coder-test/references/harness.md +3 -3
  193. package/library/skills/meta/clio-coder-test/references/lifecycle-validation.md +30 -0
  194. package/library/skills/meta/clio-coder-test/references/test-map.md +60 -89
  195. package/library/skills/registry.yaml +5 -5
  196. package/library/skills/skill-marketplace.json +12 -14
  197. package/package.json +1 -1
  198. package/src/cli/skills-eval.ts +1064 -54
  199. package/src/cli/usage.ts +2 -2
  200. package/src/core/clio-repo.ts +3 -0
  201. package/src/core/tool-names.ts +1 -0
  202. package/src/domains/context/budget/inspection.ts +9 -0
  203. package/src/domains/context/budget/live-view.ts +418 -0
  204. package/src/domains/context/budget/pressure.ts +393 -0
  205. package/src/domains/context/budget/request-fit.ts +15 -0
  206. package/src/domains/evidence/build.ts +24 -0
  207. package/src/domains/gateway/mcp/client.ts +1 -1
  208. package/src/domains/memory/commit-state.ts +160 -0
  209. package/src/domains/memory/operations.ts +18 -10
  210. package/src/domains/memory/prompt-cache.ts +95 -0
  211. package/src/domains/memory/prompt-section.ts +50 -7
  212. package/src/domains/memory/relevance.ts +93 -0
  213. package/src/domains/memory/restoration.ts +78 -0
  214. package/src/domains/memory/store.ts +39 -1
  215. package/src/domains/middleware/memory-intervention.ts +116 -24
  216. package/src/domains/observability/background-memory-usage.ts +1 -1
  217. package/src/domains/observability/cost.ts +3 -3
  218. package/src/domains/observability/extension.ts +1 -1
  219. package/src/domains/observability/metrics.ts +1 -1
  220. package/src/domains/prompts/compiler.ts +1 -1
  221. package/src/domains/prompts/extension.ts +38 -1
  222. package/src/domains/quota/anthropic-max-provider.ts +134 -0
  223. package/src/domains/quota/anthropic-usage.ts +220 -0
  224. package/src/domains/quota/antigravity-provider.ts +339 -0
  225. package/src/domains/quota/cache.ts +87 -0
  226. package/src/domains/quota/claude-code-provider.ts +169 -0
  227. package/src/domains/quota/codex-provider.ts +237 -0
  228. package/src/domains/quota/presentation.ts +240 -0
  229. package/src/domains/quota/registry.ts +23 -0
  230. package/src/domains/quota/service.ts +93 -0
  231. package/src/domains/quota/summary-feed.ts +86 -0
  232. package/src/domains/quota/types.ts +84 -0
  233. package/src/domains/resources/index.ts +11 -0
  234. package/src/domains/resources/skills/catalog-view.ts +571 -0
  235. package/src/domains/resources/skills/lexical-match.ts +136 -0
  236. package/src/domains/resources/skills/loader.ts +38 -0
  237. package/src/domains/resources/skills/promotion.ts +1 -55
  238. package/src/domains/resources/skills/provenance-pin.ts +50 -20
  239. package/src/domains/safety/action-classifier.ts +1 -0
  240. package/src/domains/session/compaction/branch-summary.ts +4 -1
  241. package/src/domains/session/compaction/compact.ts +18 -2
  242. package/src/domains/session/compaction/cut-point.ts +20 -1
  243. package/src/domains/session/compaction/tokens.ts +48 -3
  244. package/src/domains/session/context-accounting.ts +8 -1
  245. package/src/domains/session/continuity/carry.ts +59 -0
  246. package/src/domains/session/continuity/contract.ts +592 -0
  247. package/src/domains/session/continuity/evidence.ts +290 -0
  248. package/src/domains/session/continuity/fold.ts +1075 -0
  249. package/src/domains/session/continuity/note.ts +79 -0
  250. package/src/domains/session/continuity/operator-request.ts +104 -0
  251. package/src/domains/session/continuity/persistence.ts +408 -0
  252. package/src/domains/session/continuity/ports.ts +231 -0
  253. package/src/domains/session/continuity/projection.ts +538 -0
  254. package/src/domains/session/continuity/validate.ts +352 -0
  255. package/src/domains/session/entries.ts +50 -3
  256. package/src/domains/session/index.ts +48 -0
  257. package/src/domains/session/migrations/index.ts +7 -3
  258. package/src/domains/session/tree/fork.ts +15 -1
  259. package/src/domains/session/usage.ts +2 -2
  260. package/src/engine/acp/commands.ts +1 -1
  261. package/src/engine/agent.ts +133 -9
  262. package/src/engine/session.ts +13 -6
  263. package/src/entry/orchestrator.ts +116 -33
  264. package/src/interactive/chat-loop-messages.ts +2 -2
  265. package/src/interactive/chat-loop.ts +219 -63
  266. package/src/interactive/chat-renderer.ts +99 -8
  267. package/src/interactive/context-overlay.ts +24 -3
  268. package/src/interactive/continuity-controller.ts +526 -0
  269. package/src/interactive/dispatch-board.ts +21 -3
  270. package/src/interactive/footer/dashboard.ts +25 -1
  271. package/src/interactive/footer/key-hints.ts +2 -2
  272. package/src/interactive/footer/pages.ts +70 -27
  273. package/src/interactive/footer/widgets.ts +32 -7
  274. package/src/interactive/footer-panel.ts +1 -1
  275. package/src/interactive/interactive-application.ts +7 -2
  276. package/src/interactive/interactive-input-runtime.ts +2 -2
  277. package/src/interactive/interactive-presentation.ts +15 -0
  278. package/src/interactive/interactive-slash-runtime.ts +45 -8
  279. package/src/interactive/interactive-tickers.ts +7 -1
  280. package/src/interactive/model-session-replay.ts +130 -3
  281. package/src/interactive/output-reserve.ts +35 -0
  282. package/src/interactive/overlay-general-openers.ts +12 -8
  283. package/src/interactive/overlay-key-routing.ts +6 -9
  284. package/src/interactive/overlay-lifecycle.ts +10 -6
  285. package/src/interactive/overlay-session-lifecycle.ts +21 -14
  286. package/src/interactive/quota-view.ts +229 -0
  287. package/src/interactive/session-last-turn.ts +1 -1
  288. package/src/interactive/session-transcript.ts +2 -1
  289. package/src/interactive/session-usage-reseed.ts +2 -2
  290. package/src/interactive/side-question.ts +2 -2
  291. package/src/interactive/slash-commands.ts +28 -9
  292. package/src/interactive/turn-context.ts +686 -63
  293. package/src/interactive/turn-middleware.ts +39 -6
  294. package/src/interactive/turn-persistence.ts +15 -2
  295. package/src/interactive/turn-prewarm.ts +1 -1
  296. package/src/interactive/turn-runtime.ts +48 -5
  297. package/src/interactive/{cost-overlay.ts → usage-overlay.ts} +121 -33
  298. package/src/interactive/welcome-dashboard.ts +26 -3
  299. package/src/tools/agent-tools.ts +3 -1
  300. package/src/tools/bootstrap.ts +6 -0
  301. package/src/tools/builtin-tool-catalog.ts +9 -0
  302. package/src/tools/context/index.ts +148 -113
  303. package/src/tools/context/surface.ts +11 -7
  304. package/src/tools/core-bootstrap.ts +4 -0
  305. package/src/tools/observation.ts +8 -2
  306. package/src/tools/policy.ts +3 -0
  307. package/src/tools/self-compact.ts +31 -0
  308. package/src/tools/surface.ts +1 -0
  309. package/src/tools/tasks.ts +1 -1
  310. package/dist/chunk-CTFPFW3H.js +0 -44
  311. package/dist/chunk-EOJPDNUP.js +0 -1325
  312. package/dist/chunk-I4Y4WKZR.js +0 -386
@@ -1,4 +1,9 @@
1
- import { createInitialSystemMessage, toToolDeclaration } from "@earendil-works/pi-ai";
1
+ import {
2
+ type AssistantMessage,
3
+ createAssistantMessageEventStream,
4
+ createInitialSystemMessage,
5
+ toToolDeclaration,
6
+ } from "@earendil-works/pi-ai";
2
7
  import { resolvedRequestContext } from "./context.js";
3
8
  /**
4
9
  * Thin wrapper over Clio's engine Agent class.
@@ -13,12 +18,33 @@ import { resolvedRequestContext } from "./context.js";
13
18
  * Pi's resolved prompt/tool request view through streamFn.
14
19
  */
15
20
 
16
- import { Agent, type AgentOptions, type StreamFn } from "@earendil-works/pi-agent-core";
21
+ import {
22
+ Agent,
23
+ type AgentMessage,
24
+ type AgentOptions,
25
+ type BeforeToolCallContext,
26
+ type StreamFn,
27
+ } from "@earendil-works/pi-agent-core";
17
28
  import { isDispositionedToolResultError } from "../tools/result-disposition.js";
18
29
  import { engineStreamSimple } from "./api-registry.js";
19
30
 
20
31
  export type EngineStreamFn = (...args: Parameters<typeof engineStreamSimple>) => ReturnType<StreamFn>;
21
32
 
33
+ /** Pi's settled-turn port, after all awaited result listeners. Replace context, not operator messages. */
34
+ export type EnginePrepareNextTurnContext = Parameters<NonNullable<AgentOptions["prepareNextTurnWithContext"]>>[0];
35
+ export type EnginePrepareNextTurnUpdate = Awaited<ReturnType<NonNullable<AgentOptions["prepareNextTurnWithContext"]>>>;
36
+ export type EngineToolBatchContext = Pick<BeforeToolCallContext, "assistantMessage" | "context">;
37
+ export interface EngineToolBatchRejection {
38
+ reason: string;
39
+ }
40
+ export interface EngineStreamRequest {
41
+ model: Parameters<StreamFn>[0];
42
+ /** Actual normalized transcript, after conversion and the final steering drain. Inspect only. */
43
+ context: Parameters<StreamFn>[1];
44
+ options: Parameters<StreamFn>[2];
45
+ }
46
+ export type EngineStreamAdmission = { block: true; reason: string } | { block: false; correlationId?: string };
47
+
22
48
  export type EngineAgentOptions = Omit<AgentOptions, "streamFn"> & {
23
49
  /** Legacy/custom request view. Explicit overrides also replace the native hook in tests. */
24
50
  streamFn?: EngineStreamFn;
@@ -26,11 +52,63 @@ export type EngineAgentOptions = Omit<AgentOptions, "streamFn"> & {
26
52
  transcriptStreamFn?: StreamFn;
27
53
  /** Called immediately before each native stream delegate invocation. */
28
54
  onStreamInvocation?: () => void;
55
+ /** One fail-closed decision per assistant batch, before any valid sibling's per-call guard. */
56
+ beforeToolBatch?: (
57
+ context: EngineToolBatchContext,
58
+ signal?: AbortSignal,
59
+ ) => EngineToolBatchRejection | undefined | Promise<EngineToolBatchRejection | undefined>;
60
+ /** Synchronous final admission: no suspension, reduction, request mutation, or model invocation. */
61
+ beforeStreamRequest?: (request: EngineStreamRequest) => EngineStreamAdmission;
29
62
  };
30
63
 
31
64
  export interface EngineAgentHandle {
32
65
  agent: Agent;
33
66
  state(): Agent["state"];
67
+ /** Exact emitted assistant object only. In-memory attribution, never proof of success or durability. */
68
+ requestCorrelationId(message: AgentMessage): string | undefined;
69
+ }
70
+
71
+ function batchAwareBeforeToolCall(
72
+ batch: NonNullable<EngineAgentOptions["beforeToolBatch"]>,
73
+ delegate: AgentOptions["beforeToolCall"],
74
+ ): NonNullable<AgentOptions["beforeToolCall"]> {
75
+ const decisions = new WeakMap<AssistantMessage, Promise<EngineToolBatchRejection | undefined>>();
76
+ return async (context, signal) => {
77
+ let decision = decisions.get(context.assistantMessage);
78
+ if (!decision) {
79
+ decision = Promise.resolve()
80
+ .then(() => batch({ assistantMessage: context.assistantMessage, context: context.context }, signal))
81
+ .catch(() => ({ reason: "Tool batch admission failed" }));
82
+ decisions.set(context.assistantMessage, decision);
83
+ }
84
+ const rejection = await decision;
85
+ if (rejection) return { block: true, reason: rejection.reason.slice(0, 1024) || "Tool batch rejected" };
86
+ return delegate?.(context, signal);
87
+ };
88
+ }
89
+
90
+ function refusedStream(request: EngineStreamRequest, reason: string, aborted = false): ReturnType<StreamFn> {
91
+ const stream = createAssistantMessageEventStream();
92
+ const message: AssistantMessage = {
93
+ role: "assistant",
94
+ content: [],
95
+ api: request.model.api,
96
+ provider: request.model.provider,
97
+ model: request.model.id,
98
+ usage: {
99
+ input: 0,
100
+ output: 0,
101
+ cacheRead: 0,
102
+ cacheWrite: 0,
103
+ totalTokens: 0,
104
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
105
+ },
106
+ stopReason: aborted ? "aborted" : "error",
107
+ errorMessage: reason.slice(0, 1024) || "Request admission refused",
108
+ timestamp: Date.now(),
109
+ };
110
+ stream.push({ type: "error", reason: aborted ? "aborted" : "error", error: message });
111
+ return stream;
34
112
  }
35
113
 
36
114
  function dispositionAwareAfterToolCall(
@@ -46,17 +124,53 @@ function dispositionAwareAfterToolCall(
46
124
  }
47
125
 
48
126
  export function createEngineAgent(options: EngineAgentOptions = {}): EngineAgentHandle {
49
- const { streamFn, transcriptStreamFn = engineStreamSimple, onStreamInvocation, ...agentOptions } = options;
127
+ const {
128
+ streamFn,
129
+ transcriptStreamFn = engineStreamSimple,
130
+ onStreamInvocation,
131
+ beforeToolBatch,
132
+ beforeStreamRequest,
133
+ ...agentOptions
134
+ } = options;
135
+ const correlations = new WeakMap<AgentMessage, string>();
136
+ let activeCorrelationId: string | undefined;
137
+ const invoke: StreamFn = (model, context, streamOptions) => {
138
+ onStreamInvocation?.();
139
+ return streamFn
140
+ ? streamFn(model, resolvedRequestContext(context), streamOptions)
141
+ : transcriptStreamFn(model, context, streamOptions);
142
+ };
143
+ const admit: StreamFn = (model, context, streamOptions) => {
144
+ if (!beforeStreamRequest) return invoke(model, context, streamOptions);
145
+ activeCorrelationId = undefined;
146
+ const request: EngineStreamRequest = { model, context, options: streamOptions };
147
+ let admission: EngineStreamAdmission;
148
+ try {
149
+ admission = beforeStreamRequest(request);
150
+ } catch {
151
+ return refusedStream(request, "Request admission failed", streamOptions?.signal?.aborted);
152
+ }
153
+ if (streamOptions?.signal?.aborted) return refusedStream(request, "Request aborted before invocation", true);
154
+ if (admission.block) return refusedStream(request, admission.reason);
155
+ activeCorrelationId = admission.correlationId;
156
+ return invoke(model, context, streamOptions);
157
+ };
50
158
  const agent = new Agent({
51
159
  ...agentOptions,
52
- streamFn: (model, context, streamOptions) => {
53
- onStreamInvocation?.();
54
- return streamFn
55
- ? streamFn(model, resolvedRequestContext(context), streamOptions)
56
- : transcriptStreamFn(model, context, streamOptions);
57
- },
160
+ streamFn: beforeStreamRequest ? admit : invoke,
161
+ ...(beforeToolBatch ? { beforeToolCall: batchAwareBeforeToolCall(beforeToolBatch, options.beforeToolCall) } : {}),
58
162
  afterToolCall: dispositionAwareAfterToolCall(options.afterToolCall),
59
163
  });
164
+ if (beforeStreamRequest) {
165
+ agent.subscribe((event) => {
166
+ if (event.type === "message_end" && event.message.role === "assistant" && activeCorrelationId !== undefined) {
167
+ correlations.set(event.message, activeCorrelationId);
168
+ }
169
+ if (event.type === "agent_start" || event.type === "turn_end" || event.type === "agent_end") {
170
+ activeCorrelationId = undefined;
171
+ }
172
+ });
173
+ }
60
174
  // Pi correctly drops aborted assistants at the provider boundary: partial
61
175
  // reasoning and tool calls are not valid provider history. Preserve the
62
176
  // lifecycle fact and visible text as a host record instead, including on
@@ -83,6 +197,7 @@ export function createEngineAgent(options: EngineAgentOptions = {}): EngineAgent
83
197
  return {
84
198
  agent,
85
199
  state: () => agent.state,
200
+ requestCorrelationId: (message) => correlations.get(message),
86
201
  };
87
202
  }
88
203
 
@@ -99,3 +214,12 @@ export function replaceEngineMessages(
99
214
  export function setEngineSystemPrompt(agent: Agent, systemPrompt: string): void {
100
215
  replaceEngineMessages(agent, agent.state.messages, systemPrompt);
101
216
  }
217
+
218
+ /** Explicit host recovery can follow a terminal assistant. Empty input starts
219
+ * the native loop on the installed transcript without emitting a user message.
220
+ * Agent.continue() rejects that tail even when durable operator control already
221
+ * authorized another invocation. Normal tool continuation stays inside Pi.
222
+ */
223
+ export function continueEngineWithoutInput(agent: Agent): Promise<void> {
224
+ return agent.prompt([]);
225
+ }
@@ -62,19 +62,26 @@ export interface ClioSessionMeta {
62
62
  platform: string;
63
63
  nodeVersion: string;
64
64
  /**
65
- * Version 4 on every new session. Readers reject earlier versions, sessions
65
+ * Version 5 on every new session. Readers reject earlier versions, sessions
66
66
  * that omit this field, and versions from the future.
67
67
  */
68
68
  sessionFormatVersion?: number;
69
69
  }
70
70
 
71
71
  /**
72
- * Version 4 adds the working-set ledger kinds (`contextEviction`,
73
- * `contextRecall`). A version-3 reader would parse those entries as unknown
74
- * kinds and replay a session as if nothing had been evicted, so the bump is
75
- * what makes an older Clio refuse the file instead of misreading it.
72
+ * Version 4 added the working-set ledger kinds (`contextEviction`,
73
+ * `contextRecall`). Version 5 adds the durable continuity kinds
74
+ * (`handoffTransaction`, `continuityCommit`) and the optional continuity
75
+ * payload a compaction summary carries.
76
+ *
77
+ * Each bump exists for the same reason: an older reader would parse the new
78
+ * records as unknown kinds and replay the session as if the state they record
79
+ * had never happened. A v4 build reading a v5 ledger would show no accepted
80
+ * handoff note and no paused transaction, so it refuses the file instead of
81
+ * misreading it. Both steps are additive to the ledger, so no historical entry
82
+ * is rewritten; only the metadata stamp moves.
76
83
  */
77
- export const CURRENT_SESSION_FORMAT_VERSION = 4;
84
+ export const CURRENT_SESSION_FORMAT_VERSION = 5;
78
85
 
79
86
  export interface ClioSessionJsonlHeader {
80
87
  type: "session";
@@ -65,11 +65,8 @@ import {
65
65
  } from "../domains/lifecycle/index.js";
66
66
  import { getVersionInfo } from "../domains/lifecycle/version.js";
67
67
  import {
68
- buildMemoryPromptSection,
69
- canonicalMemoryRepositoryIdentity,
70
68
  createTaskMemoryTelemetrySink,
71
69
  createTaskMemoryTrace,
72
- loadMemoryRecordsSync,
73
70
  proposeInjectedTaskMemory,
74
71
  readTaskMemorySpendSummary,
75
72
  renderTaskMemoryHandoffSource,
@@ -81,6 +78,7 @@ import {
81
78
  taskMemoryHandoffSeedOffer,
82
79
  taskMemoryTracePath,
83
80
  } from "../domains/memory/index.js";
81
+ import { createMemoryPromptReader } from "../domains/memory/prompt-cache.js";
84
82
  import { TaskMemoryBank } from "../domains/memory/task-bank.js";
85
83
  import { TaskMemoryEndpointBusyError } from "../domains/memory/task-memory-policy.js";
86
84
  import { createDemoGuidanceRegistration } from "../domains/middleware/demo-guidance.js";
@@ -166,6 +164,8 @@ import type { CompactionCallObservation } from "../domains/session/compaction/co
166
164
  import { type CompactInput, type CompactResult, compact } from "../domains/session/compaction/compact.js";
167
165
  import { collectSessionEntries } from "../domains/session/compaction/session-entries.js";
168
166
  import { estimateTokens } from "../domains/session/compaction/tokens.js";
167
+ import { continuityPayloadFromFold } from "../domains/session/continuity/carry.js";
168
+ import { continuityProjectionTokens, resolveContinuityProjection } from "../domains/session/continuity/projection.js";
169
169
  import type { SessionContract, SessionMeta } from "../domains/session/contract.js";
170
170
  import { activeDecisionRefs, createDecisionBoardStore } from "../domains/session/decision-board.js";
171
171
  import type { CompactionSummaryEntry, CompactionTrigger, SessionEntry } from "../domains/session/entries.js";
@@ -205,7 +205,10 @@ import { cwdHash, openSession, readSessionTailTurns, sessionCurrentPath, session
205
205
  import type { EngineModel } from "../engine/types.js";
206
206
  import { createChatLoop } from "../interactive/chat-loop.js";
207
207
  import type { RunIo } from "../interactive/index.js";
208
- import { buildModelReplayAgentMessagesFromTurns } from "../interactive/model-session-replay.js";
208
+ import {
209
+ buildModelReplayAgentMessagesFromTurns,
210
+ continuityContextFromSession,
211
+ } from "../interactive/model-session-replay.js";
209
212
  import { prepareBackgroundModelMetadata } from "./background-model-metadata.js";
210
213
  import type { BootOptions } from "./boot-options.js";
211
214
  import { readCompactionSystemPrompt } from "./compaction-prompt.js";
@@ -824,7 +827,15 @@ async function runCompactionFlow(
824
827
  instructions?: string,
825
828
  trigger?: CompactionTrigger,
826
829
  observability?: BackgroundMemoryUsageSink,
827
- budget?: Pick<CompactInput, "keepRecentTokens" | "preserveUserTurnId" | "skillContextState" | "signal">,
830
+ budget?: Pick<
831
+ CompactInput,
832
+ | "keepRecentTokens"
833
+ | "preserveUserTurnId"
834
+ | "skillContextState"
835
+ | "signal"
836
+ | "beforeSummaryCall"
837
+ | "checkpointForSummary"
838
+ >,
828
839
  summarize?: CompactInput["summarize"],
829
840
  ): Promise<CompactResult | null> {
830
841
  const meta = session.current();
@@ -837,6 +848,26 @@ async function runCompactionFlow(
837
848
  session.current()?.id === meta.id && (session.tree(meta.id).leafId ?? undefined) === activeLeafTurnId;
838
849
  const entries = filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId);
839
850
  if (entries.length === 0) return null;
851
+ // Folded over the full applicable ledger before this compaction cuts it, so
852
+ // the carry written below reflects every durable transition, including ones
853
+ // older than the cut. Resolved once and used for three things: the payload
854
+ // the summary carries, the note's one-time price on both sides of the
855
+ // before/after comparison, and nothing else. Inspection never executes the
856
+ // action it proposes.
857
+ const continuity = resolveContinuityProjection({
858
+ entries,
859
+ sessionId: meta.id,
860
+ ...(meta.parentSessionId && meta.parentTurnId
861
+ ? { fork: { parentSessionId: meta.parentSessionId, parentTurnId: meta.parentTurnId } }
862
+ : {}),
863
+ });
864
+ // Only this session's own validated fold is carried forward. An inherited
865
+ // note is recall: writing it into this session's summary would republish
866
+ // another branch's transaction under this one's compaction.
867
+ const continuityNote = {
868
+ tokens: continuityProjectionTokens(continuity),
869
+ anchorTurnId: continuity.noteAnchorTurnId,
870
+ };
840
871
  budget?.signal?.throwIfAborted();
841
872
  const systemPrompt = await readCompactionSystemPrompt(settings.context.compaction.systemPrompt, meta.cwd);
842
873
  budget?.signal?.throwIfAborted();
@@ -876,6 +907,7 @@ async function runCompactionFlow(
876
907
  try {
877
908
  result = await compact({
878
909
  entries,
910
+ continuityNote,
879
911
  ...budget,
880
912
  ...(summarize ? { summarize } : {}),
881
913
  onCall: (call) => calls.push(call),
@@ -912,6 +944,26 @@ async function runCompactionFlow(
912
944
  result.usage = { ...result.usage, targetId: resolved.targetId, modelId: resolved.wireModelId };
913
945
  }
914
946
 
947
+ // Refold before publishing the carry. The projection above was resolved
948
+ // before prompt loading, route resolution and the summary stream, and a
949
+ // transition appended during any of those awaits leaves the message leaf
950
+ // untouched, so the origin check just above passes while the carry in hand
951
+ // still says `ready`. §3 requires the summary to embed the *current*
952
+ // validated fold, so the ledger is re-read and refolded here. Only the carry
953
+ // is re-derived: `result` indexes the entry array the cut was computed
954
+ // against, which must not move under it.
955
+ const settledContinuity = resolveContinuityProjection({
956
+ entries: filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId),
957
+ sessionId: meta.id,
958
+ ...(meta.parentSessionId && meta.parentTurnId
959
+ ? { fork: { parentSessionId: meta.parentSessionId, parentTurnId: meta.parentTurnId } }
960
+ : {}),
961
+ });
962
+ // A suppressed projection publishes no authoritative carry: the adapter marks
963
+ // its own fold unvalidated, and `continuityPayloadFromFold` refuses one.
964
+ const continuityCarry =
965
+ settledContinuity.current === null ? null : continuityPayloadFromFold(settledContinuity.current);
966
+
915
967
  const entry: Omit<CompactionSummaryEntry, "timestamp"> = {
916
968
  kind: "compactionSummary",
917
969
  turnId: randomUUID(),
@@ -923,12 +975,23 @@ async function runCompactionFlow(
923
975
  firstKeptTurnId: result.firstKeptTurnId ?? "",
924
976
  messagesSummarized: result.messagesSummarized,
925
977
  isSplitTurn: result.isSplitTurn,
926
- tokensAfter: estimateTokensAfterCompaction(entries, result),
978
+ tokensAfter: estimateTokensAfterCompaction(entries, result, continuityNote.tokens),
979
+ // The latest validated fold, carried so the transaction survives the cut
980
+ // that is about to remove its earlier records from replay. It preserves
981
+ // the immutable identity, accepted note, original policy and commit; it
982
+ // never manufactures a commit and never turns an eviction-only outcome
983
+ // into a summarized one. A later ordinary compaction with no handoff of
984
+ // its own still carries the newest validated payload forward, which is
985
+ // what keeps an eviction-only commit's state alive across summary cycles.
986
+ ...(continuityCarry === null ? {} : { continuity: continuityCarry }),
927
987
  // The summarization call is a real model call. Persisting its provider
928
- // usage on the entry is what puts it in front of `/cost` and `clio-coder usage
988
+ // usage on the entry is what puts it in front of `/usage` and `clio-coder usage
929
989
  // report`, which folded the ledger and so counted every call but this one.
930
990
  ...(result.usage !== undefined ? { usage: result.usage } : {}),
931
991
  };
992
+ if (budget?.checkpointForSummary) {
993
+ entry.continuity = budget.checkpointForSummary(entry.turnId, entry.tokensBefore, entry.tokensAfter ?? 0);
994
+ }
932
995
  if (trigger !== undefined) entry.trigger = trigger;
933
996
  try {
934
997
  session.appendEntry(entry);
@@ -986,7 +1049,15 @@ export function createProductionAutoCompact(
986
1049
  ): (
987
1050
  instructions?: string,
988
1051
  trigger?: CompactionTrigger,
989
- budget?: Pick<CompactInput, "keepRecentTokens" | "preserveUserTurnId" | "skillContextState" | "signal">,
1052
+ budget?: Pick<
1053
+ CompactInput,
1054
+ | "keepRecentTokens"
1055
+ | "preserveUserTurnId"
1056
+ | "skillContextState"
1057
+ | "signal"
1058
+ | "beforeSummaryCall"
1059
+ | "checkpointForSummary"
1060
+ >,
990
1061
  ) => Promise<CompactResult | null> {
991
1062
  return (instructions, trigger, budget) =>
992
1063
  runCompactionFlow(session, getSettings(), providers, instructions, trigger, observability, budget, summarize);
@@ -1023,13 +1094,24 @@ function estimateTokensFromSummary(result: CompactResult): number {
1023
1094
  * still holds the assistant message whose usage describes the pre-compaction
1024
1095
  * prompt. Anchoring on it reports precisely the number compaction removed.
1025
1096
  */
1026
- function estimateTokensAfterCompaction(entries: ReadonlyArray<SessionEntry>, result: CompactResult): number {
1097
+ function estimateTokensAfterCompaction(
1098
+ entries: ReadonlyArray<SessionEntry>,
1099
+ result: CompactResult,
1100
+ /**
1101
+ * The projected continuity note, which survives the reduction and is
1102
+ * therefore on both sides. It is already inside `tokensBefore` and the
1103
+ * subtraction below never removes it, because continuity records estimate at
1104
+ * zero; it only has to be restored on the floor branch, which discards
1105
+ * `tokensBefore` entirely.
1106
+ */
1107
+ continuityNoteTokens = 0,
1108
+ ): number {
1027
1109
  let droppedTokens = 0;
1028
1110
  for (const entry of entries.slice(0, result.firstKeptEntryIndex)) droppedTokens += estimateTokens(entry);
1029
1111
  const summaryTokens = estimateTokensFromSummary(result);
1030
1112
  // The summary alone is the floor: a session whose dropped estimate exceeds
1031
1113
  // the measured anchor must not report a negative or sub-summary context.
1032
- return Math.max(summaryTokens, result.tokensBefore - droppedTokens + summaryTokens);
1114
+ return Math.max(summaryTokens + continuityNoteTokens, result.tokensBefore - droppedTokens + summaryTokens);
1033
1115
  }
1034
1116
 
1035
1117
  /**
@@ -1482,7 +1564,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1482
1564
  const memorySettings = (config?.get() ?? readSettings()).context.memory;
1483
1565
  // Bound late: the registration is built here, but the buffer a deferred
1484
1566
  // reminder lands in belongs to the chat loop that has not been composed yet.
1485
- let deferredMemoryReminderSink: ((message: string) => void) | null = null;
1567
+ let deferredMemoryReminderSink: ((message: string, isCurrent?: () => boolean) => void) | null = null;
1486
1568
  // The watchdog's findings are for the operator, not the model, so they take
1487
1569
  // the transcript-notice path rather than the reminder buffer. Bound late for
1488
1570
  // the same reason: the chat loop that owns the transcript is composed below.
@@ -1495,7 +1577,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1495
1577
  // check and the cost row read the same resolution the step itself used.
1496
1578
  /**
1497
1579
  * Account for one background memory step exactly as a `/btw` side question is
1498
- * accounted for: the in-process cost tracker under its own label so `/cost`
1580
+ * accounted for: the in-process cost tracker under its own label so `/usage`
1499
1581
  * shows it while the session lives, and one durable out-of-turn row so
1500
1582
  * `clio-coder usage report` can still see it afterwards.
1501
1583
  *
@@ -1537,7 +1619,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1537
1619
  // A headless run submits no further turn, so a detached step could only
1538
1620
  // finish after the process that would have read it has exited.
1539
1621
  deliversDeferredReminders: options.headless === undefined,
1540
- onDeferredReminder: (message) => deferredMemoryReminderSink?.(message),
1622
+ onDeferredReminder: (message, isCurrent) => deferredMemoryReminderSink?.(message, isCurrent),
1541
1623
  getSettings: () => {
1542
1624
  const memory = effectiveSettingsForDispatch?.().context.memory ?? memorySettings;
1543
1625
  return {
@@ -1790,6 +1872,8 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1790
1872
  })
1791
1873
  : null;
1792
1874
  const toolBootstrap = registerAllTools(toolRegistry, {
1875
+ getContextBudget: () => chat.inspectLiveBudget(),
1876
+ requestSelfCompact: (note, toolCallId, signal) => chat.requestSelfCompact(note, toolCallId, signal),
1793
1877
  getSettings: () => getCurrentSettings(),
1794
1878
  termination,
1795
1879
  captureWorkerContext: () => chat.captureWorkerContext?.() ?? null,
@@ -2177,21 +2261,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2177
2261
  bus,
2178
2262
  ...(prompts ? { prompts } : {}),
2179
2263
  ...(session ? { session } : {}),
2180
- getMemorySection: () => {
2181
- try {
2182
- const records = loadMemoryRecordsSync(clioDataDir());
2183
- const settings = getCurrentSettings();
2184
- const targetId = session?.current()?.target ?? settings.chat?.target;
2185
- const runtimeId = targetId ? providers.getTarget(targetId)?.runtime : undefined;
2186
- return buildMemoryPromptSection(records, {
2187
- scopes: ["global", "repo", "runtime"],
2188
- activeRepository: canonicalMemoryRepositoryIdentity(process.cwd()),
2189
- activeRuntime: runtimeId === undefined ? null : { kind: "runtime", key: runtimeId },
2190
- }).section;
2191
- } catch {
2192
- return "";
2193
- }
2194
- },
2264
+ getMemorySection: createMemoryPromptReader({ getDataDir: clioDataDir }),
2195
2265
  getTaskMemoryHandoffSource: () => {
2196
2266
  const meta = session?.current();
2197
2267
  if (!meta) throw new Error("task memory handoff requires an active session");
@@ -2205,6 +2275,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2205
2275
  agentIds: [],
2206
2276
  });
2207
2277
  },
2278
+ memoryCommitBridge: memoryIntervention,
2208
2279
  registerDeferredReminderSink: (sink) => {
2209
2280
  deferredMemoryReminderSink = sink;
2210
2281
  },
@@ -2278,10 +2349,14 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2278
2349
  // parent onto.
2279
2350
  chat.resetForSession(
2280
2351
  leafTurnId,
2281
- buildModelReplayAgentMessagesFromTurns(
2282
- readCurrentSessionEntries(),
2283
- leafTurnId ? { activeLeafTurnId: leafTurnId } : {},
2284
- ),
2352
+ buildModelReplayAgentMessagesFromTurns(readCurrentSessionEntries(), {
2353
+ ...(leafTurnId ? { activeLeafTurnId: leafTurnId } : {}),
2354
+ // The same ownership the interactive /resume overlay supplies.
2355
+ // Without it this reader owns nothing, the fold finds no
2356
+ // current origin, and a resumed session boots with its accepted
2357
+ // note silently missing from the provider context.
2358
+ continuity: continuityContextFromSession(session),
2359
+ }),
2285
2360
  );
2286
2361
  } catch (err) {
2287
2362
  chat.resetForSession(leafTurnId);
@@ -2332,8 +2407,15 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2332
2407
  ...(session
2333
2408
  ? {
2334
2409
  readSessionEntries: readSessionEntriesForCompact,
2410
+ // Ownership is read at call time, not captured: an ACP client
2411
+ // can switch sessions between turns, and a fork opened this way
2412
+ // must separate its own transactions from the ones it inherited
2413
+ // exactly as the interactive path does.
2335
2414
  buildReplayMessages: (entries: ReadonlyArray<SessionEntry>, leafTurnId: string | null) =>
2336
- buildModelReplayAgentMessagesFromTurns(entries, leafTurnId === null ? {} : { activeLeafTurnId: leafTurnId }),
2415
+ buildModelReplayAgentMessagesFromTurns(entries, {
2416
+ ...(leafTurnId === null ? {} : { activeLeafTurnId: leafTurnId }),
2417
+ continuity: continuityContextFromSession(session),
2418
+ }),
2337
2419
  }
2338
2420
  : {}),
2339
2421
  providers,
@@ -2788,6 +2870,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2788
2870
  );
2789
2871
  }
2790
2872
  },
2873
+ onRecoverHandoff: (handoffId, action) => chat.recoverHandoff(handoffId, action),
2791
2874
  onCompact: async (instructions) => {
2792
2875
  await chat.compact(instructions);
2793
2876
  },
@@ -379,7 +379,7 @@ export interface RunUsageSummary {
379
379
  * messages. Earlier versions of this function walked the list from the tail
380
380
  * and returned the first match, which silently dropped every intermediate
381
381
  * API call from the cost tally. Summing instead matches what the provider
382
- * actually billed and keeps the `/cost` overlay and footer counters
382
+ * actually billed and keeps the `/usage` overlay and footer counters
383
383
  * aligned across tool-heavy runs.
384
384
  */
385
385
  export function sumRunUsage(messages: ReadonlyArray<AgentMessage>): RunUsageSummary {
@@ -539,7 +539,7 @@ export function assistantSessionPayload(
539
539
  *
540
540
  * `estimated: true` is the provenance marker: no surface may report these as
541
541
  * provider-reported numbers. The usage fold and the context estimator both skip
542
- * aborted turns already, so this never reaches `/cost` or the window math; it is
542
+ * aborted turns already, so this never reaches `/usage` or the window math; it is
543
543
  * the record's own honesty about what the cancelled call cost.
544
544
  *
545
545
  * Returns null when the provider did report usage (there is nothing to fill in)