@tangle-network/agent-eval 1.0.12 → 1.0.13

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 (227) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/adapters/http.d.ts +2 -2
  3. package/dist/{agent-profile-CoD1Dotf.d.ts → agent-profile-CPv4tENR.d.ts} +2 -2
  4. package/dist/{agent-profile-CoD1Dotf.d.ts.map → agent-profile-CPv4tENR.d.ts.map} +1 -1
  5. package/dist/{agreement-irr-w-t3XUTD.js → agreement-irr-DXcXEt4I.js} +20 -23
  6. package/dist/agreement-irr-DXcXEt4I.js.map +1 -0
  7. package/dist/analyst/index.d.ts +59 -13
  8. package/dist/analyst/index.d.ts.map +1 -1
  9. package/dist/analyst/index.js +144 -57
  10. package/dist/analyst/index.js.map +1 -1
  11. package/dist/{analyze-runs-dGYmYNtb.js → analyze-runs-C7vWBDhl.js} +11 -11
  12. package/dist/{analyze-runs-dGYmYNtb.js.map → analyze-runs-C7vWBDhl.js.map} +1 -1
  13. package/dist/{analyze-runs-BYyMShsj.d.ts → analyze-runs-pJ4fhnYg.d.ts} +2 -2
  14. package/dist/{analyze-runs-BYyMShsj.d.ts.map → analyze-runs-pJ4fhnYg.d.ts.map} +1 -1
  15. package/dist/{benchmark-command-DZQrxqUL.js → benchmark-command-BRRhjX2k.js} +12 -14
  16. package/dist/benchmark-command-BRRhjX2k.js.map +1 -0
  17. package/dist/benchmarks/index.d.ts +4 -4
  18. package/dist/benchmarks/index.js +5 -5
  19. package/dist/builder-eval/index.js +1 -1
  20. package/dist/{calibration-DLzckXnn.js → calibration-CbZPApIO.js} +2 -2
  21. package/dist/{calibration-DLzckXnn.js.map → calibration-CbZPApIO.js.map} +1 -1
  22. package/dist/campaign/index.d.ts +6 -6
  23. package/dist/campaign/index.js +12 -12
  24. package/dist/{campaign-VKtf8HRr.js → campaign-DzLlT0So.js} +17 -17
  25. package/dist/{campaign-VKtf8HRr.js.map → campaign-DzLlT0So.js.map} +1 -1
  26. package/dist/{campaign-evidence-BICB3a9Z.js → campaign-evidence-CcA7a67m.js} +3 -3
  27. package/dist/{campaign-evidence-BICB3a9Z.js.map → campaign-evidence-CcA7a67m.js.map} +1 -1
  28. package/dist/cli.js +16 -10
  29. package/dist/cli.js.map +1 -1
  30. package/dist/{cluster-bootstrap-hNXESoc1.js → cluster-bootstrap-BteuR1hg.js} +2 -2
  31. package/dist/{cluster-bootstrap-hNXESoc1.js.map → cluster-bootstrap-BteuR1hg.js.map} +1 -1
  32. package/dist/contract/index.d.ts +9 -9
  33. package/dist/contract/index.js +14 -14
  34. package/dist/{decision-point-CErE7GnP.js → decision-point-CNyoKOkz.js} +13 -7
  35. package/dist/{decision-point-CErE7GnP.js.map → decision-point-CNyoKOkz.js.map} +1 -1
  36. package/dist/{default-registry-DKlKVXMx.d.ts → default-registry-BNv7eB1C.d.ts} +10 -7
  37. package/dist/default-registry-BNv7eB1C.d.ts.map +1 -0
  38. package/dist/{default-registry-gmDeN0KT.js → default-registry-eCkZFfuF.js} +6 -6
  39. package/dist/default-registry-eCkZFfuF.js.map +1 -0
  40. package/dist/{define-agent-eval-DyzJ0wOo.js → define-agent-eval-CjF2nTA3.js} +8 -8
  41. package/dist/{define-agent-eval-DyzJ0wOo.js.map → define-agent-eval-CjF2nTA3.js.map} +1 -1
  42. package/dist/{define-agent-eval-B9zPWFSK.d.ts → define-agent-eval-DKVYlm-T.d.ts} +7 -7
  43. package/dist/{define-agent-eval-B9zPWFSK.d.ts.map → define-agent-eval-DKVYlm-T.d.ts.map} +1 -1
  44. package/dist/{descriptive-BTzLL-aj.js → descriptive-B3Eb7tX0.js} +2 -2
  45. package/dist/{descriptive-BTzLL-aj.js.map → descriptive-B3Eb7tX0.js.map} +1 -1
  46. package/dist/diagnosis/index.d.ts +2 -2
  47. package/dist/diagnosis/index.js +1 -1
  48. package/dist/{dspy-rlm-engine-Xt3znknf.js → dspy-rlm-engine-_MEe2jIO.js} +3 -3
  49. package/dist/{dspy-rlm-engine-Xt3znknf.js.map → dspy-rlm-engine-_MEe2jIO.js.map} +1 -1
  50. package/dist/{engine-CaHaCc3W.d.ts → engine-rfV8Ff8V.d.ts} +2 -2
  51. package/dist/{engine-CaHaCc3W.d.ts.map → engine-rfV8Ff8V.d.ts.map} +1 -1
  52. package/dist/{eval-campaign-B-dQl_lA.js → eval-campaign-CZSCjxVb.js} +3 -3
  53. package/dist/{eval-campaign-B-dQl_lA.js.map → eval-campaign-CZSCjxVb.js.map} +1 -1
  54. package/dist/{evaluation-B0-KVXaV.d.ts → evaluation-DYAIkPQB.d.ts} +4 -4
  55. package/dist/{evaluation-B0-KVXaV.d.ts.map → evaluation-DYAIkPQB.d.ts.map} +1 -1
  56. package/dist/evaluation.d.ts +1 -1
  57. package/dist/{exact-types-CIViGj98.d.ts → exact-types-CaLLU1Pz.d.ts} +2 -2
  58. package/dist/{exact-types-CIViGj98.d.ts.map → exact-types-CaLLU1Pz.d.ts.map} +1 -1
  59. package/dist/experiment/index.d.ts +1 -1
  60. package/dist/experiment/index.js +9 -9
  61. package/dist/{external-optimizer-process-BgitfZ-Q.js → external-optimizer-process-za-SsIap.js} +2 -2
  62. package/dist/{external-optimizer-process-BgitfZ-Q.js.map → external-optimizer-process-za-SsIap.js.map} +1 -1
  63. package/dist/{external-optimizer-subprocess-CeHuAaMJ.js → external-optimizer-subprocess-B44kOfZb.js} +3 -3
  64. package/dist/external-optimizer-subprocess-B44kOfZb.js.map +1 -0
  65. package/dist/{feedback-trajectory-BgomnFOe.d.ts → feedback-trajectory-P37ju-gW.d.ts} +3 -3
  66. package/dist/{feedback-trajectory-BgomnFOe.d.ts.map → feedback-trajectory-P37ju-gW.d.ts.map} +1 -1
  67. package/dist/{finding-composition-DlvE1Qul.js → finding-composition-Belh_qU3.js} +12 -3
  68. package/dist/finding-composition-Belh_qU3.js.map +1 -0
  69. package/dist/{findings-store-Bp-0Guuv.js → findings-store-DWiBvJVM.js} +22 -5
  70. package/dist/findings-store-DWiBvJVM.js.map +1 -0
  71. package/dist/fuzz.js +1 -1
  72. package/dist/hosted/index.js +1 -1
  73. package/dist/{index-BwutH5i7.d.ts → index-BJM4rwP2.d.ts} +6 -6
  74. package/dist/{index-BwutH5i7.d.ts.map → index-BJM4rwP2.d.ts.map} +1 -1
  75. package/dist/{index-DAk2quYu.d.ts → index-C3t17kHu.d.ts} +16 -21
  76. package/dist/index-C3t17kHu.d.ts.map +1 -0
  77. package/dist/{index-Bn-P_XAl.d.ts → index-Dt6E4BHX.d.ts} +6 -6
  78. package/dist/{index-Bn-P_XAl.d.ts.map → index-Dt6E4BHX.d.ts.map} +1 -1
  79. package/dist/{index-ByJ20lyu.d.ts → index-chNyKroE.d.ts} +8 -8
  80. package/dist/{index-ByJ20lyu.d.ts.map → index-chNyKroE.d.ts.map} +1 -1
  81. package/dist/{index-CtcCSO1Y.d.ts → index-f09MX_H_.d.ts} +2 -2
  82. package/dist/{index-CtcCSO1Y.d.ts.map → index-f09MX_H_.d.ts.map} +1 -1
  83. package/dist/index.d.ts +52 -146
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +80 -204
  86. package/dist/index.js.map +1 -1
  87. package/dist/{integrity-B1V6klyO.js → integrity-Vx5xgn3s.js} +2 -2
  88. package/dist/{integrity-B1V6klyO.js.map → integrity-Vx5xgn3s.js.map} +1 -1
  89. package/dist/{judge-calibration-D93UCm1k.js → judge-calibration-DKR6Xi-w.js} +13 -36
  90. package/dist/judge-calibration-DKR6Xi-w.js.map +1 -0
  91. package/dist/judge-calibration-DnrbQaXI.d.ts.map +1 -1
  92. package/dist/{judge-gate-DMlX38B7.js → judge-gate-CIlBrn6q.js} +2 -2
  93. package/dist/{judge-gate-DMlX38B7.js.map → judge-gate-CIlBrn6q.js.map} +1 -1
  94. package/dist/{kind-factory-VKysp1ds.js → kind-factory-Cfz8DUCc.js} +2 -2
  95. package/dist/{kind-factory-VKysp1ds.js.map → kind-factory-Cfz8DUCc.js.map} +1 -1
  96. package/dist/{llm-judge-CyDyqb6V.js → llm-judge-CHLvjbVR.js} +14 -14
  97. package/dist/{llm-judge-CyDyqb6V.js.map → llm-judge-CHLvjbVR.js.map} +1 -1
  98. package/dist/{matrix-4DcRFJ7Q.d.ts → matrix-Cy3oy7Rq.d.ts} +2 -2
  99. package/dist/{matrix-4DcRFJ7Q.d.ts.map → matrix-Cy3oy7Rq.d.ts.map} +1 -1
  100. package/dist/{mean-DDFhslBt.js → mean-D1d6gedE.js} +28 -2
  101. package/dist/{mean-DDFhslBt.js.map → mean-D1d6gedE.js.map} +1 -1
  102. package/dist/meta-eval/index.d.ts +1 -1
  103. package/dist/meta-eval/index.js +6 -6
  104. package/dist/{mint-D9ZysYtY.js → mint-BH_3Ak2x.js} +3 -3
  105. package/dist/{mint-D9ZysYtY.js.map → mint-BH_3Ak2x.js.map} +1 -1
  106. package/dist/multishot/golden/index.d.ts +1 -1
  107. package/dist/multishot/index.d.ts +2 -2
  108. package/dist/openapi.json +1 -1
  109. package/dist/{outcome-observations-ahbNtxuP.js → outcome-observations-DZu5hLYF.js} +3 -3
  110. package/dist/{outcome-observations-ahbNtxuP.js.map → outcome-observations-DZu5hLYF.js.map} +1 -1
  111. package/dist/{paired-arms-C0Vd9bGG.js → paired-arms-3-tsXvvC.js} +5 -5
  112. package/dist/{paired-arms-C0Vd9bGG.js.map → paired-arms-3-tsXvvC.js.map} +1 -1
  113. package/dist/{paired-binary-B477lksS.js → paired-binary-CMzmSSd5.js} +2 -2
  114. package/dist/{paired-binary-B477lksS.js.map → paired-binary-CMzmSSd5.js.map} +1 -1
  115. package/dist/{paired-promotion-decision-5Pl_qJLX.js → paired-promotion-decision-CiueaTTj.js} +3 -3
  116. package/dist/{paired-promotion-decision-5Pl_qJLX.js.map → paired-promotion-decision-CiueaTTj.js.map} +1 -1
  117. package/dist/{paired-tests-kASEKpc-.js → paired-tests-Buh6feJ4.js} +2 -2
  118. package/dist/{paired-tests-kASEKpc-.js.map → paired-tests-Buh6feJ4.js.map} +1 -1
  119. package/dist/{pareto-DY_x3HHg.js → pareto-B34N-vnn.js} +7 -7
  120. package/dist/{pareto-DY_x3HHg.js.map → pareto-B34N-vnn.js.map} +1 -1
  121. package/dist/pipelines/index.js +1 -1
  122. package/dist/{power-and-mde-CRxepy8y.js → power-and-mde-g3vOn0XK.js} +3 -3
  123. package/dist/{power-and-mde-CRxepy8y.js.map → power-and-mde-g3vOn0XK.js.map} +1 -1
  124. package/dist/{prime-protocol-COFkxas6.d.ts → prime-protocol-DKRk4l4U.d.ts} +2 -2
  125. package/dist/{prime-protocol-COFkxas6.d.ts.map → prime-protocol-DKRk4l4U.d.ts.map} +1 -1
  126. package/dist/{promotion-policy-f79oklG_.js → promotion-policy-DLPcEnCf.js} +4 -4
  127. package/dist/{promotion-policy-f79oklG_.js.map → promotion-policy-DLPcEnCf.js.map} +1 -1
  128. package/dist/{proposal-findings-BclX0PFh.js → proposal-findings-CZJkS_GY.js} +2 -2
  129. package/dist/{proposal-findings-BclX0PFh.js.map → proposal-findings-CZJkS_GY.js.map} +1 -1
  130. package/dist/{registry-CMtGAxOw.d.ts → registry-Ck7w-rr5.d.ts} +3 -3
  131. package/dist/{registry-CMtGAxOw.d.ts.map → registry-Ck7w-rr5.d.ts.map} +1 -1
  132. package/dist/{release-confidence--1iP-d3x.js → release-confidence-nfQNh3eb.js} +4 -4
  133. package/dist/{release-confidence--1iP-d3x.js.map → release-confidence-nfQNh3eb.js.map} +1 -1
  134. package/dist/{render-CFrJ7M0o.js → render-BVPfP7dc.js} +52 -36
  135. package/dist/{render-CFrJ7M0o.js.map → render-BVPfP7dc.js.map} +1 -1
  136. package/dist/reporting.js +5 -5
  137. package/dist/{reward-P0VD_Aft.js → reward-DqMfaEax.js} +10 -2
  138. package/dist/{reward-P0VD_Aft.js.map → reward-DqMfaEax.js.map} +1 -1
  139. package/dist/{reward-hacking-D-Vi9-fE.js → reward-hacking-B88f-Kh7.js} +4 -4
  140. package/dist/{reward-hacking-D-Vi9-fE.js.map → reward-hacking-B88f-Kh7.js.map} +1 -1
  141. package/dist/rl.d.ts +3 -3
  142. package/dist/rl.js +12 -12
  143. package/dist/rollout/index.d.ts +2 -2
  144. package/dist/rollout/index.js +3 -3
  145. package/dist/{rollout-BC4u-d_I.js → rollout-CbOGxuHl.js} +2 -2
  146. package/dist/{rollout-BC4u-d_I.js.map → rollout-CbOGxuHl.js.map} +1 -1
  147. package/dist/{rollout-nodes-BHR9Q5jP.js → rollout-nodes-DKZ3vAYY.js} +2 -2
  148. package/dist/{rollout-nodes-BHR9Q5jP.js.map → rollout-nodes-DKZ3vAYY.js.map} +1 -1
  149. package/dist/{rubric-predictive-validity-BdxXDN6h.js → rubric-predictive-validity-D_6ZPTNR.js} +2 -2
  150. package/dist/{rubric-predictive-validity-BdxXDN6h.js.map → rubric-predictive-validity-D_6ZPTNR.js.map} +1 -1
  151. package/dist/{run-record-B-G2Tu11.js → run-record-CbUi2adH.js} +3 -3
  152. package/dist/{run-record-B-G2Tu11.js.map → run-record-CbUi2adH.js.map} +1 -1
  153. package/dist/{run-record-DEjx43cI.js → run-record-CpLS4Uid.js} +2 -2
  154. package/dist/{run-record-DEjx43cI.js.map → run-record-CpLS4Uid.js.map} +1 -1
  155. package/dist/search/index.d.ts +2 -2
  156. package/dist/search/index.js +2 -2
  157. package/dist/{search-kernel-BjR2Sr7w.d.ts → search-kernel-kwO-sgCq.d.ts} +2 -2
  158. package/dist/{search-kernel-BjR2Sr7w.d.ts.map → search-kernel-kwO-sgCq.d.ts.map} +1 -1
  159. package/dist/{search-ledger-BsVi-Ngv.js → search-ledger-CV_bQQed.js} +2 -2
  160. package/dist/{search-ledger-BsVi-Ngv.js.map → search-ledger-CV_bQQed.js.map} +1 -1
  161. package/dist/{search-shipper-pdg4viK7.js → search-shipper-CA3ooBbC.js} +2 -2
  162. package/dist/{search-shipper-pdg4viK7.js.map → search-shipper-CA3ooBbC.js.map} +1 -1
  163. package/dist/{search-summary-DedyTdiM.js → search-summary-DBMjdecs.js} +4 -4
  164. package/dist/{search-summary-DedyTdiM.js.map → search-summary-DBMjdecs.js.map} +1 -1
  165. package/dist/{sequential-Zp1tsxqI.js → sequential-BPbvgzfe.js} +2 -2
  166. package/dist/{sequential-Zp1tsxqI.js.map → sequential-BPbvgzfe.js.map} +1 -1
  167. package/dist/{skillopt-optimization-method-O35aMdxU.js → skillopt-optimization-method-D72XZZfR.js} +9 -9
  168. package/dist/{skillopt-optimization-method-O35aMdxU.js.map → skillopt-optimization-method-D72XZZfR.js.map} +1 -1
  169. package/dist/{statistical-heldout-DYF5f10K.js → statistical-heldout-NBxC7jGo.js} +3 -3
  170. package/dist/{statistical-heldout-DYF5f10K.js.map → statistical-heldout-NBxC7jGo.js.map} +1 -1
  171. package/dist/{statistical-heldout-bU7FBNjR.d.ts → statistical-heldout-_dXihc9T.d.ts} +3 -3
  172. package/dist/{statistical-heldout-bU7FBNjR.d.ts.map → statistical-heldout-_dXihc9T.d.ts.map} +1 -1
  173. package/dist/{store-otlp-DmLm65XW.js → store-otlp-BfPRa2p8.js} +2 -2
  174. package/dist/{store-otlp-DmLm65XW.js.map → store-otlp-BfPRa2p8.js.map} +1 -1
  175. package/dist/{store-tool-spans-lWf6INTH.d.ts → store-tool-spans-BgwVHXma.d.ts} +4 -4
  176. package/dist/{store-tool-spans-lWf6INTH.d.ts.map → store-tool-spans-BgwVHXma.d.ts.map} +1 -1
  177. package/dist/{store-tool-spans-GGaCijFy.js → store-tool-spans-BrSWHB9O.js} +3 -3
  178. package/dist/{store-tool-spans-GGaCijFy.js.map → store-tool-spans-BrSWHB9O.js.map} +1 -1
  179. package/dist/{summary-report-B4BLQb88.js → summary-report-C3IJvp0i.js} +7 -7
  180. package/dist/{summary-report-B4BLQb88.js.map → summary-report-C3IJvp0i.js.map} +1 -1
  181. package/dist/supervisor-run/index.d.ts +6 -0
  182. package/dist/supervisor-run/index.d.ts.map +1 -1
  183. package/dist/supervisor-run/index.js +3 -3
  184. package/dist/{systemone-BDVgb6XV.d.ts → systemone-CBvL1gx-.d.ts} +14 -8
  185. package/dist/systemone-CBvL1gx-.d.ts.map +1 -0
  186. package/dist/{systemone-review-S49gsy_y.js → systemone-review-BusEIzsg.js} +2 -2
  187. package/dist/{systemone-review-S49gsy_y.js.map → systemone-review-BusEIzsg.js.map} +1 -1
  188. package/dist/{systemone-review-BLgxt-4Q.d.ts → systemone-review-nbuMxxmi.d.ts} +2 -2
  189. package/dist/{systemone-review-BLgxt-4Q.d.ts.map → systemone-review-nbuMxxmi.d.ts.map} +1 -1
  190. package/dist/systemone.d.ts +4 -4
  191. package/dist/systemone.js +3 -3
  192. package/dist/{tool-groups-C3K-d3gd.d.ts → tool-groups-BQsjZD9F.d.ts} +3 -3
  193. package/dist/tool-groups-BQsjZD9F.d.ts.map +1 -0
  194. package/dist/{tool-waste-CRJqOASv.js → tool-waste-Czt3aWID.js} +3 -3
  195. package/dist/{tool-waste-CRJqOASv.js.map → tool-waste-Czt3aWID.js.map} +1 -1
  196. package/dist/{trace-questions-ej5AEJ5X.d.ts → trace-questions-CpTrtEcm.d.ts} +3 -3
  197. package/dist/{trace-questions-ej5AEJ5X.d.ts.map → trace-questions-CpTrtEcm.d.ts.map} +1 -1
  198. package/dist/trace-repair/index.d.ts +1 -1
  199. package/dist/trace-repair/index.js +2 -2
  200. package/dist/traces.d.ts +3 -3
  201. package/dist/traces.js +4 -4
  202. package/dist/{tree-C2N31CoI.js → tree-DPwgM2LX.js} +6 -6
  203. package/dist/{tree-C2N31CoI.js.map → tree-DPwgM2LX.js.map} +1 -1
  204. package/dist/{types-DzCO-S1m.d.ts → types-B9s9Xo5_.d.ts} +24 -2
  205. package/dist/types-B9s9Xo5_.d.ts.map +1 -0
  206. package/dist/{types-BF-xQGQn.d.ts → types-CBkDo7ja.d.ts} +2 -2
  207. package/dist/{types-BF-xQGQn.d.ts.map → types-CBkDo7ja.d.ts.map} +1 -1
  208. package/dist/{types-BK0blWhe.js → types-CL1_N7AZ.js} +2 -2
  209. package/dist/{types-BK0blWhe.js.map → types-CL1_N7AZ.js.map} +1 -1
  210. package/dist/{types-yofaDmQa.d.ts → types-D91tnhzf.d.ts} +2 -2
  211. package/dist/{types-yofaDmQa.d.ts.map → types-D91tnhzf.d.ts.map} +1 -1
  212. package/dist/wire/index.d.ts +1 -1
  213. package/docs/behavioral-trace-questions.md +23 -0
  214. package/docs/public-api.md +1 -1
  215. package/package.json +2 -2
  216. package/dist/agreement-irr-w-t3XUTD.js.map +0 -1
  217. package/dist/benchmark-command-DZQrxqUL.js.map +0 -1
  218. package/dist/default-registry-DKlKVXMx.d.ts.map +0 -1
  219. package/dist/default-registry-gmDeN0KT.js.map +0 -1
  220. package/dist/external-optimizer-subprocess-CeHuAaMJ.js.map +0 -1
  221. package/dist/finding-composition-DlvE1Qul.js.map +0 -1
  222. package/dist/findings-store-Bp-0Guuv.js.map +0 -1
  223. package/dist/index-DAk2quYu.d.ts.map +0 -1
  224. package/dist/judge-calibration-D93UCm1k.js.map +0 -1
  225. package/dist/systemone-BDVgb6XV.d.ts.map +0 -1
  226. package/dist/tool-groups-C3K-d3gd.d.ts.map +0 -1
  227. package/dist/types-DzCO-S1m.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { o as SearchLedgerIntegrityError } from "./search-ledger-file-DTBD6X-y.js";
