@iowarp/clio-coder 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +7 -3
  3. package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
  4. package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
  7. package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
  11. package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
  13. package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
  14. package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
  17. package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
  18. package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
  21. package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
  22. package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
  23. package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
  27. package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
  28. package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
  29. package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
  30. package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
  31. package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
  32. package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
  33. package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
  34. package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
  35. package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
  36. package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
  37. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  38. package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
  39. package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
  40. package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
  41. package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
  42. package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
  43. package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
  44. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  45. package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
  46. package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
  47. package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
  48. package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
  49. package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
  50. package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
  51. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  52. package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
  53. package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
  54. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  55. package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
  56. package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
  57. package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
  58. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  59. package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
  60. package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
  61. package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
  62. package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
  63. package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
  64. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  65. package/dist/chunk-RKKLTLYB.js +45 -0
  66. package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
  67. package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
  68. package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
  69. package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
  70. package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
  71. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  72. package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
  73. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  74. package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
  75. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  76. package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
  77. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  78. package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
  79. package/dist/chunk-YSEHGPCT.js +127 -0
  80. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  81. package/dist/cli/index.js +27 -27
  82. package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
  83. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
  84. package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
  85. package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
  86. package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
  87. package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
  88. package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
  89. package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
  90. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  91. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
  92. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
  93. package/dist/doctor-TWBWFK5V.js +165 -0
  94. package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
  95. package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
  96. package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
  97. package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
  98. package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
  99. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
  100. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
  101. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
  102. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
  103. package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
  104. package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
  105. package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
  106. package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
  107. package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
  108. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
  109. package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
  110. package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
  111. package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
  112. package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
  113. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
  114. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  115. package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
  116. package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
  117. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
  118. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  119. package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
  120. package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
  121. package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
  122. package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
  123. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
  124. package/dist/worker/entry.js +29 -33
  125. package/docs/alcf-provider.md +1 -1
  126. package/docs/architecture.md +1 -1
  127. package/docs/artifact-versions.md +6 -1
  128. package/docs/built-in-agents.md +1 -1
  129. package/docs/capacity-and-scheduling.md +23 -2
  130. package/docs/commands-and-modes.md +1 -1
  131. package/docs/configuration-and-targets.md +30 -3
  132. package/docs/context-engine.md +63 -4
  133. package/docs/documentation-coverage.md +3 -3
  134. package/docs/documentation-guide.md +1 -1
  135. package/docs/environment-variables.md +2 -0
  136. package/docs/eval-runner.md +262 -11
  137. package/docs/evals-internal.md +72 -2
  138. package/docs/evidence-and-memory.md +11 -10
  139. package/docs/evolution.md +1 -1
  140. package/docs/extensions-and-sharing.md +3 -1
  141. package/docs/fleet-dispatch.md +4 -4
  142. package/docs/installation-and-lifecycle.md +1 -1
  143. package/docs/middleware-and-components.md +1 -1
  144. package/docs/model-catalog.md +1 -1
  145. package/docs/observability.md +53 -2
  146. package/docs/proactive-memory.md +127 -14
  147. package/docs/prompt-envelope-and-tools.md +19 -1
  148. package/docs/provider-adapter-cookbook.md +1 -1
  149. package/docs/release-cut-checklist.md +19 -3
  150. package/docs/safety-model.md +1 -1
  151. package/docs/scientific-validation.md +1 -1
  152. package/docs/skills-marketplace.md +1 -1
  153. package/docs/tool-usage.md +1 -1
  154. package/docs/trace-store.md +1 -1
  155. package/docs/troubleshooting.md +87 -0
  156. package/docs/tui-design.md +1 -1
  157. package/docs/worker-dispatch-mechanics.md +1 -1
  158. package/package.json +2 -1
  159. package/src/cli/agents.ts +1 -1
  160. package/src/cli/config-inspect.ts +33 -6
  161. package/src/cli/config.ts +1 -1
  162. package/src/cli/doctor-state-size.ts +82 -0
  163. package/src/cli/doctor.ts +3 -1
  164. package/src/cli/eval.ts +80 -16
  165. package/src/cli/extensions.ts +5 -1
  166. package/src/cli/fleet.ts +32 -3
  167. package/src/cli/targets.ts +44 -13
  168. package/src/cli/trace.ts +63 -4
  169. package/src/cli/usage.ts +63 -14
  170. package/src/core/bus-events.ts +29 -1
  171. package/src/core/cache-telemetry.ts +42 -0
  172. package/src/core/config.ts +18 -0
  173. package/src/core/defaults.ts +36 -6
  174. package/src/core/endpoint-key.ts +27 -0
  175. package/src/core/residency-target-key.ts +25 -0
  176. package/src/core/response-schema.ts +36 -2
  177. package/src/domains/config/classify.ts +3 -0
  178. package/src/domains/context/codewiki/coordinator.ts +12 -4
  179. package/src/domains/dispatch/admission.ts +40 -3
  180. package/src/domains/dispatch/capacity-lease.ts +98 -9
  181. package/src/domains/dispatch/contract.ts +11 -0
  182. package/src/domains/dispatch/execution-plan.ts +44 -4
  183. package/src/domains/dispatch/extension.ts +166 -42
  184. package/src/domains/dispatch/fleet-run.ts +23 -3
  185. package/src/domains/dispatch/heartbeat.ts +32 -8
  186. package/src/domains/dispatch/index.ts +3 -0
  187. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  188. package/src/domains/dispatch/reservation-store.ts +116 -8
  189. package/src/domains/dispatch/state.ts +4 -0
  190. package/src/domains/dispatch/worker-spawn.ts +25 -11
  191. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  192. package/src/domains/dispatch/write-boundary.ts +62 -1
  193. package/src/domains/eval/artifacts/store.ts +62 -0
  194. package/src/domains/eval/compare/behavioral.ts +224 -0
  195. package/src/domains/eval/compare/compare.ts +355 -2
  196. package/src/domains/eval/compare/envelope.ts +128 -0
  197. package/src/domains/eval/compare/gates.ts +24 -6
  198. package/src/domains/eval/compare/thresholds.ts +30 -3
  199. package/src/domains/eval/execution-provenance.ts +240 -0
  200. package/src/domains/eval/metrics/aggregate.ts +136 -0
  201. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  202. package/src/domains/eval/metrics/tracked.ts +413 -0
  203. package/src/domains/eval/provenance.ts +117 -0
  204. package/src/domains/eval/reports/comparison.ts +128 -0
  205. package/src/domains/eval/reports/junit.ts +17 -3
  206. package/src/domains/eval/reports/markdown.ts +3 -3
  207. package/src/domains/eval/reports/text.ts +14 -0
  208. package/src/domains/eval/run-compare.ts +20 -0
  209. package/src/domains/eval/runners/clio-run.ts +127 -0
  210. package/src/domains/eval/runners/external-command.ts +28 -3
  211. package/src/domains/eval/schema/adapter.ts +111 -0
  212. package/src/domains/eval/schema/artifact.ts +20 -0
  213. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  214. package/src/domains/eval/schema/behavioral.ts +520 -0
  215. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  216. package/src/domains/eval/schema/serving.ts +74 -0
  217. package/src/domains/eval/schema/suite.ts +38 -8
  218. package/src/domains/eval/schema/validate.ts +58 -3
  219. package/src/domains/eval/schema/verdict.ts +237 -0
  220. package/src/domains/eval/suites/resolve.ts +2 -0
  221. package/src/domains/eval/suites/run.ts +264 -33
  222. package/src/domains/eval/verifiers/command.ts +2 -1
  223. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  224. package/src/domains/evidence/build.ts +2 -13
  225. package/src/domains/evidence/eval.ts +2 -12
  226. package/src/domains/evidence/findings-markdown.ts +33 -0
  227. package/src/domains/evidence/run-trust.ts +7 -113
  228. package/src/domains/evidence/trust-projection.ts +2 -2
  229. package/src/domains/extensions/compatibility.ts +285 -0
  230. package/src/domains/extensions/discovery.ts +38 -3
  231. package/src/domains/extensions/resources.ts +1 -1
  232. package/src/domains/extensions/state.ts +12 -3
  233. package/src/domains/extensions/types.ts +2 -0
  234. package/src/domains/lifecycle/doctor.ts +69 -1
  235. package/src/domains/memory/index.ts +14 -0
  236. package/src/domains/memory/task-bank-promotion.ts +64 -0
  237. package/src/domains/memory/task-memory-policy.ts +77 -8
  238. package/src/domains/memory/task-memory-spend.ts +131 -0
  239. package/src/domains/memory/task-memory-status.ts +7 -0
  240. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  241. package/src/domains/middleware/index.ts +1 -0
  242. package/src/domains/middleware/memory-intervention.ts +69 -5
  243. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  244. package/src/domains/observability/background-memory-usage.ts +140 -0
  245. package/src/domains/observability/cost.ts +1 -1
  246. package/src/domains/observability/index.ts +7 -0
  247. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  248. package/src/domains/observability/trace-store.ts +192 -2
  249. package/src/domains/prompts/compiler.ts +100 -13
  250. package/src/domains/providers/endpoint-capacity.ts +96 -0
  251. package/src/domains/providers/index.ts +10 -0
  252. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  253. package/src/domains/providers/runtime-resolution.ts +8 -1
  254. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  255. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  256. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  257. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  258. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  259. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  260. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  261. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  262. package/src/domains/providers/types/capability-flags.ts +2 -0
  263. package/src/domains/providers/types/target-descriptor.ts +2 -0
  264. package/src/domains/resources/prompts/loader.ts +95 -33
  265. package/src/domains/safety/call-target.ts +52 -0
  266. package/src/domains/safety/run-effects.ts +35 -4
  267. package/src/domains/session/context-accounting.ts +52 -1
  268. package/src/domains/session/context-ledger.ts +37 -13
  269. package/src/domains/session/index.ts +6 -0
  270. package/src/domains/session/prompt-cache.ts +140 -0
  271. package/src/domains/session/prompt-manifest.ts +42 -0
  272. package/src/engine/acp/adapter.ts +18 -3
  273. package/src/engine/ai.ts +35 -0
  274. package/src/engine/apis/llamacpp-residency.ts +55 -3
  275. package/src/engine/apis/lmstudio.ts +25 -5
  276. package/src/engine/apis/ollama-native.ts +2 -1
  277. package/src/engine/apis/openai-completions.ts +80 -17
  278. package/src/engine/apis/residency-lock.ts +3 -1
  279. package/src/engine/apis/residency.ts +34 -1
  280. package/src/engine/provider-payload.ts +29 -1
  281. package/src/entry/orchestrator.ts +176 -30
  282. package/src/interactive/chat-loop-messages.ts +26 -7
  283. package/src/interactive/chat-loop.ts +318 -41
  284. package/src/interactive/chat-panel.ts +62 -8
  285. package/src/interactive/clio-editor.ts +45 -8
  286. package/src/interactive/context-activity.ts +5 -1
  287. package/src/interactive/context-meter.ts +1 -1
  288. package/src/interactive/context-overlay.ts +40 -10
  289. package/src/interactive/cost-overlay.ts +64 -6
  290. package/src/interactive/dispatch-board.ts +84 -12
  291. package/src/interactive/fleet-run-preview.ts +41 -15
  292. package/src/interactive/handoff-round.ts +41 -2
  293. package/src/interactive/interactive-application.ts +24 -1
  294. package/src/interactive/interactive-input-runtime.ts +8 -0
  295. package/src/interactive/interactive-presentation.ts +4 -0
  296. package/src/interactive/interactive-shell.ts +20 -17
  297. package/src/interactive/interactive-slash-runtime.ts +27 -4
  298. package/src/interactive/memory-overlay.ts +8 -0
  299. package/src/interactive/mutation-preview.ts +295 -0
  300. package/src/interactive/overlay-general-openers.ts +16 -0
  301. package/src/interactive/overlay-key-routing.ts +38 -0
  302. package/src/interactive/overlay-lifecycle.ts +38 -5
  303. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  304. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  305. package/src/interactive/overlays/ask-user.ts +91 -19
  306. package/src/interactive/overlays/help-reference.ts +4 -0
  307. package/src/interactive/overlays/prompts.ts +11 -1
  308. package/src/interactive/overlays/settings.ts +35 -1
  309. package/src/interactive/permission-hint.ts +34 -2
  310. package/src/interactive/permission-overlay.ts +159 -9
  311. package/src/interactive/prewarm.ts +197 -0
  312. package/src/interactive/render-trace.ts +162 -15
  313. package/src/interactive/renderers/tool-execution.ts +4 -0
  314. package/src/interactive/side-question.ts +58 -1
  315. package/src/interactive/status/controller.ts +11 -0
  316. package/src/interactive/status/state-machine.ts +54 -2
  317. package/src/interactive/status/types.ts +7 -0
  318. package/src/interactive/terminal-lease.ts +2 -0
  319. package/src/interactive/turn-context.ts +299 -31
  320. package/src/interactive/turn-persistence.ts +14 -4
  321. package/src/interactive/turn-prewarm.ts +364 -0
  322. package/src/interactive/turn-queues.ts +7 -4
  323. package/src/interactive/turn-runtime.ts +8 -1
  324. package/src/interactive/turn-state.ts +23 -0
  325. package/src/interactive/view/view-overlay.ts +28 -3
  326. package/src/tools/ask-user.ts +43 -2
  327. package/src/tools/dispatch-plan.ts +17 -9
  328. package/src/tools/dispatch-scout.ts +1 -1
  329. package/src/tools/registry.ts +16 -0
  330. package/dist/chunk-AOCYTWAV.js +0 -449
  331. package/dist/chunk-HWUFFB6L.js +0 -83
  332. package/dist/chunk-R346GLFC.js +0 -31
  333. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -0,0 +1,128 @@
