@tangle-network/agent-eval 0.150.2 → 0.161.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 (342) hide show
  1. package/CHANGELOG.md +158 -1
  2. package/README.md +7 -3
  3. package/dist/{active-curriculum-C4mk67HP.js → active-curriculum-CD5TU2yW.js} +3 -13
  4. package/dist/active-curriculum-CD5TU2yW.js.map +1 -0
  5. package/dist/{agent-profile-cell-BkcRDikH.d.ts → agent-profile-cell-CTOZJUuE.d.ts} +4 -2
  6. package/dist/agent-profile-cell-CTOZJUuE.d.ts.map +1 -0
  7. package/dist/analyst/index.d.ts +19 -36
  8. package/dist/analyst/index.d.ts.map +1 -1
  9. package/dist/analyst/index.js +8 -8
  10. package/dist/analyst/index.js.map +1 -1
  11. package/dist/{backend-integrity-DOCa_QrR.d.ts → backend-integrity-DxuQCu_A.d.ts} +4 -3
  12. package/dist/backend-integrity-DxuQCu_A.d.ts.map +1 -0
  13. package/dist/{benchmark-BtAWA8nT.d.ts → benchmark-CGPp-kDC.d.ts} +3 -3
  14. package/dist/{benchmark-BtAWA8nT.d.ts.map → benchmark-CGPp-kDC.d.ts.map} +1 -1
  15. package/dist/{benchmark-command-BU1Las59.js → benchmark-command-BDC3Gocz.js} +26 -31
  16. package/dist/benchmark-command-BDC3Gocz.js.map +1 -0
  17. package/dist/benchmarks/index.d.ts +6 -19
  18. package/dist/benchmarks/index.d.ts.map +1 -1
  19. package/dist/benchmarks/index.js +4 -4
  20. package/dist/benchmarks/index.js.map +1 -1
  21. package/dist/builder-eval/index.d.ts +3 -3
  22. package/dist/builder-eval/index.js +2 -2
  23. package/dist/campaign/index.d.ts +9 -9
  24. package/dist/campaign/index.js +7 -7
  25. package/dist/{campaign-la-gEYNz.js → campaign-BSmOwskD.js} +77 -795
  26. package/dist/campaign-BSmOwskD.js.map +1 -0
  27. package/dist/{canonical-D-XsTQ6_.js → canonical-IL-Bu-14.js} +26 -2
  28. package/dist/canonical-IL-Bu-14.js.map +1 -0
  29. package/dist/{capture-fetch-BBVFzhkk.d.ts → capture-fetch-CqwsJkkG.d.ts} +3 -3
  30. package/dist/{capture-fetch-BBVFzhkk.d.ts.map → capture-fetch-CqwsJkkG.d.ts.map} +1 -1
  31. package/dist/{chat-client-Bvmxedyv.js → chat-client-DlMlAeYI.js} +5 -55
  32. package/dist/{chat-client-Bvmxedyv.js.map → chat-client-DlMlAeYI.js.map} +1 -1
  33. package/dist/chat-json-call-6g5sJobJ.js +53 -0
  34. package/dist/chat-json-call-6g5sJobJ.js.map +1 -0
  35. package/dist/cli.js +54 -19
  36. package/dist/cli.js.map +1 -1
  37. package/dist/{client-LIuo-KPv.js → client-CX7KqIdB.js} +3 -3
  38. package/dist/client-CX7KqIdB.js.map +1 -0
  39. package/dist/{client-kPQYT_56.d.ts → client-L9VVPkim.d.ts} +4 -4
  40. package/dist/{client-kPQYT_56.d.ts.map → client-L9VVPkim.d.ts.map} +1 -1
  41. package/dist/contract/index.d.ts +13 -27
  42. package/dist/contract/index.d.ts.map +1 -1
  43. package/dist/contract/index.js +14 -17
  44. package/dist/contract/index.js.map +1 -1
  45. package/dist/{counterfactual--bpysZF0.d.ts → counterfactual-BaFUWK3H.d.ts} +4 -4
  46. package/dist/{counterfactual--bpysZF0.d.ts.map → counterfactual-BaFUWK3H.d.ts.map} +1 -1
  47. package/dist/{counterfactual-lDfCx0Uz.js → counterfactual-D_VWavVm.js} +2 -2
  48. package/dist/{counterfactual-lDfCx0Uz.js.map → counterfactual-D_VWavVm.js.map} +1 -1
  49. package/dist/{dataset-CJjKqQfA.d.ts → dataset-DQqhOCPt.d.ts} +5 -4
  50. package/dist/{dataset-CJjKqQfA.d.ts.map → dataset-DQqhOCPt.d.ts.map} +1 -1
  51. package/dist/{default-registry-Cw0Ohdoj.d.ts → default-registry-G9CKMNkc.d.ts} +7 -8
  52. package/dist/{default-registry-Cw0Ohdoj.d.ts.map → default-registry-G9CKMNkc.d.ts.map} +1 -1
  53. package/dist/{define-agent-eval-CEQWL9Hy.d.ts → define-agent-eval-Dx1JnPEa.d.ts} +26 -6
  54. package/dist/define-agent-eval-Dx1JnPEa.d.ts.map +1 -0
  55. package/dist/{define-agent-eval-C8V8sMqP.js → define-agent-eval-h-s-sI-v.js} +15 -9
  56. package/dist/define-agent-eval-h-s-sI-v.js.map +1 -0
  57. package/dist/{dspy-rlm-engine-CBYlPvNy.js → dspy-rlm-engine-DptEII26.js} +95 -15
  58. package/dist/dspy-rlm-engine-DptEII26.js.map +1 -0
  59. package/dist/{emitter-CPBAhxum.js → emitter-BpYFQPj4.js} +2 -18
  60. package/dist/emitter-BpYFQPj4.js.map +1 -0
  61. package/dist/{emitter-DGQGoLyj.d.ts → emitter-D_jYSGRd.d.ts} +4 -20
  62. package/dist/{emitter-DGQGoLyj.d.ts.map → emitter-D_jYSGRd.d.ts.map} +1 -1
  63. package/dist/{engine-BLzhNzoY.d.ts → engine-Cu5qD5Fc.d.ts} +9 -11
  64. package/dist/{engine-BLzhNzoY.d.ts.map → engine-Cu5qD5Fc.d.ts.map} +1 -1
  65. package/dist/{eval-campaign-CQuZrLR_.js → eval-campaign-BsXWL2-2.js} +17 -28
  66. package/dist/eval-campaign-BsXWL2-2.js.map +1 -0
  67. package/dist/{exact-types-ccQAyut1.d.ts → exact-types-qnexxJ1Z.d.ts} +2 -2
  68. package/dist/{exact-types-ccQAyut1.d.ts.map → exact-types-qnexxJ1Z.d.ts.map} +1 -1
  69. package/dist/{exec-y-DCLqK7.js → exec-D9WpA2p-.js} +2 -2
  70. package/dist/exec-D9WpA2p-.js.map +1 -0
  71. package/dist/experiment/index.d.ts +45 -11
  72. package/dist/experiment/index.d.ts.map +1 -1
  73. package/dist/experiment/index.js +30 -11
  74. package/dist/experiment/index.js.map +1 -1
  75. package/dist/{experiment-tracker-0MhuPArU.d.ts → experiment-tracker-DCO6Cz4s.d.ts} +2 -2
  76. package/dist/{experiment-tracker-0MhuPArU.d.ts.map → experiment-tracker-DCO6Cz4s.d.ts.map} +1 -1
  77. package/dist/{exporters-q9iL-2Jf.js → exporters-Df7TgHFv.js} +3 -3
  78. package/dist/exporters-Df7TgHFv.js.map +1 -0
  79. package/dist/{external-optimizer-contracts-DbLsm4Po.d.ts → external-optimizer-contracts-szBJ_1vh.d.ts} +2 -2
  80. package/dist/{external-optimizer-contracts-DbLsm4Po.d.ts.map → external-optimizer-contracts-szBJ_1vh.d.ts.map} +1 -1
  81. package/dist/{external-optimizer-process-x9oEXKsU.js → external-optimizer-process-WosTBChy.js} +4 -4
  82. package/dist/{external-optimizer-process-x9oEXKsU.js.map → external-optimizer-process-WosTBChy.js.map} +1 -1
  83. package/dist/{external-optimizer-subprocess-CKNb42oM.js → external-optimizer-subprocess-BIWbHpgD.js} +4 -4
  84. package/dist/{external-optimizer-subprocess-CKNb42oM.js.map → external-optimizer-subprocess-BIWbHpgD.js.map} +1 -1
  85. package/dist/{failure-cluster-BLURuWG4.d.ts → failure-cluster-CXL8NbEw.d.ts} +3 -3
  86. package/dist/{failure-cluster-BLURuWG4.d.ts.map → failure-cluster-CXL8NbEw.d.ts.map} +1 -1
  87. package/dist/{feedback-trajectory-DpTTjo0q.d.ts → feedback-trajectory-B3ZHaHV_.d.ts} +7 -7
  88. package/dist/{feedback-trajectory-DpTTjo0q.d.ts.map → feedback-trajectory-B3ZHaHV_.d.ts.map} +1 -1
  89. package/dist/fuzz.js +1 -1
  90. package/dist/{hf-dataset-XggBupCr.js → hf-dataset-D8_RNIis.js} +4 -4
  91. package/dist/{hf-dataset-XggBupCr.js.map → hf-dataset-D8_RNIis.js.map} +1 -1
  92. package/dist/hosted/index.d.ts +3 -14
  93. package/dist/hosted/index.d.ts.map +1 -1
  94. package/dist/hosted/index.js +2 -2
  95. package/dist/{index-Aj3WO3_a.d.ts → index-CGtH1piv.d.ts} +48 -26
  96. package/dist/index-CGtH1piv.d.ts.map +1 -0
  97. package/dist/{index-BNPtkBPf.d.ts → index-D-IiQIBB.d.ts} +5 -10
  98. package/dist/index-D-IiQIBB.d.ts.map +1 -0
  99. package/dist/{index-IQccV3Ou.d.ts → index-D-V8gCs_.d.ts} +13 -90
  100. package/dist/index-D-V8gCs_.d.ts.map +1 -0
  101. package/dist/{index-B8Ui1mr1.d.ts → index-lfaSeKSD.d.ts} +18 -2
  102. package/dist/index-lfaSeKSD.d.ts.map +1 -0
  103. package/dist/index-vrJugRal.d.ts +1 -0
  104. package/dist/index.d.ts +68 -56
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +59 -110
  107. package/dist/index.js.map +1 -1
  108. package/dist/{insight-report-BeT8KCgI.d.ts → insight-report-DRe8LB6d.d.ts} +4 -4
  109. package/dist/{insight-report-BeT8KCgI.d.ts.map → insight-report-DRe8LB6d.d.ts.map} +1 -1
  110. package/dist/{integrity-DL91tucI.js → integrity-CyWSSoQS.js} +2 -2
  111. package/dist/{integrity-DL91tucI.js.map → integrity-CyWSSoQS.js.map} +1 -1
  112. package/dist/{integrity-B0dZ96EO.d.ts → integrity-DUNX9Fao.d.ts} +3 -3
  113. package/dist/{integrity-B0dZ96EO.d.ts.map → integrity-DUNX9Fao.d.ts.map} +1 -1
  114. package/dist/{kind-factory-DmAa0h3K.js → kind-factory-DY8FdoXf.js} +3 -71
  115. package/dist/kind-factory-DY8FdoXf.js.map +1 -0
  116. package/dist/ledger-core/index.d.ts +2 -2
  117. package/dist/ledger-core/index.js +3 -3
  118. package/dist/{ledger-core-DTae9rv_.js → ledger-core-BOzlRygb.js} +2 -2
  119. package/dist/{ledger-core-DTae9rv_.js.map → ledger-core-BOzlRygb.js.map} +1 -1
  120. package/dist/{llm-client-Bg32RW0j.js → llm-client-hgDieDNN.js} +53 -98
  121. package/dist/llm-client-hgDieDNN.js.map +1 -0
  122. package/dist/{llm-judge-BqqMS8t7.js → llm-judge-BhasIPFT.js} +1135 -77
  123. package/dist/llm-judge-BhasIPFT.js.map +1 -0
  124. package/dist/{matrix-DrVnRp4G.d.ts → matrix-eXKRMHnL.d.ts} +74 -72
  125. package/dist/matrix-eXKRMHnL.d.ts.map +1 -0
  126. package/dist/meta-eval/index.d.ts +8 -6
  127. package/dist/meta-eval/index.d.ts.map +1 -1
  128. package/dist/meta-eval/index.js +8 -6
  129. package/dist/meta-eval/index.js.map +1 -1
  130. package/dist/{mint-BV6tLVWl.js → mint-DfODW1KW.js} +3 -3
  131. package/dist/{mint-BV6tLVWl.js.map → mint-DfODW1KW.js.map} +1 -1
  132. package/dist/multishot/golden/index.d.ts +2 -8
  133. package/dist/multishot/golden/index.d.ts.map +1 -1
  134. package/dist/multishot/golden/index.js +56 -86
  135. package/dist/multishot/golden/index.js.map +1 -1
  136. package/dist/multishot/index.d.ts +11 -46
  137. package/dist/multishot/index.d.ts.map +1 -1
  138. package/dist/multishot/index.js +30 -83
  139. package/dist/multishot/index.js.map +1 -1
  140. package/dist/openapi.json +1 -1
  141. package/dist/{opencode-sqlite-DJWAXLms.js → opencode-sqlite-eK6HW6dr.js} +2 -6
  142. package/dist/{opencode-sqlite-DJWAXLms.js.map → opencode-sqlite-eK6HW6dr.js.map} +1 -1
  143. package/dist/pipelines/index.d.ts +5 -5
  144. package/dist/pipelines/index.js +3 -3
  145. package/dist/{pre-registration-zFSLEiFU.d.ts → pre-registration-CzFCcwYk.d.ts} +55 -40
  146. package/dist/pre-registration-CzFCcwYk.d.ts.map +1 -0
  147. package/dist/pre-registration-KN9jkh58.js +110 -0
  148. package/dist/pre-registration-KN9jkh58.js.map +1 -0
  149. package/dist/{produced-state-Bnq4FaDO.js → produced-state-DZ89riy5.js} +8 -8
  150. package/dist/produced-state-DZ89riy5.js.map +1 -0
  151. package/dist/profile-cell.d.ts +1 -1
  152. package/dist/profile-cell.js +31 -5
  153. package/dist/profile-cell.js.map +1 -1
  154. package/dist/{promotion-policy-DLOUkYhI.d.ts → promotion-policy-DtnOIZvk.d.ts} +2 -2
  155. package/dist/{promotion-policy-DLOUkYhI.d.ts.map → promotion-policy-DtnOIZvk.d.ts.map} +1 -1
  156. package/dist/{query-Di7eEQ79.js → query-CHmMP42p.js} +20 -11
  157. package/dist/query-CHmMP42p.js.map +1 -0
  158. package/dist/{query-CJ_DX8vl.d.ts → query-DxPYqpmT.d.ts} +10 -4
  159. package/dist/query-DxPYqpmT.d.ts.map +1 -0
  160. package/dist/raw-provider-sink-BU29Sh8h.d.ts +134 -0
  161. package/dist/raw-provider-sink-BU29Sh8h.d.ts.map +1 -0
  162. package/dist/{registry-BQwrSYpC.d.ts → registry-8You7OK1.d.ts} +5 -7
  163. package/dist/{registry-BQwrSYpC.d.ts.map → registry-8You7OK1.d.ts.map} +1 -1
  164. package/dist/{release-confidence-BknrpBnO.js → release-confidence-DKfD2RYU.js} +28 -14
  165. package/dist/release-confidence-DKfD2RYU.js.map +1 -0
  166. package/dist/{release-confidence-4XrqlpFD.d.ts → release-confidence-Dqt0NFep.d.ts} +7 -6
  167. package/dist/release-confidence-Dqt0NFep.d.ts.map +1 -0
  168. package/dist/reporting.d.ts +3 -3
  169. package/dist/reporting.js +3 -3
  170. package/dist/{researcher-DJnoUE8c.d.ts → researcher-Cz565b7D.d.ts} +34 -21
  171. package/dist/researcher-Cz565b7D.d.ts.map +1 -0
  172. package/dist/{reward-hacking-62tojkQd.d.ts → reward-hacking-MBf7qpSB.d.ts} +2 -2
  173. package/dist/{reward-hacking-62tojkQd.d.ts.map → reward-hacking-MBf7qpSB.d.ts.map} +1 -1
  174. package/dist/{reward-hacking-C0x0xihA.js → reward-hacking-t4lB1yt8.js} +2 -2
  175. package/dist/{reward-hacking-C0x0xihA.js.map → reward-hacking-t4lB1yt8.js.map} +1 -1
  176. package/dist/rl.d.ts +11 -42
  177. package/dist/rl.d.ts.map +1 -1
  178. package/dist/rl.js +41 -24
  179. package/dist/rl.js.map +1 -1
  180. package/dist/rollout/index.d.ts +3 -3
  181. package/dist/rollout/index.js +7 -7
  182. package/dist/{rollout-ytVQ7WT8.js → rollout-Dm2tSdiQ.js} +6 -6
  183. package/dist/{rollout-ytVQ7WT8.js.map → rollout-Dm2tSdiQ.js.map} +1 -1
  184. package/dist/{rubric-predictive-validity-CzxLoZge.js → rubric-predictive-validity-CK8SCOg-.js} +5 -15
  185. package/dist/rubric-predictive-validity-CK8SCOg-.js.map +1 -0
  186. package/dist/{rubric-predictive-validity-C7LnNvF2.d.ts → rubric-predictive-validity-CxycqzX5.d.ts} +4 -3
  187. package/dist/rubric-predictive-validity-CxycqzX5.d.ts.map +1 -0
  188. package/dist/{run-record-D2lDdSAz.js → run-record-BC0ebuRP.js} +2 -2
  189. package/dist/{run-record-D2lDdSAz.js.map → run-record-BC0ebuRP.js.map} +1 -1
  190. package/dist/{run-record-DVV82Gwh.d.ts → run-record-VVy4T9OW.d.ts} +3 -3
  191. package/dist/{run-record-DVV82Gwh.d.ts.map → run-record-VVy4T9OW.d.ts.map} +1 -1
  192. package/dist/{schema-BtVldJ3T.d.ts → schema-Bjgdsn73.d.ts} +2 -4
  193. package/dist/{schema-BtVldJ3T.d.ts.map → schema-Bjgdsn73.d.ts.map} +1 -1
  194. package/dist/{schema-Cef2cFmb.d.ts → schema-BzWDXhOR.d.ts} +2 -5
  195. package/dist/schema-BzWDXhOR.d.ts.map +1 -0
  196. package/dist/{schema-C6DW4ZHR.js → schema-C1aaAxTf.js} +2 -2
  197. package/dist/schema-C1aaAxTf.js.map +1 -0
  198. package/dist/{schema-CRhEY1SO.js → schema-k6ZBftVv.js} +2 -8
  199. package/dist/{schema-CRhEY1SO.js.map → schema-k6ZBftVv.js.map} +1 -1
  200. package/dist/{semantic-concept-judge-laMCnTLn.js → semantic-concept-judge-BSkKKHeq.js} +14 -38
  201. package/dist/semantic-concept-judge-BSkKKHeq.js.map +1 -0
  202. package/dist/{sequential-eprocess-CbUt2htw.js → sequential-eprocess-D1jKoihe.js} +49 -2
  203. package/dist/sequential-eprocess-D1jKoihe.js.map +1 -0
  204. package/dist/{sequential-C458DXNf.js → sequential-rYW-Ophm.js} +41 -16
  205. package/dist/sequential-rYW-Ophm.js.map +1 -0
  206. package/dist/{series-convergence-BxKEgBwA.d.ts → series-convergence-D9WgpXGi.d.ts} +2 -2
  207. package/dist/{series-convergence-BxKEgBwA.d.ts.map → series-convergence-D9WgpXGi.d.ts.map} +1 -1
  208. package/dist/{server-dIWwF3j_.js → server-BtFd4uzB.js} +19 -42
  209. package/dist/server-BtFd4uzB.js.map +1 -0
  210. package/dist/{skillopt-optimization-method-CPBlTcj5.js → skillopt-optimization-method-DbaekMcn.js} +794 -8
  211. package/dist/skillopt-optimization-method-DbaekMcn.js.map +1 -0
  212. package/dist/{skillopt-optimization-method-BDD_o1xE.d.ts → skillopt-optimization-method-x7TTF23P.d.ts} +20 -7
  213. package/dist/{skillopt-optimization-method-BDD_o1xE.d.ts.map → skillopt-optimization-method-x7TTF23P.d.ts.map} +1 -1
  214. package/dist/{statistical-heldout-_woZ9q9j.d.ts → statistical-heldout-Cy3EhjlC.d.ts} +21 -8
  215. package/dist/statistical-heldout-Cy3EhjlC.d.ts.map +1 -0
  216. package/dist/{steps-AmkT-GIM.d.ts → steps-CiNVJry_.d.ts} +2 -17
  217. package/dist/steps-CiNVJry_.d.ts.map +1 -0
  218. package/dist/{store-CT9YIIve.d.ts → store-B06JdC56.d.ts} +2 -2
  219. package/dist/{store-CT9YIIve.d.ts.map → store-B06JdC56.d.ts.map} +1 -1
  220. package/dist/{store-otlp-CDYWW_8N.js → store-otlp-C_Rq5I4D.js} +2 -2
  221. package/dist/{store-otlp-CDYWW_8N.js.map → store-otlp-C_Rq5I4D.js.map} +1 -1
  222. package/dist/{store-tool-spans-Br2_IUhm.d.ts → store-tool-spans-DPUG7UUY.d.ts} +6 -6
  223. package/dist/{store-tool-spans-Br2_IUhm.d.ts.map → store-tool-spans-DPUG7UUY.d.ts.map} +1 -1
  224. package/dist/{store-tool-spans-CykkbOlv.js → store-tool-spans-Dlh9vkFK.js} +3 -3
  225. package/dist/{store-tool-spans-CykkbOlv.js.map → store-tool-spans-Dlh9vkFK.js.map} +1 -1
  226. package/dist/storyboard/index.d.ts +1 -1
  227. package/dist/{summary-report-DW2bEpdB.js → summary-report-BI5hUtvK.js} +6 -16
  228. package/dist/summary-report-BI5hUtvK.js.map +1 -0
  229. package/dist/{summary-report-B__Y5ub3.d.ts → summary-report-CC07PhEL.d.ts} +6 -5
  230. package/dist/summary-report-CC07PhEL.d.ts.map +1 -0
  231. package/dist/supervisor-run/index.d.ts +3 -16
  232. package/dist/supervisor-run/index.d.ts.map +1 -1
  233. package/dist/supervisor-run/index.js +3 -3
  234. package/dist/supervisor-run/index.js.map +1 -1
  235. package/dist/{task-failure-attributes--ZTP3tYO.js → task-failure-attributes-DTl-7-Kw.js} +3 -3
  236. package/dist/{task-failure-attributes--ZTP3tYO.js.map → task-failure-attributes-DTl-7-Kw.js.map} +1 -1
  237. package/dist/{tool-groups-B4tqh8jB.d.ts → tool-groups-Ci8i9ErB.d.ts} +3 -3
  238. package/dist/tool-groups-Ci8i9ErB.d.ts.map +1 -0
  239. package/dist/{tool-waste-C-VHSRwF.js → tool-waste-BqzmVdJk.js} +2 -2
  240. package/dist/{tool-waste-C-VHSRwF.js.map → tool-waste-BqzmVdJk.js.map} +1 -1
  241. package/dist/{tool-waste-DjRDEsuI.d.ts → tool-waste-Dro0gJi3.d.ts} +4 -4
  242. package/dist/{tool-waste-DjRDEsuI.d.ts.map → tool-waste-Dro0gJi3.d.ts.map} +1 -1
  243. package/dist/trace-repair/index.d.ts +4 -77
  244. package/dist/trace-repair/index.d.ts.map +1 -1
  245. package/dist/trace-repair/index.js +5 -15
  246. package/dist/trace-repair/index.js.map +1 -1
  247. package/dist/traces.d.ts +13 -23
  248. package/dist/traces.d.ts.map +1 -1
  249. package/dist/traces.js +9 -20
  250. package/dist/traces.js.map +1 -1
  251. package/dist/{trajectory-YC15QDYQ.d.ts → trajectory-Bi157Gun.d.ts} +3 -3
  252. package/dist/{trajectory-YC15QDYQ.d.ts.map → trajectory-Bi157Gun.d.ts.map} +1 -1
  253. package/dist/trajectory-replay/index.d.ts +5 -5
  254. package/dist/trajectory-replay/index.js +5 -5
  255. package/dist/{provenance-oA4-zUqm.d.ts → transient-failure-DKF5Mofa.d.ts} +468 -13
  256. package/dist/transient-failure-DKF5Mofa.d.ts.map +1 -0
  257. package/dist/types-B3jzCp0p.js.map +1 -1
  258. package/dist/{types-CLAwnY-L.d.ts → types-BPb2Kf_C2.d.ts} +3 -3
  259. package/dist/types-BPb2Kf_C2.d.ts.map +1 -0
  260. package/dist/types-Bfk0uxRj.d.ts +443 -0
  261. package/dist/types-Bfk0uxRj.d.ts.map +1 -0
  262. package/dist/{types-DdFNuyxQ.d.ts → types-D4s7Z6nq.d.ts} +30 -6
  263. package/dist/types-D4s7Z6nq.d.ts.map +1 -0
  264. package/dist/{types-B2NsbrNy.d.ts → types-D9ssmxKL.d.ts} +3 -3
  265. package/dist/{types-B2NsbrNy.d.ts.map → types-D9ssmxKL.d.ts.map} +1 -1
  266. package/dist/{types-I5WwVzQ7.d.ts → types-DeIUdzNd.d.ts} +2 -2
  267. package/dist/{types-I5WwVzQ7.d.ts.map → types-DeIUdzNd.d.ts.map} +1 -1
  268. package/dist/{verdict-BndeTAh_.js → verdict-B0xltqu6.js} +2 -2
  269. package/dist/{verdict-BndeTAh_.js.map → verdict-B0xltqu6.js.map} +1 -1
  270. package/dist/verdict-cache-CdVVTVmn.js +88 -0
  271. package/dist/verdict-cache-CdVVTVmn.js.map +1 -0
  272. package/dist/wire/index.d.ts +21 -111
  273. package/dist/wire/index.d.ts.map +1 -1
  274. package/dist/wire/index.js +2 -2
  275. package/docs/building-doctrine.md +3 -3
  276. package/docs/campaign-proposers.md +41 -10
  277. package/docs/design/statistics-decisions.md +89 -1
  278. package/docs/eval-surface-map.md +14 -0
  279. package/docs/experiment.md +19 -2
  280. package/docs/feedback-trajectories.md +1 -1
  281. package/docs/multishot-golden-records.md +4 -4
  282. package/docs/public-api.md +1616 -0
  283. package/docs/research-report-methodology.md +1 -1
  284. package/docs/search-history-receipts.md +39 -1
  285. package/docs/trace-analysis.md +1 -1
  286. package/docs/trace-repair-admission.md +1 -1
  287. package/docs/trace-repair-continuation.md +1 -1
  288. package/docs/verdicts.md +24 -0
  289. package/package.json +7 -3
  290. package/dist/active-curriculum-C4mk67HP.js.map +0 -1
  291. package/dist/agent-profile-cell-BkcRDikH.d.ts.map +0 -1
  292. package/dist/backend-integrity-DOCa_QrR.d.ts.map +0 -1
  293. package/dist/benchmark-command-BU1Las59.js.map +0 -1
  294. package/dist/campaign-la-gEYNz.js.map +0 -1
  295. package/dist/canonical-D-XsTQ6_.js.map +0 -1
  296. package/dist/client-LIuo-KPv.js.map +0 -1
  297. package/dist/define-agent-eval-C8V8sMqP.js.map +0 -1
  298. package/dist/define-agent-eval-CEQWL9Hy.d.ts.map +0 -1
  299. package/dist/dspy-rlm-engine-CBYlPvNy.js.map +0 -1
  300. package/dist/emitter-CPBAhxum.js.map +0 -1
  301. package/dist/eval-campaign-CQuZrLR_.js.map +0 -1
  302. package/dist/exec-y-DCLqK7.js.map +0 -1
  303. package/dist/exporters-q9iL-2Jf.js.map +0 -1
  304. package/dist/index-Aj3WO3_a.d.ts.map +0 -1
  305. package/dist/index-B8Ui1mr1.d.ts.map +0 -1
  306. package/dist/index-BNPtkBPf.d.ts.map +0 -1
  307. package/dist/index-C1ravkGA.d.ts +0 -1
  308. package/dist/index-IQccV3Ou.d.ts.map +0 -1
  309. package/dist/kind-factory-DmAa0h3K.js.map +0 -1
  310. package/dist/llm-client-Bg32RW0j.js.map +0 -1
  311. package/dist/llm-judge-BqqMS8t7.js.map +0 -1
  312. package/dist/matrix-DrVnRp4G.d.ts.map +0 -1
  313. package/dist/pre-registration-DakwTRXk.js +0 -96
  314. package/dist/pre-registration-DakwTRXk.js.map +0 -1
  315. package/dist/pre-registration-zFSLEiFU.d.ts.map +0 -1
  316. package/dist/produced-state-Bnq4FaDO.js.map +0 -1
  317. package/dist/provenance-oA4-zUqm.d.ts.map +0 -1
  318. package/dist/query-CJ_DX8vl.d.ts.map +0 -1
  319. package/dist/query-Di7eEQ79.js.map +0 -1
  320. package/dist/release-confidence-4XrqlpFD.d.ts.map +0 -1
  321. package/dist/release-confidence-BknrpBnO.js.map +0 -1
  322. package/dist/researcher-DJnoUE8c.d.ts.map +0 -1
  323. package/dist/rubric-predictive-validity-C7LnNvF2.d.ts.map +0 -1
  324. package/dist/rubric-predictive-validity-CzxLoZge.js.map +0 -1
  325. package/dist/schema-C6DW4ZHR.js.map +0 -1
  326. package/dist/schema-Cef2cFmb.d.ts.map +0 -1
  327. package/dist/semantic-concept-judge-laMCnTLn.js.map +0 -1
  328. package/dist/sequential-C458DXNf.js.map +0 -1
  329. package/dist/sequential-eprocess-CbUt2htw.js.map +0 -1
  330. package/dist/server-dIWwF3j_.js.map +0 -1
  331. package/dist/skillopt-optimization-method-CPBlTcj5.js.map +0 -1
  332. package/dist/statistical-heldout-_woZ9q9j.d.ts.map +0 -1
  333. package/dist/steps-AmkT-GIM.d.ts.map +0 -1
  334. package/dist/summary-report-B__Y5ub3.d.ts.map +0 -1
  335. package/dist/summary-report-DW2bEpdB.js.map +0 -1
  336. package/dist/tool-groups-B4tqh8jB.d.ts.map +0 -1
  337. package/dist/types-CLAwnY-L.d.ts.map +0 -1
  338. package/dist/types-DdFNuyxQ.d.ts.map +0 -1
  339. package/dist/types-jUBXJ7Iz.d.ts +0 -884
  340. package/dist/types-jUBXJ7Iz.d.ts.map +0 -1
  341. package/dist/verdict-cache-mZf5FEiY.js +0 -107
  342. package/dist/verdict-cache-mZf5FEiY.js.map +0 -1
