@iowarp/clio-coder 0.3.7 → 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 (400) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +13 -4
  3. package/dist/{acp-SK4MD6MM.js → acp-7LOELQFP.js} +13 -13
  4. package/dist/{agents-2FN2K6ME.js → agents-FIBG2SHA.js} +41 -37
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-QIYZWM5I.js → auth-OI4LIH2I.js} +31 -24
  7. package/dist/builtins-AD25UL3C.js +17 -0
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-EOOQZZDE.js → chunk-3DUR4WUA.js} +19 -19
  11. package/dist/{chunk-WHJYKASB.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-EBEFWSGL.js → chunk-3UUY7R3Z.js} +14 -10
  13. package/dist/{chunk-LADCF22A.js → chunk-3V5AYSEQ.js} +113 -54
  14. package/dist/{chunk-BMWK7ZIZ.js → chunk-465CC7FK.js} +16 -13
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-CEYBNUGC.js → chunk-4H6ULJ3H.js} +378 -36
  17. package/dist/{chunk-YTYFXUI3.js → chunk-4LJX2PUC.js} +9 -9
  18. package/dist/{chunk-DOOEX22V.js → chunk-56KB5IJP.js} +5 -5
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-TSHXZTOQ.js → chunk-5HFBWUMU.js} +23 -11
  21. package/dist/{chunk-5UJ6ECTS.js → chunk-5PVQ4SRS.js} +80 -8
  22. package/dist/{chunk-ZWLZP4ZT.js → chunk-5QKCQQ3E.js} +359 -17
  23. package/dist/{chunk-6M7VS3J3.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/chunk-774ILSRL.js +172 -0
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
  27. package/dist/{chunk-C4JBQ5SR.js → chunk-AD7Y7STJ.js} +6 -6
  28. package/dist/{chunk-GEYXPTRF.js → chunk-AEYBF3TB.js} +33 -12
  29. package/dist/{chunk-2SFS6XQE.js → chunk-AMKHQW3C.js} +3 -2
  30. package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
  31. package/dist/{chunk-MXI6J5JF.js → chunk-B5XRQOLB.js} +10 -10
  32. package/dist/{chunk-X2KV5FXT.js → chunk-BVDVID7E.js} +2 -2
  33. package/dist/{chunk-JNXPYBB4.js → chunk-CA42X6KT.js} +3 -3
  34. package/dist/{chunk-VREKEFLL.js → chunk-D73KXYPF.js} +3 -3
  35. package/dist/{chunk-JTSEDYVQ.js → chunk-DG4M6ZUE.js} +7 -7
  36. package/dist/{chunk-DQA7QLMD.js → chunk-EBOC7MT3.js} +10 -25
  37. package/dist/{chunk-KZ2H5X4G.js → chunk-ECUO3KDP.js} +129 -14
  38. package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
  39. package/dist/{chunk-YD734TPH.js → chunk-FALJGAWU.js} +2 -2
  40. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  41. package/dist/{chunk-XEGB6BCN.js → chunk-GAYUJ7LE.js} +68 -14
  42. package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
  43. package/dist/{chunk-IR4CFBFN.js → chunk-HAY4ZE2P.js} +12 -12
  44. package/dist/{chunk-UVDSQ6LW.js → chunk-HCBCAYZU.js} +74 -147
  45. package/dist/{chunk-4DWFMQDR.js → chunk-HJB5IUKP.js} +89 -145
  46. package/dist/{chunk-M4AKACEO.js → chunk-HKO36JWF.js} +33 -5
  47. package/dist/{chunk-KCMKRQX4.js → chunk-HPCTNZM2.js} +45 -82
  48. package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
  49. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  50. package/dist/chunk-JEQQR47K.js +3025 -0
  51. package/dist/{chunk-FO5ZOVUY.js → chunk-KV2AOLDF.js} +27 -7
  52. package/dist/chunk-LU7P4LHA.js +33 -0
  53. package/dist/{chunk-6TUKSZVF.js → chunk-LXPJXFM5.js} +11 -11
  54. package/dist/{chunk-VQNODYQ4.js → chunk-MIX5N5AC.js} +488 -3668
  55. package/dist/chunk-MLOK6ZOS.js +2888 -0
  56. package/dist/{chunk-ZZMN5OM4.js → chunk-MV2VUEJC.js} +2 -2
  57. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  58. package/dist/{chunk-OBMAI2DP.js → chunk-N3PBVRTZ.js} +12 -388
  59. package/dist/{chunk-WJHBC77E.js → chunk-N5XKWMDW.js} +17 -7
  60. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  61. package/dist/{chunk-UFQ3F4FW.js → chunk-NQ6UCCOD.js} +4 -4
  62. package/dist/chunk-NUGM5KR6.js +165 -0
  63. package/dist/{chunk-DMD2AGVS.js → chunk-NZU6YDNV.js} +20 -18
  64. package/dist/{chunk-WHGPSPT5.js → chunk-O6I4CIEU.js} +151 -13
  65. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  66. package/dist/{chunk-PD3MESLB.js → chunk-P3JGPQFL.js} +4 -4
  67. package/dist/{chunk-UHXRNZ2J.js → chunk-PNY46YEY.js} +23 -6
  68. package/dist/{chunk-THKY7CD7.js → chunk-PZ4I4JE2.js} +134 -29
  69. package/dist/{chunk-SROCI7ZU.js → chunk-QQ7EKM72.js} +5 -5
  70. package/dist/{chunk-QCTRSGHQ.js → chunk-R7LNVMCS.js} +91 -53
  71. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  72. package/dist/chunk-RKKLTLYB.js +45 -0
  73. package/dist/{chunk-OB5HIGJY.js → chunk-RKRLDWD3.js} +4 -1
  74. package/dist/{chunk-DJNLUABN.js → chunk-S4COXYBG.js} +588 -32
  75. package/dist/{chunk-3HAPLH5M.js → chunk-T3Z6VAAF.js} +172 -11
  76. package/dist/{chunk-FOT2FX5J.js → chunk-TD7UE2L5.js} +12 -10
  77. package/dist/{chunk-UUANF5CR.js → chunk-TEO2TLVN.js} +856 -967
  78. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  79. package/dist/{chunk-EELBMBT6.js → chunk-VKBMFOYV.js} +74 -15
  80. package/dist/chunk-VO2LKSTM.js +165 -0
  81. package/dist/{chunk-5C77SEEY.js → chunk-VPTUJU4P.js} +3 -3
  82. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  83. package/dist/{chunk-J7PIKKWC.js → chunk-WXCJ7VME.js} +8 -8
  84. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  85. package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
  86. package/dist/{chunk-AB4XIIVB.js → chunk-YKOFT37S.js} +6 -6
  87. package/dist/chunk-YSEHGPCT.js +127 -0
  88. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  89. package/dist/cli/index.js +32 -32
  90. package/dist/{clio-WBVQEBKO.js → clio-LT5V7SSZ.js} +9 -9
  91. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +5 -5
  92. package/dist/codewiki/build-worker.js +4 -4
  93. package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
  94. package/dist/{config-TRBL3RCF.js → config-RXS5T3JT.js} +98 -65
  95. package/dist/{configure-OLCVPHNM.js → configure-2WYWSCSD.js} +26 -22
  96. package/dist/{context-MJIJ6GOX.js → context-I3BTOTCS.js} +12 -12
  97. package/dist/{context-XEWE3MOJ.js → context-MVOORGMF.js} +54 -47
  98. package/dist/{context-WFPKQSM6.js → context-PALKKQYL.js} +28 -28
  99. package/dist/{context-clear-KNOS2JPB.js → context-clear-N2WOYZ2K.js} +53 -46
  100. package/dist/{context-index-SSR5ECNE.js → context-index-HNG3MOME.js} +6 -6
  101. package/dist/{context-working-set-EUXAZI6N.js → context-working-set-MIEVECVZ.js} +17 -18
  102. package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-VVA4SRRH.js} +90 -61
  103. package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
  104. package/dist/doctor-TWBWFK5V.js +165 -0
  105. package/dist/eval-IJ5VEZDJ.js +4483 -0
  106. package/dist/{evidence-JZNBUOQZ.js → evidence-L5APPXNV.js} +68 -61
  107. package/dist/{evolve-FJVC4KKI.js → evolve-RGNKFJ52.js} +47 -40
  108. package/dist/{extensions-IQL36S7K.js → extensions-7WYWUX5A.js} +13 -7
  109. package/dist/{fleet-BDKYJFCP.js → fleet-6CNVBZZP.js} +113 -76
  110. package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-L2SXSYEI.js} +10 -10
  111. package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-2J3OOIPO.js} +17 -15
  112. package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-CZRJ4JP5.js} +5 -6
  113. package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-C5RI6DP7.js} +20 -19
  114. package/dist/{init-LQUB5COQ.js → init-VBN2ACVA.js} +70 -63
  115. package/dist/{library-NJAHIGG4.js → library-JHGUMLY2.js} +22 -20
  116. package/dist/{memory-OG6HOYKM.js → memory-K4OQIYWG.js} +49 -42
  117. package/dist/{models-5ZG5XY7J.js → models-2NCZUWDD.js} +35 -29
  118. package/dist/{monitor-TJ7AMTGB.js → monitor-MMVTJABD.js} +64 -45
  119. package/dist/{orchestrator-WZYB54DM.js → orchestrator-ZKBPCHW6.js} +1971 -520
  120. package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
  121. package/dist/registry-LG64LTF4.js +11 -0
  122. package/dist/{reset-PXQT45IY.js → reset-DD5JGOY3.js} +11 -11
  123. package/dist/{run-FQ74YF62.js → run-QEGNX7FL.js} +89 -83
  124. package/dist/{share-FW7SVCL3.js → share-JKD3BQMW.js} +20 -18
  125. package/dist/{skills-7E7IRB3R.js → skills-LMQIKDOZ.js} +23 -21
  126. package/dist/{skills-eval-LI75W6OK.js → skills-eval-I7X2774U.js} +59 -52
  127. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  128. package/dist/support-I7LOJLIF.js +38 -0
  129. package/dist/{targets-4CIFKCTW.js → targets-RUSR6B5Z.js} +77 -42
  130. package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-QYVORFR4.js} +6 -4
  131. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  132. package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
  133. package/dist/{upgrade-K2HVIVMQ.js → upgrade-XANW3FXB.js} +29 -26
  134. package/dist/{usage-GTZELZQX.js → usage-4H7ZRXQT.js} +110 -61
  135. package/dist/{verifiers-RLAHT27O.js → verifiers-UZXNBZEB.js} +13 -13
  136. package/dist/{verify-BX3BRKH5.js → verify-BVKWTNDL.js} +9 -9
  137. package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-MY7WV2QI.js} +76 -69
  138. package/dist/worker/entry.js +69 -66
  139. package/docs/alcf-provider.md +1 -1
  140. package/docs/architecture.md +1 -1
  141. package/docs/artifact-versions.md +11 -5
  142. package/docs/built-in-agents.md +1 -1
  143. package/docs/capacity-and-scheduling.md +23 -2
  144. package/docs/commands-and-modes.md +2 -2
  145. package/docs/configuration-and-targets.md +37 -5
  146. package/docs/context-engine.md +63 -4
  147. package/docs/documentation-coverage.md +3 -3
  148. package/docs/documentation-guide.md +1 -1
  149. package/docs/environment-variables.md +2 -0
  150. package/docs/eval-runner.md +262 -11
  151. package/docs/evals-internal.md +72 -2
  152. package/docs/evidence-and-memory.md +77 -12
  153. package/docs/evolution.md +1 -1
  154. package/docs/extensions-and-sharing.md +3 -1
  155. package/docs/fleet-dispatch.md +34 -9
  156. package/docs/glossary.md +21 -1
  157. package/docs/installation-and-lifecycle.md +1 -1
  158. package/docs/middleware-and-components.md +1 -1
  159. package/docs/model-catalog.md +1 -1
  160. package/docs/observability.md +54 -3
  161. package/docs/proactive-memory.md +127 -14
  162. package/docs/prompt-envelope-and-tools.md +22 -2
  163. package/docs/provider-adapter-cookbook.md +1 -1
  164. package/docs/release-cut-checklist.md +60 -41
  165. package/docs/safety-model.md +1 -1
  166. package/docs/scientific-validation.md +1 -1
  167. package/docs/skills-marketplace.md +1 -1
  168. package/docs/tool-usage.md +1 -1
  169. package/docs/trace-store.md +1 -1
  170. package/docs/troubleshooting.md +87 -0
  171. package/docs/tui-design.md +1 -1
  172. package/docs/worker-dispatch-mechanics.md +1 -1
  173. package/package.json +2 -2
  174. package/src/cli/agents.ts +1 -1
  175. package/src/cli/argv.ts +5 -0
  176. package/src/cli/config-inspect.ts +33 -6
  177. package/src/cli/config.ts +1 -1
  178. package/src/cli/configure.ts +107 -23
  179. package/src/cli/doctor-state-size.ts +82 -0
  180. package/src/cli/doctor.ts +7 -1
  181. package/src/cli/eval.ts +80 -16
  182. package/src/cli/evidence.ts +30 -25
  183. package/src/cli/extensions.ts +5 -1
  184. package/src/cli/fleet-preflight.ts +2 -12
  185. package/src/cli/fleet.ts +32 -3
  186. package/src/cli/shared.ts +1 -0
  187. package/src/cli/targets.ts +45 -11
  188. package/src/cli/trace.ts +63 -4
  189. package/src/cli/usage.ts +63 -14
  190. package/src/cli/validate-model.ts +60 -5
  191. package/src/core/bus-events.ts +54 -1
  192. package/src/core/cache-telemetry.ts +42 -0
  193. package/src/core/commit-attribution.ts +4 -4
  194. package/src/core/config.ts +18 -0
  195. package/src/core/defaults.ts +36 -6
  196. package/src/core/endpoint-key.ts +27 -0
  197. package/src/core/path-boundary.ts +100 -0
  198. package/src/core/residency-target-key.ts +25 -0
  199. package/src/core/response-schema.ts +36 -2
  200. package/src/domains/agents/extension.ts +2 -11
  201. package/src/domains/agents/fleet-contract.ts +30 -12
  202. package/src/domains/agents/recipe.ts +7 -1
  203. package/src/domains/agents/registry.ts +73 -5
  204. package/src/domains/agents/result-contract.ts +128 -17
  205. package/src/domains/agents/write-boundary.ts +15 -50
  206. package/src/domains/config/classify.ts +3 -0
  207. package/src/domains/context/codewiki/coordinator.ts +12 -4
  208. package/src/domains/context/project-rules.ts +51 -1
  209. package/src/domains/dispatch/admission.ts +40 -3
  210. package/src/domains/dispatch/assignment-reconcile.ts +22 -5
  211. package/src/domains/dispatch/assignment-store.ts +151 -14
  212. package/src/domains/dispatch/capacity-lease.ts +98 -9
  213. package/src/domains/dispatch/contract.ts +26 -1
  214. package/src/domains/dispatch/delegation-plan.ts +2 -5
  215. package/src/domains/dispatch/execution-plan.ts +44 -4
  216. package/src/domains/dispatch/execution-role.ts +9 -1
  217. package/src/domains/dispatch/extension.ts +309 -96
  218. package/src/domains/dispatch/fleet-run.ts +78 -4
  219. package/src/domains/dispatch/gate-role-prompts.ts +38 -0
  220. package/src/domains/dispatch/heartbeat.ts +32 -8
  221. package/src/domains/dispatch/index.ts +6 -1
  222. package/src/domains/dispatch/intent-requirements.ts +40 -0
  223. package/src/domains/dispatch/intent.ts +84 -8
  224. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  225. package/src/domains/dispatch/path-scope.ts +370 -0
  226. package/src/domains/dispatch/receipt-integrity.ts +2 -1
  227. package/src/domains/dispatch/reservation-store.ts +116 -8
  228. package/src/domains/dispatch/state.ts +4 -0
  229. package/src/domains/dispatch/types.ts +14 -7
  230. package/src/domains/dispatch/validation.ts +6 -3
  231. package/src/domains/dispatch/worker-spawn.ts +25 -11
  232. package/src/domains/dispatch/write-boundary-enforcer.ts +62 -0
  233. package/src/domains/dispatch/write-boundary.ts +262 -22
  234. package/src/domains/eval/artifacts/store.ts +62 -0
  235. package/src/domains/eval/compare/behavioral.ts +224 -0
  236. package/src/domains/eval/compare/compare.ts +355 -2
  237. package/src/domains/eval/compare/envelope.ts +128 -0
  238. package/src/domains/eval/compare/gates.ts +24 -6
  239. package/src/domains/eval/compare/thresholds.ts +30 -3
  240. package/src/domains/eval/execution-provenance.ts +240 -0
  241. package/src/domains/eval/metrics/aggregate.ts +136 -0
  242. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  243. package/src/domains/eval/metrics/evidence.ts +79 -2
  244. package/src/domains/eval/metrics/tracked.ts +413 -0
  245. package/src/domains/eval/provenance.ts +117 -0
  246. package/src/domains/eval/reports/comparison.ts +128 -0
  247. package/src/domains/eval/reports/junit.ts +17 -3
  248. package/src/domains/eval/reports/markdown.ts +3 -3
  249. package/src/domains/eval/reports/text.ts +14 -0
  250. package/src/domains/eval/run-compare.ts +20 -0
  251. package/src/domains/eval/runners/clio-run.ts +139 -2
  252. package/src/domains/eval/runners/external-command.ts +28 -3
  253. package/src/domains/eval/schema/adapter.ts +111 -0
  254. package/src/domains/eval/schema/artifact.ts +20 -0
  255. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  256. package/src/domains/eval/schema/behavioral.ts +520 -0
  257. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  258. package/src/domains/eval/schema/serving.ts +74 -0
  259. package/src/domains/eval/schema/suite.ts +38 -8
  260. package/src/domains/eval/schema/validate.ts +58 -3
  261. package/src/domains/eval/schema/verdict.ts +237 -0
  262. package/src/domains/eval/suites/resolve.ts +2 -0
  263. package/src/domains/eval/suites/run.ts +264 -33
  264. package/src/domains/eval/verifiers/command.ts +2 -1
  265. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  266. package/src/domains/evidence/build.ts +68 -21
  267. package/src/domains/evidence/eval.ts +2 -12
  268. package/src/domains/evidence/findings-markdown.ts +33 -0
  269. package/src/domains/evidence/index.ts +21 -0
  270. package/src/domains/evidence/provenance.ts +46 -11
  271. package/src/domains/evidence/run-trust.ts +7 -113
  272. package/src/domains/evidence/trust-projection.ts +274 -0
  273. package/src/domains/evidence/trust-status.ts +145 -17
  274. package/src/domains/evidence/types.ts +4 -0
  275. package/src/domains/extensions/compatibility.ts +285 -0
  276. package/src/domains/extensions/discovery.ts +126 -4
  277. package/src/domains/extensions/resources.ts +21 -9
  278. package/src/domains/extensions/state.ts +18 -5
  279. package/src/domains/extensions/types.ts +6 -1
  280. package/src/domains/lifecycle/doctor.ts +209 -2
  281. package/src/domains/memory/index.ts +14 -0
  282. package/src/domains/memory/task-bank-promotion.ts +64 -0
  283. package/src/domains/memory/task-memory-policy.ts +77 -8
  284. package/src/domains/memory/task-memory-spend.ts +131 -0
  285. package/src/domains/memory/task-memory-status.ts +7 -0
  286. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  287. package/src/domains/middleware/index.ts +1 -0
  288. package/src/domains/middleware/memory-intervention.ts +69 -5
  289. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  290. package/src/domains/observability/background-memory-usage.ts +140 -0
  291. package/src/domains/observability/cost.ts +1 -1
  292. package/src/domains/observability/index.ts +7 -0
  293. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  294. package/src/domains/observability/trace-store.ts +192 -2
  295. package/src/domains/prompts/compiler.ts +100 -13
  296. package/src/domains/prompts/contract.ts +3 -5
  297. package/src/domains/providers/endpoint-capacity.ts +96 -0
  298. package/src/domains/providers/extension.ts +30 -2
  299. package/src/domains/providers/index.ts +10 -0
  300. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  301. package/src/domains/providers/runtime-resolution.ts +8 -1
  302. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  303. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  304. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  305. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  306. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  307. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  308. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  309. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  310. package/src/domains/providers/types/capability-flags.ts +2 -0
  311. package/src/domains/providers/types/target-descriptor.ts +2 -0
  312. package/src/domains/resources/common-loader.ts +3 -0
  313. package/src/domains/resources/prompts/loader.ts +184 -17
  314. package/src/domains/safety/call-target.ts +52 -0
  315. package/src/domains/safety/policy-engine.ts +5 -5
  316. package/src/domains/safety/run-effects.ts +96 -2
  317. package/src/domains/safety/scope.ts +7 -12
  318. package/src/domains/session/context-accounting.ts +52 -1
  319. package/src/domains/session/context-ledger.ts +37 -13
  320. package/src/domains/session/index.ts +6 -0
  321. package/src/domains/session/prompt-cache.ts +140 -0
  322. package/src/domains/session/prompt-manifest.ts +42 -0
  323. package/src/engine/acp/adapter.ts +18 -3
  324. package/src/engine/acp/server.ts +4 -1
  325. package/src/engine/ai.ts +35 -0
  326. package/src/engine/apis/llamacpp-residency.ts +55 -3
  327. package/src/engine/apis/lmstudio.ts +25 -5
  328. package/src/engine/apis/ollama-native.ts +2 -1
  329. package/src/engine/apis/openai-completions.ts +80 -17
  330. package/src/engine/apis/residency-lock.ts +3 -1
  331. package/src/engine/apis/residency.ts +34 -1
  332. package/src/engine/prompt-templates.ts +18 -1
  333. package/src/engine/provider-payload.ts +29 -1
  334. package/src/engine/worker-runtime.ts +6 -3
  335. package/src/entry/orchestrator.ts +176 -30
  336. package/src/interactive/chat-loop-messages.ts +26 -7
  337. package/src/interactive/chat-loop.ts +318 -41
  338. package/src/interactive/chat-panel.ts +62 -8
  339. package/src/interactive/clio-editor.ts +45 -8
  340. package/src/interactive/context-activity.ts +5 -1
  341. package/src/interactive/context-meter.ts +1 -1
  342. package/src/interactive/context-overlay.ts +40 -10
  343. package/src/interactive/cost-overlay.ts +64 -6
  344. package/src/interactive/dispatch-board.ts +127 -5
  345. package/src/interactive/fleet-run-preview.ts +41 -15
  346. package/src/interactive/handoff-round.ts +41 -2
  347. package/src/interactive/interactive-application.ts +24 -1
  348. package/src/interactive/interactive-event-projection.ts +14 -0
  349. package/src/interactive/interactive-input-runtime.ts +8 -0
  350. package/src/interactive/interactive-presentation.ts +4 -0
  351. package/src/interactive/interactive-shell.ts +20 -17
  352. package/src/interactive/interactive-slash-runtime.ts +27 -4
  353. package/src/interactive/memory-overlay.ts +8 -0
  354. package/src/interactive/mutation-preview.ts +295 -0
  355. package/src/interactive/overlay-general-openers.ts +16 -0
  356. package/src/interactive/overlay-key-routing.ts +38 -0
  357. package/src/interactive/overlay-lifecycle.ts +38 -5
  358. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  359. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  360. package/src/interactive/overlays/ask-user.ts +91 -19
  361. package/src/interactive/overlays/help-reference.ts +4 -0
  362. package/src/interactive/overlays/prompts.ts +11 -1
  363. package/src/interactive/overlays/settings.ts +176 -48
  364. package/src/interactive/permission-hint.ts +34 -2
  365. package/src/interactive/permission-overlay.ts +159 -9
  366. package/src/interactive/prewarm.ts +197 -0
  367. package/src/interactive/render-trace.ts +162 -15
  368. package/src/interactive/renderers/tool-execution.ts +4 -0
  369. package/src/interactive/side-question.ts +58 -1
  370. package/src/interactive/slash-commands.ts +7 -2
  371. package/src/interactive/status/controller.ts +11 -0
  372. package/src/interactive/status/state-machine.ts +54 -2
  373. package/src/interactive/status/types.ts +7 -0
  374. package/src/interactive/terminal-lease.ts +2 -0
  375. package/src/interactive/turn-context.ts +299 -31
  376. package/src/interactive/turn-persistence.ts +14 -4
  377. package/src/interactive/turn-prewarm.ts +364 -0
  378. package/src/interactive/turn-queues.ts +7 -4
  379. package/src/interactive/turn-runtime.ts +8 -1
  380. package/src/interactive/turn-state.ts +23 -0
  381. package/src/interactive/view/artifacts.ts +42 -9
  382. package/src/interactive/view/view-overlay.ts +43 -6
  383. package/src/interactive/worker-receipts.ts +14 -2
  384. package/src/interactive/worker-stream.ts +8 -0
  385. package/src/tools/ask-user.ts +43 -2
  386. package/src/tools/dispatch-admission.ts +12 -13
  387. package/src/tools/dispatch-arguments.ts +27 -0
  388. package/src/tools/dispatch-plan.ts +46 -9
  389. package/src/tools/dispatch-runner.ts +48 -13
  390. package/src/tools/dispatch-scout.ts +1 -1
  391. package/src/tools/monitor.ts +13 -0
  392. package/src/tools/registry.ts +16 -0
  393. package/src/tools/worker-evidence.ts +19 -13
  394. package/src/worker/spec-contract.ts +2 -1
  395. package/dist/chunk-AOCYTWAV.js +0 -449
  396. package/dist/chunk-HWUFFB6L.js +0 -83
  397. package/dist/chunk-R346GLFC.js +0 -31
  398. package/dist/chunk-ZGH7FGS5.js +0 -1079
  399. package/dist/doctor-RN4YKO2X.js +0 -87
  400. package/dist/eval-RUBJVSNQ.js +0 -2557
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iowarp/clio-coder",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Coding agent for HPC and scientific-software developers, part of IOWarp's CLIO ecosystem of agentic science.",
5
5
  "keywords": [
6
6
  "ai",
@@ -92,6 +92,7 @@
92
92
  "//": "below here: a real model target, chosen with --target <id>; costs money and/or GPU time, never run in CI",
93
93
  "live:smoke": "node --import tsx benchmarks/internal/live-smoke.ts",
94
94
  "live:fleet-dispatch": "node --import tsx benchmarks/internal/live-fleet-dispatch.ts",
95
+ "live:release-residue": "node --import tsx tests/live/release-residue.ts",
95
96
  "live:tui": "node --import tsx benchmarks/internal/pty-drive.ts",
96
97
  "live:home": "node --import tsx benchmarks/internal/live-home.ts"
97
98
  },