2
- import { r as parseSearchLedgerLine } from "./search-ledger-BsVi-Ngv.js";
2
+ import { r as parseSearchLedgerLine } from "./search-ledger-CV_bQQed.js";
3
3
  import { createHash } from "node:crypto";
4
4
  import { z } from "zod";
5
5
  import { fileURLToPath } from "node:url";
@@ -770,4 +770,4 @@ async function shipBestEffort(ship, ledgerPath) {
770
770
  }
771
771
  export { IngestTracesRequestSchema as _, createHostedClient as a, HOSTED_WIRE_VERSION as b, IngestSearchLedgerRequestSchema as c, SearchBlobPutResponseSchema as d, SearchLedgerConflictSchema as f, IngestResponseSchema as g, admitSearchLedgerBatch as h, startSearchShipper as i, SEARCH_LEDGER_BATCH_MAX_BYTES as l, SearchRunKindSchema as m, shipBestEffort as n, hostedClientFromEnv as o, SearchLedgerHeadSchema as p, shipSearchLedger as r, hostedTenantFromEnv as s, SearchShipConflictError as t, SEARCH_LEDGER_INGEST_PATH as u, TraceSpanEventSchema as v, UnixNanoTimestampSchema as y };
772
772
 
773
- //# sourceMappingURL=search-shipper-pdg4viK7.js.map
773
+ //# sourceMappingURL=search-shipper-CA3ooBbC.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-shipper-pdg4viK7.js","names":[],"sources":["../src/hosted/types.ts","../src/hosted/schemas.ts","../src/hosted/search-ledger-wire.ts","../src/hosted/client.ts","../src/hosted/search-shipper.ts"],"sourcesContent":["/**\n * # Hosted-tier wire format: the shapes every orchestrator (ours, a partner's\n * self-hosted one, a future open implementation) must accept.\n *\n * This package implements exactly one wire version. Servers reject every\n * other version instead of translating old payloads.\n *\n * The wire carries two streams in one transport:\n *\n * 1. **Search ledgers** (`/v1/search-blobs/*`, `/v1/ingest/search-ledger`).\n * A producer ships a search's hash-chained ledger lines and the blobs\n * they name; `./search-ledger-wire.ts` owns those shapes.\n *\n * 2. **Trace spans** (`POST /v1/ingest/traces`). Standard OTLP-shaped\n * spans with a few additional attributes so the orchestrator can\n * pivot from a search cell to its underlying execution. Compatible\n * with any OTel collector.\n *\n * Every endpoint is authenticated with a bearer token and a tenant id\n * header. Tenants isolate everything downstream of ingest; no tenant\n * ever sees another tenant's data.\n */\n\nexport const HOSTED_WIRE_VERSION = '2026-07-24.v1' as const\nexport type HostedWireVersion = typeof HOSTED_WIRE_VERSION\n\n// ── Transport headers ───────────────────────────────────────────────\n\n/** Every ingest request carries these. */\nexport interface HostedIngestHeaders {\n /** Bearer token. The orchestrator validates against the tenant key. */\n authorization: `Bearer ${string}`\n /** Stable tenant id (the orchestrator-side primary key for the tenant). */\n 'x-tangle-tenant-id': string\n /** Wire-version pin so the server can reject incompatible payloads. */\n 'x-tangle-wire-version': HostedWireVersion\n /** Stable request key generated once and reused across retries. */\n 'idempotency-key': string\n}\n\n// ── Trace span event ────────────────────────────────────────────────\n\n/**\n * Canonical unsigned 64-bit integer encoded as a base-10 string.\n * JSON numbers cannot represent OTLP nanosecond timestamps exactly.\n */\nexport type UnixNanoTimestamp = string\n\n/**\n * OTel-shape span with a few additional attributes for pivoting.\n * Compatible with any OTLP collector — `name`, `traceId`, `spanId`,\n * `startTimeUnixNano`, `endTimeUnixNano`, `attributes` are stock OTel.\n */\nexport interface TraceSpanEvent {\n traceId: string\n spanId: string\n parentSpanId?: string\n name: string\n startTimeUnixNano: UnixNanoTimestamp\n endTimeUnixNano: UnixNanoTimestamp\n attributes: Record<string, string | number | boolean>\n events?: Array<{\n timeUnixNano: UnixNanoTimestamp\n name: string\n attributes?: Record<string, string | number | boolean>\n }>\n status?: { code: 'OK' | 'ERROR' | 'UNSET'; message?: string }\n /** Pivot to the producing run. */\n 'tangle.runId'?: string\n /** Pivot to the specific generation. */\n 'tangle.generation'?: number\n /** Pivot to the specific cell. */\n 'tangle.cellId'?: string\n /** Pivot to the specific scenario. */\n 'tangle.scenarioId'?: string\n}\n\n// ── Ingest request bodies ───────────────────────────────────────────\n\nexport interface IngestTracesRequest {\n wireVersion: HostedWireVersion\n spans: TraceSpanEvent[]\n}\n\nexport interface IngestResponse {\n /** Accepted spans count. */\n accepted: number\n /** Rejected spans with reasons (validation failures, conflicting identity). */\n rejected: Array<{ index: number; reason: string }>\n}\n","import { z } from 'zod'\nimport type {\n IngestResponse,\n IngestTracesRequest,\n TraceSpanEvent,\n UnixNanoTimestamp,\n} from './types'\nimport { HOSTED_WIRE_VERSION } from './types'\n\nconst finiteNumber = z.number().finite()\nconst nonNegativeInteger = z.number().int().nonnegative()\nconst nonEmptyString = z\n .string()\n .min(1)\n .refine((value) => value.trim().length > 0, 'must not be blank')\nconst attributeValue = z.union([z.string(), finiteNumber, z.boolean()])\nconst attributes = z.record(z.string(), attributeValue)\nconst UINT64_MAX = 18_446_744_073_709_551_615n\n\nexport const UnixNanoTimestampSchema: z.ZodType<UnixNanoTimestamp> = z\n .string()\n .regex(/^(0|[1-9][0-9]*)$/, 'expected an unsigned base-10 integer string')\n .refine((value) => BigInt(value) <= UINT64_MAX, 'must fit in an unsigned 64-bit integer')\n\nconst TraceSpanEventEntrySchema = z\n .object({\n timeUnixNano: UnixNanoTimestampSchema,\n name: nonEmptyString,\n attributes: attributes.optional(),\n })\n .strict()\n\nexport const TraceSpanEventSchema: z.ZodType<TraceSpanEvent> = z\n .object({\n traceId: nonEmptyString,\n spanId: nonEmptyString,\n parentSpanId: nonEmptyString.optional(),\n name: nonEmptyString,\n startTimeUnixNano: UnixNanoTimestampSchema,\n endTimeUnixNano: UnixNanoTimestampSchema,\n attributes,\n events: z.array(TraceSpanEventEntrySchema).optional(),\n status: z\n .object({\n code: z.enum(['OK', 'ERROR', 'UNSET']),\n message: z.string().optional(),\n })\n .strict()\n .optional(),\n 'tangle.runId': nonEmptyString.optional(),\n 'tangle.generation': nonNegativeInteger.optional(),\n 'tangle.cellId': nonEmptyString.optional(),\n 'tangle.scenarioId': nonEmptyString.optional(),\n })\n .strict()\n .refine((span) => BigInt(span.endTimeUnixNano) >= BigInt(span.startTimeUnixNano), {\n path: ['endTimeUnixNano'],\n message: 'endTimeUnixNano must be greater than or equal to startTimeUnixNano',\n })\n\nexport const IngestTracesEnvelopeSchema = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n spans: z.array(z.unknown()),\n })\n .strict()\n\nexport const IngestTracesRequestSchema: z.ZodType<IngestTracesRequest> = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n spans: z.array(TraceSpanEventSchema),\n })\n .strict()\n\nexport const IngestResponseSchema: z.ZodType<IngestResponse> = z\n .object({\n accepted: nonNegativeInteger,\n rejected: z.array(\n z\n .object({\n index: nonNegativeInteger,\n reason: nonEmptyString,\n })\n .strict(),\n ),\n })\n .strict()\n","/**\n * The search-ledger wire: how a producer ships a search ledger to a hosted\n * store, and the rule a store applies to each batch.\n *\n * A producer ships three things, in this order:\n *\n * 1. Blobs: `PUT /v1/search-blobs/<sha256 hex>` with the exact bytes an\n * entry's artifact ref names. The store re-hashes them; the call is\n * idempotent by content.\n * 2. Head: `GET /v1/ingest/search-ledger/<searchId>/head` tells a restarted\n * producer where the store's copy of the chain ends.\n * 3. Entries: `POST /v1/ingest/search-ledger` with canonical ledger lines,\n * starting at `fromSequence`. The store verifies every line, links it to\n * its stored head, and answers with its new head.\n *\n * The lines are the ledger's own bytes, so producer and store hash identical\n * input. A line below the store's head with an equal hash is a no-op; a\n * different hash is a fork (`409 chain_conflict`); a batch that starts past\n * the head is a gap (`409 sequence_gap`). Both 409s carry the store's head,\n * so a producer resends from there or stops on a fork.\n */\n\nimport { z } from 'zod'\nimport { parseSearchLedgerLine } from '../campaign/search-ledger'\nimport { SearchLedgerIntegrityError } from '../campaign/search-ledger-errors'\nimport type {\n SearchArtifactRef,\n SearchLedgerEntry,\n SearchLedgerEvent,\n SearchLedgerHash,\n} from '../campaign/search-ledger-types'\nimport { HOSTED_WIRE_VERSION } from './types'\n\n/** At most this many lines in one ingest request. */\nexport const SEARCH_LEDGER_BATCH_MAX_LINES = 1000\n/** At most this many UTF-8 bytes across one request's lines. A single line\n * larger than this cannot be shipped. */\nexport const SEARCH_LEDGER_BATCH_MAX_BYTES = 1_048_576\n\nexport const SEARCH_LEDGER_INGEST_PATH = '/v1/ingest/search-ledger'\n\nexport function searchLedgerHeadPath(searchId: string): string {\n return `${SEARCH_LEDGER_INGEST_PATH}/${encodeURIComponent(searchId)}/head`\n}\n\n/** Blobs are addressed by the hex digest alone. */\nexport function searchBlobPath(sha256: SearchLedgerHash): string {\n return `/v1/search-blobs/${sha256.slice('sha256:'.length)}`\n}\n\nconst HASH = z.custom<SearchLedgerHash>(\n (value) => typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value),\n 'expected sha256:<64 lowercase hex characters>',\n)\nconst SEARCH_ID = z\n .string()\n .min(1)\n .refine((value) => value.trim() === value, 'must not contain surrounding whitespace')\nconst SEQUENCE = z.number().int().nonnegative().safe()\n\n/** What the run is for: an optimization, or an eval (a one-node search). */\nexport const SearchRunKindSchema = z.enum(['optimization', 'eval'])\nexport type SearchRunKind = z.infer<typeof SearchRunKindSchema>\n\n/** Where a store's copy of one search's chain ends. `headHash` is null exactly\n * when the store holds no entry. */\nexport const SearchLedgerHeadSchema = z\n .object({ searchId: SEARCH_ID, nextSequence: SEQUENCE, headHash: HASH.nullable() })\n .strict()\n .refine((head) => (head.nextSequence === 0) === (head.headHash === null), {\n message: 'headHash must be null exactly when nextSequence is 0',\n path: ['headHash'],\n })\nexport type SearchLedgerHead = z.infer<typeof SearchLedgerHeadSchema>\n\nexport const IngestSearchLedgerRequestSchema = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n searchId: SEARCH_ID,\n runKind: SearchRunKindSchema,\n fromSequence: SEQUENCE,\n lines: z.array(z.string().min(1)).min(1).max(SEARCH_LEDGER_BATCH_MAX_LINES),\n })\n .strict()\n .superRefine((request, ctx) => {\n const bytes = searchLedgerLinesByteLength(request.lines)\n if (bytes > SEARCH_LEDGER_BATCH_MAX_BYTES) {\n ctx.addIssue({\n code: 'custom',\n path: ['lines'],\n message: `lines hold ${bytes} bytes; at most ${SEARCH_LEDGER_BATCH_MAX_BYTES} per request`,\n })\n }\n })\nexport type IngestSearchLedgerRequest = z.infer<typeof IngestSearchLedgerRequestSchema>\n\n/** The body of a `409`: a fork or a gap, with the store's head. */\nexport const SearchLedgerConflictSchema = z\n .object({\n error: z.enum(['chain_conflict', 'sequence_gap']),\n message: z.string().min(1),\n head: SearchLedgerHeadSchema,\n })\n .strict()\nexport type SearchLedgerConflict = z.infer<typeof SearchLedgerConflictSchema>\n\n/** What a store kept of one blob. `masked` and `withheld` come from the\n * store's own redaction scan or the tenant's content posture. */\nexport const SearchBlobPutResponseSchema = z\n .object({\n sha256: HASH,\n byteLength: SEQUENCE,\n state: z.enum(['stored', 'masked', 'withheld']),\n })\n .strict()\nexport type SearchBlobPutResponse = z.infer<typeof SearchBlobPutResponseSchema>\n\nconst UTF8 = new TextEncoder()\n\nfunction searchLedgerLinesByteLength(lines: readonly string[]): number {\n let bytes = 0\n for (const line of lines) bytes += UTF8.encode(line).byteLength\n return bytes\n}\n\n/** Every blob an event names, once each. Ship these before the event. */\nexport function searchLedgerArtifactRefs(event: SearchLedgerEvent): SearchArtifactRef[] {\n const refs: SearchArtifactRef[] = [...event.artifacts]\n switch (event.kind) {\n case 'node-registered':\n refs.push(event.artifact, ...event.surfaces.map((surface) => surface.artifact))\n break\n case 'edge-recorded':\n for (const ref of [event.rationale, ...event.diffs]) if ('uri' in ref) refs.push(ref)\n break\n case 'cell-settled':\n for (const evidence of event.surfaceEvidence) refs.push(...evidence.evidence)\n break\n default:\n break\n }\n const unique = new Map<string, SearchArtifactRef>()\n for (const ref of refs) if (!unique.has(ref.sha256)) unique.set(ref.sha256, ref)\n return [...unique.values()]\n}\n\nexport type SearchLedgerBatchAdmission =\n | {\n status: 'accepted'\n /** Entries past the stored head, in order, with their exact lines. */\n entries: SearchLedgerEntry[]\n lines: string[]\n /** The head after storing `entries`. */\n head: SearchLedgerHead\n }\n | { status: 'conflict'; conflict: SearchLedgerConflict }\n\nexport interface AdmitSearchLedgerBatchInput {\n request: IngestSearchLedgerRequest\n /** The store's head for `request.searchId` before this batch. */\n head: SearchLedgerHead\n /** The stored hash at a sequence below the head. */\n storedEntryHash(sequence: number): SearchLedgerHash | undefined\n}\n\n/**\n * Apply the wire's chain rule to one batch. Each line is verified in isolation\n * (`parseSearchLedgerLine`) and linked to the stored head. A malformed line\n * throws `SearchLedgerIntegrityError`, which a store answers with 422; the\n * state machine (`SearchState`) runs after admission, on `entries`.\n */\nexport function admitSearchLedgerBatch(\n input: AdmitSearchLedgerBatchInput,\n): SearchLedgerBatchAdmission {\n const { request, head } = input\n if (request.searchId !== head.searchId) {\n throw new Error(`head is for search ${head.searchId}, request is for ${request.searchId}`)\n }\n if (request.fromSequence > head.nextSequence) {\n return conflict(\n 'sequence_gap',\n `batch starts at sequence ${request.fromSequence}; the store expects ${head.nextSequence}`,\n head,\n )\n }\n const entries: SearchLedgerEntry[] = []\n const lines: string[] = []\n let previousHash = head.headHash\n for (let index = 0; index < request.lines.length; index++) {\n const sequence = request.fromSequence + index\n const line = request.lines[index]!\n const entry = parseSearchLedgerLine(line, request.searchId, {\n path: `search ${request.searchId}`,\n line: sequence + 1,\n })\n if (entry.sequence !== sequence) {\n throw new SearchLedgerIntegrityError(\n `line ${index} of the batch holds sequence ${entry.sequence}, expected ${sequence}`,\n )\n }\n if (sequence < head.nextSequence) {\n if (input.storedEntryHash(sequence) !== entry.entryHash) {\n return conflict(\n 'chain_conflict',\n `sequence ${sequence} differs from the stored entry; the ledger forked`,\n head,\n )\n }\n continue\n }\n if (entry.previousHash !== previousHash) {\n if (sequence === head.nextSequence) {\n return conflict(\n 'chain_conflict',\n `sequence ${sequence} does not extend the stored head; the ledger forked`,\n head,\n )\n }\n throw new SearchLedgerIntegrityError(\n `sequence ${sequence} does not extend sequence ${sequence - 1} in the same batch`,\n )\n }\n entries.push(entry)\n lines.push(line)\n previousHash = entry.entryHash\n }\n const last = entries.at(-1)\n return {\n status: 'accepted',\n entries,\n lines,\n head: last\n ? { searchId: head.searchId, nextSequence: last.sequence + 1, headHash: last.entryHash }\n : head,\n }\n}\n\nfunction conflict(\n error: SearchLedgerConflict['error'],\n message: string,\n head: SearchLedgerHead,\n): SearchLedgerBatchAdmission {\n return { status: 'conflict', conflict: { error, message, head } }\n}\n","/**\n * # Hosted-tier ingest client.\n *\n * Ships search ledgers and trace spans to any orchestrator (ours, a\n * partner's self-hosted one, or a future open implementation) that speaks\n * the wire format in `./types.ts` and `./search-ledger-wire.ts`.\n *\n * Three modes:\n * - **Ours:** point at `https://orchestrator.tangle.tools` (the host root —\n * the client appends the versioned `/v1/...` path itself; a trailing\n * `/v1` on the endpoint is tolerated and normalized away).\n * - **Self-hosted:** point at whatever URL runs the reference receiver\n * from `examples/hosted-ingest-server/`.\n * - **Off (default):** when no tenant is configured, nothing is sent.\n * Everything stays local.\n *\n * Every call retries network errors, 408, 429 and 5xx. When the server sends\n * `Retry-After`, the client waits exactly that long before the next attempt.\n */\n\nimport type { z } from 'zod'\nimport { IngestResponseSchema, IngestTracesRequestSchema } from './schemas'\nimport {\n type IngestSearchLedgerRequest,\n IngestSearchLedgerRequestSchema,\n SEARCH_LEDGER_INGEST_PATH,\n type SearchBlobPutResponse,\n SearchBlobPutResponseSchema,\n type SearchLedgerConflict,\n SearchLedgerConflictSchema,\n type SearchLedgerHead,\n SearchLedgerHeadSchema,\n searchBlobPath,\n searchLedgerHeadPath,\n} from './search-ledger-wire'\nimport {\n HOSTED_WIRE_VERSION,\n type HostedWireVersion,\n type IngestResponse,\n type IngestTracesRequest,\n type TraceSpanEvent,\n} from './types'\n\nexport interface HostedTenant {\n /** Orchestrator endpoint base URL (no trailing slash). Required. */\n endpoint: string\n /** Bearer token issued by the orchestrator. Required. */\n apiKey: string\n /** Tenant id — the orchestrator's primary key for this consumer. Required. */\n tenantId: string\n /** Optional `fetch` override (auth wrappers, custom agent). */\n fetchImpl?: typeof fetch\n /** Per-attempt timeout in ms. Default 30s. */\n timeoutMs?: number\n /** Retries on network errors, 408, 429 and 5xx. Default 2. */\n retries?: number\n}\n\n/** A search-ledger batch the store accepted, or the fork or gap it refused. */\nexport type SearchLedgerIngestOutcome =\n | { status: 'accepted'; head: SearchLedgerHead }\n | { status: 'conflict'; conflict: SearchLedgerConflict }\n\nexport interface HostedClient {\n ingestTraces(spans: TraceSpanEvent[], idempotencyKey?: string): Promise<IngestResponse>\n /** The store's head for one search; `nextSequence` 0 when it holds nothing. */\n searchLedgerHead(searchId: string, signal?: AbortSignal): Promise<SearchLedgerHead>\n ingestSearchLedger(\n request: IngestSearchLedgerRequest,\n signal?: AbortSignal,\n ): Promise<SearchLedgerIngestOutcome>\n putSearchBlob(\n sha256: `sha256:${string}`,\n bytes: Uint8Array<ArrayBuffer>,\n contentType: string,\n signal?: AbortSignal,\n ): Promise<SearchBlobPutResponse>\n readonly tenant: HostedTenant\n readonly wireVersion: HostedWireVersion\n}\n\n/** A request the server refused, after any retries. */\nclass HostedRequestError extends Error {\n readonly status: number\n readonly body: string\n constructor(url: string, status: number, body: string) {\n super(`hosted ${url} failed (${status}): ${body.slice(0, 500)}`)\n this.name = 'HostedRequestError'\n this.status = status\n this.body = body\n }\n}\n\ninterface HostedRequest {\n method: 'GET' | 'POST' | 'PUT'\n path: string\n body?: string | Uint8Array<ArrayBuffer>\n contentType?: string\n idempotencyKey?: string\n signal?: AbortSignal\n /** Statuses returned to the caller instead of thrown. */\n expected?: readonly number[]\n}\n\nconst MAX_IDEMPOTENCY_KEY_LENGTH = 256\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason)\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timer)\n reject(signal!.reason)\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/** `Retry-After` as milliseconds: delay-seconds or an HTTP date. Null when\n * absent or unreadable, so the caller falls back to its own backoff. */\nfunction retryAfterMs(value: string | null, now = Date.now()): number | null {\n if (value === null) return null\n const trimmed = value.trim()\n if (/^\\d+$/.test(trimmed)) return Number(trimmed) * 1000\n const at = Date.parse(trimmed)\n return Number.isFinite(at) ? Math.max(0, at - now) : null\n}\n\nfunction backoffMs(attempt: number): number {\n return 2 ** attempt * 200 + Math.random() * 200\n}\n\nfunction normalizeHostedBase(endpoint: string): string {\n return endpoint.trim().replace(/\\/+$/, '').replace(/\\/v1$/, '')\n}\n\nfunction resolveIdempotencyKey(key: string | undefined): string {\n const resolved = key ?? globalThis.crypto.randomUUID()\n if (resolved.trim().length === 0) throw new Error('idempotency key must not be blank')\n if (resolved.length > MAX_IDEMPOTENCY_KEY_LENGTH) {\n throw new Error(`idempotency key must be at most ${MAX_IDEMPOTENCY_KEY_LENGTH} characters`)\n }\n return resolved\n}\n\nfunction isRetryable(status: number): boolean {\n return status >= 500 || status === 408 || status === 429\n}\n\nasync function send(\n tenant: HostedTenant,\n request: HostedRequest,\n): Promise<{ status: number; json: unknown; url: string }> {\n const timeoutMs = tenant.timeoutMs ?? 30_000\n const maxRetries = tenant.retries ?? 2\n const f: typeof fetch = tenant.fetchImpl ?? ((...args) => fetch(...args))\n const url = `${normalizeHostedBase(tenant.endpoint)}${request.path}`\n const headers: Record<string, string> = {\n authorization: `Bearer ${tenant.apiKey}`,\n 'x-tangle-tenant-id': tenant.tenantId,\n 'x-tangle-wire-version': HOSTED_WIRE_VERSION,\n }\n if (request.method !== 'GET') {\n headers['idempotency-key'] = resolveIdempotencyKey(request.idempotencyKey)\n headers['content-type'] = request.contentType ?? 'application/json'\n }\n\n let lastError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const timeout = AbortSignal.timeout(timeoutMs)\n const signal = request.signal ? AbortSignal.any([request.signal, timeout]) : timeout\n let res: Response\n try {\n res = await f(url, {\n method: request.method,\n headers,\n ...(request.body === undefined ? {} : { body: request.body }),\n signal,\n })\n } catch (err) {\n if (request.signal?.aborted) throw err\n lastError = err\n if (attempt === maxRetries) throw err\n await sleep(backoffMs(attempt), request.signal)\n continue\n }\n\n if (!res.ok && !request.expected?.includes(res.status)) {\n const text = await res.text().catch(() => '')\n const error = new HostedRequestError(url, res.status, text)\n if (!isRetryable(res.status) || attempt === maxRetries) throw error\n lastError = error\n await sleep(\n retryAfterMs(res.headers.get('retry-after')) ?? backoffMs(attempt),\n request.signal,\n )\n continue\n }\n\n try {\n return { status: res.status, json: await res.json(), url }\n } catch (error) {\n throw new Error(`hosted ${url} returned invalid JSON`, { cause: error })\n }\n }\n throw lastError ?? new Error('hosted request exhausted retries')\n}\n\nfunction parseResponse<T>(schema: z.ZodType<T>, value: unknown, url: string): T {\n const parsed = schema.safeParse(value)\n if (parsed.success) return parsed.data\n const reason = parsed.error.issues\n .map((issue) => `${issue.path.map(String).join('.') || 'value'}: ${issue.message}`)\n .join('; ')\n throw new Error(`hosted ${url} returned an invalid response: ${reason}`)\n}\n\nexport function createHostedClient(tenant: HostedTenant): HostedClient {\n if (normalizeHostedBase(tenant.endpoint).length === 0) throw new Error('endpoint is required')\n if (tenant.apiKey.trim().length === 0) throw new Error('apiKey is required')\n if (tenant.tenantId.trim().length === 0) throw new Error('tenantId is required')\n if (\n tenant.timeoutMs !== undefined &&\n (!Number.isFinite(tenant.timeoutMs) || tenant.timeoutMs <= 0)\n ) {\n throw new Error('timeoutMs must be greater than 0')\n }\n if (tenant.retries !== undefined && (!Number.isInteger(tenant.retries) || tenant.retries < 0)) {\n throw new Error('retries must be a non-negative integer')\n }\n\n return {\n tenant,\n wireVersion: HOSTED_WIRE_VERSION,\n\n async ingestTraces(spans, idempotencyKey) {\n const body: IngestTracesRequest = IngestTracesRequestSchema.parse({\n wireVersion: HOSTED_WIRE_VERSION,\n spans,\n })\n const res = await send(tenant, {\n method: 'POST',\n path: '/v1/ingest/traces',\n body: JSON.stringify(body),\n idempotencyKey,\n })\n return parseResponse(IngestResponseSchema, res.json, res.url)\n },\n\n async searchLedgerHead(searchId, signal) {\n const res = await send(tenant, {\n method: 'GET',\n path: searchLedgerHeadPath(searchId),\n signal,\n })\n const head = parseResponse(SearchLedgerHeadSchema, res.json, res.url)\n if (head.searchId !== searchId) {\n throw new Error(`hosted ${res.url} returned the head of search ${head.searchId}`)\n }\n return head\n },\n\n async ingestSearchLedger(request, signal) {\n const body = IngestSearchLedgerRequestSchema.parse(request)\n const last = body.lines.at(-1)!\n const res = await send(tenant, {\n method: 'POST',\n path: SEARCH_LEDGER_INGEST_PATH,\n body: JSON.stringify(body),\n // Equal key exactly when the batch is equal, so a cached response is\n // always the response to these lines.\n idempotencyKey: `search-ledger:${await sha256Hex(\n `${body.searchId}\\n${body.runKind}\\n${body.fromSequence}\\n${body.lines.length}\\n${last}`,\n )}`,\n signal,\n expected: [409],\n })\n if (res.status === 409) {\n return {\n status: 'conflict',\n conflict: parseResponse(SearchLedgerConflictSchema, res.json, res.url),\n }\n }\n return { status: 'accepted', head: parseResponse(SearchLedgerHeadSchema, res.json, res.url) }\n },\n\n async putSearchBlob(sha256, bytes, contentType, signal) {\n const res = await send(tenant, {\n method: 'PUT',\n path: searchBlobPath(sha256),\n body: bytes,\n contentType,\n idempotencyKey: `search-blob:${sha256}`,\n signal,\n })\n const stored = parseResponse(SearchBlobPutResponseSchema, res.json, res.url)\n if (stored.sha256 !== sha256) {\n throw new Error(`hosted ${res.url} acknowledged blob ${stored.sha256}, sent ${sha256}`)\n }\n return stored\n },\n }\n}\n\nasync function sha256Hex(text: string): Promise<string> {\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(text))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * Build a {@link HostedTenant} from env, or `undefined` when ingest is not\n * configured, so a product wires it unconditionally and it stays off until the\n * env is set. Env precedence:\n * - endpoint: `TANGLE_INGEST_URL` → `TANGLE_ORCHESTRATOR_URL`\n * - apiKey: `TANGLE_INGEST_API_KEY` → `TANGLE_API_KEY`\n * - tenantId: `TANGLE_TENANT_ID`\n * A trailing slash on the endpoint is stripped. `overrides` win over env.\n */\nexport function hostedTenantFromEnv(\n overrides: Partial<HostedTenant> & { env?: Record<string, string | undefined> } = {},\n): HostedTenant | undefined {\n const env = overrides.env ?? process.env\n const endpoint = (\n overrides.endpoint ??\n env.TANGLE_INGEST_URL ??\n env.TANGLE_ORCHESTRATOR_URL\n )?.trim()\n const apiKey = (overrides.apiKey ?? env.TANGLE_INGEST_API_KEY ?? env.TANGLE_API_KEY)?.trim()\n const tenantId = (overrides.tenantId ?? env.TANGLE_TENANT_ID)?.trim()\n if (!endpoint || !apiKey || !tenantId) return undefined\n const tenant: HostedTenant = { endpoint: endpoint.replace(/\\/+$/, ''), apiKey, tenantId }\n if (overrides.fetchImpl) tenant.fetchImpl = overrides.fetchImpl\n if (overrides.timeoutMs !== undefined) tenant.timeoutMs = overrides.timeoutMs\n if (overrides.retries !== undefined) tenant.retries = overrides.retries\n return tenant\n}\n\n/** {@link createHostedClient} over {@link hostedTenantFromEnv}; `undefined`\n * when ingest is not configured. */\nexport function hostedClientFromEnv(\n overrides: Partial<HostedTenant> & { env?: Record<string, string | undefined> } = {},\n): HostedClient | undefined {\n const tenant = hostedTenantFromEnv(overrides)\n return tenant ? createHostedClient(tenant) : undefined\n}\n","/**\n * Ship a search ledger to a hosted store while the search runs, or after.\n *\n * The local ledger is the source of truth and the search never waits on the\n * network: the shipper reads the ledger file the recorder already made\n * durable, uploads the blobs each entry names, then posts the entries. It\n * starts from the store's head, so a restarted process, a lost response, or a\n * second shipper continues where the store's chain ends instead of resending\n * the search. A store that holds a different chain for the same search stops\n * the shipper with `SearchShipConflictError`; nothing is overwritten.\n */\n\nimport { createHash } from 'node:crypto'\nimport { open, readFile } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport type { SearchLedgerEntry, SearchLedgerHash } from '../campaign/search-ledger-types'\nimport { createHostedClient, type HostedClient, type HostedTenant } from './client'\nimport {\n SEARCH_LEDGER_BATCH_MAX_BYTES,\n SEARCH_LEDGER_BATCH_MAX_LINES,\n type SearchLedgerConflict,\n type SearchLedgerHead,\n type SearchRunKind,\n searchLedgerArtifactRefs,\n} from './search-ledger-wire'\nimport { HOSTED_WIRE_VERSION } from './types'\n\nexport interface SearchShipOptions {\n tenant: HostedTenant\n ledger: { path: string; searchId: string }\n runKind: SearchRunKind\n /** `full` (default) uploads every blob an entry names; `digests` ships the\n * entries alone, so the store holds refs and digests without content. */\n content?: 'full' | 'digests'\n signal?: AbortSignal\n}\n\n/** A blob an entry names that was not uploaded, and why. */\nexport interface SearchShipMissingBlob {\n sha256: SearchLedgerHash\n uri: string\n reason: string\n}\n\nexport interface SearchShipResult {\n searchId: string\n /** The store's head after the last accepted batch. */\n head: SearchLedgerHead\n /** Local complete lines, which the store now holds when `head` reaches them. */\n localLines: number\n /** Ledger lines posted, counting resends after a gap. */\n linesPosted: number\n batches: number\n /** Resends after the store answered `sequence_gap`. */\n gapResends: number\n blobs: {\n content: 'full' | 'digests'\n uploaded: number\n stored: number\n masked: number\n withheld: number\n missing: SearchShipMissingBlob[]\n }\n}\n\n/** The store holds a different chain for this search. */\nexport class SearchShipConflictError extends Error {\n readonly conflict: SearchLedgerConflict\n constructor(conflict: SearchLedgerConflict) {\n super(\n `search ${conflict.head.searchId}: ${conflict.error}: ${conflict.message} (store head ${conflict.head.nextSequence} ${conflict.head.headHash ?? 'empty'})`,\n )\n this.name = 'SearchShipConflictError'\n this.conflict = conflict\n }\n}\n\n/** Ship every complete line of the ledger file now, then return. */\nexport async function shipSearchLedger(options: SearchShipOptions): Promise<SearchShipResult> {\n return new SearchLedgerTail(options).ship()\n}\n\nexport interface SearchShipper {\n /** Ship what is in the file now without waiting for the next tick. */\n flush(): Promise<SearchShipResult>\n /** Stop tailing, ship the rest of the file, and return the final result. */\n stop(): Promise<SearchShipResult>\n /** The last transient failure, cleared by the next successful tick. */\n readonly lastError: Error | null\n}\n\n/**\n * Tail the ledger: ship new lines every `intervalMs` (default 2 s) until\n * `stop()`. A transient failure is kept on `lastError` and retried on the next\n * tick; a conflict stops tailing and is thrown by `stop()`.\n */\nexport function startSearchShipper(\n options: SearchShipOptions & { intervalMs?: number },\n): SearchShipper {\n const tail = new SearchLedgerTail(options)\n const intervalMs = options.intervalMs ?? 2000\n const stopping = new AbortController()\n let lastError: Error | null = null\n let conflict: SearchShipConflictError | null = null\n let running: Promise<unknown> = Promise.resolve()\n\n const tick = (): Promise<unknown> => {\n running = running.then(async () => {\n if (conflict) return\n try {\n await tail.ship()\n lastError = null\n } catch (error) {\n if (error instanceof SearchShipConflictError) conflict = error\n else lastError = error instanceof Error ? error : new Error(String(error))\n }\n })\n return running\n }\n\n const loop = (async () => {\n while (!stopping.signal.aborted && !conflict) {\n await tick()\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, intervalMs)\n stopping.signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer)\n resolve()\n },\n { once: true },\n )\n })\n }\n })()\n\n return {\n get lastError() {\n return lastError\n },\n async flush() {\n await tick()\n if (conflict) throw conflict\n if (lastError) throw lastError\n return tail.result()\n },\n async stop() {\n stopping.abort()\n await loop\n await running\n if (conflict) throw conflict\n return tail.ship()\n },\n }\n}\n\n/** One full batch of lines with their newlines, plus a byte to see where a\n * maximal line ends. */\nconst READ_CHUNK_BYTES = SEARCH_LEDGER_BATCH_MAX_BYTES + SEARCH_LEDGER_BATCH_MAX_LINES + 1\n\ninterface LocalLine {\n text: string\n entry: SearchLedgerEntry\n bytes: number\n}\n\nclass SearchLedgerTail {\n private readonly client: HostedClient\n private readonly options: SearchShipOptions\n private readonly content: 'full' | 'digests'\n /** Where the store's chain ends, and the byte offset of that line locally. */\n private head: SearchLedgerHead | null = null\n private offset = 0\n private localLines = 0\n private readonly uploaded = new Set<string>()\n private readonly missing = new Map<string, SearchShipMissingBlob>()\n private readonly counts = {\n linesPosted: 0,\n batches: 0,\n gapResends: 0,\n uploaded: 0,\n stored: 0,\n masked: 0,\n withheld: 0,\n }\n\n constructor(options: SearchShipOptions) {\n this.options = options\n this.content = options.content ?? 'full'\n this.client = createHostedClient(options.tenant)\n }\n\n result(): SearchShipResult {\n const { linesPosted, batches, gapResends, uploaded, stored, masked, withheld } = this.counts\n return {\n searchId: this.options.ledger.searchId,\n head: this.head ?? {\n searchId: this.options.ledger.searchId,\n nextSequence: 0,\n headHash: null,\n },\n localLines: this.localLines,\n linesPosted,\n batches,\n gapResends,\n blobs: {\n content: this.content,\n uploaded,\n stored,\n masked,\n withheld,\n missing: [...this.missing.values()],\n },\n }\n }\n\n async ship(): Promise<SearchShipResult> {\n const { signal } = this.options\n if (this.head === null) {\n await this.syncTo(await this.client.searchLedgerHead(this.options.ledger.searchId, signal))\n }\n for (;;) {\n const lines = await this.readChunk(this.offset, this.head!)\n if (lines.length === 0) return this.result()\n const batch = takeBatch(lines)\n if (this.content === 'full') await this.uploadBlobs(batch)\n const fromSequence = this.head!.nextSequence\n const outcome = await this.client.ingestSearchLedger(\n {\n wireVersion: HOSTED_WIRE_VERSION,\n searchId: this.options.ledger.searchId,\n runKind: this.options.runKind,\n fromSequence,\n lines: batch.map((line) => line.text),\n },\n signal,\n )\n this.counts.batches++\n this.counts.linesPosted += batch.length\n if (outcome.status === 'conflict') {\n if (outcome.conflict.error === 'chain_conflict') {\n throw new SearchShipConflictError(outcome.conflict)\n }\n this.counts.gapResends++\n await this.syncTo(outcome.conflict.head)\n continue\n }\n const last = batch.at(-1)!.entry\n if (\n outcome.head.nextSequence < last.sequence + 1 ||\n (outcome.head.nextSequence === last.sequence + 1 &&\n outcome.head.headHash !== last.entryHash)\n ) {\n throw new Error(\n `store acknowledged sequence ${fromSequence}..${last.sequence} with head ${outcome.head.nextSequence} ${outcome.head.headHash}`,\n )\n }\n await this.syncTo(outcome.head)\n }\n }\n\n /** Move the cursor to the store's head, checking that the local ledger holds\n * the same entry there. A different entry is a fork; a store ahead of the\n * local file holds lines this ledger never wrote. */\n private async syncTo(head: SearchLedgerHead): Promise<void> {\n if (head.searchId !== this.options.ledger.searchId) {\n throw new Error(`store answered for search ${head.searchId}`)\n }\n const fork = (message: string) =>\n new SearchShipConflictError({ error: 'chain_conflict', message, head })\n const empty: SearchLedgerHead = { searchId: head.searchId, nextSequence: 0, headHash: null }\n const forward = this.head !== null && head.nextSequence >= this.head.nextSequence\n let offset = forward ? this.offset : 0\n let cursor = forward ? this.head! : empty\n while (cursor.nextSequence < head.nextSequence) {\n const lines = await this.readChunk(offset, cursor)\n if (lines.length === 0) {\n throw fork(\n `the store holds ${head.nextSequence} entries; the local ledger holds ${cursor.nextSequence}`,\n )\n }\n const take = Math.min(lines.length, head.nextSequence - cursor.nextSequence)\n for (let index = 0; index < take; index++) offset += lines[index]!.bytes\n const last = lines[take - 1]!.entry\n cursor = {\n searchId: head.searchId,\n nextSequence: last.sequence + 1,\n headHash: last.entryHash,\n }\n }\n if (cursor.headHash !== head.headHash) {\n throw fork(`the store's entry ${head.nextSequence - 1} differs from the local ledger's`)\n }\n this.offset = offset\n this.head = head\n }\n\n /** The complete lines in one read chunk from `offset`, checked to continue\n * the chain at `head`. A chunk holds at least one full batch. */\n private async readChunk(offset: number, head: SearchLedgerHead): Promise<LocalLine[]> {\n const bytes = await readRange(this.options.ledger.path, offset, READ_CHUNK_BYTES)\n const lines: LocalLine[] = []\n let start = 0\n let expectedSequence = head.nextSequence\n let previousHash = head.headHash\n for (;;) {\n const newline = bytes.indexOf(0x0a, start)\n // A line without its newline is still being written, or continues in\n // the next chunk.\n if (newline < 0) break\n const text = bytes.toString('utf8', start, newline)\n const entry = JSON.parse(text) as SearchLedgerEntry\n if (\n entry.searchId !== this.options.ledger.searchId ||\n entry.sequence !== expectedSequence ||\n entry.previousHash !== previousHash\n ) {\n throw new Error(\n `${this.options.ledger.path} line ${expectedSequence + 1} does not continue the chain at sequence ${expectedSequence}`,\n )\n }\n lines.push({ text, entry, bytes: newline + 1 - start })\n expectedSequence++\n previousHash = entry.entryHash\n start = newline + 1\n }\n if (lines.length === 0 && bytes.byteLength === READ_CHUNK_BYTES) {\n throw new Error(\n `${this.options.ledger.path} line ${expectedSequence + 1} is longer than ${SEARCH_LEDGER_BATCH_MAX_BYTES} bytes; one request carries at most that`,\n )\n }\n this.localLines = Math.max(this.localLines, expectedSequence)\n return lines\n }\n\n private async uploadBlobs(batch: LocalLine[]): Promise<void> {\n for (const line of batch) {\n for (const ref of searchLedgerArtifactRefs(line.entry.event)) {\n if (this.uploaded.has(ref.sha256) || this.missing.has(ref.sha256)) continue\n const bytes = await readBlob(ref.uri)\n if (typeof bytes === 'string') {\n this.missing.set(ref.sha256, { sha256: ref.sha256, uri: ref.uri, reason: bytes })\n continue\n }\n const digest = `sha256:${createHash('sha256').update(bytes).digest('hex')}`\n if (digest !== ref.sha256 || bytes.byteLength !== ref.byteLength) {\n this.missing.set(ref.sha256, {\n sha256: ref.sha256,\n uri: ref.uri,\n reason: `local bytes are ${digest} (${bytes.byteLength} bytes), not the ref's ${ref.sha256} (${ref.byteLength} bytes)`,\n })\n continue\n }\n const stored = await this.client.putSearchBlob(\n ref.sha256,\n new Uint8Array(bytes),\n ref.uri.endsWith('.json') ? 'application/json' : 'application/octet-stream',\n this.options.signal,\n )\n this.uploaded.add(ref.sha256)\n this.counts.uploaded++\n this.counts[stored.state]++\n }\n }\n }\n}\n\n/** The longest prefix of `lines` within one request's limits. */\nfunction takeBatch(lines: LocalLine[]): LocalLine[] {\n const batch: LocalLine[] = []\n let bytes = 0\n for (const line of lines) {\n const size = line.bytes - 1\n if (size > SEARCH_LEDGER_BATCH_MAX_BYTES) {\n throw new Error(\n `ledger line ${line.entry.sequence} holds ${size} bytes; one request carries at most ${SEARCH_LEDGER_BATCH_MAX_BYTES}`,\n )\n }\n if (\n batch.length === SEARCH_LEDGER_BATCH_MAX_LINES ||\n bytes + size > SEARCH_LEDGER_BATCH_MAX_BYTES\n )\n break\n batch.push(line)\n bytes += size\n }\n return batch\n}\n\n/** Up to `limit` bytes of the file from `offset`; empty before the recorder\n * created the file. */\nasync function readRange(path: string, offset: number, limit: number): Promise<Buffer> {\n let handle: Awaited<ReturnType<typeof open>>\n try {\n handle = await open(path, 'r')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT' && offset === 0) return Buffer.alloc(0)\n throw error\n }\n try {\n const { size } = await handle.stat()\n if (size < offset) throw new Error(`${path} shrank below byte ${offset}`)\n const bytes = Buffer.alloc(Math.min(limit, size - offset))\n let read = 0\n while (read < bytes.byteLength) {\n const { bytesRead } = await handle.read(bytes, read, bytes.byteLength - read, offset + read)\n if (bytesRead === 0) break\n read += bytesRead\n }\n return bytes.subarray(0, read)\n } finally {\n await handle.close()\n }\n}\n\n/** The bytes at a `file:` ref, or why there are none. */\nasync function readBlob(uri: string): Promise<Buffer | string> {\n if (!uri.startsWith('file:'))\n return `the shipper reads only file: refs, not ${uri.split(':')[0]}:`\n try {\n return await readFile(fileURLToPath(uri))\n } catch (error) {\n return `unreadable: ${(error as NodeJS.ErrnoException).code ?? String(error)}`\n }\n}\n\n/**\n * Run a ship that must never fail its caller, such as the one a loop runs at\n * its end. A failure, a store short of the local ledger, or a blob that did\n * not upload is logged with the command that resumes it.\n */\nexport async function shipBestEffort(\n ship: () => Promise<SearchShipResult>,\n ledgerPath: string,\n): Promise<SearchShipResult | undefined> {\n const resume = `agent-eval search ship ${ledgerPath}`\n try {\n const shipped = await ship()\n if (shipped.head.nextSequence < shipped.localLines) {\n console.warn(\n `[agent-eval] search ${shipped.searchId}: the store holds ${shipped.head.nextSequence} of ${shipped.localLines} ledger entries; resume with ${resume}`,\n )\n }\n if (shipped.blobs.missing.length > 0) {\n console.warn(\n `[agent-eval] search ${shipped.searchId}: ${shipped.blobs.missing.length} blob(s) were not uploaded, first: ${shipped.blobs.missing[0]!.reason}`,\n )\n }\n return shipped\n } catch (error) {\n console.warn(\n `[agent-eval] search ledger ship failed (continuing; resume with ${resume}): ${error instanceof Error ? error.message : String(error)}`,\n )\n return undefined\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"search-shipper-CA3ooBbC.js","names":[],"sources":["../src/hosted/types.ts","../src/hosted/schemas.ts","../src/hosted/search-ledger-wire.ts","../src/hosted/client.ts","../src/hosted/search-shipper.ts"],"sourcesContent":["/**\n * # Hosted-tier wire format: the shapes every orchestrator (ours, a partner's\n * self-hosted one, a future open implementation) must accept.\n *\n * This package implements exactly one wire version. Servers reject every\n * other version instead of translating old payloads.\n *\n * The wire carries two streams in one transport:\n *\n * 1. **Search ledgers** (`/v1/search-blobs/*`, `/v1/ingest/search-ledger`).\n * A producer ships a search's hash-chained ledger lines and the blobs\n * they name; `./search-ledger-wire.ts` owns those shapes.\n *\n * 2. **Trace spans** (`POST /v1/ingest/traces`). Standard OTLP-shaped\n * spans with a few additional attributes so the orchestrator can\n * pivot from a search cell to its underlying execution. Compatible\n * with any OTel collector.\n *\n * Every endpoint is authenticated with a bearer token and a tenant id\n * header. Tenants isolate everything downstream of ingest; no tenant\n * ever sees another tenant's data.\n */\n\nexport const HOSTED_WIRE_VERSION = '2026-07-24.v1' as const\nexport type HostedWireVersion = typeof HOSTED_WIRE_VERSION\n\n// ── Transport headers ───────────────────────────────────────────────\n\n/** Every ingest request carries these. */\nexport interface HostedIngestHeaders {\n /** Bearer token. The orchestrator validates against the tenant key. */\n authorization: `Bearer ${string}`\n /** Stable tenant id (the orchestrator-side primary key for the tenant). */\n 'x-tangle-tenant-id': string\n /** Wire-version pin so the server can reject incompatible payloads. */\n 'x-tangle-wire-version': HostedWireVersion\n /** Stable request key generated once and reused across retries. */\n 'idempotency-key': string\n}\n\n// ── Trace span event ────────────────────────────────────────────────\n\n/**\n * Canonical unsigned 64-bit integer encoded as a base-10 string.\n * JSON numbers cannot represent OTLP nanosecond timestamps exactly.\n */\nexport type UnixNanoTimestamp = string\n\n/**\n * OTel-shape span with a few additional attributes for pivoting.\n * Compatible with any OTLP collector — `name`, `traceId`, `spanId`,\n * `startTimeUnixNano`, `endTimeUnixNano`, `attributes` are stock OTel.\n */\nexport interface TraceSpanEvent {\n traceId: string\n spanId: string\n parentSpanId?: string\n name: string\n startTimeUnixNano: UnixNanoTimestamp\n endTimeUnixNano: UnixNanoTimestamp\n attributes: Record<string, string | number | boolean>\n events?: Array<{\n timeUnixNano: UnixNanoTimestamp\n name: string\n attributes?: Record<string, string | number | boolean>\n }>\n status?: { code: 'OK' | 'ERROR' | 'UNSET'; message?: string }\n /** Pivot to the producing run. */\n 'tangle.runId'?: string\n /** Pivot to the specific generation. */\n 'tangle.generation'?: number\n /** Pivot to the specific cell. */\n 'tangle.cellId'?: string\n /** Pivot to the specific scenario. */\n 'tangle.scenarioId'?: string\n}\n\n// ── Ingest request bodies ───────────────────────────────────────────\n\nexport interface IngestTracesRequest {\n wireVersion: HostedWireVersion\n spans: TraceSpanEvent[]\n}\n\nexport interface IngestResponse {\n /** Accepted spans count. */\n accepted: number\n /** Rejected spans with reasons (validation failures, conflicting identity). */\n rejected: Array<{ index: number; reason: string }>\n}\n","import { z } from 'zod'\nimport type {\n IngestResponse,\n IngestTracesRequest,\n TraceSpanEvent,\n UnixNanoTimestamp,\n} from './types'\nimport { HOSTED_WIRE_VERSION } from './types'\n\nconst finiteNumber = z.number().finite()\nconst nonNegativeInteger = z.number().int().nonnegative()\nconst nonEmptyString = z\n .string()\n .min(1)\n .refine((value) => value.trim().length > 0, 'must not be blank')\nconst attributeValue = z.union([z.string(), finiteNumber, z.boolean()])\nconst attributes = z.record(z.string(), attributeValue)\nconst UINT64_MAX = 18_446_744_073_709_551_615n\n\nexport const UnixNanoTimestampSchema: z.ZodType<UnixNanoTimestamp> = z\n .string()\n .regex(/^(0|[1-9][0-9]*)$/, 'expected an unsigned base-10 integer string')\n .refine((value) => BigInt(value) <= UINT64_MAX, 'must fit in an unsigned 64-bit integer')\n\nconst TraceSpanEventEntrySchema = z\n .object({\n timeUnixNano: UnixNanoTimestampSchema,\n name: nonEmptyString,\n attributes: attributes.optional(),\n })\n .strict()\n\nexport const TraceSpanEventSchema: z.ZodType<TraceSpanEvent> = z\n .object({\n traceId: nonEmptyString,\n spanId: nonEmptyString,\n parentSpanId: nonEmptyString.optional(),\n name: nonEmptyString,\n startTimeUnixNano: UnixNanoTimestampSchema,\n endTimeUnixNano: UnixNanoTimestampSchema,\n attributes,\n events: z.array(TraceSpanEventEntrySchema).optional(),\n status: z\n .object({\n code: z.enum(['OK', 'ERROR', 'UNSET']),\n message: z.string().optional(),\n })\n .strict()\n .optional(),\n 'tangle.runId': nonEmptyString.optional(),\n 'tangle.generation': nonNegativeInteger.optional(),\n 'tangle.cellId': nonEmptyString.optional(),\n 'tangle.scenarioId': nonEmptyString.optional(),\n })\n .strict()\n .refine((span) => BigInt(span.endTimeUnixNano) >= BigInt(span.startTimeUnixNano), {\n path: ['endTimeUnixNano'],\n message: 'endTimeUnixNano must be greater than or equal to startTimeUnixNano',\n })\n\nexport const IngestTracesEnvelopeSchema = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n spans: z.array(z.unknown()),\n })\n .strict()\n\nexport const IngestTracesRequestSchema: z.ZodType<IngestTracesRequest> = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n spans: z.array(TraceSpanEventSchema),\n })\n .strict()\n\nexport const IngestResponseSchema: z.ZodType<IngestResponse> = z\n .object({\n accepted: nonNegativeInteger,\n rejected: z.array(\n z\n .object({\n index: nonNegativeInteger,\n reason: nonEmptyString,\n })\n .strict(),\n ),\n })\n .strict()\n","/**\n * The search-ledger wire: how a producer ships a search ledger to a hosted\n * store, and the rule a store applies to each batch.\n *\n * A producer ships three things, in this order:\n *\n * 1. Blobs: `PUT /v1/search-blobs/<sha256 hex>` with the exact bytes an\n * entry's artifact ref names. The store re-hashes them; the call is\n * idempotent by content.\n * 2. Head: `GET /v1/ingest/search-ledger/<searchId>/head` tells a restarted\n * producer where the store's copy of the chain ends.\n * 3. Entries: `POST /v1/ingest/search-ledger` with canonical ledger lines,\n * starting at `fromSequence`. The store verifies every line, links it to\n * its stored head, and answers with its new head.\n *\n * The lines are the ledger's own bytes, so producer and store hash identical\n * input. A line below the store's head with an equal hash is a no-op; a\n * different hash is a fork (`409 chain_conflict`); a batch that starts past\n * the head is a gap (`409 sequence_gap`). Both 409s carry the store's head,\n * so a producer resends from there or stops on a fork.\n */\n\nimport { z } from 'zod'\nimport { parseSearchLedgerLine } from '../campaign/search-ledger'\nimport { SearchLedgerIntegrityError } from '../campaign/search-ledger-errors'\nimport type {\n SearchArtifactRef,\n SearchLedgerEntry,\n SearchLedgerEvent,\n SearchLedgerHash,\n} from '../campaign/search-ledger-types'\nimport { HOSTED_WIRE_VERSION } from './types'\n\n/** At most this many lines in one ingest request. */\nexport const SEARCH_LEDGER_BATCH_MAX_LINES = 1000\n/** At most this many UTF-8 bytes across one request's lines. A single line\n * larger than this cannot be shipped. */\nexport const SEARCH_LEDGER_BATCH_MAX_BYTES = 1_048_576\n\nexport const SEARCH_LEDGER_INGEST_PATH = '/v1/ingest/search-ledger'\n\nexport function searchLedgerHeadPath(searchId: string): string {\n return `${SEARCH_LEDGER_INGEST_PATH}/${encodeURIComponent(searchId)}/head`\n}\n\n/** Blobs are addressed by the hex digest alone. */\nexport function searchBlobPath(sha256: SearchLedgerHash): string {\n return `/v1/search-blobs/${sha256.slice('sha256:'.length)}`\n}\n\nconst HASH = z.custom<SearchLedgerHash>(\n (value) => typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value),\n 'expected sha256:<64 lowercase hex characters>',\n)\nconst SEARCH_ID = z\n .string()\n .min(1)\n .refine((value) => value.trim() === value, 'must not contain surrounding whitespace')\nconst SEQUENCE = z.number().int().nonnegative().safe()\n\n/** What the run is for: an optimization, or an eval (a one-node search). */\nexport const SearchRunKindSchema = z.enum(['optimization', 'eval'])\nexport type SearchRunKind = z.infer<typeof SearchRunKindSchema>\n\n/** Where a store's copy of one search's chain ends. `headHash` is null exactly\n * when the store holds no entry. */\nexport const SearchLedgerHeadSchema = z\n .object({ searchId: SEARCH_ID, nextSequence: SEQUENCE, headHash: HASH.nullable() })\n .strict()\n .refine((head) => (head.nextSequence === 0) === (head.headHash === null), {\n message: 'headHash must be null exactly when nextSequence is 0',\n path: ['headHash'],\n })\nexport type SearchLedgerHead = z.infer<typeof SearchLedgerHeadSchema>\n\nexport const IngestSearchLedgerRequestSchema = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n searchId: SEARCH_ID,\n runKind: SearchRunKindSchema,\n fromSequence: SEQUENCE,\n lines: z.array(z.string().min(1)).min(1).max(SEARCH_LEDGER_BATCH_MAX_LINES),\n })\n .strict()\n .superRefine((request, ctx) => {\n const bytes = searchLedgerLinesByteLength(request.lines)\n if (bytes > SEARCH_LEDGER_BATCH_MAX_BYTES) {\n ctx.addIssue({\n code: 'custom',\n path: ['lines'],\n message: `lines hold ${bytes} bytes; at most ${SEARCH_LEDGER_BATCH_MAX_BYTES} per request`,\n })\n }\n })\nexport type IngestSearchLedgerRequest = z.infer<typeof IngestSearchLedgerRequestSchema>\n\n/** The body of a `409`: a fork or a gap, with the store's head. */\nexport const SearchLedgerConflictSchema = z\n .object({\n error: z.enum(['chain_conflict', 'sequence_gap']),\n message: z.string().min(1),\n head: SearchLedgerHeadSchema,\n })\n .strict()\nexport type SearchLedgerConflict = z.infer<typeof SearchLedgerConflictSchema>\n\n/** What a store kept of one blob. `masked` and `withheld` come from the\n * store's own redaction scan or the tenant's content posture. */\nexport const SearchBlobPutResponseSchema = z\n .object({\n sha256: HASH,\n byteLength: SEQUENCE,\n state: z.enum(['stored', 'masked', 'withheld']),\n })\n .strict()\nexport type SearchBlobPutResponse = z.infer<typeof SearchBlobPutResponseSchema>\n\nconst UTF8 = new TextEncoder()\n\nfunction searchLedgerLinesByteLength(lines: readonly string[]): number {\n let bytes = 0\n for (const line of lines) bytes += UTF8.encode(line).byteLength\n return bytes\n}\n\n/** Every blob an event names, once each. Ship these before the event. */\nexport function searchLedgerArtifactRefs(event: SearchLedgerEvent): SearchArtifactRef[] {\n const refs: SearchArtifactRef[] = [...event.artifacts]\n switch (event.kind) {\n case 'node-registered':\n refs.push(event.artifact, ...event.surfaces.map((surface) => surface.artifact))\n break\n case 'edge-recorded':\n for (const ref of [event.rationale, ...event.diffs]) if ('uri' in ref) refs.push(ref)\n break\n case 'cell-settled':\n for (const evidence of event.surfaceEvidence) refs.push(...evidence.evidence)\n break\n default:\n break\n }\n const unique = new Map<string, SearchArtifactRef>()\n for (const ref of refs) if (!unique.has(ref.sha256)) unique.set(ref.sha256, ref)\n return [...unique.values()]\n}\n\nexport type SearchLedgerBatchAdmission =\n | {\n status: 'accepted'\n /** Entries past the stored head, in order, with their exact lines. */\n entries: SearchLedgerEntry[]\n lines: string[]\n /** The head after storing `entries`. */\n head: SearchLedgerHead\n }\n | { status: 'conflict'; conflict: SearchLedgerConflict }\n\nexport interface AdmitSearchLedgerBatchInput {\n request: IngestSearchLedgerRequest\n /** The store's head for `request.searchId` before this batch. */\n head: SearchLedgerHead\n /** The stored hash at a sequence below the head. */\n storedEntryHash(sequence: number): SearchLedgerHash | undefined\n}\n\n/**\n * Apply the wire's chain rule to one batch. Each line is verified in isolation\n * (`parseSearchLedgerLine`) and linked to the stored head. A malformed line\n * throws `SearchLedgerIntegrityError`, which a store answers with 422; the\n * state machine (`SearchState`) runs after admission, on `entries`.\n */\nexport function admitSearchLedgerBatch(\n input: AdmitSearchLedgerBatchInput,\n): SearchLedgerBatchAdmission {\n const { request, head } = input\n if (request.searchId !== head.searchId) {\n throw new Error(`head is for search ${head.searchId}, request is for ${request.searchId}`)\n }\n if (request.fromSequence > head.nextSequence) {\n return conflict(\n 'sequence_gap',\n `batch starts at sequence ${request.fromSequence}; the store expects ${head.nextSequence}`,\n head,\n )\n }\n const entries: SearchLedgerEntry[] = []\n const lines: string[] = []\n let previousHash = head.headHash\n for (let index = 0; index < request.lines.length; index++) {\n const sequence = request.fromSequence + index\n const line = request.lines[index]!\n const entry = parseSearchLedgerLine(line, request.searchId, {\n path: `search ${request.searchId}`,\n line: sequence + 1,\n })\n if (entry.sequence !== sequence) {\n throw new SearchLedgerIntegrityError(\n `line ${index} of the batch holds sequence ${entry.sequence}, expected ${sequence}`,\n )\n }\n if (sequence < head.nextSequence) {\n if (input.storedEntryHash(sequence) !== entry.entryHash) {\n return conflict(\n 'chain_conflict',\n `sequence ${sequence} differs from the stored entry; the ledger forked`,\n head,\n )\n }\n continue\n }\n if (entry.previousHash !== previousHash) {\n if (sequence === head.nextSequence) {\n return conflict(\n 'chain_conflict',\n `sequence ${sequence} does not extend the stored head; the ledger forked`,\n head,\n )\n }\n throw new SearchLedgerIntegrityError(\n `sequence ${sequence} does not extend sequence ${sequence - 1} in the same batch`,\n )\n }\n entries.push(entry)\n lines.push(line)\n previousHash = entry.entryHash\n }\n const last = entries.at(-1)\n return {\n status: 'accepted',\n entries,\n lines,\n head: last\n ? { searchId: head.searchId, nextSequence: last.sequence + 1, headHash: last.entryHash }\n : head,\n }\n}\n\nfunction conflict(\n error: SearchLedgerConflict['error'],\n message: string,\n head: SearchLedgerHead,\n): SearchLedgerBatchAdmission {\n return { status: 'conflict', conflict: { error, message, head } }\n}\n","/**\n * # Hosted-tier ingest client.\n *\n * Ships search ledgers and trace spans to any orchestrator (ours, a\n * partner's self-hosted one, or a future open implementation) that speaks\n * the wire format in `./types.ts` and `./search-ledger-wire.ts`.\n *\n * Three modes:\n * - **Ours:** point at `https://orchestrator.tangle.tools` (the host root —\n * the client appends the versioned `/v1/...` path itself; a trailing\n * `/v1` on the endpoint is tolerated and normalized away).\n * - **Self-hosted:** point at whatever URL runs the reference receiver\n * from `examples/hosted-ingest-server/`.\n * - **Off (default):** when no tenant is configured, nothing is sent.\n * Everything stays local.\n *\n * Every call retries network errors, 408, 429 and 5xx. When the server sends\n * `Retry-After`, the client waits exactly that long before the next attempt.\n */\n\nimport type { z } from 'zod'\nimport { IngestResponseSchema, IngestTracesRequestSchema } from './schemas'\nimport {\n type IngestSearchLedgerRequest,\n IngestSearchLedgerRequestSchema,\n SEARCH_LEDGER_INGEST_PATH,\n type SearchBlobPutResponse,\n SearchBlobPutResponseSchema,\n type SearchLedgerConflict,\n SearchLedgerConflictSchema,\n type SearchLedgerHead,\n SearchLedgerHeadSchema,\n searchBlobPath,\n searchLedgerHeadPath,\n} from './search-ledger-wire'\nimport {\n HOSTED_WIRE_VERSION,\n type HostedWireVersion,\n type IngestResponse,\n type IngestTracesRequest,\n type TraceSpanEvent,\n} from './types'\n\nexport interface HostedTenant {\n /** Orchestrator endpoint base URL (no trailing slash). Required. */\n endpoint: string\n /** Bearer token issued by the orchestrator. Required. */\n apiKey: string\n /** Tenant id — the orchestrator's primary key for this consumer. Required. */\n tenantId: string\n /** Optional `fetch` override (auth wrappers, custom agent). */\n fetchImpl?: typeof fetch\n /** Per-attempt timeout in ms. Default 30s. */\n timeoutMs?: number\n /** Retries on network errors, 408, 429 and 5xx. Default 2. */\n retries?: number\n}\n\n/** A search-ledger batch the store accepted, or the fork or gap it refused. */\nexport type SearchLedgerIngestOutcome =\n | { status: 'accepted'; head: SearchLedgerHead }\n | { status: 'conflict'; conflict: SearchLedgerConflict }\n\nexport interface HostedClient {\n ingestTraces(spans: TraceSpanEvent[], idempotencyKey?: string): Promise<IngestResponse>\n /** The store's head for one search; `nextSequence` 0 when it holds nothing. */\n searchLedgerHead(searchId: string, signal?: AbortSignal): Promise<SearchLedgerHead>\n ingestSearchLedger(\n request: IngestSearchLedgerRequest,\n signal?: AbortSignal,\n ): Promise<SearchLedgerIngestOutcome>\n putSearchBlob(\n sha256: `sha256:${string}`,\n bytes: Uint8Array<ArrayBuffer>,\n contentType: string,\n signal?: AbortSignal,\n ): Promise<SearchBlobPutResponse>\n readonly tenant: HostedTenant\n readonly wireVersion: HostedWireVersion\n}\n\n/** A request the server refused, after any retries. */\nclass HostedRequestError extends Error {\n readonly status: number\n readonly body: string\n constructor(url: string, status: number, body: string) {\n super(`hosted ${url} failed (${status}): ${body.slice(0, 500)}`)\n this.name = 'HostedRequestError'\n this.status = status\n this.body = body\n }\n}\n\ninterface HostedRequest {\n method: 'GET' | 'POST' | 'PUT'\n path: string\n body?: string | Uint8Array<ArrayBuffer>\n contentType?: string\n idempotencyKey?: string\n signal?: AbortSignal\n /** Statuses returned to the caller instead of thrown. */\n expected?: readonly number[]\n}\n\nconst MAX_IDEMPOTENCY_KEY_LENGTH = 256\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason)\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timer)\n reject(signal!.reason)\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/** `Retry-After` as milliseconds: delay-seconds or an HTTP date. Null when\n * absent or unreadable, so the caller falls back to its own backoff. */\nfunction retryAfterMs(value: string | null, now = Date.now()): number | null {\n if (value === null) return null\n const trimmed = value.trim()\n if (/^\\d+$/.test(trimmed)) return Number(trimmed) * 1000\n const at = Date.parse(trimmed)\n return Number.isFinite(at) ? Math.max(0, at - now) : null\n}\n\nfunction backoffMs(attempt: number): number {\n return 2 ** attempt * 200 + Math.random() * 200\n}\n\nfunction normalizeHostedBase(endpoint: string): string {\n return endpoint.trim().replace(/\\/+$/, '').replace(/\\/v1$/, '')\n}\n\nfunction resolveIdempotencyKey(key: string | undefined): string {\n const resolved = key ?? globalThis.crypto.randomUUID()\n if (resolved.trim().length === 0) throw new Error('idempotency key must not be blank')\n if (resolved.length > MAX_IDEMPOTENCY_KEY_LENGTH) {\n throw new Error(`idempotency key must be at most ${MAX_IDEMPOTENCY_KEY_LENGTH} characters`)\n }\n return resolved\n}\n\nfunction isRetryable(status: number): boolean {\n return status >= 500 || status === 408 || status === 429\n}\n\nasync function send(\n tenant: HostedTenant,\n request: HostedRequest,\n): Promise<{ status: number; json: unknown; url: string }> {\n const timeoutMs = tenant.timeoutMs ?? 30_000\n const maxRetries = tenant.retries ?? 2\n const f: typeof fetch = tenant.fetchImpl ?? ((...args) => fetch(...args))\n const url = `${normalizeHostedBase(tenant.endpoint)}${request.path}`\n const headers: Record<string, string> = {\n authorization: `Bearer ${tenant.apiKey}`,\n 'x-tangle-tenant-id': tenant.tenantId,\n 'x-tangle-wire-version': HOSTED_WIRE_VERSION,\n }\n if (request.method !== 'GET') {\n headers['idempotency-key'] = resolveIdempotencyKey(request.idempotencyKey)\n headers['content-type'] = request.contentType ?? 'application/json'\n }\n\n let lastError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const timeout = AbortSignal.timeout(timeoutMs)\n const signal = request.signal ? AbortSignal.any([request.signal, timeout]) : timeout\n let res: Response\n try {\n res = await f(url, {\n method: request.method,\n headers,\n ...(request.body === undefined ? {} : { body: request.body }),\n signal,\n })\n } catch (err) {\n if (request.signal?.aborted) throw err\n lastError = err\n if (attempt === maxRetries) throw err\n await sleep(backoffMs(attempt), request.signal)\n continue\n }\n\n if (!res.ok && !request.expected?.includes(res.status)) {\n const text = await res.text().catch(() => '')\n const error = new HostedRequestError(url, res.status, text)\n if (!isRetryable(res.status) || attempt === maxRetries) throw error\n lastError = error\n await sleep(\n retryAfterMs(res.headers.get('retry-after')) ?? backoffMs(attempt),\n request.signal,\n )\n continue\n }\n\n try {\n return { status: res.status, json: await res.json(), url }\n } catch (error) {\n throw new Error(`hosted ${url} returned invalid JSON`, { cause: error })\n }\n }\n throw lastError ?? new Error('hosted request exhausted retries')\n}\n\nfunction parseResponse<T>(schema: z.ZodType<T>, value: unknown, url: string): T {\n const parsed = schema.safeParse(value)\n if (parsed.success) return parsed.data\n const reason = parsed.error.issues\n .map((issue) => `${issue.path.map(String).join('.') || 'value'}: ${issue.message}`)\n .join('; ')\n throw new Error(`hosted ${url} returned an invalid response: ${reason}`)\n}\n\nexport function createHostedClient(tenant: HostedTenant): HostedClient {\n if (normalizeHostedBase(tenant.endpoint).length === 0) throw new Error('endpoint is required')\n if (tenant.apiKey.trim().length === 0) throw new Error('apiKey is required')\n if (tenant.tenantId.trim().length === 0) throw new Error('tenantId is required')\n if (\n tenant.timeoutMs !== undefined &&\n (!Number.isFinite(tenant.timeoutMs) || tenant.timeoutMs <= 0)\n ) {\n throw new Error('timeoutMs must be greater than 0')\n }\n if (tenant.retries !== undefined && (!Number.isInteger(tenant.retries) || tenant.retries < 0)) {\n throw new Error('retries must be a non-negative integer')\n }\n\n return {\n tenant,\n wireVersion: HOSTED_WIRE_VERSION,\n\n async ingestTraces(spans, idempotencyKey) {\n const body: IngestTracesRequest = IngestTracesRequestSchema.parse({\n wireVersion: HOSTED_WIRE_VERSION,\n spans,\n })\n const res = await send(tenant, {\n method: 'POST',\n path: '/v1/ingest/traces',\n body: JSON.stringify(body),\n idempotencyKey,\n })\n return parseResponse(IngestResponseSchema, res.json, res.url)\n },\n\n async searchLedgerHead(searchId, signal) {\n const res = await send(tenant, {\n method: 'GET',\n path: searchLedgerHeadPath(searchId),\n signal,\n })\n const head = parseResponse(SearchLedgerHeadSchema, res.json, res.url)\n if (head.searchId !== searchId) {\n throw new Error(`hosted ${res.url} returned the head of search ${head.searchId}`)\n }\n return head\n },\n\n async ingestSearchLedger(request, signal) {\n const body = IngestSearchLedgerRequestSchema.parse(request)\n const last = body.lines.at(-1)!\n const res = await send(tenant, {\n method: 'POST',\n path: SEARCH_LEDGER_INGEST_PATH,\n body: JSON.stringify(body),\n // Equal key exactly when the batch is equal, so a cached response is\n // always the response to these lines.\n idempotencyKey: `search-ledger:${await sha256Hex(\n `${body.searchId}\\n${body.runKind}\\n${body.fromSequence}\\n${body.lines.length}\\n${last}`,\n )}`,\n signal,\n expected: [409],\n })\n if (res.status === 409) {\n return {\n status: 'conflict',\n conflict: parseResponse(SearchLedgerConflictSchema, res.json, res.url),\n }\n }\n return { status: 'accepted', head: parseResponse(SearchLedgerHeadSchema, res.json, res.url) }\n },\n\n async putSearchBlob(sha256, bytes, contentType, signal) {\n const res = await send(tenant, {\n method: 'PUT',\n path: searchBlobPath(sha256),\n body: bytes,\n contentType,\n idempotencyKey: `search-blob:${sha256}`,\n signal,\n })\n const stored = parseResponse(SearchBlobPutResponseSchema, res.json, res.url)\n if (stored.sha256 !== sha256) {\n throw new Error(`hosted ${res.url} acknowledged blob ${stored.sha256}, sent ${sha256}`)\n }\n return stored\n },\n }\n}\n\nasync function sha256Hex(text: string): Promise<string> {\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(text))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * Build a {@link HostedTenant} from env, or `undefined` when ingest is not\n * configured, so a product wires it unconditionally and it stays off until the\n * env is set. Env precedence:\n * - endpoint: `TANGLE_INGEST_URL` → `TANGLE_ORCHESTRATOR_URL`\n * - apiKey: `TANGLE_INGEST_API_KEY` → `TANGLE_API_KEY`\n * - tenantId: `TANGLE_TENANT_ID`\n * A trailing slash on the endpoint is stripped. `overrides` win over env.\n */\nexport function hostedTenantFromEnv(\n overrides: Partial<HostedTenant> & { env?: Record<string, string | undefined> } = {},\n): HostedTenant | undefined {\n const env = overrides.env ?? process.env\n const endpoint = (\n overrides.endpoint ??\n env.TANGLE_INGEST_URL ??\n env.TANGLE_ORCHESTRATOR_URL\n )?.trim()\n const apiKey = (overrides.apiKey ?? env.TANGLE_INGEST_API_KEY ?? env.TANGLE_API_KEY)?.trim()\n const tenantId = (overrides.tenantId ?? env.TANGLE_TENANT_ID)?.trim()\n if (!endpoint || !apiKey || !tenantId) return undefined\n const tenant: HostedTenant = { endpoint: endpoint.replace(/\\/+$/, ''), apiKey, tenantId }\n if (overrides.fetchImpl) tenant.fetchImpl = overrides.fetchImpl\n if (overrides.timeoutMs !== undefined) tenant.timeoutMs = overrides.timeoutMs\n if (overrides.retries !== undefined) tenant.retries = overrides.retries\n return tenant\n}\n\n/** {@link createHostedClient} over {@link hostedTenantFromEnv}; `undefined`\n * when ingest is not configured. */\nexport function hostedClientFromEnv(\n overrides: Partial<HostedTenant> & { env?: Record<string, string | undefined> } = {},\n): HostedClient | undefined {\n const tenant = hostedTenantFromEnv(overrides)\n return tenant ? createHostedClient(tenant) : undefined\n}\n","/**\n * Ship a search ledger to a hosted store while the search runs, or after.\n *\n * The local ledger is the source of truth and the search never waits on the\n * network: the shipper reads the ledger file the recorder already made\n * durable, uploads the blobs each entry names, then posts the entries. It\n * starts from the store's head, so a restarted process, a lost response, or a\n * second shipper continues where the store's chain ends instead of resending\n * the search. A store that holds a different chain for the same search stops\n * the shipper with `SearchShipConflictError`; nothing is overwritten.\n */\n\nimport { createHash } from 'node:crypto'\nimport { open, readFile } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport type { SearchLedgerEntry, SearchLedgerHash } from '../campaign/search-ledger-types'\nimport { createHostedClient, type HostedClient, type HostedTenant } from './client'\nimport {\n SEARCH_LEDGER_BATCH_MAX_BYTES,\n SEARCH_LEDGER_BATCH_MAX_LINES,\n type SearchLedgerConflict,\n type SearchLedgerHead,\n type SearchRunKind,\n searchLedgerArtifactRefs,\n} from './search-ledger-wire'\nimport { HOSTED_WIRE_VERSION } from './types'\n\nexport interface SearchShipOptions {\n tenant: HostedTenant\n ledger: { path: string; searchId: string }\n runKind: SearchRunKind\n /** `full` (default) uploads every blob an entry names; `digests` ships the\n * entries alone, so the store holds refs and digests without content. */\n content?: 'full' | 'digests'\n signal?: AbortSignal\n}\n\n/** A blob an entry names that was not uploaded, and why. */\nexport interface SearchShipMissingBlob {\n sha256: SearchLedgerHash\n uri: string\n reason: string\n}\n\nexport interface SearchShipResult {\n searchId: string\n /** The store's head after the last accepted batch. */\n head: SearchLedgerHead\n /** Local complete lines, which the store now holds when `head` reaches them. */\n localLines: number\n /** Ledger lines posted, counting resends after a gap. */\n linesPosted: number\n batches: number\n /** Resends after the store answered `sequence_gap`. */\n gapResends: number\n blobs: {\n content: 'full' | 'digests'\n uploaded: number\n stored: number\n masked: number\n withheld: number\n missing: SearchShipMissingBlob[]\n }\n}\n\n/** The store holds a different chain for this search. */\nexport class SearchShipConflictError extends Error {\n readonly conflict: SearchLedgerConflict\n constructor(conflict: SearchLedgerConflict) {\n super(\n `search ${conflict.head.searchId}: ${conflict.error}: ${conflict.message} (store head ${conflict.head.nextSequence} ${conflict.head.headHash ?? 'empty'})`,\n )\n this.name = 'SearchShipConflictError'\n this.conflict = conflict\n }\n}\n\n/** Ship every complete line of the ledger file now, then return. */\nexport async function shipSearchLedger(options: SearchShipOptions): Promise<SearchShipResult> {\n return new SearchLedgerTail(options).ship()\n}\n\nexport interface SearchShipper {\n /** Ship what is in the file now without waiting for the next tick. */\n flush(): Promise<SearchShipResult>\n /** Stop tailing, ship the rest of the file, and return the final result. */\n stop(): Promise<SearchShipResult>\n /** The last transient failure, cleared by the next successful tick. */\n readonly lastError: Error | null\n}\n\n/**\n * Tail the ledger: ship new lines every `intervalMs` (default 2 s) until\n * `stop()`. A transient failure is kept on `lastError` and retried on the next\n * tick; a conflict stops tailing and is thrown by `stop()`.\n */\nexport function startSearchShipper(\n options: SearchShipOptions & { intervalMs?: number },\n): SearchShipper {\n const tail = new SearchLedgerTail(options)\n const intervalMs = options.intervalMs ?? 2000\n const stopping = new AbortController()\n let lastError: Error | null = null\n let conflict: SearchShipConflictError | null = null\n let running: Promise<unknown> = Promise.resolve()\n\n const tick = (): Promise<unknown> => {\n running = running.then(async () => {\n if (conflict) return\n try {\n await tail.ship()\n lastError = null\n } catch (error) {\n if (error instanceof SearchShipConflictError) conflict = error\n else lastError = error instanceof Error ? error : new Error(String(error))\n }\n })\n return running\n }\n\n const loop = (async () => {\n while (!stopping.signal.aborted && !conflict) {\n await tick()\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, intervalMs)\n stopping.signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer)\n resolve()\n },\n { once: true },\n )\n })\n }\n })()\n\n return {\n get lastError() {\n return lastError\n },\n async flush() {\n await tick()\n if (conflict) throw conflict\n if (lastError) throw lastError\n return tail.result()\n },\n async stop() {\n stopping.abort()\n await loop\n await running\n if (conflict) throw conflict\n return tail.ship()\n },\n }\n}\n\n/** One full batch of lines with their newlines, plus a byte to see where a\n * maximal line ends. */\nconst READ_CHUNK_BYTES = SEARCH_LEDGER_BATCH_MAX_BYTES + SEARCH_LEDGER_BATCH_MAX_LINES + 1\n\ninterface LocalLine {\n text: string\n entry: SearchLedgerEntry\n bytes: number\n}\n\nclass SearchLedgerTail {\n private readonly client: HostedClient\n private readonly options: SearchShipOptions\n private readonly content: 'full' | 'digests'\n /** Where the store's chain ends, and the byte offset of that line locally. */\n private head: SearchLedgerHead | null = null\n private offset = 0\n private localLines = 0\n private readonly uploaded = new Set<string>()\n private readonly missing = new Map<string, SearchShipMissingBlob>()\n private readonly counts = {\n linesPosted: 0,\n batches: 0,\n gapResends: 0,\n uploaded: 0,\n stored: 0,\n masked: 0,\n withheld: 0,\n }\n\n constructor(options: SearchShipOptions) {\n this.options = options\n this.content = options.content ?? 'full'\n this.client = createHostedClient(options.tenant)\n }\n\n result(): SearchShipResult {\n const { linesPosted, batches, gapResends, uploaded, stored, masked, withheld } = this.counts\n return {\n searchId: this.options.ledger.searchId,\n head: this.head ?? {\n searchId: this.options.ledger.searchId,\n nextSequence: 0,\n headHash: null,\n },\n localLines: this.localLines,\n linesPosted,\n batches,\n gapResends,\n blobs: {\n content: this.content,\n uploaded,\n stored,\n masked,\n withheld,\n missing: [...this.missing.values()],\n },\n }\n }\n\n async ship(): Promise<SearchShipResult> {\n const { signal } = this.options\n if (this.head === null) {\n await this.syncTo(await this.client.searchLedgerHead(this.options.ledger.searchId, signal))\n }\n for (;;) {\n const lines = await this.readChunk(this.offset, this.head!)\n if (lines.length === 0) return this.result()\n const batch = takeBatch(lines)\n if (this.content === 'full') await this.uploadBlobs(batch)\n const fromSequence = this.head!.nextSequence\n const outcome = await this.client.ingestSearchLedger(\n {\n wireVersion: HOSTED_WIRE_VERSION,\n searchId: this.options.ledger.searchId,\n runKind: this.options.runKind,\n fromSequence,\n lines: batch.map((line) => line.text),\n },\n signal,\n )\n this.counts.batches++\n this.counts.linesPosted += batch.length\n if (outcome.status === 'conflict') {\n if (outcome.conflict.error === 'chain_conflict') {\n throw new SearchShipConflictError(outcome.conflict)\n }\n this.counts.gapResends++\n await this.syncTo(outcome.conflict.head)\n continue\n }\n const last = batch.at(-1)!.entry\n if (\n outcome.head.nextSequence < last.sequence + 1 ||\n (outcome.head.nextSequence === last.sequence + 1 &&\n outcome.head.headHash !== last.entryHash)\n ) {\n throw new Error(\n `store acknowledged sequence ${fromSequence}..${last.sequence} with head ${outcome.head.nextSequence} ${outcome.head.headHash}`,\n )\n }\n await this.syncTo(outcome.head)\n }\n }\n\n /** Move the cursor to the store's head, checking that the local ledger holds\n * the same entry there. A different entry is a fork; a store ahead of the\n * local file holds lines this ledger never wrote. */\n private async syncTo(head: SearchLedgerHead): Promise<void> {\n if (head.searchId !== this.options.ledger.searchId) {\n throw new Error(`store answered for search ${head.searchId}`)\n }\n const fork = (message: string) =>\n new SearchShipConflictError({ error: 'chain_conflict', message, head })\n const empty: SearchLedgerHead = { searchId: head.searchId, nextSequence: 0, headHash: null }\n const forward = this.head !== null && head.nextSequence >= this.head.nextSequence\n let offset = forward ? this.offset : 0\n let cursor = forward ? this.head! : empty\n while (cursor.nextSequence < head.nextSequence) {\n const lines = await this.readChunk(offset, cursor)\n if (lines.length === 0) {\n throw fork(\n `the store holds ${head.nextSequence} entries; the local ledger holds ${cursor.nextSequence}`,\n )\n }\n const take = Math.min(lines.length, head.nextSequence - cursor.nextSequence)\n for (let index = 0; index < take; index++) offset += lines[index]!.bytes\n const last = lines[take - 1]!.entry\n cursor = {\n searchId: head.searchId,\n nextSequence: last.sequence + 1,\n headHash: last.entryHash,\n }\n }\n if (cursor.headHash !== head.headHash) {\n throw fork(`the store's entry ${head.nextSequence - 1} differs from the local ledger's`)\n }\n this.offset = offset\n this.head = head\n }\n\n /** The complete lines in one read chunk from `offset`, checked to continue\n * the chain at `head`. A chunk holds at least one full batch. */\n private async readChunk(offset: number, head: SearchLedgerHead): Promise<LocalLine[]> {\n const bytes = await readRange(this.options.ledger.path, offset, READ_CHUNK_BYTES)\n const lines: LocalLine[] = []\n let start = 0\n let expectedSequence = head.nextSequence\n let previousHash = head.headHash\n for (;;) {\n const newline = bytes.indexOf(0x0a, start)\n // A line without its newline is still being written, or continues in\n // the next chunk.\n if (newline < 0) break\n const text = bytes.toString('utf8', start, newline)\n const entry = JSON.parse(text) as SearchLedgerEntry\n if (\n entry.searchId !== this.options.ledger.searchId ||\n entry.sequence !== expectedSequence ||\n entry.previousHash !== previousHash\n ) {\n throw new Error(\n `${this.options.ledger.path} line ${expectedSequence + 1} does not continue the chain at sequence ${expectedSequence}`,\n )\n }\n lines.push({ text, entry, bytes: newline + 1 - start })\n expectedSequence++\n previousHash = entry.entryHash\n start = newline + 1\n }\n if (lines.length === 0 && bytes.byteLength === READ_CHUNK_BYTES) {\n throw new Error(\n `${this.options.ledger.path} line ${expectedSequence + 1} is longer than ${SEARCH_LEDGER_BATCH_MAX_BYTES} bytes; one request carries at most that`,\n )\n }\n this.localLines = Math.max(this.localLines, expectedSequence)\n return lines\n }\n\n private async uploadBlobs(batch: LocalLine[]): Promise<void> {\n for (const line of batch) {\n for (const ref of searchLedgerArtifactRefs(line.entry.event)) {\n if (this.uploaded.has(ref.sha256) || this.missing.has(ref.sha256)) continue\n const bytes = await readBlob(ref.uri)\n if (typeof bytes === 'string') {\n this.missing.set(ref.sha256, { sha256: ref.sha256, uri: ref.uri, reason: bytes })\n continue\n }\n const digest = `sha256:${createHash('sha256').update(bytes).digest('hex')}`\n if (digest !== ref.sha256 || bytes.byteLength !== ref.byteLength) {\n this.missing.set(ref.sha256, {\n sha256: ref.sha256,\n uri: ref.uri,\n reason: `local bytes are ${digest} (${bytes.byteLength} bytes), not the ref's ${ref.sha256} (${ref.byteLength} bytes)`,\n })\n continue\n }\n const stored = await this.client.putSearchBlob(\n ref.sha256,\n new Uint8Array(bytes),\n ref.uri.endsWith('.json') ? 'application/json' : 'application/octet-stream',\n this.options.signal,\n )\n this.uploaded.add(ref.sha256)\n this.counts.uploaded++\n this.counts[stored.state]++\n }\n }\n }\n}\n\n/** The longest prefix of `lines` within one request's limits. */\nfunction takeBatch(lines: LocalLine[]): LocalLine[] {\n const batch: LocalLine[] = []\n let bytes = 0\n for (const line of lines) {\n const size = line.bytes - 1\n if (size > SEARCH_LEDGER_BATCH_MAX_BYTES) {\n throw new Error(\n `ledger line ${line.entry.sequence} holds ${size} bytes; one request carries at most ${SEARCH_LEDGER_BATCH_MAX_BYTES}`,\n )\n }\n if (\n batch.length === SEARCH_LEDGER_BATCH_MAX_LINES ||\n bytes + size > SEARCH_LEDGER_BATCH_MAX_BYTES\n )\n break\n batch.push(line)\n bytes += size\n }\n return batch\n}\n\n/** Up to `limit` bytes of the file from `offset`; empty before the recorder\n * created the file. */\nasync function readRange(path: string, offset: number, limit: number): Promise<Buffer> {\n let handle: Awaited<ReturnType<typeof open>>\n try {\n handle = await open(path, 'r')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT' && offset === 0) return Buffer.alloc(0)\n throw error\n }\n try {\n const { size } = await handle.stat()\n if (size < offset) throw new Error(`${path} shrank below byte ${offset}`)\n const bytes = Buffer.alloc(Math.min(limit, size - offset))\n let read = 0\n while (read < bytes.byteLength) {\n const { bytesRead } = await handle.read(bytes, read, bytes.byteLength - read, offset + read)\n if (bytesRead === 0) break\n read += bytesRead\n }\n return bytes.subarray(0, read)\n } finally {\n await handle.close()\n }\n}\n\n/** The bytes at a `file:` ref, or why there are none. */\nasync function readBlob(uri: string): Promise<Buffer | string> {\n if (!uri.startsWith('file:'))\n return `the shipper reads only file: refs, not ${uri.split(':')[0]}:`\n try {\n return await readFile(fileURLToPath(uri))\n } catch (error) {\n return `unreadable: ${(error as NodeJS.ErrnoException).code ?? String(error)}`\n }\n}\n\n/**\n * Run a ship that must never fail its caller, such as the one a loop runs at\n * its end. A failure, a store short of the local ledger, or a blob that did\n * not upload is logged with the command that resumes it.\n */\nexport async function shipBestEffort(\n ship: () => Promise<SearchShipResult>,\n ledgerPath: string,\n): Promise<SearchShipResult | undefined> {\n const resume = `agent-eval search ship ${ledgerPath}`\n try {\n const shipped = await ship()\n if (shipped.head.nextSequence < shipped.localLines) {\n console.warn(\n `[agent-eval] search ${shipped.searchId}: the store holds ${shipped.head.nextSequence} of ${shipped.localLines} ledger entries; resume with ${resume}`,\n )\n }\n if (shipped.blobs.missing.length > 0) {\n console.warn(\n `[agent-eval] search ${shipped.searchId}: ${shipped.blobs.missing.length} blob(s) were not uploaded, first: ${shipped.blobs.missing[0]!.reason}`,\n )\n }\n return shipped\n } catch (error) {\n console.warn(\n `[agent-eval] search ledger ship failed (continuing; resume with ${resume}): ${error instanceof Error ? error.message : String(error)}`,\n )\n return undefined\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import "./paired-tests-kASEKpc-.js";
2
- import { r as minimumPairsForPairedDeltaTest } from "./paired-promotion-decision-5Pl_qJLX.js";
1
+ import "./paired-tests-Buh6feJ4.js";
2
+ import { r as minimumPairsForPairedDeltaTest } from "./paired-promotion-decision-CiueaTTj.js";
3
3
  import { a as SearchLedgerError } from "./search-ledger-file-DTBD6X-y.js";
