@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,78 +1,258 @@
1
+ import { boundedExternalDiagnostic } from "../../../../core/external-diagnostic.js";
2
+ import { runCommandVector, type SafeCommandResult } from "../../../../core/safe-exec.js";
1
3
  import type { Api, Model } from "../../../../engine/types.js";
2
4
 
3
5
  import { synthesizeCatalogBackedModel } from "../../catalog.js";
4
6
  import type { CapabilityFlags } from "../../types/capability-flags.js";
5
7
  import type { KnowledgeBaseHit } from "../../types/knowledge-base.js";
6
- import type { RuntimeDescriptor } from "../../types/runtime-descriptor.js";
8
+ import type { ProbeContext, ProbeResult, RuntimeDescriptor } from "../../types/runtime-descriptor.js";
7
9
  import type { TargetDescriptor } from "../../types/target-descriptor.js";
8
10
 
9
11
  /**
10
- * Antigravity CLI (`agy`) subscription runtime.
12
+ * Antigravity CLI (`agy`) external delegation runtime.
11
13
  *
12
- * A subprocess worker runtime that drives the local `agy --print`, the same
13
- * shape as the `claude-code` runtime: Clio spawns the CLI, streams its output,
14
- * and maps Clio autonomy levels onto the CLI's permission flags. `agy` brings
15
- * Google's Antigravity agent harness (Gemini 3.x and hosted Claude/GPT-OSS
16
- * models, up to ~1M tokens of context) to the Clio worker fleet.
17
- *
18
- * `agy --print` prints plain text (there is no structured event stream), so the
19
- * worker runner cannot mediate individual agy tool calls the way the Claude SDK
20
- * runner does. Permission gating is coarse: `--mode plan --sandbox` for
21
- * read-only runs and `--dangerously-skip-permissions` only under `full-auto`
22
- * plus the explicit `CLIO_CODER_ALLOW_EXTERNAL_FULL_ACCESS=1` environment gate.
23
- * agy's own `settings.json` still governs modes that Clio does not explicitly
24
- * constrain.
14
+ * This is deliberately a dispatch-only subprocess, not a Gemini inference
15
+ * provider. The installed official CLI owns authentication, its agent loop,
16
+ * tools, network access, and model access. Clio supplies one bounded work order
17
+ * and translates the documented structured result into a worker result.
25
18
  */
26
19
 
27
20
  export const ANTIGRAVITY_AUTH_NOTICE =
28
- "Uses your existing Antigravity (`agy`) login. Clio stores no Antigravity credentials. " +
29
- "agy runs its own agent harness as a subprocess; Clio maps autonomy levels onto its CLI flags " +
30
- "but cannot mediate individual agy tool calls.";
21
+ "Experimental local delegation through your installed Antigravity (`agy`) CLI. Clio neither signs in nor stores " +
22
+ "or inspects Antigravity credentials. Run `agy` yourself and complete sign-in before probing this target.";
31
23
 
32
- // Exact model strings accepted by `agy --model` (from `agy models`). The High
33
- // Flash tier leads: it is the fast, large-context default.
24
+ // Hints only until an account returns a live catalog. They are never used to
25
+ // validate a model after a successful live probe.
34
26
  export const ANTIGRAVITY_MODELS: ReadonlyArray<string> = [
35
- "Gemini 3.5 Flash (High)",
36
- "Gemini 3.5 Flash (Medium)",
37
- "Gemini 3.5 Flash (Low)",
38
- "Gemini 3.1 Pro (High)",
39
- "Gemini 3.1 Pro (Low)",
40
- "Claude Sonnet 4.6 (Thinking)",
41
- "Claude Opus 4.6 (Thinking)",
42
- "GPT-OSS 120B (Medium)",
27
+ "gemini-3.8-flash-high",
28
+ "gemini-3.8-flash-medium",
29
+ "gemini-3.8-flash-low",
30
+ "gemini-3.1-pro-high",
31
+ "gemini-3.1-pro-low",
43
32
  ];
44
33
 
34
+ /** Typed Clio capabilities only. Antigravity's own opaque tools are declared separately. */
45
35
  export const antigravityCapabilities: CapabilityFlags = {
46
36
  chat: true,
47
- tools: true,
48
- toolCallFormat: "openai",
49
- reasoning: true,
50
- vision: true,
37
+ tools: false,
38
+ reasoning: false,
39
+ vision: false,
51
40
  audio: false,
52
41
  embeddings: false,
53
42
  rerank: false,
54
43
  fim: false,
55
- contextWindow: 1_000_000,
56
- maxTokens: 8192,
44
+ contextWindow: 0,
45
+ maxTokens: 0,
57
46
  };
