@iowarp/clio-coder 0.4.2 → 0.4.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 (523) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/CONTRIBUTING.md +86 -19
  3. package/README.md +35 -6
  4. package/dist/{acp-TMDQZDIG.js → acp-WNAYYF4F.js} +12 -13
  5. package/dist/{agents-5N5NG3XG.js → agents-3OKXHLOI.js} +60 -57
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-Z5CCBXKQ.js → auth-VKNNMGPU.js} +21 -19
  8. package/dist/{builtins-K6TNDT24.js → builtins-WGALA46I.js} +9 -4
  9. package/dist/{chunk-XE3PCIXH.js → chunk-23L32XTI.js} +12 -9
  10. package/dist/{chunk-I64IFBLB.js → chunk-25QBEXRS.js} +18 -11
  11. package/dist/{chunk-CDNVLKUX.js → chunk-26QSH3EJ.js} +13 -7
  12. package/dist/{chunk-QQLGQY2A.js → chunk-2ASED4PZ.js} +22 -22
  13. package/dist/{chunk-MCEPRMZW.js → chunk-2CU2H6KE.js} +2 -2
  14. package/dist/chunk-2DSOYNFC.js +108 -0
  15. package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
  16. package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
  17. package/dist/{chunk-O3YUNJZ2.js → chunk-2ZSONWVL.js} +82 -25
  18. package/dist/{chunk-2NHR3NAY.js → chunk-36CT5VVL.js} +331 -42
  19. package/dist/{chunk-2X4RYJTJ.js → chunk-3GY4F45V.js} +3 -3
  20. package/dist/{chunk-ZW55JB7N.js → chunk-3ODX73FK.js} +4 -6
  21. package/dist/{chunk-PBP4B7XR.js → chunk-3UNOLWNZ.js} +3 -3
  22. package/dist/{chunk-4JDLP6ZS.js → chunk-3UUXNFEX.js} +14 -10
  23. package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
  24. package/dist/{chunk-ZW4HH5JJ.js → chunk-4M6Z5QVF.js} +6 -6
  25. package/dist/{chunk-K6BSR66V.js → chunk-4NSRCOYP.js} +4 -1
  26. package/dist/{chunk-M2DAX4F6.js → chunk-4WR7VSYB.js} +2 -2
  27. package/dist/{chunk-FSP7CMNU.js → chunk-54X7T7DK.js} +61 -6
  28. package/dist/{chunk-54ODD65L.js → chunk-5636DCO5.js} +4 -4
  29. package/dist/chunk-57XXR6DR.js +3763 -0
  30. package/dist/{chunk-3KIPBMUA.js → chunk-5ICU3EUH.js} +2 -2
  31. package/dist/{chunk-77QIVUZB.js → chunk-5MEZN6CB.js} +4 -4
  32. package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
  33. package/dist/{chunk-YJISEZKC.js → chunk-5TUB6SLS.js} +6 -6
  34. package/dist/{chunk-IMXMHHMQ.js → chunk-6OSVSQL5.js} +341 -57
  35. package/dist/{chunk-Q4XWMHX6.js → chunk-6PAZTBPA.js} +14 -2
  36. package/dist/{chunk-FVDGR2ZL.js → chunk-6Q3CYFD3.js} +112 -39
  37. package/dist/{chunk-IDNA72AH.js → chunk-6QOTUPRG.js} +155 -36
  38. package/dist/{chunk-X7IARSHT.js → chunk-6UINWWS6.js} +16 -10
  39. package/dist/{chunk-CYZW7JHJ.js → chunk-72YIHOZQ.js} +9 -9
  40. package/dist/{chunk-IKSLQ4XV.js → chunk-75W7L2E2.js} +752 -861
  41. package/dist/{chunk-CRFOIAX3.js → chunk-7UGL4MB5.js} +6 -6
  42. package/dist/{chunk-HIICAHCJ.js → chunk-AUPNRN7C.js} +2 -2
  43. package/dist/{chunk-7BHIY2MW.js → chunk-BJVFZO5U.js} +8 -50
  44. package/dist/{chunk-B74PXLU7.js → chunk-CUSRQKPU.js} +65 -3
  45. package/dist/chunk-DQOVN6KV.js +386 -0
  46. package/dist/{chunk-E7GT7O5N.js → chunk-DT3LWJOB.js} +7 -4
  47. package/dist/chunk-DXKJURES.js +671 -0
  48. package/dist/{chunk-JBCS7CRR.js → chunk-EL24TAU4.js} +10 -10
  49. package/dist/{chunk-TPEQIQIE.js → chunk-ELWDPP3Y.js} +8 -8
  50. package/dist/{chunk-NDINPTJ4.js → chunk-ELZVTCGV.js} +5 -4
  51. package/dist/chunk-EXLD33WO.js +381 -0
  52. package/dist/chunk-FEAXX7B6.js +101 -0
  53. package/dist/{chunk-RLYRBIYQ.js → chunk-FFUPXJC4.js} +90 -331
  54. package/dist/{chunk-5PFYMY2V.js → chunk-FTMGRKEF.js} +2 -2
  55. package/dist/{chunk-34BHNEE3.js → chunk-GHS5EBTQ.js} +58 -7
  56. package/dist/{chunk-DYHAXKHD.js → chunk-GWZNEVM2.js} +12 -8
  57. package/dist/chunk-GX5WYQO4.js +59 -0
  58. package/dist/chunk-GYV6VZOC.js +26 -0
  59. package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
  60. package/dist/chunk-I2DWJ4GM.js +390 -0
  61. package/dist/{chunk-TXOTCRLG.js → chunk-I5FWO7L5.js} +5 -5
  62. package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
  63. package/dist/{chunk-XQRY4DTA.js → chunk-IGLP3ODT.js} +10 -10
  64. package/dist/chunk-IRXAATOX.js +539 -0
  65. package/dist/chunk-IXIY2H4R.js +44 -0
  66. package/dist/{chunk-SSEYRH53.js → chunk-IZXGRF7P.js} +92 -147
  67. package/dist/{chunk-5TSRNF4G.js → chunk-JCI2ROMZ.js} +164 -6
  68. package/dist/{chunk-JWJGP5DQ.js → chunk-JEIYHLOR.js} +7 -7
  69. package/dist/{chunk-F2I26BDK.js → chunk-JQLNNIKT.js} +4 -4
  70. package/dist/{chunk-BYMNWQ7O.js → chunk-JSD46VO2.js} +315 -63
  71. package/dist/{chunk-AK5XEFVZ.js → chunk-JT2RFCC5.js} +64 -14
  72. package/dist/{chunk-MCMZMDAC.js → chunk-K6T2ZAMZ.js} +168 -6
  73. package/dist/{chunk-PGF63K6I.js → chunk-KFV5L5SK.js} +73 -4
  74. package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
  75. package/dist/{chunk-PJX3WQUQ.js → chunk-LLXSDWXS.js} +3 -3
  76. package/dist/{chunk-DZAW46HP.js → chunk-LTIKRKFL.js} +3 -3
  77. package/dist/{chunk-DZEK6CJN.js → chunk-N56KALIC.js} +21 -21
  78. package/dist/{chunk-B7HM5Z7T.js → chunk-NAI6ZFCY.js} +9 -5
  79. package/dist/{chunk-I66ZTYNP.js → chunk-NRO2BJRH.js} +2656 -2213
  80. package/dist/{chunk-ZGNYYXQ6.js → chunk-NXIMQY5W.js} +3 -3
  81. package/dist/{chunk-IKOZFYBN.js → chunk-NXYCB2VD.js} +149 -106
  82. package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
  83. package/dist/{chunk-462T4EGZ.js → chunk-O5CVSAG5.js} +2 -2
  84. package/dist/chunk-ODGTEFFI.js +50 -0
  85. package/dist/{chunk-3F7VUY77.js → chunk-OEJSLEPW.js} +2 -2
  86. package/dist/{chunk-KKOJXO6R.js → chunk-OMQNJVKW.js} +4 -2
  87. package/dist/{chunk-5KW52TEP.js → chunk-Q4WO54TA.js} +132 -77
  88. package/dist/{chunk-W6NIE6OW.js → chunk-QUFRYSWI.js} +13 -7
  89. package/dist/{chunk-42FMPA75.js → chunk-QZWQA4DE.js} +2 -2
  90. package/dist/chunk-R6Q67RJH.js +134 -0
  91. package/dist/{chunk-W4YEMFBX.js → chunk-RAY4OVGZ.js} +3 -3
  92. package/dist/{chunk-ZNT2M6TG.js → chunk-RQCKCSRL.js} +17 -17
  93. package/dist/{chunk-LJID3DYZ.js → chunk-RXTN6AKH.js} +3 -3
  94. package/dist/{chunk-P75RZCJW.js → chunk-RZDWV63N.js} +3 -3
  95. package/dist/{chunk-UH632ZYL.js → chunk-S6PYF2XF.js} +2 -2
  96. package/dist/{chunk-HJWWJ6IL.js → chunk-TOIVGRUX.js} +17 -5
  97. package/dist/{chunk-HLAFFSEK.js → chunk-TQAHXW6Y.js} +2 -2
  98. package/dist/{chunk-JIEGK6UF.js → chunk-U6TMQNSI.js} +48 -4
  99. package/dist/{chunk-2HFQNRV3.js → chunk-UEPWCCTY.js} +12 -12
  100. package/dist/chunk-UOIZ7DA4.js +41 -0
  101. package/dist/{chunk-UH347SHR.js → chunk-USR47QNF.js} +11 -11
  102. package/dist/{chunk-AZ4WMN4W.js → chunk-V6HJFQZE.js} +2 -2
  103. package/dist/chunk-V76WTFTW.js +318 -0
  104. package/dist/{chunk-NMJXSHBJ.js → chunk-W54I7H25.js} +2 -2
  105. package/dist/{chunk-KPXDY6QF.js → chunk-XRZT5WY5.js} +2 -2
  106. package/dist/{chunk-UBRFI4HS.js → chunk-XULDXHTN.js} +142 -50
  107. package/dist/chunk-XXYSBZIQ.js +283 -0
  108. package/dist/{chunk-HKMD33FO.js → chunk-Y55JBDO5.js} +405 -122
  109. package/dist/{chunk-XOXV5GKE.js → chunk-YD5GIKET.js} +17 -8
  110. package/dist/{chunk-XGDPUNND.js → chunk-YECAMM3D.js} +2 -2
  111. package/dist/{chunk-BO7Y52RY.js → chunk-YNFKXPEC.js} +7 -7
  112. package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
  113. package/dist/{chunk-M2WXEHER.js → chunk-ZA4VCIGV.js} +2 -2
  114. package/dist/cli/index.js +42 -40
  115. package/dist/{clio-7VB377CC.js → clio-QLICPCF5.js} +7 -7
  116. package/dist/{code-nav-YVLCYA7V.js → code-nav-IJR2DBPR.js} +9 -9
  117. package/dist/{components-UBWCQSRW.js → components-2TGAI2RC.js} +5 -6
  118. package/dist/{config-4HVOS65E.js → config-IUA6OYNS.js} +88 -81
  119. package/dist/{configure-PIWO7B24.js → configure-VEPX4NMX.js} +26 -25
  120. package/dist/{context-KQYIWPWT.js → context-2DKHWH2T.js} +60 -45
  121. package/dist/{context-IYEHL3WQ.js → context-4MPR7WKB.js} +78 -69
  122. package/dist/{context-N6ZE3LGJ.js → context-BOYF5EJM.js} +15 -11
  123. package/dist/{context-clear-G4OGZJDS.js → context-clear-S4ZJCQUX.js} +73 -65
  124. package/dist/context-map-COB37XXN.js +505 -0
  125. package/dist/{context-working-set-BWLF6LJP.js → context-working-set-3I3FYX6Y.js} +18 -17
  126. package/dist/detail-A7JAVSIG.js +98 -0
  127. package/dist/{dispatch-runner-2QQAITS3.js → dispatch-runner-RJ5I2F2O.js} +99 -75
  128. package/dist/{docs-PD3EXDKU.js → docs-SPOV3BAN.js} +3 -5
  129. package/dist/{doctor-LHBD36VU.js → doctor-DKICC2SN.js} +71 -48
  130. package/dist/{eval-C45FYRJ6.js → eval-OQOQUDHK.js} +308 -146
  131. package/dist/{eval-inventory-6DEJPLBF.js → eval-inventory-Y6QRFOH5.js} +4 -4
  132. package/dist/{evidence-6SHONYAF.js → evidence-4DQ25GUQ.js} +79 -175
  133. package/dist/evidence-4F5USFKH.js +208 -0
  134. package/dist/{evolve-KRKMV72X.js → evolve-GSS52E5J.js} +71 -67
  135. package/dist/{extensions-KPZ2UHBB.js → extensions-G7MFLYHT.js} +8 -9
  136. package/dist/{fleet-IVTCKDHT.js → fleet-Q37YHHAQ.js} +126 -118
  137. package/dist/{fleet-commands-EDWL3IT7.js → fleet-commands-G7E4N7SM.js} +16 -13
  138. package/dist/{fleet-decisions-YP3YEFGK.js → fleet-decisions-O7M6QBA2.js} +9 -8
  139. package/dist/{fleet-graph-ZFWKHY2M.js → fleet-graph-TOUBW6OW.js} +21 -20
  140. package/dist/{fleet-inspect-FVUNCBML.js → fleet-inspect-SW33JJNI.js} +65 -60
  141. package/dist/{fleet-preflight-UN5XED4R.js → fleet-preflight-CV2655TW.js} +4 -5
  142. package/dist/{fleet-validate-XOWC4HSX.js → fleet-validate-KESZX2YH.js} +25 -24
  143. package/dist/{fleet-verify-UN3SODEL.js → fleet-verify-UQPMTVE3.js} +66 -61
  144. package/dist/{fleet-view-TWHJKCN6.js → fleet-view-MN2VG4MR.js} +65 -60
  145. package/dist/{init-T2QORQ3Y.js → init-PXEXQSBF.js} +90 -82
  146. package/dist/{interop-IN5I2A66.js → interop-ZG5T62U3.js} +12 -13
  147. package/dist/inventory-C26CFDRR.js +101 -0
  148. package/dist/{library-LSCATDLZ.js → library-B2W4N74O.js} +29 -29
  149. package/dist/{memory-HYOKAGGJ.js → memory-YCANYS5A.js} +73 -69
  150. package/dist/{models-2GPMFYCM.js → models-GERTU3YI.js} +51 -47
  151. package/dist/{monitor-E4ASVUJH.js → monitor-CPNIUULB.js} +74 -67
  152. package/dist/{orchestrator-DDMPR3PY.js → orchestrator-J4BSH4WQ.js} +1288 -1606
  153. package/dist/{panes-E3RUXOW5.js → panes-BOHAEGYC.js} +4 -4
  154. package/dist/{panes-IXKLOKA2.js → panes-NXSLDQZ2.js} +10 -11
  155. package/dist/{paths-L7LGY6RN.js → paths-VSUWNC22.js} +6 -7
  156. package/dist/reset-TNWTB5LU.js +343 -0
  157. package/dist/{resources-OTRSN34L.js → resources-4PXNMD5G.js} +29 -22
  158. package/dist/{run-5DEYH5QK.js → run-D6XJ34CN.js} +132 -132
  159. package/dist/{share-IHWTLO3M.js → share-2NWMJJEE.js} +27 -27
  160. package/dist/{skills-IYMXMKW4.js → skills-KR7WON5G.js} +40 -33
  161. package/dist/{skills-eval-DROHSJAR.js → skills-eval-O2ZNOLDS.js} +81 -77
  162. package/dist/{skills-inventory-D7X4L4ZX.js → skills-inventory-ZZOUBK7O.js} +23 -22
  163. package/dist/{slash-commands-QBM7UZ3B.js → slash-commands-ZXPJD64J.js} +47 -37
  164. package/dist/{steer-Z5DO23FJ.js → steer-XA25PSCS.js} +4 -4
  165. package/dist/{support-U7QOWY26.js → support-7EMVWYG2.js} +6 -6
  166. package/dist/{targets-P2FUC4IL.js → targets-OMH2XCSN.js} +50 -49
  167. package/dist/tasks-IPAGMEIX.js +36 -0
  168. package/dist/{terminal-lease-YREJ3JX2.js → terminal-lease-C2J3JYRE.js} +4 -4
  169. package/dist/{tools-5B7RO6MV.js → tools-EFFEAIDP.js} +8 -9
  170. package/dist/{trace-YMGMUM6A.js → trace-FXMXUZUF.js} +7 -7
  171. package/dist/uninstall-HALS6BLF.js +407 -0
  172. package/dist/upgrade-MS72RJEP.js +306 -0
  173. package/dist/{usage-ME5MPXGX.js → usage-NHG6MCJM.js} +162 -108
  174. package/dist/{verifiers-BVZ7IWOO.js → verifiers-7AUNVXDY.js} +155 -22
  175. package/dist/{verify-5K7ZKQFC.js → verify-FWYGPKMR.js} +14 -12
  176. package/dist/{web-fetch-MPARV2K7.js → web-fetch-V4FKSDAV.js} +4 -4
  177. package/dist/{wiki-generate-F5W5QTYY.js → wiki-generate-743CIGJW.js} +99 -90
  178. package/dist/{with-panes-BYOJCLAM.js → with-panes-BDQEWBRT.js} +10 -10
  179. package/dist/worker/entry.js +72 -68
  180. package/docs/README.md +3 -2
  181. package/docs/architecture/acp.md +17 -0
  182. package/docs/architecture/artifact-placement.md +1 -0
  183. package/docs/architecture/artifact-versions.md +2 -2
  184. package/docs/architecture/context-engine.md +4 -0
  185. package/docs/architecture/dispatch-typed-intent.md +1 -1
  186. package/docs/architecture/evidence-and-memory.md +1 -1
  187. package/docs/architecture/middleware-and-components.md +1 -1
  188. package/docs/architecture/model-catalog.md +21 -10
  189. package/docs/architecture/observability.md +19 -2
  190. package/docs/architecture/prompt-envelope-and-tools.md +17 -5
  191. package/docs/architecture/provider-adapter-cookbook.md +63 -0
  192. package/docs/architecture/safety-model.md +25 -22
  193. package/docs/architecture/tui-design.md +1 -1
  194. package/docs/guide/built-in-agents.md +25 -11
  195. package/docs/guide/commands-and-modes.md +18 -3
  196. package/docs/guide/configuration-and-targets.md +100 -10
  197. package/docs/guide/configuration-reference.md +17 -7
  198. package/docs/guide/environment-variables.md +4 -2
  199. package/docs/guide/installation-and-lifecycle.md +37 -4
  200. package/docs/guide/proactive-memory.md +66 -55
  201. package/docs/guide/skills-marketplace.md +18 -0
  202. package/docs/guide/tool-usage.md +78 -3
  203. package/docs/history/config-knobs-audit.md +2 -2
  204. package/docs/process/development-pipeline.md +40 -2
  205. package/docs/process/eval-runner.md +67 -3
  206. package/docs/process/git-commit-provenance.md +15 -0
  207. package/docs/process/release-cut-checklist.md +207 -0
  208. package/docs/process/scientific-validation.md +18 -17
  209. package/evals/behavioral-machinery-support.ts +1 -0
  210. package/evals/behavioral-machinery.yaml +1 -1
  211. package/evals/behavioral-model.yaml +3 -2
  212. package/package.json +2 -2
  213. package/skills/README.md +7 -5
  214. package/skills/coding/ast-grep/SKILL.md +101 -30
  215. package/skills/coding/ast-grep/evals.md +26 -0
  216. package/skills/coding/coding-standards/SKILL.md +47 -2
  217. package/skills/coding/coding-standards/evals.md +23 -0
  218. package/skills/coding/prototype/SKILL.md +87 -28
  219. package/skills/coding/prototype/evals.md +19 -0
  220. package/skills/coding/tdd/SKILL.md +80 -53
  221. package/skills/coding/tdd/evals.md +20 -0
  222. package/skills/context/context-handoff/SKILL.md +43 -2
  223. package/skills/context/context-handoff/evals.md +44 -0
  224. package/skills/context/context-prime/SKILL.md +45 -15
  225. package/skills/context/context-prime/evals.md +45 -0
  226. package/skills/git/branch-closeout/SKILL.md +132 -0
  227. package/skills/git/branch-closeout/evals.md +133 -0
  228. package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
  229. package/skills/git/file-ticket/SKILL.md +77 -63
  230. package/skills/git/file-ticket/assets/issue-template.md +22 -0
  231. package/skills/git/file-ticket/evals.md +31 -26
  232. package/skills/git/file-ticket/references/issue-discovery.md +49 -0
  233. package/skills/git/fix-issue/SKILL.md +87 -64
  234. package/skills/git/fix-issue/evals.md +35 -31
  235. package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
  236. package/skills/git/resolve-merge-conflicts/SKILL.md +100 -51
  237. package/skills/git/resolve-merge-conflicts/evals.md +52 -25
  238. package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
  239. package/skills/git/ship/SKILL.md +103 -67
  240. package/skills/git/ship/assets/pr-template.md +21 -0
  241. package/skills/git/ship/evals.md +44 -28
  242. package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
  243. package/skills/git/worktree-create/SKILL.md +80 -50
  244. package/skills/git/worktree-create/evals.md +40 -33
  245. package/skills/git/worktree-create/references/worktree-setup.md +62 -66
  246. package/skills/git/worktree-merge/SKILL.md +112 -65
  247. package/skills/git/worktree-merge/evals.md +42 -34
  248. package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
  249. package/skills/planning/archify/SKILL.md +196 -0
  250. package/skills/planning/archify/evals.md +65 -0
  251. package/skills/planning/architecture/SKILL.md +61 -12
  252. package/skills/planning/architecture/evals.md +65 -0
  253. package/skills/planning/backlog/SKILL.md +130 -14
  254. package/skills/planning/backlog/evals.md +142 -0
  255. package/skills/planning/prd/SKILL.md +47 -6
  256. package/skills/planning/prd/evals.md +54 -0
  257. package/skills/planning/product-intent/SKILL.md +57 -2
  258. package/skills/planning/product-intent/evals.md +70 -0
  259. package/skills/planning/tech-spec/SKILL.md +53 -2
  260. package/skills/planning/tech-spec/evals.md +73 -0
  261. package/skills/registry.yaml +58 -50
  262. package/skills/remote.yaml +13 -0
  263. package/skills/research/arxiv-literature/SKILL.md +76 -18
  264. package/skills/research/arxiv-literature/evals.md +50 -0
  265. package/skills/research/experiment-protocol/SKILL.md +20 -1
  266. package/skills/research/experiment-protocol/evals.md +23 -0
  267. package/skills/research/scientific-debugging/SKILL.md +24 -1
  268. package/skills/research/scientific-debugging/evals.md +18 -0
  269. package/skills/research/scientific-modernization/SKILL.md +26 -1
  270. package/skills/research/scientific-modernization/evals.md +27 -0
  271. package/skills/skill-marketplace.json +63 -28
  272. package/skills/workflow/cut-it/SKILL.md +64 -5
  273. package/skills/workflow/cut-it/evals.md +101 -0
  274. package/skills/workflow/design-council/SKILL.md +112 -27
  275. package/skills/workflow/design-council/evals.md +161 -0
  276. package/skills/workflow/grill-me/SKILL.md +85 -10
  277. package/skills/workflow/grill-me/evals.md +153 -0
  278. package/skills/workflow/workflow-distiller/SKILL.md +76 -17
  279. package/skills/workflow/workflow-distiller/evals.md +118 -0
  280. package/src/cli/args.ts +0 -8
  281. package/src/cli/configure-interop.ts +105 -13
  282. package/src/cli/configure-oauth.ts +57 -0
  283. package/src/cli/configure-onboarding.ts +980 -0
  284. package/src/cli/configure-target.ts +594 -0
  285. package/src/cli/configure.ts +1084 -529
  286. package/src/cli/context-map.ts +114 -0
  287. package/src/cli/context.ts +4 -0
  288. package/src/cli/doctor-state-size.ts +1 -12
  289. package/src/cli/doctor-validation-contract.ts +28 -0
  290. package/src/cli/doctor.ts +5 -0
  291. package/src/cli/evidence-detail.ts +1 -75
  292. package/src/cli/evidence-inventory.ts +1 -167
  293. package/src/cli/index.ts +3 -0
  294. package/src/cli/lifecycle-presenter.ts +436 -0
  295. package/src/cli/models.ts +10 -2
  296. package/src/cli/modes/print.ts +5 -1
  297. package/src/cli/reset.ts +228 -106
  298. package/src/cli/run.ts +7 -4
  299. package/src/cli/select.ts +664 -0
  300. package/src/cli/skills.ts +9 -2
  301. package/src/cli/targets.ts +3 -0
  302. package/src/cli/tasks.ts +84 -0
  303. package/src/cli/uninstall.ts +233 -165
  304. package/src/cli/upgrade.ts +210 -150
  305. package/src/cli/usage.ts +92 -27
  306. package/src/cli/validate-model.ts +3 -3
  307. package/src/cli/verifiers.ts +147 -1
  308. package/src/cli/wiki-generate.ts +1 -0
  309. package/src/core/commit-attribution.ts +41 -1
  310. package/src/core/config.ts +56 -0
  311. package/src/core/external-diagnostic.ts +44 -0
  312. package/src/core/gateway-routing.ts +157 -0
  313. package/src/core/git-commit-attribution.ts +46 -3
  314. package/src/core/run-overrides.ts +0 -5
  315. package/src/core/safe-exec.ts +17 -2
  316. package/src/core/skill-activation.ts +92 -2
  317. package/src/core/tool-names.ts +5 -2
  318. package/src/domains/agents/builtins/architect.md +1 -1
  319. package/src/domains/agents/builtins/coder.md +1 -1
  320. package/src/domains/agents/builtins/documenter.md +1 -1
  321. package/src/domains/agents/builtins/git-master.md +1 -1
  322. package/src/domains/agents/builtins/provenance.md +7 -7
  323. package/src/domains/agents/builtins/tester.md +1 -1
  324. package/src/domains/agents/builtins/verifier.md +2 -2
  325. package/src/domains/agents/builtins/wiki-writer.md +4 -3
  326. package/src/domains/agents/builtins/world-knowledge.md +31 -0
  327. package/src/domains/agents/catalog.ts +1 -1
  328. package/src/domains/agents/result-contract.ts +70 -0
  329. package/src/domains/context/extension.ts +31 -7
  330. package/src/domains/context/refresh.ts +3 -0
  331. package/src/domains/context/wiki/frontmatter.ts +5 -2
  332. package/src/domains/context/wiki/generate.ts +6 -0
  333. package/src/domains/context/wiki/map-seed.ts +589 -0
  334. package/src/domains/context/wiki/plan.ts +2 -2
  335. package/src/domains/context/wiki/prompts.ts +43 -0
  336. package/src/domains/dispatch/active-route-planner.ts +4 -0
  337. package/src/domains/dispatch/admission.ts +29 -0
  338. package/src/domains/dispatch/agent-candidates.ts +10 -0
  339. package/src/domains/dispatch/budget-envelope.ts +86 -1
  340. package/src/domains/dispatch/capability-match.ts +1 -0
  341. package/src/domains/dispatch/capacity-lease.ts +17 -0
  342. package/src/domains/dispatch/code-step.ts +11 -4
  343. package/src/domains/dispatch/contract.ts +42 -8
  344. package/src/domains/dispatch/execution-scheduler.ts +2 -0
  345. package/src/domains/dispatch/extension.ts +184 -56
  346. package/src/domains/dispatch/fleet-commit-attribution.ts +5 -0
  347. package/src/domains/dispatch/fleet-run.ts +1 -0
  348. package/src/domains/dispatch/host-verification.ts +114 -13
  349. package/src/domains/dispatch/intent.ts +28 -18
  350. package/src/domains/dispatch/orphan-recovery.ts +2 -0
  351. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  352. package/src/domains/dispatch/reservation-store.ts +5 -3
  353. package/src/domains/dispatch/state.ts +15 -2
  354. package/src/domains/dispatch/types.ts +20 -2
  355. package/src/domains/dispatch/worker-model-metadata.ts +38 -0
  356. package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
  357. package/src/domains/eval/metrics/token-stream.ts +201 -31
  358. package/src/domains/eval/metrics/tracked.ts +40 -4
  359. package/src/domains/eval/runners/clio-run.ts +17 -11
  360. package/src/domains/eval/runners/context-index.ts +2 -7
  361. package/src/domains/eval/runners/context-init.ts +3 -6
  362. package/src/domains/eval/runners/external-command.ts +29 -11
  363. package/src/domains/eval/schema/suite.ts +28 -0
  364. package/src/domains/eval/schema/verdict.ts +2 -2
  365. package/src/domains/eval/suites/resolve.ts +13 -1
  366. package/src/domains/eval/suites/run.ts +24 -3
  367. package/src/domains/evidence/build.ts +102 -15
  368. package/src/domains/evidence/detail.ts +69 -0
  369. package/src/domains/evidence/eval.ts +13 -1
  370. package/src/domains/evidence/finish-contract-map.ts +5 -1
  371. package/src/domains/evidence/inventory.ts +167 -0
  372. package/src/domains/evidence/store.ts +16 -0
  373. package/src/domains/evidence/types.ts +12 -0
  374. package/src/domains/extensions/resources.ts +7 -0
  375. package/src/domains/interop/registry.ts +6 -2
  376. package/src/domains/interop/types.ts +4 -0
  377. package/src/domains/lifecycle/migrations/index.ts +4 -0
  378. package/src/domains/memory/task-memory-policy.ts +70 -26
  379. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  380. package/src/domains/middleware/index.ts +0 -1
  381. package/src/domains/middleware/marketplace-offer.ts +22 -35
  382. package/src/domains/middleware/memory-intervention.ts +127 -32
  383. package/src/domains/middleware/memory-step-endpoint.ts +3 -2
  384. package/src/domains/middleware/runtime.ts +7 -3
  385. package/src/domains/middleware/skills-reminder.ts +31 -2
  386. package/src/domains/mux/detect.ts +3 -6
  387. package/src/domains/observability/accountability.ts +15 -1
  388. package/src/domains/observability/compaction-usage.ts +118 -0
  389. package/src/domains/observability/contract.ts +52 -7
  390. package/src/domains/observability/cost.ts +1 -1
  391. package/src/domains/observability/evidence-index.ts +10 -0
  392. package/src/domains/observability/extension.ts +15 -6
  393. package/src/domains/observability/out-of-turn-usage.ts +52 -21
  394. package/src/domains/observability/projection.ts +394 -45
  395. package/src/{interactive → domains/observability}/worker-progress.ts +3 -3
  396. package/src/domains/prompts/fragments/operating/contract.md +2 -0
  397. package/src/domains/prompts/fragments/wiki/page.md +8 -0
  398. package/src/domains/providers/contract.ts +4 -1
  399. package/src/domains/providers/extension.ts +40 -9
  400. package/src/domains/providers/model-capabilities.ts +9 -0
  401. package/src/domains/providers/model-discovery.ts +3 -4
  402. package/src/domains/providers/model-runtime-capabilities.ts +15 -5
  403. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +48 -26
  404. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
  405. package/src/domains/providers/runtimes/claude/claude-code.ts +9 -0
  406. package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
  407. package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
  408. package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
  409. package/src/domains/providers/support.ts +11 -5
  410. package/src/domains/providers/target-model-cache.ts +25 -2
  411. package/src/domains/providers/types/capability-flags.ts +2 -0
  412. package/src/domains/providers/types/runtime-descriptor.ts +20 -1
  413. package/src/domains/providers/types/target-descriptor.ts +19 -0
  414. package/src/domains/resources/index.ts +3 -0
  415. package/src/domains/resources/skills/install.ts +72 -7
  416. package/src/domains/resources/skills/loader.ts +7 -0
  417. package/src/domains/resources/skills/marketplace.ts +63 -11
  418. package/src/domains/safety/action-classifier.ts +7 -0
  419. package/src/domains/safety/autonomy.ts +15 -0
  420. package/src/domains/safety/default-path-policy.ts +2 -0
  421. package/src/domains/safety/finish-contract-registration.ts +29 -14
  422. package/src/domains/safety/finish-contract.ts +252 -40
  423. package/src/domains/safety/index.ts +21 -1
  424. package/src/domains/safety/path-policy.ts +1 -1
  425. package/src/domains/safety/policy-engine.ts +60 -17
  426. package/src/domains/safety/protected-artifacts.ts +191 -88
  427. package/src/domains/safety/rigor.ts +53 -39
  428. package/src/domains/safety/run-effects.ts +2 -22
  429. package/src/domains/safety/skill-authority.ts +55 -0
  430. package/src/domains/safety/validation-contract.ts +388 -0
  431. package/src/domains/session/archive-readers.ts +10 -1
  432. package/src/domains/session/compaction/compact.ts +72 -22
  433. package/src/domains/session/decision-board.ts +101 -2
  434. package/src/domains/session/entries.ts +50 -7
  435. package/src/domains/session/extension.ts +4 -4
  436. package/src/domains/session/handoff.ts +2 -1
  437. package/src/domains/session/manager.ts +2 -3
  438. package/src/domains/session/task-board.ts +14 -1
  439. package/src/domains/session/tree/fork.ts +1 -2
  440. package/src/domains/session/tree/navigator.ts +1 -1
  441. package/src/domains/session/usage.ts +3 -3
  442. package/src/domains/user-tasks/acceptance.ts +56 -0
  443. package/src/domains/user-tasks/active-acceptance.ts +40 -0
  444. package/src/domains/user-tasks/store.ts +34 -3
  445. package/src/engine/acp/adapter.ts +24 -6
  446. package/src/engine/acp/server.ts +21 -4
  447. package/src/engine/acp/transport.ts +53 -8
  448. package/src/engine/acp/types.ts +4 -0
  449. package/src/engine/agent.ts +13 -3
  450. package/src/engine/ai.ts +26 -8
  451. package/src/engine/antigravity/subprocess-runtime.ts +386 -120
  452. package/src/engine/api-registry.ts +3 -0
  453. package/src/engine/apis/ollama-native.ts +15 -0
  454. package/src/engine/apis/openai-completions.ts +117 -14
  455. package/src/engine/claude/subprocess-runtime.ts +107 -60
  456. package/src/engine/external-subprocess.ts +122 -6
  457. package/src/entry/background-model-metadata.ts +18 -0
  458. package/src/entry/compaction-prompt.ts +57 -0
  459. package/src/entry/orchestrator.ts +416 -218
  460. package/src/entry/task-memory-lifecycle.ts +35 -0
  461. package/src/interactive/chat-loop-messages.ts +13 -4
  462. package/src/interactive/chat-loop.ts +65 -2
  463. package/src/interactive/chat-renderer.ts +1 -0
  464. package/src/interactive/cost-overlay.ts +26 -2
  465. package/src/interactive/dispatch-board.ts +46 -717
  466. package/src/interactive/fleet-run-preview.ts +2 -1
  467. package/src/interactive/interactive-application.ts +3 -2
  468. package/src/interactive/interactive-presentation.ts +55 -12
  469. package/src/interactive/interactive-slash-runtime.ts +4 -2
  470. package/src/interactive/oracle.ts +5 -2
  471. package/src/interactive/overlays/fleet-run-approval.ts +3 -2
  472. package/src/interactive/overlays/message-picker.ts +2 -2
  473. package/src/interactive/overlays/settings.ts +2 -2
  474. package/src/interactive/overlays/tree-selector.ts +2 -2
  475. package/src/interactive/renderers/branch-summary.ts +1 -1
  476. package/src/interactive/renderers/worker-entry.ts +32 -0
  477. package/src/interactive/slash-autocomplete.ts +4 -6
  478. package/src/interactive/slash-commands.ts +49 -45
  479. package/src/interactive/slash-spec.ts +28 -0
  480. package/src/interactive/theme/labels.ts +19 -13
  481. package/src/interactive/turn-context.ts +9 -5
  482. package/src/interactive/turn-recovery.ts +8 -0
  483. package/src/interactive/turn-runtime.ts +27 -11
  484. package/src/interactive/turn-state.ts +7 -0
  485. package/src/interactive/view/artifacts.ts +2 -0
  486. package/src/interactive/worker-receipts.ts +1 -0
  487. package/src/interactive/worker-stream.ts +13 -4
  488. package/src/tools/bootstrap.ts +4 -0
  489. package/src/tools/builtin-tool-catalog.ts +31 -0
  490. package/src/tools/compete-worktrees.ts +7 -1
  491. package/src/tools/context/index.ts +30 -9
  492. package/src/tools/core-bootstrap.ts +16 -0
  493. package/src/tools/decide.ts +136 -0
  494. package/src/tools/dispatch-admission.ts +21 -0
  495. package/src/tools/dispatch-arguments.ts +1 -0
  496. package/src/tools/dispatch-event-text.ts +10 -0
  497. package/src/tools/dispatch-plan.ts +6 -2
  498. package/src/tools/dispatch-runner.ts +27 -1
  499. package/src/tools/dispatch-types.ts +6 -0
  500. package/src/tools/evidence.ts +96 -0
  501. package/src/tools/limitation.ts +76 -0
  502. package/src/tools/policy.ts +9 -0
  503. package/src/tools/presentation.ts +3 -0
  504. package/src/tools/registry.ts +11 -5
  505. package/src/tools/result-shaping.ts +17 -5
  506. package/src/tools/task-worktree.ts +13 -3
  507. package/src/tools/tasks.ts +10 -1
  508. package/src/tools/verify/authoring.ts +170 -83
  509. package/src/tools/verify/catalog.ts +122 -5
  510. package/src/tools/verify/index.ts +2 -1
  511. package/src/tools/verify/numeric.ts +298 -0
  512. package/src/tools/verify/perf.ts +143 -0
  513. package/src/tools/verify/scripts.ts +229 -2
  514. package/src/tools/worker-evidence.ts +3 -1
  515. package/src/worker/spec-contract.ts +4 -0
  516. package/dist/chunk-2Z2IKEXI.js +0 -1554
  517. package/dist/chunk-RVG5JXAL.js +0 -41
  518. package/dist/chunk-T56WDKA5.js +0 -183
  519. package/dist/chunk-VN3SHNBN.js +0 -313
  520. package/dist/chunk-VPKWYKEY.js +0 -169
  521. package/dist/reset-OAQP3W4O.js +0 -230
  522. package/dist/uninstall-N34PCTGJ.js +0 -331
  523. package/dist/upgrade-PXK3S2YM.js +0 -325
