@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
package/src/cli/usage.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  } from "../core/response-model-id.js";
9
9
  import { clioDataDir, clioStateDir } from "../core/xdg.js";
10
10
  import { loadMemoryRecordsSync, type MemoryRecord } from "../domains/memory/index.js";
11
+ import { summarizeFailedCompactionUsage } from "../domains/observability/compaction-usage.js";
11
12
  import { readEvidenceIndex } from "../domains/observability/evidence-index.js";
12
13
  import { type OutOfTurnUsageRow, readOutOfTurnUsageRows } from "../domains/observability/out-of-turn-usage.js";
13
14
  import { loadSkills } from "../domains/resources/index.js";
@@ -119,18 +120,32 @@ interface CallOrigins {
119
120
  handoffs: number;
120
121
  prewarms: number;
121
122
  backgroundMemory: number;
123
+ failedCompaction: number;
122
124
  }
123
125
 
124
126
  function emptyCallOrigins(): CallOrigins {
125
- return { rows: 0, sideQuestions: 0, handoffs: 0, prewarms: 0, backgroundMemory: 0 };
127
+ return { rows: 0, sideQuestions: 0, handoffs: 0, prewarms: 0, backgroundMemory: 0, failedCompaction: 0 };
126
128
  }
127
129
 
128
130
  function turnsOf(origins: CallOrigins): number {
129
- return origins.rows - origins.sideQuestions - origins.handoffs - origins.prewarms - origins.backgroundMemory;
131
+ return (
132
+ origins.rows -
133
+ origins.sideQuestions -
134
+ origins.handoffs -
135
+ origins.prewarms -
136
+ origins.backgroundMemory -
137
+ origins.failedCompaction
138
+ );
130
139
  }
131
140
 
132
141
  function hasLabelledCall(origins: CallOrigins): boolean {
133
- return origins.sideQuestions > 0 || origins.handoffs > 0 || origins.prewarms > 0 || origins.backgroundMemory > 0;
142
+ return (
143
+ origins.sideQuestions > 0 ||
144
+ origins.handoffs > 0 ||
145
+ origins.prewarms > 0 ||
146
+ origins.backgroundMemory > 0 ||
147
+ origins.failedCompaction > 0
148
+ );
134
149
  }
135
150
 
136
151
  function addOutOfTurnRow(origins: CallOrigins, row: OutOfTurnUsageRow): void {
@@ -138,6 +153,7 @@ function addOutOfTurnRow(origins: CallOrigins, row: OutOfTurnUsageRow): void {
138
153
  if (row.label === "side-question") origins.sideQuestions += 1;
139
154
  else if (row.label === "prewarm") origins.prewarms += 1;
140
155
  else if (row.label === "background-memory") origins.backgroundMemory += 1;
156
+ else if (row.label === "failed-compaction") origins.failedCompaction += 1;
141
157
  else origins.handoffs += 1;
142
158
  }
143
159
 
@@ -148,16 +164,32 @@ function outOfTurnUsageCall(row: OutOfTurnUsageRow): LedgerUsageCall {
148
164
  attributedModelId: row.attributedModelId,
149
165
  requestedModelId: row.attributedModelId,
150
166
  responseModelIdObservation: { state: "not-observed" },
151
- input: row.usage.input,
152
- output: row.usage.output,
153
- cacheRead: row.usage.cacheRead,
154
- cacheWrite: row.usage.cacheWrite,
155
- reasoningTokens: row.usage.reasoning,
156
- totalTokens: row.usage.totalTokens,
157
- costUsd: row.usage.costUsd,
167
+ input: row.usage.input ?? 0,
168
+ output: row.usage.output ?? 0,
169
+ cacheRead: row.usage.cacheRead ?? 0,
170
+ cacheWrite: row.usage.cacheWrite ?? 0,
171
+ reasoningTokens: row.usage.reasoning ?? 0,
172
+ totalTokens: row.usage.totalTokens ?? 0,
173
+ costUsd: row.usage.costUsd ?? 0,
158
174
  };
159
175
  }
160
176
 
