@iowarp/clio-coder 0.3.8 → 0.3.9

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 (333) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +7 -3
  3. package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
  4. package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
  7. package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
  11. package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
  13. package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
  14. package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
  17. package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
  18. package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
  21. package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
  22. package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
  23. package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
  27. package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
  28. package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
  29. package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
  30. package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
  31. package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
  32. package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
  33. package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
  34. package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
  35. package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
  36. package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
  37. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  38. package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
  39. package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
  40. package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
  41. package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
  42. package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
  43. package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
  44. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  45. package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
  46. package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
  47. package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
  48. package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
  49. package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
  50. package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
  51. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  52. package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
  53. package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
  54. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  55. package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
  56. package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
  57. package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
  58. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  59. package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
  60. package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
  61. package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
  62. package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
  63. package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
  64. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  65. package/dist/chunk-RKKLTLYB.js +45 -0
  66. package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
  67. package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
  68. package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
  69. package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
  70. package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
  71. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  72. package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
  73. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  74. package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
  75. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  76. package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
  77. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  78. package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
  79. package/dist/chunk-YSEHGPCT.js +127 -0
  80. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  81. package/dist/cli/index.js +27 -27
  82. package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
  83. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
  84. package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
  85. package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
  86. package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
  87. package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
  88. package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
  89. package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
  90. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  91. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
  92. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
  93. package/dist/doctor-TWBWFK5V.js +165 -0
  94. package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
  95. package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
  96. package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
  97. package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
  98. package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
  99. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
  100. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
  101. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
  102. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
  103. package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
  104. package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
  105. package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
  106. package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
  107. package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
  108. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
  109. package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
  110. package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
  111. package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
  112. package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
  113. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
  114. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  115. package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
  116. package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
  117. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
  118. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  119. package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
  120. package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
  121. package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
  122. package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
  123. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
  124. package/dist/worker/entry.js +29 -33
  125. package/docs/alcf-provider.md +1 -1
  126. package/docs/architecture.md +1 -1
  127. package/docs/artifact-versions.md +6 -1
  128. package/docs/built-in-agents.md +1 -1
  129. package/docs/capacity-and-scheduling.md +23 -2
  130. package/docs/commands-and-modes.md +1 -1
  131. package/docs/configuration-and-targets.md +30 -3
  132. package/docs/context-engine.md +63 -4
  133. package/docs/documentation-coverage.md +3 -3
  134. package/docs/documentation-guide.md +1 -1
  135. package/docs/environment-variables.md +2 -0
  136. package/docs/eval-runner.md +262 -11
  137. package/docs/evals-internal.md +72 -2
  138. package/docs/evidence-and-memory.md +11 -10
  139. package/docs/evolution.md +1 -1
  140. package/docs/extensions-and-sharing.md +3 -1
  141. package/docs/fleet-dispatch.md +4 -4
  142. package/docs/installation-and-lifecycle.md +1 -1
  143. package/docs/middleware-and-components.md +1 -1
  144. package/docs/model-catalog.md +1 -1
  145. package/docs/observability.md +53 -2
  146. package/docs/proactive-memory.md +127 -14
  147. package/docs/prompt-envelope-and-tools.md +19 -1
  148. package/docs/provider-adapter-cookbook.md +1 -1
  149. package/docs/release-cut-checklist.md +19 -3
  150. package/docs/safety-model.md +1 -1
  151. package/docs/scientific-validation.md +1 -1
  152. package/docs/skills-marketplace.md +1 -1
  153. package/docs/tool-usage.md +1 -1
  154. package/docs/trace-store.md +1 -1
  155. package/docs/troubleshooting.md +87 -0
  156. package/docs/tui-design.md +1 -1
  157. package/docs/worker-dispatch-mechanics.md +1 -1
  158. package/package.json +2 -1
  159. package/src/cli/agents.ts +1 -1
  160. package/src/cli/config-inspect.ts +33 -6
  161. package/src/cli/config.ts +1 -1
  162. package/src/cli/doctor-state-size.ts +82 -0
  163. package/src/cli/doctor.ts +3 -1
  164. package/src/cli/eval.ts +80 -16
  165. package/src/cli/extensions.ts +5 -1
  166. package/src/cli/fleet.ts +32 -3
  167. package/src/cli/targets.ts +44 -13
  168. package/src/cli/trace.ts +63 -4
  169. package/src/cli/usage.ts +63 -14
  170. package/src/core/bus-events.ts +29 -1
  171. package/src/core/cache-telemetry.ts +42 -0
  172. package/src/core/config.ts +18 -0
  173. package/src/core/defaults.ts +36 -6
  174. package/src/core/endpoint-key.ts +27 -0
  175. package/src/core/residency-target-key.ts +25 -0
  176. package/src/core/response-schema.ts +36 -2
  177. package/src/domains/config/classify.ts +3 -0
  178. package/src/domains/context/codewiki/coordinator.ts +12 -4
  179. package/src/domains/dispatch/admission.ts +40 -3
  180. package/src/domains/dispatch/capacity-lease.ts +98 -9
  181. package/src/domains/dispatch/contract.ts +11 -0
  182. package/src/domains/dispatch/execution-plan.ts +44 -4
  183. package/src/domains/dispatch/extension.ts +166 -42
  184. package/src/domains/dispatch/fleet-run.ts +23 -3
  185. package/src/domains/dispatch/heartbeat.ts +32 -8
  186. package/src/domains/dispatch/index.ts +3 -0
  187. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  188. package/src/domains/dispatch/reservation-store.ts +116 -8
  189. package/src/domains/dispatch/state.ts +4 -0
  190. package/src/domains/dispatch/worker-spawn.ts +25 -11
  191. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  192. package/src/domains/dispatch/write-boundary.ts +62 -1
  193. package/src/domains/eval/artifacts/store.ts +62 -0
  194. package/src/domains/eval/compare/behavioral.ts +224 -0
  195. package/src/domains/eval/compare/compare.ts +355 -2
  196. package/src/domains/eval/compare/envelope.ts +128 -0
  197. package/src/domains/eval/compare/gates.ts +24 -6
  198. package/src/domains/eval/compare/thresholds.ts +30 -3
  199. package/src/domains/eval/execution-provenance.ts +240 -0
  200. package/src/domains/eval/metrics/aggregate.ts +136 -0
  201. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  202. package/src/domains/eval/metrics/tracked.ts +413 -0
  203. package/src/domains/eval/provenance.ts +117 -0
  204. package/src/domains/eval/reports/comparison.ts +128 -0
  205. package/src/domains/eval/reports/junit.ts +17 -3
  206. package/src/domains/eval/reports/markdown.ts +3 -3
  207. package/src/domains/eval/reports/text.ts +14 -0
  208. package/src/domains/eval/run-compare.ts +20 -0
  209. package/src/domains/eval/runners/clio-run.ts +127 -0
  210. package/src/domains/eval/runners/external-command.ts +28 -3
  211. package/src/domains/eval/schema/adapter.ts +111 -0
  212. package/src/domains/eval/schema/artifact.ts +20 -0
  213. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  214. package/src/domains/eval/schema/behavioral.ts +520 -0
  215. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  216. package/src/domains/eval/schema/serving.ts +74 -0
  217. package/src/domains/eval/schema/suite.ts +38 -8
  218. package/src/domains/eval/schema/validate.ts +58 -3
  219. package/src/domains/eval/schema/verdict.ts +237 -0
  220. package/src/domains/eval/suites/resolve.ts +2 -0
  221. package/src/domains/eval/suites/run.ts +264 -33
  222. package/src/domains/eval/verifiers/command.ts +2 -1
  223. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  224. package/src/domains/evidence/build.ts +2 -13
  225. package/src/domains/evidence/eval.ts +2 -12
  226. package/src/domains/evidence/findings-markdown.ts +33 -0
  227. package/src/domains/evidence/run-trust.ts +7 -113
  228. package/src/domains/evidence/trust-projection.ts +2 -2
  229. package/src/domains/extensions/compatibility.ts +285 -0
  230. package/src/domains/extensions/discovery.ts +38 -3
  231. package/src/domains/extensions/resources.ts +1 -1
  232. package/src/domains/extensions/state.ts +12 -3
  233. package/src/domains/extensions/types.ts +2 -0
  234. package/src/domains/lifecycle/doctor.ts +69 -1
  235. package/src/domains/memory/index.ts +14 -0
  236. package/src/domains/memory/task-bank-promotion.ts +64 -0
  237. package/src/domains/memory/task-memory-policy.ts +77 -8
  238. package/src/domains/memory/task-memory-spend.ts +131 -0
  239. package/src/domains/memory/task-memory-status.ts +7 -0
  240. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  241. package/src/domains/middleware/index.ts +1 -0
  242. package/src/domains/middleware/memory-intervention.ts +69 -5
  243. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  244. package/src/domains/observability/background-memory-usage.ts +140 -0
  245. package/src/domains/observability/cost.ts +1 -1
  246. package/src/domains/observability/index.ts +7 -0
  247. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  248. package/src/domains/observability/trace-store.ts +192 -2
  249. package/src/domains/prompts/compiler.ts +100 -13
  250. package/src/domains/providers/endpoint-capacity.ts +96 -0
  251. package/src/domains/providers/index.ts +10 -0
  252. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  253. package/src/domains/providers/runtime-resolution.ts +8 -1
  254. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  255. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  256. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  257. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  258. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  259. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  260. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  261. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  262. package/src/domains/providers/types/capability-flags.ts +2 -0
  263. package/src/domains/providers/types/target-descriptor.ts +2 -0
  264. package/src/domains/resources/prompts/loader.ts +95 -33
  265. package/src/domains/safety/call-target.ts +52 -0
  266. package/src/domains/safety/run-effects.ts +35 -4
  267. package/src/domains/session/context-accounting.ts +52 -1
  268. package/src/domains/session/context-ledger.ts +37 -13
  269. package/src/domains/session/index.ts +6 -0
  270. package/src/domains/session/prompt-cache.ts +140 -0
  271. package/src/domains/session/prompt-manifest.ts +42 -0
  272. package/src/engine/acp/adapter.ts +18 -3
  273. package/src/engine/ai.ts +35 -0
  274. package/src/engine/apis/llamacpp-residency.ts +55 -3
  275. package/src/engine/apis/lmstudio.ts +25 -5
  276. package/src/engine/apis/ollama-native.ts +2 -1
  277. package/src/engine/apis/openai-completions.ts +80 -17
  278. package/src/engine/apis/residency-lock.ts +3 -1
  279. package/src/engine/apis/residency.ts +34 -1
  280. package/src/engine/provider-payload.ts +29 -1
  281. package/src/entry/orchestrator.ts +176 -30
  282. package/src/interactive/chat-loop-messages.ts +26 -7
  283. package/src/interactive/chat-loop.ts +318 -41
  284. package/src/interactive/chat-panel.ts +62 -8
  285. package/src/interactive/clio-editor.ts +45 -8
  286. package/src/interactive/context-activity.ts +5 -1
  287. package/src/interactive/context-meter.ts +1 -1
  288. package/src/interactive/context-overlay.ts +40 -10
  289. package/src/interactive/cost-overlay.ts +64 -6
  290. package/src/interactive/dispatch-board.ts +84 -12
  291. package/src/interactive/fleet-run-preview.ts +41 -15
  292. package/src/interactive/handoff-round.ts +41 -2
  293. package/src/interactive/interactive-application.ts +24 -1
  294. package/src/interactive/interactive-input-runtime.ts +8 -0
  295. package/src/interactive/interactive-presentation.ts +4 -0
  296. package/src/interactive/interactive-shell.ts +20 -17
  297. package/src/interactive/interactive-slash-runtime.ts +27 -4
  298. package/src/interactive/memory-overlay.ts +8 -0
  299. package/src/interactive/mutation-preview.ts +295 -0
  300. package/src/interactive/overlay-general-openers.ts +16 -0
  301. package/src/interactive/overlay-key-routing.ts +38 -0
  302. package/src/interactive/overlay-lifecycle.ts +38 -5
  303. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  304. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  305. package/src/interactive/overlays/ask-user.ts +91 -19
  306. package/src/interactive/overlays/help-reference.ts +4 -0
  307. package/src/interactive/overlays/prompts.ts +11 -1
  308. package/src/interactive/overlays/settings.ts +35 -1
  309. package/src/interactive/permission-hint.ts +34 -2
  310. package/src/interactive/permission-overlay.ts +159 -9
  311. package/src/interactive/prewarm.ts +197 -0
  312. package/src/interactive/render-trace.ts +162 -15
  313. package/src/interactive/renderers/tool-execution.ts +4 -0
  314. package/src/interactive/side-question.ts +58 -1
  315. package/src/interactive/status/controller.ts +11 -0
  316. package/src/interactive/status/state-machine.ts +54 -2
  317. package/src/interactive/status/types.ts +7 -0
  318. package/src/interactive/terminal-lease.ts +2 -0
  319. package/src/interactive/turn-context.ts +299 -31
  320. package/src/interactive/turn-persistence.ts +14 -4
  321. package/src/interactive/turn-prewarm.ts +364 -0
  322. package/src/interactive/turn-queues.ts +7 -4
  323. package/src/interactive/turn-runtime.ts +8 -1
  324. package/src/interactive/turn-state.ts +23 -0
  325. package/src/interactive/view/view-overlay.ts +28 -3
  326. package/src/tools/ask-user.ts +43 -2
  327. package/src/tools/dispatch-plan.ts +17 -9
  328. package/src/tools/dispatch-scout.ts +1 -1
  329. package/src/tools/registry.ts +16 -0
  330. package/dist/chunk-AOCYTWAV.js +0 -449
  331. package/dist/chunk-HWUFFB6L.js +0 -83
  332. package/dist/chunk-R346GLFC.js +0 -31
  333. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The effective-customization graph behind `clio-coder config inspect`. This is the