@@ -1,3 +1,5 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
1
3
  import { join } from "node:path";
2
4
  import chalk from "chalk";
3
5
  import { modelBootstrapGenerate, resolveBootstrapRoute } from "../cli/bootstrap-generate.js";
@@ -10,7 +12,7 @@ import { DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS } from "../core/defaults.js";
10
12
  import { loadDomains } from "../core/domain-loader.js";
11
13
  import type { SafeEventBus } from "../core/event-bus.js";
12
14
  import { expandInlineFileReferencesAsync } from "../core/file-references.js";
13
- import { setGitCommitAttributionEnabled } from "../core/git-commit-attribution.js";
15
+ import { setCommitDecisionRefsProvider, setGitCommitAttributionEnabled } from "../core/git-commit-attribution.js";
14
16
  import { configureGuardrails, guardrailValuesFromSettings } from "../core/guardrails.js";
15
17
  import { HEADLESS_PERMISSION_DENIED_REASON } from "../core/headless-permission.js";
16
18
  import { rememberRecentModel } from "../core/recent-models.js";
@@ -44,6 +46,7 @@ import { ConfigDomainModule, createConfigDomainModule } from "../domains/config/
44
46
  import type { ContextContract } from "../domains/context/contract.js";
45
47
  import { bootstrapInputFromInitOptions } from "../domains/context/init-options.js";