@@ -1,17 +1,18 @@
1
1
  import { i as JudgeError, s as ValidationError, t as AgentEvalError } from "./errors-Dngq5h35.js";
2
+ import { i as hashCanonical, r as canonicalString } from "./canonical-IL-Bu-14.js";
2
3
  import { s as weightedComposite, t as confidenceInterval } from "./descriptive-jDOuI6mz.js";
3
4
  import { r as pairedBootstrap } from "./paired-tests-BHIhYVdu.js";
4
- import { n as contentHash, t as canonicalJson } from "./verdict-cache-mZf5FEiY.js";
5
- import { a as campaignCellCostProvenance, o as campaignCellExecutionEvidence, t as detectRewardHacking, u as projectCampaignCellQuality } from "./reward-hacking-C0x0xihA.js";
5
+ import { i as modelHasSnapshot } from "./run-record-BC0ebuRP.js";
6
+ import { n as contentHash } from "./verdict-cache-CdVVTVmn.js";
7
+ import { a as campaignCellCostProvenance, o as campaignCellExecutionEvidence, t as detectRewardHacking, u as projectCampaignCellQuality } from "./reward-hacking-t4lB1yt8.js";
6
8
  import { i as CostLedger, t as CostAccountingIncompleteError } from "./cost-ledger-B1qx30B4.js";