177
+ /** Existing numeric accumulators sum known contributions; the report retains missing coverage. */
178
+ function reportedUsageTotals(totals: UsageTotals, rows: ReadonlyArray<OutOfTurnUsageRow>) {
179
+ const failedCompaction = summarizeFailedCompactionUsage(rows);
180
+ if (failedCompaction.calls === 0) return totals;
181
+ const reported: Record<string, unknown> = { ...totals, knownSubtotals: true, failedCompaction };
182
+ for (const [field, missing] of Object.entries(failedCompaction.unobservedUsageCalls)) {
183
+ const key = field === "reasoning" ? "reasoningTokens" : field;
184
+ if (missing > 0 && reported[key] === 0) reported[key] = null;
185
+ }
186
+ return reported;
187
+ }
188
+
189
+ function usageAmount(value: unknown): string {
190
+ return typeof value === "number" ? String(value) : "unknown";
191
+ }
192
+
161
193
  function addUsageCall(totals: UsageTotals, call: LedgerUsageCall): void {
162
194
  totals.apiCalls += call.apiCalls ?? 1;
163
195
  totals.input += call.input;
@@ -442,6 +474,8 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
442
474
  // session store is gone can still have recorded out-of-turn spend, and
443
475
  // dropping those rows would report money that was spent as nothing at all.
444
476
  const usageMeasurable = presence.sessionsPresent || outOfTurnRows.length > 0;
477
+ const reportedTotals = reportedUsageTotals(usageTotals, outOfTurnRows);
478
+ const failedCompaction = summarizeFailedCompactionUsage(outOfTurnRows);
445
479
  const usageRows = [...usageByModel.values()].sort(
446
480
  (a, b) => b.totals.totalTokens - a.totals.totalTokens || a.providerId.localeCompare(b.providerId),
447
481
  );
@@ -533,7 +567,7 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
533
567
  emit({
534
568
  kind: "fact",
535
569
  fact: "tokens",
536
- ...usageTotals,
570
+ ...reportedTotals,
537
571
  ...(hasLabelledCall(callOrigins)
538
572
  ? {
539
573
  turns: turnsOf(callOrigins),
@@ -541,6 +575,7 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
541
575
  handoffs: callOrigins.handoffs,
542
576
  prewarms: callOrigins.prewarms,
543
577
  backgroundMemorySteps: callOrigins.backgroundMemory,
578
+ ...(callOrigins.failedCompaction > 0 ? { failedCompactionCalls: callOrigins.failedCompaction } : {}),
544
579
  }
545
580
  : {}),
546
581
  });
@@ -552,7 +587,12 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
552
587
  attributedModelId: row.attributedModelId,
553
588
  requestedModelIds: [...row.requestedModelIds].sort(),
554
589
  responseModelIdObservationCounts: row.responseModelIdObservationCounts,
555
- ...row.totals,
590
+ ...reportedUsageTotals(
591
+ row.totals,
592
+ outOfTurnRows.filter(
593
+ (call) => call.target === row.providerId && call.attributedModelId === row.attributedModelId,
594
+ ),
595
+ ),
556
596
  });
557
597
  }
558
598
  for (const session of cacheSessions) {
@@ -615,7 +655,7 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
615
655
  out(` audited tool calls in window: ${auditToolCalls.length} (${auditBlocked.length} blocked/denied)`);
616
656
  if (usageMeasurable) {
617
657
  out(
618
- ` tokens in window: ${usageTotals.totalTokens} over ${usageTotals.apiCalls} model calls (input ${usageTotals.input}, output ${usageTotals.output}, cache read ${usageTotals.cacheRead}, cache write ${usageTotals.cacheWrite}, reasoning ${usageTotals.reasoningTokens})`,
658
+ ` tokens in window: ${usageAmount(reportedTotals.totalTokens)} over ${usageTotals.apiCalls} model calls (input ${usageAmount(reportedTotals.input)}, output ${usageAmount(reportedTotals.output)}, cache read ${usageAmount(reportedTotals.cacheRead)}, cache write ${usageAmount(reportedTotals.cacheWrite)}, reasoning ${usageAmount(reportedTotals.reasoningTokens)})`,
619
659
  );
620
660
  if (hasLabelledCall(callOrigins)) {
621
661
  // Only printed when a labelled call is in the window. Over an archive
@@ -629,7 +669,18 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
629
669
  out(` background memory steps in window: ${callOrigins.backgroundMemory}`);
630
670
  }
631
671
  }
632
- out(` provider-reported cost in window: $${usageTotals.costUsd.toFixed(4)}`);
672
+ if (failedCompaction.calls > 0) {
673
+ out(
674
+ ` failed-compaction streams: ${failedCompaction.calls} (${failedCompaction.erroredCalls} errored, ${failedCompaction.abortedCalls} aborted, ${failedCompaction.successfulCalls} completed before the attempt failed)`,
675
+ );
676
+ out(" usage figures are known subtotals; missing compaction usage is not zero or a complete spending bound");
677
+ out(
678
+ ` errored compaction known cost: ${failedCompaction.erroredKnownUsage.costUsd === null ? "unknown" : `$${failedCompaction.erroredKnownUsage.costUsd.toFixed(4)}`}`,
679
+ );
680
+ }
681
+ out(
682
+ ` ${failedCompaction.calls > 0 ? "known reported/estimated" : "provider-reported"} cost in window: ${typeof reportedTotals.costUsd === "number" ? `$${reportedTotals.costUsd.toFixed(4)}` : "unknown"}`,
683
+ );
633
684
  }
