@hona/openeval 0.2.1 → 0.3.0

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 (213) hide show
  1. package/JUDGING.md +134 -23
  2. package/README.md +79 -9
  3. package/package.json +3 -2
  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 +46 -15
  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/remove-models.ts +60 -0
  15. package/src/app/run-benchmark.ts +4 -0
  16. package/src/app/run-eval-pipeline.ts +2 -1
  17. package/src/app/run-eval.ts +13 -0
  18. package/src/app/scores.ts +76 -34
  19. package/src/app/serve-results.ts +2 -0
  20. package/src/cli.ts +14 -2
  21. package/src/evidence.ts +4 -1
  22. package/src/index.ts +16 -3
  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 +1 -0
  40. package/src/infra/recording/index.ts +244 -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/assets/{abnfDiagram-VCTEODGH-B1kcYgQG.js → abnfDiagram-VCTEODGH-rgmalTBh.js} +1 -1
  48. package/viewer/assets/{angular-html-DrYCUiZv.js → angular-html-CcaajvLj.js} +1 -1
  49. package/viewer/assets/{angular-ts-BgjwQn-Z.js → angular-ts-COx6AtG2.js} +1 -1
  50. package/viewer/assets/{apl-DlHqe8o4.js → apl-lBEnC_vz.js} +1 -1
  51. package/viewer/assets/{arc-C4nf7ZoY.js → arc-BQ0cQOwK.js} +1 -1
  52. package/viewer/assets/architecture-7GRP2DOG-DcUKSnYa.js +1 -0
  53. package/viewer/assets/{architectureDiagram-5GKGNRK7-BIWoe0fO.js → architectureDiagram-5GKGNRK7-_XIJP-LV.js} +1 -1
  54. package/viewer/assets/{astro-BrlHnIh9.js → astro-Ca8FrXna.js} +1 -1
  55. package/viewer/assets/{blade-DOcerOiW.js → blade-B07ZVjUX.js} +1 -1
  56. package/viewer/assets/{blockDiagram-I7D4REHJ-C3x13a9m.js → blockDiagram-I7D4REHJ-BdcDRYB-.js} +1 -1
  57. package/viewer/assets/{c-BMbThxEo.js → c-4MsqXzJK.js} +1 -1
  58. package/viewer/assets/{c4Diagram-7LVT6UL2-BeWktchx.js → c4Diagram-7LVT6UL2-E3nvwqBd.js} +1 -1
  59. package/viewer/assets/channel-CViXUg9J.js +1 -0
  60. package/viewer/assets/{chapel-k2cjkSmc.js → chapel-BlwR_8zu.js} +1 -1
  61. package/viewer/assets/{chunk-4HAMMTFA-o6YtJsqE.js → chunk-4HAMMTFA-DN_vtpqx.js} +1 -1
  62. package/viewer/assets/{chunk-75Z2AOVW-KohQa9Nw.js → chunk-75Z2AOVW-DWMGmicp.js} +1 -1
  63. package/viewer/assets/{chunk-DU6HZSFF-C88wxc6m.js → chunk-DU6HZSFF-D2RGHn75.js} +1 -1
  64. package/viewer/assets/{chunk-F27PBJKO-mO4AP2as.js → chunk-F27PBJKO-wfQZ5Ilx.js} +1 -1
  65. package/viewer/assets/{chunk-GMAD6QVW-DzQX2Cld.js → chunk-GMAD6QVW-DrlfUg7k.js} +1 -1
  66. package/viewer/assets/{chunk-GVQU2GXP-CS78RfJD.js → chunk-GVQU2GXP-BHoRLKIU.js} +1 -1
  67. package/viewer/assets/{chunk-IMKFNOWR-DxS42GUo.js → chunk-IMKFNOWR-mxLyQhYU.js} +1 -1
  68. package/viewer/assets/{chunk-L3NEJ4N5-DGo-uYLy.js → chunk-L3NEJ4N5-IpGl-pvp.js} +1 -1
  69. package/viewer/assets/{chunk-OSK3NFVY-BOcXQG16.js → chunk-OSK3NFVY-t0yN50X6.js} +1 -1
  70. package/viewer/assets/{chunk-P2QGCYS3-B7GnZdIQ.js → chunk-P2QGCYS3-BPBYYOcK.js} +1 -1
  71. package/viewer/assets/{chunk-POPQ4Y6H-DgBcYHog.js → chunk-POPQ4Y6H-DIOcv86B.js} +1 -1
  72. package/viewer/assets/{chunk-PWAF6VOD-DpEq6qHA.js → chunk-PWAF6VOD-CqVB-mzq.js} +1 -1
  73. package/viewer/assets/{chunk-SHT3W25Y-CrOGxKM2.js → chunk-SHT3W25Y-DwOEgFIr.js} +1 -1
  74. package/viewer/assets/{chunk-SVP7TREG-4HY7Fljj.js → chunk-SVP7TREG-Dfg1jHc3.js} +1 -1
  75. package/viewer/assets/{chunk-TICWLB2K-B1Rl31EC.js → chunk-TICWLB2K-DE6tQNRu.js} +1 -1
  76. package/viewer/assets/{chunk-XXDRQBXY-CK8giEW-.js → chunk-XXDRQBXY-Bu8dW06M.js} +1 -1
  77. package/viewer/assets/classDiagram-ZZMXUADV-O1Zm0_p8.js +1 -0
  78. package/viewer/assets/classDiagram-v2-VYDZK3BY-O1Zm0_p8.js +1 -0
  79. package/viewer/assets/{cobol-CupUwvw9.js → cobol-BS_DrT-b.js} +1 -1
  80. package/viewer/assets/{coffee-C17pkozF.js → coffee-Clk97ecB.js} +1 -1
  81. package/viewer/assets/{cose-bilkent-JH36ORCC-V1OmrmCv.js → cose-bilkent-JH36ORCC-B3xA8n2e.js} +1 -1
  82. package/viewer/assets/{cpp-BJwVQVXg.js → cpp-aunMdKLY.js} +1 -1
  83. package/viewer/assets/{crystal-BF-oCN-L.js → crystal-B1oihgG5.js} +1 -1
  84. package/viewer/assets/{css-BMgkVI3c.js → css-ClOubSVo.js} +1 -1
  85. package/viewer/assets/{cynefin-OW5HDTMX-BEjY_gsc.js → cynefin-OW5HDTMX-DdEeq9jN.js} +1 -1
  86. package/viewer/assets/{cynefinDiagram-5FMLGOSQ-bfSYgr6z.js → cynefinDiagram-5FMLGOSQ-BwHCV_5V.js} +1 -1
  87. package/viewer/assets/{dagre-GXQ25YYZ-VtwTgGfa.js → dagre-GXQ25YYZ-BkuWRmq-.js} +1 -1
  88. package/viewer/assets/{diagram-S7CK7UJ4-CHaAAyW7.js → diagram-S7CK7UJ4-C5WGyCny.js} +1 -1
  89. package/viewer/assets/{diagram-UQ7AKVKN-CjL65-As.js → diagram-UQ7AKVKN-mdununKH.js} +1 -1
  90. package/viewer/assets/{diagram-VSXAHHWV-Cnxrk1TJ.js → diagram-VSXAHHWV-CpuvU9Ij.js} +1 -1
  91. package/viewer/assets/{diagram-VX7I27RA-D78jPXr7.js → diagram-VX7I27RA-CcLmTPwv.js} +1 -1
  92. package/viewer/assets/{diagram-Z3DM3KII-DKjnOaF2.js → diagram-Z3DM3KII-DWCnKyKY.js} +1 -1
  93. package/viewer/assets/{dist-BTYPW0dz.js → dist-1DE7d697.js} +1 -1
  94. package/viewer/assets/{ebnfDiagram-PWID7BFC-7XiXh5Lx.js → ebnfDiagram-PWID7BFC-9M_-_M-B.js} +1 -1
  95. package/viewer/assets/{edge-B34N_HjN.js → edge-NsPpWeFX.js} +1 -1
  96. package/viewer/assets/{elixir-CQzWMyKD.js → elixir-CXZ4zQTn.js} +1 -1
  97. package/viewer/assets/{elm-BNJiG-wg.js → elm-DVXugwZs.js} +1 -1
  98. package/viewer/assets/{erDiagram-RLTQ6QDP-YPFEgZxU.js → erDiagram-RLTQ6QDP-B2j6iCkZ.js} +1 -1
  99. package/viewer/assets/{erb-D5Rm3UNa.js → erb-h8ybsnSQ.js} +1 -1
  100. package/viewer/assets/eventmodeling-NTZA5JFV-CpMf6avu.js +1 -0
  101. package/viewer/assets/flowDiagram-HODETNUW-DIKEcF1k.js +1 -0
  102. package/viewer/assets/{ganttDiagram-EL5Y4UJY-ergSGtTP.js → ganttDiagram-EL5Y4UJY-CR2QLJ-I.js} +1 -1
  103. package/viewer/assets/{git-rebase-BpIMZIRG.js → git-rebase-CT9f4kn8.js} +1 -1
  104. package/viewer/assets/{gitGraph-4MIJSDKK-CZt4REVj.js → gitGraph-4MIJSDKK-DGw-0dqe.js} +1 -1
  105. package/viewer/assets/{gitGraphDiagram-WWUBYQGX-h9QMzC2r.js → gitGraphDiagram-WWUBYQGX-BW112jVV.js} +1 -1
  106. package/viewer/assets/{glimmer-js-SeSwns5_.js → glimmer-js-B17pi1gL.js} +1 -1
  107. package/viewer/assets/{glimmer-ts-BExI5tTz.js → glimmer-ts-CNmwRbOn.js} +1 -1
  108. package/viewer/assets/{glsl-CXikKw2b.js → glsl-By4kbzYa.js} +1 -1
  109. package/viewer/assets/{graphql-6GpQIInt.js → graphql-BrDJs0bG.js} +1 -1
  110. package/viewer/assets/{hack-i7rizvpB.js → hack-NosEIqRi.js} +1 -1
  111. package/viewer/assets/{haml-C68gmkTf.js → haml-B-GQD8Zj.js} +1 -1
  112. package/viewer/assets/{handlebars-BBn_c0Xt.js → handlebars-1vSTccT5.js} +1 -1
  113. package/viewer/assets/{html-BObmAZQA.js → html-BBEodwWE.js} +1 -1
  114. package/viewer/assets/{html-derivative-BjMuR1LM.js → html-derivative-ClDEAvlc.js} +1 -1
  115. package/viewer/assets/{http-BJBk8gt0.js → http-I0uH89QQ.js} +1 -1
  116. package/viewer/assets/{hurl-D7obCYfg.js → hurl-JU97iWqA.js} +1 -1
  117. package/viewer/assets/{index-FMtYXcX7.js → index-CXBRQlCH.js} +5 -5
  118. package/viewer/assets/{index-Wz0qQyIk.css → index-D6HZB4Yz.css} +1 -1
  119. package/viewer/assets/{info-A6RAGUB7-CrzitQIU.js → info-A6RAGUB7-aRrZ0_i2.js} +1 -1
  120. package/viewer/assets/{infoDiagram-27XIBGKW-D2CgRDxu.js → infoDiagram-27XIBGKW-BnD3mUdL.js} +1 -1
  121. package/viewer/assets/{ishikawaDiagram-5VMMS53U-BhnQMHOo.js → ishikawaDiagram-5VMMS53U-u-wcafFg.js} +1 -1
  122. package/viewer/assets/{java-5VJU_EW8.js → java-BxAxXJLN.js} +1 -1
  123. package/viewer/assets/{javascript-r8UgndxQ.js → javascript-DNepMMwm.js} +1 -1
  124. package/viewer/assets/{jinja-BY2ibHG5.js → jinja-CfKdKg9T.js} +1 -1
  125. package/viewer/assets/{jison-C_7YxymZ.js → jison-C62ElGA9.js} +1 -1
  126. package/viewer/assets/{journeyDiagram-3NMN7TZE-DSBzKQEa.js → journeyDiagram-3NMN7TZE-E6uhr_fk.js} +1 -1
  127. package/viewer/assets/{json-8e1WlJYD.js → json-DSQqeQzH.js} +1 -1
  128. package/viewer/assets/{jsx-BYp5GD4Y.js → jsx-CpZi9Ceb.js} +1 -1
  129. package/viewer/assets/{julia-Bkob1uVX.js → julia-CypyJgFE.js} +1 -1
  130. package/viewer/assets/{just-CUnSjty-.js → just-_p8XAy70.js} +1 -1
  131. package/viewer/assets/{kanban-definition-UXKFOSKX-CotbrQue.js → kanban-definition-UXKFOSKX-Blo6hDj7.js} +1 -1
  132. package/viewer/assets/{latex-UdE3ivqk.js → latex-Blby6_jP.js} +1 -1
  133. package/viewer/assets/{line-DDh6a6hz.js → line-CQc5Trjj.js} +1 -1
  134. package/viewer/assets/{linear-BxMsCbKb.js → linear-BjWjnaEC.js} +1 -1
  135. package/viewer/assets/{liquid-WL4MvEIT.js → liquid-x3m9Bx2x.js} +1 -1
  136. package/viewer/assets/{lua-CtOubZta.js → lua-Da9XAgwV.js} +1 -1
  137. package/viewer/assets/{marko-C0GSQJi3.js → marko-Gu1d_h3t.js} +1 -1
  138. package/viewer/assets/{mdc-DUivddFc.js → mdc-CM0pUbEe.js} +1 -1
  139. package/viewer/assets/{mermaid-parser.core-FhKE_uv2.js → mermaid-parser.core-DR191xUW.js} +3 -3
  140. package/viewer/assets/{mermaid.core-BSOWwyvd.js → mermaid.core-DnwMH2U1.js} +4 -4
  141. package/viewer/assets/{mindmap-definition-YA3MSWOX-tsLZ_Opn.js → mindmap-definition-YA3MSWOX-KrnPcZeq.js} +1 -1
  142. package/viewer/assets/{nginx-B__INAtC.js → nginx-DAKCD0HY.js} +1 -1
  143. package/viewer/assets/{nim-UEY12GJF.js → nim-BvNTuhqF.js} +1 -1
  144. package/viewer/assets/{org-DDIZsz95.js → org-B-tOc6Cc.js} +1 -1
  145. package/viewer/assets/{packet-AYTQ26CC-CqdJnqfo.js → packet-AYTQ26CC-Cl2KBEou.js} +1 -1
  146. package/viewer/assets/{pegDiagram-XKGWAZYB-BJs7qu7m.js → pegDiagram-XKGWAZYB--GvmNzTl.js} +1 -1
  147. package/viewer/assets/{perl-D5KwNZt7.js → perl-RyNpCOE-.js} +1 -1
  148. package/viewer/assets/{php-BEs7d_Dk.js → php-D5gke6Nh.js} +1 -1
  149. package/viewer/assets/{pie-WAS4IAKB-lgzqBWqy.js → pie-WAS4IAKB-RfZPDbVI.js} +1 -1
  150. package/viewer/assets/{pieDiagram-E7YTZNPT-Cxe9LnnE.js → pieDiagram-E7YTZNPT-BbcD9IEY.js} +1 -1
  151. package/viewer/assets/{pug-B0dsfQOV.js → pug-CSZdPWzF.js} +1 -1
  152. package/viewer/assets/{qml-XPIhrIzf.js → qml-CCUSNo18.js} +1 -1
  153. package/viewer/assets/{quadrantDiagram-AXDQQJYC-D5JGwC11.js → quadrantDiagram-AXDQQJYC-DHP-nbSh.js} +1 -1
  154. package/viewer/assets/{r-C6hQdpIM.js → r-Cl6F5Lf8.js} +1 -1
  155. package/viewer/assets/{radar-RG4KPBEZ-B7JRLhUA.js → radar-RG4KPBEZ-DdfC1yOQ.js} +1 -1
  156. package/viewer/assets/{railroad-74A4TZTK-Df7ZQBLn.js → railroad-74A4TZTK-MHxcKgVS.js} +1 -1
  157. package/viewer/assets/railroad-abnf-HS5TGJTU-yEJXYTCu.js +1 -0
  158. package/viewer/assets/railroad-ebnf-LZEXJU2U-CEc058YK.js +1 -0
  159. package/viewer/assets/railroad-peg-WCYAUIDC-0iFSHGbV.js +1 -0
  160. package/viewer/assets/{railroadDiagram-O6MQD6OU-DZJzpmP8.js → railroadDiagram-O6MQD6OU-sxdxPqy-.js} +1 -1
  161. package/viewer/assets/{razor-C3KSUuOw.js → razor-hm0vLE-h.js} +1 -1
  162. package/viewer/assets/{regexp-n-T936Rk.js → regexp-BhIot3VY.js} +1 -1
  163. package/viewer/assets/{requirementDiagram-BXWQKSXE-Be9lEFrk.js → requirementDiagram-BXWQKSXE-CrZXt7Yu.js} +1 -1
  164. package/viewer/assets/{rst-fVnHDyXD.js → rst-C3giCu1C.js} +1 -1
  165. package/viewer/assets/{ruby-DuRgxlXF.js → ruby-B6fOYc5V.js} +1 -1
  166. package/viewer/assets/{sankeyDiagram-P5KCCOFB-BsgKAEBN.js → sankeyDiagram-P5KCCOFB-BVK_LjzN.js} +1 -1
  167. package/viewer/assets/{sas-C0WA2OFa.js → sas-a9NUQlsy.js} +1 -1
  168. package/viewer/assets/{scss-CQDZ4smR.js → scss-ChVMSp7G.js} +1 -1
  169. package/viewer/assets/{sequenceDiagram-WJ2MYXX4-BkUDWL0D.js → sequenceDiagram-WJ2MYXX4-B4E1wY9a.js} +1 -1
  170. package/viewer/assets/{shellscript-CE84G0GP.js → shellscript-Dxbnxou2.js} +1 -1
  171. package/viewer/assets/{shellsession-BdJvdaqL.js → shellsession-DWcRFAj_.js} +1 -1
  172. package/viewer/assets/{soy-BSxOREC0.js → soy-C-D-pvKx.js} +1 -1
  173. package/viewer/assets/{sql-D4MN9uRI.js → sql-W7nl9Rxy.js} +1 -1
  174. package/viewer/assets/{src-BT_kESTA.js → src-C_D1vmKd.js} +1 -1
  175. package/viewer/assets/{stata-B6nXAaGV.js → stata-BWq3CAeQ.js} +1 -1
  176. package/viewer/assets/{stateDiagram-D77RDMKH-C80wp8xG.js → stateDiagram-D77RDMKH-Dc8nXUDp.js} +1 -1
  177. package/viewer/assets/stateDiagram-v2-MP3YSRHH-SMxxOURe.js +1 -0
  178. package/viewer/assets/{surrealql-fVjZmhV3.js → surrealql-BkXUcx4E.js} +1 -1
  179. package/viewer/assets/{svelte-DnBQ5p7d.js → svelte-CowGUt9l.js} +1 -1
  180. package/viewer/assets/{swimlanes-42K2YHIH-Cff6yT8e.js → swimlanes-42K2YHIH-BtZAMLQ7.js} +1 -1
  181. package/viewer/assets/swimlanesDiagram-VR7AAH4N-C_XphOba.js +8 -0
  182. package/viewer/assets/{templ-TCx51Uts.js → templ-ejLYfFxc.js} +1 -1
  183. package/viewer/assets/{tex-CF6SMAQ_.js → tex-McefVK6r.js} +1 -1
  184. package/viewer/assets/{timeline-definition-24CTP7MA-Cje7BvaN.js → timeline-definition-24CTP7MA-TvSh-paH.js} +1 -1
  185. package/viewer/assets/{treeView-Q6P3EWNA-D-QLY3IO.js → treeView-Q6P3EWNA-BRIxIFdd.js} +1 -1
  186. package/viewer/assets/{treemap-WGGIJYW6-BCHcTa4N.js → treemap-WGGIJYW6-CeY__ApH.js} +1 -1
  187. package/viewer/assets/{ts-tags-BqLRnADX.js → ts-tags-D8LHJIWj.js} +1 -1
  188. package/viewer/assets/{tsx-D7KpDX0b.js → tsx-BkPshBHh.js} +1 -1
  189. package/viewer/assets/{twig-CEZLObpN.js → twig-CmfWCxbk.js} +1 -1
  190. package/viewer/assets/{typescript-DAbIDLbf.js → typescript-DnVmzADb.js} +1 -1
  191. package/viewer/assets/{typst-CqN7-whv.js → typst-BpEOIbiv.js} +1 -1
  192. package/viewer/assets/{vennDiagram-4TSXK5OY-Bz20rx02.js → vennDiagram-4TSXK5OY-Ba-jimVX.js} +1 -1
  193. package/viewer/assets/{vue-html-DQmvvKbd.js → vue-html-C-XYg42q.js} +1 -1
  194. package/viewer/assets/{vue-vine-Did3qTVW.js → vue-vine-DK68dTen.js} +1 -1
  195. package/viewer/assets/{vue-kTxN6wdc.js → vue-zII4ln6o.js} +1 -1
  196. package/viewer/assets/{wardley-WFR3VGLG-CpXaqOit.js → wardley-WFR3VGLG-e2A2dYzM.js} +1 -1
  197. package/viewer/assets/{wardleyDiagram-VM6X3IG4-Cz06jHpA.js → wardleyDiagram-VM6X3IG4-D2Rq1FsU.js} +1 -1
  198. package/viewer/assets/{xml-te2hFcAA.js → xml-Cc-dIbaq.js} +1 -1
  199. package/viewer/assets/{xsl-X4fuKSVl.js → xsl-C7A6UsbO.js} +1 -1
  200. package/viewer/assets/{xychartDiagram-S5SC5T6Z-BXCw0sfu.js → xychartDiagram-S5SC5T6Z-De55Qsdg.js} +1 -1
  201. package/viewer/assets/{yaml-NHtDSZab.js → yaml-BJLNLIRM.js} +1 -1
  202. package/viewer/index.html +2 -2
  203. package/viewer/assets/architecture-7GRP2DOG-Be250VKZ.js +0 -1
  204. package/viewer/assets/channel-DtULXRee.js +0 -1
  205. package/viewer/assets/classDiagram-ZZMXUADV-Bmqu01Xf.js +0 -1
  206. package/viewer/assets/classDiagram-v2-VYDZK3BY-Bmqu01Xf.js +0 -1
  207. package/viewer/assets/eventmodeling-NTZA5JFV-CJCg5f6p.js +0 -1
  208. package/viewer/assets/flowDiagram-HODETNUW-BFzq4Zvs.js +0 -1
  209. package/viewer/assets/railroad-abnf-HS5TGJTU-Z8q3QVTf.js +0 -1
  210. package/viewer/assets/railroad-ebnf-LZEXJU2U-BWoRkG3W.js +0 -1
  211. package/viewer/assets/railroad-peg-WCYAUIDC-BzoV1cN7.js +0 -1
  212. package/viewer/assets/stateDiagram-v2-MP3YSRHH-CYdsKKlz.js +0 -1
  213. package/viewer/assets/swimlanesDiagram-VR7AAH4N-niTUGjMT.js +0 -8
