@hona/openeval 0.2.2 → 0.3.1

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 (214) hide show
  1. package/JUDGING.md +135 -23
  2. package/README.md +71 -9
  3. package/package.json +7 -6
  4. package/src/app/cost-plan.ts +20 -16
  5. package/src/app/eval-state.ts +2 -1
  6. package/src/app/grade-recording.ts +72 -0
  7. package/src/app/input-fingerprints.ts +22 -8
  8. package/src/app/judge-evidence.ts +26 -11
  9. package/src/app/judge-run.ts +36 -19
  10. package/src/app/load-benchmark.ts +48 -16
  11. package/src/app/read-results.ts +35 -2
  12. package/src/app/read-run.ts +23 -2
  13. package/src/app/rejudge.ts +2 -1
  14. package/src/app/run-benchmark.ts +8 -1
  15. package/src/app/run-eval-pipeline.ts +2 -1
  16. package/src/app/run-eval.ts +13 -0
  17. package/src/app/scores.ts +76 -34
  18. package/src/app/serve-results.ts +2 -0
  19. package/src/evidence.ts +4 -1
  20. package/src/index.ts +15 -3
  21. package/src/infra/containers/runtime/package.json +2 -2
  22. package/src/infra/containers/runtime/server.mjs +1 -1
  23. package/src/infra/evidence/index.ts +39 -2
  24. package/src/infra/evidence/tool-calls.ts +5 -2
  25. package/src/infra/judging/agent.ts +3 -2
  26. package/src/infra/judging/code-result.ts +102 -0
  27. package/src/infra/judging/code-source.ts +96 -0
  28. package/src/infra/judging/code-worker.ts +26 -0
  29. package/src/infra/judging/code.ts +97 -0
  30. package/src/infra/judging/contract.ts +82 -64
  31. package/src/infra/judging/evidence-tool.ts +3 -1
  32. package/src/infra/judging/index.ts +14 -0
  33. package/src/infra/judging/judge-agent.md +24 -18
  34. package/src/infra/judging/observer.ts +8 -7
  35. package/src/infra/judging/submission.ts +8 -8
  36. package/src/infra/judging/tools.ts +9 -9
  37. package/src/infra/opencode/host.ts +1 -1
  38. package/src/infra/opencode/read-recording.ts +6 -1
  39. package/src/infra/opencode/version.ts +2 -0
  40. package/src/infra/recording/index.ts +240 -0
  41. package/src/infra/recording/metrics.ts +193 -0
  42. package/src/infra/sqlite/index.ts +29 -19
  43. package/src/judge-context.ts +141 -0
  44. package/src/judgment.ts +26 -17
  45. package/src/types.ts +36 -17
  46. package/src/view.ts +40 -14
  47. package/viewer/THIRD_PARTY_LICENSES.md +2 -2
  48. package/viewer/assets/{abnfDiagram-VCTEODGH-B1kcYgQG.js → abnfDiagram-VCTEODGH-Ck7reUId.js} +1 -1
  49. package/viewer/assets/{angular-html-DrYCUiZv.js → angular-html-DzRW817u.js} +1 -1
  50. package/viewer/assets/{angular-ts-BgjwQn-Z.js → angular-ts-Dwpo5JSv.js} +1 -1
  51. package/viewer/assets/{apl-DlHqe8o4.js → apl-bt59SDvg.js} +1 -1
  52. package/viewer/assets/{arc-C4nf7ZoY.js → arc-Dwbl2Pkh.js} +1 -1
  53. package/viewer/assets/architecture-7GRP2DOG-BZpt0zSq.js +1 -0
  54. package/viewer/assets/{architectureDiagram-5GKGNRK7-BIWoe0fO.js → architectureDiagram-5GKGNRK7-BcmO-Qqp.js} +1 -1
  55. package/viewer/assets/{astro-BrlHnIh9.js → astro-B3siF1pJ.js} +1 -1
  56. package/viewer/assets/{blade-DOcerOiW.js → blade-BevF_cYv.js} +1 -1
  57. package/viewer/assets/{blockDiagram-I7D4REHJ-C3x13a9m.js → blockDiagram-I7D4REHJ-MzX9_xf9.js} +1 -1
  58. package/viewer/assets/{c-BMbThxEo.js → c-gavBjOcY.js} +1 -1
  59. package/viewer/assets/{c4Diagram-7LVT6UL2-BeWktchx.js → c4Diagram-7LVT6UL2-BbHHPJGg.js} +1 -1
  60. package/viewer/assets/channel-BnnBJr7O.js +1 -0
  61. package/viewer/assets/{chapel-k2cjkSmc.js → chapel-DgshBe1K.js} +1 -1
  62. package/viewer/assets/{chunk-4HAMMTFA-o6YtJsqE.js → chunk-4HAMMTFA-D1Gsg_qO.js} +1 -1
  63. package/viewer/assets/{chunk-75Z2AOVW-KohQa9Nw.js → chunk-75Z2AOVW-EfPG3FOY.js} +1 -1
  64. package/viewer/assets/{chunk-DU6HZSFF-C88wxc6m.js → chunk-DU6HZSFF-BxB0lceI.js} +1 -1
  65. package/viewer/assets/{chunk-F27PBJKO-mO4AP2as.js → chunk-F27PBJKO-DRBDr_-Q.js} +1 -1
  66. package/viewer/assets/{chunk-GMAD6QVW-DzQX2Cld.js → chunk-GMAD6QVW-AbIWvPs8.js} +1 -1
  67. package/viewer/assets/{chunk-GVQU2GXP-CS78RfJD.js → chunk-GVQU2GXP-aIYNUxo9.js} +1 -1
  68. package/viewer/assets/{chunk-IMKFNOWR-DxS42GUo.js → chunk-IMKFNOWR-BwpTTnl_.js} +1 -1
  69. package/viewer/assets/{chunk-L3NEJ4N5-DGo-uYLy.js → chunk-L3NEJ4N5-KtxD1mfg.js} +1 -1
  70. package/viewer/assets/{chunk-OSK3NFVY-BOcXQG16.js → chunk-OSK3NFVY-CinbAXbX.js} +1 -1
  71. package/viewer/assets/{chunk-P2QGCYS3-B7GnZdIQ.js → chunk-P2QGCYS3-Dst8DZSR.js} +1 -1
  72. package/viewer/assets/{chunk-POPQ4Y6H-DgBcYHog.js → chunk-POPQ4Y6H-DqvBdud7.js} +1 -1
  73. package/viewer/assets/{chunk-PWAF6VOD-DpEq6qHA.js → chunk-PWAF6VOD-Cl2eG05g.js} +1 -1
  74. package/viewer/assets/{chunk-SHT3W25Y-CrOGxKM2.js → chunk-SHT3W25Y-D9FbZ836.js} +1 -1
  75. package/viewer/assets/{chunk-SVP7TREG-4HY7Fljj.js → chunk-SVP7TREG-_4n_lRpO.js} +1 -1
  76. package/viewer/assets/{chunk-TICWLB2K-B1Rl31EC.js → chunk-TICWLB2K-DUXjjJlY.js} +1 -1
  77. package/viewer/assets/{chunk-XXDRQBXY-CK8giEW-.js → chunk-XXDRQBXY-BdRoSapX.js} +1 -1
  78. package/viewer/assets/classDiagram-ZZMXUADV-F9J3BKRU.js +1 -0
  79. package/viewer/assets/classDiagram-v2-VYDZK3BY-F9J3BKRU.js +1 -0
  80. package/viewer/assets/{cobol-CupUwvw9.js → cobol-D5zqvXzY.js} +1 -1
  81. package/viewer/assets/{coffee-C17pkozF.js → coffee-SCAyVn9N.js} +1 -1
  82. package/viewer/assets/{cose-bilkent-JH36ORCC-V1OmrmCv.js → cose-bilkent-JH36ORCC-B5uDWB-J.js} +1 -1
  83. package/viewer/assets/{cpp-BJwVQVXg.js → cpp-RUQ97EK5.js} +1 -1
  84. package/viewer/assets/{crystal-BF-oCN-L.js → crystal-FauNVdkE.js} +1 -1
  85. package/viewer/assets/{css-BMgkVI3c.js → css-DuzOk7di.js} +1 -1
  86. package/viewer/assets/{cynefin-OW5HDTMX-BEjY_gsc.js → cynefin-OW5HDTMX-D0N8Dm6P.js} +1 -1
  87. package/viewer/assets/{cynefinDiagram-5FMLGOSQ-bfSYgr6z.js → cynefinDiagram-5FMLGOSQ-IGGiqH_Y.js} +1 -1
  88. package/viewer/assets/{dagre-GXQ25YYZ-VtwTgGfa.js → dagre-GXQ25YYZ-PoopsFcY.js} +1 -1
  89. package/viewer/assets/{diagram-S7CK7UJ4-CHaAAyW7.js → diagram-S7CK7UJ4-D6GuIlRD.js} +1 -1
  90. package/viewer/assets/{diagram-UQ7AKVKN-CjL65-As.js → diagram-UQ7AKVKN-Cj8lu7Hi.js} +1 -1
  91. package/viewer/assets/{diagram-VSXAHHWV-Cnxrk1TJ.js → diagram-VSXAHHWV-bI4-SjAp.js} +1 -1
  92. package/viewer/assets/{diagram-VX7I27RA-D78jPXr7.js → diagram-VX7I27RA-jWvTT618.js} +1 -1
  93. package/viewer/assets/{diagram-Z3DM3KII-DKjnOaF2.js → diagram-Z3DM3KII-D6Vcvcr8.js} +1 -1
  94. package/viewer/assets/{dist-BTYPW0dz.js → dist-Cn65YNrn.js} +1 -1
  95. package/viewer/assets/{ebnfDiagram-PWID7BFC-7XiXh5Lx.js → ebnfDiagram-PWID7BFC-y-ZxfP2H.js} +1 -1
  96. package/viewer/assets/{edge-B34N_HjN.js → edge-Bc0EBQ7y.js} +1 -1
  97. package/viewer/assets/{elixir-CQzWMyKD.js → elixir-BmxsT0oc.js} +1 -1
  98. package/viewer/assets/{elm-BNJiG-wg.js → elm-BrTAZ55m.js} +1 -1
  99. package/viewer/assets/{erDiagram-RLTQ6QDP-YPFEgZxU.js → erDiagram-RLTQ6QDP-Wa1tikbb.js} +1 -1
  100. package/viewer/assets/{erb-D5Rm3UNa.js → erb-B60HRxmg.js} +1 -1
  101. package/viewer/assets/eventmodeling-NTZA5JFV-BNvBHnQf.js +1 -0
  102. package/viewer/assets/flowDiagram-HODETNUW-zGgqOVuO.js +1 -0
  103. package/viewer/assets/{ganttDiagram-EL5Y4UJY-ergSGtTP.js → ganttDiagram-EL5Y4UJY-DjskXB68.js} +1 -1
  104. package/viewer/assets/{git-rebase-BpIMZIRG.js → git-rebase-CK9f68l3.js} +1 -1
  105. package/viewer/assets/{gitGraph-4MIJSDKK-CZt4REVj.js → gitGraph-4MIJSDKK-CvRhO7Ae.js} +1 -1
  106. package/viewer/assets/{gitGraphDiagram-WWUBYQGX-h9QMzC2r.js → gitGraphDiagram-WWUBYQGX-Bg_OLNL1.js} +1 -1
  107. package/viewer/assets/{glimmer-js-SeSwns5_.js → glimmer-js-CVc7keaR.js} +1 -1
  108. package/viewer/assets/{glimmer-ts-BExI5tTz.js → glimmer-ts-DYraWlmH.js} +1 -1
  109. package/viewer/assets/{glsl-CXikKw2b.js → glsl-BpYswgfh.js} +1 -1
  110. package/viewer/assets/{graphql-6GpQIInt.js → graphql-C3iDzxap.js} +1 -1
  111. package/viewer/assets/{hack-i7rizvpB.js → hack-D8oEoipZ.js} +1 -1
  112. package/viewer/assets/{haml-C68gmkTf.js → haml-BJb-BQAN.js} +1 -1
  113. package/viewer/assets/{handlebars-BBn_c0Xt.js → handlebars-wB54lHaB.js} +1 -1
  114. package/viewer/assets/{html-BObmAZQA.js → html-HIvKkSZ3.js} +1 -1
  115. package/viewer/assets/{html-derivative-BjMuR1LM.js → html-derivative-CDavbmdn.js} +1 -1
  116. package/viewer/assets/{http-BJBk8gt0.js → http-DEgShh0-.js} +1 -1
  117. package/viewer/assets/{hurl-D7obCYfg.js → hurl-D8wvJcyh.js} +1 -1
  118. package/viewer/assets/{index-FMtYXcX7.js → index-Ck3NBCVJ.js} +9 -7
  119. package/viewer/assets/{index-Wz0qQyIk.css → index-DAuO1BM4.css} +1 -1
  120. package/viewer/assets/{info-A6RAGUB7-CrzitQIU.js → info-A6RAGUB7-DL3H6Iqn.js} +1 -1
  121. package/viewer/assets/{infoDiagram-27XIBGKW-D2CgRDxu.js → infoDiagram-27XIBGKW-DSLzhrL8.js} +1 -1
  122. package/viewer/assets/{ishikawaDiagram-5VMMS53U-BhnQMHOo.js → ishikawaDiagram-5VMMS53U-B-ycXUrd.js} +1 -1
  123. package/viewer/assets/{java-5VJU_EW8.js → java-C5iWdXOl.js} +1 -1
  124. package/viewer/assets/{javascript-r8UgndxQ.js → javascript-PMSXGhX1.js} +1 -1
  125. package/viewer/assets/{jinja-BY2ibHG5.js → jinja-CJ_l63gX.js} +1 -1
  126. package/viewer/assets/{jison-C_7YxymZ.js → jison-DGaZyJAT.js} +1 -1
  127. package/viewer/assets/{journeyDiagram-3NMN7TZE-DSBzKQEa.js → journeyDiagram-3NMN7TZE-2p56tZPm.js} +1 -1
  128. package/viewer/assets/{json-8e1WlJYD.js → json-CCQ1eo9o.js} +1 -1
  129. package/viewer/assets/{jsx-BYp5GD4Y.js → jsx-DeLT99JL.js} +1 -1
  130. package/viewer/assets/{julia-Bkob1uVX.js → julia-DODLsulx.js} +1 -1
  131. package/viewer/assets/{just-CUnSjty-.js → just-B_Nvjvhv.js} +1 -1
  132. package/viewer/assets/{kanban-definition-UXKFOSKX-CotbrQue.js → kanban-definition-UXKFOSKX-B42fPx4K.js} +1 -1
  133. package/viewer/assets/{latex-UdE3ivqk.js → latex-CCjy2I9z.js} +1 -1
  134. package/viewer/assets/{line-DDh6a6hz.js → line-DGK8UZYe.js} +1 -1
  135. package/viewer/assets/{linear-BxMsCbKb.js → linear-LxXu6wOl.js} +1 -1
  136. package/viewer/assets/{liquid-WL4MvEIT.js → liquid-C2tknZP0.js} +1 -1
  137. package/viewer/assets/{lua-CtOubZta.js → lua-D6UzQw5Z.js} +1 -1
  138. package/viewer/assets/{marko-C0GSQJi3.js → marko-CGd5W4Hn.js} +1 -1
  139. package/viewer/assets/{mdc-DUivddFc.js → mdc-Bp5z92Vo.js} +1 -1
  140. package/viewer/assets/{mermaid-parser.core-FhKE_uv2.js → mermaid-parser.core-xGBt_sNt.js} +3 -3
  141. package/viewer/assets/{mermaid.core-BSOWwyvd.js → mermaid.core-D1TUMBuT.js} +4 -4
  142. package/viewer/assets/{mindmap-definition-YA3MSWOX-tsLZ_Opn.js → mindmap-definition-YA3MSWOX-BY9cVMtI.js} +1 -1
  143. package/viewer/assets/{nginx-B__INAtC.js → nginx-DAsqAd3h.js} +1 -1
  144. package/viewer/assets/{nim-UEY12GJF.js → nim-COOJz623.js} +1 -1
  145. package/viewer/assets/{org-DDIZsz95.js → org-CBV1qqrA.js} +1 -1
  146. package/viewer/assets/{packet-AYTQ26CC-CqdJnqfo.js → packet-AYTQ26CC-61q5HcB3.js} +1 -1
  147. package/viewer/assets/{pegDiagram-XKGWAZYB-BJs7qu7m.js → pegDiagram-XKGWAZYB-DLEkmimO.js} +1 -1
  148. package/viewer/assets/{perl-D5KwNZt7.js → perl-6SV_ayGd.js} +1 -1
  149. package/viewer/assets/{php-BEs7d_Dk.js → php-BUb-Io9m.js} +1 -1
  150. package/viewer/assets/{pie-WAS4IAKB-lgzqBWqy.js → pie-WAS4IAKB-DgijP3YA.js} +1 -1
  151. package/viewer/assets/{pieDiagram-E7YTZNPT-Cxe9LnnE.js → pieDiagram-E7YTZNPT-Cp20J80-.js} +1 -1
  152. package/viewer/assets/{pug-B0dsfQOV.js → pug-D5s94d1b.js} +1 -1
  153. package/viewer/assets/{qml-XPIhrIzf.js → qml-D8g4NGVo.js} +1 -1
  154. package/viewer/assets/{quadrantDiagram-AXDQQJYC-D5JGwC11.js → quadrantDiagram-AXDQQJYC-Djsc9xpm.js} +1 -1
  155. package/viewer/assets/{r-C6hQdpIM.js → r-DXvz5K4A.js} +1 -1
  156. package/viewer/assets/{radar-RG4KPBEZ-B7JRLhUA.js → radar-RG4KPBEZ-BCmoUpe8.js} +1 -1
  157. package/viewer/assets/{railroad-74A4TZTK-Df7ZQBLn.js → railroad-74A4TZTK-DZ0SYESf.js} +1 -1
  158. package/viewer/assets/railroad-abnf-HS5TGJTU-C2CQDbu6.js +1 -0
  159. package/viewer/assets/railroad-ebnf-LZEXJU2U-6ssJubZr.js +1 -0
  160. package/viewer/assets/railroad-peg-WCYAUIDC-UOxDGPyg.js +1 -0
  161. package/viewer/assets/{railroadDiagram-O6MQD6OU-DZJzpmP8.js → railroadDiagram-O6MQD6OU-DmD4JR_t.js} +1 -1
  162. package/viewer/assets/{razor-C3KSUuOw.js → razor-D9CjUAvE.js} +1 -1
  163. package/viewer/assets/{regexp-n-T936Rk.js → regexp-CnuLvLnC.js} +1 -1
  164. package/viewer/assets/{requirementDiagram-BXWQKSXE-Be9lEFrk.js → requirementDiagram-BXWQKSXE-BTci7X6J.js} +1 -1
  165. package/viewer/assets/{rst-fVnHDyXD.js → rst-Chdv5DuC.js} +1 -1
  166. package/viewer/assets/{ruby-DuRgxlXF.js → ruby-DXQkNv8k.js} +1 -1
  167. package/viewer/assets/{sankeyDiagram-P5KCCOFB-BsgKAEBN.js → sankeyDiagram-P5KCCOFB-BaHSMANQ.js} +1 -1
  168. package/viewer/assets/{sas-C0WA2OFa.js → sas-B6S0Oq1H.js} +1 -1
  169. package/viewer/assets/{scss-CQDZ4smR.js → scss-BnFPXH54.js} +1 -1
  170. package/viewer/assets/{sequenceDiagram-WJ2MYXX4-BkUDWL0D.js → sequenceDiagram-WJ2MYXX4-BUk21g1t.js} +1 -1
  171. package/viewer/assets/{shellscript-CE84G0GP.js → shellscript-Brdg8vyE.js} +1 -1
  172. package/viewer/assets/{shellsession-BdJvdaqL.js → shellsession-Df33UGC7.js} +1 -1
  173. package/viewer/assets/{soy-BSxOREC0.js → soy-DE83qari.js} +1 -1
  174. package/viewer/assets/{sql-D4MN9uRI.js → sql-C838IanB.js} +1 -1
  175. package/viewer/assets/{src-BT_kESTA.js → src-tvfSkCy9.js} +1 -1
  176. package/viewer/assets/{stata-B6nXAaGV.js → stata-BjrFc6Ob.js} +1 -1
  177. package/viewer/assets/{stateDiagram-D77RDMKH-C80wp8xG.js → stateDiagram-D77RDMKH-B7yRJv9-.js} +1 -1
  178. package/viewer/assets/stateDiagram-v2-MP3YSRHH-BCwhllSg.js +1 -0
  179. package/viewer/assets/{surrealql-fVjZmhV3.js → surrealql-BQl2P64c.js} +1 -1
  180. package/viewer/assets/{svelte-DnBQ5p7d.js → svelte-WhVuP9VM.js} +1 -1
  181. package/viewer/assets/{swimlanes-42K2YHIH-Cff6yT8e.js → swimlanes-42K2YHIH-on6wyUF5.js} +1 -1
  182. package/viewer/assets/swimlanesDiagram-VR7AAH4N-D0QyhouX.js +8 -0
  183. package/viewer/assets/{templ-TCx51Uts.js → templ-GlyHWfam.js} +1 -1
  184. package/viewer/assets/{tex-CF6SMAQ_.js → tex-Wu25UU_Z.js} +1 -1
  185. package/viewer/assets/{timeline-definition-24CTP7MA-Cje7BvaN.js → timeline-definition-24CTP7MA-DsgJtBig.js} +1 -1
  186. package/viewer/assets/{treeView-Q6P3EWNA-D-QLY3IO.js → treeView-Q6P3EWNA-Bae2q_db.js} +1 -1
  187. package/viewer/assets/{treemap-WGGIJYW6-BCHcTa4N.js → treemap-WGGIJYW6-BJsxvsBb.js} +1 -1
  188. package/viewer/assets/{ts-tags-BqLRnADX.js → ts-tags-C6TgXLV6.js} +1 -1
  189. package/viewer/assets/{tsx-D7KpDX0b.js → tsx-DVoPHges.js} +1 -1
  190. package/viewer/assets/{twig-CEZLObpN.js → twig-CKzx4A42.js} +1 -1
  191. package/viewer/assets/{typescript-DAbIDLbf.js → typescript-DvXz8u_k.js} +1 -1
  192. package/viewer/assets/{typst-CqN7-whv.js → typst-BcepwfpV.js} +1 -1
  193. package/viewer/assets/{vennDiagram-4TSXK5OY-Bz20rx02.js → vennDiagram-4TSXK5OY-CKUR4uY_.js} +1 -1
  194. package/viewer/assets/{vue-kTxN6wdc.js → vue-Bqzme_T-.js} +1 -1
  195. package/viewer/assets/{vue-html-DQmvvKbd.js → vue-html-DZHKNOUy.js} +1 -1
  196. package/viewer/assets/{vue-vine-Did3qTVW.js → vue-vine-azvFs0P6.js} +1 -1
  197. package/viewer/assets/{wardley-WFR3VGLG-CpXaqOit.js → wardley-WFR3VGLG-Ddsu3qLB.js} +1 -1
  198. package/viewer/assets/{wardleyDiagram-VM6X3IG4-Cz06jHpA.js → wardleyDiagram-VM6X3IG4-CCHx9UX4.js} +1 -1
  199. package/viewer/assets/{xml-te2hFcAA.js → xml-BZNC4g25.js} +1 -1
  200. package/viewer/assets/{xsl-X4fuKSVl.js → xsl-DXEWaWc5.js} +1 -1
  201. package/viewer/assets/{xychartDiagram-S5SC5T6Z-BXCw0sfu.js → xychartDiagram-S5SC5T6Z-CkoTq405.js} +1 -1
  202. package/viewer/assets/{yaml-NHtDSZab.js → yaml-DLY3wDEk.js} +1 -1
  203. package/viewer/index.html +2 -2
  204. package/viewer/assets/architecture-7GRP2DOG-Be250VKZ.js +0 -1
  205. package/viewer/assets/channel-DtULXRee.js +0 -1
  206. package/viewer/assets/classDiagram-ZZMXUADV-Bmqu01Xf.js +0 -1
  207. package/viewer/assets/classDiagram-v2-VYDZK3BY-Bmqu01Xf.js +0 -1
  208. package/viewer/assets/eventmodeling-NTZA5JFV-CJCg5f6p.js +0 -1
  209. package/viewer/assets/flowDiagram-HODETNUW-BFzq4Zvs.js +0 -1
  210. package/viewer/assets/railroad-abnf-HS5TGJTU-Z8q3QVTf.js +0 -1
  211. package/viewer/assets/railroad-ebnf-LZEXJU2U-BWoRkG3W.js +0 -1
  212. package/viewer/assets/railroad-peg-WCYAUIDC-BzoV1cN7.js +0 -1
  213. package/viewer/assets/stateDiagram-v2-MP3YSRHH-CYdsKKlz.js +0 -1
  214. package/viewer/assets/swimlanesDiagram-VR7AAH4N-niTUGjMT.js +0 -8