634
685
  if (usageRows.length > 0) {
635
686
  out("");
@@ -650,19 +701,27 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
650
701
  "tokens",
651
702
  "cost",
652
703
  ],
653
- ...usageRows.map((row) => [
654
- row.providerId,
655
- row.attributedModelId,
656
- [...row.requestedModelIds].sort().join(","),
657
- responseModelIdObservationCountsLabel(row.responseModelIdObservationCounts),
658
- String(row.totals.apiCalls),
659
- String(row.totals.input),
660
- String(row.totals.output),
661
- String(row.totals.cacheRead),
662
- String(row.totals.reasoningTokens),
663
- String(row.totals.totalTokens),
664
- `$${row.totals.costUsd.toFixed(4)}`,
665
- ]),
704
+ ...usageRows.map((row) => {
705
+ const reported = reportedUsageTotals(
706
+ row.totals,
707
+ outOfTurnRows.filter(
708
+ (call) => call.target === row.providerId && call.attributedModelId === row.attributedModelId,
709
+ ),
710
+ );
711
+ return [
712
+ row.providerId,
713
+ row.attributedModelId,
714
+ [...row.requestedModelIds].sort().join(","),
715
+ responseModelIdObservationCountsLabel(row.responseModelIdObservationCounts),
716
+ String(row.totals.apiCalls),
717
+ usageAmount(reported.input),
718
+ usageAmount(reported.output),
719
+ usageAmount(reported.cacheRead),
720
+ usageAmount(reported.reasoningTokens),
721
+ usageAmount(reported.totalTokens),
722
+ typeof reported.costUsd === "number" ? `$${reported.costUsd.toFixed(4)}` : "unknown",
723
+ ];
724
+ }),
666
725
  ]),
667
726
  ),
668
727
  );