3
3
  * "why is Clio behaving this way" surface: it answers what settings, context
4
- * files, rules, skills, prompts, agents, extensions, safety, memory, hooks, and
4
+ * files, rules, skills, prompts, agents, fleets, extensions, safety, memory, hooks, and
5
5
  * the operator profile loaded, from where, with what precedence, and what each
6
6
  * costs in context.
7
7
  *
@@ -12,8 +12,9 @@
12
12
  */
13
13
 
14
14
  import { existsSync, readFileSync } from "node:fs";
15
- import { basename } from "node:path";
15
+ import { basename, join } from "node:path";
16
16
  import type { ClioSettings } from "../core/config.js";
17
+ import { resolvePackageRoot } from "../core/package-root.js";
17
18
  import { readLayeredSettings, type SettingsOrigin, settingsSourceFor } from "../core/settings-layers.js";
18
19
  import { clioDataDir } from "../core/xdg.js";
19
20
  import {
@@ -39,7 +40,8 @@ export type CustomizationCategory =
39
40
  | "extension"
40
41
  | "skill-root"
41
42
  | "prompt-root"
42
- | "agents"
43
+ | "agent-root"
44
+ | "fleet-root"
43
45
  | "safety"
44
46
  | "memory";
45
47
 
@@ -265,18 +267,43 @@ function inspectResourceRoots(cwd: string, graph: CustomizationGraph): void {
265
267
  for (const [kind, category] of [
266
268
  ["skills", "skill-root"],
267
269
  ["prompts", "prompt-root"],
270
+ ["agents", "agent-root"],
271
+ ["fleets", "fleet-root"],
268
272
  ] as const) {
269
273
  try {
274
+ if (kind === "agents" || kind === "fleets") {
275
+ const builtinPath = join(
276
+ resolvePackageRoot(),
277
+ "src",
278
+ "domains",
279
+ "agents",
280
+ kind === "agents" ? "builtins" : "fleets",
281
+ );
282
+ graph.entries.push({
283
+ category,
284
+ id: `${kind}:builtin`,
285
+ scope: "builtin",
286
+ sourcePath: builtinPath,
287
+ trust: "trusted",
288
+ precedence: "layer",
289
+ reloadClass: "next-turn",
290
+ detail: { present: existsSync(builtinPath), source: "builtin", resourcePrecedence: 0 },
291
+ });
292
+ }
270
293
  for (const root of defaultScopedResourceRoots(kind, cwd)) {
271
294
  graph.entries.push({
272
295
  category,
273
296
  id: `${kind}:${root.scope}`,
274
- scope: root.scope,
297
+ scope: root.scope === "package" ? "extension" : root.scope,
275
298
  sourcePath: root.path,
276
299
  trust: root.scope === "package" ? "untrusted" : "trusted",
277
- precedence: "single",
300
+ precedence: "layer",
278
301
  reloadClass: "next-turn",
279
- detail: { present: existsSync(root.path), source: root.source },
302
+ detail: {
303
+ present: existsSync(root.path),
304
+ source: root.source,
305
+ ...(root.precedence === undefined ? {} : { resourcePrecedence: root.precedence }),
306
+ },
280
307
  });
281
308
  }
282
309
  } catch (err) {
package/src/cli/config.ts CHANGED
@@ -4,7 +4,7 @@ import { printError } from "./shared.js";
4
4
  const HELP = `clio-coder config inspect [--json]
5
5
 
6
6
  Print the effective-customization graph: what settings, context files, rules,
7
- skills, prompts, agents, extensions, safety, memory, hooks, and the operator
7
+ skills, prompts, agents, fleets, extensions, safety, memory, hooks, and the operator
8
8
  profile loaded, from where, with what precedence, trust, reload class, and
9
9
  context cost. Read-only; nothing is created.
10
10
 
@@ -0,0 +1,82 @@
1
+ import { lstatSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { resolveClioDirs } from "../core/xdg.js";
4
+ import type { DoctorFinding } from "../domains/lifecycle/doctor.js";
5
+
6
+ interface MeasuredEntry {
7
+ bytes: number;
8
+ error: string | null;
9
+ }
10
+
11
+ /** Report aggregate state usage and the largest top-level contributor. */
12
+ export function stateStorageFinding(stateDir = resolveClioDirs().state): DoctorFinding {
13
+ let names: string[];
14
+ try {
15
+ names = readdirSync(stateDir);
16
+ } catch (error) {
17
+ return {
18
+ ok: false,
19
+ name: "state storage",
20
+ detail: `${stateDir} could not be measured: ${error instanceof Error ? error.message : String(error)}`,
21
+ };
22
+ }
23
+
24
+ let total = 0;
25
+ let largest: { name: string; bytes: number } | null = null;
26
+ for (const name of names.sort()) {
27
+ const measured = measureEntry(join(stateDir, name));
28
+ if (measured.error !== null) {
29
+ return { ok: false, name: "state storage", detail: measured.error };
30
+ }
31
+ total += measured.bytes;
32
+ if (largest === null || measured.bytes > largest.bytes) largest = { name, bytes: measured.bytes };
33
+ }
34
+
35
+ return {
36
+ ok: true,
37
+ name: "state storage",
38
+ detail:
39
+ largest === null
40
+ ? `${formatBytes(total)} (${total.toLocaleString("en-US")} bytes); largest contributor none`
41
+ : `${formatBytes(total)} (${total.toLocaleString("en-US")} bytes); largest contributor ${largest.name} at ${formatBytes(largest.bytes)} (${largest.bytes.toLocaleString("en-US")} bytes)`,
42
+ };
43
+ }
44
+
45
+ function measureEntry(path: string): MeasuredEntry {
46
+ let stats: ReturnType<typeof lstatSync>;
47
+ try {
48
+ stats = lstatSync(path);
49
+ } catch (error) {
50
+ return {
51
+ bytes: 0,
52
+ error: `${path} could not be inspected: ${error instanceof Error ? error.message : String(error)}`,
53
+ };
54
+ }
55
+ if (!stats.isDirectory() || stats.isSymbolicLink()) return { bytes: stats.size, error: null };
56
+
57
+ let names: string[];
58
+ try {
59
+ names = readdirSync(path);
60
+ } catch (error) {
61
+ return { bytes: 0, error: `${path} could not be listed: ${error instanceof Error ? error.message : String(error)}` };
62
+ }
63
+ let bytes = 0;
64
+ for (const name of names) {
65
+ const child = measureEntry(join(path, name));
66
+ if (child.error !== null) return child;
67
+ bytes += child.bytes;
68
+ }
69
+ return { bytes, error: null };
70
+ }
71
+
72
+ function formatBytes(bytes: number): string {
73
+ if (bytes < 1024) return `${bytes} B`;
74
+ const units = ["KiB", "MiB", "GiB", "TiB"];
75
+ let value = bytes;
76
+ let unit = -1;
77
+ do {
78
+ value /= 1024;
79
+ unit += 1;
80
+ } while (value >= 1024 && unit < units.length - 1);
81
+ return `${value.toFixed(value >= 10 ? 1 : 2)} ${units[unit]}`;
82
+ }
package/src/cli/doctor.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  runDoctorModelChecks,
8
8
  runDoctorRuntimeChecks,
9
9
  } from "../domains/lifecycle/doctor.js";
10
+ import { stateStorageFinding } from "./doctor-state-size.js";
10
11
  import { printError } from "./shared.js";
11
12
 
12
13
  const HELP = `clio-coder doctor [--fix] [--json]
@@ -35,6 +36,7 @@ export async function runDoctorCommand(args: ReadonlyArray<string> = []): Promis
35
36
  // gets every check on the home it just built.
36
37
  const untouched = !fix && isUninitializedHome();
37
38
  const findings = runDoctor({ fix });
39
+ const storageChecks = untouched ? [] : [stateStorageFinding()];
38
40
  const runtimeChecks = await runDoctorRuntimeChecks();
39
41
  // Every model pointer is checked against what its target advertises, so a
40
42
  // placeholder id saved by configure is reported here and not on the first turn.
@@ -47,7 +49,7 @@ export async function runDoctorCommand(args: ReadonlyArray<string> = []): Promis
47
49
  // Fleet preflight probes each configured node over SSH and persists the
48
50
  // per-node eligibility verdicts dispatch placement enforces.
49
51
  const fleetChecks = untouched ? [] : await runDoctorFleetChecks();
50
- const all = [...findings, ...runtimeChecks, ...modelChecks, ...interopChecks, ...fleetChecks];
52
+ const all = [...findings, ...storageChecks, ...runtimeChecks, ...modelChecks, ...interopChecks, ...fleetChecks];
51
53
  const ok = all.every((f) => f.ok);
52
54
  if (json) {
53
55
  process.stdout.write(`${JSON.stringify({ ok, fix, findings: all }, null, 2)}\n`);
package/src/cli/eval.ts CHANGED
@@ -2,10 +2,10 @@ import { resolve } from "node:path";
2
2
  import { InvalidIdError } from "../core/safe-id.js";
3
3
  import { clioDataDir } from "../core/xdg.js";
4
4
  import { loadEvalArtifactV4, writeEvalArtifactV4 } from "../domains/eval/artifacts/store.js";
5
- import { compareEvalArtifactsV4, renderEvalComparisonV4 } from "../domains/eval/compare/compare.js";
6
- import { evaluateGate, renderGateFailure } from "../domains/eval/compare/gates.js";
5
+ import { compareEvalArtifactsV4 } from "../domains/eval/compare/compare.js";
6
+ import { evaluateGate, renderGateFailure, renderInformationalBudget } from "../domains/eval/compare/gates.js";
7
7
  import { loadThresholds } from "../domains/eval/compare/thresholds.js";
8
-
8
+ import { type EvalComparisonReportFormat, renderEvalComparisonReportV1 } from "../domains/eval/reports/comparison.js";
9
9
  import { renderEvalJsonReportV4 } from "../domains/eval/reports/json.js";
10
10
  import { renderEvalJunitReportV4 } from "../domains/eval/reports/junit.js";
11
11
  import { renderEvalMarkdownReportV4 } from "../domains/eval/reports/markdown.js";
@@ -22,10 +22,10 @@ const HELP = `clio-coder eval <command>
22
22
 
23
23
  Commands:
24
24
  clio-coder eval validate --suite <suite.yaml>
25
- clio-coder eval run --suite <suite.yaml> [--target <id>] [--model <id>] [--out <path>] [--clio-coder-entry <path>]
26
- clio-coder eval run --task-file <tasks.yaml> [--repeat <n>] [--out <path>] [--clio-coder-entry <path>]
25
+ clio-coder eval run --suite <suite.yaml> [--trials <n>] [--target <id>] [--model <id>] [--out <path>] [--clio-coder-entry <path>]
26
+ clio-coder eval run --task-file <tasks.yaml> [--repeat <n>] [--out <path>] [--clio-coder-entry <path>]
27
27
  clio-coder eval report <evalId> --format text|json|md|swe-jsonl|junit
28
- clio-coder eval compare <baselineEvalId> <candidateEvalId>
28
+ clio-coder eval compare <baselineEvalId> <candidateEvalId> [--metric <name>] [--format text|json|md|junit] [--allow-config-drift]
29
29
  clio-coder eval gate <candidateEvalId> --baseline <baselineEvalId> [--thresholds <file>]
30
30
  `;
31
31
 
@@ -37,6 +37,7 @@ interface ParsedEvalArgs {
37
37
  suite?: string;
38
38
  taskFile?: string;
39
39
  repeat: number;
40
+ trials?: number;
40
41
  target?: string;
41
42
  model?: string;
42
43
  out?: string;
@@ -44,13 +45,21 @@ interface ParsedEvalArgs {
44
45
  evalId?: string;
45
46
  format: EvalReportFormat;
46
47
  compareIds: string[];
48
+ metric?: string;
49
+ allowConfigDrift: boolean;
47
50
  baseline?: string;
48
51
  thresholds?: string;
49
52
  help: boolean;
50
53
  }
51
54
 
52
55
  function parseEvalArgs(args: ReadonlyArray<string>): ParsedEvalArgs {
53
- const parsed: ParsedEvalArgs = { repeat: 1, compareIds: [], format: "text", help: false };
56
+ const parsed: ParsedEvalArgs = {
57
+ repeat: 1,
58
+ compareIds: [],
59
+ format: "text",
60
+ allowConfigDrift: false,
61
+ help: false,
62
+ };
54
63
  for (let index = 0; index < args.length; index += 1) {
55
64
  const arg = args[index];
56
65
  if (arg === undefined) continue;
@@ -110,6 +119,11 @@ function parseEvalArgs(args: ReadonlyArray<string>): ParsedEvalArgs {
110
119
  index += 1;
111
120
  continue;
112
121
  }
122
+ if (arg === "--trials") {
123
+ parsed.trials = positiveInteger(requiredValue(args, index, "--trials"), "--trials");
124
+ index += 1;
125
+ continue;
126
+ }
113
127
  throw new Error(`unknown eval run argument: ${arg}`);
114
128
  }
115
129
  if (parsed.command === "report") {
@@ -125,6 +139,20 @@ function parseEvalArgs(args: ReadonlyArray<string>): ParsedEvalArgs {
125
139
  throw new Error(`unexpected eval report argument: ${arg}`);
126
140
  }
127
141
  if (parsed.command === "compare") {
142
+ if (arg === "--format") {
143
+ parsed.format = comparisonFormat(requiredValue(args, index, "--format"));
144
+ index += 1;
145
+ continue;
146
+ }
147
+ if (arg === "--metric") {
148
+ parsed.metric = requiredValue(args, index, "--metric");
149
+ index += 1;
150
+ continue;
151
+ }
152
+ if (arg === "--allow-config-drift") {
153
+ parsed.allowConfigDrift = true;
154
+ continue;
155
+ }
128
156
  if (!arg.startsWith("-")) {
129
157
  parsed.compareIds.push(arg);
130
158
  continue;
@@ -202,15 +230,21 @@ async function runEvalRun(parsed: ParsedEvalArgs): Promise<number> {
202
230
  const loaded =
203
231
  parsed.suite !== undefined
204
232
  ? await loadEvalSuiteFile(parsed.suite)
205
- : await loadV1TaskFileAsSuite(parsed.taskFile ?? "", parsed.repeat);
233
+ : await loadV1TaskFileAsSuite(parsed.taskFile ?? "", parsed.trials ?? parsed.repeat);
206
234
  const resolveOptions: { target?: string; model?: string } = {};
207
235
  if (parsed.target !== undefined) resolveOptions.target = parsed.target;
208
236
  if (parsed.model !== undefined) resolveOptions.model = parsed.model;
209
- const suite = resolveSuiteForRun(loaded.suite, resolveOptions);
237
+ const suite = resolveSuiteForRun(loaded.suite, {
238
+ ...resolveOptions,
239
+ ...(parsed.trials ? { trials: parsed.trials } : {}),
240
+ });
210
241
  // Runner commands execute with cwd inside prepared eval workspaces, so a
211
242
  // relative --clio-coder-entry must be pinned to the invoking directory here.
212
243
  const clioEntry = resolve(parsed.clioEntry ?? process.argv[1] ?? "dist/cli/index.js");
213
- const artifact = await runEvalSuiteV2({ ...loaded, suite }, { clioEntry });
244
+ const artifact = await runEvalSuiteV2(
245
+ { ...loaded, suite },
246
+ { clioEntry, freshWorkspaces: parsed.trials !== undefined },
247
+ );
214
248
  const artifactPath = await writeEvalArtifactV4(clioDataDir(), artifact, parsed.out);
215
249
  process.stdout.write(`${renderEvalTextReportV4(artifact)}artifact: ${artifactPath}\n`);
216
250
  // A suite that declares thresholds is gated by them here, on the artifact
@@ -221,6 +255,10 @@ async function runEvalRun(parsed: ParsedEvalArgs): Promise<number> {
221
255
  process.stdout.write(`gate: fail (${gate.failures.length} threshold failure)\n`);
222
256
  for (const failure of gate.failures) process.stdout.write(renderGateFailure(failure));
223
257
  }
258
+ if (gate !== null && gate.informational.length > 0) {
259
+ process.stdout.write(`informational budgets: ${gate.informational.length} notice\n`);
260
+ for (const finding of gate.informational) process.stdout.write(renderInformationalBudget(finding));
261
+ }
224
262
  return artifact.summary.failed === 0 && (gate === null || gate.pass) ? 0 : 1;
225
263
  } catch (error) {
226
264
  return handleEvalLoadError(error, 1);
@@ -246,8 +284,12 @@ async function runEvalCompareCommand(parsed: ParsedEvalArgs): Promise<number> {
246
284
  const dataDir = clioDataDir();
247
285
  const baseline = await loadEvalArtifactV4(dataDir, baselineEvalId);
248
286
  const candidate = await loadEvalArtifactV4(dataDir, candidateEvalId);
249
- process.stdout.write(renderEvalComparisonV4(compareEvalArtifactsV4(baseline, candidate)));
250
- return 0;
287
+ const summary = compareEvalArtifactsV4(baseline, candidate, {
288
+ allowConfigDrift: parsed.allowConfigDrift,
289
+ ...(parsed.metric === undefined ? {} : { metric: parsed.metric }),
290
+ });
291
+ process.stdout.write(renderEvalComparisonReportV1(summary, parsed.format as EvalComparisonReportFormat));
292
+ return summary.hardGate.pass ? 0 : 1;
251
293
  } catch (error) {
252
294
  printError(error instanceof Error ? error.message : String(error));
253
295
  return error instanceof InvalidIdError ? 2 : 1;
@@ -258,18 +300,35 @@ async function runEvalGateCommand(parsed: ParsedEvalArgs): Promise<number> {
258
300
  try {
259
301
  const dataDir = clioDataDir();
260
302
  const candidate = await loadEvalArtifactV4(dataDir, parsed.evalId ?? "");
261
- await loadEvalArtifactV4(dataDir, parsed.baseline ?? "");
303
+ const baseline = await loadEvalArtifactV4(dataDir, parsed.baseline ?? "");
262
304
  const thresholds =
263
305
  parsed.thresholds === undefined
264
- ? { fail: [{ metric: "result.pass", op: "eq" as const, value: false }] }
306
+ ? { fail: [{ metric: "result.pass", op: "eq" as const, value: false }], informational: [] }
265
307
  : loadThresholds(parsed.thresholds);
266
308
  const gate = evaluateGate(candidate, thresholds);
267
- if (gate.pass) {
309
+ const comparison = compareEvalArtifactsV4(baseline, candidate);
310
+ if (gate.informational.length > 0) {
311
+ process.stdout.write(`informational budgets: ${gate.informational.length} notice\n`);
312
+ for (const finding of gate.informational) process.stdout.write(renderInformationalBudget(finding));
313
+ }
314
+ if (gate.pass && comparison.hardGate.pass) {
268
315
  process.stdout.write("gate: pass\n");
269
316
  return 0;
270
317
  }
271
- process.stdout.write(`gate: fail (${gate.failures.length} threshold failure)\n`);
318
+ const failureCount =
319
+ gate.failures.length + comparison.hardGate.failures.length + comparison.hardGate.envelopeFailures.length;
320
+ process.stdout.write(`gate: fail (${failureCount} hard failure)\n`);
272
321
  for (const failure of gate.failures) process.stdout.write(renderGateFailure(failure));
322
+ for (const failure of comparison.hardGate.failures) {
323
+ process.stdout.write(
324
+ ` ${failure.metric} [${failure.scenarioId}:${failure.role}:${failure.target.id}/${failure.target.model ?? "none"}]: ${failure.change} (hard behavioral gate)\n`,
325
+ );
326
+ }
327
+ for (const failure of comparison.hardGate.envelopeFailures) {
328
+ process.stdout.write(
329
+ ` execution envelope [${failure.scenarioId}:${failure.role}:${failure.target.id}/${failure.target.model ?? "none"}]: incomparable fields ${failure.fields.join(", ")}\n`,
330
+ );
331
+ }
273
332
  return 1;
274
333
  } catch (error) {
275
334
  printError(error instanceof Error ? error.message : String(error));
@@ -313,3 +372,8 @@ function reportFormat(value: string): EvalReportFormat {
313
372
  if (value === "text" || value === "json" || value === "md" || value === "swe-jsonl" || value === "junit") return value;
314
373
  throw new Error("--format must be text, json, md, swe-jsonl, or junit");
315
374
  }
375
+
376
+ function comparisonFormat(value: string): EvalComparisonReportFormat {
377
+ if (value === "text" || value === "json" || value === "md" || value === "junit") return value;
378
+ throw new Error("eval compare --format must be text, json, md, or junit");
379
+ }
@@ -91,6 +91,7 @@ function resourceSummary(extension: InstalledExtension): string {
91
91
  }
92
92
 
93
93
  function stateLabel(extension: InstalledExtension): string {
94
+ if (!extension.compatible) return "incompatible";
94
95
  if (!extension.enabled) return "disabled";
95
96
  if (!extension.effective) return `shadowed:${extension.overriddenBy ?? "higher"}`;
96
97
  return "active";
@@ -143,7 +144,10 @@ export function runExtensionsCommand(argv: ReadonlyArray<string>): number {
143
144
  case "list": {
144
145
  const items = listInstalledExtensions(process.cwd(), { ...scopeOptions, all: parsed.all });
145
146
  if (parsed.json) process.stdout.write(`${JSON.stringify({ extensions: items }, null, 2)}\n`);
146
- else printList(items);
147
+ else {
148
+ printList(items);
149
+ for (const item of items) printDiagnostics(item.diagnostics);
150
+ }
147
151
  return 0;
148
152
  }
149
153
  case "discover": {
package/src/cli/fleet.ts CHANGED
@@ -47,8 +47,8 @@ import {
47
47
  type RunToolBudgetEnvelope,
48
48
  } from "../domains/dispatch/budget-envelope.js";
49
49
  import { type CapacityDrain, capacityDrain, setCapacityDraining } from "../domains/dispatch/capacity-lease.js";
50
- import type { DispatchContract } from "../domains/dispatch/contract.js";
51
- import type { ExecutionPlan } from "../domains/dispatch/execution-plan.js";
50
+ import type { DispatchContract, DispatchRequest } from "../domains/dispatch/contract.js";
51
+ import { bindExecutionPlanEndpoints, type ExecutionPlan } from "../domains/dispatch/execution-plan.js";
52
52
  import { agentRoleFactsResolver, requestExecutionRole, withAttemptRole } from "../domains/dispatch/execution-role.js";
53
53
  import { compileFleetExecutionPlan } from "../domains/dispatch/fleet-plan.js";
54
54
  import {
@@ -67,7 +67,7 @@ import { ensureClioState, LifecycleDomainModule } from "../domains/lifecycle/ind
67
67
  import { MiddlewareDomainModule } from "../domains/middleware/index.js";
68
68
  import { ObservabilityDomainModule } from "../domains/observability/index.js";
69
69
  import { createPromptsDomainModule } from "../domains/prompts/index.js";
70
- import { ProvidersDomainModule } from "../domains/providers/index.js";
70
+ import { foregroundStreamUsage, ProvidersDomainModule } from "../domains/providers/index.js";
71
71
  import { ResourcesDomainModule } from "../domains/resources/index.js";
72
72
  import type { SafetyContract } from "../domains/safety/contract.js";
73
73
  import { SafetyDomainModule } from "../domains/safety/index.js";
@@ -372,6 +372,35 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
372
372
  await loaded.stop();
373
373
  return fail(err instanceof Error ? err.message : String(err));
374
374
  }
375
+ if (dispatch.preview !== undefined) {
376
+ try {
377
+ const foreground = foregroundStreamUsage();
378
+ const bindings: Record<string, { key: string; limit: number; foregroundHeld?: number } | undefined> = {};
379
+ for (const step of plan.steps) {
380
+ if (step.kind === "code") continue;
381
+ const request: DispatchRequest = {
382
+ agentId: step.agentId,
383
+ executionRole: step.executionRole,
384
+ task: step.task,
385
+ ...(step.scope === "readonly" ? { autonomy: "read-only" as const } : {}),
386
+ ...(step.target !== undefined ? { target: step.target } : {}),
387
+ ...(step.profile !== undefined ? { workerProfile: step.profile } : {}),
388
+ };
389
+ const endpoint = dispatch.preview(request).endpoint;
390
+ if (endpoint === undefined) continue;
391
+ const foregroundHeld = foreground[endpoint.key] ?? 0;
392
+ bindings[step.id] = {
393
+ key: endpoint.key,
394
+ limit: endpoint.limit,
395
+ ...(foregroundHeld > 0 ? { foregroundHeld } : {}),
396
+ };
397
+ }
398
+ plan = bindExecutionPlanEndpoints(plan, bindings);
399
+ } catch (err) {
400
+ await loaded.stop();
401
+ return fail(`route preflight failed: ${err instanceof Error ? err.message : String(err)}`);
402
+ }
403
+ }
375
404
  try {
376
405
  preflightWriteBoundaries(plan, process.cwd());
377
406
  } catch (err) {
@@ -14,6 +14,7 @@ import { ConfigDomainModule } from "../domains/config/index.js";
14
14
  import { ensureClioState } from "../domains/lifecycle/index.js";
15
15
  import type { ProvidersContract, TargetStatus } from "../domains/providers/contract.js";
16
16
  import {
17
+ endpointCapacityForStatus,
17
18
  isDispatchEligibleRuntime,
18
19
  isOrchestratorEligibleRuntime,
19
20
  ProvidersDomainModule,
@@ -24,7 +25,7 @@ import type { CapabilityFlags } from "../domains/providers/types/capability-flag
24
25
  import type { RuntimeTier } from "../domains/providers/types/runtime-descriptor.js";
25
26
  import { runConfigureCommand, runTargetRemove, runTargetRename } from "./configure.js";
26
27
  import { printError, printOk } from "./shared.js";
27
- import { column, terminalColumns, truncate } from "./text-layout.js";
28
+ import { column, terminalColumns, truncate, wrapPlain } from "./text-layout.js";
28
29
 
29
30
  const HEADER: ReadonlyArray<string> = ["id", "tier", "runtime", "auth", "url", "model", "health", "caps", "notes"];
30
31
  type ProviderOutputTier = RuntimeTier | "unknown";
@@ -805,9 +806,11 @@ export interface TargetTableRow {
805
806
  health: string;
806
807
  caps: string;
807
808
  notes: string;
809
+ /** Full degraded-health reason, kept separately so a narrow table can repeat it below the row. */
810
+ diagnostic?: string;
808
811
  }
809
812
 
810
- type TargetTableColumn = keyof TargetTableRow;
813
+ type TargetTableColumn = Exclude<keyof TargetTableRow, "diagnostic">;
811
814
  type TargetTableWidths = Record<TargetTableColumn, number>;
812
815
 
813
816
  const TABLE_COLUMNS: ReadonlyArray<TargetTableColumn> = [
@@ -918,20 +921,35 @@ export function formatTargetTable(
918
921
  rows: ReadonlyArray<TargetTableRow>,
919
922
  width: number,
920
923
  paintHealth: (cell: string, row: TargetTableRow) => string = (cell) => cell,
921
- ): { header: string; rows: ReadonlyArray<string> } {
924
+ ): { header: string; rows: ReadonlyArray<string>; details: ReadonlyArray<ReadonlyArray<string>> } {
922
925
  const widths = tableWidths(rows, width);
923
926
  const line = (cells: ReadonlyArray<string>): string => cells.join(" ".repeat(TABLE_GAP)).trimEnd();
927
+ const formatted = rows.map((row) => {
928
+ const cells = TABLE_COLUMNS.map((key) => column(row[key], widths[key]));
929
+ const plain = line(cells);
930
+ const rendered =
931
+ plain.length > width
932
+ ? truncate(plain, width)
933
+ : line(cells.map((cell, index) => (TABLE_COLUMNS[index] === "health" ? paintHealth(cell, row) : cell)));
934
+ const diagnosticFits = !row.diagnostic || (plain.length <= width && widths.notes >= row.diagnostic.length);
935
+ const details = diagnosticFits ? [] : targetDiagnosticDetails(row.diagnostic ?? "", width);
936
+ return { rendered, details };
937
+ });
924
938
  return {
925
939
  header: truncate(line(TABLE_COLUMNS.map((key, index) => column(HEADER[index] ?? key, widths[key]))), width),
926
- rows: rows.map((row) => {
927
- const cells = TABLE_COLUMNS.map((key) => column(row[key], widths[key]));
928
- const plain = line(cells);
929
- if (plain.length > width) return truncate(plain, width);
930
- return line(cells.map((cell, index) => (TABLE_COLUMNS[index] === "health" ? paintHealth(cell, row) : cell)));
931
- }),
940
+ rows: formatted.map(({ rendered }) => rendered),
941
+ details: formatted.map(({ details }) => details),
932
942
  };
933
943
  }
934
944
 
945
+ /** Indented diagnostic text that retains every word when the table row cannot. */
946
+ function targetDiagnosticDetails(diagnostic: string, width: number): string[] {
947
+ const indent = 2;
948
+ return wrapPlain(`reason: ${diagnostic}`, Math.max(8, width - indent), indent).map((line, index) =>
949
+ index === 0 ? `${" ".repeat(indent)}${line}` : line,
950
+ );
951
+ }
952
+
935
953
  function renderTable(providers: ProvidersContract, entries: ReadonlyArray<TargetStatus>): void {
936
954
  const sorted = [...entries].sort(compareStatusByTier);
937
955
  const table = formatTargetTable(
@@ -950,10 +968,12 @@ function renderTable(providers: ProvidersContract, entries: ReadonlyArray<Target
950
968
  process.stdout.write(`${chalk.bold(table.header)}\n`);
951
969
  }
952
970
  process.stdout.write(`${table.rows[index] ?? ""}\n`);
971
+ for (const detail of table.details[index] ?? []) process.stdout.write(`${detail}\n`);
953
972
  }
954
973
  }
955
974
 
956
975
  export function targetTableRow(providers: ProvidersContract, status: TargetStatus): TargetTableRow {
976
+ const diagnostic = degradedHealthDiagnostic(status);
957
977
  return {
958
978
  id: status.target.id,
959
979
  tier: statusTier(status),
@@ -964,6 +984,7 @@ export function targetTableRow(providers: ProvidersContract, status: TargetStatu
964
984
  health: status.health.status,
965
985
  caps: capabilityBadges(status.capabilities),
966
986
  notes: formatNotes(status),
987
+ ...(diagnostic ? { diagnostic } : {}),
967
988
  };
968
989
  }
969
990
 
@@ -1044,22 +1065,29 @@ export function formatContextWindow(
1044
1065
  : `ctx ${window}`;
1045
1066
  }
1046
1067
 
1047
- function formatNotes(status: TargetStatus): string {
1068
+ export function formatNotes(status: TargetStatus): string {
1048
1069
  const parts: string[] = [];
1070
+ const diagnostic = degradedHealthDiagnostic(status);
1071
+ // The health diagnosis leads because gateway, context, and residency facts
1072
+ // are supplementary once the target says it cannot serve its default model.
1073
+ if (diagnostic) parts.push(diagnostic);
1074
+ const endpoint = endpointCapacityForStatus(status);
1075
+ if (endpoint) parts.push(`slots ${endpoint.limit}`);
1049
1076
  if (status.target.gateway) parts.push("gateway");
1050
1077
  if (status.runtime?.auth === "oauth") parts.push("oauth");
1051
1078
  if (status.runtime?.auth === "claude-cli") parts.push("claude-cli");
1052
1079
  if (status.capabilities.contextWindow > 0) parts.push(formatContextWindow(status));
1053
1080
  if (!status.available && status.reason) parts.push(status.reason);
1054
- // A reachable target that cannot serve its own default is degraded, and the
1055
- // row has to say why or the model column reads as a model that works.
1056
- if (status.health.status === "degraded" && status.health.lastError) parts.push(status.health.lastError);
1057
1081
  const residency = residentModelsSummary(status.discoveredModelStates);
1058
1082
  if (residency) parts.push(residency);
1059
1083
  if (status.probeNotes && status.probeNotes.length > 0) parts.push(`note: ${status.probeNotes.join("; ")}`);
1060
1084
  return parts.join(" ");
1061
1085
  }
1062
1086
 
1087
+ function degradedHealthDiagnostic(status: TargetStatus): string | null {
1088
+ return status.health.status === "degraded" && status.health.lastError ? status.health.lastError : null;
1089
+ }
1090
+
1063
1091
  function statusTier(status: TargetStatus): ProviderOutputTier {
1064
1092
  return status.runtime?.tier ?? "unknown";
1065
1093
  }
@@ -1120,6 +1148,7 @@ interface SerializedStatus {
1120
1148
  tier: ProviderOutputTier;
1121
1149
  detectedReasoning: boolean | null;
1122
1150
  reasoningCandidateModelId: string | null;
1151
+ endpointCapacity?: ReturnType<typeof endpointCapacityForStatus>;
1123
1152
  }
1124
1153
 
1125
1154
  function serializeStatus(
@@ -1141,6 +1170,8 @@ function serializeStatus(
1141
1170
  detectedReasoning: extras.detectedReasoning,
1142
1171
  reasoningCandidateModelId: extras.candidateModelId,
1143
1172
  };
1173
+ const endpointCapacity = endpointCapacityForStatus(status);
1174
+ if (endpointCapacity !== null) out.endpointCapacity = endpointCapacity;
1144
1175
  if (status.contextWindowProvenance !== undefined) {
1145
1176
  out.contextWindowProvenance = status.contextWindowProvenance;
1146
1177
  }