package/src/cli.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  loadBenchmark,
7
7
  buildImage,
8
8
  addModels,
9
+ removeModels,
9
10
  retryEvalRun,
10
11
  judgeRun,
11
12
  serveResults,
@@ -39,6 +40,7 @@ Commands:
39
40
  snapshot <run> <name> Export a score snapshot
40
41
  merge-runs <to> <from> Merge results into one aggregate
41
42
  add-models <run> Add models with repeated --model flags
43
+ remove-models <run> Remove active models while retaining their evidence
42
44
  retry <run> <eval-run> Retry a candidate execution
43
45
  rejudge <run> <eval-run> Judge the saved evidence again
44
46
 
@@ -46,7 +48,7 @@ Options:
46
48
  --benchmark <dir> Benchmark directory (default: current directory)
47
49
  --run <dir> Resume a specific result directory
48
50
  --new Start a new result
49
- --model <provider/id> Add a model to the benchmark
51
+ --model <provider/id> Model to add or remove (repeatable)
50
52
  --only-model <ref> Execute only this model (repeatable)
51
53
  --only-eval <id> Execute only this eval (repeatable)
52
54
  --only-repetition <n> Execute only this repetition (repeatable)
@@ -125,6 +127,16 @@ Requires Bun 1.4.2+, Docker, and an authenticated OpenCode installation.`);
125
127
  if (!args[1])
126
128
  throw new Error("add-models requires a benchmark run directory");
127
129
  console.log((await addModels(resolve(args[1]), models())).directory);
130
+ } else if (command === "remove-models") {
131
+ if (!args[1])
132
+ throw new Error("remove-models requires a benchmark run directory");
133
+ const result = await removeModels(resolve(args[1]), models());
134
+ console.log(JSON.stringify({
135
+ directory: result.directory,
136
+ removed: result.removed,
137
+ retiredSlots: result.retiredSlots,
138
+ status: result.benchmark.state,
139
+ }, null, 2));
128
140
  } else if (command === "retry" || command === "rejudge") {
129
141
  if (!args[1] || !args[2])
130
142
  throw new Error(
@@ -137,7 +149,7 @@ Requires Bun 1.4.2+, Docker, and an authenticated OpenCode installation.`);
137
149
  console.log(JSON.stringify(result, null, 2));