@@ -1,22 +1,20 @@
1
1
  import { resolve, relative } from "node:path";
2
2
  import type { EvalRun, JudgeRun } from "../types";
3
3
  import type { ExecutionContext } from "./context";
4
- import { executeJudge } from "../infra/judging";
4
+ import { gradeRecording } from "./grade-recording";
5
5
  import { errorMessage } from "../infra/files";
6
6
  import { canJudgeEval } from "./eval-state";
7
7
  import { JUDGE_PROTOCOL } from "../judgment";
8
8
  import { JUDGE_AGENT } from "../infra/judging/agent";
9
9
 
10
- /** A new judge session references existing evidence and becomes active only after success. */
10
+ /** A new grading execution becomes active only after all present judges succeed. */
11
11
  export async function judgeEvalRun(
12
12
  context: ExecutionContext,
13
13
  candidate: EvalRun,
14
14
  monitoring: Pick<JudgeRun, "monitorError" | "monitorErrorAt"> = {},
15
15
  ): Promise<JudgeRun> {
16
16
  if (!canJudgeEval(candidate))
17
- throw new Error(
18
- "Judging requires finalized evidence from a completed, stopped, or timed-out eval run",
19
- );
17
+ throw new Error("Judging requires finalized candidate evidence");
20
18
  const slot = context.results.slot(candidate.slotId)!;
21
19
  const definition = context.definition.evals.find(
22
20
  (item) => item.id === slot.evalId,
@@ -25,20 +23,22 @@ export async function judgeEvalRun(
25
23
  evalRunId: candidate.id,
26
24
  evidence: candidate.evidence,
27
25
  rubric: definition.judge,
28
- agent: JUDGE_AGENT,
29
- model: context.definition.judge.model,
26
+ agent: definition.judge ? JUDGE_AGENT : undefined,
27
+ model: definition.judge ? context.definition.judge.model : undefined,
28
+ kind: definition.code ? (definition.judge ? "hybrid" : "code") : "llm",
29
+ code: definition.code,
30
30
  judgeHash: slot.judgeHash,
31
31
  timeoutMs: context.definition.judge.timeoutMs,
32
32
  websearch: context.definition.judge.websearch,
33
33
  mode: "final",
34
34
  runtimeHash: context.runtime.judgeHash,
35
35
  protocol: JUDGE_PROTOCOL,
36
- metrics: definition.metrics,
36
+ criteria: definition.criteria,
37
37
  });
38
38
  const directory = resolve(context.directory, "judge-runs", run.id);
39
39
  let completed: JudgeRun;
40
40
  try {
41
- const { result, session, judgment } = await executeJudge(
41
+ const graded = await gradeRecording(
42
42
  {
43
43
  ...run.input,
44
44
  evidence: {
@@ -46,6 +46,14 @@ export async function judgeEvalRun(
46
46
  directory: resolve(context.directory, run.input.evidence.directory),
47
47
  },
48
48
  },
49
+ {
50
+ evidence: {
51
+ ...candidate.evidence,
52
+ directory: resolve(context.directory, candidate.evidence.directory),
53
+ },
54
+ run: candidate,
55
+ runDirectory: context.directory,
56
+ },
49
57
  directory,
50
58
  (event) => {
51
59
  const record = {
@@ -61,16 +69,25 @@ export async function judgeEvalRun(
61
69
  completed = {
62
70
  ...run,
63
71
  ...monitoring,
64
- state: result.state,
65
- judgment,
66
- session: {
67
- ...session,
68
- database: relative(
69
- context.directory,
70
- resolve(directory, session.database),
71
- ),
72
- },
73
- error: result.error,
72
+ state: graded.state,
73
+ judgment: graded.judgment,
74
+ code: graded.code
75
+ ? {
76
+ ...graded.code,
77
+ stdout: relative(context.directory, graded.code.stdout),
78
+ stderr: relative(context.directory, graded.code.stderr),
79
+ }
80
+ : undefined,
81
+ session: graded.session
82
+ ? {
83
+ ...graded.session,
84
+ database: relative(
85
+ context.directory,
86
+ resolve(directory, graded.session.database),
87
+ ),
88
+ }
89
+ : undefined,
90
+ error: graded.error,
74
91
  completedAt: new Date().toISOString(),
75
92
  elapsedMs: Date.now() - Date.parse(run.startedAt),
76
93
  };
@@ -9,7 +9,9 @@ import type {
9
9
  ModelRef,
10
10
  } from "../types";
11
11
  import { CANDIDATE_TIMEOUT_MS } from "../types";
12
- import { rubricMetrics } from "../judgment";
12
+ import { rubricCriteria } from "../judgment";
13
+ import { compileCodeJudge } from "../infra/judging/code-source";
14
+ import { RUNTIME_IMAGE } from "../infra/opencode/version";
13
15
  import { monitorPolicy } from "./monitor-policy";
14
16
  import {
15
17
  fingerprint,
@@ -41,9 +43,14 @@ async function declaration<T>(path: string): Promise<T> {
41
43
  async function loadEval(directory: string): Promise<EvalDefinition> {
42
44
  const id = basename(directory),
43
45
  prompt = Bun.file(resolve(directory, "prompt.md")),
44
- judge = Bun.file(resolve(directory, "judge.md"));
45
- if (!(await prompt.exists()) || !(await judge.exists()))
46
- throw new Error(`${id} requires prompt.md and judge.md`);
46
+ judge = Bun.file(resolve(directory, "judge.md")),
47
+ codeFile = resolve(directory, "judge.ts");
48
+ const hasMarkdown = await judge.exists(),
49
+ hasCode = await Bun.file(codeFile).exists();
50
+ if (!(await prompt.exists()) || (!hasMarkdown && !hasCode))
51
+ throw new Error(
52
+ `${id} requires prompt.md and at least one of judge.md or judge.ts`,
53
+ );
47
54
  const settings = (await Bun.file(resolve(directory, "eval.ts")).exists())
48
55
  ? await declaration<Eval>(resolve(directory, "eval.ts"))
49
56
  : {};
@@ -56,6 +63,10 @@ async function loadEval(directory: string): Promise<EvalDefinition> {
56
63
  )
57
64
  throw new Error(`${id}/eval.ts has unsupported settings`);
58
65
  monitorPolicy(settings.earlyStop);
66
+ if (hasCode && settings.earlyStop)
67
+ throw new Error(
68
+ `${id}: judge.ts grades finalized recordings; earlyStop requires a Markdown-only judge`,
69
+ );
59
70
  const source: unknown[] = [];
60
71
  if (settings.workspace) {
61
72
  const workspace = settings.workspace;
@@ -157,9 +168,10 @@ async function loadEval(directory: string): Promise<EvalDefinition> {
157
168
  throw new Error(`${id}: preparation requires argv`);
158
169
  }
159
170
  const promptText = await prompt.text(),
160
- judgeText = await judge.text();
161
- if (!promptText.trim() || !judgeText.trim())
171
+ judgeText = hasMarkdown ? await judge.text() : "";
172
+ if (!promptText.trim() || (hasMarkdown && !judgeText.trim()))
162
173
  throw new Error(`${id}: prompt and judge must not be empty`);
174
+ const code = hasCode ? await compileCodeJudge(codeFile) : undefined;
163
175
  return {
164
176
  id,
165
177
  directory,
@@ -170,8 +182,9 @@ async function loadEval(directory: string): Promise<EvalDefinition> {
170
182
  settings: { workspace: settings.workspace, prepare: settings.prepare },
171
183
  source,
172
184
  }),
173
- judgeHash: hash(judgeText),
174
- metrics: rubricMetrics(judgeText),
185
+ judgeHash: fingerprint({ rubric: judgeText, code: code?.hash }),
186
+ criteria: hasMarkdown ? rubricCriteria(judgeText) : [],
187
+ ...(code ? { code } : {}),
175
188
  name: /^# (.+)$/m.exec(judgeText)?.[1] ?? id,
176
189
  };
177
190
  }
@@ -187,10 +200,9 @@ export async function loadBenchmark(
187
200
  if (
188
201
  !definition ||
189
202
  !Array.isArray(definition.models) ||
190
- !definition.models.length ||
191
- !definition.judge
203
+ !definition.models.length
192
204
  )
193
- throw new Error("benchmark.ts requires models and a judge");
205
+ throw new Error("benchmark.ts requires models");
194
206
  if (
195
207
  Object.keys(definition).some(
196
208
  (key) =>
@@ -207,6 +219,18 @@ export async function loadBenchmark(
207
219
  )
208
220
  throw new Error("benchmark.ts contains unsupported settings");
209
221
  const models = definition.models.map(modelRef);
222
+ if (
223
+ definition.judge !== undefined &&
224
+ (!definition.judge ||
225
+ typeof definition.judge !== "object" ||
226
+ Array.isArray(definition.judge) ||
227
+ Object.keys(definition.judge).some(
228
+ (key) => !["model", "timeoutMs", "websearch"].includes(key),
229
+ ))
230
+ )
231
+ throw new Error(
232
+ "judge must be an object with model, timeoutMs, or websearch settings",
233
+ );
210
234
  if (new Set(models).size !== models.length)
211
235
  throw new Error("Benchmark contains duplicate models");
212
236
  const timeoutMs = positive(
@@ -224,6 +248,8 @@ export async function loadBenchmark(
224
248
  const evals = await Promise.all(
225
249
  directories.map((entry) => loadEval(resolve(evalRoot, entry.name))),
226
250
  );
251
+ if (evals.some((item) => item.judge) && !definition.judge?.model)
252
+ throw new Error("A benchmark containing judge.md requires judge.model");
227
253
  const concurrency = positive(definition.concurrency, 10, "Concurrency");
228
254
  if (concurrency < 2 && evals.some((item) => item.settings.earlyStop))
229
255
  throw new Error(
@@ -234,7 +260,7 @@ export async function loadBenchmark(
234
260
  throw new Error("Container engine must be docker or podman");
235
261
  for (const search of [
236
262
  definition.candidate?.websearch,
237
- definition.judge.websearch,
263
+ definition.judge?.websearch,
238
264
  ])
239
265
  if (search !== undefined && search !== false && search !== "exa")
240
266
  throw new Error("Websearch must be exa or false");
@@ -253,13 +279,19 @@ export async function loadBenchmark(
253
279
  : {}),
254
280
  },
255
281
  judge: {
256
- model: modelRef(definition.judge.model),
257
- timeoutMs: positive(definition.judge.timeoutMs, 600_000, "Judge timeout"),
258
- websearch: definition.judge.websearch ?? "exa",
282
+ ...(definition.judge?.model
283
+ ? { model: modelRef(definition.judge.model) }
284
+ : {}),
285
+ timeoutMs: positive(
286
+ definition.judge?.timeoutMs,
287
+ 600_000,
288
+ "Judge timeout",
289
+ ),
290
+ websearch: definition.judge?.websearch ?? "exa",
259
291
  },
260
292
  container: {
261
293
  engine,
262
- image: definition.container?.image ?? "openeval-runtime:0.2",
294
+ image: definition.container?.image ?? RUNTIME_IMAGE,
263
295
  cpus: positive(definition.container?.cpus, 2, "CPU count"),
264
296
  memoryMiB: positive(definition.container?.memoryMiB, 4096, "Memory"),
265
297
  },
@@ -26,6 +26,7 @@ import {
26
26
  } from "../runtime";
27
27
  import { canJudgeEval } from "./eval-state";
28
28
  import { CandidateEvidence, type EvidenceQuery } from "../infra/evidence";
29
+ import { contained } from "../infra/files";
29
30
 
30
31
  const scheduled = (slot: Slot, benchmark: BenchmarkRun, now: number) =>
31
32
  benchmark.state === "running" &&
@@ -33,6 +34,8 @@ const scheduled = (slot: Slot, benchmark: BenchmarkRun, now: number) =>
33
34
  benchmark.scheduledSlotIds.includes(slot.id);
34
35
 
35
36
  const costOf = (run: EvalRun | JudgeRun, results?: Results): Cost => {
37
+ if ("evalRunId" in run.input && run.input.kind === "code")
38
+ return { usd: 0, reportedUSD: 0, complete: true };
36
39
  const usd = run.session?.accounting?.costUSD;
37
40
  return {
38
41
  usd: usd ?? null,
@@ -162,6 +165,21 @@ export class ResultReader {
162
165
  using results = await this.database(benchmarkId);
163
166
  const judge = results.judgeRun(judgeRunId);
164
167
  if (!judge) throw new Error("Unknown judge run");
168
+ const candidate = results.evalRun(judge.input.evalRunId);
169
+ const criteria = new Map(
170
+ judge.input.criteria.map((criterion) => [criterion.id, criterion]),
171
+ );
172
+ for (const id of Object.keys(judge.judgment?.scores ?? {}))
173
+ if (!criteria.has(id))
174
+ criteria.set(id, { id, name: id.replaceAll("_", " ") });
175
+ const log = async (path?: string) =>
176
+ path
177
+ ? (
178
+ await Bun.file(contained(dirname(results.path), path))
179
+ .text()
180
+ .catch(() => "")
181
+ ).slice(0, 40_000)
182
+ : undefined;
165
183
  return {
166
184
  judge: {
167
185
  id: judge.id,
@@ -172,8 +190,15 @@ export class ResultReader {
172
190
  monitorErrorAt: judge.monitorErrorAt,
173
191
  monitorLimit: judge.monitorLimit,
174
192
  judgment: judge.judgment,
193
+ code: judge.code,
194
+ error: judge.error,
175
195
  },
176
- metrics: judge.input.metrics,
196
+ kind: judge.input.kind,
197
+ criteria: [...criteria.values()],
198
+ metrics: candidate?.metrics ?? judge.code?.metrics,
199
+ codeSource: judge.input.code?.source,
200
+ codeStdout: await log(judge.code?.stdout),
201
+ codeStderr: await log(judge.code?.stderr),
177
202
  stop: results.evalRun(judge.input.evalRunId)?.stop,
178
203
  checks: results.judgeChecks(judgeRunId).map((check) => {
179
204
  const start = timestampMs(check.executionStartedAt);
@@ -421,6 +446,7 @@ function stageState(
421
446
  runtime,
422
447
  cost: costOf(run, results),
423
448
  message: run.error,
449
+ ...("kind" in run.input ? { kind: run.input.kind } : {}),
424
450
  };
425
451
  }
426
452
 
@@ -470,6 +496,13 @@ function liveRun(
470
496
  evalStage.status,
471
497
  );
472
498
  const judgeStage: StageState = {
499
+ kind: benchmark.definition.evals.find((item) => item.id === slot.evalId)
500
+ ?.code
501
+ ? benchmark.definition.evals.find((item) => item.id === slot.evalId)
502
+ ?.judge
503
+ ? "hybrid"
504
+ : "code"
505
+ : "llm",
473
506
  ...stageState(
474
507
  judge,
475
508
  stale,
@@ -486,7 +519,7 @@ function liveRun(
486
519
  ...(judge?.state === "completed"
487
520
  ? {
488
521
  score: judge.judgment?.value ?? null,
489
- metrics: judge.judgment?.metrics,
522
+ scores: judge.judgment?.scores,
490
523
  }
491
524
  : {}),
492
525
  };
@@ -4,6 +4,8 @@ import { CandidateEvidence } from "../infra/evidence";
4
4
  import type { EvidenceRef, Judgment } from "../types";
5
5
  import type { EvidenceView } from "../evidence";
6
6
  import { validateCitations } from "../infra/judging/contract";
7
+ import { openRecording } from "../infra/recording";
8
+ import { tmpdir } from "node:os";
7
9
 
8
10
  /** Read-only records for maintenance clients. Finalized evidence remains authoritative. */
9
11
  export function readBenchmarkRun(directory: string) {
@@ -29,7 +31,26 @@ export async function readEvidence(
29
31
  return (await CandidateEvidence.open(ref.directory, ref.hash)).view(ref);
30
32
  }
31
33
 
32
- /** Reference integrity only; interpretation of task facts stays in judge.md. */
34
+ /** Open all recorded data with lazy native readers; dispose after inspection. */
35
+ export async function readRecording(directory: string, evalRunId: string) {
36
+ using results = new Results(resolve(directory, "runner.db"), true);
37
+ const run = results.evalRun(evalRunId);
38
+ if (!run?.evidence)
39
+ throw new Error("Select an EvalRun with finalized evidence");
40
+ return openRecording(
41
+ {
42
+ run,
43
+ runDirectory: resolve(directory),
44
+ evidence: {
45
+ ...run.evidence,
46
+ directory: resolve(directory, run.evidence.directory),
47
+ },
48
+ },
49
+ process.platform === "win32" ? "C:/tmp/opencode" : tmpdir(),
50
+ );
51
+ }
52
+
53
+ /** Reference integrity only; interpretation of task facts stays in the judges. */
33
54
  export async function verifyJudgmentEvidence(
34
55
  judgment: Judgment,
35
56
  reference: EvidenceRef,
@@ -38,6 +59,6 @@ export async function verifyJudgmentEvidence(
38
59
  await validateCitations(judgment, evidence);
39
60
  return {
40
61
  hash: evidence.checkpoint.hash,
41
- metrics: judgment.metrics.length,
62
+ criteria: Object.keys(judgment.scores).length,
42
63
  };
43
64
  }
@@ -57,7 +57,8 @@ export async function judgeRuns(
57
57
  ...evalDefinition,
58
58
  judge: updated.judge,
59
59
  judgeHash: updated.judgeHash,
60
- metrics: updated.metrics,
60
+ criteria: updated.criteria,
61
+ code: updated.code,
61
62
  name: updated.name,
62
63
  }
63
64
  : evalDefinition;
@@ -20,6 +20,7 @@ import { judgeEvalRun } from "./judge-run";
20
20
  import type { ExecutionContext } from "./context";
21
21
  import { canJudgeEval } from "./eval-state";
22
22
  import { isScored } from "../judgment";
23
+ import { benchmarkScores } from "./scores";
23
24
  import { CostBudget, estimateWork } from "./cost-plan";
24
25
 
25
26
  export type RunBenchmarkOptions = {
@@ -79,12 +80,18 @@ export function finishBenchmark(context: ExecutionContext) {
79
80
  judges.some(
80
81
  (run) => run.id === slot.judgeRunId && isScored(run.judgment?.value),
81
82
  ),
83
+ ) &&
84
+ benchmarkScores(context.definition, slots, judges).every(
85
+ (score) => score.percentage !== null,
82
86
  );
87
+ // Scoped collection deliberately retains older selected executions elsewhere.
88
+ // Only admitted work must match this invocation's runtime and judge inputs.
89
+ const admitted = new Set(context.results.benchmark!.scheduledSlotIds);
83
90
  const currentInputs = planBenchmark(
84
91
  context.definition,
85
92
  context.runtime,
86
93
  context.results,
87
- ).every((item) => item.action === "reuse");
94
+ ).every((item) => !admitted.has(item.slot.id) || item.action === "reuse");
88
95
  const previous = context.results.benchmark!;
89
96
  const ended = [...executions, ...judges]
90
97
  .map((run) => run.completedAt)
@@ -67,6 +67,7 @@ export async function runEvalPipeline(
67
67
  evalRunId: candidate!.id,
68
68
  evidence: saved,
69
69
  rubric: definition.judge,
70
+ kind: "llm",
70
71
  agent: JUDGE_AGENT,
71
72
  model: context.definition.judge.model,
72
73
  judgeHash: slot.judgeHash,
@@ -75,7 +76,7 @@ export async function runEvalPipeline(
75
76
  mode: "monitor",
76
77
  runtimeHash: context.runtime.judgeHash,
77
78
  protocol: JUDGE_PROTOCOL,
78
- metrics: definition.metrics,
79
+ criteria: definition.criteria,
79
80
  monitor: policy,
80
81
  });
81
82
  return saved;
@@ -3,6 +3,8 @@ import type { Slot, EvalRun } from "../types";
3
3
  import type { ExecutionContext } from "./context";
4
4
  import { executeCandidate } from "../infra/containers/candidate";
5
5
  import type { EvidenceFeed } from "../evidence";
6
+ import { CandidateEvidence } from "../infra/evidence";
7
+ import { measureRecording } from "../infra/recording/metrics";
6
8
 
7
9
  /** The normal candidate use case: record its inputs, execute, finalize its evidence. */
8
10
  export async function runEval(
@@ -88,6 +90,17 @@ export async function runEval(
88
90
  }
89
91
  : undefined,
90
92
  };
93
+ if (completed.evidence) {
94
+ const evidence = await CandidateEvidence.open(
95
+ resolve(context.directory, completed.evidence.directory),
96
+ completed.evidence.hash,
97
+ );
98
+ completed.metrics = measureRecording(
99
+ evidence.rawEvents(),
100
+ evidence.toolCalls(),
101
+ completed.evidence,
102
+ );
103
+ }
91
104
  context.results.finishEval(completed);
92
105
  return completed;
93
106
  }
package/src/app/scores.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { BenchmarkDefinition, JudgeRun, Slot } from "../types";
2
2
  import { modelScore } from "../view";
3
+ import { isScored } from "../judgment";
3
4
 
4
5
  /** Scores are derived from a single selection snapshot; every eval has equal weight. */
5
6
  export function benchmarkScores(
@@ -9,46 +10,87 @@ export function benchmarkScores(
9
10
  evalId?: string,
10
11
  ) {
11
12
  const index = new Map(judges.map((run) => [run.id, run]));
13
+ const evals = definition.evals.filter(
14
+ (item) => !evalId || item.id === evalId,
15
+ );
16
+ const definitions = new Map(
17
+ evals.map((item) => {
18
+ const criteria = new Map(
19
+ item.criteria.map((criterion) => [criterion.id, criterion]),
20
+ );
21
+ for (const slot of slots.filter(
22
+ (slot) => slot.active && slot.evalId === item.id,
23
+ )) {
24
+ const judgment = slot.judgeRunId
25
+ ? index.get(slot.judgeRunId)?.judgment
26
+ : undefined;
27
+ for (const id of Object.keys(judgment?.scores ?? {}))
28
+ if (!criteria.has(id))
29
+ criteria.set(id, { id, name: id.replaceAll("_", " ") });
30
+ }
31
+ return [item.id, [...criteria.values()]];
32
+ }),
33
+ );
12
34
  return definition.models.map((model) =>
13
35
  modelScore(
14
36
  model,
15
- definition.evals
16
- .filter((item) => !evalId || item.id === evalId)
17
- .flatMap((item) => {
18
- const metrics = item.metrics;
19
- const selected = slots.filter(
20
- (slot) =>
21
- slot.active && slot.evalId === item.id && slot.model === model,
37
+ evals.flatMap((item) => {
38
+ const criteria = definitions.get(item.id)!;
39
+ const selected = slots.filter(
40
+ (slot) =>
41
+ slot.active && slot.evalId === item.id && slot.model === model,
42
+ );
43
+ return criteria.map((criterion) => {
44
+ const values = selected
45
+ .map(
46
+ (slot) =>
47
+ (slot.judgeRunId
48
+ ? index.get(slot.judgeRunId)?.judgment?.scores
49
+ : undefined)?.[criterion.id]?.value,
50
+ )
51
+ .filter(isScored);
52
+ const scoredSum = values.reduce<number>(
53
+ (sum, value) => sum + value,
54
+ 0,
22
55
  );
23
- return metrics.map((metric) => {
24
- const values = selected
25
- .map(
56
+ return {
57
+ eval: item.id,
58
+ criterion: criterion.id,
59
+ name: criterion.name,
60
+ value:
61
+ values.length === definition.repetitions
62
+ ? scoredSum / definition.repetitions
63
+ : null,
64
+ scored: values.length,
65
+ expected: definition.repetitions,
66
+ passed: values.filter((value) => value === 1).length,
67
+ scoredSum,
68
+ };
69
+ });
70
+ }),
71
+ evals.map((item) => item.id),
72
+ evals
73
+ .filter(
74
+ (item) =>
75
+ item.code &&
76
+ slots
77
+ .filter(
26
78
  (slot) =>
27
- (slot.judgeRunId
28
- ? index.get(slot.judgeRunId)?.judgment?.metrics
29
- : undefined
30
- )?.find((value) => value.id === metric.id)?.value,
79
+ slot.active &&
80
+ slot.evalId === item.id &&
81
+ slot.model === model,
31
82
  )
32
- .filter((value): value is 0 | 1 => value === 0 || value === 1);
33
- const scoredSum = values.reduce<number>(
34
- (sum, value) => sum + value,
35
- 0,
36
- );
37
- return {
38
- eval: item.id,
39
- metric: metric.id,
40
- name: metric.name,
41
- value:
42
- values.length === definition.repetitions
43
- ? scoredSum / definition.repetitions
44
- : null,
45
- scored: values.length,
46
- expected: definition.repetitions,
47
- passed: scoredSum,
48
- scoredSum,
49
- };
50
- });
51
- }),
83
+ .some((slot) => {
84
+ const judge = slot.judgeRunId
85
+ ? index.get(slot.judgeRunId)
86
+ : undefined;
87
+ return (
88
+ judge?.code?.state !== "completed" ||
89
+ judge.input.code?.hash !== item.code!.hash
90
+ );
91
+ }),
92
+ )
93
+ .map((item) => item.id),
52
94
  ),
53
95
  );
54
96
  }
@@ -66,6 +66,8 @@ export async function serveResults(options: {
66
66
  if (value !== null) query[key] = Number(value);
67
67
  }
68
68
  const revision = url.searchParams.get("revision");
69
+ if (url.searchParams.has("metric"))
70
+ query.metric = url.searchParams.get("metric")!;
69
71
  if (revision === "initial" || revision === "final")
70
72
  query.revision = revision;
71
73
  return Response.json(
package/src/evidence.ts CHANGED
@@ -11,7 +11,10 @@ export type EvidenceQuery = {
11
11
  | "tool"
12
12
  | "artifacts"
13
13
  | "artifact"
14
- | "diff";
14
+ | "diff"
15
+ | "metrics";
16
+ /** A dot-separated measurement path for the metrics query. */
17
+ metric?: string;
15
18
  sessionID?: string;
16
19
  type?: string;
17
20
  id?: string;
package/src/index.ts CHANGED
@@ -8,8 +8,8 @@ export type {
8
8
  EvalRun,
9
9
  JudgeRun,
10
10
  Judgment,
11
- MetricDefinition,
12
- MetricJudgment,
11
+ CriterionDefinition,
12
+ CriterionScore,
13
13
  EvidenceCitation,
14
14
  ToolCall,
15
15
  MonitorPolicy,
@@ -30,7 +30,18 @@ export type {
30
30
  JudgeSession,
31
31
  } from "./evidence";
32
32
  export { CANDIDATE_TIMEOUT_MS } from "./types";
33
- export { rubricMetrics, metricMean, isScored } from "./judgment";
33
+ export { rubricCriteria, criterionMean, isScored } from "./judgment";
34
+ export type {
35
+ JudgeContext,
36
+ JudgeFunction,
37
+ JsonValue,
38
+ ScoreValue,
39
+ RunMetrics,
40
+ ToolMetrics,
41
+ RecordedEvent,
42
+ RecordedMessage,
43
+ RecordedFile,
44
+ } from "./judge-context";
34
45
  export { loadBenchmark } from "./app/load-benchmark";
35
46
  export {
36
47
  runBenchmark,
@@ -47,6 +58,7 @@ export { judgeEvidence, recordEvidence } from "./app/judge-evidence";
47
58
  export {
48
59
  readBenchmarkRun,
49
60
  readEvidence,
61
+ readRecording,
50
62
  verifyJudgmentEvidence,
51
63
  } from "./app/read-run";
52
64
  export { snapshotBenchmarkRun } from "./app/snapshot-run";
@@ -3,8 +3,8 @@
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "dependencies": {
6
- "@opencode/server": "0.0.0-beta-19296",
7
- "@opencode/plugin": "0.0.0-beta-19296",
6
+ "@opencode/server": "2.0.3",
7
+ "@opencode/plugin": "2.0.3",
8
8
  "effect": "4.0.0-rc.112"
9
9
  }
10
10
  }
@@ -9,7 +9,7 @@ const runtime = Effect.runFork(
9
9
  hostname: "0.0.0.0",
10
10
  port: 4096,
11
11
  password: process.env.OPENCODE_SERVER_PASSWORD,
12
- app: { name: "opencode", version: "0.0.0-beta-19296" },
12
+ app: { name: "opencode", version: "2.0.3" },
13
13
  database: { path: "/home/dev/.local/share/opencode/opencode.db" },
14
14
  events: { persist: true },
15
15
  });