@iowarp/clio-coder 0.3.8 → 0.4.0

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 (552) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/NOTICE +33 -0
  3. package/README.md +12 -3
  4. package/dist/{acp-U67UHUK2.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-YU6SGALZ.js → agents-FMV2Q5G4.js} +41 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5ZPJOIVG.js → auth-3IDSJEIK.js} +18 -18
  8. package/dist/{builtins-C6JMZVV6.js → builtins-XCZWXSC7.js} +5 -5
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  11. package/dist/{chunk-XWSF374K.js → chunk-2OQE55CK.js} +3 -3
  12. package/dist/{chunk-5H3GB5BO.js → chunk-32KWKNSF.js} +8 -384
  13. package/dist/{chunk-KTYTFRMB.js → chunk-36EJLSQQ.js} +34 -36
  14. package/dist/{chunk-FHJEP5SW.js → chunk-3BT2XMV4.js} +19 -13
  15. package/dist/chunk-3DPEIQKN.js +113 -0
  16. package/dist/{chunk-GU2UIAFZ.js → chunk-3URVFKWK.js} +7 -7
  17. package/dist/{chunk-WEH5XRJQ.js → chunk-3XML7CDN.js} +3 -3
  18. package/dist/chunk-42FMPA75.js +101 -0
  19. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  20. package/dist/{chunk-VCBR6CU7.js → chunk-5PSMVOLM.js} +2 -2
  21. package/dist/{chunk-NMPKI6XL.js → chunk-6DB53AJS.js} +203 -25
  22. package/dist/{chunk-3BINW3FP.js → chunk-76ONBSIA.js} +2 -2
  23. package/dist/{chunk-26LEYJZH.js → chunk-7MCTRUCE.js} +2 -2
  24. package/dist/{chunk-TYPGUK6W.js → chunk-AB44T6BB.js} +111 -5
  25. package/dist/{chunk-K4XHGFR5.js → chunk-B7OBL7PK.js} +317 -721
  26. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  27. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  28. package/dist/{chunk-ZVJ5BLO2.js → chunk-BKFJHQCA.js} +154 -16
  29. package/dist/chunk-BKFM6EJV.js +462 -0
  30. package/dist/chunk-BMS5RKQY.js +27 -0
  31. package/dist/{chunk-ZNLWCMVZ.js → chunk-BPKCPIL7.js} +2 -2
  32. package/dist/{chunk-TLQJPP24.js → chunk-BUMFYQFY.js} +1394 -1349
  33. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  34. package/dist/chunk-C2LTL2W6.js +2447 -0
  35. package/dist/{chunk-ME6CCNFO.js → chunk-CODPRO7Q.js} +8 -8
  36. package/dist/{chunk-E77JEWSD.js → chunk-CTJ4RNAA.js} +7 -37
  37. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  38. package/dist/{chunk-7RFXX52T.js → chunk-DQITNCXG.js} +642 -172
  39. package/dist/{chunk-TTHACPOM.js → chunk-DYJP44XW.js} +578 -117
  40. package/dist/{chunk-2HFZQUHL.js → chunk-F4CKPOEQ.js} +18 -8
  41. package/dist/{chunk-DGSYXYMX.js → chunk-FEFIFZTL.js} +3 -3
  42. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  43. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  44. package/dist/chunk-GKF55TAZ.js +391 -0
  45. package/dist/{chunk-VWZOAB7K.js → chunk-GR5G2PVF.js} +9 -8
  46. package/dist/{chunk-4DGYLA73.js → chunk-GXNLGKAB.js} +80 -9
  47. package/dist/chunk-HHV2GANA.js +88 -0
  48. package/dist/{chunk-IIZWH4XA.js → chunk-HI63TFOG.js} +5 -4
  49. package/dist/{chunk-WLFILSD5.js → chunk-HJJTYUHX.js} +113 -83
  50. package/dist/{chunk-TT36MB5S.js → chunk-HLW2MRKE.js} +3 -1
  51. package/dist/{chunk-PMDBGQSJ.js → chunk-HWHKMHUA.js} +7 -7
  52. package/dist/chunk-HZHHCK24.js +1631 -0
  53. package/dist/{chunk-WSB3FPX7.js → chunk-I5VEOC6I.js} +39 -143
  54. package/dist/chunk-IBEBSCYA.js +564 -0
  55. package/dist/chunk-IQ7KR472.js +362 -0
  56. package/dist/{chunk-A3WNZD3P.js → chunk-J4W7KFM7.js} +949 -972
  57. package/dist/{chunk-TB5666IT.js → chunk-JDG2WCRO.js} +5 -5
  58. package/dist/{chunk-N22QMJKY.js → chunk-K5C3NCBD.js} +4 -4
  59. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  60. package/dist/{chunk-5C3AQNDW.js → chunk-KFZI4NIL.js} +216 -38
  61. package/dist/chunk-KMVISBZR.js +132 -0
  62. package/dist/{chunk-WXY7KU3G.js → chunk-LDQ2ZF2M.js} +2 -2
  63. package/dist/{chunk-XN3L4EYL.js → chunk-LQ3DZAMX.js} +3 -3
  64. package/dist/{chunk-U6MBIEMB.js → chunk-LY4S7GJC.js} +173 -144
  65. package/dist/{chunk-4SPRNWDE.js → chunk-MLKNTWH2.js} +19 -19
  66. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  67. package/dist/chunk-NEKRRTYW.js +56 -0
  68. package/dist/{chunk-SPULKLCF.js → chunk-NHCZP4K7.js} +3 -3
  69. package/dist/chunk-NHLBIGRH.js +1506 -0
  70. package/dist/chunk-NQQH3YT7.js +302 -0
  71. package/dist/chunk-NYS75XW5.js +15 -0
  72. package/dist/{chunk-GN57SG4G.js → chunk-O4XIVISU.js} +10 -8
  73. package/dist/{chunk-EMYUUSFG.js → chunk-O6TL7WWY.js} +6 -6
  74. package/dist/chunk-OQBA45DZ.js +97 -0
  75. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  76. package/dist/chunk-PMZCIOCJ.js +25 -0
  77. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  78. package/dist/{chunk-J3YUBZWY.js → chunk-QBJA7R7N.js} +62 -6
  79. package/dist/chunk-QDC3K2U3.js +262 -0
  80. package/dist/{chunk-2HEJ2F35.js → chunk-QLFS5GO2.js} +22 -10
  81. package/dist/{chunk-7RGZWPB6.js → chunk-QLL7ILRG.js} +95 -32
  82. package/dist/{chunk-YS5VLNH5.js → chunk-QREDIESB.js} +6 -6
  83. package/dist/chunk-QSNYB6ZV.js +195 -0
  84. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  85. package/dist/chunk-RKKLTLYB.js +45 -0
  86. package/dist/{chunk-P43ETTHK.js → chunk-SJ5ZKQ4S.js} +2 -2
  87. package/dist/{chunk-GPIEI3LY.js → chunk-SP2RXXYO.js} +6 -54
  88. package/dist/chunk-SUCTJL45.js +45 -0
  89. package/dist/{chunk-DYIM5TJT.js → chunk-SUW5DORT.js} +263 -7
  90. package/dist/chunk-T56WDKA5.js +183 -0
  91. package/dist/chunk-TVHHYFHE.js +255 -0
  92. package/dist/{chunk-FJ3H4MN5.js → chunk-TZ3SGWZZ.js} +3 -3
  93. package/dist/{chunk-MXKJU4JB.js → chunk-U77AMWDL.js} +91 -10
  94. package/dist/{chunk-5DHKRSMQ.js → chunk-ULC6OTWO.js} +11 -7
  95. package/dist/{chunk-RWSI4YD7.js → chunk-UM7N4G5A.js} +33 -12
  96. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  97. package/dist/{chunk-IGWKHNIQ.js → chunk-UXMFQ54G.js} +44 -37
  98. package/dist/{chunk-HFSBBKSQ.js → chunk-V5DHCITQ.js} +171 -3
  99. package/dist/{chunk-5WIGXA4T.js → chunk-VAZSBTKF.js} +111 -4
  100. package/dist/{chunk-VHN4MY6O.js → chunk-VEO4AP2K.js} +2 -2
  101. package/dist/{chunk-IJ7RPIYJ.js → chunk-VFA6GDY5.js} +65 -4
  102. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  103. package/dist/chunk-VO67MWHC.js +75 -0
  104. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  105. package/dist/{chunk-TANS5ZJS.js → chunk-VYMXRQI6.js} +36 -22
  106. package/dist/{chunk-WWCZ5F23.js → chunk-W5VSYASO.js} +77 -16
  107. package/dist/{chunk-WNIJTQQK.js → chunk-WZR7K7ZX.js} +72 -116
  108. package/dist/{chunk-5Q2VVUKB.js → chunk-X3YGUTOB.js} +4 -4
  109. package/dist/chunk-X75E3D2N.js +686 -0
  110. package/dist/{chunk-VAWNZU7Z.js → chunk-YDFRH54B.js} +4 -4
  111. package/dist/chunk-YJX4SHTD.js +40 -0
  112. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  113. package/dist/chunk-Z2RR6MAK.js +127 -0
  114. package/dist/{chunk-FBVTI2TJ.js → chunk-Z4TXYIEG.js} +12 -131
  115. package/dist/cli/index.js +47 -36
  116. package/dist/{clio-QVTYJ57A.js → clio-2JXHBBY5.js} +7 -7
  117. package/dist/{code-nav-FGGFIE7L.js → code-nav-3YYRMYNF.js} +8 -8
  118. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  119. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  120. package/dist/{config-LW5IJFQN.js → config-QZPCMYSO.js} +99 -63
  121. package/dist/{configure-7XIZCOU4.js → configure-TEGEBYCA.js} +23 -22
  122. package/dist/{context-Y6Y7QPR6.js → context-AV7OEZ4D.js} +12 -12
  123. package/dist/{context-L3WL3X7K.js → context-E6H5RNMC.js} +56 -47
  124. package/dist/{context-N52ZA626.js → context-GSXUE4CT.js} +29 -27
  125. package/dist/{context-clear-MBQRLSDQ.js → context-clear-SHIBYK6T.js} +55 -46
  126. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  127. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-5ZGKPGZQ.js} +13 -13
  128. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-EFMJT4LD.js} +93 -64
  129. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  130. package/dist/doctor-QOA5FNY5.js +313 -0
  131. package/dist/{eval-BEC2WHDA.js → eval-TFBYQH4H.js} +2032 -156
  132. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  133. package/dist/{evidence-REJUMSKM.js → evidence-ERGESKGN.js} +203 -48
  134. package/dist/{evolve-PY5ZBA5K.js → evolve-VDXTSYCJ.js} +52 -43
  135. package/dist/{extensions-HVKU65YU.js → extensions-7BGBHN57.js} +13 -7
  136. package/dist/{fleet-7WZEWRFA.js → fleet-2RRVDF2V.js} +228 -108
  137. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-VJ726XIA.js} +11 -11
  138. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  139. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-JF5QOATM.js} +18 -15
  140. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  141. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-AIZUEJOY.js} +5 -5
  142. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-AJRPDMDV.js} +22 -19
  143. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  144. package/dist/fleet-view-ZCON35AG.js +102 -0
  145. package/dist/{init-OG3TPGQG.js → init-DN2WWLFE.js} +72 -62
  146. package/dist/install-XGLBQY5E.js +13 -0
  147. package/dist/interop-OZBKXAYL.js +114 -0
  148. package/dist/{library-CNTMPLRF.js → library-YWZG7IMW.js} +21 -18
  149. package/dist/{memory-6IS7F275.js → memory-I4C4HMLW.js} +54 -45
  150. package/dist/{models-ENRJDA5W.js → models-CEYXJBO6.js} +33 -30
  151. package/dist/{monitor-XLDVO7TN.js → monitor-NZ6GCI3P.js} +59 -52
  152. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-GCGQ4N5I.js} +7827 -7328
  153. package/dist/panes-HMABYVO4.js +58 -0
  154. package/dist/panes-KY6W3V2E.js +103 -0
  155. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  156. package/dist/{reset-RZ4ER727.js → reset-DQ6FGCSH.js} +13 -11
  157. package/dist/resources-BB3MVJMD.js +111 -0
  158. package/dist/{run-Y2CNK5RU.js → run-H2GQDUER.js} +119 -87
  159. package/dist/{share-A55GYP6Z.js → share-GTJN6A5O.js} +20 -17
  160. package/dist/{skills-ALC5J6AT.js → skills-L55TEW6R.js} +33 -24
  161. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-XVXPH2JI.js} +67 -56
  162. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  163. package/dist/slash-commands-ZSGASKJC.js +77 -0
  164. package/dist/{steer-GGWFUJUD.js → steer-RZGSCY4R.js} +4 -4
  165. package/dist/{support-MIETYA5E.js → support-PKEUNNQL.js} +6 -6
  166. package/dist/{targets-VGNXIR3S.js → targets-NCPZ644J.js} +68 -40
  167. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-44SV3YCN.js} +6 -4
  168. package/dist/tools-DAF3DI3C.js +27 -0
  169. package/dist/{trace-PNCASAXC.js → trace-FYVW2MQA.js} +207 -10
  170. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  171. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  172. package/dist/{upgrade-FUSUAGHR.js → upgrade-3XPP6OQL.js} +27 -24
  173. package/dist/{usage-N4MKVHKD.js → usage-3NLHGTU2.js} +114 -62
  174. package/dist/{verifiers-YAWOJ3H2.js → verifiers-SSQONKRT.js} +172 -13
  175. package/dist/{verify-LTDHYBGY.js → verify-3U6J7FZI.js} +10 -10
  176. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  177. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-CEHYGPGQ.js} +75 -65
  178. package/dist/with-panes-MKB46MPQ.js +782 -0
  179. package/dist/worker/entry.js +106 -75
  180. package/docs/README.md +3 -2
  181. package/docs/acp.md +24 -3
  182. package/docs/alcf-provider.md +1 -1
  183. package/docs/architecture.md +2 -2
  184. package/docs/artifact-versions.md +9 -1
  185. package/docs/built-in-agents.md +1 -1
  186. package/docs/capacity-and-scheduling.md +62 -3
  187. package/docs/commands-and-modes.md +31 -2
  188. package/docs/configuration-and-targets.md +69 -12
  189. package/docs/context-engine.md +63 -4
  190. package/docs/development-pipeline.md +19 -0
  191. package/docs/dispatch-typed-intent.md +385 -0
  192. package/docs/documentation-coverage.md +5 -5
  193. package/docs/documentation-guide.md +1 -1
  194. package/docs/environment-variables.md +3 -0
  195. package/docs/eval-runner.md +262 -11
  196. package/docs/evals-internal.md +72 -2
  197. package/docs/evidence-and-memory.md +12 -11
  198. package/docs/evolution.md +1 -1
  199. package/docs/exit-codes-and-output.md +1 -1
  200. package/docs/extensions-and-sharing.md +27 -1
  201. package/docs/fleet-dispatch.md +25 -4
  202. package/docs/installation-and-lifecycle.md +15 -2
  203. package/docs/middleware-and-components.md +1 -1
  204. package/docs/model-catalog.md +10 -1
  205. package/docs/observability.md +55 -4
  206. package/docs/proactive-memory.md +127 -14
  207. package/docs/prompt-envelope-and-tools.md +19 -1
  208. package/docs/provider-adapter-cookbook.md +1 -1
  209. package/docs/release-cut-checklist.md +19 -3
  210. package/docs/safety-model.md +2 -2
  211. package/docs/scientific-validation.md +3 -3
  212. package/docs/session-lifecycle.md +1 -1
  213. package/docs/skills-marketplace.md +1 -1
  214. package/docs/tool-usage.md +18 -8
  215. package/docs/trace-store.md +1 -1
  216. package/docs/troubleshooting.md +88 -1
  217. package/docs/tui-design.md +1 -1
  218. package/docs/worker-dispatch-mechanics.md +1 -1
  219. package/package.json +5 -2
  220. package/src/cli/acp.ts +6 -2
  221. package/src/cli/agents.ts +1 -1
  222. package/src/cli/argv.ts +25 -0
  223. package/src/cli/config-inspect.ts +33 -6
  224. package/src/cli/config.ts +1 -1
  225. package/src/cli/configure.ts +23 -21
  226. package/src/cli/doctor-panes.ts +124 -0
  227. package/src/cli/doctor-state-size.ts +82 -0
  228. package/src/cli/doctor-toolchain.ts +57 -0
  229. package/src/cli/doctor.ts +22 -1
  230. package/src/cli/eval-inventory.ts +436 -0
  231. package/src/cli/eval.ts +93 -16
  232. package/src/cli/evidence-detail.ts +88 -0
  233. package/src/cli/evidence-inventory.ts +183 -0
  234. package/src/cli/evidence.ts +30 -5
  235. package/src/cli/extensions.ts +5 -1
  236. package/src/cli/fleet-decisions.ts +69 -0
  237. package/src/cli/fleet-inspect.ts +334 -0
  238. package/src/cli/fleet-verify.ts +133 -0
  239. package/src/cli/fleet-view.ts +810 -0
  240. package/src/cli/fleet.ts +179 -39
  241. package/src/cli/index.ts +14 -2
  242. package/src/cli/interop-inspect.ts +128 -0
  243. package/src/cli/interop.ts +34 -0
  244. package/src/cli/panes.ts +35 -0
  245. package/src/cli/reset.ts +5 -2
  246. package/src/cli/run.ts +58 -0
  247. package/src/cli/skills-inventory.ts +185 -0
  248. package/src/cli/skills.ts +16 -13
  249. package/src/cli/targets.ts +44 -13
  250. package/src/cli/tools.ts +321 -0
  251. package/src/cli/trace-inspect.ts +252 -0
  252. package/src/cli/trace.ts +85 -5
  253. package/src/cli/usage.ts +63 -14
  254. package/src/cli/verifiers-inspect.ts +347 -0
  255. package/src/cli/verifiers.ts +9 -0
  256. package/src/core/bus-events.ts +33 -1
  257. package/src/core/cache-telemetry.ts +42 -0
  258. package/src/core/config.ts +67 -0
  259. package/src/core/defaults.ts +124 -8
  260. package/src/core/endpoint-key.ts +27 -0
  261. package/src/core/residency-target-key.ts +25 -0
  262. package/src/core/response-schema.ts +80 -6
  263. package/src/core/theme-token-hex.ts +43 -0
  264. package/src/core/tool-names.ts +2 -1
  265. package/src/core/xdg.ts +1 -1
  266. package/src/domains/agents/fleets/build-review.md +0 -3
  267. package/src/domains/agents/fleets/build-test.md +0 -3
  268. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  269. package/src/domains/agents/result-contract.ts +164 -35
  270. package/src/domains/config/classify.ts +6 -0
  271. package/src/domains/context/codewiki/coordinator.ts +12 -4
  272. package/src/domains/dispatch/admission-error.ts +9 -0
  273. package/src/domains/dispatch/admission.ts +52 -14
  274. package/src/domains/dispatch/capacity-lease.ts +118 -9
  275. package/src/domains/dispatch/contract.ts +11 -0
  276. package/src/domains/dispatch/council-topology.ts +398 -0
  277. package/src/domains/dispatch/execution-plan.ts +44 -4
  278. package/src/domains/dispatch/extension.ts +378 -82
  279. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  280. package/src/domains/dispatch/fleet-plan.ts +7 -2
  281. package/src/domains/dispatch/fleet-run.ts +87 -4
  282. package/src/domains/dispatch/gate-decisions.ts +11 -1
  283. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  284. package/src/domains/dispatch/gate-topology.ts +289 -0
  285. package/src/domains/dispatch/heartbeat.ts +32 -8
  286. package/src/domains/dispatch/index.ts +22 -0
  287. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  288. package/src/domains/dispatch/intent.ts +85 -1
  289. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  290. package/src/domains/dispatch/reservation-store.ts +139 -11
  291. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  292. package/src/domains/dispatch/run-event-journal.ts +598 -0
  293. package/src/domains/dispatch/state.ts +49 -1
  294. package/src/domains/dispatch/types.ts +13 -0
  295. package/src/domains/dispatch/validation.ts +33 -8
  296. package/src/domains/dispatch/worker-spawn.ts +25 -11
  297. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  298. package/src/domains/dispatch/write-boundary.ts +62 -1
  299. package/src/domains/eval/artifacts/store.ts +62 -0
  300. package/src/domains/eval/compare/behavioral.ts +224 -0
  301. package/src/domains/eval/compare/compare.ts +342 -2
  302. package/src/domains/eval/compare/envelope.ts +128 -0
  303. package/src/domains/eval/compare/gates.ts +24 -6
  304. package/src/domains/eval/compare/thresholds.ts +30 -3
  305. package/src/domains/eval/execution-provenance.ts +240 -0
  306. package/src/domains/eval/inventory.ts +113 -0
  307. package/src/domains/eval/metrics/aggregate.ts +136 -0
  308. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  309. package/src/domains/eval/metrics/tracked.ts +413 -0
  310. package/src/domains/eval/provenance.ts +117 -0
  311. package/src/domains/eval/reports/comparison.ts +128 -0
  312. package/src/domains/eval/reports/junit.ts +17 -3
  313. package/src/domains/eval/reports/markdown.ts +3 -3
  314. package/src/domains/eval/reports/text.ts +14 -0
  315. package/src/domains/eval/run-compare.ts +20 -0
  316. package/src/domains/eval/runners/clio-run.ts +127 -0
  317. package/src/domains/eval/runners/external-command.ts +28 -3
  318. package/src/domains/eval/schema/adapter.ts +111 -0
  319. package/src/domains/eval/schema/artifact.ts +20 -0
  320. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  321. package/src/domains/eval/schema/behavioral.ts +520 -0
  322. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  323. package/src/domains/eval/schema/serving.ts +105 -0
  324. package/src/domains/eval/schema/suite.ts +38 -8
  325. package/src/domains/eval/schema/validate.ts +58 -3
  326. package/src/domains/eval/schema/verdict.ts +237 -0
  327. package/src/domains/eval/suites/resolve.ts +2 -0
  328. package/src/domains/eval/suites/run.ts +264 -33
  329. package/src/domains/eval/verifiers/command.ts +2 -1
  330. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  331. package/src/domains/evidence/build.ts +2 -13
  332. package/src/domains/evidence/eval.ts +2 -12
  333. package/src/domains/evidence/findings-markdown.ts +33 -0
  334. package/src/domains/evidence/run-trust.ts +7 -113
  335. package/src/domains/evidence/store.ts +6 -0
  336. package/src/domains/evidence/trust-projection.ts +2 -2
  337. package/src/domains/extensions/compatibility.ts +285 -0
  338. package/src/domains/extensions/discovery.ts +38 -3
  339. package/src/domains/extensions/resources.ts +1 -1
  340. package/src/domains/extensions/state.ts +12 -3
  341. package/src/domains/extensions/types.ts +2 -0
  342. package/src/domains/lifecycle/doctor.ts +69 -1
  343. package/src/domains/memory/index.ts +14 -1
  344. package/src/domains/memory/task-bank-promotion.ts +64 -0
  345. package/src/domains/memory/task-memory-policy.ts +82 -17
  346. package/src/domains/memory/task-memory-spend.ts +131 -0
  347. package/src/domains/memory/task-memory-status.ts +7 -0
  348. package/src/domains/memory/task-memory-telemetry.ts +3 -0
  349. package/src/domains/middleware/index.ts +1 -0
  350. package/src/domains/middleware/memory-intervention.ts +97 -21
  351. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  352. package/src/domains/mux/contract.ts +434 -0
  353. package/src/domains/mux/detect.ts +158 -0
  354. package/src/domains/mux/extension.ts +47 -0
  355. package/src/domains/mux/index.ts +96 -0
  356. package/src/domains/mux/manifest.ts +6 -0
  357. package/src/domains/mux/operations.ts +164 -0
  358. package/src/domains/mux/pane-registry.ts +90 -0
  359. package/src/domains/mux/protocol.ts +49 -0
  360. package/src/domains/mux/socket-client.ts +816 -0
  361. package/src/domains/mux/types.ts +222 -0
  362. package/src/domains/mux/viewer-command.ts +59 -0
  363. package/src/domains/mux/yazi/assets/init.lua +2 -0
  364. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  365. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  366. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  367. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  368. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  369. package/src/domains/mux/yazi/event-stream.ts +180 -0
  370. package/src/domains/mux/yazi/profile.ts +299 -0
  371. package/src/domains/mux/yazi/session.ts +228 -0
  372. package/src/domains/mux/yazi/theme.ts +30 -0
  373. package/src/domains/observability/background-memory-usage.ts +140 -0
  374. package/src/domains/observability/cost.ts +22 -1
  375. package/src/domains/observability/index.ts +9 -0
  376. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  377. package/src/domains/observability/trace-store.ts +234 -2
  378. package/src/domains/prompts/compiler.ts +100 -13
  379. package/src/domains/providers/endpoint-capacity.ts +228 -0
  380. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  381. package/src/domains/providers/extension.ts +20 -3
  382. package/src/domains/providers/index.ts +32 -0
  383. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  384. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  385. package/src/domains/providers/runtime-resolution.ts +8 -1
  386. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  387. package/src/domains/providers/runtimes/builtins.ts +2 -0
  388. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  389. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  390. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  391. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  392. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  393. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  394. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  395. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  396. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  397. package/src/domains/providers/support.ts +1 -0
  398. package/src/domains/providers/target-model-cache.ts +124 -0
  399. package/src/domains/providers/types/capability-flags.ts +2 -0
  400. package/src/domains/providers/types/target-descriptor.ts +2 -0
  401. package/src/domains/resources/index.ts +3 -0
  402. package/src/domains/resources/prompts/loader.ts +95 -33
  403. package/src/domains/resources/skills/loader.ts +33 -0
  404. package/src/domains/safety/action-classifier.ts +6 -0
  405. package/src/domains/safety/call-target.ts +52 -0
  406. package/src/domains/safety/run-effects.ts +35 -4
  407. package/src/domains/session/context-accounting.ts +52 -1
  408. package/src/domains/session/context-ledger.ts +37 -13
  409. package/src/domains/session/index.ts +6 -0
  410. package/src/domains/session/prompt-cache.ts +140 -0
  411. package/src/domains/session/prompt-manifest.ts +42 -0
  412. package/src/domains/toolchain/archive.ts +175 -0
  413. package/src/domains/toolchain/contract.ts +28 -0
  414. package/src/domains/toolchain/extension.ts +47 -0
  415. package/src/domains/toolchain/index.ts +39 -0
  416. package/src/domains/toolchain/install.ts +327 -0
  417. package/src/domains/toolchain/manifest.ts +8 -0
  418. package/src/domains/toolchain/paths.ts +34 -0
  419. package/src/domains/toolchain/registry.ts +265 -0
  420. package/src/domains/toolchain/remove.ts +218 -0
  421. package/src/domains/toolchain/resolve.ts +182 -0
  422. package/src/domains/toolchain/types.ts +113 -0
  423. package/src/domains/toolchain/version.ts +88 -0
  424. package/src/engine/acp/adapter.ts +18 -3
  425. package/src/engine/acp/server.ts +413 -70
  426. package/src/engine/acp/types.ts +19 -1
  427. package/src/engine/ai.ts +35 -0
  428. package/src/engine/apis/llamacpp-residency.ts +55 -3
  429. package/src/engine/apis/lmstudio.ts +25 -5
  430. package/src/engine/apis/ollama-native.ts +2 -1
  431. package/src/engine/apis/openai-completions.ts +80 -17
  432. package/src/engine/apis/residency-lock.ts +3 -1
  433. package/src/engine/apis/residency.ts +34 -1
  434. package/src/engine/claude/sdk-module.ts +98 -0
  435. package/src/engine/claude/sdk-runtime.ts +19 -11
  436. package/src/engine/provider-payload.ts +29 -1
  437. package/src/engine/tui-primitives.ts +21 -0
  438. package/src/engine/tui.ts +1 -0
  439. package/src/engine/worker-runtime.ts +2 -13
  440. package/src/entry/boot-options.ts +2 -0
  441. package/src/entry/orchestrator.ts +279 -35
  442. package/src/entry/panes-activation.ts +31 -0
  443. package/src/entry/with-panes.ts +20 -0
  444. package/src/interactive/chat-loop-messages.ts +26 -7
  445. package/src/interactive/chat-loop.ts +318 -41
  446. package/src/interactive/chat-panel.ts +62 -8
  447. package/src/interactive/clio-editor.ts +45 -8
  448. package/src/interactive/context-activity.ts +5 -1
  449. package/src/interactive/context-meter.ts +1 -1
  450. package/src/interactive/context-overlay.ts +41 -10
  451. package/src/interactive/cost-overlay.ts +66 -6
  452. package/src/interactive/council-grid.ts +1 -3
  453. package/src/interactive/council.ts +11 -0
  454. package/src/interactive/dispatch-board.ts +102 -20
  455. package/src/interactive/fleet-run-preview.ts +41 -15
  456. package/src/interactive/handoff-round.ts +41 -2
  457. package/src/interactive/interactive-application.ts +177 -7
  458. package/src/interactive/interactive-input-runtime.ts +15 -0
  459. package/src/interactive/interactive-presentation.ts +4 -0
  460. package/src/interactive/interactive-shell.ts +20 -17
  461. package/src/interactive/interactive-slash-runtime.ts +63 -10
  462. package/src/interactive/memory-overlay.ts +9 -0
  463. package/src/interactive/modal-marker.ts +170 -0
  464. package/src/interactive/mutation-preview.ts +295 -0
  465. package/src/interactive/mux-bridge.ts +214 -0
  466. package/src/interactive/overlay-frame.ts +58 -2
  467. package/src/interactive/overlay-general-openers.ts +17 -0
  468. package/src/interactive/overlay-key-routing.ts +52 -3
  469. package/src/interactive/overlay-lifecycle.ts +56 -7
  470. package/src/interactive/overlay-model-selectors.ts +40 -3
  471. package/src/interactive/overlay-permission-lifecycle.ts +112 -24
  472. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  473. package/src/interactive/overlay-transitions.ts +18 -4
  474. package/src/interactive/overlays/agents.ts +1 -0
  475. package/src/interactive/overlays/ask-user.ts +227 -49
  476. package/src/interactive/overlays/auth-dialog.ts +1 -0
  477. package/src/interactive/overlays/context-reset.ts +1 -0
  478. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  479. package/src/interactive/overlays/decisions.ts +11 -11
  480. package/src/interactive/overlays/extensions.ts +1 -0
  481. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  482. package/src/interactive/overlays/handoff-review.ts +1 -0
  483. package/src/interactive/overlays/help-reference.ts +6 -0
  484. package/src/interactive/overlays/interop.ts +1 -0
  485. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  486. package/src/interactive/overlays/library-tabs.ts +28 -0
  487. package/src/interactive/overlays/list-overlay.ts +10 -1
  488. package/src/interactive/overlays/message-picker.ts +1 -0
  489. package/src/interactive/overlays/model-scope.ts +86 -0
  490. package/src/interactive/overlays/model-selector.ts +1 -0
  491. package/src/interactive/overlays/prompts.ts +12 -1
  492. package/src/interactive/overlays/session-selector.ts +1 -0
  493. package/src/interactive/overlays/settings-sections.ts +30 -0
  494. package/src/interactive/overlays/settings.ts +614 -45
  495. package/src/interactive/overlays/side-question.ts +1 -0
  496. package/src/interactive/overlays/skills-hub.ts +3 -11
  497. package/src/interactive/overlays/tree-selector.ts +1 -0
  498. package/src/interactive/pane-policy.ts +46 -0
  499. package/src/interactive/panes-runtime.ts +292 -0
  500. package/src/interactive/permission-hint.ts +34 -2
  501. package/src/interactive/permission-overlay.ts +159 -9
  502. package/src/interactive/prewarm.ts +197 -0
  503. package/src/interactive/render-trace.ts +162 -15
  504. package/src/interactive/renderers/compaction-summary.ts +29 -0
  505. package/src/interactive/renderers/tool-execution.ts +4 -0
  506. package/src/interactive/renderers/worker-entry.ts +122 -14
  507. package/src/interactive/side-question.ts +58 -1
  508. package/src/interactive/slash-commands.ts +251 -15
  509. package/src/interactive/status/controller.ts +11 -0
  510. package/src/interactive/status/state-machine.ts +54 -2
  511. package/src/interactive/status/types.ts +7 -0
  512. package/src/interactive/tasks-overlay.ts +1 -0
  513. package/src/interactive/terminal-lease.ts +2 -0
  514. package/src/interactive/theme/tokens.ts +3 -14
  515. package/src/interactive/turn-context.ts +346 -33
  516. package/src/interactive/turn-persistence.ts +14 -4
  517. package/src/interactive/turn-prewarm.ts +364 -0
  518. package/src/interactive/turn-queues.ts +7 -4
  519. package/src/interactive/turn-runtime.ts +8 -1
  520. package/src/interactive/turn-state.ts +23 -0
  521. package/src/interactive/view/artifacts.ts +109 -1
  522. package/src/interactive/view/view-overlay.ts +29 -3
  523. package/src/interactive/watch-pane.ts +152 -0
  524. package/src/interactive/worker-progress.ts +7 -1
  525. package/src/interactive/worker-receipts.ts +19 -1
  526. package/src/interactive/worker-stream.ts +5 -0
  527. package/src/interactive/yazi-bridge.ts +444 -0
  528. package/src/tools/ask-user.ts +43 -2
  529. package/src/tools/bootstrap.ts +26 -2
  530. package/src/tools/builtin-tool-catalog.ts +15 -0
  531. package/src/tools/compete-worktrees.ts +83 -2
  532. package/src/tools/core-bootstrap.ts +2 -1
  533. package/src/tools/dispatch-admission.ts +14 -3
  534. package/src/tools/dispatch-arguments.ts +20 -20
  535. package/src/tools/dispatch-plan.ts +17 -9
  536. package/src/tools/dispatch-run-events.ts +134 -19
  537. package/src/tools/dispatch-runner.ts +29 -7
  538. package/src/tools/dispatch-scout.ts +1 -1
  539. package/src/tools/dispatch-types.ts +15 -3
  540. package/src/tools/dispatch.ts +1 -1
  541. package/src/tools/executables.ts +17 -14
  542. package/src/tools/observation.ts +54 -4
  543. package/src/tools/panes-surface.ts +38 -0
  544. package/src/tools/panes.ts +112 -0
  545. package/src/tools/policy.ts +10 -1
  546. package/src/tools/presentation.ts +1 -0
  547. package/src/tools/registry.ts +16 -0
  548. package/dist/chunk-AOCYTWAV.js +0 -449
  549. package/dist/chunk-HLE42MG7.js +0 -37
  550. package/dist/chunk-HWUFFB6L.js +0 -83
  551. package/dist/chunk-JOZYP4GM.js +0 -279
  552. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -84,6 +84,8 @@ function capabilities(model: LmStudioModelInfo, instance?: LmStudioLoadedInstanc
84
84
  out.reasoning = model.reasoningOptions.some((option) => option !== "off");
85
85
  const contextWindow = loadedContextLength(instance) ?? model.maxContextLength;
86
86
  if (contextWindow !== undefined) out.contextWindow = contextWindow;
87
+ const parallel = instance?.config.parallel;
88
+ if (typeof parallel === "number" && Number.isInteger(parallel) && parallel > 0) out.parallelSlots = parallel;
87
89
  return out;
88
90
  }
