@iowarp/clio-coder 0.4.2 → 0.4.3

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 (369) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/CONTRIBUTING.md +86 -19
  3. package/README.md +35 -6
  4. package/dist/{acp-TMDQZDIG.js → acp-H2NGRPWO.js} +11 -11
  5. package/dist/{agents-5N5NG3XG.js → agents-TL5LLUQP.js} +54 -53
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-Z5CCBXKQ.js → auth-E5SW4HMS.js} +19 -16
  8. package/dist/{builtins-K6TNDT24.js → builtins-IA7V7FUC.js} +9 -4
  9. package/dist/{chunk-ZW4HH5JJ.js → chunk-2APPQIER.js} +6 -6
  10. package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
  11. package/dist/{chunk-UBRFI4HS.js → chunk-2UG5F4C5.js} +127 -47
  12. package/dist/{chunk-UH632ZYL.js → chunk-2UH2KFUP.js} +2 -2
  13. package/dist/{chunk-3F7VUY77.js → chunk-2VIKGWFZ.js} +2 -2
  14. package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
  15. package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
  16. package/dist/{chunk-2X4RYJTJ.js → chunk-4UVU7BJ5.js} +2 -2
  17. package/dist/{chunk-M2DAX4F6.js → chunk-4WR7VSYB.js} +2 -2
  18. package/dist/{chunk-5KW52TEP.js → chunk-54CBCGIR.js} +5 -5
  19. package/dist/{chunk-3KIPBMUA.js → chunk-5ICU3EUH.js} +2 -2
  20. package/dist/{chunk-77QIVUZB.js → chunk-5MEZN6CB.js} +4 -4
  21. package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
  22. package/dist/{chunk-LJID3DYZ.js → chunk-64I3JVYM.js} +2 -2
  23. package/dist/{chunk-4JDLP6ZS.js → chunk-6PTFB5VS.js} +7 -7
  24. package/dist/{chunk-VN3SHNBN.js → chunk-7DICMOS6.js} +2 -2
  25. package/dist/{chunk-HLAFFSEK.js → chunk-7DRAWPTZ.js} +2 -2
  26. package/dist/chunk-7E7I3WLS.js +3762 -0
  27. package/dist/{chunk-YJISEZKC.js → chunk-7ZYNNDKC.js} +6 -6
  28. package/dist/{chunk-I66ZTYNP.js → chunk-AF4YM7Z4.js} +236 -101
  29. package/dist/{chunk-2HFQNRV3.js → chunk-AX2THNSA.js} +12 -12
  30. package/dist/{chunk-PGF63K6I.js → chunk-B4OAX3SI.js} +65 -3
  31. package/dist/{chunk-W6NIE6OW.js → chunk-B4VEBZKF.js} +3 -3
  32. package/dist/{chunk-JBCS7CRR.js → chunk-BEPZRGGU.js} +10 -10
  33. package/dist/{chunk-XGDPUNND.js → chunk-CE5AX47J.js} +2 -2
  34. package/dist/{chunk-I64IFBLB.js → chunk-DWUOQKRU.js} +17 -10
  35. package/dist/{chunk-DZAW46HP.js → chunk-E3TPLWFX.js} +3 -3
  36. package/dist/{chunk-HIICAHCJ.js → chunk-EKCHAPYA.js} +2 -2
  37. package/dist/{chunk-XE3PCIXH.js → chunk-F5JHEYZM.js} +7 -7
  38. package/dist/{chunk-5PFYMY2V.js → chunk-FTMGRKEF.js} +2 -2
  39. package/dist/{chunk-ZNT2M6TG.js → chunk-G76U63X4.js} +17 -17
  40. package/dist/{chunk-34BHNEE3.js → chunk-GHS5EBTQ.js} +58 -7
  41. package/dist/{chunk-2NHR3NAY.js → chunk-GI7YYQ3F.js} +40 -34
  42. package/dist/{chunk-DYHAXKHD.js → chunk-GWZNEVM2.js} +12 -8
  43. package/dist/chunk-GYV6VZOC.js +26 -0
  44. package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
  45. package/dist/{chunk-W4YEMFBX.js → chunk-HEQY7ZFI.js} +2 -2
  46. package/dist/{chunk-IKOZFYBN.js → chunk-I7ZPNEJM.js} +145 -102
  47. package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
  48. package/dist/{chunk-XQRY4DTA.js → chunk-IGLP3ODT.js} +10 -10
  49. package/dist/chunk-IJNZMHLA.js +101 -0
  50. package/dist/{chunk-JWJGP5DQ.js → chunk-INY6HTFL.js} +7 -7
  51. package/dist/{chunk-PBP4B7XR.js → chunk-IUE3Y34X.js} +2 -2
  52. package/dist/{chunk-B74PXLU7.js → chunk-IWT4SF4R.js} +3 -3
  53. package/dist/{chunk-B7HM5Z7T.js → chunk-JDAY6FIL.js} +5 -5
  54. package/dist/{chunk-PJX3WQUQ.js → chunk-JEQ3XTHC.js} +2 -2
  55. package/dist/{chunk-FSP7CMNU.js → chunk-JGRC33J2.js} +50 -4
  56. package/dist/{chunk-X7IARSHT.js → chunk-JKKCYP3C.js} +9 -9
  57. package/dist/{chunk-HJWWJ6IL.js → chunk-JSC3U7TI.js} +16 -4
  58. package/dist/{chunk-SSEYRH53.js → chunk-KK4JZPBQ.js} +19 -140
  59. package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
  60. package/dist/{chunk-Q4XWMHX6.js → chunk-L47TF46W.js} +2 -2
  61. package/dist/{chunk-O3YUNJZ2.js → chunk-LDJG7DW3.js} +81 -24
  62. package/dist/{chunk-CDNVLKUX.js → chunk-LLDJM5XK.js} +13 -7
  63. package/dist/{chunk-F2I26BDK.js → chunk-MUW2BDDH.js} +4 -4
  64. package/dist/{chunk-HKMD33FO.js → chunk-MWUZBSAQ.js} +79 -76
  65. package/dist/{chunk-QQLGQY2A.js → chunk-N2Z7HLVY.js} +20 -20
  66. package/dist/{chunk-DZEK6CJN.js → chunk-NIQJ66N4.js} +19 -19
  67. package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
  68. package/dist/{chunk-462T4EGZ.js → chunk-O5CVSAG5.js} +2 -2
  69. package/dist/{chunk-TPEQIQIE.js → chunk-OML5D5V5.js} +8 -8
  70. package/dist/{chunk-IKSLQ4XV.js → chunk-PAJQJ7BS.js} +558 -216
  71. package/dist/{chunk-ZW55JB7N.js → chunk-PUVDKJ2Y.js} +2 -2
  72. package/dist/{chunk-UH347SHR.js → chunk-QWGDJJYJ.js} +11 -11
  73. package/dist/chunk-R6Q67RJH.js +134 -0
  74. package/dist/{chunk-CRFOIAX3.js → chunk-RRNP2ANY.js} +6 -6
  75. package/dist/{chunk-IDNA72AH.js → chunk-RSJ25QSL.js} +2 -2
  76. package/dist/chunk-SKHCAU7K.js +385 -0
  77. package/dist/{chunk-RLYRBIYQ.js → chunk-TM6LQDI3.js} +20 -12
  78. package/dist/chunk-UOIZ7DA4.js +41 -0
  79. package/dist/{chunk-P75RZCJW.js → chunk-UPZU6GE4.js} +3 -3
  80. package/dist/{chunk-MCMZMDAC.js → chunk-V2ANDPVT.js} +4 -4
  81. package/dist/{chunk-AK5XEFVZ.js → chunk-VA5FNYMT.js} +26 -13
  82. package/dist/{chunk-IMXMHHMQ.js → chunk-VW6DOEDG.js} +332 -57
  83. package/dist/{chunk-XOXV5GKE.js → chunk-W6RRQCPQ.js} +16 -7
  84. package/dist/{chunk-CYZW7JHJ.js → chunk-WBKFA554.js} +8 -8
  85. package/dist/{chunk-BO7Y52RY.js → chunk-WCXUNS7U.js} +7 -7
  86. package/dist/{chunk-ZGNYYXQ6.js → chunk-WRBAGUNF.js} +3 -3
  87. package/dist/{chunk-FVDGR2ZL.js → chunk-XIVNBFZS.js} +85 -30
  88. package/dist/{chunk-BYMNWQ7O.js → chunk-XPWWI35G.js} +299 -58
  89. package/dist/{chunk-KPXDY6QF.js → chunk-XRZT5WY5.js} +2 -2
  90. package/dist/{chunk-AZ4WMN4W.js → chunk-Y3CBHOR6.js} +2 -2
  91. package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
  92. package/dist/{chunk-54ODD65L.js → chunk-YQWYVTMC.js} +4 -4
  93. package/dist/{chunk-M2WXEHER.js → chunk-ZA4VCIGV.js} +2 -2
  94. package/dist/{chunk-7BHIY2MW.js → chunk-ZDN3Y73Y.js} +6 -6
  95. package/dist/{chunk-E7GT7O5N.js → chunk-ZWPRK62N.js} +7 -4
  96. package/dist/cli/index.js +38 -37
  97. package/dist/{clio-7VB377CC.js → clio-CMMK4KRR.js} +7 -7
  98. package/dist/{code-nav-YVLCYA7V.js → code-nav-MDZNQS33.js} +7 -7
  99. package/dist/{components-UBWCQSRW.js → components-UCUQ4QXW.js} +4 -4
  100. package/dist/{config-4HVOS65E.js → config-SVM5P5YI.js} +76 -74
  101. package/dist/{configure-PIWO7B24.js → configure-LE3IK2TJ.js} +26 -24
  102. package/dist/{context-IYEHL3WQ.js → context-2OHRKS42.js} +66 -63
  103. package/dist/{context-N6ZE3LGJ.js → context-E3VC7RX5.js} +15 -11
  104. package/dist/{context-KQYIWPWT.js → context-VNCR7KAG.js} +60 -45
  105. package/dist/{context-clear-G4OGZJDS.js → context-clear-BW4O37TG.js} +61 -59
  106. package/dist/context-map-COB37XXN.js +505 -0
  107. package/dist/{context-working-set-BWLF6LJP.js → context-working-set-VDS25HXZ.js} +17 -16
  108. package/dist/{dispatch-runner-2QQAITS3.js → dispatch-runner-5AHT53RF.js} +85 -74
  109. package/dist/{doctor-LHBD36VU.js → doctor-WNNVO6FY.js} +37 -37
  110. package/dist/{eval-C45FYRJ6.js → eval-7G7SGAYO.js} +285 -114
  111. package/dist/{eval-inventory-6DEJPLBF.js → eval-inventory-Y6QRFOH5.js} +4 -4
  112. package/dist/{evidence-6SHONYAF.js → evidence-VD6736FQ.js} +63 -62
  113. package/dist/{evolve-KRKMV72X.js → evolve-AL3NGVRL.js} +62 -61
  114. package/dist/{extensions-KPZ2UHBB.js → extensions-MOVJ32NM.js} +7 -7
  115. package/dist/{fleet-IVTCKDHT.js → fleet-QZHUMAGI.js} +110 -108
  116. package/dist/{fleet-commands-EDWL3IT7.js → fleet-commands-BAYT5FJZ.js} +10 -10
  117. package/dist/{fleet-decisions-YP3YEFGK.js → fleet-decisions-IREVMRU4.js} +7 -6
  118. package/dist/{fleet-graph-ZFWKHY2M.js → fleet-graph-YCTT3HTI.js} +19 -18
  119. package/dist/{fleet-inspect-FVUNCBML.js → fleet-inspect-QVJTDAVB.js} +55 -54
  120. package/dist/{fleet-preflight-UN5XED4R.js → fleet-preflight-25QAFPK4.js} +4 -4
  121. package/dist/{fleet-validate-XOWC4HSX.js → fleet-validate-5O57AAJ7.js} +23 -22
  122. package/dist/{fleet-verify-UN3SODEL.js → fleet-verify-CPH2W2T6.js} +56 -55
  123. package/dist/{fleet-view-TWHJKCN6.js → fleet-view-SWBR3VGQ.js} +55 -54
  124. package/dist/{init-T2QORQ3Y.js → init-J477LKZH.js} +78 -76
  125. package/dist/{interop-IN5I2A66.js → interop-3FCM6XLG.js} +11 -11
  126. package/dist/{library-LSCATDLZ.js → library-QUQEIUG6.js} +28 -27
  127. package/dist/{memory-HYOKAGGJ.js → memory-SGGSEP65.js} +64 -63
  128. package/dist/{models-2GPMFYCM.js → models-HEKUAXXK.js} +49 -43
  129. package/dist/{monitor-E4ASVUJH.js → monitor-HKU57TYQ.js} +61 -60
  130. package/dist/{orchestrator-DDMPR3PY.js → orchestrator-VDFAEFAI.js} +919 -546
  131. package/dist/{panes-E3RUXOW5.js → panes-DN2SSFOH.js} +3 -3
  132. package/dist/{panes-IXKLOKA2.js → panes-TALGNPZT.js} +8 -8
  133. package/dist/{paths-L7LGY6RN.js → paths-NBMFAIEZ.js} +5 -5
  134. package/dist/reset-EAJFFJVB.js +344 -0
  135. package/dist/{resources-OTRSN34L.js → resources-OVKSEFVE.js} +27 -20
  136. package/dist/{run-5DEYH5QK.js → run-7DP7ZF2J.js} +113 -109
  137. package/dist/{share-IHWTLO3M.js → share-WML67FT3.js} +26 -25
  138. package/dist/{skills-IYMXMKW4.js → skills-SG662R2K.js} +39 -31
  139. package/dist/{skills-eval-DROHSJAR.js → skills-eval-VVZEUU46.js} +74 -73
  140. package/dist/{skills-inventory-D7X4L4ZX.js → skills-inventory-I2E23GET.js} +21 -20
  141. package/dist/{slash-commands-QBM7UZ3B.js → slash-commands-S7MBJDQK.js} +35 -34
  142. package/dist/{steer-Z5DO23FJ.js → steer-2LQOMCPB.js} +3 -3
  143. package/dist/{support-U7QOWY26.js → support-CC2UJBJ6.js} +6 -6
  144. package/dist/{targets-P2FUC4IL.js → targets-4QC3HIEW.js} +48 -45
  145. package/dist/{terminal-lease-YREJ3JX2.js → terminal-lease-TUHIJ6Y2.js} +2 -2
  146. package/dist/{tools-5B7RO6MV.js → tools-TFGJICCU.js} +8 -8
  147. package/dist/{trace-YMGMUM6A.js → trace-FXMXUZUF.js} +7 -7
  148. package/dist/uninstall-5PEVOE5B.js +408 -0
  149. package/dist/upgrade-M4WXY6KN.js +303 -0
  150. package/dist/{usage-ME5MPXGX.js → usage-N7ZNVLEM.js} +147 -102
  151. package/dist/{verifiers-BVZ7IWOO.js → verifiers-DJTP4XX6.js} +15 -15
  152. package/dist/{verify-5K7ZKQFC.js → verify-RWE4PPEK.js} +9 -9
  153. package/dist/{wiki-generate-F5W5QTYY.js → wiki-generate-C7IQOXSP.js} +84 -82
  154. package/dist/{with-panes-BYOJCLAM.js → with-panes-4GCGSL7J.js} +9 -9
  155. package/dist/worker/entry.js +61 -60
  156. package/docs/architecture/artifact-placement.md +1 -0
  157. package/docs/architecture/artifact-versions.md +1 -1
  158. package/docs/architecture/context-engine.md +4 -0
  159. package/docs/architecture/middleware-and-components.md +1 -1
  160. package/docs/architecture/model-catalog.md +21 -10
  161. package/docs/architecture/observability.md +12 -1
  162. package/docs/architecture/prompt-envelope-and-tools.md +2 -0
  163. package/docs/architecture/provider-adapter-cookbook.md +63 -0
  164. package/docs/architecture/safety-model.md +15 -5
  165. package/docs/guide/built-in-agents.md +17 -3
  166. package/docs/guide/commands-and-modes.md +1 -1
  167. package/docs/guide/configuration-and-targets.md +97 -9
  168. package/docs/guide/configuration-reference.md +7 -2
  169. package/docs/guide/environment-variables.md +2 -0
  170. package/docs/guide/installation-and-lifecycle.md +37 -4
  171. package/docs/guide/proactive-memory.md +66 -55
  172. package/docs/guide/skills-marketplace.md +18 -0
  173. package/docs/process/development-pipeline.md +34 -1
  174. package/docs/process/eval-runner.md +67 -3
  175. package/evals/behavioral-model.yaml +3 -2
  176. package/package.json +2 -2
  177. package/skills/README.md +7 -5
  178. package/skills/coding/ast-grep/SKILL.md +101 -30
  179. package/skills/coding/ast-grep/evals.md +26 -0
  180. package/skills/coding/coding-standards/SKILL.md +40 -5
  181. package/skills/coding/coding-standards/evals.md +23 -0
  182. package/skills/coding/prototype/SKILL.md +87 -28
  183. package/skills/coding/prototype/evals.md +19 -0
  184. package/skills/coding/tdd/SKILL.md +80 -53
  185. package/skills/coding/tdd/evals.md +20 -0
  186. package/skills/context/context-handoff/SKILL.md +43 -2
  187. package/skills/context/context-handoff/evals.md +44 -0
  188. package/skills/context/context-prime/SKILL.md +45 -15
  189. package/skills/context/context-prime/evals.md +45 -0
  190. package/skills/git/branch-closeout/SKILL.md +132 -0
  191. package/skills/git/branch-closeout/evals.md +133 -0
  192. package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
  193. package/skills/git/file-ticket/SKILL.md +77 -63
  194. package/skills/git/file-ticket/assets/issue-template.md +22 -0
  195. package/skills/git/file-ticket/evals.md +31 -26
  196. package/skills/git/file-ticket/references/issue-discovery.md +49 -0
  197. package/skills/git/fix-issue/SKILL.md +87 -64
  198. package/skills/git/fix-issue/evals.md +35 -31
  199. package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
  200. package/skills/git/resolve-merge-conflicts/SKILL.md +100 -51
  201. package/skills/git/resolve-merge-conflicts/evals.md +52 -25
  202. package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
  203. package/skills/git/ship/SKILL.md +103 -67
  204. package/skills/git/ship/assets/pr-template.md +21 -0
  205. package/skills/git/ship/evals.md +44 -28
  206. package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
  207. package/skills/git/worktree-create/SKILL.md +80 -50
  208. package/skills/git/worktree-create/evals.md +40 -33
  209. package/skills/git/worktree-create/references/worktree-setup.md +62 -66
  210. package/skills/git/worktree-merge/SKILL.md +112 -65
  211. package/skills/git/worktree-merge/evals.md +42 -34
  212. package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
  213. package/skills/planning/archify/SKILL.md +196 -0
  214. package/skills/planning/archify/evals.md +65 -0
  215. package/skills/planning/architecture/SKILL.md +61 -12
  216. package/skills/planning/architecture/evals.md +65 -0
  217. package/skills/planning/backlog/SKILL.md +130 -14
  218. package/skills/planning/backlog/evals.md +142 -0
  219. package/skills/planning/prd/SKILL.md +46 -6
  220. package/skills/planning/prd/evals.md +54 -0
  221. package/skills/planning/product-intent/SKILL.md +57 -2
  222. package/skills/planning/product-intent/evals.md +70 -0
  223. package/skills/planning/tech-spec/SKILL.md +53 -2
  224. package/skills/planning/tech-spec/evals.md +73 -0
  225. package/skills/registry.yaml +58 -50
  226. package/skills/remote.yaml +13 -0
  227. package/skills/research/arxiv-literature/SKILL.md +76 -18
  228. package/skills/research/arxiv-literature/evals.md +50 -0
  229. package/skills/research/experiment-protocol/SKILL.md +20 -1
  230. package/skills/research/experiment-protocol/evals.md +23 -0
  231. package/skills/research/scientific-debugging/SKILL.md +23 -1
  232. package/skills/research/scientific-debugging/evals.md +18 -0
  233. package/skills/research/scientific-modernization/SKILL.md +26 -1
  234. package/skills/research/scientific-modernization/evals.md +27 -0
  235. package/skills/skill-marketplace.json +63 -28
  236. package/skills/workflow/cut-it/SKILL.md +65 -5
  237. package/skills/workflow/cut-it/evals.md +101 -0
  238. package/skills/workflow/design-council/SKILL.md +117 -27
  239. package/skills/workflow/design-council/evals.md +161 -0
  240. package/skills/workflow/grill-me/SKILL.md +86 -10
  241. package/skills/workflow/grill-me/evals.md +153 -0
  242. package/skills/workflow/workflow-distiller/SKILL.md +76 -17
  243. package/skills/workflow/workflow-distiller/evals.md +118 -0
  244. package/src/cli/configure-interop.ts +105 -13
  245. package/src/cli/configure-oauth.ts +57 -0
  246. package/src/cli/configure-onboarding.ts +980 -0
  247. package/src/cli/configure-target.ts +594 -0
  248. package/src/cli/configure.ts +1082 -528
  249. package/src/cli/context-map.ts +114 -0
  250. package/src/cli/context.ts +4 -0
  251. package/src/cli/index.ts +1 -0
  252. package/src/cli/lifecycle-presenter.ts +436 -0
  253. package/src/cli/models.ts +10 -2
  254. package/src/cli/modes/print.ts +5 -1
  255. package/src/cli/reset.ts +228 -106
  256. package/src/cli/run.ts +7 -2
  257. package/src/cli/select.ts +664 -0
  258. package/src/cli/skills.ts +9 -2
  259. package/src/cli/targets.ts +3 -0
  260. package/src/cli/uninstall.ts +233 -165
  261. package/src/cli/upgrade.ts +204 -149
  262. package/src/cli/usage.ts +86 -27
  263. package/src/cli/validate-model.ts +3 -3
  264. package/src/core/config.ts +56 -0
  265. package/src/core/external-diagnostic.ts +44 -0
  266. package/src/core/gateway-routing.ts +157 -0
  267. package/src/core/safe-exec.ts +17 -2
  268. package/src/core/skill-activation.ts +89 -2
  269. package/src/domains/agents/builtins/world-knowledge.md +31 -0
  270. package/src/domains/agents/catalog.ts +1 -1
  271. package/src/domains/agents/result-contract.ts +70 -0
  272. package/src/domains/context/wiki/map-seed.ts +589 -0
  273. package/src/domains/context/wiki/plan.ts +2 -2
  274. package/src/domains/dispatch/admission.ts +29 -0
  275. package/src/domains/dispatch/agent-candidates.ts +10 -0
  276. package/src/domains/dispatch/budget-envelope.ts +86 -1
  277. package/src/domains/dispatch/capability-match.ts +1 -0
  278. package/src/domains/dispatch/capacity-lease.ts +17 -0
  279. package/src/domains/dispatch/contract.ts +11 -1
  280. package/src/domains/dispatch/extension.ts +134 -29
  281. package/src/domains/dispatch/types.ts +3 -0
  282. package/src/domains/dispatch/worker-model-metadata.ts +38 -0
  283. package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
  284. package/src/domains/eval/metrics/token-stream.ts +201 -31
  285. package/src/domains/eval/metrics/tracked.ts +40 -4
  286. package/src/domains/eval/runners/clio-run.ts +5 -2
  287. package/src/domains/eval/schema/suite.ts +28 -0
  288. package/src/domains/eval/schema/verdict.ts +2 -2
  289. package/src/domains/eval/suites/resolve.ts +13 -1
  290. package/src/domains/eval/suites/run.ts +24 -3
  291. package/src/domains/interop/registry.ts +6 -2
  292. package/src/domains/interop/types.ts +4 -0
  293. package/src/domains/lifecycle/migrations/index.ts +4 -0
  294. package/src/domains/memory/task-memory-policy.ts +70 -26
  295. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  296. package/src/domains/middleware/index.ts +0 -1
  297. package/src/domains/middleware/marketplace-offer.ts +3 -35
  298. package/src/domains/middleware/memory-intervention.ts +127 -32
  299. package/src/domains/middleware/memory-step-endpoint.ts +3 -2
  300. package/src/domains/middleware/skills-reminder.ts +31 -2
  301. package/src/domains/observability/compaction-usage.ts +118 -0
  302. package/src/domains/observability/cost.ts +1 -1
  303. package/src/domains/observability/extension.ts +6 -1
  304. package/src/domains/observability/out-of-turn-usage.ts +52 -21
  305. package/src/domains/providers/contract.ts +4 -1
  306. package/src/domains/providers/extension.ts +40 -9
  307. package/src/domains/providers/model-capabilities.ts +9 -0
  308. package/src/domains/providers/model-discovery.ts +2 -0
  309. package/src/domains/providers/model-runtime-capabilities.ts +15 -5
  310. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +32 -12
  311. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
  312. package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
  313. package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
  314. package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
  315. package/src/domains/providers/support.ts +11 -5
  316. package/src/domains/providers/target-model-cache.ts +25 -2
  317. package/src/domains/providers/types/capability-flags.ts +2 -0
  318. package/src/domains/providers/types/runtime-descriptor.ts +20 -1
  319. package/src/domains/providers/types/target-descriptor.ts +19 -0
  320. package/src/domains/resources/index.ts +3 -0
  321. package/src/domains/resources/skills/install.ts +72 -7
  322. package/src/domains/resources/skills/loader.ts +7 -0
  323. package/src/domains/resources/skills/marketplace.ts +63 -11
  324. package/src/domains/safety/autonomy.ts +15 -0
  325. package/src/domains/safety/index.ts +1 -0
  326. package/src/domains/safety/path-policy.ts +1 -1
  327. package/src/domains/safety/policy-engine.ts +34 -11
  328. package/src/domains/safety/protected-artifacts.ts +191 -88
  329. package/src/domains/safety/run-effects.ts +2 -22
  330. package/src/domains/safety/skill-authority.ts +55 -0
  331. package/src/domains/session/compaction/compact.ts +72 -22
  332. package/src/domains/session/entries.ts +6 -0
  333. package/src/domains/session/usage.ts +3 -3
  334. package/src/engine/agent.ts +13 -3
  335. package/src/engine/ai.ts +26 -8
  336. package/src/engine/antigravity/subprocess-runtime.ts +386 -120
  337. package/src/engine/api-registry.ts +3 -0
  338. package/src/engine/apis/openai-completions.ts +117 -14
  339. package/src/engine/external-subprocess.ts +114 -6
  340. package/src/entry/background-model-metadata.ts +18 -0
  341. package/src/entry/compaction-prompt.ts +57 -0
  342. package/src/entry/orchestrator.ts +405 -216
  343. package/src/entry/task-memory-lifecycle.ts +35 -0
  344. package/src/interactive/chat-loop-messages.ts +13 -4
  345. package/src/interactive/chat-loop.ts +65 -2
  346. package/src/interactive/chat-renderer.ts +1 -0
  347. package/src/interactive/cost-overlay.ts +26 -2
  348. package/src/interactive/interactive-slash-runtime.ts +2 -1
  349. package/src/interactive/renderers/worker-entry.ts +32 -0
  350. package/src/interactive/slash-commands.ts +24 -6
  351. package/src/interactive/theme/labels.ts +19 -13
  352. package/src/interactive/turn-context.ts +9 -5
  353. package/src/interactive/turn-recovery.ts +8 -0
  354. package/src/interactive/turn-runtime.ts +27 -11
  355. package/src/interactive/turn-state.ts +7 -0
  356. package/src/interactive/worker-receipts.ts +1 -0
  357. package/src/interactive/worker-stream.ts +6 -1
  358. package/src/tools/context/index.ts +30 -9
  359. package/src/tools/dispatch-arguments.ts +1 -0
  360. package/src/tools/dispatch-event-text.ts +10 -0
  361. package/src/tools/dispatch-plan.ts +1 -0
  362. package/src/tools/dispatch-runner.ts +12 -0
  363. package/src/tools/registry.ts +11 -5
  364. package/src/tools/worker-evidence.ts +3 -1
  365. package/src/worker/spec-contract.ts +4 -0
  366. package/dist/chunk-2Z2IKEXI.js +0 -1554
  367. package/dist/reset-OAQP3W4O.js +0 -230
  368. package/dist/uninstall-N34PCTGJ.js +0 -331
  369. 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";