@@ -107,7 +108,6 @@
107
108
  },
108
109
  "overrides": {
109
110
  "@anthropic-ai/sdk": "0.105.0",
110
- "ip-address": "10.2.0",
111
111
  "esbuild": "0.28.1"
112
112
  },
113
113
  "devDependencies": {
package/src/cli/agents.ts CHANGED
@@ -9,7 +9,7 @@ import { printError } from "./shared.js";
9
9
 
10
10
  const HELP = `clio-coder agents [--json] [--all]
11
11
 
12
- List user-facing agent specs from built-in, user, and project recipes.
12
+ List user-facing agent specs from built-in, extension, user, and project recipes.
13
13
 
14
14
  Flags:
15
15
  --json emit specs as JSON instead of the formatted table
package/src/cli/argv.ts CHANGED
@@ -15,6 +15,11 @@ export function printError(message: string, detail?: string): void {
15
15
  if (detail) process.stderr.write(` ${detail}\n`);
16
16
  }
17
17
 
18
+ /** An expected condition worth a line on stderr that is neither an error nor a success. */
19
+ export function printNote(message: string): void {
20
+ process.stderr.write(`${chalk.yellow("note:")} ${message}\n`);
21
+ }
22
+
18
23
  export function printOk(message: string): void {
19
24
  process.stdout.write(`${chalk.green("ok:")} ${message}\n`);
20
25
  }
@@ -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
 
@@ -27,7 +27,12 @@ import { fingerprintNativeRuntime } from "../domains/providers/probe/fingerprint
27
27
  import { getRuntimeRegistry } from "../domains/providers/registry.js";
28
28
  import { registerBuiltinRuntimes } from "../domains/providers/runtimes/builtins.js";
29
29
  import { greetLmStudio } from "../domains/providers/runtimes/common/lmstudio-http.js";
30
- import type { ProbeContext, ProbeResult, RuntimeDescriptor } from "../domains/providers/types/runtime-descriptor.js";
30
+ import type {
31
+ ProbeContext,
32
+ ProbeModelStatus,
33
+ ProbeResult,
34
+ RuntimeDescriptor,
35
+ } from "../domains/providers/types/runtime-descriptor.js";
31
36
  import type { TargetDescriptor } from "../domains/providers/types/target-descriptor.js";
32
37
  import { registerClioOAuthProviders } from "../engine/oauth.js";
33
38
  import { ask, askYesNo } from "./ask.js";
@@ -44,7 +49,7 @@ import { createDelayedManualCodeInput } from "./oauth-manual-input.js";
44
49
  import { promptOAuthSelection } from "./oauth-select.js";
45
50
  import { credentialWriteFailed, printError, printOk, printPlaintextCredentialWarning } from "./shared.js";
46
51
  import { terminalColumns, wrapPlain } from "./text-layout.js";
47
- import { validateModelChoice } from "./validate-model.js";
52
+ import { type LiveModelInventory, validateModelChoice } from "./validate-model.js";
48
53
 
49
54
  const HELP = `clio-coder configure
50
55
 
@@ -70,7 +75,8 @@ Non-interactive flags:
70
75
  --agent-profile-model <id> model to use for --agent-profile
71
76
  --api-key-env <VAR> read API key from this env var at call time
72
77
  --api-key <literal> store API key in credentials.yaml
73
- --force allow a model outside the runtime catalog
78
+ --force save a model outside the runtime catalog, or one the
79
+ target does not advertise, without refusing
74
80
  --gateway mark the target as a gateway
75
81
  --lifecycle <user-managed|clio-managed>
76
82
  resident model lifecycle policy
@@ -378,19 +384,35 @@ function catalogContextWindowFor(runtime: RuntimeDescriptor, modelId: string): n
378
384
  return null;
379
385
  }
