@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
@@ -0,0 +1,244 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { mkdir, mkdtemp, rm, symlink } from "node:fs/promises";
3
+ import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
4
+ import type { OpenCode } from "@opencode/sdk";
5
+ import type {
6
+ JudgeContext,
7
+ RecordedMessage,
8
+ Revision,
9
+ } from "../../judge-context";
10
+ import type { EvalRun, EvidenceRef } from "../../types";
11
+ import { CandidateEvidence } from "../evidence";
12
+ import { contained, hash } from "../files";
13
+ import { OPENCODE_VERSION } from "../opencode/version";
14
+ import { measureRecording } from "./metrics";
15
+
16
+ export type RecordingInput = {
17
+ evidence: EvidenceRef;
18
+ run?: EvalRun;
19
+ runDirectory?: string;
20
+ };
21
+
22
+ /** Own disposable native readers; the authoritative archive is only read. */
23
+ export async function openRecording(
24
+ input: RecordingInput,
25
+ workDirectory: string,
26
+ ): Promise<JudgeContext & AsyncDisposable> {
27
+ const reference = {
28
+ ...input.evidence,
29
+ directory: resolve(input.evidence.directory),
30
+ };
31
+ const evidence = await CandidateEvidence.open(
32
+ reference.directory,
33
+ reference.hash,
34
+ );
35
+ const events = evidence.rawEvents(),
36
+ tools = evidence.toolCalls(),
37
+ run = input.run ?? null;
38
+ const metrics = measureRecording(events, tools, reference);
39
+ const archive = run?.session;
40
+ const rootID = archive?.sessionId ?? null;
41
+ let temporary: Promise<string> | undefined,
42
+ database: Promise<Database> | undefined;
43
+ let sdk: Promise<OpenCode.Interface> | undefined;
44
+ const temp = () =>
45
+ (temporary ??= (async () => {
46
+ await mkdir(workDirectory, { recursive: true });
47
+ return mkdtemp(resolve(workDirectory, "recording-"));
48
+ })());
49
+ const copyDatabase = async (name: string) => {
50
+ if (!archive || !input.runDirectory)
51
+ throw new Error("This recording has no native OpenCode database");
52
+ if (archive.opencodeVersion !== OPENCODE_VERSION)
53
+ throw new Error(
54
+ `Native reader ${OPENCODE_VERSION} cannot open archive version ${archive.opencodeVersion}`,
55
+ );
56
+ const bytes = await Bun.file(
57
+ resolve(input.runDirectory, archive.database),
58
+ ).bytes();
59
+ if (hash(bytes) !== archive.databaseHash)
60
+ throw new Error("Native OpenCode database hash mismatch");
61
+ const path = resolve(await temp(), name);
62
+ await Bun.write(path, bytes);
63
+ return path;
64
+ };
65
+ const nativeSDK = () =>
66
+ (sdk ??= (async () => {
67
+ const path = await copyDatabase("sdk.db");
68
+ const { OpenCode } = await import("@opencode/sdk");
69
+ return OpenCode.create({
70
+ database: { path },
71
+ config: {
72
+ directory: resolve(dirname(path), "configuration"),
73
+ project: false,
74
+ content: JSON.stringify({ websearch: false }),
75
+ },
76
+ });
77
+ })());
78
+ const sessions = async () => {
79
+ const host = await nativeSDK();
80
+ const records = [];
81
+ let cursor: string | undefined;
82
+ do {
83
+ const page = await host.session.list({ limit: 100, cursor });
84
+ records.push(...page.data);
85
+ cursor = page.cursor.next ?? undefined;
86
+ } while (cursor);
87
+ return records;
88
+ };
89
+ const readText = async (path: string, revision: Revision = "final") =>
90
+ new TextDecoder("utf-8", { fatal: true }).decode(
91
+ await evidence.readFile(path, revision),
92
+ );
93
+ return {
94
+ response: { text: evidence.manifest.response?.text ?? "" },
95
+ prompt: evidence.manifest.prompt,
96
+ run: run ? structuredClone(run) : null,
97
+ metrics: structuredClone(metrics),
98
+ recording: {
99
+ evidence: reference,
100
+ sessionID: rootID,
101
+ coverage: evidence.manifest.coverage,
102
+ events: (filter = {}) =>
103
+ structuredClone(
104
+ events.filter(
105
+ ({ event }) =>
106
+ (!filter.type || event.type.startsWith(filter.type)) &&
107
+ (!filter.sessionID ||
108
+ ("data" in event &&
109
+ (event.data as { sessionID?: string }).sessionID ===
110
+ filter.sessionID)),
111
+ ),
112
+ ),
113
+ tools: (filter = {}) =>
114
+ structuredClone(
115
+ tools.filter(
116
+ (call) =>
117
+ (!filter.name || call.name === filter.name) &&
118
+ (!filter.sessionID || call.sessionID === filter.sessionID),
119
+ ),
120
+ ),
121
+ sessions,
122
+ async messages(sessionID) {
123
+ const host = await nativeSDK();
124
+ const ids = sessionID
125
+ ? [sessionID]
126
+ : (await sessions()).map((session) => session.id);
127
+ const records: RecordedMessage[] = [];
128
+ for (const id of ids) {
129
+ let cursor: string | undefined;
130
+ do {
131
+ const page = await host.message.list({
132
+ sessionID: id,
133
+ limit: 100,
134
+ ...(cursor ? { cursor } : { order: "asc" }),
135
+ });
136
+ records.push(
137
+ ...page.data.map((message) => ({ sessionID: id, message })),
138
+ );
139
+ cursor = page.cursor.next ?? undefined;
140
+ } while (cursor);
141
+ }
142
+ return records;
143
+ },
144
+ async export(sessionID = rootID ?? undefined) {
145
+ if (!sessionID) throw new Error("This recording has no native session");
146
+ return (await nativeSDK()).session.export({
147
+ sessionID,
148
+ sanitize: false,
149
+ });
150
+ },
151
+ },
152
+ workspace: {
153
+ files: (revision = "final") => evidence.files(revision),
154
+ read: (path, revision = "final") => evidence.readFile(path, revision),
155
+ text: readText,
156
+ async diff(path) {
157
+ const initial = evidence
158
+ .files("initial")
159
+ .some((file) => file.path === path);
160
+ const final = evidence
161
+ .files("final")
162
+ .some((file) => file.path === path);
163
+ return {
164
+ initial: initial ? await readText(path, "initial") : null,
165
+ final: final ? await readText(path, "final") : null,
166
+ };
167
+ },
168
+ async materialize(revision = "final") {
169
+ const directory = await mkdtemp(resolve(await temp(), "workspace-"));
170
+ const files = evidence.files(revision);
171
+ for (const file of files.filter((file) => file.symlink === undefined)) {
172
+ const path = contained(directory, file.path);
173
+ await mkdir(dirname(path), { recursive: true });
174
+ await Bun.write(path, await evidence.readFile(file.path, revision));
175
+ }
176
+ for (const file of files.filter((file) => file.symlink !== undefined)) {
177
+ const path = contained(directory, file.path),
178
+ link = file.symlink!;
179
+ const target = link.startsWith("/workspace/")
180
+ ? contained(directory, link.slice("/workspace/".length))
181
+ : isAbsolute(link)
182
+ ? ""
183
+ : resolve(dirname(path), link);
184
+ if (
185
+ !target ||
186
+ (target !== directory && !target.startsWith(directory + sep))
187
+ )
188
+ throw new Error(
189
+ `Recorded symlink leaves its workspace: ${file.path}`,
190
+ );
191
+ await mkdir(dirname(path), { recursive: true });
192
+ const isDirectory = files.some((item) =>
193
+ item.path.startsWith(
194
+ relative(directory, target).replaceAll("\\", "/") + "/",
195
+ ),
196
+ );
197
+ await symlink(
198
+ process.platform === "win32" && isDirectory
199
+ ? target
200
+ : relative(dirname(path), target),
201
+ path,
202
+ process.platform === "win32" && isDirectory
203
+ ? "junction"
204
+ : isDirectory
205
+ ? "dir"
206
+ : "file",
207
+ );
208
+ }
209
+ return directory;
210
+ },
211
+ },
212
+ native: {
213
+ version: archive?.opencodeVersion ?? null,
214
+ readerVersion: OPENCODE_VERSION,
215
+ database: () =>
216
+ (database ??= (async () => {
217
+ const db = new Database(await copyDatabase("readonly.db"), {
218
+ readonly: true,
219
+ });
220
+ db.exec("PRAGMA query_only=ON");
221
+ return db;
222
+ })()),
223
+ sdk: nativeSDK,
224
+ schema: () => import("@opencode/schema"),
225
+ },
226
+ async [Symbol.asyncDispose]() {
227
+ try {
228
+ if (sdk) await (await sdk).close();
229
+ } finally {
230
+ try {
231
+ if (database) (await database).close();
232
+ } finally {
233
+ if (temporary)
234
+ await rm(await temporary, {
235
+ recursive: true,
236
+ force: true,
237
+ maxRetries: 5,
238
+ retryDelay: 100,
239
+ });
240
+ }
241
+ }
242
+ },
243
+ };
244
+ }
@@ -0,0 +1,193 @@
1
+ import type { EvidenceRef, ToolCall } from "../../types";
2
+ import type {
3
+ RecordedEvent,
4
+ RunMetrics,
5
+ ToolMetrics,
6
+ } from "../../judge-context";
7
+ import { mergeRuntime, runtimeMs, type RuntimeInterval } from "../../runtime";
8
+
9
+ const toolsSummary = (tools: ToolCall[]): ToolMetrics => {
10
+ const succeeded = tools.filter((call) => call.status === "succeeded").length;
11
+ const failed = tools.filter((call) => call.status === "failed").length;
12
+ return {
13
+ calls: tools.length,
14
+ succeeded,
15
+ failed,
16
+ unfinished: tools.length - succeeded - failed,
17
+ errorRate: succeeded + failed ? failed / (succeeded + failed) : null,
18
+ };
19
+ };
20
+
21
+ /** Measurements use deduplicated native records, never scored criterion values. */
22
+ export function measureRecording(
23
+ supplied: RecordedEvent[],
24
+ tools: ToolCall[],
25
+ evidence: EvidenceRef,
26
+ ): RunMetrics {
27
+ const events = [
28
+ ...new Map(
29
+ supplied.map((item) => [
30
+ "id" in item.event ? item.event.id : String(item.sequence),
31
+ item.event,
32
+ ]),
33
+ ).values(),
34
+ ].sort(
35
+ (a, b) =>
36
+ ("created" in a ? a.created : 0) - ("created" in b ? b.created : 0),
37
+ );
38
+ const count = (type: string) =>
39
+ events.filter((event) => event.type === type).length;
40
+ const tokens = {
41
+ input: 0,
42
+ output: 0,
43
+ reasoning: 0,
44
+ cache: { read: 0, write: 0 },
45
+ };
46
+ let reportedUSD = 0,
47
+ usageRecords = 0,
48
+ usageComplete = true,
49
+ costComplete = true;
50
+ const intervals: RuntimeInterval[] = [],
51
+ starts = new Map<string, number>();
52
+ const executions: RuntimeInterval[] = [],
53
+ executionStarts = new Map<string, number>();
54
+ const created = new Set<string>();
55
+ const sessions = new Set<string>(),
56
+ sequences = new Map<string, number[]>();
57
+ for (const event of events) {
58
+ if (!("data" in event)) continue;
59
+ const data = event.data as Record<string, unknown>;
60
+ if (typeof data.sessionID === "string") sessions.add(data.sessionID);
61
+ if (event.type === "session.created") created.add(event.data.sessionID);
62
+ if (event.type === "session.execution.started")
63
+ executionStarts.set(event.data.sessionID, event.created);
64
+ if (
65
+ [
66
+ "session.execution.succeeded",
67
+ "session.execution.failed",
68
+ "session.execution.interrupted",
69
+ ].includes(event.type) &&
70
+ typeof data.sessionID === "string" &&
71
+ "created" in event
72
+ ) {
73
+ const start = executionStarts.get(data.sessionID);
74
+ if (start !== undefined) executions.push({ start, end: event.created });
75
+ executionStarts.delete(data.sessionID);
76
+ }
77
+ if ("durable" in event) {
78
+ const values = sequences.get(event.durable.aggregateID) ?? [];
79
+ values.push(event.durable.seq);
80
+ sequences.set(event.durable.aggregateID, values);
81
+ }
82
+ const key = `${data.sessionID}:${data.assistantMessageID ?? "compaction"}`;
83
+ if (
84
+ event.type === "session.step.started" ||
85
+ event.type === "session.compaction.started"
86
+ )
87
+ starts.set(key, event.created);
88
+ if (
89
+ [
90
+ "session.step.ended",
91
+ "session.step.failed",
92
+ "session.compaction.ended",
93
+ "session.compaction.failed",
94
+ ].includes(event.type)
95
+ ) {
96
+ const start = starts.get(key);
97
+ if (start !== undefined && "created" in event)
98
+ intervals.push({ start, end: event.created });
99
+ starts.delete(key);
100
+ }
101
+ if (
102
+ ![
103
+ "session.step.ended",
104
+ "session.step.failed",
105
+ "session.usage.recorded",
106
+ ].includes(event.type)
107
+ )
108
+ continue;
109
+ usageRecords++;
110
+ if (typeof data.cost === "number" && Number.isFinite(data.cost))
111
+ reportedUSD += data.cost;
112
+ else costComplete = false;
113
+ const usage = data.tokens as typeof tokens | undefined;
114
+ if (
115
+ !usage ||
116
+ ![
117
+ usage.input,
118
+ usage.output,
119
+ usage.reasoning,
120
+ usage.cache?.read,
121
+ usage.cache?.write,
122
+ ].every(Number.isFinite)
123
+ ) {
124
+ usageComplete = false;
125
+ continue;
126
+ }
127
+ tokens.input += usage.input;
128
+ tokens.output += usage.output;
129
+ tokens.reasoning += usage.reasoning;
130
+ tokens.cache.read += usage.cache.read;
131
+ tokens.cache.write += usage.cache.write;
132
+ }
133
+ const measuredTokens = usageRecords && usageComplete ? tokens : null;
134
+ const cost = usageRecords && costComplete ? reportedUSD : null;
135
+ const clock = events.reduce(
136
+ (latest, event) =>
137
+ "created" in event ? Math.max(latest, event.created) : latest,
138
+ 0,
139
+ );
140
+ const modelActiveMs = runtimeMs(mergeRuntime(intervals), clock);
141
+ const complete =
142
+ sequences.size > 0 &&
143
+ starts.size === 0 &&
144
+ executionStarts.size === 0 &&
145
+ [...sessions].every((id) => created.has(id)) &&
146
+ [...sequences.values()].every((values) => {
147
+ const sorted = [...new Set(values)].sort((a, b) => a - b);
148
+ return sorted.every(
149
+ (value, index) => index === 0 || value === sorted[index - 1] + 1,
150
+ );
151
+ });
152
+ return {
153
+ version: 1,
154
+ scope: "candidate",
155
+ complete,
156
+ sessions: sessions.size,
157
+ requests: {
158
+ started: count("session.step.started"),
159
+ succeeded: count("session.step.ended"),
160
+ failed: count("session.step.failed"),
161
+ retries: count("session.retry.scheduled"),
162
+ },
163
+ tokens: measuredTokens,
164
+ cost: { usd: cost, reportedUSD, complete: cost !== null },
165
+ tools: {
166
+ ...toolsSummary(tools),
167
+ byName: Object.fromEntries(
168
+ [...new Set(tools.map((call) => call.name))]
169
+ .sort()
170
+ .map((name) => [
171
+ name,
172
+ toolsSummary(tools.filter((call) => call.name === name)),
173
+ ]),
174
+ ),
175
+ },
176
+ compactions: {
177
+ started: count("session.compaction.started"),
178
+ completed: count("session.compaction.ended"),
179
+ failed: count("session.compaction.failed"),
180
+ },
181
+ timing: {
182
+ elapsedMs: executions.length
183
+ ? runtimeMs(mergeRuntime(executions), clock)
184
+ : null,
185
+ modelActiveMs,
186
+ outputTokensPerSecond:
187
+ measuredTokens && modelActiveMs > 0
188
+ ? (measuredTokens.output * 1000) / modelActiveMs
189
+ : null,
190
+ },
191
+ evidence,
192
+ };
193
+ }
@@ -5,7 +5,7 @@ import { mkdirSync, existsSync } from "node:fs";
5
5
  import { dirname, resolve } from "node:path";