@@ -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,6 +156,7 @@ 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";
@@ -167,7 +181,7 @@ import { ToolchainDomainModule } from "../domains/toolchain/index.js";
167
181
  import { createUserTasksStore } from "../domains/user-tasks/store.js";
168
182
  import { type AcpSafeSettingsPatch, type AcpSafeSettingsSnapshot, serveClioAcpAgent } from "../engine/acp/server.js";
169
183
  import { createStdioServerTransport } from "../engine/acp/transport.js";
170
- import { completeEngineText } from "../engine/ai.js";
184
+ import { completeEngineText, type EngineTextCompletionResult } from "../engine/ai.js";
171
185
  import { setProtectedModelsProvider } from "../engine/apis/residency.js";
172
186
  import {
173
187
  createLoopGuardRegistration,
@@ -179,9 +193,12 @@ import type { EngineModel } from "../engine/types.js";
179
193
  import { createChatLoop } from "../interactive/chat-loop.js";
180
194
  import { type RunIo, startInteractive } from "../interactive/index.js";
181
195
  import { buildModelReplayAgentMessagesFromTurns } from "../interactive/model-session-replay.js";
196
+ import { prepareBackgroundModelMetadata } from "./background-model-metadata.js";
182
197
  import type { BootOptions } from "./boot-options.js";
198
+ import { readCompactionSystemPrompt } from "./compaction-prompt.js";
183
199
  import { createExtensionReloadCoordinator } from "./extension-reload.js";
184
200
  import { resolvePanesEnablement } from "./panes-activation.js";
201
+ import { bindTaskMemoryLifecycle, captureTaskMemoryUsage } from "./task-memory-lifecycle.js";
185
202
 
186
203
  export type { BootOptions, HeadlessSamplingOverrides } from "./boot-options.js";
187
204
 
@@ -287,6 +304,8 @@ interface CompactionResolution {
287
304
  model: EngineModel;
288
305
  targetId: string;
289
306
  endpointKey: string | null;
307
+ wireModelId: string;
308
+ headers?: Record<string, string>;
290
309
  apiKey?: string;
291
310
  }
292
311
 
@@ -312,53 +331,71 @@ function advanceThinkingLevel(current: ThinkingLevel, available: ReadonlyArray<T
312
331
  */
313
332
  const LOCAL_API_KEY_FALLBACK = "clio-coder-local-target";
314
333
 
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
334
  /**
333
335
  * The background memory role, resolved to the endpoint it would call.
334
336
  *
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.
337
+ * The endpoint key joins the existing dispatch capacity evidence and local
338
+ * request holds. A gateway key does not identify one physical model server or
339
+ * imply a one-slot limit; the configured protocol owns downstream routing.
338
340
  */
339
341
  interface BackgroundMemoryRoute {
340
342
  client: TaskMemoryModelClient;
343
+ selection: "dedicated" | "chat-fallback";
344
+ fallbackReason?: string;
341
345
  targetId: string;
342
346
  wireModelId: string;
343
347
  endpointKey: string | null;
344
348
  modelMaxTokens(configuredMaxTokens: number): number;
345
349
  }
346
350
 
347
- function createBackgroundMemoryModelClient(
351
+ export function createBackgroundMemoryModelClient(
348
352
  providers: ProvidersContract,
349
353
  settings: Readonly<ClioSettings>,
350
354
  timeoutMs: number,
351
355
  bus: Pick<SafeEventBus, "emit"> | null,
356
+ fallbackOnly = false,
352
357
  ): 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;
358
+ const configuredTarget = settings.context.memory.target?.trim();
359
+ const configuredModel = settings.context.memory.model?.trim();
360
+ if (!configuredTarget || !configuredModel) return null;
361
+ const chatTarget = settings.chat.target?.trim();
362
+ const chatModel = settings.chat.model?.trim();
363
+ const sameRoute = configuredTarget === chatTarget && configuredModel === chatModel;
364
+ if (fallbackOnly && sameRoute) return null;
365
+ let fallbackReason: string | undefined;
366
+ if (!fallbackOnly) {
367
+ try {
368
+ return prepareBackgroundMemoryRoute(providers, configuredTarget, configuredModel, timeoutMs, bus, "dedicated");
369
+ } catch {
370
+ fallbackReason = "configured route unavailable";
371
+ }
372
+ } else fallbackReason = "configured route client error";
373
+ if (!chatTarget || !chatModel || sameRoute) {
374
+ if (fallbackOnly) return null;
375
+ throw new Error("configured memory route is unavailable and no distinct chat fallback can run");
376
+ }
377
+ try {
378
+ return {
379
+ ...prepareBackgroundMemoryRoute(providers, chatTarget, chatModel, timeoutMs, bus, "chat-fallback"),
380
+ fallbackReason,
381
+ };
382
+ } catch {
383
+ if (fallbackOnly) return null;
384
+ throw new Error("neither the configured memory route nor the active chat fallback is available");
385
+ }
386
+ }
387
+
388
+ function prepareBackgroundMemoryModel(providers: ProvidersContract, targetId: string, wireModelId: string) {
389
+ const status = providers.list().find((entry) => entry.target.id === targetId);
390
+ if (
391
+ status &&
392
+ (!status.available ||
393
+ status.health.status === "down" ||
394
+ (hasLiveModelCatalog(status) && !status.discoveredModels.includes(wireModelId)) ||
395
+ modelResidencyForStatus(status, wireModelId) === "absent" ||
396
+ modelResidencyForStatus(status, wireModelId) === "loading")
397
+ )
398
+ throw new Error("background memory route is unavailable");
362
399
  const resolved = resolveRuntimeTarget(providers, {
363
400
  targetId,
364
401
  wireModelId,
@@ -378,9 +415,37 @@ function createBackgroundMemoryModelClient(
378
415
  const model = resolved.target.runtime.synthesizeModel(resolved.target.target, resolved.target.wireModelId, kbHit);
379
416
  const refined = refineRuntimeTargetWithModelHints(resolved.target, model, providers.knowledgeBase);
380
417
  applyModelCapabilityPatch(model, refined.capabilities);
381
- const costProvenance = normalizeCostProvenance(refined.costProvenance);
418
+ return { model, refined };
419
+ }
420
+
421
+ function backgroundMemoryEndpointBusy(
422
+ providers: ProvidersContract,
423
+ endpointKey: string | null,
424
+ targets: ReadonlyArray<TargetDescriptor>,
425
+ ): boolean {
426
+ if (endpointKey === null) return false;
427
+ const capacity = resolveEndpointCapacities({
428
+ statuses: providers.list(),
429
+ targets,
430
+ runtimeFor: (id) => providers.getRuntime(id),
431
+ })[endpointKey];
432
+ // Gateways and non-fixed schedulers have no invented local one-slot cap.
433
+ return capacity !== undefined && (endpointCapacityUsage()[endpointKey] ?? 0) >= capacity.limit;
434
+ }
435
+
436
+ function prepareBackgroundMemoryRoute(
437
+ providers: ProvidersContract,
438
+ targetId: string,
439
+ wireModelId: string,
440
+ timeoutMs: number,
441
+ bus: Pick<SafeEventBus, "emit"> | null,
442
+ selection: BackgroundMemoryRoute["selection"],
443
+ ): BackgroundMemoryRoute {
444
+ const initial = prepareBackgroundMemoryModel(providers, targetId, wireModelId);
445
+ const { refined } = initial;
382
446
  const endpointKey = canonicalEndpointKey(refined.target);
383
447
  return {
448
+ selection,
384
449
  targetId,
385
450
  wireModelId: refined.wireModelId,
386
451
  endpointKey,
@@ -394,47 +459,121 @@ function createBackgroundMemoryModelClient(
394
459
  // Wrapped at the layer that knows a request left the process: the step
395
460
  // holds endpoint capacity while it is out and publishes the cache
396
461
  // disturbance even when a timeout means the usage sink never sees it.
397
- complete: announceMemoryStepEndpoint({ bus, endpointKey, targetId }, async (request) => {
462
+ complete: async (request) => {
463
+ await prepareBackgroundModelMetadata(providers, targetId, request.signal);
464
+ const { model, refined } = prepareBackgroundMemoryModel(providers, targetId, wireModelId);
465
+ if (refined.runtimeId !== initial.refined.runtimeId || canonicalEndpointKey(refined.target) !== endpointKey) {
466
+ throw new Error("background memory target changed during preparation");
467
+ }
468
+ const costProvenance = normalizeCostProvenance(refined.costProvenance);
398
469
  const apiKey = targetRequiresAuth(refined.target, refined.runtime)
399
470
  ? (await providers.auth.resolveForTarget(refined.target, refined.runtime, { signal: request.signal })).apiKey
400
471
  : 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: {
472
+ request.signal.throwIfAborted();
473
+ // Preparation yielded after the middleware's first check. Recheck current
474
+ // occupancy immediately before the synchronous foreground-slot registration;
475
+ // no await may separate this admission from announceMemoryStepEndpoint.
476
+ if (backgroundMemoryEndpointBusy(providers, endpointKey, [refined.target])) {
477
+ throw new TaskMemoryEndpointBusyError("background memory endpoint is busy");
478
+ }
479
+ return announceMemoryStepEndpoint({ bus, endpointKey, targetId }, async () => {
480
+ const startedAt = Date.now();
481
+ let observedUsage: TaskMemoryStepUsage | undefined;
482
+ const mapUsage = (completion: Pick<EngineTextCompletionResult, "usage" | "backend">): TaskMemoryStepUsage => ({
483
+ ...completion.usage,
423
484
  targetId,
424
485
  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
486
  costProvenance,
433
487
  durationMs: Date.now() - startedAt,
434
488
  backend: completion.backend,
435
- },
436
- };
437
- }),
489
+ });
490
+ const completion = await completeEngineText({
491
+ model,
492
+ systemPrompt: request.systemPrompt,
493
+ userPrompt: request.userPrompt,
494
+ maxTokens:
495
+ refined.capabilityDecisions.maxTokens > 0
496
+ ? Math.min(request.maxTokens, refined.capabilityDecisions.maxTokens)
497
+ : request.maxTokens,
498
+ // Always off, never the operator's chat thinking level. A model that
499
+ // reasons anyway still works: `completeEngineText` keeps only text
500
+ // blocks, and the memory output budget leaves room for the preamble.
501
+ thinkingLevel: "off",
502
+ signal: request.signal,
503
+ timeoutMs,
504
+ onUsage: (observation) => {
505
+ observedUsage = mapUsage(observation);
506
+ request.onUsage?.(observedUsage);
507
+ },
508
+ ...(apiKey === undefined ? {} : { apiKey }),
509
+ ...(refined.target.auth?.headers ? { headers: refined.target.auth.headers } : {}),
510
+ });
511
+ // The step is billed here whatever the policy later decides about the
512
+ // answer. A model that read a trajectory and chose silence spent the
513
+ // same prefill as one that produced a reminder.
514
+ return {
515
+ text: completion.text,
516
+ inputTokens: completion.inputTokens,
517
+ outputTokens: completion.outputTokens,
518
+ usage: observedUsage ?? mapUsage(completion),
519
+ };
520
+ })(request);
521
+ },
522
+ },
523
+ };
524
+ }
525
+
526
+ /** Production callback composition shared with routing/capacity contracts. */
527
+ export function createBackgroundMemoryRouting(
528
+ providers: ProvidersContract,
529
+ getSettings: () => Readonly<ClioSettings> | undefined,
530
+ bus: Pick<SafeEventBus, "emit"> | null,
531
+ ) {
532
+ let route: BackgroundMemoryRoute | null = null;
533
+ let snapshot: Readonly<ClioSettings> | undefined;
534
+ let lastNotice: string | null = null;
535
+ const noteFallback = (): void => {
536
+ if (route?.selection !== "chat-fallback") {
537
+ lastNotice = null;
538
+ return;
539
+ }
540
+ const message = `Memory: ${route.fallbackReason}; selected chat fallback ${route.targetId}/${route.wireModelId} for this step, subject to available endpoint capacity.`;
541
+ if (message === lastNotice) return;
542
+ lastNotice = message;
543
+ try {
544
+ bus?.emit(BusChannels.RuntimeNotice, {
545
+ kind: "degraded",
546
+ level: "info",
547
+ message,
548
+ targetId: route.targetId,
549
+ runtimeId: providers.getTarget(route.targetId)?.runtime ?? "unknown",
550
+ model: route.wireModelId,
551
+ });
552
+ } catch {
553
+ /* Advisory only. */
554
+ }
555
+ };
556
+ return {
557
+ getModelClient: (): TaskMemoryModelClient | null => {
558
+ const current = getSettings();
559
+ snapshot = current === undefined ? undefined : structuredClone(current);
560
+ route =
561
+ snapshot === undefined
562
+ ? null
563
+ : createBackgroundMemoryModelClient(providers, snapshot, snapshot.context.memory.timeoutMs, bus);
564
+ noteFallback();
565
+ return route?.client ?? null;
566
+ },
567
+ getFallbackModelClient: (): TaskMemoryModelClient | null => {
568
+ if (route?.selection !== "dedicated" || snapshot === undefined) return null;
569
+ route = createBackgroundMemoryModelClient(providers, snapshot, snapshot.context.memory.timeoutMs, bus, true);
570
+ noteFallback();
571
+ return route?.client ?? null;
572
+ },
573
+ getModelMaxTokens: (configured: number): number => route?.modelMaxTokens(configured) ?? configured,
574
+ backgroundEndpointBusy: (): boolean => {
575
+ if (route?.endpointKey == null || snapshot === undefined) return false;
576
+ return backgroundMemoryEndpointBusy(providers, route.endpointKey, snapshot.targets);
438
577
  },
439
578
  };
440
579
  }
@@ -472,74 +611,84 @@ function agentRoleToolWarnings(providers: ProvidersContract, settings: Readonly<
472
611
  return warnings;
473
612
  }
474
613
 
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(
614
+ async function resolveCompactionModel(
615
+ settings: ClioSettings,
481
616
  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;
617
+ ): Promise<CompactionResolution | null> {
618
+ const override = settings.context.compaction.model;
619
+ let targetId = settings.chat.target;
620
+ let wireModelId = settings.chat.model;
621
+ if (override !== undefined && override !== null) {
622
+ let selected: ReturnType<typeof resolveModelReference>;
623
+ try {
624
+ selected = resolveModelReference(override, providers);
625
+ } catch {
626
+ throw new Error("context.compaction.model is an invalid pattern; set a unique target/model reference");
627
+ }
628
+ if (!selected.ref || selected.warning) {
629
+ throw new Error(
630
+ "context.compaction.model must match exactly one configured model; set a unique target/model reference",
631
+ );
632
+ }
633
+ targetId = selected.ref.target;
634
+ wireModelId = selected.ref.model;
496
635
  }
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;
636
+ if (!targetId || !wireModelId) return null;
637
+ await prepareBackgroundModelMetadata(providers, targetId);
638
+ const status = providers.list().find((entry) => entry.target.id === targetId);
639
+ if (!status?.available) {
640
+ throw new Error("context.compaction.model target is unavailable; check the selected target with clio-coder targets");
641
+ }
642
+ const resolved = resolveRuntimeTarget(providers, {
643
+ targetId,
644
+ wireModelId,
645
+ use: "orchestrator",
646
+ requestedThinkingLevel: "off",
647
+ requireTools: false,
648
+ requireStreaming: true,
649
+ requireOutputBudget: true,
650
+ });
651
+ if (!resolved.ok) {
652
+ // Configured identifiers and provider diagnostics can contain terminal controls.
653
+ // Keep the failure actionable without echoing arbitrary provider/config text.
654
+ throw new Error(
655
+ "context.compaction.model cannot run as a summarizer; select an available HTTP chat model with an output budget",
656
+ );
657
+ }
658
+ const route = resolved.target;
506
659
  let model: EngineModel;
507
660
  try {
508
- const kbHit = providers.knowledgeBase?.lookup(wireModelId) ?? null;
509
- model = runtime.synthesizeModel(target, wireModelId, kbHit);
661
+ model = route.runtime.synthesizeModel(
662
+ route.target,
663
+ route.wireModelId,
664
+ providers.knowledgeBase?.lookup(route.wireModelId) ?? null,
665
+ );
510
666
  } catch {
511
- return null;
667
+ throw new Error("context.compaction.model could not be prepared; check the selected target/model configuration");
512
668
  }
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);
669
+ const refined = refineRuntimeTargetWithModelHints(route, model, providers.knowledgeBase);
670
+ if (refined.capabilityDecisions.maxTokens <= 0) {
671
+ throw new Error("context.compaction.model has no output budget; select a model with a known positive output limit");
672
+ }
673
+ applyModelCapabilityPatch(model, refined.capabilities);
674
+ let apiKey: string | undefined = LOCAL_API_KEY_FALLBACK;
675
+ if (targetRequiresAuth(route.target, route.runtime)) {
676
+ const auth = await providers.auth.resolveForTarget(route.target, route.runtime);
677
+ if (!auth.available || !auth.apiKey) {
678
+ throw new Error(
679
+ "context.compaction.model authentication is unavailable; authenticate the selected target with clio-coder auth",
680
+ );
520
681
  }
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.
682
+ apiKey = auth.apiKey;
524
683
  }
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;
684
+ return {
685
+ model,
686
+ targetId: route.targetId,
687
+ wireModelId: route.wireModelId,
688
+ endpointKey: canonicalEndpointKey(route.target),
689
+ apiKey,
690
+ ...(route.target.auth?.headers ? { headers: route.target.auth.headers } : {}),
691
+ };
543
692
  }
544
693
 
545
694
  function readSessionEntriesForCompact(sessionId: string): SessionEntry[] {
@@ -644,11 +793,19 @@ async function runCompactionFlow(
644
793
  providers: ProvidersContract,
645
794
  instructions?: string,
646
795
  trigger?: CompactionTrigger,
796
+ observability?: BackgroundMemoryUsageSink,
647
797
  ): Promise<CompactResult | null> {
648
798
  const meta = session.current();
649
799
  if (!meta) {
650
800
  throw new Error("no current session to compact; start one with /new or /resume first");
651
801
  }
802
+ const stateDir = clioStateDir();
803
+ const activeLeafTurnId = session.tree(meta.id).leafId ?? undefined;
804
+ const isOriginCurrent = () =>
805
+ session.current()?.id === meta.id && (session.tree(meta.id).leafId ?? undefined) === activeLeafTurnId;
806
+ const entries = filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId);
807
+ if (entries.length === 0) return null;
808
+ const systemPrompt = await readCompactionSystemPrompt(settings.context.compaction.systemPrompt, meta.cwd);
652
809
  const resolved = await resolveCompactionModel(settings, providers);
653
810
  if (!resolved) {
654
811
  throw new Error("no model configured; set chat.target + chat.model");
@@ -661,11 +818,22 @@ async function runCompactionFlow(
661
818
  // read the full file too (last taskLedger entry in file order, with no
662
819
  // branch filter at all), which was a second, independent instance of this
663
820
  // 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;
821
+ if (!isOriginCurrent()) throw new Error("compaction session or branch changed before summarization");
822
+ const calls: CompactionCallObservation[] = [];
823
+ const preserveCalls = () =>
824
+ recordFailedCompactionCalls(
825
+ {
826
+ stateDir,
827
+ sessionId: meta.id,
828
+ repoIdentity: meta.cwdHash || cwdHash(meta.cwd),
829
+ target: resolved.targetId,
830
+ model: resolved.wireModelId,
831
+ },
832
+ calls,
833
+ isOriginCurrent() ? observability : undefined,
834
+ );
667
835
 
668
- // A compaction summary is a full streamed request against the chat target.
836
+ // A compaction summary is a full streamed request against its resolved target.
669
837
  // Hold the same canonical endpoint slot as an ordinary turn, /btw round, or
670
838
  // pre-warm so dispatch admission and background memory see its real usage.
671
839
  const releaseEndpointSlot = resolved.endpointKey === null ? () => {} : registerForegroundStream(resolved.endpointKey);
@@ -673,17 +841,39 @@ async function runCompactionFlow(
673
841
  try {
674
842
  result = await compact({
675
843
  entries,
844
+ onCall: (call) => calls.push(call),
676
845
  model: resolved.model,
846
+ ...(systemPrompt !== undefined ? { systemPrompt } : {}),
847
+ ...(resolved.headers !== undefined ? { headers: resolved.headers } : {}),
677
848
  ...(resolved.apiKey !== undefined ? { apiKey: resolved.apiKey } : {}),
678
849
  ...(instructions !== undefined ? { instructions } : {}),
679
850
  });
851
+ } catch (error) {
852
+ try {
853
+ preserveCalls();
854
+ } catch (recordingError) {
855
+ throw new AggregateError([error, recordingError], "compaction failed and its usage could not be fully recorded");
856
+ }
857
+ throw error;
680
858
  } finally {
681
859
  releaseEndpointSlot();
682
860
  }
683
- if (result.messagesSummarized === 0 || result.summary.length === 0) return null;
861
+ if (!isOriginCurrent()) {
862
+ preserveCalls();
863
+ throw new Error("compaction session or branch changed; summary discarded and originating usage retained");
864
+ }
865
+ if (result.messagesSummarized === 0 || result.summary.length === 0) {
866
+ preserveCalls();
867
+ if (calls.length > 0) throw new Error("compaction returned no summary; reported usage retained without a checkpoint");
868
+ return null;
869
+ }
870
+ if (result.usage) {
871
+ result.usage = { ...result.usage, targetId: resolved.targetId, modelId: resolved.wireModelId };
872
+ }
684
873
 
685
- const entry: Omit<CompactionSummaryEntry, "turnId" | "timestamp"> = {
874
+ const entry: Omit<CompactionSummaryEntry, "timestamp"> = {
686
875
  kind: "compactionSummary",
876
+ turnId: randomUUID(),
687
877
  parentTurnId: result.firstKeptTurnId ?? null,
688
878
  summary: result.summary,
689
879
  tokensBefore: result.tokensBefore,
@@ -697,22 +887,61 @@ async function runCompactionFlow(
697
887
  ...(result.usage !== undefined ? { usage: result.usage } : {}),
698
888
  };
699
889
  if (trigger !== undefined) entry.trigger = trigger;
700
- session.appendEntry(entry);
890
+ try {
891
+ session.appendEntry(entry);
892
+ } catch (error) {
893
+ // A write may have reached the ledger before its caller threw. Reconcile
894
+ // this exact checkpoint identity before choosing a second accounting store.
895
+ try {
896
+ if (!compactionCheckpointWasWritten(sessionPaths(meta).current, entry.turnId)) preserveCalls();
897
+ } catch (recordingError) {
898
+ throw new AggregateError(
899
+ [error, recordingError],
900
+ "compaction checkpoint persistence is unresolved; usage was not duplicated",
901
+ );
902
+ }
903
+ throw error;
904
+ }
701
905
  return result;
702
906
  }
703
907
 
908
+ function compactionCheckpointWasWritten(path: string, turnId: string): boolean {
909
+ let malformed = false;
910
+ for (const line of readFileSync(path, "utf8").split("\n")) {
911
+ if (line.trim().length === 0) continue;
912
+ try {
913
+ const entry: unknown = JSON.parse(line);
914
+ if (
915
+ entry &&
916
+ typeof entry === "object" &&
917
+ "turnId" in entry &&
918
+ entry.turnId === turnId &&
919
+ "kind" in entry &&
920
+ entry.kind === "compactionSummary"
921
+ )
922
+ return true;
923
+ } catch {
924
+ malformed = true;
925
+ }
926
+ }
927
+ if (malformed) throw new Error("cannot prove checkpoint absence in a malformed session ledger");
928
+ return false;
929
+ }
930
+
704
931
  /**
705
932
  * Compose the production chat-loop compaction callback. Errors intentionally
706
933
  * propagate: the chat loop owns activity failure reporting and distinguishes
707
934
  * a thrown read/model/persistence failure from the legitimate null no-op that
708
935
  * `runCompactionFlow` returns for an empty session or an unavailable cut.
709
936
  */
710
- function createProductionAutoCompact(
937
+ export function createProductionAutoCompact(
711
938
  session: SessionContract,
712
939
  getSettings: () => ClioSettings,
713
940
  providers: ProvidersContract,
941
+ observability?: BackgroundMemoryUsageSink,
714
942
  ): (instructions?: string, trigger?: CompactionTrigger) => Promise<CompactResult | null> {
715
- return (instructions, trigger) => runCompactionFlow(session, getSettings(), providers, instructions, trigger);
943
+ return (instructions, trigger) =>
944
+ runCompactionFlow(session, getSettings(), providers, instructions, trigger, observability);
716
945
  }
717
946
 
718
947
  function estimateTokensFromSummary(summary: string): number {
@@ -1167,37 +1396,12 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1167
1396
  middleware.registerHook(
1168
1397
  createSkillActivationObserver((activation) => appendSkillActivationRegistryEvent(session, activation)),
1169
1398
  );
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
1399
  // Task-board reminder: same user-message-visible channel, fired once per
1189
1400
  // session when a request literally enumerates three or more steps. The
1190
1401
  // static routing line and tasks hint ask for the same board; battery-tested
1191
1402
  // local models only comply when the instruction rides the user message.
1192
1403
  middleware.registerHook(createTaskBoardReminderRegistration());
1193
1404
  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
1405
  const memorySettings = (config?.get() ?? readSettings()).context.memory;
1202
1406
  // Bound late: the registration is built here, but the buffer a deferred
1203
1407
  // reminder lands in belongs to the chat loop that has not been composed yet.
@@ -1212,7 +1416,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1212
1416
  const memoryTrace = memoryTracePath === null ? null : createTaskMemoryTrace(memoryTracePath);
1213
1417
  // The route the last resolved memory client would call, kept so the endpoint
1214
1418
  // check and the cost row read the same resolution the step itself used.
1215
- let backgroundMemoryRoute: BackgroundMemoryRoute | null = null;
1216
1419
  /**
1217
1420
  * Account for one background memory step exactly as a `/btw` side question is
1218
1421
  * accounted for: the in-process cost tracker under its own label so `/cost`
@@ -1221,23 +1424,16 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1221
1424
  *
1222
1425
  * Accounting only. The client wrapper (`announceMemoryStepEndpoint`) holds the
1223
1426
  * 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.
1427
+ * thrown steps that report no usage. Late reported usage still reaches this sink.
1225
1428
  */
1226
- const recordBackgroundMemoryUsage = (usage: TaskMemoryStepUsage): void => {
1429
+ const captureBackgroundMemoryUsage = () => {
1227
1430
  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
- }
1431
+ return captureTaskMemoryUsage({
1432
+ stateDir: clioStateDir(),
1433
+ sessionId: meta?.id ?? null,
1434
+ repoIdentity: meta ? meta.cwdHash || cwdHash(meta.cwd || process.cwd()) : null,
1435
+ ...(observability === undefined ? {} : { observability }),
1436
+ });
1241
1437
  };
1242
1438
  const proposeInjectedMemoryEntries = (entries: ReadonlyArray<TaskMemoryEntry>): void => {
1243
1439
  const meta = session?.current() ?? null;
@@ -1266,7 +1462,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1266
1462
  deliversDeferredReminders: options.headless === undefined,
1267
1463
  onDeferredReminder: (message) => deferredMemoryReminderSink?.(message),
1268
1464
  getSettings: () => {
1269
- ensureTaskMemorySession();
1270
1465
  const memory = effectiveSettingsForDispatch?.().context.memory ?? memorySettings;
1271
1466
  return {
1272
1467
  enabled: memory.enabled,
@@ -1276,34 +1471,17 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1276
1471
  timeoutMs: memory.timeoutMs,
1277
1472
  };
1278
1473
  },
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),
1474
+ ...createBackgroundMemoryRouting(providers, () => effectiveSettingsForDispatch?.(), bus),
1475
+ captureStepUsage: captureBackgroundMemoryUsage,
1302
1476
  onInjectedEntries: (entries) => proposeInjectedMemoryEntries(entries),
1303
1477
  });
1304
1478
  middleware.registerHook(memoryIntervention);
1305
1479
  const unsubscribeMemoryLoop = bus.on(BusChannels.LoopBlocked, () => memoryIntervention.signalLoop());
1306
- termination.onDrain(() => unsubscribeMemoryLoop());
1480
+ const disposeMemoryLifecycle = bindTaskMemoryLifecycle(bus, memoryIntervention);
1481
+ termination.onDrain(() => {
1482
+ unsubscribeMemoryLoop();
1483
+ disposeMemoryLifecycle();
1484
+ });
1307
1485
  if (contextDomain) {
1308
1486
  middleware.registerHook(createFileMutationObserver(({ paths }) => contextDomain.noteFileChanges(paths)));
1309
1487
  }
@@ -1346,17 +1524,34 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1346
1524
  (config?.get() ?? readSettings()).safety.autonomy ??
1347
1525
  "auto-edit";
1348
1526
  const resolveEffectiveAutonomy = (): AutonomyLevel => activeAcpSessionAutonomy ?? resolveBaselineAutonomy();
1527
+ // First-turn skills reminder: user-message-visible text is the one channel
1528
+ // the battery-tested local models act on. Which protocol it teaches follows
1529
+ // the effective autonomy level, resolved one line up: suggest-and-wait at
1530
+ // read-only and suggest, load-it-yourself at auto-edit and full-auto.
1531
+ if (resources) {
1532
+ middleware.registerHook(
1533
+ createSkillsReminderRegistration({
1534
+ countModelVisibleSkills: () => modelVisibleSkills(resources.skills(process.cwd()).items).length,
1535
+ // Same lookup context(scope="skills") lists under its Marketplace
1536
+ // heading, minus what is already installed, so the count the
1537
+ // reminder quotes is the count the listing will show.
1538
+ countInstallableSkills: () => {
1539
+ const installed = new Set(resources.skills(process.cwd()).items.map((skill) => skill.name));
1540
+ return discoverMarketplaceSkills({ cwd: process.cwd() }).skills.filter((skill) => !installed.has(skill.name))
1541
+ .length;
1542
+ },
1543
+ modelMayActivateSkills: () => modelMayActivateSkills(resolveEffectiveAutonomy()),
1544
+ }),
1545
+ );
1546
+ }
1349
1547
  // 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.
1548
+ // dispatch worker), local matcher and operator-consented installs at every autonomy level. The
1549
+ // registration also checks the own-marketplace source gate before installing.
1354
1550
  if (resources) {
1355
1551
  middleware.registerHook(
1356
1552
  createMarketplaceOfferRegistration({
1357
1553
  listInstalledSkillNames: () => resources.skills(process.cwd()).items.map((skill) => skill.name),
1358
1554
  listMarketplaceEntries: () => discoverMarketplaceSkills({ cwd: process.cwd() }).skills,
1359
- getAutonomy: resolveEffectiveAutonomy,
1360
1555
  installEntry: (entry, scope) => {
1361
1556
  const installed = installSkill({ source: entry.sourceUrl, scope, name: entry.name, cwd: process.cwd() });
1362
1557
  return { path: installed.path, sourceUrl: installed.sourceUrl, installedHash: installed.installedHash };
@@ -1593,7 +1788,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1593
1788
  return taskMemoryHandoffSeedOffer(process.cwd(), getCurrentSettings().context.memory.enabled);
1594
1789
  };
1595
1790
  const seedCurrentTaskMemoryFromHandoff = () => {
1596
- ensureTaskMemorySession();
1597
1791
  return seedTaskMemoryFromNewestHandoff(taskMemoryBank, process.cwd(), getCurrentSettings().context.memory.enabled);
1598
1792
  };
1599
1793
  if (resumedSessionAtBoot) {
@@ -1835,6 +2029,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1835
2029
  return key === null ? null : registerForegroundStream(key);
1836
2030
  },
1837
2031
  getSettings: getCurrentSettings,
2032
+ getAutonomy: resolveEffectiveAutonomy,
1838
2033
  providers,
1839
2034
  middleware,
1840
2035
  middlewareToolChoice,
@@ -1863,7 +2058,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1863
2058
  }
1864
2059
  },
1865
2060
  getTaskMemoryHandoffSource: () => {
1866
- ensureTaskMemorySession();
1867
2061
  const meta = session?.current();
1868
2062
  if (!meta) throw new Error("task memory handoff requires an active session");
1869
2063
  const settings = getCurrentSettings();
@@ -1888,7 +2082,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
1888
2082
  ...(session
1889
2083
  ? {
1890
2084
  readSessionEntries: readCurrentSessionEntries,
1891
- autoCompact: createProductionAutoCompact(session, getCurrentSettings, providers),
2085
+ autoCompact: createProductionAutoCompact(session, getCurrentSettings, providers, observability),
1892
2086
  }
1893
2087
  : {}),
1894
2088
  toolRegistry,
@@ -2165,7 +2359,6 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2165
2359
  supersedeDecision: (interviewId, key, correction) => decisionBoard.supersede(interviewId, key, correction),
2166
2360
  userTasks,
2167
2361
  getTaskMemoryStatus: () => {
2168
- ensureTaskMemorySession();
2169
2362
  const settings = getCurrentSettings();
2170
2363
  const bank = taskMemoryBank.snapshot();
2171
2364
  return {
@@ -2319,10 +2512,8 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2319
2512
  ? {
2320
2513
  onResumeSession: (sessionId) => {
2321
2514
  try {
2322
- const previousSessionId = session.current()?.id ?? null;
2323
2515
  session.resume(sessionId);
2324
2516
  taskBoard.snapshot();
2325
- if (previousSessionId !== sessionId) ensureTaskMemorySession();
2326
2517
  } catch (err) {
2327
2518
  process.stderr.write(
2328
2519
  `[/resume] failed to resume ${sessionId}: ${err instanceof Error ? err.message : String(err)}\n`,
@@ -2336,13 +2527,11 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
2336
2527
  if (settings.chat.model) input.model = settings.chat.model;
2337
2528
  session.create(input);
2338
2529
  taskBoard.snapshot();
2339
- ensureTaskMemorySession();
2340
2530
  },
2341
2531
  onForkSession: (parentTurnId) => {
2342
2532
  try {
2343
2533
  session.fork(parentTurnId);
2344
2534
  taskBoard.snapshot();
2345
- ensureTaskMemorySession();
2346
2535
  } catch (err) {
2347
2536
  process.stderr.write(
2348
2537
  `[/fork] failed at turn ${parentTurnId}: ${err instanceof Error ? err.message : String(err)}\n`,