@iowarp/clio-coder 0.4.2 → 0.4.4

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 (523) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/CONTRIBUTING.md +86 -19
  3. package/README.md +35 -6
  4. package/dist/{acp-TMDQZDIG.js → acp-WNAYYF4F.js} +12 -13
  5. package/dist/{agents-5N5NG3XG.js → agents-3OKXHLOI.js} +60 -57
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-Z5CCBXKQ.js → auth-VKNNMGPU.js} +21 -19
  8. package/dist/{builtins-K6TNDT24.js → builtins-WGALA46I.js} +9 -4
  9. package/dist/{chunk-XE3PCIXH.js → chunk-23L32XTI.js} +12 -9
  10. package/dist/{chunk-I64IFBLB.js → chunk-25QBEXRS.js} +18 -11
  11. package/dist/{chunk-CDNVLKUX.js → chunk-26QSH3EJ.js} +13 -7
  12. package/dist/{chunk-QQLGQY2A.js → chunk-2ASED4PZ.js} +22 -22
  13. package/dist/{chunk-MCEPRMZW.js → chunk-2CU2H6KE.js} +2 -2
  14. package/dist/chunk-2DSOYNFC.js +108 -0
  15. package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
  16. package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
  17. package/dist/{chunk-O3YUNJZ2.js → chunk-2ZSONWVL.js} +82 -25
  18. package/dist/{chunk-2NHR3NAY.js → chunk-36CT5VVL.js} +331 -42
  19. package/dist/{chunk-2X4RYJTJ.js → chunk-3GY4F45V.js} +3 -3
  20. package/dist/{chunk-ZW55JB7N.js → chunk-3ODX73FK.js} +4 -6
  21. package/dist/{chunk-PBP4B7XR.js → chunk-3UNOLWNZ.js} +3 -3
  22. package/dist/{chunk-4JDLP6ZS.js → chunk-3UUXNFEX.js} +14 -10
  23. package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
  24. package/dist/{chunk-ZW4HH5JJ.js → chunk-4M6Z5QVF.js} +6 -6
  25. package/dist/{chunk-K6BSR66V.js → chunk-4NSRCOYP.js} +4 -1
  26. package/dist/{chunk-M2DAX4F6.js → chunk-4WR7VSYB.js} +2 -2
  27. package/dist/{chunk-FSP7CMNU.js → chunk-54X7T7DK.js} +61 -6
  28. package/dist/{chunk-54ODD65L.js → chunk-5636DCO5.js} +4 -4
  29. package/dist/chunk-57XXR6DR.js +3763 -0
  30. package/dist/{chunk-3KIPBMUA.js → chunk-5ICU3EUH.js} +2 -2
  31. package/dist/{chunk-77QIVUZB.js → chunk-5MEZN6CB.js} +4 -4
  32. package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
  33. package/dist/{chunk-YJISEZKC.js → chunk-5TUB6SLS.js} +6 -6
  34. package/dist/{chunk-IMXMHHMQ.js → chunk-6OSVSQL5.js} +341 -57
  35. package/dist/{chunk-Q4XWMHX6.js → chunk-6PAZTBPA.js} +14 -2
  36. package/dist/{chunk-FVDGR2ZL.js → chunk-6Q3CYFD3.js} +112 -39
  37. package/dist/{chunk-IDNA72AH.js → chunk-6QOTUPRG.js} +155 -36
  38. package/dist/{chunk-X7IARSHT.js → chunk-6UINWWS6.js} +16 -10
  39. package/dist/{chunk-CYZW7JHJ.js → chunk-72YIHOZQ.js} +9 -9
  40. package/dist/{chunk-IKSLQ4XV.js → chunk-75W7L2E2.js} +752 -861
  41. package/dist/{chunk-CRFOIAX3.js → chunk-7UGL4MB5.js} +6 -6
  42. package/dist/{chunk-HIICAHCJ.js → chunk-AUPNRN7C.js} +2 -2
  43. package/dist/{chunk-7BHIY2MW.js → chunk-BJVFZO5U.js} +8 -50
  44. package/dist/{chunk-B74PXLU7.js → chunk-CUSRQKPU.js} +65 -3
  45. package/dist/chunk-DQOVN6KV.js +386 -0
  46. package/dist/{chunk-E7GT7O5N.js → chunk-DT3LWJOB.js} +7 -4
  47. package/dist/chunk-DXKJURES.js +671 -0
  48. package/dist/{chunk-JBCS7CRR.js → chunk-EL24TAU4.js} +10 -10
  49. package/dist/{chunk-TPEQIQIE.js → chunk-ELWDPP3Y.js} +8 -8
  50. package/dist/{chunk-NDINPTJ4.js → chunk-ELZVTCGV.js} +5 -4
  51. package/dist/chunk-EXLD33WO.js +381 -0
  52. package/dist/chunk-FEAXX7B6.js +101 -0
  53. package/dist/{chunk-RLYRBIYQ.js → chunk-FFUPXJC4.js} +90 -331
  54. package/dist/{chunk-5PFYMY2V.js → chunk-FTMGRKEF.js} +2 -2
  55. package/dist/{chunk-34BHNEE3.js → chunk-GHS5EBTQ.js} +58 -7
  56. package/dist/{chunk-DYHAXKHD.js → chunk-GWZNEVM2.js} +12 -8
  57. package/dist/chunk-GX5WYQO4.js +59 -0
  58. package/dist/chunk-GYV6VZOC.js +26 -0
  59. package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
  60. package/dist/chunk-I2DWJ4GM.js +390 -0
  61. package/dist/{chunk-TXOTCRLG.js → chunk-I5FWO7L5.js} +5 -5
  62. package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
  63. package/dist/{chunk-XQRY4DTA.js → chunk-IGLP3ODT.js} +10 -10
  64. package/dist/chunk-IRXAATOX.js +539 -0
  65. package/dist/chunk-IXIY2H4R.js +44 -0
  66. package/dist/{chunk-SSEYRH53.js → chunk-IZXGRF7P.js} +92 -147
  67. package/dist/{chunk-5TSRNF4G.js → chunk-JCI2ROMZ.js} +164 -6
  68. package/dist/{chunk-JWJGP5DQ.js → chunk-JEIYHLOR.js} +7 -7
  69. package/dist/{chunk-F2I26BDK.js → chunk-JQLNNIKT.js} +4 -4
  70. package/dist/{chunk-BYMNWQ7O.js → chunk-JSD46VO2.js} +315 -63
  71. package/dist/{chunk-AK5XEFVZ.js → chunk-JT2RFCC5.js} +64 -14
  72. package/dist/{chunk-MCMZMDAC.js → chunk-K6T2ZAMZ.js} +168 -6
  73. package/dist/{chunk-PGF63K6I.js → chunk-KFV5L5SK.js} +73 -4
  74. package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
  75. package/dist/{chunk-PJX3WQUQ.js → chunk-LLXSDWXS.js} +3 -3
  76. package/dist/{chunk-DZAW46HP.js → chunk-LTIKRKFL.js} +3 -3
  77. package/dist/{chunk-DZEK6CJN.js → chunk-N56KALIC.js} +21 -21
  78. package/dist/{chunk-B7HM5Z7T.js → chunk-NAI6ZFCY.js} +9 -5
  79. package/dist/{chunk-I66ZTYNP.js → chunk-NRO2BJRH.js} +2656 -2213
  80. package/dist/{chunk-ZGNYYXQ6.js → chunk-NXIMQY5W.js} +3 -3
  81. package/dist/{chunk-IKOZFYBN.js → chunk-NXYCB2VD.js} +149 -106
  82. package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
  83. package/dist/{chunk-462T4EGZ.js → chunk-O5CVSAG5.js} +2 -2
  84. package/dist/chunk-ODGTEFFI.js +50 -0
  85. package/dist/{chunk-3F7VUY77.js → chunk-OEJSLEPW.js} +2 -2
  86. package/dist/{chunk-KKOJXO6R.js → chunk-OMQNJVKW.js} +4 -2
  87. package/dist/{chunk-5KW52TEP.js → chunk-Q4WO54TA.js} +132 -77
  88. package/dist/{chunk-W6NIE6OW.js → chunk-QUFRYSWI.js} +13 -7
  89. package/dist/{chunk-42FMPA75.js → chunk-QZWQA4DE.js} +2 -2
  90. package/dist/chunk-R6Q67RJH.js +134 -0
  91. package/dist/{chunk-W4YEMFBX.js → chunk-RAY4OVGZ.js} +3 -3
  92. package/dist/{chunk-ZNT2M6TG.js → chunk-RQCKCSRL.js} +17 -17
  93. package/dist/{chunk-LJID3DYZ.js → chunk-RXTN6AKH.js} +3 -3
  94. package/dist/{chunk-P75RZCJW.js → chunk-RZDWV63N.js} +3 -3
  95. package/dist/{chunk-UH632ZYL.js → chunk-S6PYF2XF.js} +2 -2
  96. package/dist/{chunk-HJWWJ6IL.js → chunk-TOIVGRUX.js} +17 -5
  97. package/dist/{chunk-HLAFFSEK.js → chunk-TQAHXW6Y.js} +2 -2
  98. package/dist/{chunk-JIEGK6UF.js → chunk-U6TMQNSI.js} +48 -4
  99. package/dist/{chunk-2HFQNRV3.js → chunk-UEPWCCTY.js} +12 -12
  100. package/dist/chunk-UOIZ7DA4.js +41 -0
  101. package/dist/{chunk-UH347SHR.js → chunk-USR47QNF.js} +11 -11
  102. package/dist/{chunk-AZ4WMN4W.js → chunk-V6HJFQZE.js} +2 -2
  103. package/dist/chunk-V76WTFTW.js +318 -0
  104. package/dist/{chunk-NMJXSHBJ.js → chunk-W54I7H25.js} +2 -2
  105. package/dist/{chunk-KPXDY6QF.js → chunk-XRZT5WY5.js} +2 -2
  106. package/dist/{chunk-UBRFI4HS.js → chunk-XULDXHTN.js} +142 -50
  107. package/dist/chunk-XXYSBZIQ.js +283 -0
  108. package/dist/{chunk-HKMD33FO.js → chunk-Y55JBDO5.js} +405 -122
  109. package/dist/{chunk-XOXV5GKE.js → chunk-YD5GIKET.js} +17 -8
  110. package/dist/{chunk-XGDPUNND.js → chunk-YECAMM3D.js} +2 -2
  111. package/dist/{chunk-BO7Y52RY.js → chunk-YNFKXPEC.js} +7 -7
  112. package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
  113. package/dist/{chunk-M2WXEHER.js → chunk-ZA4VCIGV.js} +2 -2
  114. package/dist/cli/index.js +42 -40
  115. package/dist/{clio-7VB377CC.js → clio-QLICPCF5.js} +7 -7
  116. package/dist/{code-nav-YVLCYA7V.js → code-nav-IJR2DBPR.js} +9 -9
  117. package/dist/{components-UBWCQSRW.js → components-2TGAI2RC.js} +5 -6
  118. package/dist/{config-4HVOS65E.js → config-IUA6OYNS.js} +88 -81
  119. package/dist/{configure-PIWO7B24.js → configure-VEPX4NMX.js} +26 -25
  120. package/dist/{context-KQYIWPWT.js → context-2DKHWH2T.js} +60 -45
  121. package/dist/{context-IYEHL3WQ.js → context-4MPR7WKB.js} +78 -69
  122. package/dist/{context-N6ZE3LGJ.js → context-BOYF5EJM.js} +15 -11
  123. package/dist/{context-clear-G4OGZJDS.js → context-clear-S4ZJCQUX.js} +73 -65
  124. package/dist/context-map-COB37XXN.js +505 -0
  125. package/dist/{context-working-set-BWLF6LJP.js → context-working-set-3I3FYX6Y.js} +18 -17
  126. package/dist/detail-A7JAVSIG.js +98 -0
  127. package/dist/{dispatch-runner-2QQAITS3.js → dispatch-runner-RJ5I2F2O.js} +99 -75
  128. package/dist/{docs-PD3EXDKU.js → docs-SPOV3BAN.js} +3 -5
  129. package/dist/{doctor-LHBD36VU.js → doctor-DKICC2SN.js} +71 -48
  130. package/dist/{eval-C45FYRJ6.js → eval-OQOQUDHK.js} +308 -146
  131. package/dist/{eval-inventory-6DEJPLBF.js → eval-inventory-Y6QRFOH5.js} +4 -4
  132. package/dist/{evidence-6SHONYAF.js → evidence-4DQ25GUQ.js} +79 -175
  133. package/dist/evidence-4F5USFKH.js +208 -0
  134. package/dist/{evolve-KRKMV72X.js → evolve-GSS52E5J.js} +71 -67
  135. package/dist/{extensions-KPZ2UHBB.js → extensions-G7MFLYHT.js} +8 -9
  136. package/dist/{fleet-IVTCKDHT.js → fleet-Q37YHHAQ.js} +126 -118
  137. package/dist/{fleet-commands-EDWL3IT7.js → fleet-commands-G7E4N7SM.js} +16 -13
  138. package/dist/{fleet-decisions-YP3YEFGK.js → fleet-decisions-O7M6QBA2.js} +9 -8
  139. package/dist/{fleet-graph-ZFWKHY2M.js → fleet-graph-TOUBW6OW.js} +21 -20
  140. package/dist/{fleet-inspect-FVUNCBML.js → fleet-inspect-SW33JJNI.js} +65 -60
  141. package/dist/{fleet-preflight-UN5XED4R.js → fleet-preflight-CV2655TW.js} +4 -5
  142. package/dist/{fleet-validate-XOWC4HSX.js → fleet-validate-KESZX2YH.js} +25 -24
  143. package/dist/{fleet-verify-UN3SODEL.js → fleet-verify-UQPMTVE3.js} +66 -61
  144. package/dist/{fleet-view-TWHJKCN6.js → fleet-view-MN2VG4MR.js} +65 -60
  145. package/dist/{init-T2QORQ3Y.js → init-PXEXQSBF.js} +90 -82
  146. package/dist/{interop-IN5I2A66.js → interop-ZG5T62U3.js} +12 -13
  147. package/dist/inventory-C26CFDRR.js +101 -0
  148. package/dist/{library-LSCATDLZ.js → library-B2W4N74O.js} +29 -29
  149. package/dist/{memory-HYOKAGGJ.js → memory-YCANYS5A.js} +73 -69
  150. package/dist/{models-2GPMFYCM.js → models-GERTU3YI.js} +51 -47
  151. package/dist/{monitor-E4ASVUJH.js → monitor-CPNIUULB.js} +74 -67
  152. package/dist/{orchestrator-DDMPR3PY.js → orchestrator-J4BSH4WQ.js} +1288 -1606
  153. package/dist/{panes-E3RUXOW5.js → panes-BOHAEGYC.js} +4 -4
  154. package/dist/{panes-IXKLOKA2.js → panes-NXSLDQZ2.js} +10 -11
  155. package/dist/{paths-L7LGY6RN.js → paths-VSUWNC22.js} +6 -7
  156. package/dist/reset-TNWTB5LU.js +343 -0
  157. package/dist/{resources-OTRSN34L.js → resources-4PXNMD5G.js} +29 -22
  158. package/dist/{run-5DEYH5QK.js → run-D6XJ34CN.js} +132 -132
  159. package/dist/{share-IHWTLO3M.js → share-2NWMJJEE.js} +27 -27
  160. package/dist/{skills-IYMXMKW4.js → skills-KR7WON5G.js} +40 -33
  161. package/dist/{skills-eval-DROHSJAR.js → skills-eval-O2ZNOLDS.js} +81 -77
  162. package/dist/{skills-inventory-D7X4L4ZX.js → skills-inventory-ZZOUBK7O.js} +23 -22
  163. package/dist/{slash-commands-QBM7UZ3B.js → slash-commands-ZXPJD64J.js} +47 -37
  164. package/dist/{steer-Z5DO23FJ.js → steer-XA25PSCS.js} +4 -4
  165. package/dist/{support-U7QOWY26.js → support-7EMVWYG2.js} +6 -6
  166. package/dist/{targets-P2FUC4IL.js → targets-OMH2XCSN.js} +50 -49
  167. package/dist/tasks-IPAGMEIX.js +36 -0
  168. package/dist/{terminal-lease-YREJ3JX2.js → terminal-lease-C2J3JYRE.js} +4 -4
  169. package/dist/{tools-5B7RO6MV.js → tools-EFFEAIDP.js} +8 -9
  170. package/dist/{trace-YMGMUM6A.js → trace-FXMXUZUF.js} +7 -7
  171. package/dist/uninstall-HALS6BLF.js +407 -0
  172. package/dist/upgrade-MS72RJEP.js +306 -0
  173. package/dist/{usage-ME5MPXGX.js → usage-NHG6MCJM.js} +162 -108
  174. package/dist/{verifiers-BVZ7IWOO.js → verifiers-7AUNVXDY.js} +155 -22
  175. package/dist/{verify-5K7ZKQFC.js → verify-FWYGPKMR.js} +14 -12
  176. package/dist/{web-fetch-MPARV2K7.js → web-fetch-V4FKSDAV.js} +4 -4
  177. package/dist/{wiki-generate-F5W5QTYY.js → wiki-generate-743CIGJW.js} +99 -90
  178. package/dist/{with-panes-BYOJCLAM.js → with-panes-BDQEWBRT.js} +10 -10
  179. package/dist/worker/entry.js +72 -68
  180. package/docs/README.md +3 -2
  181. package/docs/architecture/acp.md +17 -0
  182. package/docs/architecture/artifact-placement.md +1 -0
  183. package/docs/architecture/artifact-versions.md +2 -2
  184. package/docs/architecture/context-engine.md +4 -0
  185. package/docs/architecture/dispatch-typed-intent.md +1 -1
  186. package/docs/architecture/evidence-and-memory.md +1 -1
  187. package/docs/architecture/middleware-and-components.md +1 -1
  188. package/docs/architecture/model-catalog.md +21 -10
  189. package/docs/architecture/observability.md +19 -2
  190. package/docs/architecture/prompt-envelope-and-tools.md +17 -5
  191. package/docs/architecture/provider-adapter-cookbook.md +63 -0
  192. package/docs/architecture/safety-model.md +25 -22
  193. package/docs/architecture/tui-design.md +1 -1
  194. package/docs/guide/built-in-agents.md +25 -11
  195. package/docs/guide/commands-and-modes.md +18 -3
  196. package/docs/guide/configuration-and-targets.md +100 -10
  197. package/docs/guide/configuration-reference.md +17 -7
  198. package/docs/guide/environment-variables.md +4 -2
  199. package/docs/guide/installation-and-lifecycle.md +37 -4
  200. package/docs/guide/proactive-memory.md +66 -55
  201. package/docs/guide/skills-marketplace.md +18 -0
  202. package/docs/guide/tool-usage.md +78 -3
  203. package/docs/history/config-knobs-audit.md +2 -2
  204. package/docs/process/development-pipeline.md +40 -2
  205. package/docs/process/eval-runner.md +67 -3
  206. package/docs/process/git-commit-provenance.md +15 -0
  207. package/docs/process/release-cut-checklist.md +207 -0
  208. package/docs/process/scientific-validation.md +18 -17
  209. package/evals/behavioral-machinery-support.ts +1 -0
  210. package/evals/behavioral-machinery.yaml +1 -1
  211. package/evals/behavioral-model.yaml +3 -2
  212. package/package.json +2 -2
  213. package/skills/README.md +7 -5
  214. package/skills/coding/ast-grep/SKILL.md +101 -30
  215. package/skills/coding/ast-grep/evals.md +26 -0
  216. package/skills/coding/coding-standards/SKILL.md +47 -2
  217. package/skills/coding/coding-standards/evals.md +23 -0
  218. package/skills/coding/prototype/SKILL.md +87 -28
  219. package/skills/coding/prototype/evals.md +19 -0
  220. package/skills/coding/tdd/SKILL.md +80 -53
  221. package/skills/coding/tdd/evals.md +20 -0
  222. package/skills/context/context-handoff/SKILL.md +43 -2
  223. package/skills/context/context-handoff/evals.md +44 -0
  224. package/skills/context/context-prime/SKILL.md +45 -15
  225. package/skills/context/context-prime/evals.md +45 -0
  226. package/skills/git/branch-closeout/SKILL.md +132 -0
  227. package/skills/git/branch-closeout/evals.md +133 -0
  228. package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
  229. package/skills/git/file-ticket/SKILL.md +77 -63
  230. package/skills/git/file-ticket/assets/issue-template.md +22 -0
  231. package/skills/git/file-ticket/evals.md +31 -26
  232. package/skills/git/file-ticket/references/issue-discovery.md +49 -0
  233. package/skills/git/fix-issue/SKILL.md +87 -64
  234. package/skills/git/fix-issue/evals.md +35 -31
  235. package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
  236. package/skills/git/resolve-merge-conflicts/SKILL.md +100 -51
  237. package/skills/git/resolve-merge-conflicts/evals.md +52 -25
  238. package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
  239. package/skills/git/ship/SKILL.md +103 -67
  240. package/skills/git/ship/assets/pr-template.md +21 -0
  241. package/skills/git/ship/evals.md +44 -28
  242. package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
  243. package/skills/git/worktree-create/SKILL.md +80 -50
  244. package/skills/git/worktree-create/evals.md +40 -33
  245. package/skills/git/worktree-create/references/worktree-setup.md +62 -66
  246. package/skills/git/worktree-merge/SKILL.md +112 -65
  247. package/skills/git/worktree-merge/evals.md +42 -34
  248. package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
  249. package/skills/planning/archify/SKILL.md +196 -0
  250. package/skills/planning/archify/evals.md +65 -0
  251. package/skills/planning/architecture/SKILL.md +61 -12
  252. package/skills/planning/architecture/evals.md +65 -0
  253. package/skills/planning/backlog/SKILL.md +130 -14
  254. package/skills/planning/backlog/evals.md +142 -0
  255. package/skills/planning/prd/SKILL.md +47 -6
  256. package/skills/planning/prd/evals.md +54 -0
  257. package/skills/planning/product-intent/SKILL.md +57 -2
  258. package/skills/planning/product-intent/evals.md +70 -0
  259. package/skills/planning/tech-spec/SKILL.md +53 -2
  260. package/skills/planning/tech-spec/evals.md +73 -0
  261. package/skills/registry.yaml +58 -50
  262. package/skills/remote.yaml +13 -0
  263. package/skills/research/arxiv-literature/SKILL.md +76 -18
  264. package/skills/research/arxiv-literature/evals.md +50 -0
  265. package/skills/research/experiment-protocol/SKILL.md +20 -1
  266. package/skills/research/experiment-protocol/evals.md +23 -0
  267. package/skills/research/scientific-debugging/SKILL.md +24 -1
  268. package/skills/research/scientific-debugging/evals.md +18 -0
  269. package/skills/research/scientific-modernization/SKILL.md +26 -1
  270. package/skills/research/scientific-modernization/evals.md +27 -0
  271. package/skills/skill-marketplace.json +63 -28
  272. package/skills/workflow/cut-it/SKILL.md +64 -5
  273. package/skills/workflow/cut-it/evals.md +101 -0
  274. package/skills/workflow/design-council/SKILL.md +112 -27
  275. package/skills/workflow/design-council/evals.md +161 -0
  276. package/skills/workflow/grill-me/SKILL.md +85 -10
  277. package/skills/workflow/grill-me/evals.md +153 -0
  278. package/skills/workflow/workflow-distiller/SKILL.md +76 -17
  279. package/skills/workflow/workflow-distiller/evals.md +118 -0
  280. package/src/cli/args.ts +0 -8
  281. package/src/cli/configure-interop.ts +105 -13
  282. package/src/cli/configure-oauth.ts +57 -0
  283. package/src/cli/configure-onboarding.ts +980 -0
  284. package/src/cli/configure-target.ts +594 -0
  285. package/src/cli/configure.ts +1084 -529
  286. package/src/cli/context-map.ts +114 -0
  287. package/src/cli/context.ts +4 -0
  288. package/src/cli/doctor-state-size.ts +1 -12
  289. package/src/cli/doctor-validation-contract.ts +28 -0
  290. package/src/cli/doctor.ts +5 -0
  291. package/src/cli/evidence-detail.ts +1 -75
  292. package/src/cli/evidence-inventory.ts +1 -167
  293. package/src/cli/index.ts +3 -0
  294. package/src/cli/lifecycle-presenter.ts +436 -0
  295. package/src/cli/models.ts +10 -2
  296. package/src/cli/modes/print.ts +5 -1
  297. package/src/cli/reset.ts +228 -106
  298. package/src/cli/run.ts +7 -4
  299. package/src/cli/select.ts +664 -0
  300. package/src/cli/skills.ts +9 -2
  301. package/src/cli/targets.ts +3 -0
  302. package/src/cli/tasks.ts +84 -0
  303. package/src/cli/uninstall.ts +233 -165
  304. package/src/cli/upgrade.ts +210 -150
  305. package/src/cli/usage.ts +92 -27
  306. package/src/cli/validate-model.ts +3 -3
  307. package/src/cli/verifiers.ts +147 -1
  308. package/src/cli/wiki-generate.ts +1 -0
  309. package/src/core/commit-attribution.ts +41 -1
  310. package/src/core/config.ts +56 -0
  311. package/src/core/external-diagnostic.ts +44 -0
  312. package/src/core/gateway-routing.ts +157 -0
  313. package/src/core/git-commit-attribution.ts +46 -3
  314. package/src/core/run-overrides.ts +0 -5
  315. package/src/core/safe-exec.ts +17 -2
  316. package/src/core/skill-activation.ts +92 -2
  317. package/src/core/tool-names.ts +5 -2
  318. package/src/domains/agents/builtins/architect.md +1 -1
  319. package/src/domains/agents/builtins/coder.md +1 -1
  320. package/src/domains/agents/builtins/documenter.md +1 -1
  321. package/src/domains/agents/builtins/git-master.md +1 -1
  322. package/src/domains/agents/builtins/provenance.md +7 -7
  323. package/src/domains/agents/builtins/tester.md +1 -1
  324. package/src/domains/agents/builtins/verifier.md +2 -2
  325. package/src/domains/agents/builtins/wiki-writer.md +4 -3
  326. package/src/domains/agents/builtins/world-knowledge.md +31 -0
  327. package/src/domains/agents/catalog.ts +1 -1
  328. package/src/domains/agents/result-contract.ts +70 -0
  329. package/src/domains/context/extension.ts +31 -7
  330. package/src/domains/context/refresh.ts +3 -0
  331. package/src/domains/context/wiki/frontmatter.ts +5 -2
  332. package/src/domains/context/wiki/generate.ts +6 -0
  333. package/src/domains/context/wiki/map-seed.ts +589 -0
  334. package/src/domains/context/wiki/plan.ts +2 -2
  335. package/src/domains/context/wiki/prompts.ts +43 -0
  336. package/src/domains/dispatch/active-route-planner.ts +4 -0
  337. package/src/domains/dispatch/admission.ts +29 -0
  338. package/src/domains/dispatch/agent-candidates.ts +10 -0
  339. package/src/domains/dispatch/budget-envelope.ts +86 -1
  340. package/src/domains/dispatch/capability-match.ts +1 -0
  341. package/src/domains/dispatch/capacity-lease.ts +17 -0
  342. package/src/domains/dispatch/code-step.ts +11 -4
  343. package/src/domains/dispatch/contract.ts +42 -8
  344. package/src/domains/dispatch/execution-scheduler.ts +2 -0
  345. package/src/domains/dispatch/extension.ts +184 -56
  346. package/src/domains/dispatch/fleet-commit-attribution.ts +5 -0
  347. package/src/domains/dispatch/fleet-run.ts +1 -0
  348. package/src/domains/dispatch/host-verification.ts +114 -13
  349. package/src/domains/dispatch/intent.ts +28 -18
  350. package/src/domains/dispatch/orphan-recovery.ts +2 -0
  351. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  352. package/src/domains/dispatch/reservation-store.ts +5 -3
  353. package/src/domains/dispatch/state.ts +15 -2
  354. package/src/domains/dispatch/types.ts +20 -2
  355. package/src/domains/dispatch/worker-model-metadata.ts +38 -0
  356. package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
  357. package/src/domains/eval/metrics/token-stream.ts +201 -31
  358. package/src/domains/eval/metrics/tracked.ts +40 -4
  359. package/src/domains/eval/runners/clio-run.ts +17 -11
  360. package/src/domains/eval/runners/context-index.ts +2 -7
  361. package/src/domains/eval/runners/context-init.ts +3 -6
  362. package/src/domains/eval/runners/external-command.ts +29 -11
  363. package/src/domains/eval/schema/suite.ts +28 -0
  364. package/src/domains/eval/schema/verdict.ts +2 -2
  365. package/src/domains/eval/suites/resolve.ts +13 -1
  366. package/src/domains/eval/suites/run.ts +24 -3
  367. package/src/domains/evidence/build.ts +102 -15
  368. package/src/domains/evidence/detail.ts +69 -0
  369. package/src/domains/evidence/eval.ts +13 -1
  370. package/src/domains/evidence/finish-contract-map.ts +5 -1
  371. package/src/domains/evidence/inventory.ts +167 -0
  372. package/src/domains/evidence/store.ts +16 -0
  373. package/src/domains/evidence/types.ts +12 -0
  374. package/src/domains/extensions/resources.ts +7 -0
  375. package/src/domains/interop/registry.ts +6 -2
  376. package/src/domains/interop/types.ts +4 -0
  377. package/src/domains/lifecycle/migrations/index.ts +4 -0
  378. package/src/domains/memory/task-memory-policy.ts +70 -26
  379. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  380. package/src/domains/middleware/index.ts +0 -1
  381. package/src/domains/middleware/marketplace-offer.ts +22 -35
  382. package/src/domains/middleware/memory-intervention.ts +127 -32
  383. package/src/domains/middleware/memory-step-endpoint.ts +3 -2
  384. package/src/domains/middleware/runtime.ts +7 -3
  385. package/src/domains/middleware/skills-reminder.ts +31 -2
  386. package/src/domains/mux/detect.ts +3 -6
  387. package/src/domains/observability/accountability.ts +15 -1
  388. package/src/domains/observability/compaction-usage.ts +118 -0
  389. package/src/domains/observability/contract.ts +52 -7
  390. package/src/domains/observability/cost.ts +1 -1
  391. package/src/domains/observability/evidence-index.ts +10 -0
  392. package/src/domains/observability/extension.ts +15 -6
  393. package/src/domains/observability/out-of-turn-usage.ts +52 -21
  394. package/src/domains/observability/projection.ts +394 -45
  395. package/src/{interactive → domains/observability}/worker-progress.ts +3 -3
  396. package/src/domains/prompts/fragments/operating/contract.md +2 -0
  397. package/src/domains/prompts/fragments/wiki/page.md +8 -0
  398. package/src/domains/providers/contract.ts +4 -1
  399. package/src/domains/providers/extension.ts +40 -9
  400. package/src/domains/providers/model-capabilities.ts +9 -0
  401. package/src/domains/providers/model-discovery.ts +3 -4
  402. package/src/domains/providers/model-runtime-capabilities.ts +15 -5
  403. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +48 -26
  404. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
  405. package/src/domains/providers/runtimes/claude/claude-code.ts +9 -0
  406. package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
  407. package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
  408. package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
  409. package/src/domains/providers/support.ts +11 -5
  410. package/src/domains/providers/target-model-cache.ts +25 -2
  411. package/src/domains/providers/types/capability-flags.ts +2 -0
  412. package/src/domains/providers/types/runtime-descriptor.ts +20 -1
  413. package/src/domains/providers/types/target-descriptor.ts +19 -0
  414. package/src/domains/resources/index.ts +3 -0
  415. package/src/domains/resources/skills/install.ts +72 -7
  416. package/src/domains/resources/skills/loader.ts +7 -0
  417. package/src/domains/resources/skills/marketplace.ts +63 -11
  418. package/src/domains/safety/action-classifier.ts +7 -0
  419. package/src/domains/safety/autonomy.ts +15 -0
  420. package/src/domains/safety/default-path-policy.ts +2 -0
  421. package/src/domains/safety/finish-contract-registration.ts +29 -14
  422. package/src/domains/safety/finish-contract.ts +252 -40
  423. package/src/domains/safety/index.ts +21 -1
  424. package/src/domains/safety/path-policy.ts +1 -1
  425. package/src/domains/safety/policy-engine.ts +60 -17
  426. package/src/domains/safety/protected-artifacts.ts +191 -88
  427. package/src/domains/safety/rigor.ts +53 -39
  428. package/src/domains/safety/run-effects.ts +2 -22
  429. package/src/domains/safety/skill-authority.ts +55 -0
  430. package/src/domains/safety/validation-contract.ts +388 -0
  431. package/src/domains/session/archive-readers.ts +10 -1
  432. package/src/domains/session/compaction/compact.ts +72 -22
  433. package/src/domains/session/decision-board.ts +101 -2
  434. package/src/domains/session/entries.ts +50 -7
  435. package/src/domains/session/extension.ts +4 -4
  436. package/src/domains/session/handoff.ts +2 -1
  437. package/src/domains/session/manager.ts +2 -3
  438. package/src/domains/session/task-board.ts +14 -1
  439. package/src/domains/session/tree/fork.ts +1 -2
  440. package/src/domains/session/tree/navigator.ts +1 -1
  441. package/src/domains/session/usage.ts +3 -3
  442. package/src/domains/user-tasks/acceptance.ts +56 -0
  443. package/src/domains/user-tasks/active-acceptance.ts +40 -0
  444. package/src/domains/user-tasks/store.ts +34 -3
  445. package/src/engine/acp/adapter.ts +24 -6
  446. package/src/engine/acp/server.ts +21 -4
  447. package/src/engine/acp/transport.ts +53 -8
  448. package/src/engine/acp/types.ts +4 -0
  449. package/src/engine/agent.ts +13 -3
  450. package/src/engine/ai.ts +26 -8
  451. package/src/engine/antigravity/subprocess-runtime.ts +386 -120
  452. package/src/engine/api-registry.ts +3 -0
  453. package/src/engine/apis/ollama-native.ts +15 -0
  454. package/src/engine/apis/openai-completions.ts +117 -14
  455. package/src/engine/claude/subprocess-runtime.ts +107 -60
  456. package/src/engine/external-subprocess.ts +122 -6
  457. package/src/entry/background-model-metadata.ts +18 -0
  458. package/src/entry/compaction-prompt.ts +57 -0
  459. package/src/entry/orchestrator.ts +416 -218
  460. package/src/entry/task-memory-lifecycle.ts +35 -0
  461. package/src/interactive/chat-loop-messages.ts +13 -4
  462. package/src/interactive/chat-loop.ts +65 -2
  463. package/src/interactive/chat-renderer.ts +1 -0
  464. package/src/interactive/cost-overlay.ts +26 -2
  465. package/src/interactive/dispatch-board.ts +46 -717
  466. package/src/interactive/fleet-run-preview.ts +2 -1
  467. package/src/interactive/interactive-application.ts +3 -2
  468. package/src/interactive/interactive-presentation.ts +55 -12
  469. package/src/interactive/interactive-slash-runtime.ts +4 -2
  470. package/src/interactive/oracle.ts +5 -2
  471. package/src/interactive/overlays/fleet-run-approval.ts +3 -2
  472. package/src/interactive/overlays/message-picker.ts +2 -2
  473. package/src/interactive/overlays/settings.ts +2 -2
  474. package/src/interactive/overlays/tree-selector.ts +2 -2
  475. package/src/interactive/renderers/branch-summary.ts +1 -1
  476. package/src/interactive/renderers/worker-entry.ts +32 -0
  477. package/src/interactive/slash-autocomplete.ts +4 -6
  478. package/src/interactive/slash-commands.ts +49 -45
  479. package/src/interactive/slash-spec.ts +28 -0
  480. package/src/interactive/theme/labels.ts +19 -13
  481. package/src/interactive/turn-context.ts +9 -5
  482. package/src/interactive/turn-recovery.ts +8 -0
  483. package/src/interactive/turn-runtime.ts +27 -11
  484. package/src/interactive/turn-state.ts +7 -0
  485. package/src/interactive/view/artifacts.ts +2 -0
  486. package/src/interactive/worker-receipts.ts +1 -0
  487. package/src/interactive/worker-stream.ts +13 -4
  488. package/src/tools/bootstrap.ts +4 -0
  489. package/src/tools/builtin-tool-catalog.ts +31 -0
  490. package/src/tools/compete-worktrees.ts +7 -1
  491. package/src/tools/context/index.ts +30 -9
  492. package/src/tools/core-bootstrap.ts +16 -0
  493. package/src/tools/decide.ts +136 -0
  494. package/src/tools/dispatch-admission.ts +21 -0
  495. package/src/tools/dispatch-arguments.ts +1 -0
  496. package/src/tools/dispatch-event-text.ts +10 -0
  497. package/src/tools/dispatch-plan.ts +6 -2
  498. package/src/tools/dispatch-runner.ts +27 -1
  499. package/src/tools/dispatch-types.ts +6 -0
  500. package/src/tools/evidence.ts +96 -0
  501. package/src/tools/limitation.ts +76 -0
  502. package/src/tools/policy.ts +9 -0
  503. package/src/tools/presentation.ts +3 -0
  504. package/src/tools/registry.ts +11 -5
  505. package/src/tools/result-shaping.ts +17 -5
  506. package/src/tools/task-worktree.ts +13 -3
  507. package/src/tools/tasks.ts +10 -1
  508. package/src/tools/verify/authoring.ts +170 -83
  509. package/src/tools/verify/catalog.ts +122 -5
  510. package/src/tools/verify/index.ts +2 -1
  511. package/src/tools/verify/numeric.ts +298 -0
  512. package/src/tools/verify/perf.ts +143 -0
  513. package/src/tools/verify/scripts.ts +229 -2
  514. package/src/tools/worker-evidence.ts +3 -1
  515. package/src/worker/spec-contract.ts +4 -0
  516. package/dist/chunk-2Z2IKEXI.js +0 -1554
  517. package/dist/chunk-RVG5JXAL.js +0 -41
  518. package/dist/chunk-T56WDKA5.js +0 -183
  519. package/dist/chunk-VN3SHNBN.js +0 -313
  520. package/dist/chunk-VPKWYKEY.js +0 -169
  521. package/dist/reset-OAQP3W4O.js +0 -230
  522. package/dist/uninstall-N34PCTGJ.js +0 -331
  523. package/dist/upgrade-PXK3S2YM.js +0 -325
