@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
@@ -0,0 +1,112 @@
1
+ import { performance } from "node:perf_hooks";
2
+ import type { SessionEntry } from "../../session/entries.js";
3
+
4
+ export interface EvalCallLedgerFold {
5
+ push(chunk: string): void;
6
+ entries(): SessionEntry[];
7
+ }
8
+
9
+ /** Fold structured assistant completions into the eval's per-call ledger. */
10
+ export function createEvalCallLedgerFold(now: () => number = () => performance.now()): EvalCallLedgerFold {
11
+ const entries: SessionEntry[] = [];
12
+ let pending = "";
13
+ let activeStartedAt: number | null = null;
14
+ let activeFirstOutputAt: number | null = null;
15
+
16
+ const consume = (line: string): void => {
17
+ const event = parseRecord(line);
18
+ if (event === null) return;
19
+ if (event.type === "message_start" && isAssistantMessage(event.message)) {
20
+ activeStartedAt = now();
21
+ activeFirstOutputAt = null;
22
+ return;
23
+ }
24
+ if (event.type === "message_update" && activeStartedAt !== null && activeFirstOutputAt === null) {
25
+ activeFirstOutputAt = now();
26
+ return;
27
+ }
28
+ if (event.type !== "message_end" || !isAssistantMessage(event.message)) return;
29
+ const message = event.message;
30
+ const usage = isRecord(message.usage) ? message.usage : null;
31
+ if (usage === null) {
32
+ activeStartedAt = null;
33
+ activeFirstOutputAt = null;
34
+ return;
35
+ }
36
+ const endedAt = now();
37
+ const promptCache: Record<string, unknown> = {
38
+ input: nonNegativeNumber(usage.input) ?? 0,
39
+ cacheRead: nonNegativeNumber(usage.cacheRead) ?? 0,
40
+ cacheWrite: nonNegativeNumber(usage.cacheWrite) ?? 0,
41
+ backendVerdict: "unknown",
42
+ };
43
+ if (isRecord(message.backendTimings)) promptCache.backend = structuredClone(message.backendTimings);
44
+ const previous = entries.at(-1);
45
+ entries.push({
46
+ kind: "message",
47
+ role: "assistant",
48
+ turnId: `eval-call-${entries.length + 1}`,
49
+ parentTurnId: previous?.turnId ?? null,
50
+ timestamp: messageTimestamp(message.timestamp),
51
+ payload: {
52
+ promptCache,
53
+ timing: {
54
+ ttftMs:
55
+ activeStartedAt === null ? null : Math.round(Math.max(0, (activeFirstOutputAt ?? endedAt) - activeStartedAt)),
56
+ apiMs: activeStartedAt === null ? 0 : Math.round(Math.max(0, endedAt - activeStartedAt)),
57
+ },
58
+ usage: structuredClone(usage),
59
+ },
60
+ });
61
+ activeStartedAt = null;
62
+ activeFirstOutputAt = null;
63
+ };
64
+
65
+ return {
66
+ push(chunk: string): void {
67
+ pending += chunk;
68
+ for (;;) {
69
+ const newline = pending.indexOf("\n");
70
+ if (newline === -1) break;
71
+ consume(pending.slice(0, newline).replace(/\r$/u, ""));
72
+ pending = pending.slice(newline + 1);
73
+ }
74
+ },
75
+ entries(): SessionEntry[] {
76
+ if (pending.length > 0) {
77
+ consume(pending.replace(/\r$/u, ""));
78
+ pending = "";
79
+ }
80
+ return structuredClone(entries);
81
+ },
82
+ };
83
+ }
84
+
85
+ function isAssistantMessage(value: unknown): value is Record<string, unknown> & { role: "assistant" } {
86
+ return isRecord(value) && value.role === "assistant";
87
+ }
88
+
89
+ function messageTimestamp(value: unknown): string {
90
+ const milliseconds = nonNegativeNumber(value);
91
+ if (milliseconds === null) return new Date(0).toISOString();
92
+ const date = new Date(milliseconds);
93
+ return Number.isFinite(date.getTime()) ? date.toISOString() : new Date(0).toISOString();
94
+ }
95
+
96
+ function nonNegativeNumber(value: unknown): number | null {
97
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
98
+ }
99
+
100
+ function parseRecord(line: string): Record<string, unknown> | null {
101
+ if (line.trim().length === 0) return null;
102
+ try {
103
+ const value: unknown = JSON.parse(line);
104
+ return isRecord(value) ? value : null;
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+
110
+ function isRecord(value: unknown): value is Record<string, unknown> {
111
+ return typeof value === "object" && value !== null && !Array.isArray(value);
112
+ }
@@ -6,7 +6,35 @@
6
6
  * than silently pass.
7
7
  */
8
8
 
9
- import type { RunReceipt } from "../../dispatch/types.js";
9
+ import { readFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import type { RunEnvelope, RunReceipt } from "../../dispatch/types.js";
12
+ import { formatTrustSummary, summarizeTrustStatus } from "../../evidence/trust-projection.js";
13
+ import {
14
+ adaptRunReceiptTrustStatus,
15
+ inspectRunReceiptTrustStatus,
16
+ TRUST_STATUS_AXES,
17
+ } from "../../evidence/trust-status.js";
18
+
19
+ function dispatchScopeMetrics(receipt: RunReceipt): Record<string, string | boolean | number> {
20
+ const scope = receipt.pathScope;
21
+ if (scope === undefined) return {};
22
+ const entries = [...scope.workingContextPaths, ...scope.writeBoundaries];
23
+ const evidence = entries.flatMap((entry) => entry.evidence);
24
+ const count = (source: string): number => evidence.filter((entry) => entry.source === source).length;
25
+ return {
26
+ "dispatch.scope.mode": scope.mode,
27
+ "dispatch.scope.inferredPathCount": entries.filter((entry) =>
28
+ entry.evidence.some((item) => item.provenance === "inferred"),
29
+ ).length,
30
+ "dispatch.scope.derivedPathCount": entries.filter((entry) =>
31
+ entry.evidence.some((item) => item.provenance === "derived"),
32
+ ).length,
33
+ "dispatch.scope.source.task": count("task"),
34
+ "dispatch.scope.source.briefing": count("briefing"),
35
+ "dispatch.scope.source.writeRoots": count("writeRoots"),
36
+ };
37
+ }
10
38
 
11
39
  /**
12
40
  * Extract the sealed RunReceipt that `clio-coder run --agent … --json` prints after
@@ -53,9 +81,14 @@ function isReceiptShaped(value: unknown): value is RunReceipt {
53
81
  * mirrors the durable findings summary and is omitted when that summary is
54
82
  * absent, so a gate on the metric fails closed instead of inventing a value.
55
83
  */
56
- export function evidenceMetricsFromReceipt(receipt: RunReceipt): Record<string, string | boolean | number> {
84
+ export function evidenceMetricsFromReceipt(
85
+ receipt: RunReceipt,
86
+ options: { envelope?: RunEnvelope | null } = {},
87
+ ): Record<string, string | boolean | number> {
57
88
  return {
58
89
  "evidence.verification": receipt.verification.state,
90
+ ...dispatchScopeMetrics(receipt),
91
+ ...evidenceTrustMetrics(receipt, options.envelope ?? null),
59
92
  ...(receipt.findingsSummary === undefined
60
93
  ? {}
61
94
  : { "evidence.firstPassSuccess": receipt.findingsSummary.firstPassSuccess === true }),
@@ -64,3 +97,47 @@ export function evidenceMetricsFromReceipt(receipt: RunReceipt): Record<string,
64
97
  ...(typeof receipt.costUsd === "number" && Number.isFinite(receipt.costUsd) ? { "cost.usd": receipt.costUsd } : {}),
65
98
  };
66
99
  }
100
+
101
+ /**
102
+ * The canonical trust status as metrics, so a benchmark table reads the same
103
+ * verdict as every operator surface instead of the raw receipt marker. The
104
+ * receipt is authenticated against its ledger row when the caller can supply
105
+ * one; without a row the canonical model reports the seal unchecked and the
106
+ * receipt-owned axes unobserved, which is the honest answer for a receipt
107
+ * that only ever arrived through stdout.
108
+ */
109
+ function evidenceTrustMetrics(
110
+ receipt: RunReceipt,
111
+ envelope: RunEnvelope | null,
112
+ ): Record<string, string | boolean | number> {
113
+ // A missing row means the seal was never checked, not that it failed:
114
+ // only a row that exists and disagrees can break a seal.
115
+ const status =
116
+ envelope === null ? adaptRunReceiptTrustStatus(receipt) : inspectRunReceiptTrustStatus(receipt, envelope).status;
117
+ const summary = summarizeTrustStatus(status);
118
+ return {
119
+ "evidence.trust.version": summary.version,
120
+ "evidence.trust.verdict": summary.verdict,
121
+ "evidence.trust.summary": formatTrustSummary(status),
122
+ ...Object.fromEntries(TRUST_STATUS_AXES.map((axis) => [`evidence.trust.${axis}`, status[axis].state])),
123
+ };
124
+ }
125
+
126
+ /**
127
+ * The ledger row a stdout receipt was sealed from, read from the state dir
128
+ * the run wrote to. Null when the ledger or the row cannot be read; the
129
+ * caller then reports the seal unchecked rather than guessing.
130
+ */
131
+ export function readRunEnvelopeForReceipt(receipt: RunReceipt, stateDir: string): RunEnvelope | null {
132
+ try {
133
+ const parsed: unknown = JSON.parse(readFileSync(join(stateDir, "runs.json"), "utf8"));
134
+ if (!Array.isArray(parsed)) return null;
135
+ const row = parsed.find(
136
+ (entry): entry is RunEnvelope =>
137
+ typeof entry === "object" && entry !== null && (entry as { id?: unknown }).id === receipt.runId,
138
+ );
139
+ return row ?? null;
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
@@ -0,0 +1,413 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import type { RunReceipt } from "../../dispatch/types.js";
4
+ import { listSessionLedgerRefs, parseSessionEntries } from "../../session/archive-readers.js";
5
+ import { extractReasoningTokens } from "../../session/context-accounting.js";
6
+ import type { SessionEntry } from "../../session/entries.js";
7
+ import { evalHarnessMetricsFromReceipt } from "../harness-metrics.js";
8
+ import type {
9
+ EvalMetricSource,
10
+ EvalSourcedNullableNumber,
11
+ EvalSourcedNumber,
12
+ EvalTrackedMetricsV1,
13
+ } from "../schema/verdict.js";
14
+
15
+ export interface EvalLedgerSnapshot {
16
+ entries: SessionEntry[];
17
+ compiledPromptHashes: string[];
18
+ promptManifests: EvalPromptManifestObservation[];
19
+ contextSnapshots: EvalContextSnapshotObservation[];
20
+ }
21
+
22
+ export interface EvalPromptManifestObservation {
23
+ systemPromptHash: string;
24
+ thinkingLevel: string | null;
25
+ projectPreload: {
26
+ mode: "full" | "synopsis" | "none";
27
+ chars: number;
28
+ lines: number;
29
+ reason: string | null;
30
+ nearLimit: boolean;
31
+ label: string;
32
+ } | null;
33
+ fragments: Array<{ id: string; contentHash: string }>;
34
+ }
35
+
36
+ export interface EvalContextSnapshotObservation {
37
+ runtimeId: string | null;
38
+ modelId: string | null;
39
+ promptHash: string | null;
40
+ toolSignature: string | null;
41
+ }
42
+
43
+ export interface BuildEvalTrackedMetricsInput {
44
+ ledgerEntries: ReadonlyArray<SessionEntry>;
45
+ receipt: RunReceipt | null;
46
+ fallbackWallClockMs: number;
47
+ }
48
+
49
+ interface AssistantCall {
50
+ payload: Record<string, unknown>;
51
+ promptCache: Record<string, unknown> | null;
52
+ backend: Record<string, unknown> | null;
53
+ timing: Record<string, unknown> | null;
54
+ usage: Record<string, unknown> | null;
55
+ }
56
+
57
+ interface NumericReading {
58
+ value: number;
59
+ source: EvalMetricSource;
60
+ }
61
+
62
+ /** Read every isolated session ledger and its prompt manifest before cleanup. */
63
+ export async function readEvalLedgerSnapshot(stateDir: string): Promise<EvalLedgerSnapshot> {
64
+ const refs = await listSessionLedgerRefs(stateDir);
65
+ const entries: SessionEntry[] = [];
66
+ const compiledPromptHashes: string[] = [];
67
+ const promptManifests: EvalPromptManifestObservation[] = [];
68
+ const contextSnapshots: EvalContextSnapshotObservation[] = [];
69
+ for (const ref of refs) {
70
+ try {
71
+ const raw = await readFile(ref.path, "utf8");
72
+ entries.push(...parseSessionEntries(raw, ref.path).entries);
73
+ } catch {
74
+ // An unreadable ledger contributes no invented measurements.
75
+ }
76
+ try {
77
+ const manifest = await readFile(join(dirname(ref.path), "prompt-manifest.jsonl"), "utf8");
78
+ for (const line of manifest.split(/\r?\n/u)) {
79
+ const record = parseJsonRecord(line);
80
+ if (record === null) continue;
81
+ const hash = record?.systemPromptHash;
82
+ if (typeof hash !== "string" || !/^[a-f0-9]{64}$/u.test(hash)) continue;
83
+ compiledPromptHashes.push(hash);
84
+ const observation = promptManifestObservation(record);
85
+ if (observation !== null) promptManifests.push(observation);
86
+ }
87
+ } catch {
88
+ // A missing manifest leaves compiled prompt provenance unresolved.
89
+ }
90
+ try {
91
+ const snapshots = await readFile(join(dirname(ref.path), "context-snapshots.jsonl"), "utf8");
92
+ for (const line of snapshots.split(/\r?\n/u)) {
93
+ const record = parseJsonRecord(line);
94
+ if (record === null) continue;
95
+ contextSnapshots.push({
96
+ runtimeId: nullableString(record.runtimeId),
97
+ modelId: nullableString(record.modelId),
98
+ promptHash: nullableDigest(record.promptHash),
99
+ toolSignature: nullableDigest(record.toolSignature),
100
+ });
101
+ }
102
+ } catch {
103
+ // A missing context snapshot leaves runtime and tool-surface provenance unresolved.
104
+ }
105
+ }
106
+ return { entries, compiledPromptHashes: [...new Set(compiledPromptHashes)], promptManifests, contextSnapshots };
107
+ }
108
+
109
+ /** Fold structured ledger calls and a sealed receipt into verdict metrics. */
110
+ export function buildEvalTrackedMetrics(input: BuildEvalTrackedMetricsInput): EvalTrackedMetricsV1 {
111
+ const calls = assistantCalls(input.ledgerEntries);
112
+ const compactionEntries = input.ledgerEntries.filter((entry) => entry.kind === "compactionSummary");
113
+ const compactionUsage = compactionEntries.flatMap((entry) => {
114
+ if (entry.kind !== "compactionSummary" || !isRecord(entry.usage)) return [];
115
+ return [entry.usage];
116
+ });
117
+
118
+ const modelCallReadings: NumericReading[] = calls.map(() => ledgerReading(1));
119
+ for (const entry of compactionEntries) {
120
+ if (entry.kind !== "compactionSummary") continue;
121
+ const apiCalls = isRecord(entry.usage) ? nonNegativeNumber(entry.usage.apiCalls) : null;
122
+ modelCallReadings.push(apiCalls === null ? estimatedReading(1) : ledgerReading(apiCalls));
123
+ }
124
+
125
+ const uncachedReadings = calls.map(uncachedPrefillForCall);
126
+ const cacheReadings = calls.map(cacheReadForCall);
127
+ const generatedReadings = calls.map(generatedForCall);
128
+ for (const usage of compactionUsage) {
129
+ uncachedReadings.push(readingFromUsage(usage, "input"));
130
+ cacheReadings.push(readingFromUsage(usage, "cacheRead"));
131
+ generatedReadings.push(readingFromUsage(usage, "output"));
132
+ }
133
+
134
+ const reasoning = reasoningMetric(input.receipt, calls, compactionUsage);
135
+ const receiptToolMetrics = input.receipt === null ? null : evalHarnessMetricsFromReceipt(input.receipt);
136
+ const ledgerToolCalls = input.ledgerEntries.filter(
137
+ (entry) => entry.kind === "message" && entry.role === "tool_call",
138
+ ).length;
139
+ const ledgerToolErrors = input.ledgerEntries.filter((entry) => {
140
+ if (entry.kind !== "message" || entry.role !== "tool_result" || !isRecord(entry.payload)) return false;
141
+ return entry.payload.isError === true || entry.payload.outcome === "error";
142
+ }).length;
143
+ const receiptToolErrors = input.receipt?.toolStats.reduce((sum, stat) => sum + finiteNonNegative(stat.errors), 0);
144
+ const expectedColdReasons = expectedColdReasonMetrics(calls);
145
+
146
+ return {
147
+ modelCalls: sumReadings(modelCallReadings, "ledger"),
148
+ uncachedPrefillTokens: sumReadings(uncachedReadings, "estimated"),
149
+ cacheReadTokens: sumReadings(cacheReadings, "estimated"),
150
+ generatedTokens: sumReadings(generatedReadings, "estimated"),
151
+ reasoningTokens: reasoning,
152
+ toolCalls:
153
+ receiptToolMetrics === null
154
+ ? { value: ledgerToolCalls, source: "ledger" }
155
+ : { value: receiptToolMetrics.toolCalls, source: "receipt" },
156
+ toolErrors:
157
+ receiptToolErrors === undefined
158
+ ? { value: ledgerToolErrors, source: "ledger" }
159
+ : { value: receiptToolErrors, source: "receipt" },
160
+ ttftMsFirstCall: firstCallTtft(calls),
161
+ wallClockMs: wallClockMetric(input.receipt, input.fallbackWallClockMs),
162
+ contextTokensAtEnd: contextTokensAtEnd(calls, compactionEntries),
163
+ compactions: { value: compactionEntries.length, source: "ledger" },
164
+ expectedColdReasons,
165
+ };
166
+ }
167
+
168
+ export function emptyEvalTrackedMetrics(source: EvalMetricSource = "estimated"): EvalTrackedMetricsV1 {
169
+ const zero = (): EvalSourcedNumber => ({ value: 0, source });
170
+ return {
171
+ modelCalls: zero(),
172
+ uncachedPrefillTokens: zero(),
173
+ cacheReadTokens: zero(),
174
+ generatedTokens: zero(),
175
+ reasoningTokens: { value: null, source },
176
+ toolCalls: zero(),
177
+ toolErrors: zero(),
178
+ ttftMsFirstCall: zero(),
179
+ wallClockMs: zero(),
180
+ contextTokensAtEnd: zero(),
181
+ compactions: zero(),
182
+ expectedColdReasons: {},
183
+ };
184
+ }
185
+
186
+ function assistantCalls(entries: ReadonlyArray<SessionEntry>): AssistantCall[] {
187
+ return entries.flatMap((entry) => {
188
+ if (entry.kind !== "message" || entry.role !== "assistant" || !isRecord(entry.payload)) return [];
189
+ const promptCache = recordField(entry.payload, "promptCache");
190
+ const timing = recordField(entry.payload, "timing");
191
+ const usage = recordField(entry.payload, "usage");
192
+ if (promptCache === null && timing === null && usage === null) return [];
193
+ return [
194
+ {
195
+ payload: entry.payload,
196
+ promptCache,
197
+ backend: promptCache === null ? null : recordField(promptCache, "backend"),
198
+ timing,
199
+ usage,
200
+ },
201
+ ];
202
+ });
203
+ }
204
+
205
+ function uncachedPrefillForCall(call: AssistantCall): NumericReading {
206
+ const promptTokens = call.backend === null ? null : nonNegativeNumber(call.backend.promptTokens);
207
+ const cachedTokens = call.backend === null ? null : nonNegativeNumber(call.backend.cachedTokens);
208
+ if (promptTokens !== null && cachedTokens !== null && cachedTokens <= promptTokens) {
209
+ return ledgerReading(promptTokens - cachedTokens);
210
+ }
211
+ const piInput = call.promptCache === null ? null : nonNegativeNumber(call.promptCache.input);
212
+ if (piInput !== null) return ledgerReading(piInput);
213
+ const legacyInput = call.usage === null ? null : nonNegativeNumber(call.usage.input);
214
+ return legacyInput === null ? estimatedReading(0) : estimatedReading(legacyInput);
215
+ }
216
+
217
+ function cacheReadForCall(call: AssistantCall): NumericReading {
218
+ const cachedTokens = call.backend === null ? null : nonNegativeNumber(call.backend.cachedTokens);
219
+ if (cachedTokens !== null) return ledgerReading(cachedTokens);
220
+ const piCacheRead = call.promptCache === null ? null : nonNegativeNumber(call.promptCache.cacheRead);
221
+ if (piCacheRead !== null) return ledgerReading(piCacheRead);
222
+ const legacyCacheRead = call.usage === null ? null : nonNegativeNumber(call.usage.cacheRead);
223
+ return legacyCacheRead === null ? estimatedReading(0) : estimatedReading(legacyCacheRead);
224
+ }
225
+
226
+ function generatedForCall(call: AssistantCall): NumericReading {
227
+ const predictedTokens = call.backend === null ? null : nonNegativeNumber(call.backend.predictedTokens);
228
+ if (predictedTokens !== null) return ledgerReading(predictedTokens);
229
+ const output = call.usage === null ? null : nonNegativeNumber(call.usage.output);
230
+ return output === null ? estimatedReading(0) : ledgerReading(output);
231
+ }
232
+
233
+ function readingFromUsage(usage: Record<string, unknown>, field: string): NumericReading {
234
+ const value = nonNegativeNumber(usage[field]);
235
+ return value === null ? estimatedReading(0) : ledgerReading(value);
236
+ }
237
+
238
+ function reasoningMetric(
239
+ receipt: RunReceipt | null,
240
+ calls: ReadonlyArray<AssistantCall>,
241
+ compactionUsage: ReadonlyArray<Record<string, unknown>>,
242
+ ): EvalSourcedNullableNumber {
243
+ if (receipt !== null && typeof receipt.reasoningTokenCount === "number") {
244
+ return { value: finiteNonNegative(receipt.reasoningTokenCount), source: "receipt" };
245
+ }
246
+ let total = 0;
247
+ let measured = false;
248
+ for (const call of calls) {
249
+ const value = extractReasoningTokens(call.usage);
250
+ if (value === null) continue;
251
+ measured = true;
252
+ total += finiteNonNegative(value);
253
+ }
254
+ for (const usage of compactionUsage) {
255
+ const value = nonNegativeNumber(usage.reasoning);
256
+ if (value === null) continue;
257
+ measured = true;
258
+ total += value;
259
+ }
260
+ return measured ? { value: total, source: "ledger" } : { value: null, source: "estimated" };
261
+ }
262
+
263
+ function firstCallTtft(calls: ReadonlyArray<AssistantCall>): EvalSourcedNumber {
264
+ const first = calls[0];
265
+ const value = first?.timing === null || first?.timing === undefined ? null : nonNegativeNumber(first.timing.ttftMs);
266
+ return value === null ? { value: 0, source: "estimated" } : { value, source: "ledger" };
267
+ }
268
+
269
+ function wallClockMetric(receipt: RunReceipt | null, fallback: number): EvalSourcedNumber {
270
+ if (receipt !== null) {
271
+ const started = Date.parse(receipt.startedAt);
272
+ const ended = Date.parse(receipt.endedAt);
273
+ if (Number.isFinite(started) && Number.isFinite(ended) && ended >= started) {
274
+ return { value: ended - started, source: "receipt" };
275
+ }
276
+ }
277
+ return { value: finiteNonNegative(fallback), source: "estimated" };
278
+ }
279
+
280
+ function contextTokensAtEnd(
281
+ calls: ReadonlyArray<AssistantCall>,
282
+ compactions: ReadonlyArray<SessionEntry>,
283
+ ): EvalSourcedNumber {
284
+ const lastCall = calls.at(-1);
285
+ if (lastCall !== undefined) {
286
+ const lastReading = contextForCall(lastCall);
287
+ if (lastReading !== null && lastReading > 0) return { value: lastReading, source: "ledger" };
288
+ if (lastReading === 0) {
289
+ for (const call of [...calls.slice(0, -1)].reverse()) {
290
+ const reading = contextForCall(call);
291
+ if (reading !== null && reading > 0) return { value: reading, source: "ledger" };
292
+ }
293
+ }
294
+ }
295
+ const lastCompaction = compactions.at(-1);
296
+ if (lastCompaction?.kind === "compactionSummary") {
297
+ const tokensAfter = nonNegativeNumber(lastCompaction.tokensAfter);
298
+ if (tokensAfter !== null) return { value: tokensAfter, source: "ledger" };
299
+ }
300
+ return { value: 0, source: "estimated" };
301
+ }
302
+
303
+ function contextForCall(call: AssistantCall): number | null {
304
+ const promptTokens = call.backend === null ? null : nonNegativeNumber(call.backend.promptTokens);
305
+ const predictedTokens = call.backend === null ? null : nonNegativeNumber(call.backend.predictedTokens);
306
+ if (promptTokens !== null && predictedTokens !== null) return promptTokens + predictedTokens;
307
+ const input = call.promptCache === null ? null : nonNegativeNumber(call.promptCache.input);
308
+ const cacheRead = call.promptCache === null ? null : nonNegativeNumber(call.promptCache.cacheRead);
309
+ const output = call.usage === null ? null : nonNegativeNumber(call.usage.output);
310
+ return input === null || cacheRead === null || output === null ? null : input + cacheRead + output;
311
+ }
312
+
313
+ function expectedColdReasonMetrics(calls: ReadonlyArray<AssistantCall>): Record<string, EvalSourcedNumber> {
314
+ const counts = new Map<string, number>();
315
+ for (const call of calls) {
316
+ const reasons = call.promptCache?.expectedColdReasons;
317
+ if (!Array.isArray(reasons)) continue;
318
+ const unique = new Set(reasons.filter((reason): reason is string => typeof reason === "string" && reason.length > 0));
319
+ for (const reason of unique) counts.set(reason, (counts.get(reason) ?? 0) + 1);
320
+ }
321
+ return Object.fromEntries(
322
+ [...counts.entries()]
323
+ .sort(([left], [right]) => left.localeCompare(right))
324
+ .map(([reason, value]) => [reason, { value, source: "ledger" as const }]),
325
+ );
326
+ }
327
+
328
+ function sumReadings(readings: ReadonlyArray<NumericReading>, emptySource: EvalMetricSource): EvalSourcedNumber {
329
+ if (readings.length === 0) return { value: 0, source: emptySource };
330
+ return {
331
+ value: readings.reduce((sum, reading) => sum + reading.value, 0),
332
+ source: readings.some((reading) => reading.source === "estimated") ? "estimated" : "ledger",
333
+ };
334
+ }
335
+
336
+ function ledgerReading(value: number): NumericReading {
337
+ return { value: finiteNonNegative(value), source: "ledger" };
338
+ }
339
+
340
+ function estimatedReading(value: number): NumericReading {
341
+ return { value: finiteNonNegative(value), source: "estimated" };
342
+ }
343
+
344
+ function finiteNonNegative(value: number): number {
345
+ return Number.isFinite(value) && value >= 0 ? value : 0;
346
+ }
347
+
348
+ function nonNegativeNumber(value: unknown): number | null {
349
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
350
+ }
351
+
352
+ function recordField(record: Record<string, unknown>, field: string): Record<string, unknown> | null {
353
+ return isRecord(record[field]) ? record[field] : null;
354
+ }
355
+
356
+ function parseJsonRecord(line: string): Record<string, unknown> | null {
357
+ if (line.trim().length === 0) return null;
358
+ try {
359
+ const parsed: unknown = JSON.parse(line);
360
+ return isRecord(parsed) ? parsed : null;
361
+ } catch {
362
+ return null;
363
+ }
364
+ }
365
+
366
+ function promptManifestObservation(record: Record<string, unknown>): EvalPromptManifestObservation | null {
367
+ const systemPromptHash = nullableDigest(record.systemPromptHash);
368
+ if (systemPromptHash === null || !Array.isArray(record.fragments)) return null;
369
+ const fragments = record.fragments.flatMap((entry) => {
370
+ if (!isRecord(entry) || typeof entry.id !== "string") return [];
371
+ const contentHash = nullableDigest(entry.contentHash);
372
+ return contentHash === null ? [] : [{ id: entry.id, contentHash }];
373
+ });
374
+ const preload = record.projectPreload;
375
+ const projectPreload: EvalPromptManifestObservation["projectPreload"] =
376
+ preload === null
377
+ ? null
378
+ : isRecord(preload) &&
379
+ (preload.mode === "full" || preload.mode === "synopsis" || preload.mode === "none") &&
380
+ typeof preload.chars === "number" &&
381
+ Number.isInteger(preload.chars) &&
382
+ typeof preload.lines === "number" &&
383
+ Number.isInteger(preload.lines) &&
384
+ typeof preload.nearLimit === "boolean" &&
385
+ typeof preload.label === "string"
386
+ ? {
387
+ mode: preload.mode as "full" | "synopsis" | "none",
388
+ chars: preload.chars,
389
+ lines: preload.lines,
390
+ reason: nullableString(preload.reason),
391
+ nearLimit: preload.nearLimit,
392
+ label: preload.label,
393
+ }
394
+ : null;
395
+ return {
396
+ systemPromptHash,
397
+ thinkingLevel: nullableString(record.thinkingLevel),
398
+ projectPreload,
399
+ fragments,
400
+ };
401
+ }
402
+
403
+ function nullableString(value: unknown): string | null {
404
+ return typeof value === "string" && value.length > 0 ? value : null;
405
+ }
406
+
407
+ function nullableDigest(value: unknown): string | null {
408
+ return typeof value === "string" && /^[a-f0-9]{64}$/u.test(value) ? value : null;
409
+ }
410
+
411
+ function isRecord(value: unknown): value is Record<string, unknown> {
412
+ return typeof value === "object" && value !== null && !Array.isArray(value);
413
+ }