1
+ import type { EvalArtifactResultV4 } from "../schema/artifact.js";
2
+ import type { EvalExecutionEnvelopeV1, EvalExecutionMatrixDimensionV1 } from "../schema/execution-envelope.js";
3
+
4
+ export interface EvalEnvelopeMismatchV1 {
5
+ scenarioId: string;
6
+ role: string;
7
+ target: { id: string; model: string | null };
8
+ fields: string[];
9
+ }
10
+
11
+ export interface EvalEnvelopeComparabilityV1 {
12
+ comparable: boolean;
13
+ mismatchedFields: string[];
14
+ }
15
+
16
+ export function compareEvalExecutionEnvelopesV1(
17
+ identity: { scenarioId: string; role: string; target: { id: string; model: string | null } },
18
+ baseline: ReadonlyArray<EvalArtifactResultV4>,
19
+ candidate: ReadonlyArray<EvalArtifactResultV4>,
20
+ baselineDimensions: ReadonlyArray<EvalExecutionMatrixDimensionV1>,
21
+ candidateDimensions: ReadonlyArray<EvalExecutionMatrixDimensionV1>,
22
+ ): EvalEnvelopeMismatchV1 | null {
23
+ const leftDimensions = [...baselineDimensions].sort();
24
+ const rightDimensions = [...candidateDimensions].sort();
25
+ if (stableJson(leftDimensions) !== stableJson(rightDimensions)) {
26
+ return { ...identity, fields: ["matrix.dimensions"] };
27
+ }
28
+ if (
29
+ (baseline.some((result) => result.executionEnvelope !== undefined) &&
30
+ baseline.some((result) => result.executionEnvelope === undefined)) ||
31
+ (candidate.some((result) => result.executionEnvelope !== undefined) &&
32
+ candidate.some((result) => result.executionEnvelope === undefined))
33
+ ) {
34
+ return { ...identity, fields: ["executionEnvelope.missingTrial"] };
35
+ }
36
+ const ignored = new Set(leftDimensions);
37
+ const baselineEnvelopes = uniqueEnvelopes(baseline, ignored);
38
+ const candidateEnvelopes = uniqueEnvelopes(candidate, ignored);
39
+ if (baselineEnvelopes.length === 0 && candidateEnvelopes.length === 0) return null;
40
+ if (baselineEnvelopes.length === 0 || candidateEnvelopes.length === 0) {
41
+ return { ...identity, fields: ["executionEnvelope"] };
42
+ }
43
+ if (baselineEnvelopes.length > 1 || candidateEnvelopes.length > 1) {
44
+ return { ...identity, fields: ["executionEnvelope.withinRunVariance"] };
45
+ }
46
+ const left = baselineEnvelopes[0];
47
+ const right = candidateEnvelopes[0];
48
+ if (left === undefined || right === undefined || stableJson(left) === stableJson(right)) return null;
49
+ return { ...identity, fields: differingFields(left, right, ignored) };
50
+ }
51
+
52
+ function uniqueEnvelopes(
53
+ results: ReadonlyArray<EvalArtifactResultV4>,
54
+ ignored: ReadonlySet<EvalExecutionMatrixDimensionV1>,
55
+ ): EvalExecutionEnvelopeV1[] {
56
+ const byIdentity = new Map<string, EvalExecutionEnvelopeV1>();
57
+ for (const result of results) {
58
+ if (result.executionEnvelope === undefined) continue;
59
+ const normalized = normalizedEnvelope(result.executionEnvelope, ignored);
60
+ byIdentity.set(stableJson(normalized), normalized);
61
+ }
62
+ return [...byIdentity.values()];
63
+ }
64
+
65
+ function normalizedEnvelope(
66
+ envelope: EvalExecutionEnvelopeV1,
67
+ ignored: ReadonlySet<EvalExecutionMatrixDimensionV1>,
68
+ ): EvalExecutionEnvelopeV1 {
69
+ return {
70
+ ...envelope,
71
+ prompt: ignored.has("prompt") ? { fragments: [], compositionHash: null } : envelope.prompt,
72
+ recipe: ignored.has("recipe") ? null : envelope.recipe,
73
+ target: ignored.has("target") ? "<matrix>" : envelope.target,
74
+ wireModel: ignored.has("wireModel") ? null : envelope.wireModel,
75
+ runtime: ignored.has("runtime") ? null : envelope.runtime,
76
+ thinkingLevel: ignored.has("thinkingLevel") ? null : envelope.thinkingLevel,
77
+ toolSignature: ignored.has("toolSignature") ? null : envelope.toolSignature,
78
+ autonomy: ignored.has("autonomy") ? null : envelope.autonomy,
79
+ policyHashes: ignored.has("policy") ? { rulePack: null, project: null } : envelope.policyHashes,
80
+ projectContext: ignored.has("projectContext")
81
+ ? {
82
+ kind: "none",
83
+ tier: null,
84
+ contentHash: null,
85
+ chars: null,
86
+ sections: [],
87
+ rulesApplied: [],
88
+ operatorProfileApplied: null,
89
+ }
90
+ : envelope.projectContext,
91
+ corpus: ignored.has("corpus") ? { id: "<matrix>", version: "<matrix>" } : envelope.corpus,
92
+ };
93
+ }
94
+
95
+ function differingFields(
96
+ left: EvalExecutionEnvelopeV1,
97
+ right: EvalExecutionEnvelopeV1,
98
+ ignored: ReadonlySet<EvalExecutionMatrixDimensionV1>,
99
+ ): string[] {
100
+ const fields: Array<[EvalExecutionMatrixDimensionV1, string, unknown, unknown]> = [
101
+ ["prompt", "prompt", left.prompt, right.prompt],
102
+ ["recipe", "recipe", left.recipe, right.recipe],
103
+ ["target", "target", left.target, right.target],
104
+ ["wireModel", "wireModel", left.wireModel, right.wireModel],
105
+ ["runtime", "runtime", left.runtime, right.runtime],
106
+ ["thinkingLevel", "thinkingLevel", left.thinkingLevel, right.thinkingLevel],
107
+ ["toolSignature", "toolSignature", left.toolSignature, right.toolSignature],
108
+ ["autonomy", "autonomy", left.autonomy, right.autonomy],
109
+ ["policy", "policyHashes", left.policyHashes, right.policyHashes],
110
+ ["projectContext", "projectContext", left.projectContext, right.projectContext],
111
+ ["corpus", "corpus", left.corpus, right.corpus],
112
+ ];
113
+ return fields.flatMap(([dimension, field, baseline, candidate]) =>
114
+ ignored.has(dimension) || stableJson(baseline) === stableJson(candidate) ? [] : [field],
115
+ );
116
+ }
117
+
118
+ function stableJson(value: unknown): string {
119
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
120
+ if (typeof value === "object" && value !== null) {
121
+ return `{${Object.entries(value as Record<string, unknown>)
122
+ .filter(([, entry]) => entry !== undefined)
123
+ .sort(([left], [right]) => left.localeCompare(right))
124
+ .map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
125
+ .join(",")}}`;
126
+ }
127
+ return JSON.stringify(value);
128
+ }
@@ -15,6 +15,7 @@ export interface EvalGateFailure {
15
15
  export interface EvalGateResult {
16
16
  pass: boolean;
17
17
  failures: EvalGateFailure[];
18
+ informational: EvalGateFailure[];
18
19
  }
19
20
 
20
21
  /**
@@ -30,21 +31,30 @@ export interface EvalGateResult {
30
31
  * collapsing the matrix into a single aggregate nobody can act on.
31
32
  */
32
33
  export function evaluateGate(artifact: EvalArtifactV4, thresholds: EvalSuiteThresholdsV2): EvalGateResult {
33
- const failures: EvalGateFailure[] = [];
34
- for (const assertion of thresholds.fail) {
34
+ const failures = evaluateAssertions(artifact, thresholds.fail);
35
+ const informational = evaluateAssertions(artifact, thresholds.informational ?? []);
36
+ return { pass: failures.length === 0, failures, informational };
37
+ }
38
+
39
+ function evaluateAssertions(
40
+ artifact: EvalArtifactV4,
41
+ assertions: ReadonlyArray<EvalMetricAssertion>,
42
+ ): EvalGateFailure[] {
43
+ const findings: EvalGateFailure[] = [];
44
+ for (const assertion of assertions) {
35
45
  const whole = resolveMetricAssertion(assertion, {}, artifact);
36
46
  if (!whole.unresolved) {
37
- if (whole.holds) failures.push({ assertion, actual: whole.actual, unresolved: false });
47
+ if (whole.holds) findings.push({ assertion, actual: whole.actual, unresolved: false });
38
48
  continue;
39
49
  }
40
50
  if (artifact.results.length === 0) {
41
- failures.push({ assertion, actual: null, unresolved: true });
51
+ findings.push({ assertion, actual: null, unresolved: true });
42
52
  continue;
43
53
  }
44
54
  for (const result of artifact.results) {
45
55
  const perRun = resolveMetricAssertion(assertion, result.metrics);
46
56
  if (!perRun.unresolved && !perRun.holds) continue;
47
- failures.push({
57
+ findings.push({
48
58
  assertion,
49
59
  actual: perRun.actual,
50
60
  unresolved: perRun.unresolved,
@@ -53,7 +63,7 @@ export function evaluateGate(artifact: EvalArtifactV4, thresholds: EvalSuiteThre
53
63
  });
54
64
  }
55
65
  }
56
- return { pass: failures.length === 0, failures };
66
+ return findings;
57
67
  }
58
68
 
59
69
  /** One operator-facing line per gate failure, naming the run when there is one. */
@@ -64,3 +74,11 @@ export function renderGateFailure(failure: EvalGateFailure): string {
64
74
  ? ` ${metric}${run}: unresolved metric (fail closed)\n`
65
75
  : ` ${metric} ${op} ${JSON.stringify(value)}${run}: actual ${JSON.stringify(failure.actual)}\n`;
66
76
  }
77
+
78
+ export function renderInformationalBudget(finding: EvalGateFailure): string {
79
+ const run = finding.taskId === undefined ? "" : ` [${finding.taskId}#${finding.repeatIndex ?? 0}]`;
80
+ const { metric, op, value } = finding.assertion;
81
+ return finding.unresolved
82
+ ? ` ${metric}${run}: unmeasured informational budget\n`
83
+ : ` ${metric} ${op} ${JSON.stringify(value)}${run}: actual ${JSON.stringify(finding.actual)}\n`;
84
+ }
@@ -5,13 +5,40 @@ import type { EvalMetricAssertion, EvalSuiteThresholdsV2 } from "../schema/suite
5
5
 
6
6
  export function loadThresholds(path: string): EvalSuiteThresholdsV2 {
7
7
  const parsed = parseYaml(readFileSync(path, "utf8")) as unknown;
8
- if (isRecord(parsed) && Array.isArray(parsed.fail)) return { fail: parsed.fail as EvalMetricAssertion[] };
9
- if (isRecord(parsed) && isRecord(parsed.thresholds) && Array.isArray(parsed.thresholds.fail)) {
10
- return { fail: parsed.thresholds.fail as EvalMetricAssertion[] };
8
+ const root = isRecord(parsed) && isRecord(parsed.thresholds) ? parsed.thresholds : parsed;
9
+ if (isRecord(root) && (Array.isArray(root.fail) || Array.isArray(root.informational))) {
10
+ return {
11
+ fail: parseAssertions(root.fail, `${path}.fail`),
12
+ informational: parseAssertions(root.informational, `${path}.informational`),
13
+ };
11
14
  }
12
15
  throw new Error(`invalid thresholds file: ${path}`);
13
16
  }
14
17
 
18
+ function parseAssertions(value: unknown, source: string): EvalMetricAssertion[] {
19
+ if (value === undefined) return [];
20
+ if (!Array.isArray(value)) throw new Error(`${source}: expected array`);
21
+ return value.map((entry, index) => {
22
+ if (!isRecord(entry)) throw new Error(`${source}[${index}]: expected object`);
23
+ if (typeof entry.metric !== "string" || entry.metric.length === 0) {
24
+ throw new Error(`${source}[${index}].metric: expected non-empty string`);
25
+ }
26
+ if (!isOp(entry.op)) throw new Error(`${source}[${index}].op: expected lt, lte, gt, gte, eq, or neq`);
27
+ if (!isScalar(entry.value)) throw new Error(`${source}[${index}].value: expected scalar`);
28
+ return { metric: entry.metric, op: entry.op, value: entry.value };
29
+ });
30
+ }
31
+
32
+ function isOp(value: unknown): value is EvalMetricAssertion["op"] {
33
+ return value === "lt" || value === "lte" || value === "gt" || value === "gte" || value === "eq" || value === "neq";
34
+ }
35
+
36
+ function isScalar(value: unknown): value is number | string | boolean {
37
+ return (
38
+ (typeof value === "number" && Number.isFinite(value)) || typeof value === "string" || typeof value === "boolean"
39
+ );
40
+ }
41
+
15
42
  export interface EvalAssertionResolution {
16
43
  actual: number | string | boolean | null;
17
44
  /** True when neither the metric map nor the artifact carries a value for this metric. */
@@ -0,0 +1,240 @@
1
+ import { createHash } from "node:crypto";
2
+ import { discoverAgentRecipes } from "../agents/registry.js";
3
+ import { agentSpecFingerprint, normalizeAgentSpec } from "../agents/spec.js";
4
+ import type { RunReceipt } from "../dispatch/types.js";
5
+ import { loadFragments } from "../prompts/fragment-loader.js";
6
+ import { createSafetyPolicyEngine } from "../safety/policy-engine.js";
7
+ import type { EvalLedgerSnapshot, EvalPromptManifestObservation } from "./metrics/tracked.js";
8
+ import type { EvalExecutionEnvelopeV1, EvalPromptFragmentIdentityV1 } from "./schema/execution-envelope.js";
9
+ import { EVAL_EXECUTION_ENVELOPE_SCHEMA_V1 } from "./schema/execution-envelope.js";
10
+ import type { EvalSuiteTargetV2, EvalSuiteTaskV2 } from "./schema/suite.js";
11
+
12
+ /** Bounded execution facts a deterministic grader can surface from an in-process dispatch. */
13
+ export interface EvalExecutionObservationV1 {
14
+ compositionHash: string | null;
15
+ target: string | null;
16
+ wireModel: string | null;
17
+ runtime: string | null;
18
+ thinkingLevel: string | null;
19
+ toolSignature: string | null;
20
+ autonomy: string | null;
21
+ policyHashes: { rulePack: string | null; project: string | null };
22
+ projectContext: {
23
+ tier: string | null;
24
+ contentHash: string | null;
25
+ chars: number | null;
26
+ sections: string[];
27
+ rulesApplied: string[];
28
+ operatorProfileApplied: boolean | null;
29
+ } | null;
30
+ }
31
+
32
+ export interface BuildEvalExecutionEnvelopeInput {
33
+ task: EvalSuiteTaskV2;
34
+ target: EvalSuiteTargetV2;
35
+ cwd: string | null;
36
+ receipt: RunReceipt | null;
37
+ ledger: EvalLedgerSnapshot;
38
+ observation?: EvalExecutionObservationV1;
39
+ }
40
+
41
+ /**
42
+ * Bind one behavioral result to the exact model-facing and policy-facing
43
+ * identity available at the runner boundary. Nulls mean the declared
44
+ * machinery-only mode has no such model concept; they never stand in for a
45
+ * guessed value.
46
+ */
47
+ export function buildEvalExecutionEnvelopeV1(input: BuildEvalExecutionEnvelopeInput): EvalExecutionEnvelopeV1 {
48
+ const scenario = input.task.behavioral;
49
+ if (scenario === undefined) throw new Error(`behavioral task ${input.task.id} has no behavioral scenario`);
50
+ const manifest = input.ledger.promptManifests.at(-1) ?? null;
51
+ const contextSnapshot = input.ledger.contextSnapshots.at(-1) ?? null;
52
+ const recipe = recipeIdentity(
53
+ input,
54
+ scenario.execution.subject.kind === "worker" ? scenario.execution.subject.role : null,
55
+ );
56
+ const policy = policyIdentity(input.cwd, input.receipt, input.observation);
57
+ const autonomy =
58
+ input.receipt?.autonomyEnforcement?.autonomy ?? input.observation?.autonomy ?? input.task.runner.autonomy ?? null;
59
+ const promptFragments = promptFragmentIdentities(manifest, recipe, autonomy);
60
+ const compositionHash =
61
+ input.receipt?.staticCompositionHash ??
62
+ input.observation?.compositionHash ??
63
+ manifest?.systemPromptHash ??
64
+ contextSnapshot?.promptHash ??
65
+ null;
66
+ const projectContext = projectContextIdentity(input, manifest, promptFragments);
67
+ return {
68
+ schema: EVAL_EXECUTION_ENVELOPE_SCHEMA_V1,
69
+ prompt: { fragments: promptFragments, compositionHash },
70
+ recipe: recipe === null ? null : { id: recipe.id, version: recipe.version, contentHash: recipe.contentHash },
71
+ target: input.receipt?.targetId ?? input.observation?.target ?? input.target.id,
72
+ wireModel:
73
+ input.receipt?.wireModelId ?? input.observation?.wireModel ?? contextSnapshot?.modelId ?? input.target.model ?? null,
74
+ runtime: input.receipt?.runtimeId ?? input.observation?.runtime ?? contextSnapshot?.runtimeId ?? null,
75
+ thinkingLevel:
76
+ input.receipt?.runtimeResolution?.effectiveThinkingLevel ??
77
+ input.observation?.thinkingLevel ??
78
+ manifest?.thinkingLevel ??
79
+ input.target.thinking ??
80
+ null,
81
+ toolSignature:
82
+ input.receipt?.toolSignature ?? input.observation?.toolSignature ?? contextSnapshot?.toolSignature ?? null,
83
+ autonomy,
84
+ policyHashes: policy,
85
+ projectContext,
86
+ corpus: { ...scenario.corpus },
87
+ };
88
+ }
89
+
90
+ function recipeIdentity(
91
+ input: BuildEvalExecutionEnvelopeInput,
92
+ role: string | null,
93
+ ): { id: string; version: number; contentHash: string; personaHash: string } | null {
94
+ const id = input.receipt?.agentId ?? input.task.runner.agent ?? role;
95
+ if (id === null || input.cwd === null) return null;
96
+ try {
97
+ const recipe = discoverAgentRecipes(input.cwd).find((entry) => entry.id === id);
98
+ if (recipe === undefined) return null;
99
+ return {
100
+ id: recipe.id,
101
+ version: recipe.version,
102
+ contentHash: agentSpecFingerprint(normalizeAgentSpec(recipe)),
103
+ personaHash: sha256(recipe.body),
104
+ };
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+
110
+ function promptFragmentIdentities(
111
+ manifest: EvalPromptManifestObservation | null,
112
+ recipe: { id: string; version: number; personaHash: string } | null,
113
+ autonomy: string | null,
114
+ ): EvalPromptFragmentIdentityV1[] {
115
+ let versions = new Map<string, number>();
116
+ try {
117
+ versions = new Map([...loadFragments().byId.values()].map((fragment) => [fragment.id, fragment.version]));
118
+ } catch {
119
+ // The manifest still carries exact hashes; only authored versions become unavailable.
120
+ }
121
+ if (manifest !== null) {
122
+ return manifest.fragments
123
+ .map((fragment) => ({
124
+ id: fragment.id,
125
+ version: versions.get(fragment.id) ?? ("unversioned" as const),
126
+ contentHash: fragment.contentHash,
127
+ }))
128
+ .sort((left, right) => left.id.localeCompare(right.id));
129
+ }
130
+ if (recipe === null) return [];
131
+ const selected = ["identity.clio-worker", "operating.contract", "operating.worker"];
132
+ if (autonomy !== null) selected.push(`safety.${autonomy}`);
133
+ const fragments: EvalPromptFragmentIdentityV1[] = [];
134
+ try {
135
+ const table = loadFragments();
136
+ for (const id of selected) {
137
+ const fragment = table.byId.get(id);
138
+ if (fragment !== undefined) {
139
+ fragments.push({ id, version: fragment.version, contentHash: fragment.contentHash });
140
+ }
141
+ }
142
+ } catch {
143
+ // The composition hash remains authoritative when resource discovery fails.
144
+ }
145
+ fragments.push({ id: `persona.${recipe.id}`, version: recipe.version, contentHash: recipe.personaHash });
146
+ return fragments.sort((left, right) => left.id.localeCompare(right.id));
147
+ }
148
+
149
+ function policyIdentity(
150
+ cwd: string | null,
151
+ receipt: RunReceipt | null,
152
+ observation: EvalExecutionObservationV1 | undefined,
153
+ ): EvalExecutionEnvelopeV1["policyHashes"] {
154
+ const sealed = receipt?.reproducibility?.safetyPolicy;
155
+ if (sealed !== undefined) return { rulePack: sealed.rulePackHash, project: sealed.projectPolicyHash };
156
+ if (observation !== undefined) return { ...observation.policyHashes };
157
+ if (cwd === null) return { rulePack: null, project: null };
158
+ try {
159
+ const metadata = createSafetyPolicyEngine({ cwd }).metadata();
160
+ return { rulePack: metadata.rulePackHash, project: metadata.projectPolicyHash };
161
+ } catch {
162
+ return { rulePack: null, project: null };
163
+ }
164
+ }
165
+
166
+ function projectContextIdentity(
167
+ input: BuildEvalExecutionEnvelopeInput,
168
+ manifest: EvalPromptManifestObservation | null,
169
+ fragments: ReadonlyArray<EvalPromptFragmentIdentityV1>,
170
+ ): EvalExecutionEnvelopeV1["projectContext"] {
171
+ const receipt = input.receipt;
172
+ if (receipt?.projectContext !== undefined) {
173
+ const sections = [...(receipt.projectContext.sections ?? [])].sort();
174
+ const hasContentBearingContext = sections.some((section) => section !== "workspace-root");
175
+ return {
176
+ kind: "worker",
177
+ tier: receipt.projectContext.tier,
178
+ contentHash: hasContentBearingContext ? (receipt.projectContext.contentHash ?? null) : null,
179
+ chars: hasContentBearingContext ? (receipt.projectContext.chars ?? null) : null,
180
+ sections,
181
+ rulesApplied: [...(receipt.rulesApplied ?? [])].sort(),
182
+ operatorProfileApplied: receipt.operatorProfileApplied ?? null,
183
+ };
184
+ }
185
+ const observed = input.observation?.projectContext;
186
+ if (observed !== undefined && observed !== null) {
187
+ const sections = [...observed.sections].sort();
188
+ const hasContentBearingContext = sections.some((section) => section !== "workspace-root");
189
+ return {
190
+ kind: "worker",
191
+ tier: observed.tier,
192
+ contentHash: hasContentBearingContext ? observed.contentHash : null,
193
+ chars: hasContentBearingContext ? observed.chars : null,
194
+ sections,
195
+ rulesApplied: [...observed.rulesApplied].sort(),
196
+ operatorProfileApplied: observed.operatorProfileApplied,
197
+ };
198
+ }
199
+ if (manifest !== null) {
200
+ const contextFragments = fragments.filter((fragment) => fragment.id.startsWith("context."));
201
+ const preload = manifest.projectPreload;
202
+ const identity = {
203
+ preload,
204
+ fragments: contextFragments.map((fragment) => [fragment.id, fragment.contentHash]),
205
+ };
206
+ return {
207
+ kind: "session",
208
+ tier: preload?.mode ?? null,
209
+ contentHash: sha256(stableJson(identity)),
210
+ chars: preload?.chars ?? null,
211
+ sections: contextFragments.map((fragment) => fragment.id).sort(),
212
+ rulesApplied: [],
213
+ operatorProfileApplied: contextFragments.some((fragment) => fragment.id === "context.operator-profile"),
214
+ };
215
+ }
216
+ return {
217
+ kind: "none",
218
+ tier: null,
219
+ contentHash: null,
220
+ chars: null,
221
+ sections: [],
222
+ rulesApplied: [],
223
+ operatorProfileApplied: null,
224
+ };
225
+ }
226
+
227
+ function stableJson(value: unknown): string {
228
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
229
+ if (typeof value === "object" && value !== null) {
230
+ return `{${Object.entries(value as Record<string, unknown>)
231
+ .sort(([left], [right]) => left.localeCompare(right))
232
+ .map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
233
+ .join(",")}}`;
234
+ }
235
+ return JSON.stringify(value);
236
+ }
237
+
238
+ function sha256(value: string): string {
239
+ return createHash("sha256").update(value, "utf8").digest("hex");
240
+ }
@@ -0,0 +1,136 @@
1
+ import {
2
+ EVAL_TRACKED_METRIC_NAMES,
3
+ type EvalMetricSource,
4
+ type EvalTrackedMetricName,
5
+ type EvalVerdictEnvelopeV1,
6
+ } from "../schema/verdict.js";
7
+
8
+ export interface EvalMetricDistributionV1 {
9
+ observations: number;
10
+ measured: number;
11
+ unmeasured: number;
12
+ mean: number | null;
13
+ min: number | null;
14
+ max: number | null;
15
+ p90: number | null;
16
+ variance: number | null;
17
+ standardDeviation: number | null;
18
+ sources: EvalMetricSource[];
19
+ }
20
+
21
+ export type EvalTrackedMetricDistributionsV1 = Record<EvalTrackedMetricName, EvalMetricDistributionV1> & {
22
+ expectedColdReasons: Record<string, EvalMetricDistributionV1>;
23
+ };
24
+
25
+ export interface EvalScenarioAggregateV1 {
26
+ scenarioId: string;
27
+ trials: number;
28
+ k: number;
29
+ passed: number;
30
+ failed: number;
31
+ unmeasured: number;
32
+ machineryFailures: number;
33
+ passAtK: number;
34
+ passPowK: number;
35
+ trackedMetrics: EvalTrackedMetricDistributionsV1;
36
+ }
37
+
38
+ /** Aggregate each scenario independently so trial outcomes never cross tasks. */
39
+ export function aggregateEvalVerdicts(verdicts: ReadonlyArray<EvalVerdictEnvelopeV1>): EvalScenarioAggregateV1[] {
40
+ const byScenario = new Map<string, EvalVerdictEnvelopeV1[]>();
41
+ for (const verdict of verdicts) {
42
+ const group = byScenario.get(verdict.scenarioId) ?? [];
43
+ group.push(verdict);
44
+ byScenario.set(verdict.scenarioId, group);
45
+ }
46
+ return [...byScenario.entries()]
47
+ .sort(([left], [right]) => left.localeCompare(right))
48
+ .map(([scenarioId, group]) => aggregateScenario(scenarioId, group));
49
+ }
50
+
51
+ function aggregateScenario(
52
+ scenarioId: string,
53
+ verdicts: ReadonlyArray<EvalVerdictEnvelopeV1>,
54
+ ): EvalScenarioAggregateV1 {
55
+ const ordered = [...verdicts].sort((left, right) => left.trialIndex - right.trialIndex);
56
+ const passed = ordered.filter((verdict) => verdict.outcome === "pass").length;
57
+ const failed = ordered.filter((verdict) => verdict.outcome === "fail").length;
58
+ const unmeasured = ordered.filter((verdict) => verdict.outcome === "unmeasured").length;
59
+ const machineryFailures = ordered.filter((verdict) => verdict.machinery === "infrastructure_failure").length;
60
+ const fixed = Object.fromEntries(
61
+ EVAL_TRACKED_METRIC_NAMES.map((name) => [name, distribution(ordered.map((verdict) => verdict.trackedMetrics[name]))]),
62
+ ) as Record<EvalTrackedMetricName, EvalMetricDistributionV1>;
63
+ const reasons = new Set(ordered.flatMap((verdict) => Object.keys(verdict.trackedMetrics.expectedColdReasons)));
64
+ const expectedColdReasons = Object.fromEntries(
65
+ [...reasons]
66
+ .sort((left, right) => left.localeCompare(right))
67
+ .map((reason) => [
68
+ reason,
69
+ distribution(
70
+ ordered.map(
71
+ (verdict) => verdict.trackedMetrics.expectedColdReasons[reason] ?? { value: 0, source: "ledger" as const },
72
+ ),
73
+ ),
74
+ ]),
75
+ );
76
+ const k = ordered.length;
77
+ return {
78
+ scenarioId,
79
+ trials: k,
80
+ k,
81
+ passed,
82
+ failed,
83
+ unmeasured,
84
+ machineryFailures,
85
+ passAtK: k > 0 && passed > 0 ? 1 : 0,
86
+ passPowK: k > 0 && passed === k ? 1 : 0,
87
+ trackedMetrics: { ...fixed, expectedColdReasons },
88
+ };
89
+ }
90
+
91
+ function distribution(
92
+ metrics: ReadonlyArray<{ value: number | null; source: EvalMetricSource }>,
93
+ ): EvalMetricDistributionV1 {
94
+ const values = metrics.flatMap((metric) => (metric.value === null ? [] : [metric.value]));
95
+ const sources = [...new Set(metrics.map((metric) => metric.source))].sort(compareSources);
96
+ if (values.length === 0) {
97
+ return {
98
+ observations: metrics.length,
99
+ measured: 0,
100
+ unmeasured: metrics.length,
101
+ mean: null,
102
+ min: null,
103
+ max: null,
104
+ p90: null,
105
+ variance: null,
106
+ standardDeviation: null,
107
+ sources,
108
+ };
109
+ }
110
+ const ordered = [...values].sort((left, right) => left - right);
111
+ const p90Index = Math.max(0, Math.ceil(ordered.length * 0.9) - 1);
112
+ const mean = ordered.reduce((sum, value) => sum + value, 0) / ordered.length;
113
+ const variance = ordered.reduce((sum, value) => sum + (value - mean) ** 2, 0) / ordered.length;
114
+ return {
115
+ observations: metrics.length,
116
+ measured: values.length,
117
+ unmeasured: metrics.length - values.length,
118
+ mean,
119
+ min: ordered[0] ?? null,
120
+ max: ordered.at(-1) ?? null,
121
+ p90: ordered[p90Index] ?? null,
122
+ variance,
123
+ standardDeviation: Math.sqrt(variance),
124
+ sources,
125
+ };
126
+ }
127
+
128
+ function compareSources(left: EvalMetricSource, right: EvalMetricSource): number {
129
+ return sourceOrder(left) - sourceOrder(right);
130
+ }
131
+
132
+ function sourceOrder(source: EvalMetricSource): number {
133
+ if (source === "ledger") return 0;
134
+ if (source === "receipt") return 1;
135
+ return 2;
136
+ }