6
6
  import { randomUUID } from "node:crypto";
7
7
  import * as tables from "./schema";
8
- import { isScored, JUDGE_PROTOCOL, metricMean } from "../../judgment";
8
+ import { isScored, JUDGE_PROTOCOL, criterionMean } from "../../judgment";
9
9
  import type {
10
10
  BenchmarkRun,
11
11
  EvalRun,
@@ -19,7 +19,7 @@ import type {
19
19
  } from "../../types";
20
20
 
21
21
  const APPLICATION_ID = 0x4f455632;
22
- const SCHEMA_VERSION = 4;
22
+ const SCHEMA_VERSION = 5;
23
23
  const timestamp = () => new Date().toISOString();
24
24
 
25
25
  /** One owner for run selection and append-only finalized execution records. */
@@ -257,13 +257,16 @@ export class Results {
257
257
  }
258
258
  startJudge(input: JudgeRunInput) {
259
259
  if (
260
- !input.agent ||
261
- !input.metrics.length ||
260
+ (!input.code &&
261
+ (!input.agent || !input.model || !input.criteria.length)) ||
262
+ (!!input.rubric &&
263
+ (!input.agent || !input.model || !input.criteria.length)) ||
264
+ !["llm", "code", "hybrid"].includes(input.kind) ||
262
265
  !input.runtimeHash ||
263
266
  input.protocol !== JUDGE_PROTOCOL
264
267
  )
265
268
  throw new Error(
266
- "Judge inputs must use the current agent, metrics, runtime, and protocol",
269
+ "Judge inputs must include their code or LLM configuration, criteria, runtime, and protocol",
267
270
  );
268
271
  const value: JudgeRun = {
269
272
  id: `judge_${randomUUID()}`,
@@ -301,21 +304,28 @@ export class Results {
301
304
  const saved = this.judgeRun(value.id);
302
305
  if (!saved || saved.state !== "running")
303
306
  throw new Error("Judge run is not running");
304
- if (
305
- value.state === "completed" &&
306
- (!value.judgment ||
307
- !Array.isArray(value.judgment.metrics) ||
308
- value.judgment.metrics.length !== value.input.metrics.length ||
309
- new Set(value.judgment.metrics.map((metric) => metric.id)).size !==
310
- value.input.metrics.length ||
311
- value.judgment.metrics.some(
312
- (metric) =>
313
- !value.input.metrics.some((declared) => declared.id === metric.id),
307
+ if (value.state === "completed") {
308
+ const expected = [
309
+ ...value.input.criteria.map((criterion) => criterion.id),
310
+ ...Object.keys(value.code?.scores ?? {}),
311
+ ];
312
+ const scores = value.judgment?.scores;
313
+ if (
314
+ !scores ||
315
+ Array.isArray(scores) ||
316
+ new Set(expected).size !== expected.length ||
317
+ Object.keys(scores).length !== expected.length ||
318
+ expected.some((id) => !Object.hasOwn(scores, id)) ||
319
+ Object.values(scores).some(
320
+ (score) => score.value !== null && !isScored(score.value),
314
321
  ) ||
315
- value.judgment.value !== metricMean(value.judgment.metrics) ||
316
- (value.judgment.value !== null && !isScored(value.judgment.value)))
317
- )
318
- throw new Error("A completed judge run requires a valid judgment");
322
+ value.judgment!.value !== criterionMean(scores) ||
323
+ (value.input.code && value.code?.state !== "completed")
324
+ )
325
+ throw new Error(
326
+ "A completed judge run requires a valid, complete judgment",
327
+ );
328
+ }
319
329
  if (
320
330
  value.state === "completed" &&
321
331
  this.evalRun(value.input.evalRunId)?.evidence?.hash !==
@@ -0,0 +1,141 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import type { OpenCode } from "@opencode/sdk";
3
+ import type {
4
+ SessionInfo,
5
+ SessionMessageInfo,
6
+ SessionTransferData,
7
+ } from "@opencode/client";
8
+ import type {
9
+ EvalRun,
10
+ EvidenceRef,
11
+ OpenCodeStreamEvent,
12
+ ToolCall,
13
+ Cost,
14
+ Accounting,
15
+ } from "./types";
16
+
17
+ export type JsonValue =
18
+ | null
19
+ | boolean
20
+ | number
21
+ | string
22
+ | JsonValue[]
23
+ | { [key: string]: JsonValue };
24
+ export type ScoreValue = boolean | number | null;
25
+
26
+ /** Code judges are ordinary functions. Only an optional scores map has grading semantics. */
27
+ export type JudgeFunction = (
28
+ context: JudgeContext,
29
+ ) => JsonValue | Promise<JsonValue>;
30
+
31
+ export type ToolMetrics = {
32
+ calls: number;
33
+ succeeded: number;
34
+ failed: number;
35
+ unfinished: number;
36
+ /** Failed / (succeeded + failed); null when there are no terminal calls. */
37
+ errorRate: number | null;
38
+ };
39
+ export type RunMetrics = {
40
+ version: 1;
41
+ scope: "candidate";
42
+ /** False for constructed controls or incomplete durable execution history. */
43
+ complete: boolean;
44
+ sessions: number;
45
+ requests: {
46
+ started: number;
47
+ succeeded: number;
48
+ failed: number;
49
+ retries: number;
50
+ };
51
+ tokens: Accounting["tokens"] | null;
52
+ cost: Cost;
53
+ tools: ToolMetrics & { byName: Record<string, ToolMetrics> };
54
+ compactions: { started: number; completed: number; failed: number };
55
+ timing: {
56
+ elapsedMs: number | null;
57
+ modelActiveMs: number;
58
+ /** Output tokens / model-active seconds; overlapping requests count once. */
59
+ outputTokensPerSecond: number | null;
60
+ };
61
+ evidence: EvidenceRef;
62
+ };
63
+
64
+ export type RecordedEvent = {
65
+ sequence: number;
66
+ time: string;
67
+ event: OpenCodeStreamEvent;
68
+ };
69
+ export type RecordedMessage = {
70
+ sessionID: string;
71
+ message: SessionMessageInfo;
72
+ };
73
+ export type RecordedFile = {
74
+ path: string;
75
+ bytes: number;
76
+ sha256?: string;
77
+ symlink?: string;
78
+ };
79
+ export type Revision = "initial" | "final";
80
+
81
+ export interface JudgeContext {
82
+ /** Missing response text is normalized to an empty string. */
83
+ readonly response: { readonly text: string };
84
+ readonly prompt: string;
85
+ /** Null for constructed evidence that did not execute a candidate. */
86
+ readonly run: Readonly<EvalRun> | null;
87
+ readonly metrics: Readonly<RunMetrics>;
88
+ readonly recording: {
89
+ readonly evidence: EvidenceRef;
90
+ readonly sessionID: string | null;
91
+ readonly coverage: "recorded-session" | "recorded-prefix";
92
+ events(filter?: { sessionID?: string; type?: string }): RecordedEvent[];
93
+ tools(filter?: { sessionID?: string; name?: string }): ToolCall[];
94
+ sessions(): Promise<SessionInfo[]>;
95
+ /** Full projected history, including messages before compaction. */
96
+ messages(sessionID?: string): Promise<RecordedMessage[]>;
97
+ export(sessionID?: string): Promise<SessionTransferData>;
98
+ };
99
+ readonly workspace: {
100
+ files(revision?: Revision): RecordedFile[];
101
+ read(path: string, revision?: Revision): Promise<Uint8Array>;
102
+ text(path: string, revision?: Revision): Promise<string>;
103
+ diff(
104
+ path: string,
105
+ ): Promise<{ initial: string | null; final: string | null }>;
106
+ /** Restore a disposable snapshot for author-owned verification commands. */
107
+ materialize(revision?: Revision): Promise<string>;
108
+ };
109
+ readonly native: {
110
+ readonly version: string | null;
111
+ readonly readerVersion: string;
112
+ /** Read-only SQL access to a verified, disposable native database copy. */
113
+ database(): Promise<Database>;
114
+ /** The pinned OpenCode API/SDK over a separate disposable database copy. */
115
+ sdk(): Promise<OpenCode.Interface>;
116
+ schema(): Promise<typeof import("@opencode/schema")>;
117
+ };
118
+ }
119
+
120
+ /** Frozen judging source and dependency identity, kept in host-side run records. */
121
+ export type CodeJudgeDefinition = {
122
+ file: string;
123
+ hash: string;
124
+ source: string;
125
+ sourceMap: string;
126
+ dependencies: Record<string, string>;
127
+ };
128
+ export type CodeJudgeExecution = {
129
+ state: "completed" | "failed" | "timed_out";
130
+ startedAt: string;
131
+ completedAt: string;
132
+ elapsedMs: number;
133
+ /** Original JSON, before boolean score normalization. */
134
+ output?: JsonValue;
135
+ scores?: Record<string, number | null>;
136
+ metrics?: RunMetrics;
137
+ error?: string;
138
+ sourceHash: string;
139
+ stdout: string;
140
+ stderr: string;
141
+ };