@@ -1,14 +1,17 @@
1
+ import { extractReasoningTokens } from "../../session/context-accounting.js";
1
2
  import type { EvalTokenMetricsV4 } from "../schema/artifact.js";
2
3
 
3
4
  export interface EvalTokenStreamUsage {
4
5
  /**
5
6
  * True when the stream carried at least one completed assistant message
6
- * with provider usage. False means this runner observed no token
7
+ * with observed token facts. False means this runner observed no token
7
8
  * accounting at all, which is not the same as a run that cost nothing.
8
9
  */
9
10
  measured: boolean;
11
+ /** Known token subtotals; missing failed-call coverage is reported separately. */
10
12
  tokens: EvalTokenMetricsV4;
11
13
  costUsd: number;
14
+ provider: EvalProviderObservation;
12
15
  }
13
16
 
14
17
  export interface EvalTokenUsageFold {
@@ -21,6 +24,7 @@ export const UNMEASURED_TOKEN_USAGE: EvalTokenStreamUsage = {
21
24
  measured: false,
22
25
  tokens: { input: 0, output: 0, total: 0, cacheRead: 0, cacheWrite: 0 },
23
26
  costUsd: 0,
27
+ provider: emptyProviderObservation(),
24
28
  };
25
29
 
26
30
  /**
@@ -30,8 +34,12 @@ export const UNMEASURED_TOKEN_USAGE: EvalTokenStreamUsage = {
30
34
  * carrying a completed message's usage exactly once; `turn_end` republishes
31
35
  * the same assistant message and `agent_end` republishes its segment's
32
36
  * summary, so counting those too would multiply a run's reported cost. Every
33
- * message counts, because a headless turn spans several agent segments and the
34
- * run's cost is their sum.
37
+ * reported call counts, including known errored-call spend: a headless turn
38
+ * spans several segments and its cost is their sum. Provider facts and the
39
+ * errored known share are folded here too, from the same events before truncation.
40
+ * Failed all-zero usage is ambiguous (adapters synthesize it), so it cannot
41
+ * establish measured usage. Unknown failed spend is retained as missing coverage,
42
+ * never subtracted from inclusive totals or used to change task pass policy.
35
43
  *
36
44
  * Folding as the stream arrives is what makes the count trustworthy: the
37
45
  * operator-facing stdout artifact keeps only a bounded head and tail, so a
@@ -42,6 +50,7 @@ export function createTokenUsageFold(): EvalTokenUsageFold {
42
50
  let costUsd = 0;
43
51
  let measured = false;
44
52
  let pending = "";
53
+ const provider = emptyProviderObservation();
45
54
 
46
55
  const consume = (line: string): void => {
47
56
  if (line.trim().length === 0) return;
@@ -51,23 +60,16 @@ export function createTokenUsageFold(): EvalTokenUsageFold {
51
60
  } catch {
52
61
  return;
53
62
  }
54
- if (!isRecord(event) || event.type !== "message_end") return;
55
- const message = isRecord(event.message) ? event.message : undefined;
56
- if (message === undefined || message.role !== "assistant") return;
57
- const usage = isRecord(message.usage) ? message.usage : undefined;
58
- if (usage === undefined) return;
59
- measured = true;
60
- const input = numberField(usage, "input");
61
- const output = numberField(usage, "output");
62
- const cacheRead = numberField(usage, "cacheRead");
63
- const cacheWrite = numberField(usage, "cacheWrite");
64
- const totalTokens = numberField(usage, "totalTokens");
65
- tokens.input += input;
66
- tokens.output += output;
67
- tokens.cacheRead += cacheRead;
68
- tokens.cacheWrite += cacheWrite;
69
- tokens.total += totalTokens > 0 ? totalTokens : input + output + cacheRead + cacheWrite;
70
- if (isRecord(usage.cost)) costUsd += numberField(usage.cost, "total");
63
+ if (!isRecord(event)) return;
64
+ observeProviderEvent(provider, event);
65
+ if (event.type !== "message_end" || !isRecord(event.message) || event.message.role !== "assistant") return;
66
+ const usage = readMessageUsage(event.message);
67
+ if (usage === null) return;
68
+ if (Object.keys(usage.tokens).length > 0) {
69
+ measured = true;
70
+ for (const field of TOKEN_FIELDS) tokens[field] += usage.tokens[field] ?? 0;
71
+ }
72
+ costUsd += usage.costUsd ?? 0;
71
73
  };
72
74
 
73
75
  return {
@@ -85,7 +87,7 @@ export function createTokenUsageFold(): EvalTokenUsageFold {
85
87
  consume(pending.replace(/\r$/u, ""));
86
88
  pending = "";
87
89
  }
88
- return { measured, tokens: { ...tokens }, costUsd };
90
+ return { measured, tokens: { ...tokens }, costUsd, provider: structuredClone(provider) };
89
91
  },
90
92
  };
91
93
  }
@@ -101,6 +103,7 @@ export function addTokenStreamUsage(left: EvalTokenStreamUsage, right: EvalToken
101
103
  cacheWrite: left.tokens.cacheWrite + right.tokens.cacheWrite,
102
104
  },
103
105
  costUsd: left.costUsd + right.costUsd,
106
+ provider: addProviderObservations(left.provider, right.provider),
104
107
  };
105
108
  }
106
109
 
@@ -110,21 +113,188 @@ export function addTokenStreamUsage(left: EvalTokenStreamUsage, right: EvalToken
110
113
  * read an absence as a zero-cost run.
111
114
  */
112
115
  export function tokenMetricEntries(usage: EvalTokenStreamUsage): Record<string, number | boolean> {
113
- if (!usage.measured) return { "tokens.measured": false };
114
116
  return {
115
- "tokens.measured": true,
116
- "tokens.input": usage.tokens.input,
117
- "tokens.output": usage.tokens.output,
118
- "tokens.total": usage.tokens.total,
119
- "tokens.cacheRead": usage.tokens.cacheRead,
120
- "tokens.cacheWrite": usage.tokens.cacheWrite,
117
+ ...providerMetricEntries(usage.provider),
118
+ "tokens.measured": usage.measured,
119
+ ...(usage.measured
120
+ ? {
121
+ "tokens.input": usage.tokens.input,
122
+ "tokens.output": usage.tokens.output,
123
+ "tokens.total": usage.tokens.total,
124
+ "tokens.cacheRead": usage.tokens.cacheRead,
125
+ "tokens.cacheWrite": usage.tokens.cacheWrite,
126
+ }
127
+ : {}),
121
128
  ...(usage.costUsd > 0 ? { "cost.usd": usage.costUsd } : {}),
122
129
  };
123
130
  }
124
131
 
125
- function numberField(record: Record<string, unknown>, field: string): number {
126
- const value = record[field];
127
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
132
+ const TOKEN_FIELDS = ["input", "output", "total", "cacheRead", "cacheWrite"] as const;
133
+ const STOP_REASONS = ["stop", "toolUse", "length", "error", "aborted", "other"] as const;
134
+ const RETRY_PHASES = {
135
+ scheduled: "retryScheduled",
136
+ retrying: "retryStarted",
137
+ cancelled: "retryCancelled",
138
+ exhausted: "retryExhausted",
139
+ recovered: "retryRecovered",
140
+ } as const;
141
+ type StopReason = (typeof STOP_REASONS)[number];
142
+ type RetryPhase = keyof typeof RETRY_PHASES;
143
+
144
+ interface EvalProviderObservation {
145
+ measured: boolean;
146
+ stops: Record<StopReason, number>;
147
+ retries: Record<RetryPhase, number>;
148
+ errorUsageObservedCalls: number;
149
+ errorUsageUnobservedCalls: number;
150
+ errorUsageIncompleteCalls: number;
151
+ errorCostUnobservedCalls: number;
152
+ errorReasoningUnobservedCalls: number;
153
+ errorReasoningTokens: number | null;
154
+ errorTokens: Partial<EvalTokenMetricsV4>;
155
+ errorCostUsd: number | null;
156
+ }
157
+
158
+ interface MessageUsageObservation {
159
+ reasoningTokens: number | null;
160
+ tokens: Partial<EvalTokenMetricsV4>;
161
+ costUsd: number | null;
162
+ completeTokens: boolean;
163
+ }
164
+
165
+ function emptyProviderObservation(): EvalProviderObservation {
166
+ return {
167
+ measured: false,
168
+ stops: { stop: 0, toolUse: 0, length: 0, error: 0, aborted: 0, other: 0 },
169
+ retries: { scheduled: 0, retrying: 0, cancelled: 0, exhausted: 0, recovered: 0 },
170
+ errorUsageObservedCalls: 0,
171
+ errorUsageUnobservedCalls: 0,
172
+ errorUsageIncompleteCalls: 0,
173
+ errorCostUnobservedCalls: 0,
174
+ errorReasoningUnobservedCalls: 0,
175
+ errorReasoningTokens: null,
176
+ errorTokens: {},
177
+ errorCostUsd: null,
178
+ };
179
+ }
180
+
181
+ /** Same usage interpretation for inclusive totals and their known errored share. */
182
+ function readMessageUsage(message: Record<string, unknown>): MessageUsageObservation | null {
183
+ if (!isRecord(message.usage)) return null;
184
+ const usage = message.usage;
185
+ const failed = message.stopReason === "error" || message.stopReason === "aborted";
186
+ const raw: Partial<EvalTokenMetricsV4> = {};
187
+ for (const field of TOKEN_FIELDS) {
188
+ const value = nonNegativeNumber(usage[field === "total" ? "totalTokens" : field]);
189
+ // Failed adapter objects can initialize each absent field to zero even
190
+ // when another field is reported. Keep only attributable positive facts.
191
+ if (value !== null && (!failed || value > 0)) raw[field] = value;
192
+ }
193
+ const cost = isRecord(usage.cost) ? nonNegativeNumber(usage.cost.total) : null;
194
+ // Zero prices are also initialized by adapters without pricing evidence.
195
+ const costUsd = cost !== null && cost > 0 ? cost : null;
196
+
197
+ // The compat adapter can synthesize the root reasoningTokens alias from
198
+ // thinking text without marking it estimated. Preserve explicit provider
199
+ // detail through the shared reader, but never promote that ambiguous alias.
200
+ // Normalized zero reasoning can also mean omitted provider detail.
201
+ const reportedReasoning = extractReasoningTokens({ ...usage, reasoningTokens: undefined });
202
+ const reasoningTokens = reportedReasoning !== null && reportedReasoning > 0 ? reportedReasoning : null;
203
+ const tokens = raw;
204
+ if (Object.keys(tokens).length === 0 && costUsd === null && reasoningTokens === null) return null;
205
+ const completeTokens = Object.keys(tokens).length === TOKEN_FIELDS.length;
206
+ if (Object.keys(tokens).length > 0 && !(tokens.total !== undefined && tokens.total > 0)) {
207
+ // This is a known subtotal when fields are missing; coverage stays incomplete.
208
+ tokens.total = (tokens.input ?? 0) + (tokens.output ?? 0) + (tokens.cacheRead ?? 0) + (tokens.cacheWrite ?? 0);
209
+ }
210
+ return { tokens, costUsd, completeTokens, reasoningTokens };
211
+ }
212
+
213
+ function observeProviderEvent(provider: EvalProviderObservation, event: Record<string, unknown>): void {
214
+ if (event.type === "retry_status" && isRecord(event.status)) {
215
+ const phase = event.status.phase;
216
+ if (typeof phase === "string" && Object.hasOwn(RETRY_PHASES, phase)) {
217
+ provider.measured = true;
218
+ provider.retries[phase as RetryPhase] += 1;
219
+ }
220
+ // Waiting frames are countdown ticks, not attempts. Attempt numbers may
221
+ // restart in another chain, so neither frames nor attempts are deduplicated.
222
+ return;
223
+ }
224
+ if (event.type !== "message_end" || !isRecord(event.message) || event.message.role !== "assistant") return;
225
+ const reason = event.message.stopReason;
226
+ if (typeof reason !== "string" || reason.length === 0 || reason === "pending") return;
227
+ provider.measured = true;
228
+ const stop = STOP_REASONS.includes(reason as StopReason) ? (reason as StopReason) : "other";
229
+ provider.stops[stop] += 1;
230
+ if (reason !== "error") return;
231
+ const usage = readMessageUsage(event.message);
232
+ if (usage === null) provider.errorUsageUnobservedCalls += 1;
233
+ else {
234
+ provider.errorUsageObservedCalls += 1;
235
+ for (const field of TOKEN_FIELDS) {
236
+ const value = usage.tokens[field];
237
+ if (value !== undefined) provider.errorTokens[field] = (provider.errorTokens[field] ?? 0) + value;
238
+ }
239
+ }
240
+ if (usage?.completeTokens !== true) provider.errorUsageIncompleteCalls += 1;
241
+ if (usage?.costUsd === undefined || usage.costUsd === null) provider.errorCostUnobservedCalls += 1;
242
+ else provider.errorCostUsd = (provider.errorCostUsd ?? 0) + usage.costUsd;
243
+ if (usage?.reasoningTokens === undefined || usage.reasoningTokens === null)
244
+ provider.errorReasoningUnobservedCalls += 1;
245
+ else provider.errorReasoningTokens = (provider.errorReasoningTokens ?? 0) + usage.reasoningTokens;
246
+ }
247
+
248
+ function addProviderObservations(
249
+ left: EvalProviderObservation,
250
+ right: EvalProviderObservation,
251
+ ): EvalProviderObservation {
252
+ const result = emptyProviderObservation();
253
+ result.measured = left.measured || right.measured;
254
+ for (const reason of STOP_REASONS) result.stops[reason] = left.stops[reason] + right.stops[reason];
255
+ for (const phase of Object.keys(RETRY_PHASES) as RetryPhase[])
256
+ result.retries[phase] = left.retries[phase] + right.retries[phase];
257
+ result.errorUsageObservedCalls = left.errorUsageObservedCalls + right.errorUsageObservedCalls;
258
+ result.errorUsageUnobservedCalls = left.errorUsageUnobservedCalls + right.errorUsageUnobservedCalls;
259
+ result.errorUsageIncompleteCalls = left.errorUsageIncompleteCalls + right.errorUsageIncompleteCalls;
260
+ result.errorCostUnobservedCalls = left.errorCostUnobservedCalls + right.errorCostUnobservedCalls;
261
+ result.errorReasoningUnobservedCalls = left.errorReasoningUnobservedCalls + right.errorReasoningUnobservedCalls;
262
+ if (left.errorReasoningTokens !== null || right.errorReasoningTokens !== null)
263
+ result.errorReasoningTokens = (left.errorReasoningTokens ?? 0) + (right.errorReasoningTokens ?? 0);
264
+ for (const field of TOKEN_FIELDS) {
265
+ if (left.errorTokens[field] !== undefined || right.errorTokens[field] !== undefined) {
266
+ result.errorTokens[field] = (left.errorTokens[field] ?? 0) + (right.errorTokens[field] ?? 0);
267
+ }
268
+ }
269
+ if (left.errorCostUsd !== null || right.errorCostUsd !== null)
270
+ result.errorCostUsd = (left.errorCostUsd ?? 0) + (right.errorCostUsd ?? 0);
271
+ return result;
272
+ }
273
+
274
+ /** Provider observations are independent of task success and receipt billing. */
275
+ function providerMetricEntries(provider: EvalProviderObservation): Record<string, number | boolean> {
276
+ if (!provider.measured) return { "provider.measured": false };
277
+ const metrics: Record<string, number | boolean> = { "provider.measured": true };
278
+ for (const reason of STOP_REASONS) metrics[`provider.stopReason.${reason}`] = provider.stops[reason];
279
+ for (const phase of Object.keys(RETRY_PHASES) as RetryPhase[])
280
+ metrics[`provider.${RETRY_PHASES[phase]}`] = provider.retries[phase];
281
+ if (provider.stops.error === 0) return metrics;
282
+ metrics["provider.errorUsageObservedCalls"] = provider.errorUsageObservedCalls;
283
+ metrics["provider.errorUsageUnobservedCalls"] = provider.errorUsageUnobservedCalls;
284
+ metrics["provider.errorUsageIncompleteCalls"] = provider.errorUsageIncompleteCalls;
285
+ metrics["provider.errorCostUnobservedCalls"] = provider.errorCostUnobservedCalls;
286
+ metrics["provider.errorReasoningUnobservedCalls"] = provider.errorReasoningUnobservedCalls;
287
+ if (provider.errorReasoningTokens !== null) metrics["provider.errorReasoningTokens"] = provider.errorReasoningTokens;
288
+ for (const field of TOKEN_FIELDS) {
289
+ const value = provider.errorTokens[field];
290
+ if (value !== undefined) metrics[`provider.errorTokens.${field}`] = value;
291
+ }
292
+ if (provider.errorCostUsd !== null) metrics["provider.errorCostUsd"] = provider.errorCostUsd;
293
+ return metrics;
294
+ }
295
+
296
+ function nonNegativeNumber(value: unknown): number | null {
297
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
128
298
  }
129
299
 
130
300
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -47,6 +47,7 @@ export interface BuildEvalTrackedMetricsInput {
47
47
  }
48
48
 
49
49
  interface AssistantCall {
50
+ timestamp: string | null;
50
51
  payload: Record<string, unknown>;
51
52
  promptCache: Record<string, unknown> | null;
52
53
  backend: Record<string, unknown> | null;
@@ -59,6 +60,28 @@ interface NumericReading {
59
60
  source: EvalMetricSource;
60
61
  }
61
62
 
63
+ /**
64
+ * The session ledger and stdout fold observe the same assistant calls on the
65
+ * native runner. Prefer the durable calls (including their richer timing and
66
+ * cache facts), with stdout as a fallback only when no durable calls exist.
67
+ * Keep non-call entries in either case: compaction is billed but not streamed.
68
+ * Counts are observations, NOT identities or evidence of matching coverage;
69
+ * partial/mixed ledgers cannot be unioned without a shared call identity.
70
+ */
71
+ export function selectEvalLedgerEntries(
72
+ durable: ReadonlyArray<SessionEntry>,
73
+ stream: ReadonlyArray<SessionEntry>,
74
+ ): { entries: ReadonlyArray<SessionEntry>; source: "session" | "stream"; sessionCalls: number; streamCalls: number } {
75
+ const sessionCalls = assistantCalls(durable).length;
76
+ const streamCalls = assistantCalls(stream).length;
77
+ return {
78
+ entries: sessionCalls > 0 ? durable : [...durable, ...stream],
79
+ source: sessionCalls > 0 ? "session" : "stream",
80
+ sessionCalls,
81
+ streamCalls,
82
+ };
83
+ }
84
+
62
85
  /** Read every isolated session ledger and its prompt manifest before cleanup. */
63
86
  export async function readEvalLedgerSnapshot(stateDir: string): Promise<EvalLedgerSnapshot> {
64
87
  const refs = await listSessionLedgerRefs(stateDir);
@@ -175,7 +198,7 @@ export function emptyEvalTrackedMetrics(source: EvalMetricSource = "estimated"):
175
198
  reasoningTokens: { value: null, source },
176
199
  toolCalls: zero(),
177
200
  toolErrors: zero(),
178
- ttftMsFirstCall: zero(),
201
+ ttftMsFirstCall: { value: null, source: "estimated" },
179
202
  wallClockMs: zero(),
180
203
  contextTokensAtEnd: zero(),
181
204
  compactions: zero(),
@@ -192,6 +215,7 @@ function assistantCalls(entries: ReadonlyArray<SessionEntry>): AssistantCall[] {
192
215
  if (promptCache === null && timing === null && usage === null) return [];
193
216
  return [
194
217
  {
218
+ timestamp: entry.payload.timestampEstimated === true ? null : entry.timestamp,
195
219
  payload: entry.payload,
196
220
  promptCache,
197
221
  backend: promptCache === null ? null : recordField(promptCache, "backend"),
@@ -260,10 +284,22 @@ function reasoningMetric(
260
284
  return measured ? { value: total, source: "ledger" } : { value: null, source: "estimated" };
261
285
  }
262
286
 
263
- function firstCallTtft(calls: ReadonlyArray<AssistantCall>): EvalSourcedNumber {
264
- const first = calls[0];
287
+ function firstCallTtft(calls: ReadonlyArray<AssistantCall>): EvalSourcedNullableNumber {
288
+ // Session directories are read by name, not by call time. Select without
289
+ // reordering the evidence or borrowing a later call's available timing.
290
+ let first: AssistantCall | undefined;
291
+ let firstAt = Number.POSITIVE_INFINITY;
292
+ for (const call of calls) {
293
+ if (call.timestamp === null) return { value: null, source: "estimated" };
294
+ const at = Date.parse(call.timestamp);
295
+ if (!Number.isFinite(at)) return { value: null, source: "estimated" };
296
+ if (at < firstAt) {
297
+ first = call;
298
+ firstAt = at;
299
+ }
300
+ }
265
301
  const value = first?.timing === null || first?.timing === undefined ? null : nonNegativeNumber(first.timing.ttftMs);
266
- return value === null ? { value: 0, source: "estimated" } : { value, source: "ledger" };
302
+ return value === null ? { value: null, source: "estimated" } : { value, source: "ledger" };
267
303
  }
268
304
 
269
305
  function wallClockMetric(receipt: RunReceipt | null, fallback: number): EvalSourcedNumber {
@@ -1,6 +1,5 @@
1
1
  import { isAbsolute, relative, resolve, sep } from "node:path";
2
2
  import { normalizeClioCoderEventRecord } from "../../../core/naming-events.js";
3
- import { shellQuote } from "../../../core/shell-quote.js";
4
3
  import { clioStateDir } from "../../../core/xdg.js";
5
4
  import {
6
5
  evidenceMetricsFromReceipt,
@@ -22,19 +21,23 @@ export async function runClioRunRunner(
22
21
  readObservation?: { allowedPaths: string[]; decoyPaths: string[] },
23
22
  ): Promise<EvalRunnerOutput> {
24
23
  const prompt = runner.prompt ?? "";
25
- const args = [
26
- shellQuote(clioEntry),
24
+ // Spawned as an argv, not a shell line: the task timeout has to reach the
25
+ // Clio process itself so the receipt it seals and the exit code this
26
+ // runner reports describe the same ending. See runShellCommand.
27
+ const args: [string, ...string[]] = [
28
+ process.execPath,
29
+ clioEntry,
27
30
  "run",
28
31
  "--json",
29
- ...(runner.agent === undefined ? [] : ["--agent", shellQuote(runner.agent)]),
32
+ ...(runner.agent === undefined ? [] : ["--agent", runner.agent]),
30
33
  "--target",
31
- shellQuote(target.id),
32
- ...(target.model === undefined ? [] : ["--model", shellQuote(target.model)]),
33
- ...(target.thinking === undefined ? [] : ["--thinking", shellQuote(target.thinking)]),
34
+ target.id,
35
+ ...(target.model === undefined ? [] : ["--model", target.model]),
36
+ ...(target.thinking === undefined ? [] : ["--thinking", target.thinking]),
34
37
  ...(runner.autonomy === undefined ? [] : ["--autonomy", runner.autonomy]),
35
- shellQuote(prompt),
38
+ prompt,
36
39
  ];
37
- const result = await runShellCommand(`${process.execPath} ${args.join(" ")}`, cwd, runner.timeoutMs ?? timeoutMs, env);
40
+ const result = await runShellCommand(args, cwd, runner.timeoutMs ?? timeoutMs, env);
38
41
  // Usage is folded from the live stream, not from the bounded stdout
39
42
  // artifact: a verbose run's `message_end` events do not survive truncation.
40
43
  const tokens = result.usage;
@@ -149,7 +152,7 @@ interface BehavioralToolTerminal {
149
152
  * bounded counters against the suite's explicit public allowlist and decoy
150
153
  * list; they never become behavioral fact values or evidence excerpts.
151
154
  */
152
- function toolBehaviorMetricEntriesFromJsonl(
155
+ export function toolBehaviorMetricEntriesFromJsonl(
153
156
  stdout: string,
154
157
  cwd: string,
155
158
  readObservation?: { allowedPaths: string[]; decoyPaths: string[] },
@@ -201,16 +204,19 @@ function toolBehaviorMetricEntriesFromJsonl(
201
204
 
202
205
  const terminals = canonicalFinishes.length > 0 ? canonicalFinishes : executionEnds;
203
206
  const calls = new Map<string, number>();
207
+ const succeeded = new Map<string, number>();
204
208
  const blocked = new Map<string, number>();
205
209
  for (const terminal of terminals) {
206
210
  const tool = metricToolName(terminal.tool);
207
211
  calls.set(tool, (calls.get(tool) ?? 0) + 1);
212
+ if (terminal.outcome === "ok") succeeded.set(tool, (succeeded.get(tool) ?? 0) + 1);
208
213
  if (terminal.outcome === "blocked") blocked.set(tool, (blocked.get(tool) ?? 0) + 1);
209
214
  }
210
- const namedTools = new Set(["bash", "dispatch", "read", ...calls.keys(), ...blocked.keys()]);
215
+ const namedTools = new Set(["bash", "dispatch", "read", ...calls.keys(), ...succeeded.keys(), ...blocked.keys()]);
211
216
  const entries: Record<string, number> = { "tools.read.distinctPaths": readPaths.size };
212
217
  for (const tool of [...namedTools].sort()) {
213
218
  entries[`tools.calls.${tool}`] = calls.get(tool) ?? 0;
219
+ entries[`tools.succeeded.${tool}`] = succeeded.get(tool) ?? 0;
214
220
  entries[`tools.blocked.${tool}`] = blocked.get(tool) ?? 0;
215
221
  }
216
222
  if (readObservation !== undefined) {
@@ -1,4 +1,3 @@
1
- import { shellQuote } from "../../../core/shell-quote.js";
2
1
  import type { EvalSuiteTargetV2 } from "../schema/suite.js";
3
2
  import { type EvalRunnerOutput, runShellCommand } from "./external-command.js";
4
3
 
@@ -9,12 +8,8 @@ export async function runContextIndexRunner(
9
8
  target: EvalSuiteTargetV2,
10
9
  env?: NodeJS.ProcessEnv,
11
10
  ): Promise<EvalRunnerOutput> {
12
- const result = await runShellCommand(
13
- `${shellQuote(process.execPath)} ${shellQuote(clioEntry)} context index --json`,
14
- cwd,
15
- timeoutMs,
16
- env,
17
- );
11
+ const args: [string, ...string[]] = [process.execPath, clioEntry, "context", "index", "--json"];
12
+ const result = await runShellCommand(args, cwd, timeoutMs, env);
18
13
  const parsed = parseContextIndexOutput(result.stdout);
19
14
  return {
20
15
  assignmentId: null,
@@ -1,6 +1,5 @@
1
1
  import { existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { shellQuote } from "../../../core/shell-quote.js";
4
3
  import type { EvalRunnerV2, EvalSuiteTargetV2 } from "../schema/suite.js";
5
4
  import { type EvalRunnerOutput, runShellCommand } from "./external-command.js";
6
5
 
@@ -13,7 +12,7 @@ export async function runContextInitRunner(
13
12
  env?: NodeJS.ProcessEnv,
14
13
  ): Promise<EvalRunnerOutput> {
15
14
  const extraArgs = runner.args ?? [];
16
- const command = [
15
+ const args: [string, ...string[]] = [
17
16
  process.execPath,
18
17
  clioEntry,
19
18
  "context",
@@ -25,10 +24,8 @@ export async function runContextInitRunner(
25
24
  ...(target.model === undefined ? [] : ["--model", target.model]),
26
25
  ...(target.thinking === undefined ? [] : ["--thinking", target.thinking]),
27
26
  ...extraArgs,
28
- ]
29
- .map(shellQuote)
30
- .join(" ");
31
- const result = await runShellCommand(command, cwd, runner.timeoutMs ?? timeoutMs, env);
27
+ ];
28
+ const result = await runShellCommand(args, cwd, runner.timeoutMs ?? timeoutMs, env);
32
29
  const payload = parseInitPayload(result.stdout);
33
30
  const candidateGeneration = recordField(payload, "generation");
34
31
  const generation = isValidGenerationPayload(payload, candidateGeneration) ? candidateGeneration : null;
@@ -65,6 +65,11 @@ const OUTPUT_HEAD_LIMIT = 20_000;
65
65
  const OUTPUT_TRUNCATION_MARKER = "\n[output middle truncated; tail preserved]\n";
66
66
  const METRIC_JSONL_LIMIT = 256_000;
67
67
  const METRIC_JSONL_LINE_LIMIT = 64_000;
68
+ // After the deadline's SIGTERM, how long a run gets to end on its own terms
69
+ // before SIGKILL. Clio seals the interrupted run's receipt inside its
70
+ // coordinated shutdown, which budgets 500ms per hook across several hooks; a
71
+ // one-second grace killed it mid-seal and the timed-out run left no receipt.
72
+ const TIMEOUT_KILL_GRACE_MS = 5_000;
68
73
 
69
74
  export async function runExternalCommandRunner(
70
75
  runner: EvalRunnerV2,
@@ -133,8 +138,23 @@ export async function runExternalCommandRunner(
133
138
  };
134
139
  }
135
140
 
141
+ /**
142
+ * Run one eval subprocess to completion or to the timeout.
143
+ *
144
+ * A string runs through the shell, which is what an operator's
145
+ * `external-command` line and the verifier commands are. An argv array spawns
146
+ * the program directly, with no shell between the runner and the process
147
+ * whose exit code it reports. That distinction matters on the timeout path:
148
+ * `/bin/sh -c` on dash keeps the shell as the parent, so SIGTERM to the shell
149
+ * stops the shell alone while the program runs on to completion. A
150
+ * `clio-coder run` spawned that way finished its turn after the deadline,
151
+ * sealed a succeeded receipt with exit 0, and exited 0, and this runner
152
+ * reported the shell's signal death as exit 124 (issue #275). With an argv the
153
+ * signal reaches Clio itself, which seals the run as canceled with the exit
154
+ * status it then reports, so the receipt and the exit code agree either way.
155
+ */
136
156
  export function runShellCommand(
137
- command: string,
157
+ command: string | readonly [string, ...string[]],
138
158
  cwd: string,
139
159
  timeoutMs: number,
140
160
  env?: NodeJS.ProcessEnv,
@@ -153,14 +173,12 @@ export function runShellCommand(
153
173
  const callLedgerFold = createEvalCallLedgerFold();
154
174
  let timedOut = false;
155
175
  let settled = false;
156
- const child = spawn(command, {
157
- cwd,
158
- shell: true,
159
- stdio: ["ignore", "pipe", "pipe"],
160
- // The overlay is additive: an eval item pins where Clio writes its
161
- // journal, and inherits everything else the operator's shell provides.
162
- env: env === undefined ? process.env : { ...process.env, ...env },
163
- });
176
+ // The overlay is additive: an eval item pins where Clio writes its
177
+ // journal, and inherits everything else the operator's shell provides.
178
+ const spawnEnv = env === undefined ? process.env : { ...process.env, ...env };
179
+ const [file, args, shell] = typeof command === "string" ? [command, [], true] : [command[0], command.slice(1), false];
180
+ const child = spawn(file, args, { cwd, shell, stdio: ["ignore", "pipe", "pipe"], env: spawnEnv });
181
+ const commandLine = typeof command === "string" ? command : command.join(" ");
164
182
  child.stdout.setEncoding("utf8");
165
183
  child.stderr.setEncoding("utf8");
166
184
  child.stdout.on("data", (chunk: string) => {
@@ -177,14 +195,14 @@ export function runShellCommand(
177
195
  const timer = setTimeout(() => {
178
196
  timedOut = true;
179
197
  child.kill("SIGTERM");
180
- setTimeout(() => child.kill("SIGKILL"), 1000);
198
+ setTimeout(() => child.kill("SIGKILL"), TIMEOUT_KILL_GRACE_MS);
181
199
  }, timeoutMs);
182
200
  const finish = (exitCode: number): void => {
183
201
  if (settled) return;
184
202
  settled = true;
185
203
  clearTimeout(timer);
186
204
  resolve({
187
- command,
205
+ command: commandLine,
188
206
  exitCode,
189
207
  stdout,
190
208
  metricJsonl: metricCapture.finish(),
@@ -13,6 +13,33 @@ export const CANONICAL_METRICS = [
13
13
  // are absent in that case rather than zero, so a gate on cost can require
14
14
  // measurement instead of reading silence as free.
15
15
  "tokens.measured",
16
+ // Observed stdout terminal/retry facts, independent of task success. Opaque
17
+ // runs carry measured:false and no counters. Errored usage stays in inclusive
18
+ // tokens.* totals; errorTokens/errorCostUsd are known subtotals with coverage.
19
+ "provider.measured",
20
+ "provider.stopReason.stop",
21
+ "provider.stopReason.toolUse",
22
+ "provider.stopReason.length",
23
+ "provider.stopReason.error",
24
+ "provider.stopReason.aborted",
25
+ "provider.stopReason.other",
26
+ "provider.retryScheduled",
27
+ "provider.retryStarted",
28
+ "provider.retryCancelled",
29
+ "provider.retryExhausted",
30
+ "provider.retryRecovered",
31
+ "provider.errorUsageObservedCalls",
32
+ "provider.errorUsageUnobservedCalls",
33
+ "provider.errorUsageIncompleteCalls",
34
+ "provider.errorCostUnobservedCalls",
35
+ "provider.errorReasoningUnobservedCalls",
36
+ "provider.errorReasoningTokens",
37
+ "provider.errorTokens.input",
38
+ "provider.errorTokens.output",
39
+ "provider.errorTokens.total",
40
+ "provider.errorTokens.cacheRead",
41
+ "provider.errorTokens.cacheWrite",
42
+ "provider.errorCostUsd",
16
43
  "latency.wallMs",
17
44
  "latency.modelMs",
18
45
  "tools.totalCalls",
@@ -22,6 +49,7 @@ export const CANONICAL_METRICS = [
22
49
  "tools.read.outsideAllowed",
23
50
  "tools.read.decoyHits",
24
51
  "tools.calls.dispatch",
52
+ "tools.succeeded.dispatch",
25
53
  "tools.blocked.bash",
26
54
  "claims.unsupported",
27
55
  "completion.reported",
@@ -39,7 +39,7 @@ export interface EvalTrackedMetricsV1 {
39
39
  reasoningTokens: EvalSourcedNullableNumber;
40
40
  toolCalls: EvalSourcedNumber;
41
41
  toolErrors: EvalSourcedNumber;
42
- ttftMsFirstCall: EvalSourcedNumber;
42
+ ttftMsFirstCall: EvalSourcedNullableNumber;
43
43
  wallClockMs: EvalSourcedNumber;
44
44
  contextTokensAtEnd: EvalSourcedNumber;
45
45
  compactions: EvalSourcedNumber;
@@ -125,7 +125,7 @@ function parseTrackedMetrics(value: unknown, source: string): EvalTrackedMetrics
125
125
  reasoningTokens: readSourcedNullableNumber(record.reasoningTokens, `${source}.reasoningTokens`),
126
126
  toolCalls: readSourcedNumber(record.toolCalls, `${source}.toolCalls`),
127
127
  toolErrors: readSourcedNumber(record.toolErrors, `${source}.toolErrors`),
128
- ttftMsFirstCall: readSourcedNumber(record.ttftMsFirstCall, `${source}.ttftMsFirstCall`),
128
+ ttftMsFirstCall: readSourcedNullableNumber(record.ttftMsFirstCall, `${source}.ttftMsFirstCall`),
129
129
  wallClockMs: readSourcedNumber(record.wallClockMs, `${source}.wallClockMs`),
130
130
  contextTokensAtEnd: readSourcedNumber(record.contextTokensAtEnd, `${source}.contextTokensAtEnd`),
131
131
  compactions: readSourcedNumber(record.compactions, `${source}.compactions`),
@@ -38,7 +38,19 @@ function resolveTargets(targets: ReadonlyArray<EvalSuiteTargetV2>, options: Reso
38
38
  const filtered =
39
39
  options.target === undefined ? [...targets] : targets.filter((target) => target.id === options.target);
40
40
  const selected =
41
- filtered.length > 0 ? filtered : options.target === undefined ? [...targets] : [{ id: options.target }];
41
+ filtered.length > 0
42
+ ? filtered
43
+ : options.target === undefined
44
+ ? [...targets]
45
+ : [
46
+ {
47
+ id: options.target,
48
+ // A one-row suite still owns its thinking dimension when the CLI
49
+ // substitutes the target/model pair. Dropping it silently changes
50
+ // an off baseline into the target's configured default.
51
+ ...(targets.length === 1 && targets[0]?.thinking !== undefined ? { thinking: targets[0].thinking } : {}),
52
+ },
53
+ ];
42
54
  return selected.map((target) => ({
43
55
  ...target,
44
56
  ...(options.model === undefined ? {} : { model: options.model }),