380
386
 
381
- function validateResolvedModel(runtimeId: string, modelId: string | undefined, force: boolean): boolean {
387
+ /**
388
+ * Check the resolved model against the catalog where there is one and against
389
+ * the target's own list where there is not. A runtime with no catalog used to
390
+ * pass any string here while the same command wrote the live list that ruled
391
+ * it out into `wireModels`, so the target saved as `ok` and failed on its first
392
+ * turn. Without a catalog or a live list there is nothing to check against, and
393
+ * the note says so rather than implying the id was verified.
394
+ */
395
+ function validateResolvedModel(
396
+ runtime: RuntimeDescriptor,
397
+ target: Pick<TargetDescriptor, "id" | "url">,
398
+ modelId: string | undefined,
399
+ force: boolean,
400
+ inventory: WireModelInventory,
401
+ ): boolean {
382
402
  if (!modelId) return true;
383
- const validation = validateModelChoice({
384
- runtimeId,
385
- modelId,
386
- knownModels: runtimeKnownModelsFor(runtimeId),
387
- force,
388
- });
403
+ const knownModels = runtimeKnownModelsFor(runtime.id);
404
+ const live = inventory.source === "probe" ? liveInventoryFor(target, inventory) : undefined;
405
+ const validation = validateModelChoice({ runtimeId: runtime.id, modelId, knownModels, live, force });
389
406
  if (!validation.ok) {
390
407
  process.stderr.write(`error: ${validation.reason}\n`);
391
408
  return false;
392
409
  }
393
410
  if (validation.warning) process.stderr.write(`warning: ${validation.warning}\n`);
411
+ if (knownModels.length === 0 && live === undefined && runtime.kind === "http") {
412
+ process.stderr.write(
413
+ `warning: could not verify model '${modelId}': ${target.url ?? `target '${target.id}'`} returned no model list; it is saved as written\n`,
414
+ );
415
+ }
394
416
  return true;
395
417
  }
396
418
 
@@ -412,10 +434,15 @@ function validateContextWindowOverride(
412
434
  return true;
413
435
  }
414
436
 
415
- async function runtimeProbe(runtime: RuntimeDescriptor, target: TargetDescriptor): Promise<ProbeResult | null> {
437
+ async function runtimeProbe(
438
+ runtime: RuntimeDescriptor,
439
+ target: TargetDescriptor,
440
+ authToken?: string,
441
+ ): Promise<ProbeResult | null> {
416
442
  if (typeof runtime.probe !== "function") return null;
417
443
  try {
418
- return await runtime.probe(target, await buildProbeContext(runtime, target));
444
+ const context = await buildProbeContext(runtime, target);
445
+ return await runtime.probe(target, authToken === undefined ? context : { ...context, authToken });
419
446
  } catch (err) {
420
447
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
421
448
  }
@@ -679,17 +706,69 @@ async function loginOAuthRuntime(rl: ReturnType<typeof createInterface>, runtime
679
706
  }
680
707
  }
681
708
 
709
+ /**
710
+ * The model ids a target can be configured with, and where they came from.
711
+ * `probe` is the only source that says anything about the server in front of
712
+ * us right now; `catalog` is static provider knowledge, and `existing` is the
713
+ * list a previous configure recorded, which the server may no longer match.
714
+ */
715
+ interface WireModelInventory {
716
+ models: string[];
717
+ source: "catalog" | "probe" | "existing" | "none";
718
+ /** Per-model load state when the probe reported it. */
719
+ modelStates?: Readonly<Record<string, ProbeModelStatus>> | undefined;
720
+ }
721
+
722
+ /**
723
+ * Every id the runtime would resolve. LM Studio lists a loaded model under its
724
+ * instance id and keeps the model key only in the state map, and the request
725
+ * path accepts either, so both count as advertised.
726
+ */
727
+ function advertisedModelIds(inventory: Pick<WireModelInventory, "models" | "modelStates">): string[] {
728
+ const ids = [...inventory.models];
729
+ for (const id of Object.keys(inventory.modelStates ?? {})) if (!ids.includes(id)) ids.push(id);
730
+ return ids;
731
+ }
732
+
733
+ function residentModelIds(modelStates: WireModelInventory["modelStates"]): string[] {
734
+ return Object.entries(modelStates ?? {})
735
+ .filter(([, status]) => status.state === "loaded" || status.state === "loading")
736
+ .map(([id]) => id);
737
+ }
738
+
739
+ function liveInventoryFor(
740
+ target: Pick<TargetDescriptor, "id" | "url">,
741
+ inventory: WireModelInventory,
742
+ ): LiveModelInventory {
743
+ return {
744
+ targetId: target.id,
745
+ url: target.url,
746
+ models: advertisedModelIds(inventory),
747
+ resident: residentModelIds(inventory.modelStates),
748
+ };
749
+ }
750
+
682
751
  async function resolveSupportedWireModels(
683
752
  runtime: RuntimeDescriptor,
684
753
  target: TargetDescriptor,
685
754
  existing?: TargetDescriptor,
686
755
  authToken?: string,
687
- ): Promise<string[]> {
756
+ ): Promise<WireModelInventory> {
688
757
  const known = listKnownModelsForRuntime(runtime.id);
689
- if (known.length > 0) return known;
690
- const discovered = runtime.kind === "http" ? await runtimeProbeModels(runtime, target, authToken) : [];
691
- if (discovered.length > 0) return discovered;
692
- return existing?.wireModels ? [...existing.wireModels] : [];
758
+ if (known.length > 0) return { models: known, source: "catalog" };
759
+ if (runtime.kind === "http") {
760
+ // The full probe carries load state alongside the ids; a runtime that
761
+ // lists models only through probeModels still gets its ids checked.
762
+ const probe = await runtimeProbe(runtime, target, authToken);
763
+ if (probe?.ok && probe.models && probe.models.length > 0) {
764
+ return { models: [...probe.models], source: "probe", modelStates: probe.modelStates };
765
+ }
766
+ const discovered = await runtimeProbeModels(runtime, target, authToken);
767
+ if (discovered.length > 0) return { models: discovered, source: "probe" };
768
+ }
769
+ if (existing?.wireModels && existing.wireModels.length > 0)
770
+ return { models: [...existing.wireModels], source: "existing" };
771
+ return { models: [], source: "none" };
693
772
  }
694
773
 
695
774
  function resolveModelChoice(
@@ -810,7 +889,8 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
810
889
  ...(args.reasoning !== undefined ? { reasoning: args.reasoning } : {}),
811
890
  ...(existing?.lmstudio ? { lmstudio: existing.lmstudio } : {}),
812
891
  });
813
- const wireModels = await resolveSupportedWireModels(runtime, seed, existing, args.apiKey);
892
+ const inventory = await resolveSupportedWireModels(runtime, seed, existing, args.apiKey);
893
+ const wireModels = inventory.models;
814
894
  const model =
815
895
  args.model ??
816
896
  existing?.defaultModel ??
@@ -820,7 +900,7 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
820
900
  refuseCatalogSeededModel(runtime, support);
821
901
  return 2;
822
902
  }