58
47
 
48
+ export const ANTIGRAVITY_MAX_CATALOG_BYTES = 1024 * 1024;
49
+ export const ANTIGRAVITY_MAX_DISCOVERED_MODELS = 4096;
50
+ const MAX_MODEL_ID_CHARS = 1024;
51
+ const MAX_MODEL_LABEL_CHARS = 1024;
52
+
53
+ type AntigravityProbeFailureKind = NonNullable<ProbeResult["failureKind"]>;
54
+
55
+ class AntigravityCatalogError extends Error {
56
+ readonly kind: AntigravityProbeFailureKind;
57
+
58
+ constructor(kind: AntigravityProbeFailureKind, message: string) {
59
+ super(message);
60
+ this.name = "AntigravityCatalogError";
61
+ this.kind = kind;
62
+ }
63
+ }
64
+
65
+ function record(value: unknown): Record<string, unknown> | null {
66
+ return typeof value === "object" && value !== null && !Array.isArray(value)
67
+ ? (value as Record<string, unknown>)
68
+ : null;
69
+ }
70
+
71
+ export interface AntigravityModelCatalog {
72
+ models: string[];
73
+ labels: Record<string, string>;
74
+ }
75
+
76
+ /** Decode the machine-readable catalog emitted by `agy --output-format json models`. */
77
+ export function parseAntigravityModelCatalogDetails(output: string): AntigravityModelCatalog {
78
+ let decoded: unknown;
79
+ try {
80
+ decoded = JSON.parse(output);
81
+ } catch {
82
+ throw new AntigravityCatalogError(
83
+ "unsupported-feature",
84
+ "agy returned an unreadable model catalog; update the Antigravity CLI and retry",
85
+ );
86
+ }
87
+ const envelope = record(decoded);
88
+ const command = record(envelope?.command);
89
+ const data = record(command?.data);
90
+ if (envelope?.status !== "SUCCESS") {
91
+ const detail = boundedExternalDiagnostic(
92
+ typeof envelope?.error === "string"
93
+ ? envelope.error
94
+ : typeof command?.error === "string"
95
+ ? command.error
96
+ : "agy could not list models",
97
+ );
98
+ const kind = classifyAntigravityDiagnostic(detail);
99
+ throw new AntigravityCatalogError(kind, antigravityProbeMessage(kind, detail));
100
+ }
101
+ if (command?.name !== "models" || !Array.isArray(data?.models)) {
102
+ throw new AntigravityCatalogError(
103
+ "unsupported-feature",
104
+ "agy returned an unsupported model catalog; update the Antigravity CLI and retry",
105
+ );
106
+ }
107
+ if (data.models.length === 0) {
108
+ throw new AntigravityCatalogError(
109
+ "catalog-unavailable",
110
+ "agy returned no available models; run `agy` locally and verify the signed-in account has model access",
111
+ );
112
+ }
113
+ if (data.models.length > ANTIGRAVITY_MAX_DISCOVERED_MODELS) {
114
+ throw new AntigravityCatalogError(
115
+ "catalog-unavailable",
116
+ `agy returned more than ${ANTIGRAVITY_MAX_DISCOVERED_MODELS} models; the authoritative catalog was refused rather than truncated`,
117
+ );
118
+ }
119
+
120
+ const models: string[] = [];
121
+ const labels: Record<string, string> = {};
122
+ const seen = new Set<string>();
123
+ for (const value of data.models) {
124
+ const model = record(value);
125
+ const id = typeof model?.id === "string" ? model.id.trim() : "";
126
+ const label = typeof model?.label === "string" ? model.label.trim() : "";
127
+ if (id.length === 0 || id.length > MAX_MODEL_ID_CHARS || /[\r\n\0]/u.test(id)) {
128
+ throw new AntigravityCatalogError("catalog-unavailable", "agy returned an invalid model identifier");
129
+ }
130
+ if (label.length === 0 || label.length > MAX_MODEL_LABEL_CHARS || /[\r\n\0]/u.test(label)) {
131
+ throw new AntigravityCatalogError("catalog-unavailable", `agy returned an invalid label for model '${id}'`);
132
+ }
133
+ if (seen.has(id)) {
134
+ if (labels[id] !== label) {
135
+ throw new AntigravityCatalogError(
136
+ "catalog-unavailable",
137
+ `agy returned conflicting labels for duplicate model '${id}'`,
138
+ );
139
+ }
140
+ continue;
141
+ }
142
+ seen.add(id);
143
+ models.push(id);
144
+ labels[id] = label;
145
+ }
146
+ return { models, labels };
147
+ }
148
+
149
+ function classifyAntigravityDiagnostic(detail: string): AntigravityProbeFailureKind {
150
+ if (/(?:enoent|not found|cannot find|could not start|spawn .*agy)/iu.test(detail)) return "missing";
151
+ if (/(?:sign[ -]?in|log[ -]?in|unauthori[sz]ed|authenticat|credential|account.*required)/iu.test(detail)) {
152
+ return "authentication";
153
+ }
154
+ if (
155
+ /(?:unknown|unsupported|unrecognized|invalid) (?:option|argument|command)|output-format|models command/iu.test(detail)
156
+ ) {
157
+ return "unsupported-feature";
158
+ }
159
+ return "generic";
160
+ }
161
+
162
+ function antigravityProbeMessage(kind: AntigravityProbeFailureKind, detail: string): string {
163
+ const safeDetail = boundedExternalDiagnostic(detail || "no diagnostic was provided");
164
+ switch (kind) {
165
+ case "missing":
166
+ return "Antigravity CLI is not installed or `agy` is not on PATH";
167
+ case "authentication":
168
+ return `Antigravity sign-in is required; run \`agy\` yourself and complete sign-in (${safeDetail})`;
169
+ case "unsupported-feature":
170
+ return `the installed Antigravity CLI does not expose the required structured model catalog; update \`agy\` (${safeDetail})`;
171
+ case "catalog-unavailable":
172
+ return safeDetail;
173
+ case "cancelled":
174
+ return "Antigravity model discovery was cancelled";
175
+ default:
176
+ return `Antigravity model discovery failed: ${safeDetail}`;
177
+ }
178
+ }
179
+
180
+ export async function probeAntigravityModelCatalog(
181
+ ctx: ProbeContext,
182
+ runner: typeof runCommandVector = runCommandVector,
183
+ ): Promise<ProbeResult> {
184
+ const result: SafeCommandResult = await runner("agy", ["--output-format", "json", "models"], {
185
+ cwd: process.cwd(),
186
+ workspaceRoot: process.cwd(),
187
+ timeoutMs: Math.max(1, ctx.httpTimeoutMs),
188
+ maxOutputBytes: ANTIGRAVITY_MAX_CATALOG_BYTES,
189
+ ...(ctx.signal ? { signal: ctx.signal } : {}),
190
+ });
191
+ if (result.aborted || result.timedOut) {
192
+ return {
193
+ ok: false,
194
+ latencyMs: result.durationMs,
195
+ failureKind: "cancelled",
196
+ error: antigravityProbeMessage("cancelled", ""),
197
+ };
198
+ }
199
+ if (result.outputCapped) {
200
+ return {
201
+ ok: false,
202
+ latencyMs: result.durationMs,
203
+ failureKind: "catalog-unavailable",
204
+ error: `agy model catalog exceeded ${ANTIGRAVITY_MAX_CATALOG_BYTES} bytes`,
205
+ };
206
+ }
207
+ if (result.exitCode !== 0) {
208
+ const detail = boundedExternalDiagnostic(result.stderr || result.stdout || `agy exited ${String(result.exitCode)}`);
209
+ const kind = classifyAntigravityDiagnostic(detail);
210
+ return { ok: false, latencyMs: result.durationMs, failureKind: kind, error: antigravityProbeMessage(kind, detail) };
211
+ }
212
+ try {
213
+ const catalog = parseAntigravityModelCatalogDetails(result.stdout);
214
+ return {
215
+ ok: true,
216
+ latencyMs: result.durationMs,
217
+ models: catalog.models,
218
+ modelLabels: catalog.labels,
219
+ };
220
+ } catch (cause) {
221
+ const kind = cause instanceof AntigravityCatalogError ? cause.kind : "generic";
222
+ const detail = cause instanceof Error ? cause.message : String(cause);
223
+ return { ok: false, latencyMs: result.durationMs, failureKind: kind, error: boundedExternalDiagnostic(detail) };
224
+ }
225
+ }
226
+
59
227
  const antigravityCodeRuntime: RuntimeDescriptor = {
60
228
  id: "antigravity-code",
61
- displayName: "Antigravity CLI",
229
+ displayName: "Antigravity CLI — experimental local delegation",
62
230
  kind: "subprocess",
63
231
  tier: "subscription",
64
- // Reuses the existing `google-generative-ai` api family: the worker branches
65
- // to its own runner before any pi-ai inference, so this only needs to be a
66
- // valid, google-backed family. The runner is selected by runtime id, and the
67
- // plain-text parser is named by `outputParser`.
68
- apiFamily: "google-generative-ai",
232
+ apiFamily: "external-agent-subprocess",
69
233
  auth: "none",
70
234
  authNotice: ANTIGRAVITY_AUTH_NOTICE,
71
235
  knownModels: [...ANTIGRAVITY_MODELS],
72
236
  binaryName: "agy",
73
- headlessCommand: "agy --print",
74
- outputParser: "antigravity-print-text",
237
+ headlessCommand: "agy --input-format stream-json --output-format stream-json",
238
+ outputParser: "antigravity-stream-json",
75
239
  defaultCapabilities: antigravityCapabilities,
240
+ externalAgentLoop: {
241
+ tools: "externally-governed-unobserved",
242
+ network: "externally-governed-unobserved",
243
+ budget: "external-one-shot",
244
+ generatingRetry: "forbidden",
245
+ modelCatalog: "live-authoritative",
246
+ },
247
+ async probe(_target: TargetDescriptor, ctx: ProbeContext) {
248
+ try {
249
+ return await probeAntigravityModelCatalog(ctx);
250
+ } catch (cause) {
251
+ const detail = boundedExternalDiagnostic(cause instanceof Error ? cause.message : String(cause));
252
+ const kind = classifyAntigravityDiagnostic(detail);
253
+ return { ok: false, failureKind: kind, error: antigravityProbeMessage(kind, detail) };
254
+ }
255
+ },
76
256
  synthesizeModel(target: TargetDescriptor, wireModelId: string, kb: KnowledgeBaseHit | null): Model<Api> {
77
257
  return synthesizeCatalogBackedModel({
78
258
  target,
@@ -80,9 +260,9 @@ const antigravityCodeRuntime: RuntimeDescriptor = {
80
260
  kb,
81
261
  defaultCapabilities: antigravityCapabilities,
82
262
  runtimeId: "antigravity-code",
83
- api: "google-generative-ai",
84
- provider: "google",
85
- defaultBaseUrl: "antigravity://local",
263
+ api: "external-agent-subprocess",
264
+ provider: "antigravity",
265
+ defaultBaseUrl: "external-agent://antigravity/local",
86
266
  });
87
267
  },
88
268
  };
@@ -300,9 +300,13 @@ export function lmStudioReasoningLevels(options: ReadonlyArray<string> | undefin
300
300
  export function lmStudioReasoningEffort(
301
301
  level: ThinkingLevel,
302
302
  options?: ReadonlyArray<string>,
303
- ): "none" | "low" | "medium" | "high" {
303
+ ): "none" | "low" | "medium" | "high" | undefined {
304
+ // `allowed_options` describes the model's binary setting, but LM Studio's
305
+ // OpenAI-compatible request schema still accepts only effort-level values.
306
+ // Omit an active override for binary/default-on models; sending `low` works
307
+ // only through a warning and sending the advertised literal `on` is a 400.
308
+ if (options?.includes("on") && options.includes("off")) return level === "off" ? "none" : undefined;
304
309
  if (level === "off") return "none";
305
- if (options?.includes("on") && !options.includes("medium") && !options.includes("high")) return "low";
306
310
  if (level === "medium" && options?.includes("medium") !== false) return "medium";
307
311
  if ((level === "high" || level === "xhigh" || level === "max") && options?.includes("high") !== false) return "high";
308
312
  return "low";
@@ -24,6 +24,7 @@ export interface ClioLocalModelMetadata {
24
24
  quirks?: LocalModelQuirks;
25
25
  chatTemplateKwargsUnsupported?: boolean;
26
26
  lmstudio?: TargetDescriptor["lmstudio"];
27
+ litellm?: TargetDescriptor["litellm"];
27
28
  };
28
29
  }
29
30
 
@@ -109,6 +110,7 @@ export function synthLocalModel(input: LocalSynthesisInput): Model<Api> {
109
110
  ...(kb?.entry.family ? { family: kb.entry.family } : {}),
110
111
  ...(quirks ? { quirks } : {}),
111
112
  ...(target.lmstudio ? { lmstudio: target.lmstudio } : {}),
113
+ ...(target.litellm ? { litellm: target.litellm } : {}),
112
114
  },
113
115
  };
114
116
  if (headers) model.headers = headers;
@@ -4,11 +4,11 @@
4
4
  * This is a separate runtime from `openai-compat` rather than a target flag on
5
5
  * it, because a gateway answers two questions the generic protocol cannot.
6
6
  *
7
- * First, capability discovery. A gateway publishes semantic aliases: the client
8
- * asks for `code` and the proxy decides that means `katcoder2.5-35b-moe` on one
9
- * particular host. LiteLLM's `/v1/models` is the plain OpenAI listing, so every
10
- * row flattens to a bare id and the context window, tool support, and modality
11
- * of the model actually behind the alias are all erased. Reached over
7
+ * First, capability discovery. A gateway may publish aliases or truthful
8
+ * physical names such as `dynamo/qwen3.8-27b`. LiteLLM's `/v1/models` is the
9
+ * plain OpenAI listing, so every row flattens to a bare id and the context
10
+ * window, tool support, and modality of the model behind it are all erased.
11
+ * Reached over
12
12
  * `openai-compat`, a 262k tool-calling coder is indistinguishable from a 4k
13
13
  * chat toy, and Clio plans the session against its own assumed floor. LiteLLM
14
14
  * publishes the real numbers on `/v1/model/info` instead, which is what this
@@ -26,7 +26,8 @@
26
26
  * and rejected HTTP 400 by LM Studio, and the same prompt with no
27
27
  * `response_format` returned plain prose. `drop_params: true` did not strip
28
28
  * either spelling. The standard `json_schema` dialect is therefore the one that
29
- * holds across every upstream, and it is what this runtime declares.
29
+ * holds across every measured upstream, and a gateway must publish that fact
30
+ * explicitly per route in `model_info` before Clio relies on it.
30
31
  */
31
32
 
32
33
  import { CLIO_MIN_CONTEXT_WINDOW, CLIO_MIN_MAX_OUTPUT_TOKENS } from "../../../../core/context-floor.js";
@@ -55,7 +56,7 @@ const defaultCapabilities: CapabilityFlags = {
55
56
  embeddings: false,
56
57
  rerank: false,
57
58
  fim: false,
58
- structuredOutputs: "json-schema",
59
+ structuredOutputs: "none",
59
60
  contextWindow: CLIO_MIN_CONTEXT_WINDOW,
60
61
  maxTokens: CLIO_MIN_MAX_OUTPUT_TOKENS,
61
62
  };
@@ -106,8 +107,11 @@ function boolean(value: unknown): boolean | undefined {
106
107
  * so the caller can tell "the gateway says this model has no vision" apart from
107
108
  * "the gateway did not say", and merge accordingly.
108
109
  */
109
- function capabilitiesFromLiteLLMModelInfo(info: Record<string, unknown>): Partial<CapabilityFlags> {
110
+ export function capabilitiesFromLiteLLMModelInfo(info: Record<string, unknown>): Partial<CapabilityFlags> {
110
111
  const caps: Partial<CapabilityFlags> = {};
112
+ // This is a deployment declaration, never inferred from a URL, port or alias.
113
+ if (info.runtime === "lm-studio") caps.thinkingControlRuntime = "lmstudio";
114
+ else if (info.runtime === "llama.cpp") caps.thinkingControlRuntime = "llamacpp";
111
115
  const contextWindow = positiveInteger(info.max_input_tokens) ?? positiveInteger(info.max_tokens);
112
116
  if (contextWindow !== undefined) caps.contextWindow = contextWindow;
113
117
  const maxTokens = positiveInteger(info.max_output_tokens);
@@ -132,20 +136,77 @@ function capabilitiesFromLiteLLMModelInfo(info: Record<string, unknown>): Partia
132
136
  caps.rerank = true;
133
137
  }
134
138
  if (info.mode === "chat") caps.chat = true;
135
- // Measured, not inferred: see the module header.
136
- if (boolean(info.supports_response_schema) === true) caps.structuredOutputs = "json-schema";
139
+ // Measured, not inferred: see the module header. An explicit false must
140
+ // override this runtime's measured default for an upstream that cannot
141
+ // preserve the schema contract.
142
+ const responseSchema = boolean(info.supports_response_schema);
143
+ if (responseSchema !== undefined) caps.structuredOutputs = responseSchema ? "json-schema" : "none";
137
144
  return caps;
138
145
  }
139
146
 
147
+ const BOOLEAN_CAPABILITIES = [
148
+ "chat",
149
+ "tools",
150
+ "reasoning",
151
+ "vision",
152
+ "audio",
153
+ "embeddings",
154
+ "rerank",
155
+ "fim",
156
+ ] as const satisfies ReadonlyArray<keyof CapabilityFlags>;
157
+
158
+ /**
159
+ * One alias may load-balance across heterogeneous deployments. Clio can only
160
+ * promise what every possible route supports: numerical limits take the
161
+ * smallest fully-published value, booleans require unanimous publication, and
162
+ * one explicit false defeats a true. Missing data never becomes a capability.
163
+ */
164
+ export function aggregateLiteLLMCapabilities(rows: ReadonlyArray<Partial<CapabilityFlags>>): Partial<CapabilityFlags> {
165
+ if (rows.length === 0) return {};
166
+ const aggregate: Partial<CapabilityFlags> = {};
167
+ for (const key of ["contextWindow", "maxTokens"] as const) {
168
+ const values = rows.map((row) => row[key]);
169
+ if (values.every((value): value is number => typeof value === "number")) {
170
+ aggregate[key] = Math.min(...values);
171
+ }
172
+ }
173
+ for (const key of BOOLEAN_CAPABILITIES) {
174
+ const values = rows.map((row) => row[key]);
175
+ if (values.some((value) => value === false)) aggregate[key] = false;
176
+ else if (values.every((value) => value === true)) aggregate[key] = true;
177
+ }
178
+ const controlRuntime = rows[0]?.thinkingControlRuntime;
179
+ if (controlRuntime !== undefined && rows.every((row) => row.thinkingControlRuntime === controlRuntime)) {
180
+ aggregate.thinkingControlRuntime = controlRuntime;
181
+ }
182
+ const structuredOutputs = rows.map((row) => row.structuredOutputs);
183
+ if (structuredOutputs.some((value) => value === "none")) aggregate.structuredOutputs = "none";
184
+ else if (structuredOutputs.every((value) => value === "json-schema")) aggregate.structuredOutputs = "json-schema";
185
+ return aggregate;
186
+ }
187
+
140
188
  interface LiteLLMCatalog {
141
189
  models: string[];
142
190
  modelCapabilities: Record<string, Partial<CapabilityFlags>>;
143
191
  modelStates: Record<string, ProbeModelStatus>;
144
192
  /** Physical deployment behind each alias, for display. */
145
- deployments: Record<string, { model: string; apiBase?: string }>;
193
+ deployments: Record<string, Array<{ model: string; apiBase?: string }>>;
194
+ /** Whether the empty catalog is specifically because the gateway rejected the key. */
195
+ authFailed: boolean;
146
196
  }
147
197
 
148
- const EMPTY_CATALOG: LiteLLMCatalog = { models: [], modelCapabilities: {}, modelStates: {}, deployments: {} };
198
+ const EMPTY_CATALOG: LiteLLMCatalog = {
199
+ models: [],
200
+ modelCapabilities: {},
201
+ modelStates: {},
202
+ deployments: {},
203
+ authFailed: false,
204
+ };
205
+
206
+ /** Whether a failed probe leg failed specifically on the credential, not on reachability. */
207
+ function isAuthError(leg: { ok: boolean; error?: string }): boolean {
208
+ return !leg.ok && /^HTTP 40[13]\b/u.test(leg.error ?? "");
209
+ }
149
210
 
150
211
  /**
151
212
  * Read the catalog from `/v1/model/info`, falling back to `/v1/models`.
@@ -163,37 +224,50 @@ async function fetchCatalog(base: string, ctx: ProbeContext, headers: Record<str
163
224
  : probeJson<LiteLLMModelInfoResponse>(detailOpts));
164
225
 
165
226
  if (detail.ok && Array.isArray(detail.data?.data)) {
166
- const catalog: LiteLLMCatalog = { models: [], modelCapabilities: {}, modelStates: {}, deployments: {} };
227
+ const catalog: LiteLLMCatalog = {
228
+ models: [],
229
+ modelCapabilities: {},
230
+ modelStates: {},
231
+ deployments: {},
232
+ authFailed: false,
233
+ };
234
+ const capabilityRows: Record<string, Array<Partial<CapabilityFlags>>> = {};
167
235
  for (const row of detail.data.data) {
168
236
  if (typeof row?.model_name !== "string" || row.model_name.length === 0) continue;
169
237
  const alias = row.model_name;
170
- // One alias can carry several deployments. They share a name and Clio
171
- // addresses them as one model, so the first row establishes the entry
172
- // and later rows for the same alias are the load-balancing pool behind
173
- // it rather than separate models.
174
- if (catalog.deployments[alias] !== undefined) continue;
175
- catalog.models.push(alias);
238
+ if (catalog.deployments[alias] === undefined) {
239
+ catalog.models.push(alias);
240
+ catalog.deployments[alias] = [];
241
+ capabilityRows[alias] = [];
242
+ }
176
243
  const caps = row.model_info ? capabilitiesFromLiteLLMModelInfo(row.model_info) : {};
177
- if (Object.keys(caps).length > 0) catalog.modelCapabilities[alias] = caps;
244
+ capabilityRows[alias]?.push(caps);
178
245
  const upstream = row.litellm_params?.model;
179
246
  const apiBase = row.litellm_params?.api_base;
180
- catalog.deployments[alias] = {
247
+ catalog.deployments[alias]?.push({
181
248
  model: typeof upstream === "string" ? upstream : alias,
182
249
  ...(typeof apiBase === "string" ? { apiBase } : {}),
183
- };
250
+ });
251
+ }
252
+ for (const alias of catalog.models) {
253
+ const caps = aggregateLiteLLMCapabilities(capabilityRows[alias] ?? []);
254
+ if (Object.keys(caps).length > 0) catalog.modelCapabilities[alias] = caps;
184
255
  }
185
256
  if (catalog.models.length > 0) return catalog;
186
257
  }
258
+ const detailAuthFailed = isAuthError(detail);
187
259
 
188
260
  const listOpts = { url: `${base}/v1/models`, timeoutMs: ctx.httpTimeoutMs, headers } as const;
189
261
  const list = await (ctx.signal
190
262
  ? probeJson<LiteLLMModelsResponse>({ ...listOpts, signal: ctx.signal })
191
263
  : probeJson<LiteLLMModelsResponse>(listOpts));
192
- if (!list.ok || !Array.isArray(list.data?.data)) return EMPTY_CATALOG;
264
+ if (!list.ok || !Array.isArray(list.data?.data)) {
265
+ return { ...EMPTY_CATALOG, authFailed: detailAuthFailed || isAuthError(list) };
266
+ }
193
267
  const models = list.data.data
194
268
  .map((row) => (typeof row?.id === "string" ? row.id : null))
195
269
  .filter((id): id is string => id !== null && id.length > 0);
196
- return { models, modelCapabilities: {}, modelStates: {}, deployments: {} };
270
+ return { models, modelCapabilities: {}, modelStates: {}, deployments: {}, authFailed: false };
197
271
  }
198
272
 
199
273
  const litellmRuntime: RuntimeDescriptor = {
@@ -226,8 +300,11 @@ const litellmRuntime: RuntimeDescriptor = {
226
300
  if (catalog.models.length === 0) {
227
301
  const result: ProbeResult = {
228
302
  ok: false,
229
- error: "gateway is live but served no model catalog; check the target's API key",
303
+ error: catalog.authFailed
304
+ ? "gateway rejected the key"
305
+ : "gateway is live but served no model catalog; check the target's API key",
230
306
  };
307
+ if (catalog.authFailed) result.authFailed = true;
231
308
  if (live.latencyMs !== undefined) result.latencyMs = live.latencyMs;
232
309
  return result;
233
310
  }
@@ -248,10 +325,23 @@ const litellmRuntime: RuntimeDescriptor = {
248
325
  if (configured && !catalog.models.includes(configured)) {
249
326
  notes.push(`configured model '${configured}' is not in the gateway catalog`);
250
327
  }
251
- // Naming the physical model behind each alias is the difference between a
252
- // catalog an operator can audit and a list of words.
253
- const routed = Object.entries(catalog.deployments)
254
- .map(([alias, deployment]) => `${alias}=${deployment.model}`)
328
+ // A node/model catalog already names placement. Summarize those one-to-one
329
+ // routes instead of flooding target diagnostics with dozens of tautologies;
330
+ // retain the explicit mapping for genuine aliases and multi-deployments.
331
+ const routeEntries = Object.entries(catalog.deployments);
332
+ const physical = routeEntries.filter(([alias, deployments]) => {
333
+ const slash = alias.indexOf("/");
334
+ if (slash <= 0 || deployments.length !== 1) return false;
335
+ const upstream = deployments[0]?.model.replace(/^openai\//u, "");
336
+ return upstream === alias.slice(slash + 1);
337
+ });
338
+ if (physical.length > 0) notes.push(`physical routes: ${physical.length} node/model names map one-to-one`);
339
+ const routed = routeEntries
340
+ .filter(([alias]) => !physical.some(([physicalAlias]) => physicalAlias === alias))
341
+ .map(([alias, deployments]) => {
342
+ const models = [...new Set(deployments.map((deployment) => deployment.model))];
343
+ return `${alias}=${models.join("|")}`;
344
+ })
255
345
  .sort();
256
346
  if (routed.length > 0) notes.push(`routes: ${routed.join(", ")}`);
257
347
  if (notes.length > 0) result.notes = notes;
@@ -5,7 +5,7 @@ import { getRuntimeRegistry } from "./registry.js";
5
5
  import type { RuntimeDescriptor } from "./types/runtime-descriptor.js";
6
6
  import type { TargetDescriptor } from "./types/target-descriptor.js";
7
7
 
8
- export type ProviderSupportGroup = "featured" | "cloud-api" | "subscription" | "local-http";
8
+ export type ProviderSupportGroup = "featured" | "external-worker" | "cloud-api" | "subscription" | "local-http";
9
9
 
10
10
  /**
11
11
  * Where a runtime's model ids come from, which decides whether their order
@@ -44,6 +44,7 @@ const SUMMARY_BY_RUNTIME_ID: Readonly<Record<string, string>> = {
44
44
  alcf: "ALCF inference gateway (Sophia/Metis) via Globus",
45
45
  anthropic: "Anthropic API",
46
46
  "anthropic-max": "Claude Pro/Max subscription via Anthropic OAuth",
47
+ "antigravity-code": "Experimental local delegation via the installed Antigravity CLI",
47
48
  bedrock: "Amazon Bedrock",
48
49
  "claude-code": "Claude Code subscription via installed claude CLI",
49
50
  "claude-sdk": "Claude Code subscription via Claude Agent SDK",
@@ -59,19 +60,21 @@ const SUMMARY_BY_RUNTIME_ID: Readonly<Record<string, string>> = {
59
60
  llamacpp: "llama.cpp server (auto-detect surface)",
60
61
  "anthropic-compat": "Generic Anthropic-compatible REST",
61
62
  "openai-compat": "Generic OpenAI-compatible REST",
62
- litellm: "LiteLLM gateway with per-alias capability discovery and routed-model attribution",
63
+ litellm: "LiteLLM gateway with per-route capability discovery and physical-model attribution",
63
64
  };
64
65
 
65
66
  function groupPriority(group: ProviderSupportGroup): number {
66
67
  switch (group) {
67
68
  case "featured":
68
69
  return 0;
69
- case "subscription":
70
+ case "external-worker":
70
71
  return 1;
71
- case "cloud-api":
72
+ case "subscription":
72
73
  return 2;
73
- case "local-http":
74
+ case "cloud-api":
74
75
  return 3;
76
+ case "local-http":
77
+ return 4;
75
78
  }
76
79
  }
77
80
 
@@ -79,6 +82,8 @@ export function supportGroupLabel(group: ProviderSupportGroup): string {
79
82
  switch (group) {
80
83
  case "featured":
81
84
  return "Featured";
85
+ case "external-worker":
86
+ return "External delegation";
82
87
  case "subscription":
83
88
  return "Subscriptions";
84
89
  case "cloud-api":
@@ -90,6 +95,7 @@ export function supportGroupLabel(group: ProviderSupportGroup): string {
90
95
 
91
96
  function classifyGroup(runtime: RuntimeDescriptor): ProviderSupportGroup {
92
97
  if (runtime.id === "openai-codex") return "featured";
98
+ if (runtime.externalAgentLoop !== undefined) return "external-worker";
93
99
  if (runtime.id === "alcf") return "cloud-api";
94
100
  if (runtime.auth === "oauth" || runtime.auth === "claude-cli") return "subscription";
95
101
  if (catalogProviderForRuntime(runtime.id) || (runtime.auth === "api-key" && !runtime.probe)) {