89
91
 
@@ -144,6 +146,7 @@ function probeFromCatalog(catalog: LmStudioCatalog, target: TargetDescriptor): P
144
146
  const selected = resolveModel(models, configuredModel(target));
145
147
  const result: ProbeResult = {
146
148
  ok: true,
149
+ discoveredCapabilities: { parallelSlots: 1 },
147
150
  models: ids,
148
151
  modelCapabilities,
149
152
  modelStates,
@@ -161,7 +164,7 @@ function probeFromCatalog(catalog: LmStudioCatalog, target: TargetDescriptor): P
161
164
  };
162
165
  if (catalog.latencyMs !== undefined) result.latencyMs = catalog.latencyMs;
163
166
  if (selected) {
164
- result.discoveredCapabilities = capabilities(selected.model, selected.instance);
167
+ result.discoveredCapabilities = { parallelSlots: 1, ...capabilities(selected.model, selected.instance) };
165
168
  result.capabilityModelId = configuredModel(target) ?? selected.model.key;
166
169
  }
167
170
  const loaded = models.flatMap((model) =>
@@ -33,6 +33,11 @@ function positiveNumber(value: unknown): number | undefined {
33
33
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
34
34
  }
35
35
 
36
+ function ollamaParallelSlots(): number {
37
+ const parsed = Number(process.env.OLLAMA_NUM_PARALLEL);
38
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 1;
39
+ }
40
+
36
41
  /**
37
42
  * Resident models reported by `/api/ps`, keyed by wire id. Best-effort: the
38
43
  * probe still succeeds on `/api/tags` alone, so an `/api/ps` failure (older
@@ -83,7 +88,7 @@ const ollamaNativeRuntime: RuntimeDescriptor = {
83
88
  if (result.latencyMs !== undefined) failed.latencyMs = result.latencyMs;
84
89
  return failed;
85
90
  }
86
- const out: ProbeResult = { ok: true };
91
+ const out: ProbeResult = { ok: true, discoveredCapabilities: { parallelSlots: ollamaParallelSlots() } };
87
92
  if (result.latencyMs !== undefined) out.latencyMs = result.latencyMs;
88
93
  const modelStates = await probeResidentModelStates(base, ctx);
89
94
  if (modelStates) out.modelStates = modelStates;
@@ -0,0 +1,375 @@
1
+ /**
2
+ * LiteLLM proxy: an OpenAI-compatible gateway that fronts a fleet.
3
+ *
4
+ * This is a separate runtime from `openai-compat` rather than a target flag on
5
+ * it, because a gateway answers two questions the generic protocol cannot.
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
12
+ * `openai-compat`, a 262k tool-calling coder is indistinguishable from a 4k
13
+ * chat toy, and Clio plans the session against its own assumed floor. LiteLLM
14
+ * publishes the real numbers on `/v1/model/info` instead, which is what this
15
+ * probe reads.
16
+ *
17
+ * Second, routed-deployment attribution. Which physical model answered is a
18
+ * per-response fact behind an alias, and LiteLLM returns it in `x-litellm-*`
19
+ * response headers. See {@link routedDeploymentFromHeaders}.
20
+ *
21
+ * Structured output is the one property worth stating explicitly, because
22
+ * getting it wrong is silent. `src/core/response-schema.ts` deliberately keys
23
+ * the schema dialect on runtime id and not on api family, since a generic
24
+ * OpenAI-compatible gateway answers HTTP 200 to a `response_format` spelling it
25
+ * does not implement and returns unconstrained JSON. LiteLLM earns its entry in
26
+ * that table by measurement rather than by assumption: against the homelab
27
+ * gateway on 2026-08-30, `{"type":"json_schema", json_schema:{...}}` was
28
+ * grammar-enforced end to end on both a llama.cpp upstream and an LM Studio
29
+ * upstream, `{"type":"json_object", schema:{...}}` was enforced on llama.cpp
30
+ * and rejected HTTP 400 by LM Studio, and the same prompt with no
31
+ * `response_format` returned plain prose. `drop_params: true` did not strip
32
+ * either spelling. The standard `json_schema` dialect is therefore the one that
33
+ * holds across every upstream, and it is what this runtime declares.
34
+ */
35
+
36
+ import { CLIO_MIN_CONTEXT_WINDOW, CLIO_MIN_MAX_OUTPUT_TOKENS } from "../../../../core/context-floor.js";
37
+ import type { Api, Model } from "../../../../engine/types.js";
38
+ import { probeJson } from "../../probe/http.js";
39
+ import type { CapabilityFlags } from "../../types/capability-flags.js";
40
+ import type { KnowledgeBaseHit } from "../../types/knowledge-base.js";
41
+ import type { ProbeContext, ProbeModelStatus, ProbeResult, RuntimeDescriptor } from "../../types/runtime-descriptor.js";
42
+ import type { TargetDescriptor } from "../../types/target-descriptor.js";
43
+ import { stripTrailingSlash, synthLocalModel, withV1 } from "../common/local-synth.js";
44
+
45
+ const LITELLM_PROVIDER = "litellm";
46
+
47
+ /**
48
+ * Conservative until the probe answers. A gateway that does not publish
49
+ * `model_info` has told Clio nothing, and claiming tools on a target whose tool
50
+ * surface is unknown produces a worker that fails on its first tool call rather
51
+ * than one that is never admitted.
52
+ */
53
+ const defaultCapabilities: CapabilityFlags = {
54
+ chat: true,
55
+ tools: false,
56
+ reasoning: false,
57
+ vision: false,
58
+ audio: false,
59
+ embeddings: false,
60
+ rerank: false,
61
+ fim: false,
62
+ structuredOutputs: "json-schema",
63
+ contextWindow: CLIO_MIN_CONTEXT_WINDOW,
64
+ maxTokens: CLIO_MIN_MAX_OUTPUT_TOKENS,
65
+ };
66
+
67
+ /** The server root. This runtime's own paths carry `/v1` where they need it. */
68
+ function rootUrl(target: TargetDescriptor): string | null {
69
+ if (!target.url) return null;
70
+ const trimmed = stripTrailingSlash(target.url);
71
+ return trimmed.endsWith("/v1") ? trimmed.slice(0, -"/v1".length) : trimmed;
72
+ }
73
+
74
+ function bearerHeaders(target: TargetDescriptor, ctx: ProbeContext): Record<string, string> {
75
+ const headers: Record<string, string> = { ...(target.auth?.headers ?? {}) };
76
+ const envName = target.auth?.apiKeyEnvVar;
77
+ const fromEnv = envName && ctx.credentialsPresent.has(envName) ? process.env[envName]?.trim() : undefined;
78
+ const token = ctx.authToken?.trim() || fromEnv;
79
+ if (token) headers.authorization = `Bearer ${token}`;
80
+ return headers;
81
+ }
82
+
83
+ interface LiteLLMModelInfoRow {
84
+ model_name?: unknown;
85
+ litellm_params?: { model?: unknown; api_base?: unknown };
86
+ model_info?: Record<string, unknown>;
87
+ }
88
+
89
+ interface LiteLLMModelInfoResponse {
90
+ data?: ReadonlyArray<LiteLLMModelInfoRow>;
91
+ }
92
+
93
+ interface LiteLLMModelsResponse {
94
+ data?: ReadonlyArray<{ id?: unknown }>;
95
+ }
96
+
97
+ function positiveInteger(value: unknown): number | undefined {
98
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
99
+ }
100
+
101
+ function boolean(value: unknown): boolean | undefined {
102
+ return typeof value === "boolean" ? value : undefined;
103
+ }
104
+
105
+ /**
106
+ * Project one LiteLLM `model_info` block onto Clio's capability flags.
107
+ *
108
+ * Only fields the gateway actually filled in are returned. A `model_info` that
109
+ * declares nothing leaves the whole record empty rather than reporting zeros,
110
+ * so the caller can tell "the gateway says this model has no vision" apart from
111
+ * "the gateway did not say", and merge accordingly.
112
+ */
113
+ export function capabilitiesFromLiteLLMModelInfo(info: Record<string, unknown>): Partial<CapabilityFlags> {
114
+ const caps: Partial<CapabilityFlags> = {};
115
+ const contextWindow = positiveInteger(info.max_input_tokens) ?? positiveInteger(info.max_tokens);
116
+ if (contextWindow !== undefined) caps.contextWindow = contextWindow;
117
+ const maxTokens = positiveInteger(info.max_output_tokens);
118
+ if (maxTokens !== undefined) caps.maxTokens = maxTokens;
119
+ const tools = boolean(info.supports_function_calling);
120
+ if (tools !== undefined) caps.tools = tools;
121
+ const vision = boolean(info.supports_vision);
122
+ if (vision !== undefined) caps.vision = vision;
123
+ const reasoning = boolean(info.supports_reasoning);
124
+ if (reasoning !== undefined) caps.reasoning = reasoning;
125
+ const audio = boolean(info.supports_audio_input);
126
+ if (audio !== undefined) caps.audio = audio;
127
+ // `mode` is LiteLLM's own surface discriminator. An embedding deployment is
128
+ // not a chat deployment, and admitting it as one produces a run that fails
129
+ // at the first completion rather than at selection.
130
+ if (info.mode === "embedding") {
131
+ caps.chat = false;
132
+ caps.embeddings = true;
133
+ }
134
+ if (info.mode === "rerank") {
135
+ caps.chat = false;
136
+ caps.rerank = true;
137
+ }
138
+ if (info.mode === "chat") caps.chat = true;
139
+ // Measured, not inferred: see the module header.
140
+ if (boolean(info.supports_response_schema) === true) caps.structuredOutputs = "json-schema";
141
+ return caps;
142
+ }
143
+
144
+ interface LiteLLMCatalog {
145
+ models: string[];
146
+ modelCapabilities: Record<string, Partial<CapabilityFlags>>;
147
+ modelStates: Record<string, ProbeModelStatus>;
148
+ /** Physical deployment behind each alias, for display. */
149
+ deployments: Record<string, { model: string; apiBase?: string }>;
150
+ }
151
+
152
+ const EMPTY_CATALOG: LiteLLMCatalog = { models: [], modelCapabilities: {}, modelStates: {}, deployments: {} };
153
+
154
+ /**
155
+ * Read the catalog from `/v1/model/info`, falling back to `/v1/models`.
156
+ *
157
+ * The detail endpoint is the only one that carries capabilities, but it can be
158
+ * refused for a virtual key with narrower permissions than the master key. A
159
+ * gateway that answers the plain listing and refuses the detail is still usable;
160
+ * it just cannot tell Clio anything beyond which aliases exist, so the target
161
+ * falls back to its declared capabilities exactly as `openai-compat` would.
162
+ */
163
+ async function fetchCatalog(base: string, ctx: ProbeContext, headers: Record<string, string>): Promise<LiteLLMCatalog> {
164
+ const detailOpts = { url: `${base}/v1/model/info`, timeoutMs: ctx.httpTimeoutMs, headers } as const;
165
+ const detail = await (ctx.signal
166
+ ? probeJson<LiteLLMModelInfoResponse>({ ...detailOpts, signal: ctx.signal })
167
+ : probeJson<LiteLLMModelInfoResponse>(detailOpts));
168
+
169
+ if (detail.ok && Array.isArray(detail.data?.data)) {
170
+ const catalog: LiteLLMCatalog = { models: [], modelCapabilities: {}, modelStates: {}, deployments: {} };
171
+ for (const row of detail.data.data) {
172
+ if (typeof row?.model_name !== "string" || row.model_name.length === 0) continue;
173
+ const alias = row.model_name;
174
+ // One alias can carry several deployments. They share a name and Clio
175
+ // addresses them as one model, so the first row establishes the entry
176
+ // and later rows for the same alias are the load-balancing pool behind
177
+ // it rather than separate models.
178
+ if (catalog.deployments[alias] !== undefined) continue;
179
+ catalog.models.push(alias);
180
+ const caps = row.model_info ? capabilitiesFromLiteLLMModelInfo(row.model_info) : {};
181
+ if (Object.keys(caps).length > 0) catalog.modelCapabilities[alias] = caps;
182
+ const upstream = row.litellm_params?.model;
183
+ const apiBase = row.litellm_params?.api_base;
184
+ catalog.deployments[alias] = {
185
+ model: typeof upstream === "string" ? upstream : alias,
186
+ ...(typeof apiBase === "string" ? { apiBase } : {}),
187
+ };
188
+ }
189
+ if (catalog.models.length > 0) return catalog;
190
+ }
191
+
192
+ const listOpts = { url: `${base}/v1/models`, timeoutMs: ctx.httpTimeoutMs, headers } as const;
193
+ const list = await (ctx.signal
194
+ ? probeJson<LiteLLMModelsResponse>({ ...listOpts, signal: ctx.signal })
195
+ : probeJson<LiteLLMModelsResponse>(listOpts));
196
+ if (!list.ok || !Array.isArray(list.data?.data)) return EMPTY_CATALOG;
197
+ const models = list.data.data
198
+ .map((row) => (typeof row?.id === "string" ? row.id : null))
199
+ .filter((id): id is string => id !== null && id.length > 0);
200
+ return { models, modelCapabilities: {}, modelStates: {}, deployments: {} };
201
+ }
202
+
203
+ /**
204
+ * The routed deployment LiteLLM reports for one response.
205
+ *
206
+ * Behind an alias the physical model is a per-request fact: a fallback, a
207
+ * context-window promotion, or an ordinary load-balancing choice all change it
208
+ * without changing what the client asked for. Reporting the alias back as the
209
+ * model is how a gateway hides an outage, and it is exactly what an operator
210
+ * needs to see. LiteLLM answers in headers on every completion.
211
+ */
212
+ export interface RoutedDeployment {
213
+ /** The alias the client asked for, e.g. `code`. */
214
+ group?: string;
215
+ /** The physical model that answered, e.g. `openai/katcoder2.5-35b-moe`. */
216
+ model?: string;
217
+ /** The upstream that served it. */
218
+ apiBase?: string;
219
+ /** Stable id of the specific deployment inside the group. */
220
+ deploymentId?: string;
221
+ /** Non-zero when the request did not reach its first-choice deployment. */
222
+ attemptedFallbacks?: number;
223
+ attemptedRetries?: number;
224
+ }
225
+
226
+ function headerValue(headers: { get(name: string): string | null }, name: string): string | undefined {
227
+ const raw = headers.get(name)?.trim();
228
+ return raw && raw.length > 0 ? raw : undefined;
229
+ }
230
+
231
+ function headerInteger(headers: { get(name: string): string | null }, name: string): number | undefined {
232
+ const raw = headerValue(headers, name);
233
+ if (raw === undefined) return undefined;
234
+ const parsed = Number.parseInt(raw, 10);
235
+ return Number.isFinite(parsed) ? parsed : undefined;
236
+ }
237
+
238
+ /**
239
+ * Extract the routed deployment from a LiteLLM response, or null when the
240
+ * headers are absent. Absent is the normal answer for every non-LiteLLM target,
241
+ * so callers treat null as "no attribution available" rather than an error.
242
+ */
243
+ export function routedDeploymentFromHeaders(headers: { get(name: string): string | null }): RoutedDeployment | null {
244
+ const routed: RoutedDeployment = {};
245
+ const group = headerValue(headers, "x-litellm-model-group");
246
+ if (group !== undefined) routed.group = group;
247
+ const model = headerValue(headers, "x-litellm-model-name");
248
+ if (model !== undefined) routed.model = model;
249
+ const apiBase = headerValue(headers, "x-litellm-model-api-base");
250
+ if (apiBase !== undefined) routed.apiBase = apiBase;
251
+ const deploymentId = headerValue(headers, "x-litellm-model-id");
252
+ if (deploymentId !== undefined) routed.deploymentId = deploymentId;
253
+ const fallbacks = headerInteger(headers, "x-litellm-attempted-fallbacks");
254
+ if (fallbacks !== undefined) routed.attemptedFallbacks = fallbacks;
255
+ const retries = headerInteger(headers, "x-litellm-attempted-retries");
256
+ if (retries !== undefined) routed.attemptedRetries = retries;
257
+ return Object.keys(routed).length > 0 ? routed : null;
258
+ }
259
+
260
+ /**
261
+ * One-line attribution for a routed response.
262
+ *
263
+ * A request that fell back says so. `code -> openai/qwen3.8-27b-dense @
264
+ * mini.tail78f300.ts.net:8080 (fallback)` is the whole point of the runtime:
265
+ * the operator asked for the coder, the coder was asleep, and something else
266
+ * answered.
267
+ */
268
+ export function formatRoutedDeployment(routed: RoutedDeployment): string {
269
+ const parts: string[] = [];
270
+ if (routed.group && routed.model && routed.group !== routed.model) {
271
+ parts.push(`${routed.group} -> ${routed.model}`);
272
+ } else if (routed.model ?? routed.group) {
273
+ parts.push((routed.model ?? routed.group) as string);
274
+ }
275
+ if (routed.apiBase) {
276
+ let host = routed.apiBase;
277
+ try {
278
+ host = new URL(routed.apiBase).host;
279
+ } catch {
280
+ // A non-URL api_base is displayed verbatim.
281
+ }
282
+ parts.push(`@ ${host}`);
283
+ }
284
+ if ((routed.attemptedFallbacks ?? 0) > 0) parts.push("(fallback)");
285
+ else if ((routed.attemptedRetries ?? 0) > 0) parts.push("(retried)");
286
+ return parts.join(" ");
287
+ }
288
+
289
+ const litellmRuntime: RuntimeDescriptor = {
290
+ id: "litellm",
291
+ displayName: "LiteLLM gateway",
292
+ kind: "http",
293
+ tier: "protocol",
294
+ apiFamily: "openai-completions",
295
+ auth: "api-key",
296
+ defaultCapabilities,
297
+
298
+ async probe(target: TargetDescriptor, ctx: ProbeContext): Promise<ProbeResult> {
299
+ const base = rootUrl(target);
300
+ if (!base) return { ok: false, error: "target has no url" };
301
+ const headers = bearerHeaders(target, ctx);
302
+
303
+ // Liveness first, and deliberately on the unauthenticated endpoint. A
304
+ // gateway with authentication enforced answers 401 to `/v1/models` when
305
+ // credentials are missing or wrong, which is indistinguishable from an
306
+ // unreachable host if that is the only thing probed. `/health/liveliness`
307
+ // separates "the gateway is down" from "this key is not valid", and the
308
+ // two have completely different remedies.
309
+ const liveOpts = { url: `${base}/health/liveliness`, timeoutMs: ctx.httpTimeoutMs } as const;
310
+ const live = await (ctx.signal
311
+ ? probeJson<unknown>({ ...liveOpts, signal: ctx.signal })
312
+ : probeJson<unknown>(liveOpts));
313
+ if (!live.ok) return { ok: false, error: live.error ?? "gateway is not reachable" };
314
+
315
+ const catalog = await fetchCatalog(base, ctx, headers);
316
+ if (catalog.models.length === 0) {
317
+ const result: ProbeResult = {
318
+ ok: false,
319
+ error: "gateway is live but served no model catalog; check the target's API key",
320
+ };
321
+ if (live.latencyMs !== undefined) result.latencyMs = live.latencyMs;
322
+ return result;
323
+ }
324
+
325
+ const result: ProbeResult = { ok: true, models: catalog.models };
326
+ if (live.latencyMs !== undefined) result.latencyMs = live.latencyMs;
327
+ if (Object.keys(catalog.modelCapabilities).length > 0) {
328
+ result.modelCapabilities = catalog.modelCapabilities;
329
+ const selected = target.defaultModel?.trim();
330
+ const selectedCaps = selected ? catalog.modelCapabilities[selected] : undefined;
331
+ if (selected && selectedCaps) {
332
+ result.discoveredCapabilities = selectedCaps;
333
+ result.capabilityModelId = selected;
334
+ }
335
+ }
336
+ const notes: string[] = [];
337
+ const configured = target.defaultModel?.trim();
338
+ if (configured && !catalog.models.includes(configured)) {
339
+ notes.push(`configured model '${configured}' is not in the gateway catalog`);
340
+ }
341
+ // Naming the physical model behind each alias is the difference between a
342
+ // catalog an operator can audit and a list of words.
343
+ const routed = Object.entries(catalog.deployments)
344
+ .map(([alias, deployment]) => `${alias}=${deployment.model}`)
345
+ .sort();
346
+ if (routed.length > 0) notes.push(`routes: ${routed.join(", ")}`);
347
+ if (notes.length > 0) result.notes = notes;
348
+ return result;
349
+ },
350
+
351
+ async probeModels(target: TargetDescriptor, ctx: ProbeContext): Promise<string[]> {
352
+ const base = rootUrl(target);
353
+ if (!base) return [];
354
+ return (await fetchCatalog(base, ctx, bearerHeaders(target, ctx))).models;
355
+ },
356
+
357
+ synthesizeModel(target: TargetDescriptor, wireModelId: string, kb: KnowledgeBaseHit | null): Model<Api> {
358
+ return synthLocalModel({
359
+ // A LiteLLM target is a gateway whether or not settings said so. The
360
+ // residency layer reads this to stay observe-only: the models behind an
361
+ // alias are the proxy's to load and evict, and a Clio stream that tried
362
+ // to manage them would be issuing load commands to a host that does not
363
+ // take them.
364
+ target: { ...target, gateway: true },
365
+ wireModelId,
366
+ kb,
367
+ defaultCapabilities,
368
+ apiFamily: "openai-completions",
369
+ provider: LITELLM_PROVIDER,
370
+ baseUrlForTarget: withV1,
371
+ });
372
+ },
373
+ };
374
+
375
+ export default litellmRuntime;
@@ -59,6 +59,7 @@ const SUMMARY_BY_RUNTIME_ID: Readonly<Record<string, string>> = {
59
59
  llamacpp: "llama.cpp server (auto-detect surface)",
60
60
  "anthropic-compat": "Generic Anthropic-compatible REST",
61
61
  "openai-compat": "Generic OpenAI-compatible REST",
62
+ litellm: "LiteLLM gateway with per-alias capability discovery and routed-model attribution",
62
63
  };
63
64
 
64
65
  function groupPriority(group: ProviderSupportGroup): number {
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Disposable per-target model catalogs discovered by provider probes.
3
+ *
4
+ * A live endpoint catalog is machine state, not operator policy. Persisting it
5
+ * in settings.yaml made a three-target config carry hundreds of volatile model
6
+ * ids. Cache files keep that snapshot available to a new process without
7
+ * turning the hand-edited settings file into a probe transcript.
8
+ */
9
+
10
+ import { createHash } from "node:crypto";
11
+ import { existsSync, readFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { safeResourceWrite } from "../../core/safe-resource-write.js";
14
+ import { clioCacheDir } from "../../core/xdg.js";
15
+ import type { TargetDescriptor } from "./types/target-descriptor.js";
16
+
17
+ export const TARGET_MODEL_CACHE_TTL_MS = 24 * 60 * 60 * 1_000;
18
+ const TARGET_MODEL_CACHE_VERSION = 1;
19
+ const TARGET_MODEL_CACHE_MAX_MODELS = 4_096;
20
+ const TARGET_MODEL_CACHE_MAX_MODEL_ID_CHARS = 1_024;
21
+
22
+ export interface TargetModelSnapshot {
23
+ version: 1;
24
+ targetId: string;
25
+ runtimeId: string;
26
+ url: string | null;
27
+ models: string[];
28
+ observedAt: string;
29
+ }
30
+
31
+ interface TargetModelCacheOptions {
32
+ cacheDir?: string;
33
+ nowMs?: number;
34
+ ttlMs?: number;
35
+ }
36
+
37
+ function targetCacheKey(targetId: string): string {
38
+ return createHash("sha256").update(targetId).digest("hex").slice(0, 24);
39
+ }
40
+
41
+ export function targetModelSnapshotPath(targetId: string, cacheDir = clioCacheDir()): string {
42
+ return join(cacheDir, "provider-target-models", `${targetCacheKey(targetId)}.json`);
43
+ }
44
+
45
+ function normalizedModels(models: ReadonlyArray<string>): string[] {
46
+ const seen = new Set<string>();
47
+ const out: string[] = [];
48
+ for (const raw of models) {
49
+ const model = raw.trim();
50
+ if (model.length === 0 || model.length > TARGET_MODEL_CACHE_MAX_MODEL_ID_CHARS || seen.has(model)) continue;
51
+ seen.add(model);
52
+ out.push(model);
53
+ if (out.length >= TARGET_MODEL_CACHE_MAX_MODELS) break;
54
+ }
55
+ return out;
56
+ }
57
+
58
+ function parseSnapshot(value: unknown): TargetModelSnapshot | null {
59
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
60
+ const row = value as Partial<TargetModelSnapshot>;
61
+ if (row.version !== TARGET_MODEL_CACHE_VERSION) return null;
62
+ if (typeof row.targetId !== "string" || row.targetId.length === 0) return null;
63
+ if (typeof row.runtimeId !== "string" || row.runtimeId.length === 0) return null;
64
+ if (row.url !== null && typeof row.url !== "string") return null;
65
+ if (!Array.isArray(row.models) || row.models.some((model) => typeof model !== "string")) return null;
66
+ if (typeof row.observedAt !== "string" || !Number.isFinite(Date.parse(row.observedAt))) return null;
67
+ const models = normalizedModels(row.models);
68
+ if (models.length !== row.models.length) return null;
69
+ return {
70
+ version: TARGET_MODEL_CACHE_VERSION,
71
+ targetId: row.targetId,
72
+ runtimeId: row.runtimeId,
73
+ url: row.url,
74
+ models,
75
+ observedAt: row.observedAt,
76
+ };
77
+ }
78
+
79
+ /** Read a fresh snapshot only when it still describes this target identity. */
80
+ export function readTargetModelSnapshot(
81
+ target: Pick<TargetDescriptor, "id" | "runtime" | "url">,
82
+ options: TargetModelCacheOptions = {},
83
+ ): TargetModelSnapshot | null {
84
+ const path = targetModelSnapshotPath(target.id, options.cacheDir);
85
+ if (!existsSync(path)) return null;
86
+ try {
87
+ const snapshot = parseSnapshot(JSON.parse(readFileSync(path, "utf8")) as unknown);
88
+ if (snapshot === null) return null;
89
+ if (snapshot.targetId !== target.id || snapshot.runtimeId !== target.runtime) return null;
90
+ if (snapshot.url !== (target.url ?? null)) return null;
91
+ const observedMs = Date.parse(snapshot.observedAt);
92
+ const nowMs = options.nowMs ?? Date.now();
93
+ const ttlMs = options.ttlMs ?? TARGET_MODEL_CACHE_TTL_MS;
94
+ if (observedMs > nowMs || nowMs - observedMs >= ttlMs) return null;
95
+ return snapshot;
96
+ } catch {
97
+ return null;
98
+ }
99
+ }
100
+
101
+ /** Best-effort atomic replacement of one target's disposable probe snapshot. */
102
+ export function recordTargetModelSnapshot(
103
+ target: Pick<TargetDescriptor, "id" | "runtime" | "url">,
104
+ models: ReadonlyArray<string>,
105
+ options: Pick<TargetModelCacheOptions, "cacheDir" | "nowMs"> = {},
106
+ ): boolean {
107
+ if (target.id.length === 0 || target.runtime.length === 0) return false;
108
+ const snapshot: TargetModelSnapshot = {
109
+ version: TARGET_MODEL_CACHE_VERSION,
110
+ targetId: target.id,
111
+ runtimeId: target.runtime,
112
+ url: target.url ?? null,
113
+ models: normalizedModels(models),
114
+ observedAt: new Date(options.nowMs ?? Date.now()).toISOString(),
115
+ };
116
+ try {
117
+ safeResourceWrite(targetModelSnapshotPath(target.id, options.cacheDir), `${JSON.stringify(snapshot, null, 2)}\n`, {
118
+ encoding: "utf8",
119
+ });
120
+ return true;
121
+ } catch {
122
+ return false;
123
+ }
124
+ }
@@ -28,6 +28,8 @@ export interface CapabilityFlags {
28
28
  fim: boolean;
29
29
  contextWindow: number;
30
30
  maxTokens: number;
31
+ /** Request slots reported by the endpoint probe. This is deployment capacity, not a model trait. */
32
+ parallelSlots?: number;
31
33
  }
32
34
 
33
35
  export const EMPTY_CAPABILITIES: CapabilityFlags = {
@@ -57,4 +57,6 @@ export interface TargetDescriptor {
57
57
  gateway?: boolean;
58
58
  pricing?: TargetPricing;
59
59
  lmstudio?: LmStudioTargetSettings;
60
+ /** Explicit request-slot limit for this inference endpoint. It overrides live discovery. */
61
+ maxConcurrentRequests?: number;
60
62
  }
@@ -68,11 +68,14 @@ export {
68
68
  parsePendingSkillRequests,
69
69
  parseSkillCommand,
70
70
  type Skill,
71
+ type SkillCatalogInvalidReason,
72
+ type SkillCatalogValidity,
71
73
  type SkillExpansion,
72
74
  type SkillList,
73
75
  type SkillProvenance,
74
76
  type SkillRoot,
75
77
  type SkillSource,
78
+ skillCatalogValidity,
76
79
  } from "./skills/loader.js";
77
80
  export {
78
81
  type DiscoverMarketplaceOptions,