7
- import { u as mapConcurrent } from "./ledger-core-DTae9rv_.js";
8
- import { i as hashCanonical } from "./canonical-D-XsTQ6_.js";
9
- import { b as createRunCostLedger, h as isRecord, m as isExternalTextCandidate, x as fsCampaignStorage } from "./external-optimizer-subprocess-CKNb42oM.js";
9
+ import { u as mapConcurrent } from "./ledger-core-BOzlRygb.js";
10
+ import { b as createRunCostLedger, h as isRecord, m as isExternalTextCandidate, x as fsCampaignStorage } from "./external-optimizer-subprocess-BIWbHpgD.js";
10
11
  import { t as DEFAULT_REDACTION_RULES } from "./redact-7Aq1ukl-.js";
11
12
  import { a as heldoutSignificance, i as dimensionRegressions, o as pairHoldout } from "./power-preflight-DEw-uC7q.js";
12
13
  import { a as combineAbortSignals, n as clamp01, r as assertProposalFindings } from "./run-score-lDzV0X8j.js";
13
14
  import { c as deepFreezeCanonicalJson } from "./types-BI4fT3HN.js";
14
- import { f as maximumChargeForLlmRequest, l as costReceiptFromLlm, m as stripFencedJson, u as costReceiptFromLlmError } from "./llm-client-Bg32RW0j.js";
15
+ import { d as stripFencedJson, o as costReceiptFromLlm, s as costReceiptFromLlmError, u as maximumChargeForLlmRequest } from "./llm-client-hgDieDNN.js";
15
16
  import { createHash } from "node:crypto";
16
17
  import { writeFileSync } from "node:fs";
17
18
  import { z } from "zod";
@@ -795,12 +796,13 @@ async function executeCell(args) {
795
796
  durationMs: Date.now() - startMs,
796
797
  seed: args.slot.cellSeed,
797
798
  cached: false,
799
+ ...args.attempt > 1 ? { retryAttempts: args.attempt - 1 } : {},
798
800
  ...failure ? { errorStage: failure.stage } : {},
799
801
  ...failure?.judge ? { errorJudge: failure.judge } : {},
800
802
  error: errorMessage
801
803
  };
804
+ let retry = false;
802
805
  if (failure) {
803
- const failurePath = join(cellDir, "failure-receipt.json");
804
806
  const receipt = {
805
807
  schemaVersion: 1,
806
808
  runAttemptId: args.runAttemptId,
@@ -816,9 +818,12 @@ async function executeCell(args) {
816
818
  tags: costTags
817
819
  })
818
820
  };
821
+ retry = args.cellRetry !== void 0 && args.attempt < args.cellRetry.attempts && !args.signal.aborted && fatalCellError === void 0 && args.cellRetry.retryable(receipt.failure);
822
+ const receiptName = retry ? `failure-receipt.attempt-${args.attempt}.json` : "failure-receipt.json";
823
+ const failurePath = join(cellDir, receiptName);
819
824
  storage.write(failurePath, JSON.stringify(receipt, null, 2));
820
- artifactsByPath[`${args.slot.cellId}/failure-receipt.json`] = failurePath;
821
- args.onFailure?.(failure);
825
+ artifactsByPath[`${args.slot.cellId}/${receiptName}`] = failurePath;
826
+ if (!retry) args.onFailure?.(failure);
822
827
  }
823
828
  await trace.flush();
824
829
  if (!errorMessage && args.resumable) storage.write(cachePath, JSON.stringify(cell));
@@ -826,7 +831,8 @@ async function executeCell(args) {
826
831
  return {
827
832
  cell,
828
833
  artifactsByPath,
829
- ...failure ? { failure } : {}
834
+ ...failure ? { failure } : {},
835
+ ...retry ? { retry } : {}
830
836
  };
831
837
  }