4
- import { w as estimateNode } from "./pareto-DY_x3HHg.js";
4
+ import { w as estimateNode } from "./pareto-B34N-vnn.js";
5
5
  // src/campaign/search-summary.ts
6
6
  /**
7
7
  * The agent's view of a search: the same projection a human reads.
@@ -129,4 +129,4 @@ function formatEstimate(estimate) {
129
129
  }
130
130
  export { searchProposerView as n, renderSearchSummary as t };
131
131
 
132
- //# sourceMappingURL=search-summary-DedyTdiM.js.map
132
+ //# sourceMappingURL=search-summary-DBMjdecs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-summary-DedyTdiM.js","names":[],"sources":["../src/campaign/search-summary.ts"],"sourcesContent":["/**\n * The agent's view of a search: the same projection a human reads.\n *\n * `renderSearchSummary` turns a `SearchStateView` into compact text — the\n * leading nodes with their estimates, the most recently discarded ideas with\n * why they were discarded, and a log of recent proposals — as AIDE's journal\n * summary does for its own search. `agent-eval search show <ledger>` prints\n * the same text for a terminal; `runOptimization` puts it on every\n * `ProposeContext` a proposer receives.\n *\n * `searchProposerView` is the train-only read `ProposeContext.train` carries.\n * It has no parameter that can select another split, so a proposer built on\n * it cannot reach the selection or test split even by mistake: the sealed\n * splits (§6.6 of the search-tree design) are unreachable by construction,\n * not by convention.\n */\n\nimport { minimumPairsForPairedDeltaTest } from '../paired-delta-test'\nimport { BOOTSTRAP_GATE_MIN_N } from '../statistics'\nimport { estimateNode } from './estimate-node'\nimport { SearchLedgerError } from './search-ledger-errors'\nimport type { NodeEstimate, SearchSplit } from './search-ledger-types'\nimport type { SearchNode, SearchScoredCell, SearchStateView, SearchUnitScore } from './search-state'\n\nconst DESCRIPTIVE_FROM = minimumPairsForPairedDeltaTest(0.95)\n\n/** The proposer's own read of a search: its node's cells on the train split,\n * the only split a proposer may ever see. */\nexport interface SearchProposerView {\n readonly searchId: string\n readonly direction: 'maximize' | 'minimize'\n /** `nodeId`'s scored train cells. Unscored cells (errored, cancelled or in\n * flight) are absent, never zero. */\n scoredCells(nodeId: string): readonly SearchScoredCell[]\n /** Per-unit train-split means of `nodeId`, in unitId order. */\n unitScores(nodeId: string): readonly SearchUnitScore[]\n}\n\n/** The train-only view `ProposeContext.train` carries. */\nexport function searchProposerView(state: SearchStateView): SearchProposerView {\n const header = state.header\n if (!header) throw new SearchLedgerError(`search ${state.searchId} has not been opened`)\n return {\n searchId: state.searchId,\n direction: header.objective.direction,\n scoredCells: (nodeId) => state.scoredCells(nodeId, 'train'),\n unitScores: (nodeId) => state.unitScores(nodeId, 'train'),\n }\n}\n\nexport interface SearchSummaryOptions {\n /**\n * The split every estimate in the summary is computed on. A proposer's\n * context must always pass `'train'`: the selection and test splits stay\n * unread by anything a proposer's text can reach. The CLI passes the\n * search's own ranking split, `'selection'` when the search declares one\n * else `'train'`, matching `searchPolicyView`.\n */\n split: SearchSplit\n /** Nodes shown under \"Leading\" and under \"Recently discarded\". Default 5. */\n limit?: number\n}\n\n/**\n * `SearchState` as compact text: a header line with the search's shape and\n * spend, the leading nodes against the root with their estimates, the most\n * recently discarded nodes with the measurement that discarded them, and a\n * log of recent proposals (operator and label — the ledger's inline,\n * redacted summary of each rationale; the full rationale is a blob this\n * function never reads, so it stays a pure, I/O-free projection of the\n * state already in hand).\n */\nexport function renderSearchSummary(state: SearchStateView, options: SearchSummaryOptions): string {\n const header = state.header\n if (!header) return `search ${state.searchId}: not yet opened`\n const limit = options.limit ?? 5\n if (limit < 1 || !Number.isInteger(limit)) {\n throw new SearchLedgerError(\n `renderSearchSummary: limit must be a positive integer, got ${limit}`,\n )\n }\n const split = options.split\n const direction = header.objective.direction\n const root = state.rootNodeId\n const lines: string[] = [headerLine(state), spendLine(state), statusLine(state)]\n\n if (root === null) {\n lines.push('', 'No nodes yet.')\n return lines.join('\\n')\n }\n\n const others = state.nodes().filter((node) => node.nodeId !== root)\n const estimated = others\n .filter((node) => node.status !== 'invalid')\n .map((node) => ({ node, estimate: estimateNode(state, node.nodeId, { against: root, split }) }))\n\n const leading = estimated\n .filter(({ estimate }) => estimate.delta !== null)\n .sort((a, b) => signed(b.estimate, direction) - signed(a.estimate, direction))\n .slice(0, limit)\n lines.push('', `Leading nodes (vs root, ${split} split):`)\n if (leading.length === 0) lines.push(' none measured yet')\n for (const { node, estimate } of leading)\n lines.push(` ${nodeLine(node)}: ${formatEstimate(estimate)}`)\n\n const discarded = others\n .filter(\n (node) => node.status === 'pruned' || node.status === 'rejected' || node.status === 'invalid',\n )\n .sort((a, b) => b.updatedSequence - a.updatedSequence)\n .slice(0, limit)\n lines.push('', 'Recently discarded:')\n if (discarded.length === 0) lines.push(' none yet')\n for (const node of discarded) lines.push(` ${nodeLine(node)}: ${discardLine(node)}`)\n\n const recent = [...state.edges()].reverse().slice(0, limit)\n lines.push('', 'Recent proposals:')\n if (recent.length === 0) lines.push(' none yet')\n for (const edge of recent) {\n const childId = edge.childNodeId\n const label = edge.label.length > 0 ? edge.label : '(no label)'\n lines.push(` ${edge.operator} → ${childId}: ${label}`)\n }\n\n return lines.join('\\n')\n}\n\nfunction headerLine(state: SearchStateView): string {\n const header = state.header!\n return `search ${state.searchId} — ${header.subject} — ${header.objective.direction} ${header.objective.metric}`\n}\n\nfunction spendLine(state: SearchStateView): string {\n const { audit } = state\n const known = audit.spend.knownUsd.toFixed(2)\n const floor = audit.spend.floorUsd.toFixed(2)\n const unknown = audit.spend.unknownCostCells\n const floorPart =\n unknown > 0\n ? ` + floor $${floor} across ${unknown} unknown-cost cell${unknown === 1 ? '' : 's'}`\n : ''\n return ` ${audit.nodes} nodes · ${audit.cells.settled} settled cells (${audit.cells.open} open) · $${known} known${floorPart}`\n}\n\nfunction statusLine(state: SearchStateView): string {\n if (!state.closed) return ' status: open'\n const claim = state.closed.claim\n const claimText =\n claim === null\n ? ''\n : claim.decision === 'ship'\n ? ` · claim: ship ${claim.selected}`\n : ` · claim: ${claim.decision}`\n return ` status: closed (${state.closed.reason})${claimText}`\n}\n\nfunction nodeLine(node: SearchNode): string {\n const rung = node.status === 'advanced' ? ` rung ${node.rung}` : ''\n return `${node.nodeId} (${node.status ?? 'undecided'}${rung})`\n}\n\nfunction discardLine(node: SearchNode): string {\n const decision = node.decisions.at(-1)\n if (!decision) return 'no decision recorded'\n const basis = decision.basis === null ? 'no measured basis' : formatEstimate(decision.basis)\n return `${basis} — ${decision.rule}: ${decision.reason}`\n}\n\n/** `estimate.delta` in the objective's own units; positive is always better. */\nfunction signed(estimate: NodeEstimate, direction: 'maximize' | 'minimize'): number {\n if (estimate.delta === null) return Number.NEGATIVE_INFINITY\n return direction === 'maximize' ? estimate.delta : -estimate.delta\n}\n\n/** The display rules of search-tree-design §6.4: no whisker below 6 shared\n * units, a dashed descriptive interval with its exact sign p from 6 to 19,\n * a decision-grade whisker from `BOOTSTRAP_GATE_MIN_N`. */\nfunction formatEstimate(estimate: NodeEstimate): string {\n const unit = (n: number) => `${n} unit${n === 1 ? '' : 's'}`\n if (estimate.method === 'none') return `unknown (${unit(estimate.pairs)})`\n if (estimate.indeterminate)\n return `no measurable difference (indeterminate, ${unit(estimate.pairs)})`\n const delta =\n estimate.delta === null ? 'null' : (estimate.delta >= 0 ? '+' : '') + estimate.delta.toFixed(4)\n if (estimate.method === 'insufficient') {\n return `Δ=${delta} (${estimate.pairs} of ${DESCRIPTIVE_FROM} units, insufficient)`\n }\n const interval = estimate.interval\n ? ` [${estimate.interval[0].toFixed(4)}, ${estimate.interval[1].toFixed(4)}]`\n : ''\n if (estimate.method === 'descriptive') {\n const signP = estimate.exactSignP === null ? 'null' : estimate.exactSignP.toPrecision(3)\n return `Δ=${delta}${interval} dashed, sign p=${signP} (${unit(estimate.pairs)})`\n }\n return `Δ=${delta}${interval} (${unit(estimate.pairs)}, ≥${BOOTSTRAP_GATE_MIN_N})`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,mBAAmB,+BAA+B,GAAI;;AAe5D,SAAgB,mBAAmB,OAA4C;CAC7E,MAAM,SAAS,MAAM;CACrB,IAAI,CAAC,QAAQ,MAAM,IAAI,kBAAkB,UAAU,MAAM,SAAS,qBAAqB;CACvF,OAAO;EACL,UAAU,MAAM;EAChB,WAAW,OAAO,UAAU;EAC5B,cAAc,WAAW,MAAM,YAAY,QAAQ,OAAO;EAC1D,aAAa,WAAW,MAAM,WAAW,QAAQ,OAAO;CAC1D;AACF;;;;;;;;;;AAwBA,SAAgB,oBAAoB,OAAwB,SAAuC;CACjG,MAAM,SAAS,MAAM;CACrB,IAAI,CAAC,QAAQ,OAAO,UAAU,MAAM,SAAS;CAC7C,MAAM,QAAQ,QAAQ,SAAS;CAC/B,IAAI,QAAQ,KAAK,CAAC,OAAO,UAAU,KAAK,GACtC,MAAM,IAAI,kBACR,8DAA8D,OAChE;CAEF,MAAM,QAAQ,QAAQ;CACtB,MAAM,YAAY,OAAO,UAAU;CACnC,MAAM,OAAO,MAAM;CACnB,MAAM,QAAkB;EAAC,WAAW,KAAK;EAAG,UAAU,KAAK;EAAG,WAAW,KAAK;CAAC;CAE/E,IAAI,SAAS,MAAM;EACjB,MAAM,KAAK,IAAI,eAAe;EAC9B,OAAO,MAAM,KAAK,IAAI;CACxB;CAEA,MAAM,SAAS,MAAM,MAAM,CAAC,CAAC,QAAQ,SAAS,KAAK,WAAW,IAAI;CAKlE,MAAM,UAJY,OACf,QAAQ,SAAS,KAAK,WAAW,SAAS,CAAC,CAC3C,KAAK,UAAU;EAAE;EAAM,UAAU,aAAa,OAAO,KAAK,QAAQ;GAAE,SAAS;GAAM;EAAM,CAAC;CAAE,EAEvE,CAAC,CACtB,QAAQ,EAAE,eAAe,SAAS,UAAU,IAAI,CAAC,CACjD,MAAM,GAAG,MAAM,OAAO,EAAE,UAAU,SAAS,IAAI,OAAO,EAAE,UAAU,SAAS,CAAC,CAAC,CAC7E,MAAM,GAAG,KAAK;CACjB,MAAM,KAAK,IAAI,2BAA2B,MAAM,SAAS;CACzD,IAAI,QAAQ,WAAW,GAAG,MAAM,KAAK,qBAAqB;CAC1D,KAAK,MAAM,EAAE,MAAM,cAAc,SAC/B,MAAM,KAAK,KAAK,SAAS,IAAI,EAAE,IAAI,eAAe,QAAQ,GAAG;CAE/D,MAAM,YAAY,OACf,QACE,SAAS,KAAK,WAAW,YAAY,KAAK,WAAW,cAAc,KAAK,WAAW,SACtF,CAAC,CACA,MAAM,GAAG,MAAM,EAAE,kBAAkB,EAAE,eAAe,CAAC,CACrD,MAAM,GAAG,KAAK;CACjB,MAAM,KAAK,IAAI,qBAAqB;CACpC,IAAI,UAAU,WAAW,GAAG,MAAM,KAAK,YAAY;CACnD,KAAK,MAAM,QAAQ,WAAW,MAAM,KAAK,KAAK,SAAS,IAAI,EAAE,IAAI,YAAY,IAAI,GAAG;CAEpF,MAAM,SAAS,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,KAAK;CAC1D,MAAM,KAAK,IAAI,mBAAmB;CAClC,IAAI,OAAO,WAAW,GAAG,MAAM,KAAK,YAAY;CAChD,KAAK,MAAM,QAAQ,QAAQ;EACzB,MAAM,UAAU,KAAK;EACrB,MAAM,QAAQ,KAAK,MAAM,SAAS,IAAI,KAAK,QAAQ;EACnD,MAAM,KAAK,KAAK,KAAK,SAAS,KAAK,QAAQ,IAAI,OAAO;CACxD;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,WAAW,OAAgC;CAClD,MAAM,SAAS,MAAM;CACrB,OAAO,UAAU,MAAM,SAAS,KAAK,OAAO,QAAQ,KAAK,OAAO,UAAU,UAAU,GAAG,OAAO,UAAU;AAC1G;AAEA,SAAS,UAAU,OAAgC;CACjD,MAAM,EAAE,UAAU;CAClB,MAAM,QAAQ,MAAM,MAAM,SAAS,QAAQ,CAAC;CAC5C,MAAM,QAAQ,MAAM,MAAM,SAAS,QAAQ,CAAC;CAC5C,MAAM,UAAU,MAAM,MAAM;CAC5B,MAAM,YACJ,UAAU,IACN,aAAa,MAAM,UAAU,QAAQ,oBAAoB,YAAY,IAAI,KAAK,QAC9E;CACN,OAAO,KAAK,MAAM,MAAM,WAAW,MAAM,MAAM,QAAQ,kBAAkB,MAAM,MAAM,KAAK,YAAY,MAAM,QAAQ;AACtH;AAEA,SAAS,WAAW,OAAgC;CAClD,IAAI,CAAC,MAAM,QAAQ,OAAO;CAC1B,MAAM,QAAQ,MAAM,OAAO;CAC3B,MAAM,YACJ,UAAU,OACN,KACA,MAAM,aAAa,SACjB,kBAAkB,MAAM,aACxB,aAAa,MAAM;CAC3B,OAAO,qBAAqB,MAAM,OAAO,OAAO,GAAG;AACrD;AAEA,SAAS,SAAS,MAA0B;CAC1C,MAAM,OAAO,KAAK,WAAW,aAAa,SAAS,KAAK,SAAS;CACjE,OAAO,GAAG,KAAK,OAAO,IAAI,KAAK,UAAU,cAAc,KAAK;AAC9D;AAEA,SAAS,YAAY,MAA0B;CAC7C,MAAM,WAAW,KAAK,UAAU,GAAG,EAAE;CACrC,IAAI,CAAC,UAAU,OAAO;CAEtB,OAAO,GADO,SAAS,UAAU,OAAO,sBAAsB,eAAe,SAAS,KAAK,EAC3E,KAAK,SAAS,KAAK,IAAI,SAAS;AAClD;;AAGA,SAAS,OAAO,UAAwB,WAA4C;CAClF,IAAI,SAAS,UAAU,MAAM,OAAO,OAAO;CAC3C,OAAO,cAAc,aAAa,SAAS,QAAQ,CAAC,SAAS;AAC/D;;;;AAKA,SAAS,eAAe,UAAgC;CACtD,MAAM,QAAQ,MAAc,GAAG,EAAE,OAAO,MAAM,IAAI,KAAK;CACvD,IAAI,SAAS,WAAW,QAAQ,OAAO,YAAY,KAAK,SAAS,KAAK,EAAE;CACxE,IAAI,SAAS,eACX,OAAO,4CAA4C,KAAK,SAAS,KAAK,EAAE;CAC1E,MAAM,QACJ,SAAS,UAAU,OAAO,UAAU,SAAS,SAAS,IAAI,MAAM,MAAM,SAAS,MAAM,QAAQ,CAAC;CAChG,IAAI,SAAS,WAAW,gBACtB,OAAO,KAAK,MAAM,IAAI,SAAS,MAAM,MAAM,iBAAiB;CAE9D,MAAM,WAAW,SAAS,WACtB,KAAK,SAAS,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,SAAS,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,KACzE;CACJ,IAAI,SAAS,WAAW,eAEtB,OAAO,KAAK,QAAQ,SAAS,kBADf,SAAS,eAAe,OAAO,SAAS,SAAS,WAAW,YAAY,CAAC,EAClC,IAAI,KAAK,SAAS,KAAK,EAAE;CAEhF,OAAO,KAAK,QAAQ,SAAS,IAAI,KAAK,SAAS,KAAK,EAAE;AACxD"}
1
+ {"version":3,"file":"search-summary-DBMjdecs.js","names":[],"sources":["../src/campaign/search-summary.ts"],"sourcesContent":["/**\n * The agent's view of a search: the same projection a human reads.\n *\n * `renderSearchSummary` turns a `SearchStateView` into compact text — the\n * leading nodes with their estimates, the most recently discarded ideas with\n * why they were discarded, and a log of recent proposals — as AIDE's journal\n * summary does for its own search. `agent-eval search show <ledger>` prints\n * the same text for a terminal; `runOptimization` puts it on every\n * `ProposeContext` a proposer receives.\n *\n * `searchProposerView` is the train-only read `ProposeContext.train` carries.\n * It has no parameter that can select another split, so a proposer built on\n * it cannot reach the selection or test split even by mistake: the sealed\n * splits (§6.6 of the search-tree design) are unreachable by construction,\n * not by convention.\n */\n\nimport { minimumPairsForPairedDeltaTest } from '../paired-delta-test'\nimport { BOOTSTRAP_GATE_MIN_N } from '../statistics'\nimport { estimateNode } from './estimate-node'\nimport { SearchLedgerError } from './search-ledger-errors'\nimport type { NodeEstimate, SearchSplit } from './search-ledger-types'\nimport type { SearchNode, SearchScoredCell, SearchStateView, SearchUnitScore } from './search-state'\n\nconst DESCRIPTIVE_FROM = minimumPairsForPairedDeltaTest(0.95)\n\n/** The proposer's own read of a search: its node's cells on the train split,\n * the only split a proposer may ever see. */\nexport interface SearchProposerView {\n readonly searchId: string\n readonly direction: 'maximize' | 'minimize'\n /** `nodeId`'s scored train cells. Unscored cells (errored, cancelled or in\n * flight) are absent, never zero. */\n scoredCells(nodeId: string): readonly SearchScoredCell[]\n /** Per-unit train-split means of `nodeId`, in unitId order. */\n unitScores(nodeId: string): readonly SearchUnitScore[]\n}\n\n/** The train-only view `ProposeContext.train` carries. */\nexport function searchProposerView(state: SearchStateView): SearchProposerView {\n const header = state.header\n if (!header) throw new SearchLedgerError(`search ${state.searchId} has not been opened`)\n return {\n searchId: state.searchId,\n direction: header.objective.direction,\n scoredCells: (nodeId) => state.scoredCells(nodeId, 'train'),\n unitScores: (nodeId) => state.unitScores(nodeId, 'train'),\n }\n}\n\nexport interface SearchSummaryOptions {\n /**\n * The split every estimate in the summary is computed on. A proposer's\n * context must always pass `'train'`: the selection and test splits stay\n * unread by anything a proposer's text can reach. The CLI passes the\n * search's own ranking split, `'selection'` when the search declares one\n * else `'train'`, matching `searchPolicyView`.\n */\n split: SearchSplit\n /** Nodes shown under \"Leading\" and under \"Recently discarded\". Default 5. */\n limit?: number\n}\n\n/**\n * `SearchState` as compact text: a header line with the search's shape and\n * spend, the leading nodes against the root with their estimates, the most\n * recently discarded nodes with the measurement that discarded them, and a\n * log of recent proposals (operator and label — the ledger's inline,\n * redacted summary of each rationale; the full rationale is a blob this\n * function never reads, so it stays a pure, I/O-free projection of the\n * state already in hand).\n */\nexport function renderSearchSummary(state: SearchStateView, options: SearchSummaryOptions): string {\n const header = state.header\n if (!header) return `search ${state.searchId}: not yet opened`\n const limit = options.limit ?? 5\n if (limit < 1 || !Number.isInteger(limit)) {\n throw new SearchLedgerError(\n `renderSearchSummary: limit must be a positive integer, got ${limit}`,\n )\n }\n const split = options.split\n const direction = header.objective.direction\n const root = state.rootNodeId\n const lines: string[] = [headerLine(state), spendLine(state), statusLine(state)]\n\n if (root === null) {\n lines.push('', 'No nodes yet.')\n return lines.join('\\n')\n }\n\n const others = state.nodes().filter((node) => node.nodeId !== root)\n const estimated = others\n .filter((node) => node.status !== 'invalid')\n .map((node) => ({ node, estimate: estimateNode(state, node.nodeId, { against: root, split }) }))\n\n const leading = estimated\n .filter(({ estimate }) => estimate.delta !== null)\n .sort((a, b) => signed(b.estimate, direction) - signed(a.estimate, direction))\n .slice(0, limit)\n lines.push('', `Leading nodes (vs root, ${split} split):`)\n if (leading.length === 0) lines.push(' none measured yet')\n for (const { node, estimate } of leading)\n lines.push(` ${nodeLine(node)}: ${formatEstimate(estimate)}`)\n\n const discarded = others\n .filter(\n (node) => node.status === 'pruned' || node.status === 'rejected' || node.status === 'invalid',\n )\n .sort((a, b) => b.updatedSequence - a.updatedSequence)\n .slice(0, limit)\n lines.push('', 'Recently discarded:')\n if (discarded.length === 0) lines.push(' none yet')\n for (const node of discarded) lines.push(` ${nodeLine(node)}: ${discardLine(node)}`)\n\n const recent = [...state.edges()].reverse().slice(0, limit)\n lines.push('', 'Recent proposals:')\n if (recent.length === 0) lines.push(' none yet')\n for (const edge of recent) {\n const childId = edge.childNodeId\n const label = edge.label.length > 0 ? edge.label : '(no label)'\n lines.push(` ${edge.operator} → ${childId}: ${label}`)\n }\n\n return lines.join('\\n')\n}\n\nfunction headerLine(state: SearchStateView): string {\n const header = state.header!\n return `search ${state.searchId} — ${header.subject} — ${header.objective.direction} ${header.objective.metric}`\n}\n\nfunction spendLine(state: SearchStateView): string {\n const { audit } = state\n const known = audit.spend.knownUsd.toFixed(2)\n const floor = audit.spend.floorUsd.toFixed(2)\n const unknown = audit.spend.unknownCostCells\n const floorPart =\n unknown > 0\n ? ` + floor $${floor} across ${unknown} unknown-cost cell${unknown === 1 ? '' : 's'}`\n : ''\n return ` ${audit.nodes} nodes · ${audit.cells.settled} settled cells (${audit.cells.open} open) · $${known} known${floorPart}`\n}\n\nfunction statusLine(state: SearchStateView): string {\n if (!state.closed) return ' status: open'\n const claim = state.closed.claim\n const claimText =\n claim === null\n ? ''\n : claim.decision === 'ship'\n ? ` · claim: ship ${claim.selected}`\n : ` · claim: ${claim.decision}`\n return ` status: closed (${state.closed.reason})${claimText}`\n}\n\nfunction nodeLine(node: SearchNode): string {\n const rung = node.status === 'advanced' ? ` rung ${node.rung}` : ''\n return `${node.nodeId} (${node.status ?? 'undecided'}${rung})`\n}\n\nfunction discardLine(node: SearchNode): string {\n const decision = node.decisions.at(-1)\n if (!decision) return 'no decision recorded'\n const basis = decision.basis === null ? 'no measured basis' : formatEstimate(decision.basis)\n return `${basis} — ${decision.rule}: ${decision.reason}`\n}\n\n/** `estimate.delta` in the objective's own units; positive is always better. */\nfunction signed(estimate: NodeEstimate, direction: 'maximize' | 'minimize'): number {\n if (estimate.delta === null) return Number.NEGATIVE_INFINITY\n return direction === 'maximize' ? estimate.delta : -estimate.delta\n}\n\n/** The display rules of search-tree-design §6.4: no whisker below 6 shared\n * units, a dashed descriptive interval with its exact sign p from 6 to 19,\n * a decision-grade whisker from `BOOTSTRAP_GATE_MIN_N`. */\nfunction formatEstimate(estimate: NodeEstimate): string {\n const unit = (n: number) => `${n} unit${n === 1 ? '' : 's'}`\n if (estimate.method === 'none') return `unknown (${unit(estimate.pairs)})`\n if (estimate.indeterminate)\n return `no measurable difference (indeterminate, ${unit(estimate.pairs)})`\n const delta =\n estimate.delta === null ? 'null' : (estimate.delta >= 0 ? '+' : '') + estimate.delta.toFixed(4)\n if (estimate.method === 'insufficient') {\n return `Δ=${delta} (${estimate.pairs} of ${DESCRIPTIVE_FROM} units, insufficient)`\n }\n const interval = estimate.interval\n ? ` [${estimate.interval[0].toFixed(4)}, ${estimate.interval[1].toFixed(4)}]`\n : ''\n if (estimate.method === 'descriptive') {\n const signP = estimate.exactSignP === null ? 'null' : estimate.exactSignP.toPrecision(3)\n return `Δ=${delta}${interval} dashed, sign p=${signP} (${unit(estimate.pairs)})`\n }\n return `Δ=${delta}${interval} (${unit(estimate.pairs)}, ≥${BOOTSTRAP_GATE_MIN_N})`\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { u as manifestContentDigest } from "./agent-profile-cell-CT5JYueM.js";
2
2
  import { t as mulberry32 } from "./random-DrzcgC7F.js";
3
3
  import { t as eProcess } from "./sequential-eprocess-C1YLwFaz.js";
4
- import { n as aggregatePairedHoldout, o as pairHoldout } from "./statistical-heldout-DYF5f10K.js";
4
+ import { n as aggregatePairedHoldout, o as pairHoldout } from "./statistical-heldout-NBxC7jGo.js";
5
5
  // src/campaign/gates/sequential.ts
6
6
  /**
7
7
  * Anytime-valid sequential promotion gate — an e-process (betting
@@ -337,4 +337,4 @@ function sequentialDecide(options = {}) {
337
337
  }
338
338
  export { sequentialPairedGate as n, sequentialDecide as t };
339
339
 
340
- //# sourceMappingURL=sequential-Zp1tsxqI.js.map
340
+ //# sourceMappingURL=sequential-BPbvgzfe.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sequential-Zp1tsxqI.js","names":[],"sources":["../src/campaign/gates/sequential.ts"],"sourcesContent":["/**\n * Anytime-valid sequential promotion gate — an e-process (betting\n * test-martingale, see `eProcess` in `statistics.ts`) over paired\n * per-scenario deltas, so a campaign stops the MOMENT evidence decides\n * instead of burning a fixed-n budget. Decisions remain valid at any\n * data-dependent stopping time (Ville's inequality), which is exactly what\n * fixed-n machinery cannot offer: peeking at a bootstrap CI after every\n * observation and stopping on the first significant peek inflates type-I\n * error far beyond alpha.\n *\n * REPLACES, never layers on, a fixed-n gate. Running `heldoutSignificance`\n * or `paretoSignificanceGate` repeatedly on a growing sample and stopping\n * early is optional stopping no matter how it is dressed up; this gate is\n * the valid way to stop early. Use one or the other per evidence stream.\n *\n * Pre-registration binding: anytime validity holds only for the\n * PRE-REGISTERED statistic. When a `SignedManifest` is bound, the gate takes\n * alpha from `manifest.alpha`, the observation budget from\n * `manifest.preRegisteredN`, orients deltas by `manifest.direction`, and\n * shifts the null boundary by `manifest.minEffect` — re-deciding the same\n * stream under different parameters after seeing data would reopen optional\n * stopping under a fancier name. The manifest's content hash is verified at\n * construction with the canonical RFC 8785 digest used by signManifest.\n *\n * The supermartingale guarantee requires E[delta_t | past] ≤ the null\n * boundary under H0. Independent sampling with that mean bound suffices.\n * Exchangeability or shuffling alone does not: one fair sign repeated many\n * times is exchangeable and has marginal mean zero, but its conditional mean\n * becomes the revealed sign. A seeded shuffle cannot remove that dependence.\n * `decide(ctx)` pairs cells, then averages repetitions within each scenario\n * before testing. Pass independentUnitByScenarioId for scenarios sharing a\n * latent source or incident. Units must still satisfy the conditional-mean null.\n * Direct `observe()` callers own this sampling and aggregation contract.\n */\n\nimport { manifestContentDigest, type SignedManifest } from '../../pre-registration'\nimport { type EProcessState, eProcess, mulberry32 } from '../../statistics'\nimport type { Gate, GateContext, GateResult, GenerationRecord, Scenario } from '../types'\nimport { aggregatePairedHoldout, pairHoldout } from './statistical-heldout'\n\nexport type SequentialGateDecision = 'promote' | 'continue' | 'undecided-at-maxN'\n\nexport interface SequentialObservation {\n decision: SequentialGateDecision\n /** Current e-value (the betting wealth) against H0. */\n eValue: number\n /** Paired deltas consumed so far. */\n n: number\n /** Names the decision basis. For 'undecided-at-maxN' it states explicitly\n * that exhausting the budget is NOT evidence of no effect. */\n reason: string\n}\n\nexport interface SequentialPairedGateOptions {\n /** Type-I budget. With `preRegistration` bound this MUST match\n * `manifest.alpha` (conflict throws). Default 0.05. */\n alpha?: number\n /** Minimum independent paired units before a promote may fire. The stopping rule is\n * \"first n ≥ minN with e-value ≥ 1/alpha\" — still a valid stopping time.\n * Default 5. */\n minN?: number\n /** Pre-registered independent-unit budget (scenarios by default in decide()). Required unless `preRegistration`\n * supplies it via `preRegisteredN` (conflict throws). */\n maxN?: number\n /** Bet truncation forwarded to `eProcess`. Default 0.5. */\n maxBet?: number\n /** Bound on |delta| in the judge's native scale; deltas are mapped to\n * x = (d/scale + 1)/2 ∈ [0,1]. A delta outside ±scale throws (use\n * `detectScale` to pick 1 vs 100 BEFORE streaming). Default 1. */\n scale?: number\n /** Seed for reproducible ordering of scenario deltas in decide().\n * Shuffling does not establish the conditional-mean null. Default 1337. */\n shuffleSeed?: number\n /** Independent unit for every scenario passed to decide(). Full cell pairs\n * are averaged within each unit before testing. Defaults to scenario IDs.\n * The mapping is copied at construction. Direct observe() is unchanged. */\n independentUnitByScenarioId?: ReadonlyMap<string, string>\n /** Bind the pre-registered hypothesis. Verified (content hash) at\n * construction; alpha/maxN/direction/minEffect come FROM the manifest. */\n preRegistration?: SignedManifest\n /** Override the gate name in reports. */\n name?: string\n /** Continue the observe-stream from a `state()` snapshot taken before a\n * restart. The gate's parameters still resolve from this options object\n * (and the manifest); the snapshot must have been recorded under the same\n * alpha, maxBet, and null boundary, and its decision must be one this\n * configuration could have reached at its n, or construction throws.\n * `decide(ctx)` is unaffected — it always runs on a fresh stream. */\n resume?: SequentialStreamState\n}\n\n/** Snapshot of one observe-stream: the e-process state plus the gate's own\n * decision, which applies `minN` and `maxN` on top of the core latch. A\n * gate rebuilt with `resume` from this value continues exactly where the\n * snapshot was taken. */\nexport type SequentialStreamState = EProcessState & { decision: SequentialGateDecision }\n\nexport interface SequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>\n extends Gate<TArtifact, TScenario> {\n /** Streaming entry point: feed one paired independent-unit delta\n * (candidate − baseline, native scale). Aggregate correlated repetitions\n * before calling; the caller must justify E[delta_t | past] under H0.\n * Each gate instance carries ONE\n * observe-stream; `decide(ctx)` runs on its own fresh stream and never\n * consumes or advances this one. 'promote' is sticky; observing past the\n * pre-registered maxN throws (extending a finished stream after seeing\n * the result reopens optional stopping — start a NEW pre-registered\n * test). */\n observe(delta: number): SequentialObservation\n /** Read-only snapshot of the observe-stream. Pass it back as\n * `resume` to continue the stream after a restart. */\n state(): SequentialStreamState\n}\n\ninterface ResolvedConfig {\n alpha: number\n minN: number\n maxN: number\n maxBet: number\n scale: number\n shuffleSeed: number\n /** 'decrease' negates deltas so \"better\" is always positive. */\n direction: 'increase' | 'decrease'\n /** H0 boundary in x-space: 1/2 + minEffect/(2·scale). */\n nullMean: number\n minEffect: number\n}\n\n/** Sync twin of `verifyManifest`, so gate construction stays synchronous and\n * fails loud before any observation is consumed. The digest scheme belongs to\n * `pre-registration`; this gate only compares. */\nfunction verifyManifestSync(m: SignedManifest): boolean {\n return manifestContentDigest(m) === m.contentHash\n}\n\nfunction resolveConfig(opts: SequentialPairedGateOptions): ResolvedConfig {\n const m = opts.preRegistration\n let alpha: number\n let maxN: number\n let direction: 'increase' | 'decrease'\n let minEffect: number\n if (m) {\n if (!verifyManifestSync(m)) {\n throw new Error(\n `sequentialPairedGate: pre-registration manifest '${m.id}' content hash mismatch (tampered)`,\n )\n }\n if (opts.alpha !== undefined && opts.alpha !== m.alpha) {\n throw new Error(\n `sequentialPairedGate: alpha ${opts.alpha} conflicts with pre-registered alpha ${m.alpha} — ` +\n `the registered statistic is the only one anytime validity covers`,\n )\n }\n if (opts.maxN !== undefined && opts.maxN !== m.preRegisteredN) {\n throw new Error(\n `sequentialPairedGate: maxN ${opts.maxN} conflicts with pre-registered N ${m.preRegisteredN}`,\n )\n }\n alpha = m.alpha\n maxN = m.preRegisteredN\n direction = m.direction\n minEffect = m.minEffect\n } else {\n alpha = opts.alpha ?? 0.05\n if (opts.maxN === undefined) {\n throw new Error(\n 'sequentialPairedGate: maxN is required (or bind a preRegistration manifest whose ' +\n 'preRegisteredN is the budget) — an unbounded stream has no pre-registered budget',\n )\n }\n maxN = opts.maxN\n direction = 'increase'\n minEffect = 0\n }\n const scale = opts.scale ?? 1\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`sequentialPairedGate: scale must be > 0, got ${scale}`)\n }\n if (!Number.isInteger(maxN) || maxN < 1) {\n throw new Error(`sequentialPairedGate: maxN must be a positive integer, got ${maxN}`)\n }\n const minN = opts.minN ?? 5\n if (!Number.isInteger(minN) || minN < 1 || minN > maxN) {\n throw new Error(\n `sequentialPairedGate: minN must be an integer in [1, maxN=${maxN}], got ${minN}`,\n )\n }\n if (!Number.isFinite(minEffect) || minEffect < 0 || minEffect >= scale) {\n throw new Error(\n `sequentialPairedGate: minEffect must be in [0, scale=${scale}), got ${minEffect}`,\n )\n }\n // H0: mean delta ≤ minEffect. In x = (d/scale + 1)/2 space that is\n // E[x] ≤ 1/2 + minEffect/(2·scale) — a shifted null boundary, NOT a clamp\n // of the observations (clamping is mean-distorting for skewed deltas).\n const nullMean = 0.5 + minEffect / (2 * scale)\n return {\n alpha,\n minN,\n maxN,\n maxBet: opts.maxBet ?? 0.5,\n scale,\n shuffleSeed: opts.shuffleSeed ?? 1337,\n direction,\n nullMean,\n minEffect,\n }\n}\n\ninterface SequentialStream {\n observe(delta: number): SequentialObservation\n state(): SequentialStreamState\n}\n\nconst SEQUENTIAL_DECISIONS: readonly SequentialGateDecision[] = [\n 'promote',\n 'continue',\n 'undecided-at-maxN',\n]\n\n/** Refuse a snapshot whose gate decision this configuration could not have\n * reached at the snapshot's n. The e-process parameters are checked by\n * `eProcess` itself; this covers the gate's own stopping rule. */\nfunction assertResumableStream(resume: SequentialStreamState, cfg: ResolvedConfig): void {\n const fail = (detail: string): never => {\n throw new Error(`sequentialPairedGate: cannot resume — ${detail}`)\n }\n if (!SEQUENTIAL_DECISIONS.includes(resume.decision)) {\n fail(`unknown decision '${String(resume.decision)}'`)\n }\n if (resume.n > cfg.maxN) {\n fail(`snapshot n=${resume.n} exceeds the pre-registered maxN=${cfg.maxN}`)\n }\n const threshold = 1 / cfg.alpha\n switch (resume.decision) {\n case 'promote':\n if (resume.n < cfg.minN) {\n fail(`decision 'promote' at n=${resume.n} is below minN=${cfg.minN}`)\n }\n break\n case 'undecided-at-maxN':\n if (resume.n !== cfg.maxN) {\n fail(`decision 'undecided-at-maxN' at n=${resume.n} does not match maxN=${cfg.maxN}`)\n }\n break\n case 'continue':\n if (resume.n >= cfg.maxN) {\n fail(`decision 'continue' at n=${resume.n} with maxN=${cfg.maxN}; the stream is finished`)\n }\n if (resume.n >= cfg.minN && resume.wealth >= threshold) {\n fail(\n `decision 'continue' at n=${resume.n} ≥ minN=${cfg.minN} with e-value ` +\n `${resume.wealth} ≥ 1/α=${threshold}; this stream would have promoted`,\n )\n }\n break\n }\n}\n\nfunction makeStream(cfg: ResolvedConfig, resume?: SequentialStreamState): SequentialStream {\n if (resume !== undefined) assertResumableStream(resume, cfg)\n const proc = eProcess({\n alpha: cfg.alpha,\n maxBet: cfg.maxBet,\n nullMean: cfg.nullMean,\n ...(resume === undefined ? {} : { resume }),\n })\n const threshold = 1 / cfg.alpha\n let terminal: SequentialGateDecision | undefined =\n resume === undefined || resume.decision === 'continue' ? undefined : resume.decision\n return {\n observe(delta: number): SequentialObservation {\n if (terminal === 'undecided-at-maxN') {\n throw new Error(\n `sequentialPairedGate: pre-registered maxN=${cfg.maxN} exhausted — extending the ` +\n 'stream after seeing the result reopens optional stopping; start a NEW ' +\n 'pre-registered test',\n )\n }\n if (!Number.isFinite(delta) || Math.abs(delta) > cfg.scale) {\n throw new Error(\n `sequentialPairedGate: delta ${delta} outside ±scale=${cfg.scale} — pass the judge's ` +\n `native scale explicitly (detectScale helps pick 1 vs 100)`,\n )\n }\n const d = cfg.direction === 'decrease' ? -delta : delta\n const step = proc.update((d / cfg.scale + 1) / 2)\n // Stopping rule: FIRST n ≥ minN with current wealth ≥ 1/alpha — a valid\n // stopping time (measurable w.r.t. the past), sticky once latched. The\n // core's own `decided` latch ignores minN, so the gate re-derives the\n // decision from current wealth here.\n if (terminal === undefined && step.n >= cfg.minN && step.wealth >= threshold) {\n terminal = 'promote'\n }\n if (terminal === 'promote') {\n return {\n decision: 'promote',\n eValue: step.wealth,\n n: step.n,\n reason:\n `e-value ${step.wealth.toFixed(2)} ≥ 1/α=${threshold.toFixed(2)} at n=${step.n} ` +\n `(minN=${cfg.minN}): the paired improvement exceeds ${cfg.minEffect} at anytime-valid ` +\n `level α=${cfg.alpha}`,\n }\n }\n if (step.n >= cfg.maxN) {\n terminal = 'undecided-at-maxN'\n return {\n decision: 'undecided-at-maxN',\n eValue: step.wealth,\n n: step.n,\n reason:\n `undecided at pre-registered maxN=${cfg.maxN} (e-value ${step.wealth.toFixed(2)} < ` +\n `1/α=${threshold.toFixed(2)}). This is NOT evidence of no effect — the effect may be ` +\n 'real but smaller than this budget can detect; re-register with a larger N to test that',\n }\n }\n return {\n decision: 'continue',\n eValue: step.wealth,\n n: step.n,\n reason: `e-value ${step.wealth.toFixed(2)} < 1/α=${threshold.toFixed(2)} at n=${step.n}/${cfg.maxN} — keep observing`,\n }\n },\n state() {\n return { ...proc.state(), decision: terminal ?? 'continue' }\n },\n }\n}\n\n/** Data-independent in-place Fisher–Yates with a seeded PRNG — the permutation\n * depends only on the seed, never the values, so bet predictability survives. */\nfunction seededShuffle<T>(items: T[], seed: number): T[] {\n const rng = mulberry32(seed)\n for (let i = items.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n const tmp = items[i]!\n items[i] = items[j]!\n items[j] = tmp\n }\n return items\n}\n\n/**\n * Anytime-valid sequential paired gate. Conforms to the existing `Gate`\n * contract: decide(ctx) pairs candidate and baseline by full cellId, then\n * averages deltas within each configured unit (scenario by default). The\n * e-process consumes one observation per unit, with equal unit weights.\n * The streaming observe(delta) entry consumes caller-aggregated independent\n * units. Repetitions improve a unit's precision and do not increase n.\n *\n * Decision mapping onto the substrate's five-valued `GateDecision`:\n * - 'promote' → 'ship'\n * - 'continue' → 'need_more_work' (stream ended before maxN with\n * the e-value undecided — more independent units needed)\n * - 'undecided-at-maxN' → 'hold', with the reason stating it is NOT\n * evidence of no effect (never a silent default)\n */\nexport function sequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(\n options: SequentialPairedGateOptions,\n): SequentialPairedGate<TArtifact, TScenario> {\n const cfg = resolveConfig(options)\n const name = options.name ?? 'sequentialPairedGate'\n const manifest = options.preRegistration\n const observeStream = makeStream(cfg, options.resume)\n const independentUnitByScenarioId =\n options.independentUnitByScenarioId === undefined\n ? undefined\n : new Map(options.independentUnitByScenarioId)\n\n return {\n name,\n observe(delta: number): SequentialObservation {\n return observeStream.observe(delta)\n },\n state() {\n return observeStream.state()\n },\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n if (!ctx.baselineJudgeScores) {\n throw new Error(\n `${name}: ctx.baselineJudgeScores is required — falling back to the candidate's own ` +\n 'scores would compare the candidate against itself (delta 0, silent no-op)',\n )\n }\n const scenarioIds = new Set(ctx.scenarios.map((s) => s.id))\n const unitMap = independentUnitByScenarioId ?? new Map([...scenarioIds].map((id) => [id, id]))\n for (const id of scenarioIds) {\n const unit = unitMap.get(id)\n if (typeof unit !== 'string' || unit.length === 0 || unit.trim() !== unit) {\n throw new Error(`${name}: missing independent unit for scenario '${id}'`)\n }\n }\n const paired = pairHoldout(\n ctx.judgeScores,\n ctx.baselineJudgeScores,\n scenarioIds,\n (s) => s.composite,\n )\n for (let i = 0; i < paired.cellIds.length; i++) {\n const delta = paired.after[i]! - paired.before[i]!\n if (Math.abs(delta) > cfg.scale) {\n throw new Error(\n `${name}: cell '${paired.cellIds[i]}' delta ${delta} outside ±scale=${cfg.scale}`,\n )\n }\n }\n const observations = aggregatePairedHoldout(paired, unitMap)\n const deltas = observations.after.map((after, i) => after - observations.before[i]!)\n seededShuffle(deltas, cfg.shuffleSeed)\n\n const stream = makeStream(cfg)\n let last: SequentialObservation | undefined\n for (const d of deltas) {\n last = stream.observe(d)\n if (last.decision !== 'continue') break\n }\n\n const detail = {\n ...stream.state(),\n minN: cfg.minN,\n maxN: cfg.maxN,\n scale: cfg.scale,\n shuffleSeed: cfg.shuffleSeed,\n direction: cfg.direction,\n minEffect: cfg.minEffect,\n pairedN: deltas.length,\n pairedCellN: paired.cellIds.length,\n observationUnit: independentUnitByScenarioId === undefined ? 'scenario' : 'registered',\n unitIds: observations.unitIds,\n ...(manifest ? { preRegisteredId: manifest.id, metric: manifest.metric } : {}),\n }\n const meanDelta =\n deltas.length === 0 ? undefined : deltas.reduce((s, d) => s + d, 0) / deltas.length\n\n if (last === undefined) {\n return {\n decision: 'need_more_work',\n reasons: [`${name}: no paired holdout observations — nothing to test`],\n contributingGates: [{ name, status: 'not_evaluated', detail }],\n }\n }\n const decision =\n last.decision === 'promote'\n ? 'ship'\n : last.decision === 'continue'\n ? 'need_more_work'\n : 'hold'\n return {\n decision,\n reasons: [`${name}: ${last.reason}`],\n contributingGates: [\n {\n name,\n status:\n decision === 'ship'\n ? 'pass'\n : decision === 'need_more_work'\n ? 'not_evaluated'\n : 'fail',\n detail,\n },\n ],\n delta: meanDelta,\n }\n },\n }\n}\n\nexport interface SequentialDecideOptions {\n /** Type-I budget for the early-stop evidence. Default 0.05. */\n alpha?: number\n /** Minimum paired deltas before a stop may fire. Default 5. */\n minN?: number\n /** Bet truncation forwarded to `eProcess`. Default 0.5. */\n maxBet?: number\n /** Bound on |per-scenario composite delta|. Default 1. */\n scale?: number\n}\n\nexport interface SequentialDecideFn {\n (args: { history: GenerationRecord[] }): { stop: boolean; reason?: string }\n /** Read-only snapshot of the accumulated e-process (observability + tests). */\n state(): EProcessState\n}\n\n/**\n * SurfaceProposer.decide adapter that stops exploration at an e-value threshold.\n * The selected candidate still requires an independent held-out decision.\n *\n * Stream: for each generation g ≥ 1, the per-scenario composite deltas of\n * generation g's top candidate vs the generation-0 top candidate (the\n * incumbent the loop set out to beat), paired by scenarioId. H0: no proposed\n * surface improves any scenario's expected composite over the incumbent —\n * under it every delta has conditional mean ≤ 0 and the e-process is valid.\n * Once wealth ≥ 1/alpha the loop stops and hands the winner to the promotion\n * gate (which re-scores on HELD-OUT data — this adapter only spends the\n * exploration budget, it never promotes).\n *\n * This is an exploration stopping heuristic. Selection of the best measured\n * candidate and reuse of measured incumbent scores generally violate the\n * conditional-mean null, even when the candidates have no true improvement.\n * More repetitions reduce noise but do not establish exact type-I control.\n * An UNDECIDED process never stops the loop — absence of a\n * crossing is NOT evidence of no effect, so the loop simply runs its normal\n * course. Calling the adapter repeatedly with a growing history consumes each\n * generation exactly once (re-feeding an already-seen record would double-count\n * evidence).\n */\nexport function sequentialDecide(options: SequentialDecideOptions = {}): SequentialDecideFn {\n const alpha = options.alpha ?? 0.05\n const minN = options.minN ?? 5\n const scale = options.scale ?? 1\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`sequentialDecide: scale must be > 0, got ${scale}`)\n }\n const proc = eProcess({ alpha, maxBet: options.maxBet ?? 0.5, nullMean: 0.5 })\n const threshold = 1 / alpha\n let processedGenerations = 0\n let reference: Map<string, number> | undefined\n let stopped: { stop: true; reason: string } | undefined\n\n const topCandidate = (record: GenerationRecord) => {\n if (record.candidates.length === 0) {\n throw new Error(\n `sequentialDecide: generation ${record.generationIndex} has no candidates — ` +\n 'cannot extract a top candidate',\n )\n }\n return record.candidates.reduce((best, candidate) => {\n if (best.composite === null) return candidate\n if (candidate.composite === null) return best\n return candidate.composite > best.composite ? candidate : best\n })\n }\n\n const decide: SequentialDecideFn = ({ history }) => {\n if (stopped) return stopped\n if (history.length === 0) return { stop: false }\n if (reference === undefined) {\n reference = new Map(\n topCandidate(history[0]!).scenarios.map((s) => [s.scenarioId, s.composite]),\n )\n }\n // Consume only generations not yet seen; start at 1 — generation 0 IS the\n // reference and would contribute all-zero deltas (pure wealth dilution).\n for (let g = Math.max(1, processedGenerations); g < history.length; g++) {\n const top = topCandidate(history[g]!)\n const byScenario = new Map(top.scenarios.map((s) => [s.scenarioId, s.composite]))\n for (const [scenarioId, refComposite] of reference) {\n const candComposite = byScenario.get(scenarioId)\n if (candComposite === undefined) {\n throw new Error(\n `sequentialDecide: generation ${history[g]!.generationIndex} top candidate is missing ` +\n `scenario '${scenarioId}' — generations must score the same scenario set to pair`,\n )\n }\n const delta = candComposite - refComposite\n if (!Number.isFinite(delta) || Math.abs(delta) > scale) {\n throw new Error(\n `sequentialDecide: paired delta ${delta} outside ±scale=${scale} on scenario ` +\n `'${scenarioId}' — pass the composite scale explicitly`,\n )\n }\n const step = proc.update((delta / scale + 1) / 2)\n if (step.n >= minN && step.wealth >= threshold) {\n stopped = {\n stop: true,\n reason:\n `sequential e-process decided at generation ${history[g]!.generationIndex}: ` +\n `e-value ${step.wealth.toFixed(2)} ≥ 1/α=${threshold.toFixed(2)} after n=${step.n} ` +\n 'paired deltas vs the generation-0 incumbent; stop exploring and require ' +\n 'an independent held-out promotion decision',\n }\n processedGenerations = history.length\n return stopped\n }\n }\n }\n processedGenerations = history.length\n return { stop: false }\n }\n decide.state = () => proc.state()\n return decide\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sequential-BPbvgzfe.js","names":[],"sources":["../src/campaign/gates/sequential.ts"],"sourcesContent":["/**\n * Anytime-valid sequential promotion gate — an e-process (betting\n * test-martingale, see `eProcess` in `statistics.ts`) over paired\n * per-scenario deltas, so a campaign stops the MOMENT evidence decides\n * instead of burning a fixed-n budget. Decisions remain valid at any\n * data-dependent stopping time (Ville's inequality), which is exactly what\n * fixed-n machinery cannot offer: peeking at a bootstrap CI after every\n * observation and stopping on the first significant peek inflates type-I\n * error far beyond alpha.\n *\n * REPLACES, never layers on, a fixed-n gate. Running `heldoutSignificance`\n * or `paretoSignificanceGate` repeatedly on a growing sample and stopping\n * early is optional stopping no matter how it is dressed up; this gate is\n * the valid way to stop early. Use one or the other per evidence stream.\n *\n * Pre-registration binding: anytime validity holds only for the\n * PRE-REGISTERED statistic. When a `SignedManifest` is bound, the gate takes\n * alpha from `manifest.alpha`, the observation budget from\n * `manifest.preRegisteredN`, orients deltas by `manifest.direction`, and\n * shifts the null boundary by `manifest.minEffect` — re-deciding the same\n * stream under different parameters after seeing data would reopen optional\n * stopping under a fancier name. The manifest's content hash is verified at\n * construction with the canonical RFC 8785 digest used by signManifest.\n *\n * The supermartingale guarantee requires E[delta_t | past] ≤ the null\n * boundary under H0. Independent sampling with that mean bound suffices.\n * Exchangeability or shuffling alone does not: one fair sign repeated many\n * times is exchangeable and has marginal mean zero, but its conditional mean\n * becomes the revealed sign. A seeded shuffle cannot remove that dependence.\n * `decide(ctx)` pairs cells, then averages repetitions within each scenario\n * before testing. Pass independentUnitByScenarioId for scenarios sharing a\n * latent source or incident. Units must still satisfy the conditional-mean null.\n * Direct `observe()` callers own this sampling and aggregation contract.\n */\n\nimport { manifestContentDigest, type SignedManifest } from '../../pre-registration'\nimport { type EProcessState, eProcess, mulberry32 } from '../../statistics'\nimport type { Gate, GateContext, GateResult, GenerationRecord, Scenario } from '../types'\nimport { aggregatePairedHoldout, pairHoldout } from './statistical-heldout'\n\nexport type SequentialGateDecision = 'promote' | 'continue' | 'undecided-at-maxN'\n\nexport interface SequentialObservation {\n decision: SequentialGateDecision\n /** Current e-value (the betting wealth) against H0. */\n eValue: number\n /** Paired deltas consumed so far. */\n n: number\n /** Names the decision basis. For 'undecided-at-maxN' it states explicitly\n * that exhausting the budget is NOT evidence of no effect. */\n reason: string\n}\n\nexport interface SequentialPairedGateOptions {\n /** Type-I budget. With `preRegistration` bound this MUST match\n * `manifest.alpha` (conflict throws). Default 0.05. */\n alpha?: number\n /** Minimum independent paired units before a promote may fire. The stopping rule is\n * \"first n ≥ minN with e-value ≥ 1/alpha\" — still a valid stopping time.\n * Default 5. */\n minN?: number\n /** Pre-registered independent-unit budget (scenarios by default in decide()). Required unless `preRegistration`\n * supplies it via `preRegisteredN` (conflict throws). */\n maxN?: number\n /** Bet truncation forwarded to `eProcess`. Default 0.5. */\n maxBet?: number\n /** Bound on |delta| in the judge's native scale; deltas are mapped to\n * x = (d/scale + 1)/2 ∈ [0,1]. A delta outside ±scale throws (use\n * `detectScale` to pick 1 vs 100 BEFORE streaming). Default 1. */\n scale?: number\n /** Seed for reproducible ordering of scenario deltas in decide().\n * Shuffling does not establish the conditional-mean null. Default 1337. */\n shuffleSeed?: number\n /** Independent unit for every scenario passed to decide(). Full cell pairs\n * are averaged within each unit before testing. Defaults to scenario IDs.\n * The mapping is copied at construction. Direct observe() is unchanged. */\n independentUnitByScenarioId?: ReadonlyMap<string, string>\n /** Bind the pre-registered hypothesis. Verified (content hash) at\n * construction; alpha/maxN/direction/minEffect come FROM the manifest. */\n preRegistration?: SignedManifest\n /** Override the gate name in reports. */\n name?: string\n /** Continue the observe-stream from a `state()` snapshot taken before a\n * restart. The gate's parameters still resolve from this options object\n * (and the manifest); the snapshot must have been recorded under the same\n * alpha, maxBet, and null boundary, and its decision must be one this\n * configuration could have reached at its n, or construction throws.\n * `decide(ctx)` is unaffected — it always runs on a fresh stream. */\n resume?: SequentialStreamState\n}\n\n/** Snapshot of one observe-stream: the e-process state plus the gate's own\n * decision, which applies `minN` and `maxN` on top of the core latch. A\n * gate rebuilt with `resume` from this value continues exactly where the\n * snapshot was taken. */\nexport type SequentialStreamState = EProcessState & { decision: SequentialGateDecision }\n\nexport interface SequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>\n extends Gate<TArtifact, TScenario> {\n /** Streaming entry point: feed one paired independent-unit delta\n * (candidate − baseline, native scale). Aggregate correlated repetitions\n * before calling; the caller must justify E[delta_t | past] under H0.\n * Each gate instance carries ONE\n * observe-stream; `decide(ctx)` runs on its own fresh stream and never\n * consumes or advances this one. 'promote' is sticky; observing past the\n * pre-registered maxN throws (extending a finished stream after seeing\n * the result reopens optional stopping — start a NEW pre-registered\n * test). */\n observe(delta: number): SequentialObservation\n /** Read-only snapshot of the observe-stream. Pass it back as\n * `resume` to continue the stream after a restart. */\n state(): SequentialStreamState\n}\n\ninterface ResolvedConfig {\n alpha: number\n minN: number\n maxN: number\n maxBet: number\n scale: number\n shuffleSeed: number\n /** 'decrease' negates deltas so \"better\" is always positive. */\n direction: 'increase' | 'decrease'\n /** H0 boundary in x-space: 1/2 + minEffect/(2·scale). */\n nullMean: number\n minEffect: number\n}\n\n/** Sync twin of `verifyManifest`, so gate construction stays synchronous and\n * fails loud before any observation is consumed. The digest scheme belongs to\n * `pre-registration`; this gate only compares. */\nfunction verifyManifestSync(m: SignedManifest): boolean {\n return manifestContentDigest(m) === m.contentHash\n}\n\nfunction resolveConfig(opts: SequentialPairedGateOptions): ResolvedConfig {\n const m = opts.preRegistration\n let alpha: number\n let maxN: number\n let direction: 'increase' | 'decrease'\n let minEffect: number\n if (m) {\n if (!verifyManifestSync(m)) {\n throw new Error(\n `sequentialPairedGate: pre-registration manifest '${m.id}' content hash mismatch (tampered)`,\n )\n }\n if (opts.alpha !== undefined && opts.alpha !== m.alpha) {\n throw new Error(\n `sequentialPairedGate: alpha ${opts.alpha} conflicts with pre-registered alpha ${m.alpha} — ` +\n `the registered statistic is the only one anytime validity covers`,\n )\n }\n if (opts.maxN !== undefined && opts.maxN !== m.preRegisteredN) {\n throw new Error(\n `sequentialPairedGate: maxN ${opts.maxN} conflicts with pre-registered N ${m.preRegisteredN}`,\n )\n }\n alpha = m.alpha\n maxN = m.preRegisteredN\n direction = m.direction\n minEffect = m.minEffect\n } else {\n alpha = opts.alpha ?? 0.05\n if (opts.maxN === undefined) {\n throw new Error(\n 'sequentialPairedGate: maxN is required (or bind a preRegistration manifest whose ' +\n 'preRegisteredN is the budget) — an unbounded stream has no pre-registered budget',\n )\n }\n maxN = opts.maxN\n direction = 'increase'\n minEffect = 0\n }\n const scale = opts.scale ?? 1\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`sequentialPairedGate: scale must be > 0, got ${scale}`)\n }\n if (!Number.isInteger(maxN) || maxN < 1) {\n throw new Error(`sequentialPairedGate: maxN must be a positive integer, got ${maxN}`)\n }\n const minN = opts.minN ?? 5\n if (!Number.isInteger(minN) || minN < 1 || minN > maxN) {\n throw new Error(\n `sequentialPairedGate: minN must be an integer in [1, maxN=${maxN}], got ${minN}`,\n )\n }\n if (!Number.isFinite(minEffect) || minEffect < 0 || minEffect >= scale) {\n throw new Error(\n `sequentialPairedGate: minEffect must be in [0, scale=${scale}), got ${minEffect}`,\n )\n }\n // H0: mean delta ≤ minEffect. In x = (d/scale + 1)/2 space that is\n // E[x] ≤ 1/2 + minEffect/(2·scale) — a shifted null boundary, NOT a clamp\n // of the observations (clamping is mean-distorting for skewed deltas).\n const nullMean = 0.5 + minEffect / (2 * scale)\n return {\n alpha,\n minN,\n maxN,\n maxBet: opts.maxBet ?? 0.5,\n scale,\n shuffleSeed: opts.shuffleSeed ?? 1337,\n direction,\n nullMean,\n minEffect,\n }\n}\n\ninterface SequentialStream {\n observe(delta: number): SequentialObservation\n state(): SequentialStreamState\n}\n\nconst SEQUENTIAL_DECISIONS: readonly SequentialGateDecision[] = [\n 'promote',\n 'continue',\n 'undecided-at-maxN',\n]\n\n/** Refuse a snapshot whose gate decision this configuration could not have\n * reached at the snapshot's n. The e-process parameters are checked by\n * `eProcess` itself; this covers the gate's own stopping rule. */\nfunction assertResumableStream(resume: SequentialStreamState, cfg: ResolvedConfig): void {\n const fail = (detail: string): never => {\n throw new Error(`sequentialPairedGate: cannot resume — ${detail}`)\n }\n if (!SEQUENTIAL_DECISIONS.includes(resume.decision)) {\n fail(`unknown decision '${String(resume.decision)}'`)\n }\n if (resume.n > cfg.maxN) {\n fail(`snapshot n=${resume.n} exceeds the pre-registered maxN=${cfg.maxN}`)\n }\n const threshold = 1 / cfg.alpha\n switch (resume.decision) {\n case 'promote':\n if (resume.n < cfg.minN) {\n fail(`decision 'promote' at n=${resume.n} is below minN=${cfg.minN}`)\n }\n break\n case 'undecided-at-maxN':\n if (resume.n !== cfg.maxN) {\n fail(`decision 'undecided-at-maxN' at n=${resume.n} does not match maxN=${cfg.maxN}`)\n }\n break\n case 'continue':\n if (resume.n >= cfg.maxN) {\n fail(`decision 'continue' at n=${resume.n} with maxN=${cfg.maxN}; the stream is finished`)\n }\n if (resume.n >= cfg.minN && resume.wealth >= threshold) {\n fail(\n `decision 'continue' at n=${resume.n} ≥ minN=${cfg.minN} with e-value ` +\n `${resume.wealth} ≥ 1/α=${threshold}; this stream would have promoted`,\n )\n }\n break\n }\n}\n\nfunction makeStream(cfg: ResolvedConfig, resume?: SequentialStreamState): SequentialStream {\n if (resume !== undefined) assertResumableStream(resume, cfg)\n const proc = eProcess({\n alpha: cfg.alpha,\n maxBet: cfg.maxBet,\n nullMean: cfg.nullMean,\n ...(resume === undefined ? {} : { resume }),\n })\n const threshold = 1 / cfg.alpha\n let terminal: SequentialGateDecision | undefined =\n resume === undefined || resume.decision === 'continue' ? undefined : resume.decision\n return {\n observe(delta: number): SequentialObservation {\n if (terminal === 'undecided-at-maxN') {\n throw new Error(\n `sequentialPairedGate: pre-registered maxN=${cfg.maxN} exhausted — extending the ` +\n 'stream after seeing the result reopens optional stopping; start a NEW ' +\n 'pre-registered test',\n )\n }\n if (!Number.isFinite(delta) || Math.abs(delta) > cfg.scale) {\n throw new Error(\n `sequentialPairedGate: delta ${delta} outside ±scale=${cfg.scale} — pass the judge's ` +\n `native scale explicitly (detectScale helps pick 1 vs 100)`,\n )\n }\n const d = cfg.direction === 'decrease' ? -delta : delta\n const step = proc.update((d / cfg.scale + 1) / 2)\n // Stopping rule: FIRST n ≥ minN with current wealth ≥ 1/alpha — a valid\n // stopping time (measurable w.r.t. the past), sticky once latched. The\n // core's own `decided` latch ignores minN, so the gate re-derives the\n // decision from current wealth here.\n if (terminal === undefined && step.n >= cfg.minN && step.wealth >= threshold) {\n terminal = 'promote'\n }\n if (terminal === 'promote') {\n return {\n decision: 'promote',\n eValue: step.wealth,\n n: step.n,\n reason:\n `e-value ${step.wealth.toFixed(2)} ≥ 1/α=${threshold.toFixed(2)} at n=${step.n} ` +\n `(minN=${cfg.minN}): the paired improvement exceeds ${cfg.minEffect} at anytime-valid ` +\n `level α=${cfg.alpha}`,\n }\n }\n if (step.n >= cfg.maxN) {\n terminal = 'undecided-at-maxN'\n return {\n decision: 'undecided-at-maxN',\n eValue: step.wealth,\n n: step.n,\n reason:\n `undecided at pre-registered maxN=${cfg.maxN} (e-value ${step.wealth.toFixed(2)} < ` +\n `1/α=${threshold.toFixed(2)}). This is NOT evidence of no effect — the effect may be ` +\n 'real but smaller than this budget can detect; re-register with a larger N to test that',\n }\n }\n return {\n decision: 'continue',\n eValue: step.wealth,\n n: step.n,\n reason: `e-value ${step.wealth.toFixed(2)} < 1/α=${threshold.toFixed(2)} at n=${step.n}/${cfg.maxN} — keep observing`,\n }\n },\n state() {\n return { ...proc.state(), decision: terminal ?? 'continue' }\n },\n }\n}\n\n/** Data-independent in-place Fisher–Yates with a seeded PRNG — the permutation\n * depends only on the seed, never the values, so bet predictability survives. */\nfunction seededShuffle<T>(items: T[], seed: number): T[] {\n const rng = mulberry32(seed)\n for (let i = items.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n const tmp = items[i]!\n items[i] = items[j]!\n items[j] = tmp\n }\n return items\n}\n\n/**\n * Anytime-valid sequential paired gate. Conforms to the existing `Gate`\n * contract: decide(ctx) pairs candidate and baseline by full cellId, then\n * averages deltas within each configured unit (scenario by default). The\n * e-process consumes one observation per unit, with equal unit weights.\n * The streaming observe(delta) entry consumes caller-aggregated independent\n * units. Repetitions improve a unit's precision and do not increase n.\n *\n * Decision mapping onto the substrate's five-valued `GateDecision`:\n * - 'promote' → 'ship'\n * - 'continue' → 'need_more_work' (stream ended before maxN with\n * the e-value undecided — more independent units needed)\n * - 'undecided-at-maxN' → 'hold', with the reason stating it is NOT\n * evidence of no effect (never a silent default)\n */\nexport function sequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(\n options: SequentialPairedGateOptions,\n): SequentialPairedGate<TArtifact, TScenario> {\n const cfg = resolveConfig(options)\n const name = options.name ?? 'sequentialPairedGate'\n const manifest = options.preRegistration\n const observeStream = makeStream(cfg, options.resume)\n const independentUnitByScenarioId =\n options.independentUnitByScenarioId === undefined\n ? undefined\n : new Map(options.independentUnitByScenarioId)\n\n return {\n name,\n observe(delta: number): SequentialObservation {\n return observeStream.observe(delta)\n },\n state() {\n return observeStream.state()\n },\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n if (!ctx.baselineJudgeScores) {\n throw new Error(\n `${name}: ctx.baselineJudgeScores is required — falling back to the candidate's own ` +\n 'scores would compare the candidate against itself (delta 0, silent no-op)',\n )\n }\n const scenarioIds = new Set(ctx.scenarios.map((s) => s.id))\n const unitMap = independentUnitByScenarioId ?? new Map([...scenarioIds].map((id) => [id, id]))\n for (const id of scenarioIds) {\n const unit = unitMap.get(id)\n if (typeof unit !== 'string' || unit.length === 0 || unit.trim() !== unit) {\n throw new Error(`${name}: missing independent unit for scenario '${id}'`)\n }\n }\n const paired = pairHoldout(\n ctx.judgeScores,\n ctx.baselineJudgeScores,\n scenarioIds,\n (s) => s.composite,\n )\n for (let i = 0; i < paired.cellIds.length; i++) {\n const delta = paired.after[i]! - paired.before[i]!\n if (Math.abs(delta) > cfg.scale) {\n throw new Error(\n `${name}: cell '${paired.cellIds[i]}' delta ${delta} outside ±scale=${cfg.scale}`,\n )\n }\n }\n const observations = aggregatePairedHoldout(paired, unitMap)\n const deltas = observations.after.map((after, i) => after - observations.before[i]!)\n seededShuffle(deltas, cfg.shuffleSeed)\n\n const stream = makeStream(cfg)\n let last: SequentialObservation | undefined\n for (const d of deltas) {\n last = stream.observe(d)\n if (last.decision !== 'continue') break\n }\n\n const detail = {\n ...stream.state(),\n minN: cfg.minN,\n maxN: cfg.maxN,\n scale: cfg.scale,\n shuffleSeed: cfg.shuffleSeed,\n direction: cfg.direction,\n minEffect: cfg.minEffect,\n pairedN: deltas.length,\n pairedCellN: paired.cellIds.length,\n observationUnit: independentUnitByScenarioId === undefined ? 'scenario' : 'registered',\n unitIds: observations.unitIds,\n ...(manifest ? { preRegisteredId: manifest.id, metric: manifest.metric } : {}),\n }\n const meanDelta =\n deltas.length === 0 ? undefined : deltas.reduce((s, d) => s + d, 0) / deltas.length\n\n if (last === undefined) {\n return {\n decision: 'need_more_work',\n reasons: [`${name}: no paired holdout observations — nothing to test`],\n contributingGates: [{ name, status: 'not_evaluated', detail }],\n }\n }\n const decision =\n last.decision === 'promote'\n ? 'ship'\n : last.decision === 'continue'\n ? 'need_more_work'\n : 'hold'\n return {\n decision,\n reasons: [`${name}: ${last.reason}`],\n contributingGates: [\n {\n name,\n status:\n decision === 'ship'\n ? 'pass'\n : decision === 'need_more_work'\n ? 'not_evaluated'\n : 'fail',\n detail,\n },\n ],\n delta: meanDelta,\n }\n },\n }\n}\n\nexport interface SequentialDecideOptions {\n /** Type-I budget for the early-stop evidence. Default 0.05. */\n alpha?: number\n /** Minimum paired deltas before a stop may fire. Default 5. */\n minN?: number\n /** Bet truncation forwarded to `eProcess`. Default 0.5. */\n maxBet?: number\n /** Bound on |per-scenario composite delta|. Default 1. */\n scale?: number\n}\n\nexport interface SequentialDecideFn {\n (args: { history: GenerationRecord[] }): { stop: boolean; reason?: string }\n /** Read-only snapshot of the accumulated e-process (observability + tests). */\n state(): EProcessState\n}\n\n/**\n * SurfaceProposer.decide adapter that stops exploration at an e-value threshold.\n * The selected candidate still requires an independent held-out decision.\n *\n * Stream: for each generation g ≥ 1, the per-scenario composite deltas of\n * generation g's top candidate vs the generation-0 top candidate (the\n * incumbent the loop set out to beat), paired by scenarioId. H0: no proposed\n * surface improves any scenario's expected composite over the incumbent —\n * under it every delta has conditional mean ≤ 0 and the e-process is valid.\n * Once wealth ≥ 1/alpha the loop stops and hands the winner to the promotion\n * gate (which re-scores on HELD-OUT data — this adapter only spends the\n * exploration budget, it never promotes).\n *\n * This is an exploration stopping heuristic. Selection of the best measured\n * candidate and reuse of measured incumbent scores generally violate the\n * conditional-mean null, even when the candidates have no true improvement.\n * More repetitions reduce noise but do not establish exact type-I control.\n * An UNDECIDED process never stops the loop — absence of a\n * crossing is NOT evidence of no effect, so the loop simply runs its normal\n * course. Calling the adapter repeatedly with a growing history consumes each\n * generation exactly once (re-feeding an already-seen record would double-count\n * evidence).\n */\nexport function sequentialDecide(options: SequentialDecideOptions = {}): SequentialDecideFn {\n const alpha = options.alpha ?? 0.05\n const minN = options.minN ?? 5\n const scale = options.scale ?? 1\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`sequentialDecide: scale must be > 0, got ${scale}`)\n }\n const proc = eProcess({ alpha, maxBet: options.maxBet ?? 0.5, nullMean: 0.5 })\n const threshold = 1 / alpha\n let processedGenerations = 0\n let reference: Map<string, number> | undefined\n let stopped: { stop: true; reason: string } | undefined\n\n const topCandidate = (record: GenerationRecord) => {\n if (record.candidates.length === 0) {\n throw new Error(\n `sequentialDecide: generation ${record.generationIndex} has no candidates — ` +\n 'cannot extract a top candidate',\n )\n }\n return record.candidates.reduce((best, candidate) => {\n if (best.composite === null) return candidate\n if (candidate.composite === null) return best\n return candidate.composite > best.composite ? candidate : best\n })\n }\n\n const decide: SequentialDecideFn = ({ history }) => {\n if (stopped) return stopped\n if (history.length === 0) return { stop: false }\n if (reference === undefined) {\n reference = new Map(\n topCandidate(history[0]!).scenarios.map((s) => [s.scenarioId, s.composite]),\n )\n }\n // Consume only generations not yet seen; start at 1 — generation 0 IS the\n // reference and would contribute all-zero deltas (pure wealth dilution).\n for (let g = Math.max(1, processedGenerations); g < history.length; g++) {\n const top = topCandidate(history[g]!)\n const byScenario = new Map(top.scenarios.map((s) => [s.scenarioId, s.composite]))\n for (const [scenarioId, refComposite] of reference) {\n const candComposite = byScenario.get(scenarioId)\n if (candComposite === undefined) {\n throw new Error(\n `sequentialDecide: generation ${history[g]!.generationIndex} top candidate is missing ` +\n `scenario '${scenarioId}' — generations must score the same scenario set to pair`,\n )\n }\n const delta = candComposite - refComposite\n if (!Number.isFinite(delta) || Math.abs(delta) > scale) {\n throw new Error(\n `sequentialDecide: paired delta ${delta} outside ±scale=${scale} on scenario ` +\n `'${scenarioId}' — pass the composite scale explicitly`,\n )\n }\n const step = proc.update((delta / scale + 1) / 2)\n if (step.n >= minN && step.wealth >= threshold) {\n stopped = {\n stop: true,\n reason:\n `sequential e-process decided at generation ${history[g]!.generationIndex}: ` +\n `e-value ${step.wealth.toFixed(2)} ≥ 1/α=${threshold.toFixed(2)} after n=${step.n} ` +\n 'paired deltas vs the generation-0 incumbent; stop exploring and require ' +\n 'an independent held-out promotion decision',\n }\n processedGenerations = history.length\n return stopped\n }\n }\n }\n processedGenerations = history.length\n return { stop: false }\n }\n decide.state = () => proc.state()\n return decide\n}\n"],"mappings":"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"}