823
- if (!validateResolvedModel(runtime.id, model, args.force)) return 2;
903
+ if (!validateResolvedModel(runtime, seed, model, args.force, inventory)) return 2;
824
904
  if (!validateContextWindowOverride(runtime, model, args.contextWindow, args.force)) return 2;
825
905
  const descriptor = buildDescriptor(runtime, args.id, {
826
906
  ...(url !== undefined ? { url } : {}),
@@ -1192,7 +1272,10 @@ async function runInteractive(
1192
1272
  }
1193
1273
 
1194
1274
  let model: string | undefined = defaults.model;
1195
- let wireModels: string[] = existing?.wireModels ? [...existing.wireModels] : [];
1275
+ let inventory: WireModelInventory =
1276
+ existing?.wireModels && existing.wireModels.length > 0
1277
+ ? { models: [...existing.wireModels], source: "existing" }
1278
+ : { models: [], source: "none" };
1196
1279
  const tentative = buildDescriptor(runtime, targetId, {
1197
1280
  ...(url !== undefined ? { url } : {}),
1198
1281
  ...(apiKeyEnv !== undefined ? { apiKeyEnv } : {}),
@@ -1211,8 +1294,9 @@ async function runInteractive(
1211
1294
  });
1212
1295
 
1213
1296
  if (runtime.kind === "http") {
1214
- wireModels = await resolveSupportedWireModels(runtime, tentative, existing ?? undefined);
1297
+ inventory = await resolveSupportedWireModels(runtime, tentative, existing ?? undefined);
1215
1298
  }
1299
+ const wireModels = inventory.models;
1216
1300
  // The wizard shows the whole list, so a catalog-ordered runtime does not need
1217
1301
  // --model here the way the non-interactive path does. It does need to stop
1218
1302
  // offering the alphabetically first id as though it were the recommended one.
@@ -1234,11 +1318,11 @@ async function runInteractive(
1234
1318
  process.stdout.write(" a model is required: enter a number from the list or a model id.\n");
1235
1319
  continue;
1236
1320
  }
1237
- if (validateResolvedModel(runtime.id, model, defaults.force)) break;
1321
+ if (validateResolvedModel(runtime, tentative, model, defaults.force, inventory)) break;
1238
1322
  }
1239
1323
  } else {
1240
1324
  for (;;) {
1241
- if (model && validateResolvedModel(runtime.id, model, defaults.force)) break;
1325
+ if (model && validateResolvedModel(runtime, tentative, model, defaults.force, inventory)) break;
1242
1326
  const manual = await ask(rl, "Default model id (blank to leave empty)", model ?? "");
1243
1327
  if (manual === null) return 0;
1244
1328
  model = manual.length > 0 ? manual : undefined;
@@ -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
@@ -4,8 +4,10 @@ import {
4
4
  runDoctor,
5
5
  runDoctorFleetChecks,
6
6
  runDoctorInteropChecks,
7
+ runDoctorModelChecks,
7
8
  runDoctorRuntimeChecks,
8
9
  } from "../domains/lifecycle/doctor.js";
10
+ import { stateStorageFinding } from "./doctor-state-size.js";
9
11
  import { printError } from "./shared.js";
10
12
 
11
13
  const HELP = `clio-coder doctor [--fix] [--json]
@@ -34,7 +36,11 @@ export async function runDoctorCommand(args: ReadonlyArray<string> = []): Promis
34
36
  // gets every check on the home it just built.
35
37
  const untouched = !fix && isUninitializedHome();
36
38
  const findings = runDoctor({ fix });
39
+ const storageChecks = untouched ? [] : [stateStorageFinding()];
37
40
  const runtimeChecks = await runDoctorRuntimeChecks();
41
+ // Every model pointer is checked against what its target advertises, so a
42
+ // placeholder id saved by configure is reported here and not on the first turn.
43
+ const modelChecks = await runDoctorModelChecks();
38
44
  // The interop and fleet sweeps read the state and config roots through the
39
45
  // ensuring accessors, which create them, and there is no fleet or interop
40
46
  // state to inspect before Clio has ever written anything. On a home Clio has
@@ -43,7 +49,7 @@ export async function runDoctorCommand(args: ReadonlyArray<string> = []): Promis
43
49
  // Fleet preflight probes each configured node over SSH and persists the
44
50
  // per-node eligibility verdicts dispatch placement enforces.
45
51
  const fleetChecks = untouched ? [] : await runDoctorFleetChecks();
46
- const all = [...findings, ...runtimeChecks, ...interopChecks, ...fleetChecks];
52
+ const all = [...findings, ...storageChecks, ...runtimeChecks, ...modelChecks, ...interopChecks, ...fleetChecks];
47
53
  const ok = all.every((f) => f.ok);
48
54
  if (json) {
49
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
+ }