46
48
  import { createContextDomainModule } from "../domains/context/runtime.js";
49
+ import { endpointCapacityUsage } from "../domains/dispatch/capacity-lease.js";
47
50
  import type { DispatchContract } from "../domains/dispatch/contract.js";
48
51
  import { createDispatchDedupRegistration } from "../domains/dispatch/dedup.js";
49
52
  import { agentRoleFactsResolver } from "../domains/dispatch/execution-role.js";
@@ -77,6 +80,7 @@ import {
77
80
  taskMemoryTracePath,
78
81
  } from "../domains/memory/index.js";
79
82
  import { TaskMemoryBank } from "../domains/memory/task-bank.js";
83
+ import { TaskMemoryEndpointBusyError } from "../domains/memory/task-memory-policy.js";
80
84
  import {
81
85
  createDetachedDispatchNudgeRegistration,
82
86
  createReadOnlyExplorationNudgeRegistration,
@@ -99,8 +103,10 @@ import { createTaskBoardReminderRegistration } from "../domains/middleware/task-
99
103
  import { createTaskNudgeRegistration } from "../domains/middleware/task-nudge.js";
100
104
  import { createWatchdogRegistration } from "../domains/middleware/watchdog.js";
101
105
  import type { MuxContract } from "../domains/mux/index.js";
106
+ import type { BackgroundMemoryUsageSink } from "../domains/observability/background-memory-usage.js";
107
+ import { recordFailedCompactionCalls } from "../domains/observability/compaction-usage.js";
102
108
  import type { ObservabilityContract } from "../domains/observability/index.js";
103
- import { ObservabilityDomainModule, recordBackgroundMemoryStep } from "../domains/observability/index.js";
109
+ import { ObservabilityDomainModule } from "../domains/observability/index.js";
104
110
  import type { PromptsContract } from "../domains/prompts/contract.js";
105
111
  import { createPromptsDomainModule } from "../domains/prompts/index.js";
106
112
  import type { ProvidersContract, TargetDescriptor, ThinkingLevel } from "../domains/providers/index.js";
@@ -109,12 +115,12 @@ import {
109
115
  applyModelCapabilityPatch,
110
116
  canonicalEndpointKey,
111
117
  firstRuntimeResolutionError,
112
- foregroundStreamUsage,
113
118
  isOrchestratorEligibleRuntime,
114
119
  normalizeCostProvenance,
115
120
  ProvidersDomainModule,
116
121
  refineRuntimeTargetWithModelHints,
117
122
  registerForegroundStream,
123
+ resolveEndpointCapacities,
118
124
  resolveModelCapabilities,
119
125
  resolveModelRuntimeCapabilitiesForProviders,
120
126
  resolveRuntimeTarget,
@@ -122,8 +128,10 @@ import {
122
128
  targetRequiresAuth,
123
129
  VALID_THINKING_LEVELS,
124
130
  } from "../domains/providers/index.js";
131
+ import { hasLiveModelCatalog, modelResidencyForStatus } from "../domains/providers/model-discovery.js";
125
132
  import { memoryInterventionModelMaxTokens } from "../domains/providers/model-runtime-capabilities.js";
126
133
  import { getRuntimeRegistry } from "../domains/providers/registry.js";
134
+ import { resolveModelReference } from "../domains/providers/resolver.js";
127
135
  import { registerBuiltinRuntimes } from "../domains/providers/runtimes/builtins.js";
128
136
  import {
129
137
  createResourcesDomainModule,
@@ -135,7 +143,12 @@ import {
135
143
  import { DEFAULT_RECENT_ENTRY_LIMIT } from "../domains/safety/finish-contract.js";
136
144
  import { createFinishContractRegistration } from "../domains/safety/finish-contract-registration.js";
137
145
  import type { AutonomyLevel, SafetyContract } from "../domains/safety/index.js";
138
- import { parseRigorOverride, resolveRigor, SafetyDomainModule } from "../domains/safety/index.js";
146
+ import {
147
+ modelMayActivateSkills,
148
+ parseRigorOverride,
149
+ resolveRigor,
150
+ SafetyDomainModule,
151
+ } from "../domains/safety/index.js";
139
152
  import type { ProtectedArtifactState } from "../domains/safety/protected-artifacts.js";
140
153
  import {
141
154
  createProtectedArtifactsRegistration,
@@ -143,12 +156,13 @@ import {
143
156
  } from "../domains/safety/protected-artifacts-registration.js";
144
157
  import type { SchedulingContract } from "../domains/scheduling/contract.js";
145
158
  import { SchedulingDomainModule } from "../domains/scheduling/index.js";
159
+ import type { CompactionCallObservation } from "../domains/session/compaction/compact.js";
146
160
  import { type CompactResult, compact } from "../domains/session/compaction/compact.js";
147
161
  import { collectSessionEntries } from "../domains/session/compaction/session-entries.js";
148
162
  import { estimateTokens } from "../domains/session/compaction/tokens.js";
149
163
  import { ceilChars } from "../domains/session/context-accounting.js";
150
164
  import type { SessionContract, SessionMeta } from "../domains/session/contract.js";
151
- import { createDecisionBoardStore } from "../domains/session/decision-board.js";
165
+ import { activeDecisionRefs, createDecisionBoardStore } from "../domains/session/decision-board.js";
152
166
  import type { CompactionSummaryEntry, CompactionTrigger, SessionEntry } from "../domains/session/entries.js";
153
167
  import { SessionDomainModule } from "../domains/session/index.js";
154
168
  import {
@@ -164,10 +178,11 @@ import { createTaskBoardStore } from "../domains/session/task-board.js";
164
178
  import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
165
179
  import { type ShareContract, ShareDomainModule } from "../domains/share/index.js";
166
180
  import { ToolchainDomainModule } from "../domains/toolchain/index.js";
181
+ import { activeUserTaskAcceptance } from "../domains/user-tasks/active-acceptance.js";
167
182
  import { createUserTasksStore } from "../domains/user-tasks/store.js";
168
183
  import { type AcpSafeSettingsPatch, type AcpSafeSettingsSnapshot, serveClioAcpAgent } from "../engine/acp/server.js";
169
184
  import { createStdioServerTransport } from "../engine/acp/transport.js";
170
- import { completeEngineText } from "../engine/ai.js";
185
+ import { completeEngineText, type EngineTextCompletionResult } from "../engine/ai.js";
171
186
  import { setProtectedModelsProvider } from "../engine/apis/residency.js";
172
187
  import {
173
188
  createLoopGuardRegistration,
@@ -179,9 +194,12 @@ import type { EngineModel } from "../engine/types.js";
179
194
  import { createChatLoop } from "../interactive/chat-loop.js";
180
195
  import { type RunIo, startInteractive } from "../interactive/index.js";
181
196
  import { buildModelReplayAgentMessagesFromTurns } from "../interactive/model-session-replay.js";
197
+ import { prepareBackgroundModelMetadata } from "./background-model-metadata.js";
182
198
  import type { BootOptions } from "./boot-options.js";
199
+ import { readCompactionSystemPrompt } from "./compaction-prompt.js";
183
200
  import { createExtensionReloadCoordinator } from "./extension-reload.js";
184
201
  import { resolvePanesEnablement } from "./panes-activation.js";
202
+ import { bindTaskMemoryLifecycle, captureTaskMemoryUsage } from "./task-memory-lifecycle.js";
185
203
 
186
204
  export type { BootOptions, HeadlessSamplingOverrides } from "./boot-options.js";
187
205
 
@@ -287,6 +305,8 @@ interface CompactionResolution {
287
305
  model: EngineModel;
288
306
  targetId: string;
289
307
  endpointKey: string | null;
308
+ wireModelId: string;
309
+ headers?: Record<string, string>;
290
310
  apiKey?: string;
291
311
  }
292
312
 
@@ -312,53 +332,71 @@ function advanceThinkingLevel(current: ThinkingLevel, available: ReadonlyArray<T
312
332
  */
313
333
  const LOCAL_API_KEY_FALLBACK = "clio-coder-local-target";
314
334
 
315
- async function resolveApiKeyForTarget(
316
- target: TargetDescriptor,
317
- providers: ProvidersContract,
318
- signal?: AbortSignal,
319
- ): Promise<string | undefined> {
320
- const runtime = providers.getRuntime(target.runtime);
321
- if (!runtime) return undefined;
322
- // A target that needs no credential still needs the placeholder. Returning
323
- // nothing here left compaction as the one path that did not send it, so
324
- // `/context compact` and every automatic compaction at the window threshold
325
- // failed on exactly the local runtimes Clio is built for, while ordinary
326
- // turns against the same target succeeded.
327
- if (!targetRequiresAuth(target, runtime)) return LOCAL_API_KEY_FALLBACK;
328
- const resolved = await providers.auth.resolveForTarget(target, runtime, signal ? { signal } : undefined);
329
- return resolved.apiKey;
330
- }
331
-
332
335
  /**
333
336
  * The background memory role, resolved to the endpoint it would call.
334
337
  *
335
- * The endpoint key is what lets the middleware decline a step that would land
336
- * on the same inference scheduler the chat target is streaming against (#229,
337
- * #250), and it is the same key dispatch admission counts slots on.
338
+ * The endpoint key joins the existing dispatch capacity evidence and local
339
+ * request holds. A gateway key does not identify one physical model server or
340
+ * imply a one-slot limit; the configured protocol owns downstream routing.
338
341
  */
339
342
  interface BackgroundMemoryRoute {
340
343
  client: TaskMemoryModelClient;
344
+ selection: "dedicated" | "chat-fallback";
345
+ fallbackReason?: string;
341
346
  targetId: string;
342
347
  wireModelId: string;
343
348
  endpointKey: string | null;
344
349
  modelMaxTokens(configuredMaxTokens: number): number;
345
350
  }
346
351
 
347
- function createBackgroundMemoryModelClient(
352
+ export function createBackgroundMemoryModelClient(
348
353
  providers: ProvidersContract,
349
354
  settings: Readonly<ClioSettings>,
350
355
  timeoutMs: number,
351
356
  bus: Pick<SafeEventBus, "emit"> | null,
357
+ fallbackOnly = false,
352
358
  ): BackgroundMemoryRoute | null {
353
- const targetId = settings.context.memory.target?.trim();
354
- const wireModelId = settings.context.memory.model?.trim();
355
- if (!targetId || !wireModelId) return null;
356
- // One model cannot both drive the action agent and think its way through a
357
- // memory step: the memory call would contend with the agent's own decoding on
358
- // the same server, and a reasoning model spends most of a memory step
359
- // deliberating over a task it is not executing. Memory falls back to its free
360
- // deterministic tier rather than degrading the work the operator asked for.
361
- if (backgroundSharesReasoningModelWithOrchestrator(providers, settings)) return null;
359
+ const configuredTarget = settings.context.memory.target?.trim();
360
+ const configuredModel = settings.context.memory.model?.trim();
361
+ if (!configuredTarget || !configuredModel) return null;
362
+ const chatTarget = settings.chat.target?.trim();
363
+ const chatModel = settings.chat.model?.trim();
364
+ const sameRoute = configuredTarget === chatTarget && configuredModel === chatModel;
365
+ if (fallbackOnly && sameRoute) return null;
366
+ let fallbackReason: string | undefined;
367
+ if (!fallbackOnly) {
368
+ try {
369
+ return prepareBackgroundMemoryRoute(providers, configuredTarget, configuredModel, timeoutMs, bus, "dedicated");
370
+ } catch {
371
+ fallbackReason = "configured route unavailable";
372
+ }
373
+ } else fallbackReason = "configured route client error";
374
+ if (!chatTarget || !chatModel || sameRoute) {
375
+ if (fallbackOnly) return null;
376
+ throw new Error("configured memory route is unavailable and no distinct chat fallback can run");
377
+ }
378
+ try {
379
+ return {
380
+ ...prepareBackgroundMemoryRoute(providers, chatTarget, chatModel, timeoutMs, bus, "chat-fallback"),
381
+ fallbackReason,
382
+ };
383
+ } catch {
384
+ if (fallbackOnly) return null;
385
+ throw new Error("neither the configured memory route nor the active chat fallback is available");
386
+ }
387
+ }
388
+
389
+ function prepareBackgroundMemoryModel(providers: ProvidersContract, targetId: string, wireModelId: string) {
390
+ const status = providers.list().find((entry) => entry.target.id === targetId);
391
+ if (
392
+ status &&
393
+ (!status.available ||
394
+ status.health.status === "down" ||
395
+ (hasLiveModelCatalog(status) && !status.discoveredModels.includes(wireModelId)) ||
396
+ modelResidencyForStatus(status, wireModelId) === "absent" ||
397
+ modelResidencyForStatus(status, wireModelId) === "loading")
398
+ )
399
+ throw new Error("background memory route is unavailable");
362
400
  const resolved = resolveRuntimeTarget(providers, {
363
401
  targetId,
364
402
  wireModelId,
@@ -378,9 +416,37 @@ function createBackgroundMemoryModelClient(
378
416
  const model = resolved.target.runtime.synthesizeModel(resolved.target.target, resolved.target.wireModelId, kbHit);
379
417
  const refined = refineRuntimeTargetWithModelHints(resolved.target, model, providers.knowledgeBase);
380
418
  applyModelCapabilityPatch(model, refined.capabilities);
381
- const costProvenance = normalizeCostProvenance(refined.costProvenance);
419
+ return { model, refined };
420
+ }
421
+
422
+ function backgroundMemoryEndpointBusy(
423
+ providers: ProvidersContract,
424
+ endpointKey: string | null,
425
+ targets: ReadonlyArray<TargetDescriptor>,
426
+ ): boolean {
427
+ if (endpointKey === null) return false;
428
+ const capacity = resolveEndpointCapacities({
429
+ statuses: providers.list(),
430
+ targets,
431
+ runtimeFor: (id) => providers.getRuntime(id),
432
+ })[endpointKey];
433
+ // Gateways and non-fixed schedulers have no invented local one-slot cap.
434
+ return capacity !== undefined && (endpointCapacityUsage()[endpointKey] ?? 0) >= capacity.limit;
435
+ }
436
+
437
+ function prepareBackgroundMemoryRoute(
438
+ providers: ProvidersContract,
439
+ targetId: string,
440
+ wireModelId: string,
441
+ timeoutMs: number,
442
+ bus: Pick<SafeEventBus, "emit"> | null,
443
+ selection: BackgroundMemoryRoute["selection"],
444
+ ): BackgroundMemoryRoute {
445
+ const initial = prepareBackgroundMemoryModel(providers, targetId, wireModelId);
446
+ const { refined } = initial;
382
447
  const endpointKey = canonicalEndpointKey(refined.target);
383
448
  return {
449
+ selection,
384
450
  targetId,
385
451
  wireModelId: refined.wireModelId,
386
452
  endpointKey,
@@ -394,47 +460,121 @@ function createBackgroundMemoryModelClient(
394
460
  // Wrapped at the layer that knows a request left the process: the step
395
461
  // holds endpoint capacity while it is out and publishes the cache
396
462
  // disturbance even when a timeout means the usage sink never sees it.
397
- complete: announceMemoryStepEndpoint({ bus, endpointKey, targetId }, async (request) => {
463
+ complete: async (request) => {
464
+ await prepareBackgroundModelMetadata(providers, targetId, request.signal);
465
+ const { model, refined } = prepareBackgroundMemoryModel(providers, targetId, wireModelId);
466
+ if (refined.runtimeId !== initial.refined.runtimeId || canonicalEndpointKey(refined.target) !== endpointKey) {
467
+ throw new Error("background memory target changed during preparation");
468
+ }
469
+ const costProvenance = normalizeCostProvenance(refined.costProvenance);
398
470
  const apiKey = targetRequiresAuth(refined.target, refined.runtime)
399
471
  ? (await providers.auth.resolveForTarget(refined.target, refined.runtime, { signal: request.signal })).apiKey
400
472
  : LOCAL_API_KEY_FALLBACK;
401
- const startedAt = Date.now();
402
- const completion = await completeEngineText({
403
- model,
404
- systemPrompt: request.systemPrompt,
405
- userPrompt: request.userPrompt,
406
- maxTokens: request.maxTokens,
407
- // Always off, never the operator's chat thinking level. A model that
408
- // reasons anyway still works: `completeEngineText` keeps only text
409
- // blocks, and the memory output budget leaves room for the preamble.
410
- thinkingLevel: "off",
411
- signal: request.signal,
412
- timeoutMs,
413
- ...(apiKey === undefined ? {} : { apiKey }),
414
- });
415
- // The step is billed here whatever the policy later decides about the
416
- // answer. A model that read a trajectory and chose silence spent the
417
- // same prefill as one that produced a reminder.
418
- return {
419
- text: completion.text,
420
- inputTokens: completion.inputTokens,
421
- outputTokens: completion.outputTokens,
422
- usage: {
473
+ request.signal.throwIfAborted();
474
+ // Preparation yielded after the middleware's first check. Recheck current
475
+ // occupancy immediately before the synchronous foreground-slot registration;
476
+ // no await may separate this admission from announceMemoryStepEndpoint.
477
+ if (backgroundMemoryEndpointBusy(providers, endpointKey, [refined.target])) {
478
+ throw new TaskMemoryEndpointBusyError("background memory endpoint is busy");
479
+ }
480
+ return announceMemoryStepEndpoint({ bus, endpointKey, targetId }, async () => {
481
+ const startedAt = Date.now();
482
+ let observedUsage: TaskMemoryStepUsage | undefined;
483
+ const mapUsage = (completion: Pick<EngineTextCompletionResult, "usage" | "backend">): TaskMemoryStepUsage => ({
484
+ ...completion.usage,
423
485
  targetId,
424
486
  attributedModelId: refined.wireModelId,
425
- input: completion.usage.input,
426
- output: completion.usage.output,
427
- cacheRead: completion.usage.cacheRead,
428
- cacheWrite: completion.usage.cacheWrite,
429
- reasoning: completion.usage.reasoning,
430
- totalTokens: completion.usage.totalTokens,
431
- costUsd: completion.usage.costUsd,
432
487
  costProvenance,
433
488
  durationMs: Date.now() - startedAt,
434
489
  backend: completion.backend,
435
- },
436
- };
437
- }),
490
+ });
491
+ const completion = await completeEngineText({
492
+ model,
493
+ systemPrompt: request.systemPrompt,
494
+ userPrompt: request.userPrompt,
495
+ maxTokens:
496
+ refined.capabilityDecisions.maxTokens > 0
497
+ ? Math.min(request.maxTokens, refined.capabilityDecisions.maxTokens)
498
+ : request.maxTokens,
499
+ // Always off, never the operator's chat thinking level. A model that
500
+ // reasons anyway still works: `completeEngineText` keeps only text
501
+ // blocks, and the memory output budget leaves room for the preamble.
502
+ thinkingLevel: "off",
503
+ signal: request.signal,
504
+ timeoutMs,
505
+ onUsage: (observation) => {
506
+ observedUsage = mapUsage(observation);
507
+ request.onUsage?.(observedUsage);
508
+ },
509
+ ...(apiKey === undefined ? {} : { apiKey }),
510
+ ...(refined.target.auth?.headers ? { headers: refined.target.auth.headers } : {}),
511
+ });
512
+ // The step is billed here whatever the policy later decides about the
513
+ // answer. A model that read a trajectory and chose silence spent the
514
+ // same prefill as one that produced a reminder.
515
+ return {
516
+ text: completion.text,
517
+ inputTokens: completion.inputTokens,
518
+ outputTokens: completion.outputTokens,
519
+ usage: observedUsage ?? mapUsage(completion),
520
+ };
521
+ })(request);
522
+ },
523
+ },
524
+ };
525
+ }
526
+
527
+ /** Production callback composition shared with routing/capacity contracts. */
528
+ export function createBackgroundMemoryRouting(
529
+ providers: ProvidersContract,
530
+ getSettings: () => Readonly<ClioSettings> | undefined,
531
+ bus: Pick<SafeEventBus, "emit"> | null,
532
+ ) {
533
+ let route: BackgroundMemoryRoute | null = null;
534
+ let snapshot: Readonly<ClioSettings> | undefined;
535
+ let lastNotice: string | null = null;
536
+ const noteFallback = (): void => {
537
+ if (route?.selection !== "chat-fallback") {
538
+ lastNotice = null;
539
+ return;
540
+ }
541
+ const message = `Memory: ${route.fallbackReason}; selected chat fallback ${route.targetId}/${route.wireModelId} for this step, subject to available endpoint capacity.`;
542
+ if (message === lastNotice) return;
543
+ lastNotice = message;
544
+ try {
545
+ bus?.emit(BusChannels.RuntimeNotice, {
546
+ kind: "degraded",
547
+ level: "info",
548
+ message,
549
+ targetId: route.targetId,
550
+ runtimeId: providers.getTarget(route.targetId)?.runtime ?? "unknown",
551
+ model: route.wireModelId,
552
+ });
553
+ } catch {
554
+ /* Advisory only. */
555
+ }
556
+ };
557
+ return {
558
+ getModelClient: (): TaskMemoryModelClient | null => {
559
+ const current = getSettings();
560
+ snapshot = current === undefined ? undefined : structuredClone(current);
561
+ route =
562
+ snapshot === undefined
563
+ ? null
564
+ : createBackgroundMemoryModelClient(providers, snapshot, snapshot.context.memory.timeoutMs, bus);
565
+ noteFallback();
566
+ return route?.client ?? null;
567
+ },
568
+ getFallbackModelClient: (): TaskMemoryModelClient | null => {
569
+ if (route?.selection !== "dedicated" || snapshot === undefined) return null;
570
+ route = createBackgroundMemoryModelClient(providers, snapshot, snapshot.context.memory.timeoutMs, bus, true);
571
+ noteFallback();
572
+ return route?.client ?? null;
573
+ },
574
+ getModelMaxTokens: (configured: number): number => route?.modelMaxTokens(configured) ?? configured,
575
+ backgroundEndpointBusy: (): boolean => {
576
+ if (route?.endpointKey == null || snapshot === undefined) return false;
577
+ return backgroundMemoryEndpointBusy(providers, route.endpointKey, snapshot.targets);
438
578
  },
439
579
  };
440
580
  }
@@ -472,74 +612,84 @@ function agentRoleToolWarnings(providers: ProvidersContract, settings: Readonly<
472
612
  return warnings;
473
613
  }
474
614
 
475
- /**
476
- * True when the background role names the same model the orchestrator runs and
477
- * that model reasons. Distinct models, or a non-reasoning shared model, are both
478
- * fine; this only catches the one-model-does-everything configuration.
479
- */
480
- function backgroundSharesReasoningModelWithOrchestrator(
615
+ async function resolveCompactionModel(
616
+ settings: ClioSettings,
481
617
  providers: ProvidersContract,
482
- settings: Readonly<ClioSettings>,
483
- ): boolean {
484
- const backgroundModel = settings.context.memory.model?.trim();
485
- const orchestratorModel = settings.chat.model?.trim();
486
- if (!backgroundModel || backgroundModel !== orchestratorModel) return false;
487
- if (settings.context.memory.target?.trim() !== settings.chat.target?.trim()) return false;
488
- try {
489
- const status = providers.list().find((entry) => entry.target.id === settings.context.memory.target?.trim());
490
- if (!status) return false;
491
- return resolveModelCapabilities(status, backgroundModel, providers.knowledgeBase).reasoning === true;
492
- } catch {
493
- // An unresolvable capability is not evidence of a reasoning model; the
494
- // operator's explicit configuration stands.
495
- return false;
618
+ ): Promise<CompactionResolution | null> {
619
+ const override = settings.context.compaction.model;
620
+ let targetId = settings.chat.target;
621
+ let wireModelId = settings.chat.model;
622
+ if (override !== undefined && override !== null) {
623
+ let selected: ReturnType<typeof resolveModelReference>;
624
+ try {
625
+ selected = resolveModelReference(override, providers);
626
+ } catch {
627
+ throw new Error("context.compaction.model is an invalid pattern; set a unique target/model reference");
628
+ }
629
+ if (!selected.ref || selected.warning) {
630
+ throw new Error(
631
+ "context.compaction.model must match exactly one configured model; set a unique target/model reference",
632
+ );
633
+ }
634
+ targetId = selected.ref.target;
635
+ wireModelId = selected.ref.model;
496
636
  }
497
- }
498
-
499
- function synthesizeOrchestratorModel(
500
- providers: ProvidersContract,
501
- target: TargetDescriptor,
502
- wireModelId: string,
503
- ): EngineModel | null {
504
- const runtime = providers.getRuntime(target.runtime);
505
- if (!runtime) return null;
637
+ if (!targetId || !wireModelId) return null;
638
+ await prepareBackgroundModelMetadata(providers, targetId);
639
+ const status = providers.list().find((entry) => entry.target.id === targetId);
640
+ if (!status?.available) {
641
+ throw new Error("context.compaction.model target is unavailable; check the selected target with clio-coder targets");
642
+ }
643
+ const resolved = resolveRuntimeTarget(providers, {
644
+ targetId,
645
+ wireModelId,
646
+ use: "orchestrator",
647
+ requestedThinkingLevel: "off",
648
+ requireTools: false,
649
+ requireStreaming: true,
650
+ requireOutputBudget: true,
651
+ });
652
+ if (!resolved.ok) {
653
+ // Configured identifiers and provider diagnostics can contain terminal controls.
654
+ // Keep the failure actionable without echoing arbitrary provider/config text.
655
+ throw new Error(
656
+ "context.compaction.model cannot run as a summarizer; select an available HTTP chat model with an output budget",
657
+ );
658
+ }
659
+ const route = resolved.target;
506
660
  let model: EngineModel;
507
661
  try {
508
- const kbHit = providers.knowledgeBase?.lookup(wireModelId) ?? null;
509
- model = runtime.synthesizeModel(target, wireModelId, kbHit);
662
+ model = route.runtime.synthesizeModel(
663
+ route.target,
664
+ route.wireModelId,
665
+ providers.knowledgeBase?.lookup(route.wireModelId) ?? null,
666
+ );
510
667
  } catch {
511
- return null;
668
+ throw new Error("context.compaction.model could not be prepared; check the selected target/model configuration");
512
669
  }
513
- try {
514
- const status = providers.list().find((entry) => entry.target.id === target.id);
515
- if (status) {
516
- const caps = resolveModelCapabilities(status, wireModelId, providers.knowledgeBase, {
517
- detectedReasoning: providers.getDetectedReasoning(target.id, wireModelId),
518
- });
519
- applyModelCapabilityPatch(model, caps);
670
+ const refined = refineRuntimeTargetWithModelHints(route, model, providers.knowledgeBase);
671
+ if (refined.capabilityDecisions.maxTokens <= 0) {
672
+ throw new Error("context.compaction.model has no output budget; select a model with a known positive output limit");
673
+ }
674
+ applyModelCapabilityPatch(model, refined.capabilities);
675
+ let apiKey: string | undefined = LOCAL_API_KEY_FALLBACK;
676
+ if (targetRequiresAuth(route.target, route.runtime)) {
677
+ const auth = await providers.auth.resolveForTarget(route.target, route.runtime);
678
+ if (!auth.available || !auth.apiKey) {
679
+ throw new Error(
680
+ "context.compaction.model authentication is unavailable; authenticate the selected target with clio-coder auth",
681
+ );
520
682
  }
521
- } catch {
522
- // Older test doubles and degraded provider bundles may not expose live
523
- // status. The synthesized model still carries runtime and catalog caps.
683
+ apiKey = auth.apiKey;
524
684
  }
525
- return model;
526
- }
527
-
528
- async function resolveCompactionModel(
529
- settings: ClioSettings,
530
- providers: ProvidersContract,
531
- ): Promise<CompactionResolution | null> {
532
- const targetId = settings.chat?.target ?? null;
533
- const wireModelId = settings.chat?.model ?? null;
534
- if (!targetId || !wireModelId) return null;
535
- const target = resolveTarget(providers, targetId);
536
- if (!target) return null;
537
- const model = synthesizeOrchestratorModel(providers, target, wireModelId);
538
- if (!model) return null;
539
- const apiKey = await resolveApiKeyForTarget(target, providers);
540
- const resolution: CompactionResolution = { model, targetId, endpointKey: canonicalEndpointKey(target) };
541
- if (apiKey !== undefined) resolution.apiKey = apiKey;
542
- return resolution;
685
+ return {
686
+ model,
687
+ targetId: route.targetId,
688
+ wireModelId: route.wireModelId,
689
+ endpointKey: canonicalEndpointKey(route.target),
690
+ apiKey,
691
+ ...(route.target.auth?.headers ? { headers: route.target.auth.headers } : {}),
692
+ };
543
693
  }
544
694
 
545
695
  function readSessionEntriesForCompact(sessionId: string): SessionEntry[] {
@@ -644,11 +794,19 @@ async function runCompactionFlow(
644
794
  providers: ProvidersContract,
645
795
  instructions?: string,
646
796
  trigger?: CompactionTrigger,
797
+ observability?: BackgroundMemoryUsageSink,
647
798
  ): Promise<CompactResult | null> {
648
799
  const meta = session.current();
649
800
  if (!meta) {
650
801
  throw new Error("no current session to compact; start one with /new or /resume first");
651
802
  }
803
+ const stateDir = clioStateDir();
804
+ const activeLeafTurnId = session.tree(meta.id).leafId ?? undefined;
805
+ const isOriginCurrent = () =>
806
+ session.current()?.id === meta.id && (session.tree(meta.id).leafId ?? undefined) === activeLeafTurnId;
807
+ const entries = filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId);
808
+ if (entries.length === 0) return null;
809
+ const systemPrompt = await readCompactionSystemPrompt(settings.context.compaction.systemPrompt, meta.cwd);
652
810
  const resolved = await resolveCompactionModel(settings, providers);
653
811
  if (!resolved) {
654
812
  throw new Error("no model configured; set chat.target + chat.model");
@@ -661,11 +819,22 @@ async function runCompactionFlow(
661
819
  // read the full file too (last taskLedger entry in file order, with no
662
820
  // branch filter at all), which was a second, independent instance of this
663
821
  // same bug. See the taskBoard wiring below for the fix.
664
- const activeLeafTurnId = session.tree(meta.id).leafId ?? undefined;
665
- const entries = filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId);
666
- if (entries.length === 0) return null;
822
+ if (!isOriginCurrent()) throw new Error("compaction session or branch changed before summarization");
823
+ const calls: CompactionCallObservation[] = [];
824
+ const preserveCalls = () =>
825
+ recordFailedCompactionCalls(
826
+ {
827
+ stateDir,
828
+ sessionId: meta.id,
829
+ repoIdentity: meta.cwdHash || cwdHash(meta.cwd),
830
+ target: resolved.targetId,
831
+ model: resolved.wireModelId,
832
+ },
833
+ calls,
834
+ isOriginCurrent() ? observability : undefined,
835
+ );
667
836
 
668
- // A compaction summary is a full streamed request against the chat target.
837
+ // A compaction summary is a full streamed request against its resolved target.
669
838
  // Hold the same canonical endpoint slot as an ordinary turn, /btw round, or
670
839
  // pre-warm so dispatch admission and background memory see its real usage.
671
840
  const releaseEndpointSlot = resolved.endpointKey === null ? () => {} : registerForegroundStream(resolved.endpointKey);
@@ -673,17 +842,39 @@ async function runCompactionFlow(
673
842
  try {
674
843
  result = await compact({
675
844
  entries,
845
+ onCall: (call) => calls.push(call),
676
846
  model: resolved.model,
847
+ ...(systemPrompt !== undefined ? { systemPrompt } : {}),
848
+ ...(resolved.headers !== undefined ? { headers: resolved.headers } : {}),
677
849
  ...(resolved.apiKey !== undefined ? { apiKey: resolved.apiKey } : {}),
678
850
  ...(instructions !== undefined ? { instructions } : {}),
679
851
  });
852
+ } catch (error) {
853
+ try {
854
+ preserveCalls();
855
+ } catch (recordingError) {
856
+ throw new AggregateError([error, recordingError], "compaction failed and its usage could not be fully recorded");
857
+ }
858
+ throw error;
680
859
  } finally {
681
860
  releaseEndpointSlot();
682
861
  }
683
- if (result.messagesSummarized === 0 || result.summary.length === 0) return null;
862
+ if (!isOriginCurrent()) {
863
+ preserveCalls();
864
+ throw new Error("compaction session or branch changed; summary discarded and originating usage retained");
865
+ }
866
+ if (result.messagesSummarized === 0 || result.summary.length === 0) {
867
+ preserveCalls();
868
+ if (calls.length > 0) throw new Error("compaction returned no summary; reported usage retained without a checkpoint");
869
+ return null;
870
+ }
871
+ if (result.usage) {
872
+ result.usage = { ...result.usage, targetId: resolved.targetId, modelId: resolved.wireModelId };
873
+ }
684
874
 
685
- const entry: Omit<CompactionSummaryEntry, "turnId" | "timestamp"> = {
875
+ const entry: Omit<CompactionSummaryEntry, "timestamp"> = {
686
876
  kind: "compactionSummary",
877
+ turnId: randomUUID(),
687
878
  parentTurnId: result.firstKeptTurnId ?? null,
688
879
  summary: result.summary,
689
880
  tokensBefore: result.tokensBefore,
@@ -697,22 +888,61 @@ async function runCompactionFlow(
697
888
  ...(result.usage !== undefined ? { usage: result.usage } : {}),
698
889
  };
699
890
  if (trigger !== undefined) entry.trigger = trigger;
700
- session.appendEntry(entry);
891
+ try {
892
+ session.appendEntry(entry);
893
+ } catch (error) {
894
+ // A write may have reached the ledger before its caller threw. Reconcile
895
+ // this exact checkpoint identity before choosing a second accounting store.
896
+ try {
897
+ if (!compactionCheckpointWasWritten(sessionPaths(meta).current, entry.turnId)) preserveCalls();
898
+ } catch (recordingError) {
899
+ throw new AggregateError(
900
+ [error, recordingError],
901
+ "compaction checkpoint persistence is unresolved; usage was not duplicated",
902
+ );
903
+ }
904
+ throw error;
905
+ }
701
906
  return result;
702
907
  }
703
908
 
909
+ function compactionCheckpointWasWritten(path: string, turnId: string): boolean {
910
+ let malformed = false;
911
+ for (const line of readFileSync(path, "utf8").split("\n")) {
912
+ if (line.trim().length === 0) continue;
913
+ try {
914
+ const entry: unknown = JSON.parse(line);
915
+ if (
916
+ entry &&
917
+ typeof entry === "object" &&
918
+ "turnId" in entry &&
919
+ entry.turnId === turnId &&
920
+ "kind" in entry &&
921
+ entry.kind === "compactionSummary"
922
+ )
923
+ return true;
924
+ } catch {
925
+ malformed = true;
926
+ }
927
+ }
928
+ if (malformed) throw new Error("cannot prove checkpoint absence in a malformed session ledger");
929
+ return false;
930
+ }
931
+
704
932
  /**
705
933
  * Compose the production chat-loop compaction callback. Errors intentionally
706
934
  * propagate: the chat loop owns activity failure reporting and distinguishes
707
935
  * a thrown read/model/persistence failure from the legitimate null no-op that
708
936
  * `runCompactionFlow` returns for an empty session or an unavailable cut.
709
937
  */
710
- function createProductionAutoCompact(
938
+ export function createProductionAutoCompact(
711
939
  session: SessionContract,
712
940
  getSettings: () => ClioSettings,
713
941
  providers: ProvidersContract,
942
+ observability?: BackgroundMemoryUsageSink,
714
943
  ): (instructions?: string, trigger?: CompactionTrigger) => Promise<CompactResult | null> {
715
- return (instructions, trigger) => runCompactionFlow(session, getSettings(), providers, instructions, trigger);
944
+ return (instructions, trigger) =>
945
+ runCompactionFlow(session, getSettings(), providers, instructions, trigger, observability);
716
946
  }
717
947
 
718
948
  function estimateTokensFromSummary(summary: string): number {
@@ -1167,37 +1397,12 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1167
1397
  middleware.registerHook(
1168
1398
  createSkillActivationObserver((activation) => appendSkillActivationRegistryEvent(session, activation)),
1169
1399
  );
1170
- // First-turn skills reminder: user-message-visible text is the one channel
1171
- // the battery-tested local models act on; suggestion protocol only, the
1172
- // operator load gate is untouched.
1173
- if (resources) {
1174
- middleware.registerHook(
1175
- createSkillsReminderRegistration({
1176
- countModelVisibleSkills: () => modelVisibleSkills(resources.skills(process.cwd()).items).length,
1177
- // Same lookup context(scope="skills") lists under its Marketplace
1178
- // heading, minus what is already installed, so the count the
1179
- // reminder quotes is the count the listing will show.
1180
- countInstallableSkills: () => {
1181
- const installed = new Set(resources.skills(process.cwd()).items.map((skill) => skill.name));
1182
- return discoverMarketplaceSkills({ cwd: process.cwd() }).skills.filter((skill) => !installed.has(skill.name))
1183
- .length;
1184
- },
1185
- }),
1186
- );
1187
- }
1188
1400
  // Task-board reminder: same user-message-visible channel, fired once per
1189
1401
  // session when a request literally enumerates three or more steps. The
1190
1402
  // static routing line and tasks hint ask for the same board; battery-tested
1191
1403
  // local models only comply when the instruction rides the user message.
1192
1404
  middleware.registerHook(createTaskBoardReminderRegistration());
1193
1405
  const taskMemoryBank = new TaskMemoryBank();
1194
- let taskMemorySessionId = session?.current()?.id ?? null;
1195
- const ensureTaskMemorySession = (): void => {
1196
- const currentSessionId = session?.current()?.id ?? null;
1197
- if (currentSessionId === taskMemorySessionId) return;
1198
- taskMemoryBank.clear();
1199
- taskMemorySessionId = currentSessionId;
1200
- };
1201
1406
  const memorySettings = (config?.get() ?? readSettings()).context.memory;
1202
1407
  // Bound late: the registration is built here, but the buffer a deferred
1203
1408
  // reminder lands in belongs to the chat loop that has not been composed yet.
@@ -1212,7 +1417,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1212
1417
  const memoryTrace = memoryTracePath === null ? null : createTaskMemoryTrace(memoryTracePath);
1213
1418
  // The route the last resolved memory client would call, kept so the endpoint
1214
1419
  // check and the cost row read the same resolution the step itself used.
1215
- let backgroundMemoryRoute: BackgroundMemoryRoute | null = null;
1216
1420
  /**
1217
1421
  * Account for one background memory step exactly as a `/btw` side question is
1218
1422
  * accounted for: the in-process cost tracker under its own label so `/cost`
@@ -1221,23 +1425,16 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1221
1425
  *
1222
1426
  * Accounting only. The client wrapper (`announceMemoryStepEndpoint`) holds the
1223
1427
  * endpoint slot and publishes the disturbance; it sees the timed-out and
1224
- * thrown steps this sink never hears about because their usage stays null.
1428
+ * thrown steps that report no usage. Late reported usage still reaches this sink.
1225
1429
  */
1226
- const recordBackgroundMemoryUsage = (usage: TaskMemoryStepUsage): void => {
1430
+ const captureBackgroundMemoryUsage = () => {
1227
1431
  const meta = session?.current() ?? null;
1228
- try {
1229
- recordBackgroundMemoryStep({
1230
- usage,
1231
- stateDir: clioStateDir(),
1232
- sessionId: meta?.id ?? null,
1233
- // The identity the session ledger is filed under, so `usage report
1234
- // --repo` selects these rows with the same hash it selects ledgers with.
1235
- repoIdentity: meta ? meta.cwdHash || cwdHash(meta.cwd || process.cwd()) : null,
1236
- ...(observability === undefined ? {} : { observability }),
1237
- });
1238
- } catch {
1239
- // Accounting is bookkeeping. Its failure never reaches the memory step.
1240
- }
1432
+ return captureTaskMemoryUsage({
1433
+ stateDir: clioStateDir(),
1434
+ sessionId: meta?.id ?? null,
1435
+ repoIdentity: meta ? meta.cwdHash || cwdHash(meta.cwd || process.cwd()) : null,
1436
+ ...(observability === undefined ? {} : { observability }),
1437
+ });
1241
1438
  };
1242
1439
  const proposeInjectedMemoryEntries = (entries: ReadonlyArray<TaskMemoryEntry>): void => {
1243
1440
  const meta = session?.current() ?? null;
@@ -1266,7 +1463,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1266
1463
  deliversDeferredReminders: options.headless === undefined,
1267
1464
  onDeferredReminder: (message) => deferredMemoryReminderSink?.(message),
1268
1465
  getSettings: () => {
1269
- ensureTaskMemorySession();
1270
1466
  const memory = effectiveSettingsForDispatch?.().context.memory ?? memorySettings;
1271
1467
  return {
1272
1468
  enabled: memory.enabled,
@@ -1276,34 +1472,17 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1276
1472
  timeoutMs: memory.timeoutMs,
1277
1473
  };
1278
1474
  },
1279
- getModelClient: () => {
1280
- const settings = effectiveSettingsForDispatch?.();
1281
- backgroundMemoryRoute =
1282
- settings === undefined
1283
- ? null
1284
- : createBackgroundMemoryModelClient(providers, settings, settings.context.memory.timeoutMs, bus);
1285
- return backgroundMemoryRoute?.client ?? null;
1286
- },
1287
- getModelMaxTokens: (configuredMaxTokens) =>
1288
- backgroundMemoryRoute?.modelMaxTokens(configuredMaxTokens) ?? configuredMaxTokens,
1289
- // A single-slot local server serves one request at a time, so a memory step
1290
- // started while the operator's turn is streaming either waits behind it or
1291
- // makes the server swap the resident model out to answer. The chat loop
1292
- // registers its stream on the endpoint it streams against, so the two are
1293
- // compared by the same canonical key dispatch admission counts slots on.
1294
- backgroundEndpointBusy: () => {
1295
- const endpointKey = backgroundMemoryRoute?.endpointKey ?? null;
1296
- if (endpointKey === null) return false;
1297
- // This runs before the client wrapper registers the admitted step, so the
1298
- // count contains other requests only and the step cannot refuse itself.
1299
- return (foregroundStreamUsage()[endpointKey] ?? 0) > 0;
1300
- },
1301
- onStepUsage: (usage) => recordBackgroundMemoryUsage(usage),
1475
+ ...createBackgroundMemoryRouting(providers, () => effectiveSettingsForDispatch?.(), bus),
1476
+ captureStepUsage: captureBackgroundMemoryUsage,
1302
1477
  onInjectedEntries: (entries) => proposeInjectedMemoryEntries(entries),
1303
1478
  });
1304
1479
  middleware.registerHook(memoryIntervention);
1305
1480
  const unsubscribeMemoryLoop = bus.on(BusChannels.LoopBlocked, () => memoryIntervention.signalLoop());
1306
- termination.onDrain(() => unsubscribeMemoryLoop());
1481
+ const disposeMemoryLifecycle = bindTaskMemoryLifecycle(bus, memoryIntervention);
1482
+ termination.onDrain(() => {
1483
+ unsubscribeMemoryLoop();
1484
+ disposeMemoryLifecycle();
1485
+ });
1307
1486
  if (contextDomain) {
1308
1487
  middleware.registerHook(createFileMutationObserver(({ paths }) => contextDomain.noteFileChanges(paths)));
1309
1488
  }
@@ -1346,17 +1525,35 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1346
1525
  (config?.get() ?? readSettings()).safety.autonomy ??
1347
1526
  "auto-edit";
1348
1527
  const resolveEffectiveAutonomy = (): AutonomyLevel => activeAcpSessionAutonomy ?? resolveBaselineAutonomy();
1528
+ // First-turn skills reminder: user-message-visible text is the one channel
1529
+ // the battery-tested local models act on. Which protocol it teaches follows
1530
+ // the effective autonomy level, resolved one line up: suggest-and-wait at
1531
+ // read-only and suggest, load-it-yourself at auto-edit and full-auto.
1532
+ if (resources) {
1533
+ middleware.registerHook(
1534
+ createSkillsReminderRegistration({
1535
+ countModelVisibleSkills: () => modelVisibleSkills(resources.skills(process.cwd()).items).length,
1536
+ // Same lookup context(scope="skills") lists under its Marketplace
1537
+ // heading, minus what is already installed, so the count the
1538
+ // reminder quotes is the count the listing will show.
1539
+ countInstallableSkills: () => {
1540
+ const installed = new Set(resources.skills(process.cwd()).items.map((skill) => skill.name));
1541
+ return discoverMarketplaceSkills({ cwd: process.cwd() }).skills.filter((skill) => !installed.has(skill.name))
1542
+ .length;
1543
+ },
1544
+ modelMayActivateSkills: () => modelMayActivateSkills(resolveEffectiveAutonomy()),
1545
+ }),
1546
+ );
1547
+ }
1349
1548
  // Marketplace self-promotion: coordinator-only by this wiring (never a
1350
- // dispatch worker), local matcher, consented installs and full-auto
1351
- // autonomous installs both pass the own-marketplace source gate inside the
1352
- // registration. Registered here because it needs the effective-autonomy
1353
- // resolution defined one line up.
1549
+ // dispatch worker), local matcher and operator-consented installs at every autonomy level. The
1550
+ // registration also checks the own-marketplace source gate before installing.
1354
1551
  if (resources) {
1355
1552
  middleware.registerHook(
1356
1553
  createMarketplaceOfferRegistration({
1554
+ interactive,
1357
1555
  listInstalledSkillNames: () => resources.skills(process.cwd()).items.map((skill) => skill.name),
1358
1556
  listMarketplaceEntries: () => discoverMarketplaceSkills({ cwd: process.cwd() }).skills,
1359
- getAutonomy: resolveEffectiveAutonomy,
1360
1557
  installEntry: (entry, scope) => {
1361
1558
  const installed = installSkill({ source: entry.sourceUrl, scope, name: entry.name, cwd: process.cwd() });
1362
1559
  return { path: installed.path, sourceUrl: installed.sourceUrl, installedHash: installed.installedHash };
@@ -1432,6 +1629,9 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1432
1629
  session.appendEntry(entry);
1433
1630
  },
1434
1631
  });
1632
+ // Every Clio-spawned commit from this session is stamped with the decisions
1633
+ // active on the board at spawn time, the same way it is stamped as assisted.
1634
+ setCommitDecisionRefsProvider(() => activeDecisionRefs(decisionBoard.snapshot()));
1435
1635
  // getSessionId alone never notices a /tree switch: it moves the active
1436
1636
  // append point inside the same session, so the id-keyed cache above kept
1437
1637
  // showing the abandoned branch's board (issue #94). SessionTurnSwitched is
@@ -1491,6 +1691,8 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1491
1691
  }
1492
1692
  : {}),
1493
1693
  taskBoard,
1694
+ decisionBoard,
1695
+ getDecisionBoard: () => decisionBoard.snapshot(),
1494
1696
  userTasks,
1495
1697
  dispatch,
1496
1698
  bus,
@@ -1593,7 +1795,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1593
1795
  return taskMemoryHandoffSeedOffer(process.cwd(), getCurrentSettings().context.memory.enabled);
1594
1796
  };
1595
1797
  const seedCurrentTaskMemoryFromHandoff = () => {
1596
- ensureTaskMemorySession();
1597
1798
  return seedTaskMemoryFromNewestHandoff(taskMemoryBank, process.cwd(), getCurrentSettings().context.memory.enabled);
1598
1799
  };
1599
1800
  if (resumedSessionAtBoot) {
@@ -1822,6 +2023,8 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1822
2023
  },
1823
2024
  resolveRigor: () =>
1824
2025
  resolveRigor({ cwd: process.cwd(), override: parseRigorOverride(process.env.CLIO_CODER_RIGOR) }),
2026
+ readActiveAcceptance: (window) =>
2027
+ activeUserTaskAcceptance(userTasks.snapshot(), taskBoard.snapshot(), session.current()?.id ?? null, window),
1825
2028
  recordDecision: (record) => safety.audit.recordCompletionContract?.(record),
1826
2029
  }),
1827
2030
  );
@@ -1835,6 +2038,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1835
2038
  return key === null ? null : registerForegroundStream(key);
1836
2039
  },
1837
2040
  getSettings: getCurrentSettings,
2041
+ getAutonomy: resolveEffectiveAutonomy,
1838
2042
  providers,
1839
2043
  middleware,
1840
2044
  middlewareToolChoice,
@@ -1863,7 +2067,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1863
2067
  }
1864
2068
  },
1865
2069
  getTaskMemoryHandoffSource: () => {
1866
- ensureTaskMemorySession();
1867
2070
  const meta = session?.current();
1868
2071
  if (!meta) throw new Error("task memory handoff requires an active session");
1869
2072
  const settings = getCurrentSettings();
@@ -1888,7 +2091,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1888
2091
  ...(session
1889
2092
  ? {
1890
2093
  readSessionEntries: readCurrentSessionEntries,
1891
- autoCompact: createProductionAutoCompact(session, getCurrentSettings, providers),
2094
+ autoCompact: createProductionAutoCompact(session, getCurrentSettings, providers, observability),
1892
2095
  }
1893
2096
  : {}),
1894
2097
  toolRegistry,
@@ -2165,7 +2368,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2165
2368
  supersedeDecision: (interviewId, key, correction) => decisionBoard.supersede(interviewId, key, correction),
2166
2369
  userTasks,
2167
2370
  getTaskMemoryStatus: () => {
2168
- ensureTaskMemorySession();
2169
2371
  const settings = getCurrentSettings();
2170
2372
  const bank = taskMemoryBank.snapshot();
2171
2373
  return {
@@ -2319,10 +2521,8 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2319
2521
  ? {
2320
2522
  onResumeSession: (sessionId) => {
2321
2523
  try {
2322
- const previousSessionId = session.current()?.id ?? null;
2323
2524
  session.resume(sessionId);
2324
2525
  taskBoard.snapshot();
2325
- if (previousSessionId !== sessionId) ensureTaskMemorySession();
2326
2526
  } catch (err) {
2327
2527
  process.stderr.write(
2328
2528
  `[/resume] failed to resume ${sessionId}: ${err instanceof Error ? err.message : String(err)}\n`,
@@ -2336,13 +2536,11 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2336
2536
  if (settings.chat.model) input.model = settings.chat.model;
2337
2537
  session.create(input);
2338
2538
  taskBoard.snapshot();
2339
- ensureTaskMemorySession();
2340
2539
  },
2341
2540
  onForkSession: (parentTurnId) => {
2342
2541
  try {
2343
2542
  session.fork(parentTurnId);
2344
2543
  taskBoard.snapshot();
2345
- ensureTaskMemorySession();
2346
2544
  } catch (err) {
2347
2545
  process.stderr.write(
2348
2546
  `[/fork] failed at turn ${parentTurnId}: ${err instanceof Error ? err.message : String(err)}\n`,