138
150
  } else
139
151
  throw new Error(
140
- "Commands: image, plan, run, view, add-models, merge-runs, retry, rejudge, snapshot",
152
+ "Commands: image, plan, run, view, add-models, remove-models, merge-runs, retry, rejudge, snapshot",
141
153
  );
142
154
  } catch (error) {
143
155
  console.error(error instanceof Error ? error.message : String(error));
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,
@@ -38,6 +49,7 @@ export {
38
49
  type RunBenchmarkOptions,
39
50
  } from "./app/run-benchmark";
40
51
  export { addModels } from "./app/add-models";
52
+ export { removeModels } from "./app/remove-models";
41
53
  export type { CostEstimate } from "./app/cost-plan";
42
54
  export { mergeBenchmarkRuns } from "./app/merge-runs";
43
55
  export { retryEvalRun } from "./app/retry-run";
@@ -46,6 +58,7 @@ export { judgeEvidence, recordEvidence } from "./app/judge-evidence";
46
58
  export {
47
59
  readBenchmarkRun,
48
60
  readEvidence,
61
+ readRecording,
49
62
  verifyJudgmentEvidence,
50
63
  } from "./app/read-run";
51
64
  export { snapshotBenchmarkRun } from "./app/snapshot-run";
@@ -11,6 +11,7 @@ import type { EvidenceFeed, EvidenceView, EvidenceQuery } from "../../evidence";
11
11
  export type { EvidenceQuery } from "../../evidence";
12
12
  import { trackTool } from "./tool-calls";
13
13
  import { hash as sha256, writeJson } from "../files";
14
+ import { measureRecording } from "../recording/metrics";
14
15
  type InferenceResponse = { text: string; textBlocks?: string[] };
15
16
  type ArtifactPolicy = { excludeDirectories: string[] };
16
17
 
@@ -25,7 +26,7 @@ export type EvidenceQueryAudit = {
25
26
  checkId?: string;
26
27
  checkpoint?: EvidenceCheckpoint;
27
28
  };
28
- type FileEntry = {
29
+ export type FileEntry = {
29
30
  path: string;
30
31
  bytes: number;
31
32
  sha256?: string;
@@ -37,7 +38,7 @@ export type EvidenceEvent = {
37
38
  time: string;
38
39
  event: OpenCodeStreamEvent;
39
40
  };
40
- type EvidenceManifest = {
41
+ export type EvidenceManifest = {
41
42
  version: 1;
42
43
  handle: "candidate";
43
44
  coverage: "recorded-session" | "recorded-prefix";
@@ -446,6 +447,28 @@ export class CandidateEvidence {
446
447
  readonly hash: string,
447
448
  ) {}
448
449
 
450
+ /** Native records for trusted code judges. LLM queries use the separate blinded view. */
451
+ rawEvents(): EvidenceEvent[] {
452
+ return structuredClone(this.records);
453
+ }
454
+
455
+ toolCalls(): ToolCall[] {
456
+ return structuredClone(this.manifest.tools);
457
+ }
458
+
459
+ files(revision: "initial" | "final" = "final"): FileEntry[] {
460
+ const snapshot = this.manifest[revision];
461
+ if (!snapshot)
462
+ throw new Error(`${revision} workspace snapshot is unavailable`);
463
+ return structuredClone(snapshot.files);
464
+ }
465
+
466
+ async readFile(path: string, revision: "initial" | "final" = "final") {
467
+ const entry = this.entry(path, revision);
468
+ if (!entry) throw new Error(`Artifact not found: ${path}`);
469
+ return this.bytes(entry);
470
+ }
471
+
449
472
  view(reference: EvidenceRef): EvidenceView {
450
473
  if (reference.hash !== this.hash)
451
474
  throw new Error("Evidence view reference does not match its archive");
@@ -629,6 +652,20 @@ export class CandidateEvidence {
629
652
 
630
653
  async query(input: EvidenceQuery): Promise<Record<string, unknown>> {
631
654
  if (input.action === "summary") return this.summary();
655
+ if (input.action === "metrics") {
656
+ const metrics = measureRecording(this.records, this.manifest.tools, {
657
+ directory: this.directory,
658
+ hash: this.hash,
659
+ });
660
+ if (!input.metric) return { ...metrics };
661
+ let value: unknown = metrics;
662
+ for (const key of input.metric.split(".")) {
663
+ if (!value || typeof value !== "object" || !Object.hasOwn(value, key))
664
+ throw new Error(`Unknown recorded metric: ${input.metric}`);
665
+ value = (value as Record<string, unknown>)[key];
666
+ }
667
+ return { metric: input.metric, value, evidence: metrics.evidence };
668
+ }
632
669
  if (input.action === "response")
633
670
  return textPage(this.manifest.response.text, input);
634
671
  if (input.action === "events")
@@ -8,6 +8,7 @@ export function trackTool(
8
8
  if (!("data" in event)) return;
9
9
  const data = event.data as {
10
10
  id?: string;
11
+ sessionID?: string;
11
12
  name?: string;
12
13
  assistantMessageID?: string;
13
14
  input?: Record<string, unknown>;
@@ -17,8 +18,10 @@ export function trackTool(
17
18
  executed?: boolean;
18
19
  };
19
20
  if (!data.id) return;
20
- const value = calls.get(data.id) ?? {
21
+ const key = `${data.sessionID ?? ""}:${data.assistantMessageID ?? ""}:${data.id}`;
22
+ const value = calls.get(key) ?? {
21
23
  id: data.id,
24
+ sessionID: data.sessionID,
22
25
  name: data.name ?? "unknown",
23
26
  assistantMessageId: data.assistantMessageID ?? "",
24
27
  status: "preparing" as const,
@@ -45,5 +48,5 @@ export function trackTool(
45
48
  if (value.startedAt !== undefined)
46
49
  value.durationMs = event.created - value.startedAt;
47
50
  }
48
- calls.set(value.id, value);
51
+ calls.set(key, value);
49
52
  }
@@ -18,7 +18,7 @@ export const JUDGE_TOOLS = [
18
18
  */
19
19
  export const JUDGE_AGENT = {
20
20
  id: "openeval-judge",
21
- description: "Grades immutable recorded work against eval metrics",
21
+ description: "Grades immutable recorded work against eval criteria",
22
22
  mode: "primary",
23
23
  system: readFileSync(new URL("./judge-agent.md", import.meta.url), "utf8"),
24
24
  permissions: [
@@ -35,6 +35,7 @@ export async function judgeConfiguration(
35
35
  input: Pick<JudgeRunInput, "agent" | "rubric" | "websearch">,
36
36
  directory: string,
37
37
  ) {
38
+ if (!input.agent) throw new Error("An LLM judge requires an agent profile");
38
39
  const { id, ...agent } = input.agent;
39
40
  const configuration = resolve(directory, "configuration");
40
41
  const config = {
@@ -45,7 +46,7 @@ export async function judgeConfiguration(
45
46
  ...agent,
46
47
  // Keep the rubric in the native system prompt so compaction cannot lose
47
48
  // it. Request context and result data move through registered tools.
48
- system: `${agent.system.trim()}\n\n# Eval metrics\n\n${input.rubric.trim()}`,
49
+ system: `${agent.system.trim()}\n\n# Eval rubric\n\n${input.rubric.trim()}`,
49
50
  },
50
51
  },
51
52
  websearch: input.websearch ? { provider: input.websearch } : false,
@@ -0,0 +1,102 @@
1
+ import type { JsonValue } from "../../judge-context";
2
+ import type { CriterionScore, Judgment } from "../../types";
3
+ import { criterionMean, isScored } from "../../judgment";
4
+
5
+ /** Preserve author JSON exactly; reject values JSON would silently discard or coerce. */
6
+ export function jsonOutput(
7
+ value: unknown,
8
+ path = "output",
9
+ seen = new Set<object>(),
10
+ ): JsonValue {
11
+ if (value === null || typeof value === "boolean" || typeof value === "string")
12
+ return value;
13
+ if (typeof value === "number" && Number.isFinite(value)) return value;
14
+ if (typeof value !== "object" || !value || seen.has(value))
15
+ throw new Error(
16
+ `${path} must be JSON-compatible (no undefined, non-finite numbers, or cycles)`,
17
+ );
18
+ if (
19
+ !Array.isArray(value) &&
20
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value))
21
+ )
22
+ throw new Error(`${path} must be a plain JSON object`);
23
+ if (Object.getOwnPropertySymbols(value).length)
24
+ throw new Error(`${path} contains a symbol key`);
25
+ seen.add(value);
26
+ try {
27
+ if (Array.isArray(value))
28
+ return Array.from(value, (item, index) =>
29
+ jsonOutput(item, `${path}[${index}]`, seen),
30
+ );
31
+ return Object.fromEntries(
32
+ Object.entries(value).map(([key, item]) => [
33
+ key,
34
+ jsonOutput(item, `${path}.${key}`, seen),
35
+ ]),
36
+ );
37
+ } finally {
38
+ seen.delete(value);
39
+ }
40
+ }
41
+
42
+ export function codeJudgment(output: JsonValue): Judgment {
43
+ const values =
44
+ output &&
45
+ typeof output === "object" &&
46
+ !Array.isArray(output) &&
47
+ "scores" in output
48
+ ? output.scores
49
+ : {};
50
+ if (!values || typeof values !== "object" || Array.isArray(values))
51
+ throw new Error("output.scores must be an object keyed by criterion ID");
52
+ const scores = Object.fromEntries(
53
+ Object.entries(values).map(([id, supplied]): [string, CriterionScore] => {
54
+ if (!/^[a-z][a-z0-9_]*$/.test(id))
55
+ throw new Error(`Invalid criterion ID: ${id}`);
56
+ const value = typeof supplied === "boolean" ? Number(supplied) : supplied;
57
+ if (value !== null && !isScored(value))
58
+ throw new Error(
59
+ `scores.${id} must be a boolean, a finite number from 0 to 1, or null`,
60
+ );
61
+ return [
62
+ id,
63
+ {
64
+ value: value as number | null,
65
+ reason:
66
+ value === null
67
+ ? "judge.ts returned an unresolved score."
68
+ : `judge.ts returned ${String(supplied)}.`,
69
+ evidence: value === null ? [] : [{ kind: "recording" }],
70
+ source: "judge.ts",
71
+ },
72
+ ];
73
+ }),
74
+ );
75
+ return {
76
+ scores,
77
+ value: criterionMean(scores),
78
+ reason: Object.keys(scores).length
79
+ ? "Criterion scores computed by judge.ts."
80
+ : "The code judge returned data without criterion scores.",
81
+ };
82
+ }
83
+
84
+ export function combineJudgments(...judgments: Judgment[]): Judgment {
85
+ const scores: Record<string, CriterionScore> = {};
86
+ for (const judgment of judgments)
87
+ for (const [id, score] of Object.entries(judgment.scores)) {
88
+ if (Object.hasOwn(scores, id))
89
+ throw new Error(
90
+ `Duplicate criterion ID from judge.md and judge.ts: ${id}`,
91
+ );
92
+ scores[id] = score;
93
+ }
94
+ return {
95
+ scores,
96
+ value: criterionMean(scores),
97
+ reason: judgments
98
+ .map((judgment) => judgment.reason)
99
+ .filter(Boolean)
100
+ .join("\n"),
101
+ };
102
+ }
@@ -0,0 +1,96 @@
1
+ import { dirname, isAbsolute, resolve } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import type { CodeJudgeDefinition } from "../../judge-context";
4
+ import { fingerprint } from "../files";
5
+
6
+ /** Bundle local imports without executing author code during planning. */
7
+ export async function compileCodeJudge(
8
+ file: string,
9
+ ): Promise<CodeJudgeDefinition> {
10
+ file = resolve(file);
11
+ const dependencies: Record<string, string> = {};
12
+ const sdkManifest = fileURLToPath(
13
+ new URL("../../../package.json", import.meta.url),
14
+ );
15
+ dependencies[sdkManifest] = await Bun.file(sdkManifest).text();
16
+ const external = new Set<string>();
17
+ const built = await Bun.build({
18
+ entrypoints: [file],
19
+ root: dirname(file),
20
+ target: "bun",
21
+ format: "esm",
22
+ packages: "external",
23
+ sourcemap: "external",
24
+ plugins: [
25
+ {
26
+ name: "judge-dependencies",
27
+ setup(build) {
28
+ build.onResolve({ filter: /^[^./]/ }, (args) => {
29
+ if (
30
+ args.path.startsWith("node:") ||
31
+ args.path.startsWith("bun:") ||
32
+ isAbsolute(args.path)
33
+ )
34
+ return;
35
+ const path = Bun.resolveSync(
36
+ args.path,
37
+ args.resolveDir || dirname(file),
38
+ );
39
+ external.add(path);
40
+ return { path, external: true };
41
+ });
42
+ },
43
+ },
44
+ ],
45
+ });
46
+ if (!built.success)
47
+ throw new Error(`Cannot compile judge.ts: ${built.logs.join("\n")}`);
48
+ const source = await built.outputs
49
+ .find((output) => output.kind === "entry-point")!
50
+ .text();
51
+ if (
52
+ !new Bun.Transpiler({ loader: "js" })
53
+ .scan(source)
54
+ .exports.includes("default")
55
+ )
56
+ throw new Error("judge.ts must have a default function export");
57
+ const sourceMap =
58
+ (await built.outputs
59
+ .find((output) => output.kind === "sourcemap")
60
+ ?.text()) ?? "";
61
+ const collect = async (directory: string, stopAtPackage: boolean) => {
62
+ for (;;) {
63
+ let found = false;
64
+ for (const name of [
65
+ "package.json",
66
+ "bun.lock",
67
+ "bun.lockb",
68
+ "package-lock.json",
69
+ "pnpm-lock.yaml",
70
+ "yarn.lock",
71
+ ]) {
72
+ const path = resolve(directory, name);
73
+ if (await Bun.file(path).exists()) {
74
+ dependencies[path] =
75
+ name === "bun.lockb"
76
+ ? Buffer.from(await Bun.file(path).bytes()).toString("base64")
77
+ : await Bun.file(path).text();
78
+ found ||= name === "package.json";
79
+ }
80
+ }
81
+ if (found && stopAtPackage) return;
82
+ const parent = dirname(directory);
83
+ if (parent === directory) return;
84
+ directory = parent;
85
+ }
86
+ };
87
+ await collect(dirname(file), false);
88
+ for (const path of external) await collect(dirname(path), true);
89
+ return {
90
+ file,
91
+ source,
92
+ sourceMap,
93
+ dependencies,
94
+ hash: fingerprint({ source, sourceMap, dependencies }),
95
+ };
96
+ }
@@ -0,0 +1,26 @@
1
+ import { pathToFileURL } from "node:url";
2
+ import { openRecording, type RecordingInput } from "../recording";
3
+ import { jsonOutput } from "./code-result";
4
+ import { errorMessage, writeJson } from "../files";
5
+ import type { JudgeFunction, RunMetrics } from "../../judge-context";
6
+
7
+ type Job = RecordingInput & { module: string; output: string; scratch: string };
8
+ const job: Job = JSON.parse(await Bun.stdin.text());
9
+ let metrics: RunMetrics | undefined;
10
+ async function grade() {
11
+ await using context = await openRecording(job, job.scratch);
12
+ metrics = context.metrics;
13
+ const module = await import(pathToFileURL(job.module).href);
14
+ if (typeof module.default !== "function")
15
+ throw new Error("judge.ts must default-export a function");
16
+ const output = jsonOutput(await (module.default as JudgeFunction)(context));
17
+ return { output, metrics: context.metrics };
18
+ }
19
+ try {
20
+ await writeJson(job.output, await grade());
21
+ process.exit(0);
22
+ } catch (error) {
23
+ await writeJson(job.output, { error: errorMessage(error), metrics });
24
+ console.error(error);
25
+ process.exit(1);
26
+ }
@@ -0,0 +1,97 @@
1
+ import { mkdir, rm } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import type {
5
+ CodeJudgeDefinition,
6
+ CodeJudgeExecution,
7
+ JsonValue,
8
+ RunMetrics,
9
+ } from "../../judge-context";
10
+ import type { RecordingInput } from "../recording";
11
+ import { codeJudgment, jsonOutput } from "./code-result";
12
+ import { errorMessage, writeJson } from "../files";
13
+
14
+ /** A separate process bounds synchronous loops as well as asynchronous judges. */
15
+ export async function executeCodeJudge(
16
+ code: CodeJudgeDefinition,
17
+ recording: RecordingInput,
18
+ directory: string,
19
+ timeoutMs: number,
20
+ ): Promise<CodeJudgeExecution> {
21
+ directory = resolve(directory);
22
+ await mkdir(directory, { recursive: true });
23
+ const module = resolve(directory, "judge.js"),
24
+ output = resolve(directory, "output.json");
25
+ const scratch = resolve(directory, "scratch");
26
+ const stdout = resolve(directory, "stdout.log"),
27
+ stderr = resolve(directory, "stderr.log");
28
+ await Bun.write(module, code.source);
29
+ await Bun.write(resolve(directory, "judge.js.map"), code.sourceMap);
30
+ await writeJson(resolve(directory, "dependencies.json"), code.dependencies);
31
+ const startedAt = new Date().toISOString();
32
+ let timer: ReturnType<typeof setTimeout> | undefined;
33
+ let timedOut = false;
34
+ let raw: JsonValue | undefined, metrics: RunMetrics | undefined;
35
+ const base = { startedAt, sourceHash: code.hash, stdout, stderr };
36
+ try {
37
+ const child = Bun.spawn(
38
+ [
39
+ process.execPath,
40
+ fileURLToPath(new URL("./code-worker.ts", import.meta.url)),
41
+ ],
42
+ {
43
+ cwd: directory,
44
+ stdin: "pipe",
45
+ stdout: Bun.file(stdout),
46
+ stderr: Bun.file(stderr),
47
+ windowsHide: true,
48
+ },
49
+ );
50
+ timer = setTimeout(() => {
51
+ timedOut = true;
52
+ child.kill();
53
+ }, timeoutMs);
54
+ await child.stdin.write(
55
+ JSON.stringify({ ...recording, module, output, scratch }),
56
+ );
57
+ child.stdin.end();
58
+ const exit = await child.exited;
59
+ if (timedOut)
60
+ throw new Error(`judge.ts exceeded its ${timeoutMs}ms time limit`);
61
+ const result = await Bun.file(output).json();
62
+ metrics = result.metrics;
63
+ if (exit !== 0 || result.error)
64
+ throw new Error(result.error ?? `judge.ts exited with code ${exit}`);
65
+ raw = jsonOutput(result.output);
66
+ const judgment = codeJudgment(raw);
67
+ return {
68
+ ...base,
69
+ state: "completed",
70
+ completedAt: new Date().toISOString(),
71
+ elapsedMs: Date.now() - Date.parse(startedAt),
72
+ output: raw,
73
+ metrics,
74
+ scores: Object.fromEntries(
75
+ Object.entries(judgment.scores).map(([id, score]) => [id, score.value]),
76
+ ),
77
+ };
78
+ } catch (error) {
79
+ return {
80
+ ...base,
81
+ state: timedOut ? "timed_out" : "failed",
82
+ completedAt: new Date().toISOString(),
83
+ elapsedMs: Date.now() - Date.parse(startedAt),
84
+ error: errorMessage(error),
85
+ output: raw,
86
+ metrics,
87
+ };
88
+ } finally {
89
+ clearTimeout(timer);
90
+ await rm(scratch, {
91
+ recursive: true,
92
+ force: true,
93
+ maxRetries: 5,
94
+ retryDelay: 100,
95
+ });
96
+ }
97
+ }