@@ -15,9 +15,8 @@ export interface ValidateModelInput {
15
15
  modelId: string;
16
16
  knownModels: ReadonlyArray<string>;
17
17
  /**
18
- * The live list, when one was fetched. It is consulted only when the static
19
- * catalog is empty, so the catalog stays the authority for cloud runtimes
20
- * and the probe becomes the authority where there is nothing else.
18
+ * The live list, when one was fetched. Callers provide it only when that
19
+ * runtime declares live discovery authoritative for the concrete target.
21
20
  */
22
21
  live?: LiveModelInventory | undefined;
23
22
  force: boolean;
@@ -50,6 +49,7 @@ function formatUnadvertisedModelReason(modelId: string, live: LiveModelInventory
50
49
 
51
50
  export function validateModelChoice(input: ValidateModelInput): ValidateModelResult {
52
51
  const { runtimeId, modelId, knownModels, live, force } = input;
52
+ if (live !== undefined) return validateAgainstLive(modelId, live, force);
53
53
  if (knownModels.length === 0) return validateAgainstLive(modelId, live, force);
54
54
  if (knownModels.includes(modelId)) return { ok: true };
55
55
  if (force) {
@@ -525,6 +525,57 @@ function validateLmStudioSettings(
525
525
  return Object.keys(out).length > 0 ? out : undefined;
526
526
  }
527
527
 
528
+ function validateLiteLLMSettings(
529
+ issues: Issues,
530
+ path: string,
531
+ value: unknown,
532
+ ): ClioSettings["targets"][number]["litellm"] | undefined {
533
+ if (!isPlainObject(value)) {
534
+ issues.add(path, `expected a map, got ${describe(value)}`);
535
+ return undefined;
536
+ }
537
+ issues.unknownKeys(path, value, ["request"]);
538
+ const out: NonNullable<ClioSettings["targets"][number]["litellm"]> = {};
539
+ if ("request" in value) {
540
+ if (!isPlainObject(value.request)) {
541
+ issues.add(`${path}.request`, `expected a map, got ${describe(value.request)}`);
542
+ } else {
543
+ issues.unknownKeys(`${path}.request`, value.request, [
544
+ "tags",
545
+ "sendSessionId",
546
+ "timeoutSeconds",
547
+ "streamTimeoutSeconds",
548
+ "numRetries",
549
+ ]);
550
+ const request: NonNullable<typeof out.request> = {};
551
+ if ("tags" in value.request) {
552
+ const parsed = expectStringArray(issues, `${path}.request.tags`, value.request.tags);
553
+ if (parsed !== undefined) {
554
+ const invalidTag = parsed.find((tag) => tag.includes(","));
555
+ if (invalidTag) issues.add(`${path}.request.tags`, "tags may not contain commas");
556
+ else if (parsed.length > 0) request.tags = [...new Set(parsed)];
557
+ }
558
+ }
559
+ if ("sendSessionId" in value.request) {
560
+ const parsed = expectBoolean(issues, `${path}.request.sendSessionId`, value.request.sendSessionId);
561
+ if (parsed !== undefined) request.sendSessionId = parsed;
562
+ }
563
+ for (const key of ["timeoutSeconds", "streamTimeoutSeconds"] as const) {
564
+ if (key in value.request) {
565
+ const parsed = expectNumber(issues, `${path}.request.${key}`, value.request[key], { min: 0.001, max: 86_400 });
566
+ if (parsed !== undefined) request[key] = parsed;
567
+ }
568
+ }
569
+ if ("numRetries" in value.request) {
570
+ const parsed = expectInteger(issues, `${path}.request.numRetries`, value.request.numRetries, { min: 0, max: 100 });
571
+ if (parsed !== undefined) request.numRetries = parsed;
572
+ }
573
+ if (Object.keys(request).length > 0) out.request = request;
574
+ }
575
+ }
576
+ return Object.keys(out).length > 0 ? out : undefined;
577
+ }
578
+
528
579
  function validateTarget(issues: Issues, path: string, value: unknown): ClioSettings["targets"][number] | null {
529
580
  if (!isPlainObject(value)) {
530
581
  issues.add(path, `expected a map, got ${describe(value)}`);
@@ -542,6 +593,7 @@ function validateTarget(issues: Issues, path: string, value: unknown): ClioSetti
542
593
  "gateway",
543
594
  "pricing",
544
595
  "lmstudio",
596
+ "litellm",
545
597
  "maxConcurrentRequests",
546
598
  ]);
547
599
  const id = "id" in value ? expectString(issues, `${path}.id`, value.id) : undefined;
@@ -593,6 +645,10 @@ function validateTarget(issues: Issues, path: string, value: unknown): ClioSetti
593
645
  const v = validateLmStudioSettings(issues, `${path}.lmstudio`, value.lmstudio);
594
646
  if (v !== undefined) target.lmstudio = v;
595
647
  }
648
+ if ("litellm" in value) {
649
+ const v = validateLiteLLMSettings(issues, `${path}.litellm`, value.litellm);
650
+ if (v !== undefined) target.litellm = v;
651
+ }
596
652
  if ("maxConcurrentRequests" in value) {
597
653
  const v = expectInteger(issues, `${path}.maxConcurrentRequests`, value.maxConcurrentRequests, { min: 1 });
598
654
  if (v !== undefined) target.maxConcurrentRequests = v;
@@ -0,0 +1,44 @@
1
+ const REDACTED = "[redacted]";
2
+
3
+ /**
4
+ * Sanitize free-form text received from an external process before it crosses
5
+ * into errors, receipts, transcripts, evidence, or model-catalog snapshots.
6
+ */
7
+ function redactExternalDiagnostic(input: string): string {
8
+ return input
9
+ .replace(/\b(?:authorization|proxy-authorization)\s*:\s*[^\r\n]+/giu, `Authorization: ${REDACTED}`)
10
+ .replace(/\b(?:set-cookie|cookie)\s*:\s*[^\r\n]+/giu, `Cookie: ${REDACTED}`)
11
+ .replace(/\bbearer\s+[A-Za-z0-9._~+/-]+=*/giu, `Bearer ${REDACTED}`)
12
+ .replace(
13
+ /\b(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|secret|password)\s*[:=]\s*[^\s,;]+/giu,
14
+ (match) => {
15
+ const separator = match.includes(":") ? ":" : "=";
16
+ return `${match.slice(0, match.indexOf(separator)).trim()}${separator}${REDACTED}`;
17
+ },
18
+ )
19
+ .replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, REDACTED)
20
+ .replace(
21
+ /(?:~|\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*)\/\.(?:gemini\/antigravity-cli|antigravitycli)(?:\/[^\s]*)?/gu,
22
+ "[redacted credential path]",
23
+ )
24
+ .replace(/https?:\/\/[^\s"'<>]+/giu, (url) => {
25
+ try {
26
+ const parsed = new URL(url);
27
+ if (/(?:auth|oauth|login|signin|callback)/iu.test(parsed.pathname)) return "[redacted authorization URL]";
28
+ parsed.username = "";
29
+ parsed.password = "";
30
+ parsed.search = "";
31
+ parsed.hash = "";
32
+ return parsed.toString();
33
+ } catch {
34
+ return REDACTED;
35
+ }
36
+ });
37
+ }
38
+
39
+ export function boundedExternalDiagnostic(input: string, maxBytes = 8192): string {
40
+ const redacted = redactExternalDiagnostic(input).trim();
41
+ const bytes = Buffer.from(redacted, "utf8");
42
+ if (bytes.byteLength <= maxBytes) return redacted;
43
+ return `${bytes.subarray(0, Math.max(0, maxBytes - 32)).toString("utf8")}\n[diagnostic truncated]`;
44
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Durable routing facts reported by an AI gateway for one upstream response.
3
+ *
4
+ * The gateway is authoritative for the physical deployment that served an
5
+ * alias. Clio deliberately records only the host portion of an upstream API
6
+ * base: LiteLLM deployments may embed paths, query credentials, or user-info
7
+ * in the configured URL, none of which belongs in a session or sealed receipt.
8
+ */
9
+ export interface LiteLLMGatewayRoutingObservation {
10
+ gateway: "litellm";
11
+ callId?: string;
12
+ modelGroup?: string;
13
+ modelName?: string;
14
+ apiBaseHost?: string;
15
+ deploymentId?: string;
16
+ attemptedFallbacks?: number;
17
+ attemptedRetries?: number;
18
+ overheadMs?: number;
19
+ responseDurationMs?: number;
20
+ version?: string;
21
+ }
22
+
23
+ export type GatewayRoutingObservation = LiteLLMGatewayRoutingObservation;
24
+
25
+ /**
26
+ * Preserve the provider's diagnostic while making Clio's failure policy
27
+ * explicit and actionable. LiteLLM targets deliberately do not enter Clio's
28
+ * transient retry ladder: a named route is an operator choice, not permission
29
+ * to substitute another model behind their back.
30
+ */
31
+ export function liteLLMRouteFailureMessage(
32
+ errorMessage: string | null | undefined,
33
+ targetId: string,
34
+ modelId: string,
35
+ ): string {
36
+ const providerMessage = errorMessage?.trim() || "LiteLLM request failed.";
37
+ const advice =
38
+ `LiteLLM route '${modelId}' on target '${targetId}' failed. ` +
39
+ "Clio did not retry or substitute another model; select a different route with /model, then resend.";
40
+ return providerMessage.includes(advice) ? providerMessage : `${providerMessage}\n\n${advice}`;
41
+ }
42
+
43
+ interface HeaderReader {
44
+ get(name: string): string | null;
45
+ }
46
+
47
+ function nonEmptyString(value: unknown): string | undefined {
48
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
49
+ }
50
+
51
+ function nonnegativeNumber(value: unknown): number | undefined {
52
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
53
+ if (typeof value !== "string" || value.trim().length === 0) return undefined;
54
+ const parsed = Number(value);
55
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
56
+ }
57
+
58
+ function nonnegativeInteger(value: unknown): number | undefined {
59
+ const parsed = nonnegativeNumber(value);
60
+ return parsed === undefined ? undefined : Math.floor(parsed);
61
+ }
62
+
63
+ function apiBaseHost(value: unknown): string | undefined {
64
+ const raw = nonEmptyString(value);
65
+ if (raw === undefined) return undefined;
66
+ try {
67
+ return new URL(raw).host || undefined;
68
+ } catch {
69
+ return undefined;
70
+ }
71
+ }
72
+
73
+ function storedApiBaseHost(value: unknown): string | undefined {
74
+ const raw = nonEmptyString(value);
75
+ if (raw === undefined) return undefined;
76
+ try {
77
+ return new URL(raw.includes("://") ? raw : `http://${raw}`).host || undefined;
78
+ } catch {
79
+ return undefined;
80
+ }
81
+ }
82
+
83
+ /** Read LiteLLM's response headers without retaining sensitive API-base data. */
84
+ export function liteLLMGatewayRoutingFromHeaders(headers: HeaderReader): GatewayRoutingObservation | null {
85
+ const callId = nonEmptyString(headers.get("x-litellm-call-id"));
86
+ const modelGroup = nonEmptyString(headers.get("x-litellm-model-group"));
87
+ const modelName = nonEmptyString(headers.get("x-litellm-model-name"));
88
+ const host = apiBaseHost(headers.get("x-litellm-model-api-base"));
89
+ const deploymentId = nonEmptyString(headers.get("x-litellm-model-id"));
90
+ const attemptedFallbacks = nonnegativeInteger(headers.get("x-litellm-attempted-fallbacks"));
91
+ const attemptedRetries = nonnegativeInteger(headers.get("x-litellm-attempted-retries"));
92
+ const overheadMs = nonnegativeNumber(headers.get("x-litellm-overhead-duration-ms"));
93
+ const responseDurationMs = nonnegativeNumber(headers.get("x-litellm-response-duration-ms"));
94
+ const version = nonEmptyString(headers.get("x-litellm-version"));
95
+ const observation: GatewayRoutingObservation = {
96
+ gateway: "litellm",
97
+ ...(callId !== undefined ? { callId } : {}),
98
+ ...(modelGroup !== undefined ? { modelGroup } : {}),
99
+ ...(modelName !== undefined ? { modelName } : {}),
100
+ ...(host !== undefined ? { apiBaseHost: host } : {}),
101
+ ...(deploymentId !== undefined ? { deploymentId } : {}),
102
+ ...(attemptedFallbacks !== undefined ? { attemptedFallbacks } : {}),
103
+ ...(attemptedRetries !== undefined ? { attemptedRetries } : {}),
104
+ ...(overheadMs !== undefined ? { overheadMs } : {}),
105
+ ...(responseDurationMs !== undefined ? { responseDurationMs } : {}),
106
+ ...(version !== undefined ? { version } : {}),
107
+ };
108
+ return Object.keys(observation).length > 1 ? observation : null;
109
+ }
110
+
111
+ /** Parse a live message, persisted session row, or receipt response defensively. */
112
+ export function gatewayRoutingObservationFromRecord(
113
+ record: Readonly<Record<string, unknown>>,
114
+ ): GatewayRoutingObservation | null {
115
+ const value = record.gatewayRouting;
116
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
117
+ const raw = value as Record<string, unknown>;
118
+ if (raw.gateway !== "litellm") return null;
119
+ const callId = nonEmptyString(raw.callId);
120
+ const modelGroup = nonEmptyString(raw.modelGroup);
121
+ const modelName = nonEmptyString(raw.modelName);
122
+ const host = storedApiBaseHost(raw.apiBaseHost);
123
+ const deploymentId = nonEmptyString(raw.deploymentId);
124
+ const attemptedFallbacks = nonnegativeInteger(raw.attemptedFallbacks);
125
+ const attemptedRetries = nonnegativeInteger(raw.attemptedRetries);
126
+ const overheadMs = nonnegativeNumber(raw.overheadMs);
127
+ const responseDurationMs = nonnegativeNumber(raw.responseDurationMs);
128
+ const version = nonEmptyString(raw.version);
129
+ const observation: GatewayRoutingObservation = {
130
+ gateway: "litellm",
131
+ ...(callId !== undefined ? { callId } : {}),
132
+ ...(modelGroup !== undefined ? { modelGroup } : {}),
133
+ ...(modelName !== undefined ? { modelName } : {}),
134
+ ...(host !== undefined ? { apiBaseHost: host } : {}),
135
+ ...(deploymentId !== undefined ? { deploymentId } : {}),
136
+ ...(attemptedFallbacks !== undefined ? { attemptedFallbacks } : {}),
137
+ ...(attemptedRetries !== undefined ? { attemptedRetries } : {}),
138
+ ...(overheadMs !== undefined ? { overheadMs } : {}),
139
+ ...(responseDurationMs !== undefined ? { responseDurationMs } : {}),
140
+ ...(version !== undefined ? { version } : {}),
141
+ };
142
+ return Object.keys(observation).length > 1 ? observation : null;
143
+ }
144
+
145
+ /** Compact human projection for receipts and operator notices. */
146
+ export function gatewayRoutingObservationLabel(observation: GatewayRoutingObservation): string {
147
+ const route = [observation.modelGroup, observation.modelName].filter(Boolean).join("->");
148
+ const destination = [route, observation.apiBaseHost ? `@${observation.apiBaseHost}` : ""].filter(Boolean).join("");
149
+ const parts = [
150
+ "gateway=litellm",
151
+ ...(destination ? [`route=${destination}`] : []),
152
+ ...(observation.attemptedFallbacks !== undefined ? [`fallbacks=${observation.attemptedFallbacks}`] : []),
153
+ ...(observation.attemptedRetries !== undefined ? [`retries=${observation.attemptedRetries}`] : []),
154
+ ...(observation.overheadMs !== undefined ? [`overhead_ms=${observation.overheadMs}`] : []),
155
+ ];
156
+ return parts.join(" ");
157
+ }
@@ -24,6 +24,18 @@ const ENV_ALLOWLIST = [
24
24
  "TMP",
25
25
  "CI",
26
26
  "NO_COLOR",
27
+ // Platform/session location variables needed by installed local CLIs. These
28
+ // locate operator-owned state; provider keys and Clio gates are deliberately
29
+ // absent from this allowlist.
30
+ "XDG_CONFIG_HOME",
31
+ "XDG_CACHE_HOME",
32
+ "XDG_STATE_HOME",
33
+ "APPDATA",
34
+ "LOCALAPPDATA",
35
+ "SYSTEMROOT",
36
+ "WINDIR",
37
+ "COMSPEC",
38
+ "PATHEXT",
27
39
  ] as const;
28
40
 
29
41
  export interface SafeCommandResult {
@@ -49,10 +61,13 @@ export interface RunCommandVectorOptions {
49
61
  env?: Record<string, string>;
50
62
  }
51
63
 
52
- export function buildSafeToolEnv(extra: Record<string, string> = {}): NodeJS.ProcessEnv {
64
+ export function buildSafeToolEnv(
65
+ extra: Record<string, string> = {},
66
+ source: NodeJS.ProcessEnv = process.env,
67
+ ): NodeJS.ProcessEnv {
53
68
  const env: NodeJS.ProcessEnv = {};
54
69
  for (const key of ENV_ALLOWLIST) {
55
- const value = process.env[key];
70
+ const value = source[key];
56
71
  if (value !== undefined) env[key] = value;
57
72
  }
58
73
  for (const [key, value] of Object.entries(extra)) {
@@ -58,6 +58,90 @@ export interface PendingSkillToolPolicy {
58
58
  loadedSkillNames: Set<string>;
59
59
  /** Declared tool policy per successfully loaded skill, recorded by context(scope=skills). */
60
60
  loadedSkillPolicies: Map<string, SkillDeclaredToolPolicy>;
61
+ /**
62
+ * True once this policy is a surface that outlived the turn that armed it.
63
+ * The narrowing is identical; the flag exists so a load request the policy
64
+ * does not cover reads as "only the operator activates skills" rather than
65
+ * claiming a pending request that no longer exists.
66
+ */
67
+ carriedSurface?: boolean;
68
+ /**
69
+ * True when the session's autonomy level lets the model activate an
70
+ * installed skill itself instead of emitting the suggestion anchor and
71
+ * waiting for `/skill`. Activation is the same per-run policy the operator's
72
+ * path produces: skills only ever narrow the tool surface, so this grants
73
+ * nothing the level had not already granted. Marketplace (not yet
74
+ * installed) skills stay operator-gated at every level.
75
+ */
76
+ modelActivation?: boolean;
77
+ }
78
+
79
+ /**
80
+ * The surface that stays armed after an interactive turn ends.
81
+ *
82
+ * A skill an operator loaded narrows the tools for the workflow it started,
83
+ * and a multi-turn workflow (an interview skill asking a question per turn)
84
+ * is still inside that workflow on the operator's next message. Dropping the
85
+ * narrowing at turn end handed the workflow back the full surface partway
86
+ * through, which is how a `bash` call correctly refused in turn one ran in
87
+ * turn six. Returns undefined when nothing declared a narrowing, which is the
88
+ * signal to clear whatever surface was armed before.
89
+ */
90
+ export function armedSkillSurface(policy: PendingSkillToolPolicy | undefined): PendingSkillToolPolicy | undefined {
91
+ if (!policy) return undefined;
92
+ const declared = [...policy.loadedSkillPolicies.entries()].filter(
93
+ ([, declaration]) => (declaration.allowedTools?.length ?? 0) > 0 || (declaration.disallowedTools?.length ?? 0) > 0,
94
+ );
95
+ if (declared.length === 0) return undefined;
96
+ return {
97
+ allowedSkillNames: [...policy.allowedSkillNames],
98
+ requests: [...policy.requests],
99
+ loadedSkillNames: new Set(policy.loadedSkillNames),
100
+ loadedSkillPolicies: new Map(declared),
101
+ carriedSurface: true,
102
+ };
103
+ }
104
+
105
+ /** Skill names contributing a declaration to an armed surface, for the operator notice. */
106
+ export function skillSurfaceNames(policy: PendingSkillToolPolicy | undefined): ReadonlyArray<string> {
107
+ return policy ? [...policy.loadedSkillPolicies.keys()] : [];
108
+ }
109
+
110
+ /**
111
+ * The same names, each tagged with who activated it. A skill the operator
112
+ * named arrives as a pending request; anything else was activated by the model
113
+ * under `modelActivation`, and the operator's one line about the surface is
114
+ * where that distinction has to be visible.
115
+ */
116
+ export function skillSurfaceLabels(policy: PendingSkillToolPolicy | undefined): ReadonlyArray<string> {
117
+ if (!policy) return [];
118
+ return [...policy.loadedSkillPolicies.keys()].map((name) =>
119
+ policy.requests.some((request) => request.name === name) ? `${name} (operator)` : `${name} (model)`,
120
+ );
121
+ }
122
+
123
+ /**
124
+ * Stamp (or clear) the model-activation grant on the policy this turn runs
125
+ * under, creating one when nothing else did. Mutates in place so a surface
126
+ * carried from an earlier turn keeps its identity, and re-stamps every turn so
127
+ * a level change takes effect on the next message rather than the next skill.
128
+ */
129
+ export function withModelSkillActivation(
130
+ policy: PendingSkillToolPolicy | undefined,
131
+ enabled: boolean,
132
+ ): PendingSkillToolPolicy | undefined {
133
+ if (policy) {
134
+ policy.modelActivation = enabled;
135
+ return policy;
136
+ }
137
+ if (!enabled) return undefined;
138
+ return {
139
+ allowedSkillNames: [],
140
+ requests: [],
141
+ loadedSkillNames: new Set<string>(),
142
+ loadedSkillPolicies: new Map<string, SkillDeclaredToolPolicy>(),
143
+ modelActivation: true,
144
+ };
61
145
  }
62
146
 
63
147
  /**
@@ -75,6 +159,8 @@ export interface SkillToolSurfaceViolation {
75
159
  mergedAllowedTools: ReadonlyArray<string> | null;
76
160
  /** Skills whose disallowed-tools name the tool directly. */
77
161
  disallowedBy: ReadonlyArray<string>;
162
+ /** True when the surface outlived the turn that armed it, which changes the lifetime the block message states. */
163
+ carriedSurface: boolean;
78
164
  }
79
165
 
80
166
  /**
@@ -98,15 +184,16 @@ export function evaluateSkillToolSurface(
98
184
  if (SKILL_SURFACE_EXEMPT_TOOLS.has(tool)) return null;
99
185
  const entries = [...policy.loadedSkillPolicies.entries()];
100
186
  const skills = entries.map(([name]) => name);
187
+ const carriedSurface = policy.carriedSurface === true;
101
188
  const disallowedBy = entries
102
189
  .filter(([, declared]) => declared.disallowedTools?.includes(tool) === true)
103
190
  .map(([name]) => name);
104
- if (disallowedBy.length > 0) return { skills, mergedAllowedTools: null, disallowedBy };
191
+ if (disallowedBy.length > 0) return { skills, mergedAllowedTools: null, disallowedBy, carriedSurface };
105
192
  const allowLists = entries.map(([, declared]) => declared.allowedTools);
106
193
  if (allowLists.some((list) => list === undefined || list.length === 0)) return null;
107
194
  const merged = [...new Set(allowLists.flatMap((list) => [...(list ?? [])]))];
108
195
  if (merged.includes(tool)) return null;
109
- return { skills, mergedAllowedTools: merged, disallowedBy: [] };
196
+ return { skills, mergedAllowedTools: merged, disallowedBy: [], carriedSurface };
110
197
  }
111
198
 
112
199
  /**