832
838
  async function waitForFailedCellCostSettlement(input) {
@@ -1047,6 +1053,11 @@ async function runCampaign(opts) {
1047
1053
  const dispatchShutdownTimeoutMs = opts.dispatchShutdownTimeoutMs ?? 5e3;
1048
1054
  assertCampaignDesign(opts.scenarios, reps);
1049
1055
  if (!Number.isSafeInteger(dispatchShutdownTimeoutMs) || dispatchShutdownTimeoutMs <= 0) throw new Error("runCampaign: dispatchShutdownTimeoutMs must be a positive safe integer");
1056
+ const cellRetry = opts.cellRetry;
1057
+ if (cellRetry !== void 0) {
1058
+ if (!Number.isSafeInteger(cellRetry.attempts) || cellRetry.attempts < 1) throw new Error("runCampaign: cellRetry.attempts must be a positive safe integer (total attempts, including the first)");
1059
+ if (typeof cellRetry.retryable !== "function") throw new Error("runCampaign: cellRetry.retryable must be a function");
1060
+ }
1050
1061
  if (typeof opts.runDir !== "string" || opts.runDir.trim().length === 0) throw new Error("runCampaign: runDir is required and must be a non-empty string");
1051
1062
  opts.runDir = resolveRunDir(opts.runDir, opts.repo);
1052
1063
  storage.ensureDir(opts.runDir);
@@ -1099,29 +1110,37 @@ async function runCampaign(opts) {
1099
1110
  const myIdx = nextIdx++;
1100
1111
  if (myIdx >= schedule.length) return;
1101
1112
  const slot = schedule[myIdx];
1102
- const result = await executeCell({
1103
- slot,
1104
- opts,
1105
- manifestHash,
1106
- resumable,
1107
- now,
1108
- storage,
1109
- buildTraceWriter: opts.buildTraceWriter ?? defaultBuildTraceWriter(storage),
1110
- signal: campaignSignal,
1111
- dispatchTimeoutMs: opts.dispatchTimeoutMs,
1112
- dispatchShutdownTimeoutMs,
1113
- costLedger,
1114
- costPhase,
1115
- runAttemptId,
1116
- onFailure: opts.abortOnCellError ? (failure) => {
1117
- if (firstCellFailure === void 0) {
1118
- firstCellFailure = failure;
1119
- campaignAbort.abort(failure.cause);
1120
- }
1121
- } : void 0
1122
- });
1113
+ let attempt = 1;
1114
+ let result;
1115
+ while (true) {
1116
+ result = await executeCell({
1117
+ slot,
1118
+ opts,
1119
+ manifestHash,
1120
+ resumable,
1121
+ now,
1122
+ storage,
1123
+ buildTraceWriter: opts.buildTraceWriter ?? defaultBuildTraceWriter(storage),
1124
+ signal: campaignSignal,
1125
+ dispatchTimeoutMs: opts.dispatchTimeoutMs,
1126
+ dispatchShutdownTimeoutMs,
1127
+ costLedger,
1128
+ costPhase,
1129
+ runAttemptId,
1130
+ attempt,
1131
+ cellRetry,
1132
+ onFailure: opts.abortOnCellError ? (failure) => {
1133
+ if (firstCellFailure === void 0) {
1134
+ firstCellFailure = failure;
1135
+ campaignAbort.abort(failure.cause);
1136
+ }
1137
+ } : void 0
1138
+ });
1139
+ Object.assign(artifactsByPath, result.artifactsByPath);
1140
+ if (!result.retry) break;
1141
+ attempt += 1;
1142
+ }
1123
1143
  cellsRef.push(result.cell);
1124
- Object.assign(artifactsByPath, result.artifactsByPath);
1125
1144
  if (opts.labeledStore && opts.labeledStore !== "off" && !result.cell.error) await captureToStore({
1126
1145
  store: opts.labeledStore,
1127
1146
  cell: result.cell,
@@ -1641,11 +1660,12 @@ var Dataset = class Dataset {
1641
1660
  this.scenarios.splice(idx, 1);
1642
1661
  }
1643
1662
  /**
1644
- * Stable JSON-Lines serialization — deterministic byte-for-byte.
1645
- * Write to disk for contamination-verifiable archives.
1663
+ * Stable JSON-Lines serialization — one RFC 8785 canonical row per scenario,
1664
+ * sorted by id, deterministic byte-for-byte. Write to disk for
1665
+ * contamination-verifiable archives.
1646
1666
  */
1647
1667
  toJsonl() {
1648
- return `${this.scenarios.slice().sort((a, b) => a.id.localeCompare(b.id)).map((s) => JSON.stringify(canonicalize(s))).join("\n")}\n`;
1668
+ return `${this.scenarios.slice().sort((a, b) => a.id.localeCompare(b.id)).map((s) => canonicalString(s)).join("\n")}\n`;
1649
1669
  }
1650
1670
  static fromJsonl(jsonl, manifest) {
1651
1671
  const scenarios = [];
@@ -1661,20 +1681,15 @@ var Dataset = class Dataset {
1661
1681
  });
1662
1682
  }
1663
1683
  };
1684
+ /**
1685
+ * Hex sha-256 over the RFC 8785 canonical JSON of the scenarios sorted by id,
1686
+ * so the digest is independent of insertion order. A scenario with no
1687
+ * canonical JSON form (an `undefined`-valued field, a non-finite number, a
1688
+ * class instance) is refused: the digest is the dataset's identity and a
1689
+ * coerced field would let two different datasets share one.
1690
+ */
1664
1691
  async function hashScenarios(scenarios) {
1665
- const canonical = scenarios.slice().sort((a, b) => a.id.localeCompare(b.id)).map(canonicalize);
1666
- const text = JSON.stringify(canonical);
1667
- const bytes = new TextEncoder().encode(text);
1668
- const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
1669
- return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
1670
- }
1671
- function canonicalize(v) {
1672
- if (v === null || typeof v !== "object") return v;
1673
- if (Array.isArray(v)) return v.map(canonicalize);
1674
- const keys = Object.keys(v).sort();
1675
- const out = {};
1676
- for (const k of keys) out[k] = canonicalize(v[k]);
1677
- return out;
1692
+ return hashCanonical(scenarios.slice().sort((a, b) => a.id.localeCompare(b.id))).slice(7);
1678
1693
  }
1679
1694
  /** Splitmix-ish deterministic shuffle — small, self-contained, no deps. */
1680
1695
  function seededShuffle(items, seed) {
@@ -2893,9 +2908,9 @@ function createSearchHistoryReceipt(input) {
2893
2908
  /** Verify the bounded receipt. Full ledger bytes are verified by SearchLedger. */
2894
2909
  function verifySearchHistoryReceipt(receipt) {
2895
2910
  if (!receipt || typeof receipt !== "object") throw new TypeError("search history receipt is required");
2896
- if (receipt.schemaVersion !== "1.0.0") throw new Error(`unsupported search history schemaVersion '${receipt.schemaVersion}'`);
2911
+ if (receipt.schemaVersion !== SEARCH_HISTORY_RECEIPT_SCHEMA_VERSION) throw new Error(`unsupported search history schemaVersion '${receipt.schemaVersion}'`);
2897
2912
  if (receipt.kind !== "search-history-receipt") throw new Error(`search history kind must be 'search-history-receipt'`);
2898
- if (receipt.digestAlgorithm !== "rfc8785-sha256") throw new Error(`unsupported search history digestAlgorithm '${receipt.digestAlgorithm}'`);
2913
+ if (receipt.digestAlgorithm !== SEARCH_HISTORY_RECEIPT_DIGEST_ALGORITHM) throw new Error(`unsupported search history digestAlgorithm '${receipt.digestAlgorithm}'`);
2899
2914
  const producerId = nonEmpty(receipt.producerId, "search history producerId");
2900
2915
  const runId = nonEmpty(receipt.runId, "search history runId");
2901
2916
  const ledger = normalizeArtifactRef(receipt.ledger);
@@ -3833,6 +3848,845 @@ function paretoFrontier(candidates, objectives) {
3833
3848
  }))
3834
3849
  };
3835
3850
  }
3851
+ /**
3852
+ * NSGA-II crowding distance — secondary sort for ties on the frontier.
3853
+ *
3854
+ * When the Pareto front collapses to a single point (or many candidates tie
3855
+ * on dominance), naive selection picks arbitrarily and the population
3856
+ * degenerates over generations. NSGA-II preserves diversity by preferring
3857
+ * candidates with more empty space around them on the frontier.
3858
+ *
3859
+ * Returns an array of `{ candidate, distance }` in the SAME order as the
3860
+ * input. Higher distance = more isolated = should be preferred when
3861
+ * preserving diversity.
3862
+ */
3863
+ function crowdingDistance(candidates, objectives) {
3864
+ const distances = new Map(candidates.map((c) => [c, 0]));
3865
+ for (const obj of objectives) {
3866
+ const sorted = [...candidates].sort((a, b) => obj.value(a) - obj.value(b));
3867
+ const min = obj.value(sorted[0]);
3868
+ const range = obj.value(sorted[sorted.length - 1]) - min || 1;
3869
+ distances.set(sorted[0], Infinity);
3870
+ distances.set(sorted[sorted.length - 1], Infinity);
3871
+ for (let i = 1; i < sorted.length - 1; i++) {
3872
+ const prev = obj.value(sorted[i - 1]);
3873
+ const next = obj.value(sorted[i + 1]);
3874
+ const current = distances.get(sorted[i]);
3875
+ if (current === Infinity) continue;
3876
+ distances.set(sorted[i], current + (next - prev) / range);
3877
+ }
3878
+ }
3879
+ return candidates.map((c) => ({
3880
+ candidate: c,
3881
+ distance: distances.get(c) ?? 0
3882
+ }));
3883
+ }
3884
+ /**
3885
+ * Pareto frontier with tie-break by crowding distance — the canonical
3886
+ * NSGA-II selection step. Returns the frontier sorted by descending crowding
3887
+ * distance so callers can `.slice(0, k)` to pick K diverse winners.
3888
+ */
3889
+ function paretoFrontierWithCrowding(candidates, objectives) {
3890
+ const { frontier } = paretoFrontier(candidates, objectives);
3891
+ if (frontier.length === 0) return [];
3892
+ return crowdingDistance(frontier, objectives).sort((a, b) => b.distance - a.distance);
3893
+ }
3894
+ //#endregion
3895
+ //#region src/campaign/search-ledger-recording.ts
3896
+ const SEARCH_LEDGER_DIR = "search-ledger";
3897
+ /**
3898
+ * Recorder for one `runOptimization` run: `open()`, then `recordGeneration()`
3899
+ * and `recordResults()` per generation, then `finish()`.
3900
+ *
3901
+ * Every event id is derived from the run, and an id already durable is not
3902
+ * appended again, so a resumed run continues one ledger instead of conflicting
3903
+ * with its own history.
3904
+ */
3905
+ var SearchRecorder = class SearchRecorder {
3906
+ opts;
3907
+ tasks;
3908
+ registered = /* @__PURE__ */ new Map();
3909
+ order = [];
3910
+ coverage = /* @__PURE__ */ new Map();
3911
+ openSlots = /* @__PURE__ */ new Set();
3912
+ durableEventIds = /* @__PURE__ */ new Set();
3913
+ lastStampMs = 0;
3914
+ proposalReceiptCount = 0;
3915
+ constructor(opts) {
3916
+ this.opts = opts;
3917
+ this.tasks = plannedTasks(opts.scenarios, opts.reps, opts.runDir, opts.splitDigest);
3918
+ }
3919
+ /** Open the recorder and append the plan. An existing ledger for the same
3920
+ * run is re-read first, so a resumed run keeps one plan and one lineage. */
3921
+ static async open(opts) {
3922
+ const recorder = new SearchRecorder(opts);
3923
+ await recorder.hydrate();
3924
+ await recorder.plan();
3925
+ return recorder;
3926
+ }
3927
+ /**
3928
+ * Record one generation's candidate-generation call and the candidates it
3929
+ * produced. A proposal larger than the planned population extends the plan
3930
+ * with the extra slots; a proposal that fills fewer closes the rest.
3931
+ */
3932
+ async recordGeneration(input) {
3933
+ const { generation, candidates } = input;
3934
+ const planned = this.opts.populationSize;
3935
+ if (candidates.length > planned) {
3936
+ const extra = [];
3937
+ for (let index = planned; index < candidates.length; index++) {
3938
+ const slot = {
3939
+ slotId: slotId(generation, index),
3940
+ generationOperationId: generationOperationId(generation)
3941
+ };
3942
+ extra.push(slot);
3943
+ this.openSlots.add(slot.slotId);
3944
+ }
3945
+ await this.append({
3946
+ kind: "search-plan-extended",
3947
+ eventId: `search:plan-extended:gen-${generation}`,
3948
+ occurredAt: this.stamp(),
3949
+ artifacts: [this.proposalArtifact(generation, candidates)],
3950
+ extension: {
3951
+ candidateSlots: extra,
3952
+ operations: []
3953
+ }
3954
+ });
3955
+ }
3956
+ await this.recordGenerationOperation(generation, candidates, planned - candidates.length);
3957
+ const parent = this.registered.get(input.parentSurfaceHash);
3958
+ for (const [index, candidate] of candidates.entries()) {
3959
+ const candidateId = candidate.surfaceHash;
3960
+ const slot = slotId(generation, index);
3961
+ const surfaceArtifact = this.writeArtifact("candidate-surface", `candidate-${candidateId}.json`, {
3962
+ surfaceHash: candidateId,
3963
+ surface: candidate.surface,
3964
+ label: candidate.label ?? ""
3965
+ });
3966
+ const ledgerGeneration = parent ? parent.generation + 1 : 0;
3967
+ await this.append({
3968
+ kind: "candidate-registered",
3969
+ eventId: `candidate:${candidateId}`,
3970
+ occurredAt: this.stamp(),
3971
+ artifacts: [surfaceArtifact],
3972
+ slotId: slot,
3973
+ generationOperationId: generationOperationId(generation),
3974
+ candidateId,
3975
+ lineage: {
3976
+ lineageNodeId: candidateId,
3977
+ parentCandidateIds: parent ? [parent.candidateId] : [],
3978
+ generation: ledgerGeneration,
3979
+ proposer: this.opts.proposerLabel,
3980
+ proposerSource: this.opts.binding.identity.proposer.source
3981
+ },
3982
+ surfaces: candidateSurfaces(candidate.surface, surfaceArtifact)
3983
+ });
3984
+ this.remember({
3985
+ candidateId,
3986
+ slotId: slot,
3987
+ generation: ledgerGeneration
3988
+ });
3989
+ }
3990
+ for (let index = candidates.length; index < planned; index++) await this.closeSlot(slotId(generation, index), generationOperationId(generation), {
3991
+ code: "proposal-short",
3992
+ message: `generation ${generation} proposed ${candidates.length} of ${planned} planned candidates`
3993
+ });
3994
+ }
3995
+ /** Append one task attempt per designed cell of each candidate campaign. */
3996
+ async recordResults(candidates) {
3997
+ for (const candidate of candidates) {
3998
+ const registered = this.registered.get(candidate.surfaceHash);
3999
+ if (!registered) throw new Error(`search ledger: candidate ${candidate.surfaceHash} produced results without a registration`);
4000
+ this.coverage.set(registered.candidateId, candidate.coverageComplete);
4001
+ const evidence = this.writeArtifact("candidate-cells", `cells-${registered.candidateId}.json`, {
4002
+ surfaceHash: candidate.surfaceHash,
4003
+ runDir: candidate.runDir,
4004
+ cells: candidate.cells.map((cell) => ({
4005
+ cellId: cell.cellId,
4006
+ scenarioId: cell.scenarioId,
4007
+ rep: cell.rep,
4008
+ judgeScores: cell.judgeScores,
4009
+ costUsd: cell.costUsd,
4010
+ costProvenance: cell.costProvenance,
4011
+ tokenUsage: cell.tokenUsage,
4012
+ ...cell.error === void 0 ? {} : {
4013
+ error: cell.error,
4014
+ errorStage: cell.errorStage
4015
+ }
4016
+ }))
4017
+ });
4018
+ const surfaceIds = candidateSurfaces(candidate.surface, evidence).map((surface) => surface.surfaceId);
4019
+ for (const cell of candidate.cells) {
4020
+ const taskId = taskIdFor(cell.scenarioId, cell.rep);
4021
+ const task = this.tasks.find((planned) => planned.taskId === taskId);
4022
+ if (!task) throw new Error(`search ledger: cell ${cell.cellId} is outside the planned task set`);
4023
+ await this.append({
4024
+ kind: "task-attempted",
4025
+ eventId: `attempt:${registered.candidateId}:${taskId}`,
4026
+ occurredAt: this.stamp(),
4027
+ artifacts: [evidence],
4028
+ candidateId: registered.candidateId,
4029
+ runId: `${registered.candidateId}:${cell.cellId}`,
4030
+ attemptIndex: 0,
4031
+ task: {
4032
+ taskId,
4033
+ source: task.source
4034
+ },
4035
+ identity: {
4036
+ model: this.cellModel(cell),
4037
+ agent: this.opts.binding.identity.agent,
4038
+ benchmark: task.benchmark
4039
+ },
4040
+ outcome: cellOutcome(cell),
4041
+ accounting: cellAccounting(cell),
4042
+ surfaceEvidence: surfaceIds.map((surfaceId) => surfaceEvidenceFor(surfaceId, evidence))
4043
+ });
4044
+ }
4045
+ }
4046
+ }
4047
+ /**
4048
+ * Close the search: unreached generations, the selection operation, one
4049
+ * decision per candidate, then the terminal event.
4050
+ *
4051
+ * The terminal event is appended only when canonical replay accounts for the
4052
+ * whole planned denominator. An interrupted or partly unscored search stays
4053
+ * `in-progress` and its receipt reports the exact gap, instead of claiming a
4054
+ * closed search.
4055
+ */
4056
+ async finish(input) {
4057
+ const stopped = {
4058
+ code: "generation-not-reached",
4059
+ message: `the search stopped after ${input.generationsRun} generation(s)`
4060
+ };
4061
+ for (let generation = input.generationsRun; generation < this.opts.maxGenerations; generation++) {
4062
+ await this.recordGenerationOperation(generation, [], this.opts.populationSize, stopped);
4063
+ for (let index = 0; index < this.opts.populationSize; index++) await this.closeSlot(slotId(generation, index), generationOperationId(generation), stopped);
4064
+ }
4065
+ const decisions = this.writeArtifact("search-decisions", "decisions.json", {
4066
+ winnerSurfaceHash: input.winnerSurfaceHash,
4067
+ candidates: this.order.map((entry) => entry.candidateId)
4068
+ });
4069
+ await this.append({
4070
+ kind: "search-operation-recorded",
4071
+ eventId: "operation:selection",
4072
+ occurredAt: this.stamp(),
4073
+ artifacts: [decisions],
4074
+ operationId: "selection",
4075
+ operationKind: "selection",
4076
+ execution: {
4077
+ kind: "deterministic",
4078
+ source: this.opts.binding.identity.search
4079
+ },
4080
+ outcome: { status: "completed" },
4081
+ accounting: {
4082
+ tokens: {
4083
+ status: "known",
4084
+ inputTokens: 0,
4085
+ outputTokens: 0,
4086
+ cachedTokens: 0
4087
+ },
4088
+ cost: {
4089
+ status: "known",
4090
+ usd: 0,
4091
+ source: "free"
4092
+ }
4093
+ }
4094
+ });
4095
+ const winner = this.registered.get(input.winnerSurfaceHash);
4096
+ for (const entry of this.order) await this.append({
4097
+ kind: "candidate-decided",
4098
+ eventId: `decision:${entry.candidateId}`,
4099
+ occurredAt: this.stamp(),
4100
+ artifacts: [decisions],
4101
+ candidateId: entry.candidateId,
4102
+ decision: winner?.candidateId === entry.candidateId ? { status: "selected" } : {
4103
+ status: "rejected",
4104
+ reason: this.coverage.get(entry.candidateId) === false ? {
4105
+ code: "coverage-incomplete",
4106
+ message: "the candidate missed a designed cell and could not be ranked"
4107
+ } : {
4108
+ code: "not-promoted",
4109
+ message: "the candidate did not beat the incumbent"
4110
+ }
4111
+ }
4112
+ });
4113
+ const replay = await this.opts.binding.ledger.replay();
4114
+ const { missingCandidateSlots, missingTaskOutcomes, missingOperations } = replay.audit.expected;
4115
+ if (missingCandidateSlots.length === 0 && missingTaskOutcomes.length === 0 && missingOperations.length === 0 && replay.audit.decisions.pending === 0) await this.append({
4116
+ kind: "search-completed",
4117
+ eventId: "search:completed",
4118
+ occurredAt: this.stamp(),
4119
+ artifacts: [decisions],
4120
+ result: winner ? {
4121
+ status: "selected",
4122
+ candidateId: winner.candidateId
4123
+ } : {
4124
+ status: "all-rejected",
4125
+ reason: {
4126
+ code: "no-promoted-candidate",
4127
+ message: "no candidate beat the incumbent on the designed denominator"
4128
+ }
4129
+ }
4130
+ });
4131
+ return this.receipt(input.runId);
4132
+ }
4133
+ /** Bounded receipt over the exact ledger bytes this run produced. */
4134
+ async receipt(runId) {
4135
+ const { ledger } = this.opts.binding;
4136
+ const replay = await ledger.replay();
4137
+ const bytes = replay.entries.map((entry) => `${canonicalString(entry)}\n`).join("");
4138
+ return createSearchHistoryReceipt({
4139
+ producerId: this.opts.proposerLabel,
4140
+ runId,
4141
+ ledger: {
4142
+ role: "search-ledger",
4143
+ uri: `file://${ledger.path}`,
4144
+ sha256: hashCanonical(bytes),
4145
+ byteLength: new TextEncoder().encode(bytes).byteLength
4146
+ },
4147
+ replay
4148
+ });
4149
+ }
4150
+ /** Read an existing ledger for this run so a resume continues it. */
4151
+ async hydrate() {
4152
+ const replay = await this.opts.binding.ledger.replay();
4153
+ for (const entry of replay.entries) {
4154
+ this.durableEventIds.add(entry.event.eventId);
4155
+ const stamped = Date.parse(entry.event.occurredAt);
4156
+ if (stamped > this.lastStampMs) this.lastStampMs = stamped;
4157
+ }
4158
+ for (const slot of replay.plan?.plan.candidateSlots ?? []) this.openSlots.add(slot.slotId);
4159
+ for (const extension of replay.planExtensions) for (const slot of extension.extension.candidateSlots) this.openSlots.add(slot.slotId);
4160
+ for (const closed of replay.closedCandidateSlots) this.openSlots.delete(closed.slotId);
4161
+ for (const candidate of replay.candidates) {
4162
+ this.remember({
4163
+ candidateId: candidate.candidateId,
4164
+ slotId: candidate.slotId,
4165
+ generation: candidate.lineage.generation
4166
+ });
4167
+ this.openSlots.delete(candidate.slotId);
4168
+ }
4169
+ }
4170
+ async plan() {
4171
+ const candidateSlots = [];
4172
+ const operations = [{
4173
+ operationId: "selection",
4174
+ kind: "selection"
4175
+ }];
4176
+ for (let generation = 0; generation < this.opts.maxGenerations; generation++) {
4177
+ operations.push({
4178
+ operationId: generationOperationId(generation),
4179
+ kind: "candidate-generation"
4180
+ });
4181
+ for (let index = 0; index < this.opts.populationSize; index++) {
4182
+ const slot = {
4183
+ slotId: slotId(generation, index),
4184
+ generationOperationId: generationOperationId(generation)
4185
+ };
4186
+ candidateSlots.push(slot);
4187
+ if (!this.registeredSlot(slot.slotId)) this.openSlots.add(slot.slotId);
4188
+ }
4189
+ }
4190
+ await this.append({
4191
+ kind: "search-planned",
4192
+ eventId: "search:plan",
4193
+ occurredAt: this.stamp(),
4194
+ artifacts: [this.writeArtifact("search-plan", "plan.json", {
4195
+ runDir: this.opts.runDir,
4196
+ splitDigest: this.opts.splitDigest,
4197
+ maxGenerations: this.opts.maxGenerations,
4198
+ populationSize: this.opts.populationSize,
4199
+ tasks: this.tasks
4200
+ })],
4201
+ plan: {
4202
+ candidateSlots,
4203
+ tasks: this.tasks,
4204
+ operations
4205
+ }
4206
+ });
4207
+ }
4208
+ registeredSlot(slot) {
4209
+ return this.order.some((entry) => entry.slotId === slot);
4210
+ }
4211
+ remember(entry) {
4212
+ if (this.registered.has(entry.candidateId)) return;
4213
+ this.registered.set(entry.candidateId, entry);
4214
+ this.order.push(entry);
4215
+ }
4216
+ async recordGenerationOperation(generation, candidates, unfilled, failure) {
4217
+ const shortfall = {
4218
+ code: "proposal-short",
4219
+ message: `generation ${generation} left ${unfilled} planned candidate slot(s) unfilled`
4220
+ };
4221
+ const outcome = failure ? {
4222
+ status: "failed",
4223
+ failure
4224
+ } : unfilled <= 0 ? { status: "completed" } : candidates.length === 0 ? {
4225
+ status: "failed",
4226
+ failure: shortfall
4227
+ } : {
4228
+ status: "partial",
4229
+ failure: shortfall
4230
+ };
4231
+ await this.append({
4232
+ kind: "search-operation-recorded",
4233
+ eventId: `operation:${generationOperationId(generation)}`,
4234
+ occurredAt: this.stamp(),
4235
+ artifacts: [this.proposalArtifact(generation, candidates)],
4236
+ operationId: generationOperationId(generation),
4237
+ operationKind: "candidate-generation",
4238
+ execution: this.opts.binding.identity.proposer,
4239
+ outcome,
4240
+ accounting: this.proposalAccounting()
4241
+ });
4242
+ }
4243
+ async closeSlot(slot, generationOperation, reason) {
4244
+ if (!this.openSlots.has(slot)) return;
4245
+ this.openSlots.delete(slot);
4246
+ await this.append({
4247
+ kind: "candidate-slot-closed",
4248
+ eventId: `slot-closed:${slot}`,
4249
+ occurredAt: this.stamp(),
4250
+ artifacts: [this.writeArtifact("closed-slot", `slot-${slot}.json`, {
4251
+ slot,
4252
+ reason
4253
+ })],
4254
+ slotId: slot,
4255
+ generationOperationId: generationOperation,
4256
+ reason
4257
+ });
4258
+ }
4259
+ /** Spend booked to candidate generation since the previous generation. */
4260
+ proposalAccounting() {
4261
+ const receipts = this.opts.costLedger.list({ phase: "search.proposal" });
4262
+ const fresh = receipts.slice(this.proposalReceiptCount);
4263
+ this.proposalReceiptCount = receipts.length;
4264
+ return receiptAccounting(fresh);
4265
+ }
4266
+ cellModel(cell) {
4267
+ const resolved = cell.resolvedModel;
4268
+ if (resolved === void 0) return this.opts.binding.identity.model;
4269
+ if (!modelHasSnapshot(resolved)) throw new Error(`search ledger: cell ${cell.cellId} ran model '${resolved}', which carries no immutable snapshot; the ledger cannot record a moving alias as execution identity`);
4270
+ return {
4271
+ provider: this.opts.binding.identity.model.provider,
4272
+ snapshot: resolved
4273
+ };
4274
+ }
4275
+ proposalArtifact(generation, candidates) {
4276
+ return this.writeArtifact("candidate-proposal", `proposal-gen-${generation}.json`, {
4277
+ generation,
4278
+ candidates: candidates.map((candidate) => ({
4279
+ surfaceHash: candidate.surfaceHash,
4280
+ label: candidate.label ?? ""
4281
+ }))
4282
+ });
4283
+ }
4284
+ /** Write one canonical evidence document and return its content address. */
4285
+ writeArtifact(role, name, body) {
4286
+ const directory = `${this.opts.runDir}/${SEARCH_LEDGER_DIR}`;
4287
+ const path = `${directory}/${name}`;
4288
+ const contents = canonicalString(body);
4289
+ this.opts.storage.ensureDir(directory);
4290
+ this.opts.storage.write(path, contents);
4291
+ return {
4292
+ role,
4293
+ uri: `file://${path}`,
4294
+ sha256: hashCanonical(contents),
4295
+ byteLength: new TextEncoder().encode(contents).byteLength
4296
+ };
4297
+ }
4298
+ async append(event) {
4299
+ if (this.durableEventIds.has(event.eventId)) return;
4300
+ await this.opts.binding.ledger.append(event);
4301
+ this.durableEventIds.add(event.eventId);
4302
+ }
4303
+ /** Non-decreasing ISO stamps; the ledger refuses an event that moves back. */
4304
+ stamp() {
4305
+ const now = Date.now();
4306
+ this.lastStampMs = now > this.lastStampMs ? now : this.lastStampMs + 1;
4307
+ return new Date(this.lastStampMs).toISOString();
4308
+ }
4309
+ };
4310
+ /** One task per designed (scenario, replicate) cell. */
4311
+ function plannedTasks(scenarios, reps, runDir, splitDigest) {
4312
+ const tasks = [];
4313
+ for (const scenario of scenarios) for (let rep = 0; rep < reps; rep++) tasks.push({
4314
+ taskId: taskIdFor(scenario.id, rep),
4315
+ source: {
4316
+ uri: `scenario://${scenario.id}`,
4317
+ revision: hashCanonical(scenario)
4318
+ },
4319
+ benchmark: {
4320
+ uri: `campaign://${runDir}`,
4321
+ revision: splitDigest
4322
+ },
4323
+ maxAttempts: 1
4324
+ });
4325
+ return tasks;
4326
+ }
4327
+ function taskIdFor(scenarioId, rep) {
4328
+ return `${scenarioId}#rep-${rep}`;
4329
+ }
4330
+ function slotId(generation, index) {
4331
+ return `gen-${generation}-slot-${index}`;
4332
+ }
4333
+ function generationOperationId(generation) {
4334
+ return `candidate-generation:gen-${generation}`;
4335
+ }
4336
+ /** Declared surfaces of one candidate. A component surface declares one
4337
+ * surface per named component, so per-component evidence stays addressable. */
4338
+ function candidateSurfaces(surface, artifact) {
4339
+ if (typeof surface === "string") return [{
4340
+ surfaceId: "prompt",
4341
+ kind: "prompt",
4342
+ artifact
4343
+ }];
4344
+ if (surface.kind === "code") return [{
4345
+ surfaceId: "code",
4346
+ kind: "code",
4347
+ artifact
4348
+ }];
4349
+ return Object.keys(surface.components).sort().map((name) => ({
4350
+ surfaceId: `component:${name}`,
4351
+ kind: "prompt",
4352
+ artifact
4353
+ }));
4354
+ }
4355
+ /** The campaign measures a candidate surface as a whole, so per-surface
4356
+ * attribution stays unmeasured instead of inventing a per-component delta. */
4357
+ function surfaceEvidenceFor(surfaceId, evidence) {
4358
+ return {
4359
+ surfaceId,
4360
+ fired: true,
4361
+ firingCount: 1,
4362
+ effect: {
4363
+ status: "not-measured",
4364
+ reason: "the campaign measures the candidate surface as a whole, not per surface"
4365
+ },
4366
+ evidence: [evidence]
4367
+ };
4368
+ }
4369
+ function cellOutcome(cell) {
4370
+ const scores = Object.entries(cell.judgeScores).filter(([, score]) => score.failed !== true && Number.isFinite(score.composite));
4371
+ if (cell.error !== void 0 || scores.length === 0) return {
4372
+ status: "errored",
4373
+ metrics: {},
4374
+ error: {
4375
+ code: cell.errorStage ?? "unscored",
4376
+ message: cell.error ?? "the cell produced no complete judge score",
4377
+ retryable: false
4378
+ }
4379
+ };
4380
+ const composite = scores.reduce((sum, [, score]) => sum + score.composite, 0) / scores.length;
4381
+ const metrics = { composite };
4382
+ for (const [judge, score] of scores) metrics[`judge.${judge}`] = score.composite;
4383
+ return {
4384
+ status: "passed",
4385
+ score: composite,
4386
+ metrics
4387
+ };
4388
+ }
4389
+ function cellAccounting(cell) {
4390
+ const usage = cell.tokenUsage;
4391
+ return {
4392
+ tokens: usage.tokensKnown === false ? {
4393
+ status: "unknown",
4394
+ reason: "a paid call in this cell reported no token usage"
4395
+ } : {
4396
+ status: "known",
4397
+ inputTokens: usage.input,
4398
+ outputTokens: usage.output,
4399
+ cachedTokens: 0
4400
+ },
4401
+ cost: cell.costProvenance.kind === "uncaptured" ? {
4402
+ status: "unknown",
4403
+ knownLowerBoundUsd: cell.costUsd,
4404
+ reason: "the cell recorded spend without a provider receipt"
4405
+ } : {
4406
+ status: "known",
4407
+ usd: cell.costUsd,
4408
+ source: cell.costProvenance.kind === "observed" ? "provider" : "pricing-table"
4409
+ }
4410
+ };
4411
+ }
4412
+ function receiptAccounting(receipts) {
4413
+ let inputTokens = 0;
4414
+ let outputTokens = 0;
4415
+ let cachedTokens = 0;
4416
+ let usd = 0;
4417
+ let tokensKnown = true;
4418
+ let costKnown = true;
4419
+ for (const receipt of receipts) {
4420
+ if (receipt.usageUnknown === true) tokensKnown = false;
4421
+ inputTokens += receipt.inputTokens;
4422
+ outputTokens += receipt.outputTokens;
4423
+ cachedTokens += receipt.cachedTokens ?? 0;
4424
+ if (receipt.costUnknown) costKnown = false;
4425
+ else usd += receipt.costUsd;
4426
+ }
4427
+ return {
4428
+ tokens: tokensKnown ? {
4429
+ status: "known",
4430
+ inputTokens,
4431
+ outputTokens,
4432
+ cachedTokens
4433
+ } : {
4434
+ status: "unknown",
4435
+ reason: "a candidate-generation call reported no token usage"
4436
+ },
4437
+ cost: costKnown ? {
4438
+ status: "known",
4439
+ usd,
4440
+ source: usd === 0 ? "free" : "provider"
4441
+ } : {
4442
+ status: "unknown",
4443
+ knownLowerBoundUsd: usd,
4444
+ reason: "a candidate-generation call recorded no provider cost"
4445
+ }
4446
+ };
4447
+ }
4448
+ /**
4449
+ * Record an optimizer's own candidate graph into the same ledger.
4450
+ *
4451
+ * A complete optimization method searches inside its own process and reports
4452
+ * one artifact when it finishes: the candidate population, with each
4453
+ * candidate's parents and its score per selection scenario. This turns that
4454
+ * artifact into the canonical event stream, so a first-party method returns
4455
+ * the same `SearchHistoryReceipt` the in-process loop returns, and
4456
+ * `compareOptimizationMethods({ searchHistoryPolicy: 'require-complete' })`
4457
+ * accepts it.
4458
+ *
4459
+ * A candidate the optimizer left unscored on a planned scenario leaves the
4460
+ * planned denominator open, so the receipt reports the gap instead of closing
4461
+ * the search.
4462
+ */
4463
+ async function recordCandidatePopulationSearch(input) {
4464
+ const { population, ledger, identity } = input;
4465
+ const stamps = monotonicStamps();
4466
+ const writeArtifact = artifactWriter(input.storage, input.runDir);
4467
+ const populationArtifact = {
4468
+ role: "candidate-population",
4469
+ uri: `file://${population.summary.path}`,
4470
+ sha256: population.summary.sha256,
4471
+ byteLength: population.summary.bytes
4472
+ };
4473
+ const tasks = input.scenarios.filter((scenario) => population.summary.scenarioIds.includes(scenario.id)).map((scenario) => ({
4474
+ taskId: taskIdFor(scenario.id, 0),
4475
+ source: {
4476
+ uri: `scenario://${scenario.id}`,
4477
+ revision: hashCanonical(scenario)
4478
+ },
4479
+ benchmark: {
4480
+ uri: `optimizer://${input.producerId}`,
4481
+ revision: population.summary.sha256
4482
+ },
4483
+ maxAttempts: 1
4484
+ }));
4485
+ if (tasks.length !== population.summary.scenarioIds.length) throw new Error(`search ledger: the candidate population scored ${population.summary.scenarioIds.length} scenarios, but ${tasks.length} were supplied`);
4486
+ const generationOperation = "candidate-generation:population";
4487
+ await ledger.append({
4488
+ kind: "search-planned",
4489
+ eventId: "search:plan",
4490
+ occurredAt: stamps(),
4491
+ artifacts: [populationArtifact],
4492
+ plan: {
4493
+ candidateSlots: population.candidates.map((candidate) => ({
4494
+ slotId: `candidate-${candidate.index}`,
4495
+ generationOperationId: generationOperation
4496
+ })),
4497
+ tasks,
4498
+ operations: [{
4499
+ operationId: generationOperation,
4500
+ kind: "candidate-generation"
4501
+ }, {
4502
+ operationId: "selection",
4503
+ kind: "selection"
4504
+ }]
4505
+ }
4506
+ });
4507
+ await ledger.append({
4508
+ kind: "search-operation-recorded",
4509
+ eventId: `operation:${generationOperation}`,
4510
+ occurredAt: stamps(),
4511
+ artifacts: [populationArtifact],
4512
+ operationId: generationOperation,
4513
+ operationKind: "candidate-generation",
4514
+ execution: identity.proposer,
4515
+ outcome: { status: "completed" },
4516
+ accounting: input.generationAccounting
4517
+ });
4518
+ const candidateIds = /* @__PURE__ */ new Map();
4519
+ const depths = /* @__PURE__ */ new Map();
4520
+ for (const candidate of population.candidates) {
4521
+ const parents = candidate.parentIndices.filter((index) => index !== null);
4522
+ const candidateId = candidate.candidateHash;
4523
+ candidateIds.set(candidate.index, candidateId);
4524
+ const depth = parents.length === 0 ? 0 : Math.max(...parents.map((index) => depths.get(index) ?? 0)) + 1;
4525
+ depths.set(candidate.index, depth);
4526
+ const surfaceArtifact = writeArtifact("candidate-surface", `candidate-${candidate.index}.json`, {
4527
+ index: candidate.index,
4528
+ candidateDigest: candidate.candidateDigest,
4529
+ candidate: candidate.candidate
4530
+ });
4531
+ await ledger.append({
4532
+ kind: "candidate-registered",
4533
+ eventId: `candidate:${candidate.index}`,
4534
+ occurredAt: stamps(),
4535
+ artifacts: [surfaceArtifact],
4536
+ slotId: `candidate-${candidate.index}`,
4537
+ generationOperationId: generationOperation,
4538
+ candidateId,
4539
+ lineage: {
4540
+ lineageNodeId: candidateId.slice(0, 16),
4541
+ parentCandidateIds: parents.map((index) => {
4542
+ const parentId = candidateIds.get(index);
4543
+ if (!parentId) throw new Error(`search ledger: candidate ${candidate.index} names parent ${index}, which precedes no registered candidate`);
4544
+ return parentId;
4545
+ }),
4546
+ generation: depth,
4547
+ proposer: input.producerId,
4548
+ proposerSource: identity.proposer.source
4549
+ },
4550
+ surfaces: candidateSurfaces(externalSurface(candidate.candidate), surfaceArtifact)
4551
+ });
4552
+ const surfaceIds = candidateSurfaces(externalSurface(candidate.candidate), surfaceArtifact).map((surface) => surface.surfaceId);
4553
+ for (const score of candidate.selectionScores) {
4554
+ const task = tasks.find((planned) => planned.taskId === taskIdFor(score.scenarioId, 0));
4555
+ if (!task) throw new Error(`search ledger: candidate ${candidate.index} scored unplanned scenario ${score.scenarioId}`);
4556
+ await ledger.append({
4557
+ kind: "task-attempted",
4558
+ eventId: `attempt:${candidate.index}:${task.taskId}`,
4559
+ occurredAt: stamps(),
4560
+ artifacts: [populationArtifact],
4561
+ candidateId,
4562
+ runId: `${input.runId}:${candidate.index}:${task.taskId}`,
4563
+ attemptIndex: 0,
4564
+ task: {
4565
+ taskId: task.taskId,
4566
+ source: task.source
4567
+ },
4568
+ identity: {
4569
+ model: identity.model,
4570
+ agent: identity.agent,
4571
+ benchmark: task.benchmark
4572
+ },
4573
+ outcome: {
4574
+ status: "passed",
4575
+ score: score.score,
4576
+ metrics: { composite: score.score }
4577
+ },
4578
+ accounting: {
4579
+ tokens: {
4580
+ status: "unknown",
4581
+ reason: "the optimizer reports no token usage per candidate evaluation"
4582
+ },
4583
+ cost: {
4584
+ status: "unknown",
4585
+ knownLowerBoundUsd: 0,
4586
+ reason: "the optimizer reports no cost per candidate evaluation"
4587
+ }
4588
+ },
4589
+ surfaceEvidence: surfaceIds.map((surfaceId) => surfaceEvidenceFor(surfaceId, populationArtifact))
4590
+ });
4591
+ }
4592
+ }
4593
+ await ledger.append({
4594
+ kind: "search-operation-recorded",
4595
+ eventId: "operation:selection",
4596
+ occurredAt: stamps(),
4597
+ artifacts: [populationArtifact],
4598
+ operationId: "selection",
4599
+ operationKind: "selection",
4600
+ execution: {
4601
+ kind: "deterministic",
4602
+ source: identity.search
4603
+ },
4604
+ outcome: { status: "completed" },
4605
+ accounting: {
4606
+ tokens: {
4607
+ status: "known",
4608
+ inputTokens: 0,
4609
+ outputTokens: 0,
4610
+ cachedTokens: 0
4611
+ },
4612
+ cost: {
4613
+ status: "known",
4614
+ usd: 0,
4615
+ source: "free"
4616
+ }
4617
+ }
4618
+ });
4619
+ const winnerId = candidateIds.get(population.bestIndex);
4620
+ for (const candidate of population.candidates) await ledger.append({
4621
+ kind: "candidate-decided",
4622
+ eventId: `decision:${candidate.index}`,
4623
+ occurredAt: stamps(),
4624
+ artifacts: [populationArtifact],
4625
+ candidateId: candidateIds.get(candidate.index),
4626
+ decision: candidate.index === population.bestIndex ? { status: "selected" } : {
4627
+ status: "rejected",
4628
+ reason: {
4629
+ code: "not-selected",
4630
+ message: "the optimizer selected another candidate"
4631
+ }
4632
+ }
4633
+ });
4634
+ const replay = await ledger.replay();
4635
+ const { missingCandidateSlots, missingTaskOutcomes, missingOperations } = replay.audit.expected;
4636
+ if (winnerId && missingCandidateSlots.length === 0 && missingTaskOutcomes.length === 0 && missingOperations.length === 0 && replay.audit.decisions.pending === 0) await ledger.append({
4637
+ kind: "search-completed",
4638
+ eventId: "search:completed",
4639
+ occurredAt: stamps(),
4640
+ artifacts: [populationArtifact],
4641
+ result: {
4642
+ status: "selected",
4643
+ candidateId: winnerId
4644
+ }
4645
+ });
4646
+ const final = await ledger.replay();
4647
+ const bytes = final.entries.map((entry) => `${canonicalString(entry)}\n`).join("");
4648
+ return createSearchHistoryReceipt({
4649
+ producerId: input.producerId,
4650
+ runId: input.runId,
4651
+ ledger: {
4652
+ role: "search-ledger",
4653
+ uri: `file://${ledger.path}`,
4654
+ sha256: hashCanonical(bytes),
4655
+ byteLength: new TextEncoder().encode(bytes).byteLength
4656
+ },
4657
+ replay: final
4658
+ });
4659
+ }
4660
+ /** An optimizer candidate as the canonical mutable surface it represents. */
4661
+ function externalSurface(candidate) {
4662
+ return typeof candidate === "string" ? candidate : {
4663
+ kind: "components",
4664
+ components: candidate
4665
+ };
4666
+ }
4667
+ function artifactWriter(storage, runDir) {
4668
+ const directory = `${runDir}/${SEARCH_LEDGER_DIR}`;
4669
+ return (role, name, body) => {
4670
+ const contents = canonicalString(body);
4671
+ storage.ensureDir(directory);
4672
+ storage.write(`${directory}/${name}`, contents);
4673
+ return {
4674
+ role,
4675
+ uri: `file://${directory}/${name}`,
4676
+ sha256: hashCanonical(contents),
4677
+ byteLength: new TextEncoder().encode(contents).byteLength
4678
+ };
4679
+ };
4680
+ }
4681
+ /** Non-decreasing ISO stamps; the ledger refuses an event that moves back. */
4682
+ function monotonicStamps() {
4683
+ let last = 0;
4684
+ return () => {
4685
+ const now = Date.now();
4686
+ last = now > last ? now : last + 1;
4687
+ return new Date(last).toISOString();
4688
+ };
4689
+ }
3836
4690
  //#endregion
3837
4691
  //#region src/campaign/types.ts
3838
4692
  /** Type guard: a proposal carrying its rationale vs a bare
@@ -3854,7 +4708,9 @@ function labelTrustRank(trust) {
3854
4708
  /**
3855
4709
  * `runOptimization` runs a caller-owned candidate generator for a bounded
3856
4710
  * number of rounds. Each candidate is measured on the same cases, and only a
3857
- * candidate that beats the current best becomes the next parent.
4711
+ * candidate that beats the current best becomes the incumbent. By default the
4712
+ * incumbent is also the parent every generation mutates; a `selectParent`
4713
+ * policy draws the parent from the Pareto frontier instead.
3858
4714
  * The same loop accepts deterministic, model-backed, or agent-backed
3859
4715
  * proposers; they differ only in how `propose()` picks candidates.
3860
4716
  *
@@ -3862,7 +4718,7 @@ function labelTrustRank(trust) {
3862
4718
  * and optional pull request creation.
3863
4719
  */
3864
4720
  /**
3865
- * Improvement loop body: N generations of propose → campaign → rank, maintaining a Pareto frontier and one global incumbent across generations.
4721
+ * Improvement loop body: N generations of propose → campaign → rank, maintaining a Pareto frontier and one global incumbent across generations. The parent each generation mutates is the incumbent unless `selectParent` draws it from the frontier.
3866
4722
  */
3867
4723
  async function runOptimization(opts) {
3868
4724
  const { proposer } = opts;
@@ -3899,6 +4755,18 @@ async function runOptimization(opts) {
3899
4755
  const label = opts.premeasuredBaseline ? "premeasured baseline" : "baseline";
3900
4756
  throw new Error(`runOptimization: ${label} is incomplete (${baselineCoverage.scorableCellIds.length}/${baselineCoverage.expectedCellIds.length} designed cells scorable) — ${formatCoverageFailures(baselineCoverage)}. Refusing to optimize against an incomplete incumbent.`);
3901
4757
  }
4758
+ const recorder = opts.searchLedger ? await SearchRecorder.open({
4759
+ binding: opts.searchLedger,
4760
+ storage,
4761
+ runDir: opts.runDir,
4762
+ scenarios: opts.scenarios,
4763
+ reps,
4764
+ maxGenerations: opts.maxGenerations,
4765
+ populationSize: opts.populationSize,
4766
+ splitDigest: baselineCampaign.splitDigest,
4767
+ proposerLabel: proposer.kind,
4768
+ costLedger
4769
+ }) : void 0;
3902
4770
  const generations = [];
3903
4771
  const history = [];
3904
4772
  let currentFindings = initialFindings;
@@ -3914,6 +4782,10 @@ async function runOptimization(opts) {
3914
4782
  let winnerLabel;
3915
4783
  let winnerRationale;
3916
4784
  const scored = [toParetoParent(baselineSurface, winnerSurfaceHash, baselineCampaign, -1)];
4785
+ const measuredByHash = /* @__PURE__ */ new Map([[winnerSurfaceHash, {
4786
+ parent: scored[0],
4787
+ outcome: baselineOutcome
4788
+ }]]);
3917
4789
  if (opts.analyzeGeneration && opts.maxGenerations > 0 && baselineCampaign.cells.length > 0) {
3918
4790
  const fresh = await opts.analyzeGeneration({
3919
4791
  generation: -1,
@@ -3933,20 +4805,30 @@ async function runOptimization(opts) {
3933
4805
  for (let gen = 0; gen < opts.maxGenerations; gen++) {
3934
4806
  const proposalHistory = immutableProposalSnapshot(history, "history");
3935
4807
  if (proposer.decide?.({ history: proposalHistory }).stop) break;
3936
- const paretoParents = computeParetoFrontier(scored);
3937
- const parentSurfaceHash = winnerSurfaceHash;
3938
- const parentComposite = winnerComposite;
4808
+ const paretoParents = immutableProposalSnapshot(computeParetoFrontier(scored), "Pareto parents");
4809
+ const incumbentOutcome = immutableProposalSnapshot(winnerOutcome, "incumbent outcome");
4810
+ const selected = opts.selectParent ? resolveSelectedParent(opts.selectParent(Object.freeze({
4811
+ frontier: paretoParents,
4812
+ incumbent: incumbentOutcome,
4813
+ history: proposalHistory,
4814
+ generation: gen
4815
+ })), measuredByHash, gen) : void 0;
4816
+ const parentSurface = selected ? selected.parent.surface : winnerSurface;
4817
+ const parentSurfaceHash = selected ? selected.parent.surfaceHash : winnerSurfaceHash;
4818
+ const parentComposite = selected ? selected.outcome.composite : winnerComposite;
4819
+ const parentOutcome = selected ? selected.outcome : winnerOutcome;
3939
4820
  const proposalContext = Object.freeze({
3940
- currentSurface: immutableProposalSnapshot(winnerSurface, "current surface"),
4821
+ currentSurface: immutableProposalSnapshot(parentSurface, "current surface"),
3941
4822
  history: proposalHistory,
3942
4823
  findings: immutableProposalSnapshot(assertProposalFindings(currentFindings, "runOptimization proposal findings"), "findings"),
3943
4824
  populationSize: opts.populationSize,
3944
4825
  generation: gen,
3945
4826
  signal: opts.signal ?? new AbortController().signal,
3946
4827
  baselineOutcome: immutableProposalSnapshot(baselineOutcome, "baseline outcome"),
3947
- incumbentOutcome: immutableProposalSnapshot(winnerOutcome, "incumbent outcome"),
4828
+ incumbentOutcome,
4829
+ parentOutcome: immutableProposalSnapshot(parentOutcome, "parent outcome"),
3948
4830
  maxImprovementShots: opts.maxImprovementShots,
3949
- paretoParents: immutableProposalSnapshot(paretoParents, "Pareto parents"),
4831
+ paretoParents,
3950
4832
  costLedger,
3951
4833
  costPhase: "search.proposal"
3952
4834
  });
@@ -3966,6 +4848,15 @@ async function runOptimization(opts) {
3966
4848
  generationHashes.add(hash);
3967
4849
  }
3968
4850
  for (const hash of generationHashes) admittedCandidateHashes.add(hash);
4851
+ await recorder?.recordGeneration({
4852
+ generation: gen,
4853
+ parentSurfaceHash,
4854
+ candidates: candidates.map(({ surface, label }) => ({
4855
+ surface,
4856
+ surfaceHash: surfaceHash(surface),
4857
+ ...label ? { label } : {}
4858
+ }))
4859
+ });
3969
4860
  const surfaceResults = await mapConcurrent(candidates, candidateConcurrency, async ({ surface, label, rationale }, i, signal) => {
3970
4861
  const hash = surfaceHash(surface);
3971
4862
  const campaign = await runCampaign({
@@ -3993,8 +4884,22 @@ async function runOptimization(opts) {
3993
4884
  }, opts.signal);
3994
4885
  for (const result of surfaceResults) {
3995
4886
  const { surface, surfaceHash: hash, campaign, coverage, label, rationale } = result;
3996
- if (coverage.complete) scored.push(toParetoParent(surface, hash, campaign, gen, label || void 0, rationale || void 0));
4887
+ if (coverage.complete) {
4888
+ const parent = toParetoParent(surface, hash, campaign, gen, label || void 0, rationale || void 0);
4889
+ scored.push(parent);
4890
+ measuredByHash.set(hash, {
4891
+ parent,
4892
+ outcome: toScoredSurfaceOutcome(hash, campaign, coverage, gen)
4893
+ });
4894
+ }
3997
4895
  }
4896
+ await recorder?.recordResults(surfaceResults.map((result) => ({
4897
+ surface: result.surface,
4898
+ surfaceHash: result.surfaceHash,
4899
+ cells: result.campaign.cells,
4900
+ runDir: result.campaign.runDir,
4901
+ coverageComplete: result.coverage.complete
4902
+ })));
3998
4903
  surfaceResults.sort((a, b) => {
3999
4904
  if (a.coverage.complete !== b.coverage.complete) return a.coverage.complete ? -1 : 1;
4000
4905
  if (a.rankKey && b.rankKey) return compareRankKeys(b.rankKey, a.rankKey);
@@ -4064,6 +4969,11 @@ async function runOptimization(opts) {
4064
4969
  currentFindings = immutableProposalSnapshot(assertProposalFindings(fresh, "runOptimization generation analysis findings"), "generation analysis findings");
4065
4970
  }
4066
4971
  }
4972
+ const searchHistory = await recorder?.finish({
4973
+ winnerSurfaceHash,
4974
+ generationsRun: generations.length,
4975
+ runId: opts.runDir
4976
+ });
4067
4977
  return {
4068
4978
  generations,
4069
4979
  baselineSurface,
@@ -4072,10 +4982,22 @@ async function runOptimization(opts) {
4072
4982
  winnerLabel,
4073
4983
  winnerRationale,
4074
4984
  baselineCampaign,
4985
+ ...searchHistory ? { searchHistory } : {},
4075
4986
  paretoFrontier: computeParetoFrontier(scored),
4076
4987
  cost: costLedger.summary()
4077
4988
  };
4078
4989
  }
4990
+ /** Resolve the parent a `selectParent` policy returned to the measured record
4991
+ * this run holds for it. Refuses a surface the run never measured to
4992
+ * completion, and a parent whose surface does not hash to its `surfaceHash`. */
4993
+ function resolveSelectedParent(returned, measuredByHash, generation) {
4994
+ if (typeof returned !== "object" || returned === null || typeof returned.surfaceHash !== "string") throw new TypeError(`runOptimization: selectParent must return a ParetoParent (generation ${generation})`);
4995
+ const parent = returned;
4996
+ const measured = measuredByHash.get(parent.surfaceHash);
4997
+ if (!measured) throw new Error(`runOptimization: selectParent returned surface "${parent.surfaceHash}" in generation ${generation}, which this run has not measured to completion; a parent must be a scored surface from the frontier`);
4998
+ if (surfaceHash(parent.surface) !== parent.surfaceHash) throw new Error(`runOptimization: selectParent returned a parent whose surface does not match its surfaceHash "${parent.surfaceHash}" (generation ${generation})`);
4999
+ return measured;
5000
+ }
4079
5001
  function immutableProposalSnapshot(value, label) {
4080
5002
  try {
4081
5003
  return deepFreeze(structuredClone(value));
@@ -4508,7 +5430,7 @@ function buildLoopProvenanceRecord(args) {
4508
5430
  gate: {
4509
5431
  decision: args.gate.decision,
4510
5432
  reasons: args.gate.reasons,
4511
- delta: args.gate.delta,
5433
+ ...args.gate.delta === void 0 ? {} : { delta: args.gate.delta },
4512
5434
  contributingGates: args.gate.contributingGates.map((g) => ({
4513
5435
  name: g.name,
4514
5436
  status: g.status,
@@ -4544,7 +5466,7 @@ function durableOptimizationMethod(value) {
4544
5466
  if (!value.cost || !Number.isFinite(value.cost.totalCostUsd) || value.cost.totalCostUsd < 0 || typeof value.cost.accountingComplete !== "boolean" || !Array.isArray(value.cost.incompleteReasons) || value.cost.incompleteReasons.some((reason) => typeof reason !== "string" || !reason.trim()) || value.cost.accountingComplete !== (value.cost.incompleteReasons.length === 0)) throw new Error("buildLoopProvenanceRecord: optimization method cost is invalid");
4545
5467
  if (value.durationMs !== void 0 && (!Number.isFinite(value.durationMs) || value.durationMs < 0)) throw new Error("buildLoopProvenanceRecord: optimization method duration is invalid");
4546
5468
  try {
4547
- return JSON.parse(canonicalJson(value));
5469
+ return JSON.parse(canonicalString(value));
4548
5470
  } catch (cause) {
4549
5471
  throw new Error("buildLoopProvenanceRecord: optimization method data must be canonical JSON", { cause });
4550
5472
  }
@@ -4590,22 +5512,15 @@ function verifyLoopProvenanceRecord(record) {
4590
5512
  assertGateContributions(record.gate?.contributingGates, "loop provenance");
4591
5513
  return record;
4592
5514
  }
4593
- /** Return the canonical SHA-256 digest of a JSON-serializable value. */
5515
+ /** SHA-256 over the RFC 8785 canonical JSON of `value`. Throws
5516
+ * `LedgerCanonicalizationError` for a value with no canonical form. */
4594
5517
  function canonicalDigest(value) {
4595
- const json = JSON.stringify(value, function strictJson(_key, item) {
4596
- if (typeof item === "number" && !Number.isFinite(item)) throw new Error("canonical digest cannot encode a non-finite number");
4597
- if (typeof item === "bigint" || typeof item === "function" || typeof item === "symbol") throw new Error(`canonical digest cannot encode ${typeof item}`);
4598
- if (item === void 0 && Array.isArray(this)) throw new Error("canonical digest cannot encode an undefined array item");
4599
- if (item instanceof Map || item instanceof Set) throw new Error(`canonical digest cannot encode ${item instanceof Map ? "Map" : "Set"}`);
4600
- return item;
4601
- });
4602
- if (json === void 0) throw new Error("canonical digest value is not serializable");
4603
- return `sha256:${contentHash(JSON.parse(json))}`;
5518
+ return hashCanonical(value);
4604
5519
  }
4605
5520
  function durableGateDetail(detail) {
4606
5521
  if (detail === void 0) return null;
4607
5522
  try {
4608
- return JSON.parse(canonicalJson(detail));
5523
+ return JSON.parse(canonicalString(detail));
4609
5524
  } catch (cause) {
4610
5525
  throw new Error("buildLoopProvenanceRecord: gate detail must be canonical JSON", { cause });
4611
5526
  }
@@ -4890,6 +5805,33 @@ async function emitLoopProvenance(args) {
4890
5805
  };
4891
5806
  }
4892
5807
  //#endregion
5808
+ //#region src/campaign/transient-failure.ts
5809
+ const BASE_TRANSIENT = /\b50[234]\b|no stream output|produced no stream|admission timed out|admission_rejected|queue_timeout|fetch failed|ECONNRESET|This operation was aborted/i;
5810
+ const TIMEOUT_PATTERN = /timeout after \d+ ?ms|cli-bridge timeout/i;
5811
+ /**
5812
+ * True when the error text describes an infrastructure hiccup that should be
5813
+ * retried rather than scored. Empty/undefined input is not transient.
5814
+ */
5815
+ function isTransientTransportFailure(message, opts = {}) {
5816
+ if (!message) return false;
5817
+ if (BASE_TRANSIENT.test(message)) return true;
5818
+ if ((opts.retryFullDurationTimeouts ?? false) && TIMEOUT_PATTERN.test(message)) return true;
5819
+ for (const p of opts.extraPatterns ?? []) if (p.test(message)) return true;
5820
+ return false;
5821
+ }
5822
+ /**
5823
+ * Ready-made `cellRetry.retryable` predicate: true for a dispatch-stage
5824
+ * failure whose error message `isTransientTransportFailure` classifies as an
5825
+ * infrastructure hiccup. A judge-stage failure is never retried here — the
5826
+ * dispatch already produced an artifact, so re-dispatching would score a
5827
+ * different sample. A per-cell dispatch deadline ("dispatch exceeded <N>ms")
5828
+ * is not transient by default; opt in via `extraPatterns` or
5829
+ * `retryFullDurationTimeouts` when queue starvation eats the clock.
5830
+ */
5831
+ function transientDispatchFailure(opts = {}) {
5832
+ return (failure) => failure.stage === "dispatch" && isTransientTransportFailure(failure.error.message, opts);
5833
+ }
5834
+ //#endregion
4893
5835
  //#region src/judges.ts
4894
5836
  /**
4895
5837
  * A judge's model response could not be parsed into scored dimensions.
@@ -4950,6 +5892,8 @@ function llmJudge(name, prompt, opts) {
4950
5892
  const dimensions = normalizeDimensions(opts.dimensions, name);
4951
5893
  const scale = opts.scale ?? "unit";
4952
5894
  const divisor = scale === "ten" ? 10 : 1;
5895
+ const scoring = opts.scoring ?? { method: "sampled" };
5896
+ if (scoring.method === "expectation" && scale !== "ten") throw new Error(`llmJudge '${name}': expectation scoring requires scale 'ten' — a [0,1] score is not one token, so no single position carries its distribution`);
4953
5897
  const renderUser = opts.renderUser ?? ((input) => JSON.stringify({
4954
5898
  scenario: input.scenario,
4955
5899
  artifact: input.artifact
@@ -4983,6 +5927,7 @@ function llmJudge(name, prompt, opts) {
4983
5927
  maxTokens: opts.maxTokens ?? 800,
4984
5928
  weights: opts.weights ?? null,
4985
5929
  scale,
5930
+ scoring,
4986
5931
  jsonSchema: jsonSchema ?? null,
4987
5932
  renderUser: opts.renderUser?.toString() ?? null
4988
5933
  }),
@@ -5002,6 +5947,7 @@ function llmJudge(name, prompt, opts) {
5002
5947
  }],
5003
5948
  jsonMode: true,
5004
5949
  jsonSchema,
5950
+ ...scoring.method === "expectation" ? { logprobs: { topLogprobs: EXPECTATION_TOP_LOGPROBS } } : {},
5005
5951
  temperature: opts.temperature ?? .1,
5006
5952
  maxTokens: opts.maxTokens ?? 800
5007
5953
  };
@@ -5037,7 +5983,7 @@ function llmJudge(name, prompt, opts) {
5037
5983
  cause: /* @__PURE__ */ new Error("response has no `dimensions` object"),
5038
5984
  llmCall
5039
5985
  });
5040
- const dims = {};
5986
+ const sampled = {};
5041
5987
  for (const { key } of dimensions) {
5042
5988
  const raw = rawDims[key];
5043
5989
  const value = Number(raw);
@@ -5045,8 +5991,26 @@ function llmJudge(name, prompt, opts) {
5045
5991
  cause: /* @__PURE__ */ new Error(`dimension '${key}' missing or non-numeric (got ${JSON.stringify(raw)})`),
5046
5992
  llmCall
5047
5993
  });
5048
- dims[key] = clamp01(value / divisor);
5994
+ sampled[key] = value;
5995
+ }
5996
+ let scoringMethod = "sampled";
5997
+ let distribution;
5998
+ const dims = {};
5999
+ if (scoring.method === "expectation") {
6000
+ const expectation = expectedDimensionScores({
6001
+ tokens: response.logprobs ?? null,
6002
+ sampled,
6003
+ maxScore: divisor
6004
+ });
6005
+ if (expectation.kind === "unavailable") {
6006
+ if (scoring.whenUnavailable === "fail") throw new Error(`llmJudge '${name}': expectation scoring is unavailable — ${expectation.reason}`);
6007
+ } else {
6008
+ scoringMethod = "expectation";
6009
+ distribution = expectation.distribution;
6010
+ for (const { key } of dimensions) dims[key] = clamp01(expectation.scores[key] / divisor);
6011
+ }
5049
6012
  }
6013
+ if (scoringMethod === "sampled") for (const { key } of dimensions) dims[key] = clamp01(sampled[key] / divisor);
5050
6014
  const { composite } = weightedComposite({
5051
6015
  dims,
5052
6016
  weights: opts.weights ?? Object.fromEntries(dimensions.map((d) => [d.key, 1 / dimensions.length]))
@@ -5055,11 +6019,105 @@ function llmJudge(name, prompt, opts) {
5055
6019
  dimensions: dims,
5056
6020
  composite,
5057
6021
  notes: firstString(parsed.notes) ?? firstString(parsed.rationale) ?? `${name}: composite ${composite.toFixed(3)} over ${dimensions.length} dimension(s)`,
5058
- llmCall
6022
+ llmCall,
6023
+ ...opts.scoring ? { scoringMethod } : {},
6024
+ ...distribution ? { distribution } : {}
5059
6025
  };
5060
6026
  }
5061
6027
  };
5062
6028
  }
6029
+ /** Alternatives requested per score token. Twenty is the documented ceiling of
6030
+ * the OpenAI-compatible `top_logprobs` field, and a grade scale has at most
6031
+ * eleven integer candidates, so the window always covers the whole scale. */
6032
+ const EXPECTATION_TOP_LOGPROBS = 20;
6033
+ /**
6034
+ * Expected grade per dimension: locate the token that carried each dimension's
6035
+ * integer score, then average the integer grades the model considered at that
6036
+ * position, weighted by their probabilities.
6037
+ *
6038
+ * Unavailable — never silently approximated — when the provider returned no
6039
+ * log probabilities, when a dimension's grade is not in the token stream, or
6040
+ * when the grade did not land in exactly one token (a two-token `10`, a float).
6041
+ */
6042
+ function expectedDimensionScores(input) {
6043
+ const { tokens, sampled, maxScore } = input;
6044
+ if (tokens === null) return {
6045
+ kind: "unavailable",
6046
+ reason: "the provider returned no logprobs"
6047
+ };
6048
+ if (tokens.length === 0) return {
6049
+ kind: "unavailable",
6050
+ reason: "the provider returned an empty logprob stream"
6051
+ };
6052
+ const offsets = [];
6053
+ let text = "";
6054
+ for (const token of tokens) {
6055
+ offsets.push(text.length);
6056
+ text += token.token;
6057
+ }
6058
+ const scores = {};
6059
+ const distribution = {};
6060
+ for (const [key, value] of Object.entries(sampled)) {
6061
+ const match = new RegExp(`"${escapeRegExp(key)}"\\s*:\\s*`).exec(text);
6062
+ if (!match) return {
6063
+ kind: "unavailable",
6064
+ reason: `dimension '${key}' is not in the token stream`
6065
+ };
6066
+ const scoreOffset = match.index + match[0].length;
6067
+ let index = -1;
6068
+ for (let position = 0; position < offsets.length; position++) if (offsets[position] <= scoreOffset) index = position;
6069
+ else break;
6070
+ const token = tokens[index];
6071
+ if (!token) return {
6072
+ kind: "unavailable",
6073
+ reason: `dimension '${key}' has no token at its score position`
6074
+ };
6075
+ if (integerGrade(token.token, maxScore) !== value) return {
6076
+ kind: "unavailable",
6077
+ reason: `dimension '${key}' scored ${value}, which is not the whole of its token '${token.token}'`
6078
+ };
6079
+ const candidates = [];
6080
+ let total = 0;
6081
+ for (const alternative of token.top.length > 0 ? token.top : [token]) {
6082
+ const grade = integerGrade(alternative.token, maxScore);
6083
+ if (grade === null) continue;
6084
+ const probability = Math.exp(alternative.logprob);
6085
+ if (!Number.isFinite(probability) || probability <= 0) continue;
6086
+ candidates.push({
6087
+ score: grade,
6088
+ probability
6089
+ });
6090
+ total += probability;
6091
+ }
6092
+ if (candidates.length === 0 || total <= 0) return {
6093
+ kind: "unavailable",
6094
+ reason: `dimension '${key}' has no integer grade in its probability window`
6095
+ };
6096
+ let expectation = 0;
6097
+ for (const candidate of candidates) expectation += candidate.probability / total * candidate.score;
6098
+ scores[key] = expectation;
6099
+ distribution[key] = candidates.map((candidate) => ({
6100
+ score: candidate.score,
6101
+ probability: candidate.probability / total
6102
+ })).sort((left, right) => left.score - right.score);
6103
+ }
6104
+ return {
6105
+ kind: "expectation",
6106
+ scores,
6107
+ distribution
6108
+ };
6109
+ }
6110
+ /** The token's integer grade on the judge's scale, or null when the token is
6111
+ * not exactly one integer in range. */
6112
+ function integerGrade(token, maxScore) {
6113
+ const trimmed = token.trim();
6114
+ if (!/^\d+$/.test(trimmed)) return null;
6115
+ const value = Number(trimmed);
6116
+ return Number.isInteger(value) && value >= 0 && value <= maxScore ? value : null;
6117
+ }
6118
+ function escapeRegExp(value) {
6119
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6120
+ }
5063
6121
  function normalizeDimensions(input, name) {
5064
6122
  const raw = input && input.length > 0 ? input : ["quality"];
5065
6123
  const out = [];
@@ -5115,6 +6173,6 @@ function firstString(value) {
5115
6173
  return typeof value === "string" && value.trim() ? value : void 0;
5116
6174
  }
5117
6175
  //#endregion
5118
- export { runEval as $, surfaceHash as A, campaignMeanComposite as B, optimizationTokenUsageFromSummary as C, componentSurfaceIdentityMaterial as D, codeSurfaceIdentityMaterial as E, assertSearchHistoryMatchesReplay as F, assertGepaCandidatePopulationSummary as G, buildReflectionPrompt as H, createSearchHistoryReceipt as I, DEFAULT_RED_TEAM_CORPUS as J, readGepaCandidatePopulationArtifact as K, searchHistoryCoverageRow as L, SEARCH_HISTORY_RECEIPT_SCHEMA_VERSION as M, SearchHistoryRequiredError as N, renderSurfaceDiff as O, assertCompleteSearchHistory as P, runCanaries as Q, verifySearchHistoryReceipt as R, costFromLedgerSummary as S, assertComponentSurface as T, parseReflectionResponse as U, compareRankKeys as V, recoverTruncatedJson as W, redTeamReport as X, redTeamDataset as Y, scoreRedTeamOutput as Z, paretoFrontier as _, canonicalDigest as a, assertRealBackend as at, combineComparisonCosts as b, loopProvenanceSpans as c, assertCampaignSplitIdentity as ct, verifyLoopProvenanceRecord as d, campaignSplitDigestFromIdentities as dt, runCampaign as et, runImprovementLoop as f, readCachedCell as ft, dominates as g, labelTrustRank as h, campaignMeasurementDigest as i, BackendIntegrityError as it, SEARCH_HISTORY_RECEIPT_DIGEST_ALGORITHM as j, surfaceContentHash as k, provenanceRecordPath as l, campaignScenarioIdentity as lt, isProposedCandidate as m, computeManifestHash as mt, JudgeParseError as n, resolveRunDir as nt, emitLoopProvenance as o, summarizeBackendIntegrity as ot, runOptimization as p, cellCachePath as pt, defaultProductionGate as q, buildLoopProvenanceRecord as r, tangleTracesRoot as rt, loopProvenanceArgsFromResult as s, assertCampaignDesign as st, llmJudge as t, planCampaignRun as tt, provenanceSpansPath as u, campaignSplitDigest as ut, openAutoPr as v, assertCodeSurfaceIdentity as w, compareOptimizationMethods as x, assertOptimizationResult as y, campaignBreakdown as z };
6176
+ export { scoreRedTeamOutput as $, codeSurfaceIdentityMaterial as A, verifySearchHistoryReceipt as B, openAutoPr as C, costFromLedgerSummary as D, compareOptimizationMethods as E, SearchHistoryRequiredError as F, parseReflectionResponse as G, campaignMeanComposite as H, assertCompleteSearchHistory as I, readGepaCandidatePopulationArtifact as J, recoverTruncatedJson as K, assertSearchHistoryMatchesReplay as L, renderSurfaceDiff as M, surfaceContentHash as N, optimizationTokenUsageFromSummary as O, surfaceHash as P, redTeamReport as Q, createSearchHistoryReceipt as R, paretoFrontierWithCrowding as S, combineComparisonCosts as T, compareRankKeys as U, campaignBreakdown as V, buildReflectionPrompt as W, DEFAULT_RED_TEAM_CORPUS as X, defaultProductionGate as Y, redTeamDataset as Z, labelTrustRank as _, buildLoopProvenanceRecord as a, tangleTracesRoot as at, dominates as b, emitLoopProvenance as c, summarizeBackendIntegrity as ct, provenanceRecordPath as d, campaignScenarioIdentity as dt, runCanaries as et, provenanceSpansPath as f, campaignSplitDigest as ft, isProposedCandidate as g, computeManifestHash as gt, runOptimization as h, cellCachePath as ht, transientDispatchFailure as i, resolveRunDir as it, componentSurfaceIdentityMaterial as j, assertCodeSurfaceIdentity as k, loopProvenanceArgsFromResult as l, assertCampaignDesign as lt, runImprovementLoop as m, readCachedCell as mt, JudgeParseError as n, runCampaign as nt, campaignMeasurementDigest as o, BackendIntegrityError as ot, verifyLoopProvenanceRecord as p, campaignSplitDigestFromIdentities as pt, assertGepaCandidatePopulationSummary as q, isTransientTransportFailure as r, planCampaignRun as rt, canonicalDigest as s, assertRealBackend as st, llmJudge as t, runEval as tt, loopProvenanceSpans as u, assertCampaignSplitIdentity as ut, SearchRecorder as v, assertOptimizationResult as w, paretoFrontier as x, recordCandidatePopulationSearch as y, searchHistoryCoverageRow as z };
5119
6177
 
5120
- //# sourceMappingURL=llm-judge-BqqMS8t7.js.map
6178
+ //# sourceMappingURL=llm-judge-BhasIPFT.js.map