@tangle-network/agent-eval 0.218.1 → 0.219.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/adapters/http.d.ts +3 -3
  3. package/dist/adapters/http.d.ts.map +1 -1
  4. package/dist/adapters/http.js +1 -0
  5. package/dist/adapters/http.js.map +1 -1
  6. package/dist/{agent-profile-DTt8uGP9.d.ts → agent-profile-D01Io2Xw.d.ts} +4 -4
  7. package/dist/{agent-profile-DTt8uGP9.d.ts.map → agent-profile-D01Io2Xw.d.ts.map} +1 -1
  8. package/dist/analyst/index.d.ts +13 -13
  9. package/dist/analyst/index.d.ts.map +1 -1
  10. package/dist/analyst/index.js +8 -8
  11. package/dist/analyst/index.js.map +1 -1
  12. package/dist/{analyze-runs-DjlZOVXx.d.ts → analyze-runs-BWAHBDAw.d.ts} +4 -4
  13. package/dist/{analyze-runs-DjlZOVXx.d.ts.map → analyze-runs-BWAHBDAw.d.ts.map} +1 -1
  14. package/dist/{benchmark-command-BupdHRyf.js → benchmark-command-BzmMTnvC.js} +10 -10
  15. package/dist/{benchmark-command-BupdHRyf.js.map → benchmark-command-BzmMTnvC.js.map} +1 -1
  16. package/dist/benchmarks/index.d.ts +5 -5
  17. package/dist/benchmarks/index.js +3 -3
  18. package/dist/benchmarks/index.js.map +1 -1
  19. package/dist/campaign/index.d.ts +8 -8
  20. package/dist/campaign/index.js +8 -8
  21. package/dist/{campaign-CGvUTnmc.js → campaign-BVPC6ODQ.js} +149 -10
  22. package/dist/campaign-BVPC6ODQ.js.map +1 -0
  23. package/dist/campaign-evidence-DunzcJGv.js.map +1 -1
  24. package/dist/{chat-client-B2pXlPdx.js → chat-client-CMQ8_h4J.js} +2 -2
  25. package/dist/{chat-client-B2pXlPdx.js.map → chat-client-CMQ8_h4J.js.map} +1 -1
  26. package/dist/{chat-json-call-Dm9c-b41.js → chat-json-call-BrZKn0L3.js} +2 -2
  27. package/dist/{chat-json-call-Dm9c-b41.js.map → chat-json-call-BrZKn0L3.js.map} +1 -1
  28. package/dist/cli.js +8 -8
  29. package/dist/{completion-verifier-DbLVf3i8.js → completion-verifier-Cigap8CG.js} +4 -4
  30. package/dist/{completion-verifier-DbLVf3i8.js.map → completion-verifier-Cigap8CG.js.map} +1 -1
  31. package/dist/contract/index.d.ts +11 -11
  32. package/dist/contract/index.js +7 -7
  33. package/dist/{cost-ledger-D0352Z4d.js → cost-ledger-BGZB6yY2.js} +75 -2
  34. package/dist/cost-ledger-BGZB6yY2.js.map +1 -0
  35. package/dist/{cost-ledger-Jk62OBfF.d.ts → cost-ledger-C1W3ICmP.d.ts} +26 -2
  36. package/dist/cost-ledger-C1W3ICmP.d.ts.map +1 -0
  37. package/dist/{default-registry-BJ-Pq6yL.js → default-registry-BrKmT01n.js} +3 -3
  38. package/dist/{default-registry-BJ-Pq6yL.js.map → default-registry-BrKmT01n.js.map} +1 -1
  39. package/dist/{default-registry-DXgLmoDI.d.ts → default-registry-Cwg_QN3d.d.ts} +6 -6
  40. package/dist/{default-registry-DXgLmoDI.d.ts.map → default-registry-Cwg_QN3d.d.ts.map} +1 -1
  41. package/dist/{define-agent-eval-BlH10zEw.d.ts → define-agent-eval-C2nFhHPu.d.ts} +8 -8
  42. package/dist/{define-agent-eval-BlH10zEw.d.ts.map → define-agent-eval-C2nFhHPu.d.ts.map} +1 -1
  43. package/dist/{define-agent-eval-BGdFKlrc.js → define-agent-eval-UQY3VbJ2.js} +4 -4
  44. package/dist/{define-agent-eval-BGdFKlrc.js.map → define-agent-eval-UQY3VbJ2.js.map} +1 -1
  45. package/dist/diagnosis/index.d.ts +2 -2
  46. package/dist/{dspy-rlm-engine-CSMwudLO.js → dspy-rlm-engine-Bn_Mt_LW.js} +3 -3
  47. package/dist/{dspy-rlm-engine-CSMwudLO.js.map → dspy-rlm-engine-Bn_Mt_LW.js.map} +1 -1
  48. package/dist/{engine-BvZ0R-36.d.ts → engine-BpGGw6cd.d.ts} +3 -3
  49. package/dist/{engine-BvZ0R-36.d.ts.map → engine-BpGGw6cd.d.ts.map} +1 -1
  50. package/dist/{evaluation-BOZ38vOH.d.ts → evaluation-CBdQt_kr.d.ts} +4 -4
  51. package/dist/{evaluation-BOZ38vOH.d.ts.map → evaluation-CBdQt_kr.d.ts.map} +1 -1
  52. package/dist/evaluation.d.ts +1 -1
  53. package/dist/evaluation.js +1 -1
  54. package/dist/{exact-types-CflbJ9f3.d.ts → exact-types-BYFmYq44.d.ts} +2 -2
  55. package/dist/{exact-types-CflbJ9f3.d.ts.map → exact-types-BYFmYq44.d.ts.map} +1 -1
  56. package/dist/experiment/index.d.ts +24 -1
  57. package/dist/experiment/index.d.ts.map +1 -1
  58. package/dist/experiment/index.js.map +1 -1
  59. package/dist/{external-optimizer-process-C3Wm7Vhn.js → external-optimizer-process-wOLfzsCC.js} +2 -2
  60. package/dist/{external-optimizer-process-C3Wm7Vhn.js.map → external-optimizer-process-wOLfzsCC.js.map} +1 -1
  61. package/dist/{external-optimizer-subprocess-B5gdLRZv.js → external-optimizer-subprocess-D670Oxf2.js} +3 -3
  62. package/dist/{external-optimizer-subprocess-B5gdLRZv.js.map → external-optimizer-subprocess-D670Oxf2.js.map} +1 -1
  63. package/dist/{feedback-trajectory-BWmhRBGu.d.ts → feedback-trajectory-DhB9WzYg.d.ts} +3 -3
  64. package/dist/{feedback-trajectory-BWmhRBGu.d.ts.map → feedback-trajectory-DhB9WzYg.d.ts.map} +1 -1
  65. package/dist/{findings-store-BtTzPL0d.js → findings-store-CvNo712L.js} +5 -5
  66. package/dist/{findings-store-BtTzPL0d.js.map → findings-store-CvNo712L.js.map} +1 -1
  67. package/dist/fuzz.d.ts +1 -1
  68. package/dist/fuzz.js +1 -1
  69. package/dist/hosted/index.js +1 -1
  70. package/dist/{index-CX-GAUpw.d.ts → index-CHxwfW5g.d.ts} +2 -2
  71. package/dist/{index-CX-GAUpw.d.ts.map → index-CHxwfW5g.d.ts.map} +1 -1
  72. package/dist/{index-BLGqZ81N.d.ts → index-CVFStrQD.d.ts} +8 -8
  73. package/dist/{index-BLGqZ81N.d.ts.map → index-CVFStrQD.d.ts.map} +1 -1
  74. package/dist/{index-CiRwLv2R.d.ts → index-D7fmC-1K.d.ts} +3 -4
  75. package/dist/index-D7fmC-1K.d.ts.map +1 -0
  76. package/dist/{index-DbMOXYnP.d.ts → index-DAXfpVf7.d.ts} +10 -10
  77. package/dist/{index-DbMOXYnP.d.ts.map → index-DAXfpVf7.d.ts.map} +1 -1
  78. package/dist/{index-B16FNdW5.d.ts → index-Q6DvdcwI.d.ts} +37 -11
  79. package/dist/index-Q6DvdcwI.d.ts.map +1 -0
  80. package/dist/index.d.ts +26 -26
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +14 -14
  83. package/dist/index.js.map +1 -1
  84. package/dist/{integrity-_qgZLA5p.d.ts → integrity-CCwvezWS.d.ts} +2 -2
  85. package/dist/{integrity-_qgZLA5p.d.ts.map → integrity-CCwvezWS.d.ts.map} +1 -1
  86. package/dist/judge-gate-Co5tuh57.js.map +1 -1
  87. package/dist/{judge-gate-DsL0yFcb.d.ts → judge-gate-Dc_mXQsL.d.ts} +2 -2
  88. package/dist/judge-gate-Dc_mXQsL.d.ts.map +1 -0
  89. package/dist/{kind-factory-Gy4fXixd.js → kind-factory-BhxbtUsQ.js} +2 -2
  90. package/dist/{kind-factory-Gy4fXixd.js.map → kind-factory-BhxbtUsQ.js.map} +1 -1
  91. package/dist/{llm-client-_Y6ic-tf.js → llm-client-DXuOTseU.js} +2 -2
  92. package/dist/{llm-client-_Y6ic-tf.js.map → llm-client-DXuOTseU.js.map} +1 -1
  93. package/dist/{llm-judge-Djxh-Uhs.js → llm-judge-Cc8vDJh_.js} +9 -8
  94. package/dist/llm-judge-Cc8vDJh_.js.map +1 -0
  95. package/dist/matrix/index.d.ts +2 -2
  96. package/dist/{matrix-CCSBna4q.d.ts → matrix-Cf-AGxGh.d.ts} +4 -4
  97. package/dist/{matrix-CCSBna4q.d.ts.map → matrix-Cf-AGxGh.d.ts.map} +1 -1
  98. package/dist/meta-eval/index.d.ts +2 -2
  99. package/dist/{mint-BRZXuwRi.d.ts → mint-CkzpVPdz.d.ts} +3 -3
  100. package/dist/{mint-BRZXuwRi.d.ts.map → mint-CkzpVPdz.d.ts.map} +1 -1
  101. package/dist/multishot/golden/index.d.ts +1 -1
  102. package/dist/multishot/index.d.ts +2 -2
  103. package/dist/multishot/index.js.map +1 -1
  104. package/dist/openapi.json +1 -1
  105. package/dist/{pareto-CTv8vozZ.js → pareto-BiBnQXAE.js} +304 -210
  106. package/dist/pareto-BiBnQXAE.js.map +1 -0
  107. package/dist/pipelines/index.d.ts.map +1 -1
  108. package/dist/pipelines/index.js.map +1 -1
  109. package/dist/{prime-protocol-BWG2DQIk.d.ts → prime-protocol-CckHBxGu.d.ts} +3 -3
  110. package/dist/{prime-protocol-BWG2DQIk.d.ts.map → prime-protocol-CckHBxGu.d.ts.map} +1 -1
  111. package/dist/{promotion-gate-CxEHRsZW.d.ts → promotion-gate-CkVZHagC.d.ts} +3 -3
  112. package/dist/{promotion-gate-CxEHRsZW.d.ts.map → promotion-gate-CkVZHagC.d.ts.map} +1 -1
  113. package/dist/{proposal-findings-n-5WBbEx.js → proposal-findings-E1aSC_cU.js} +2 -2
  114. package/dist/{proposal-findings-n-5WBbEx.js.map → proposal-findings-E1aSC_cU.js.map} +1 -1
  115. package/dist/{registry-ssNoHzwD.d.ts → registry-B_yG-1jh.d.ts} +5 -5
  116. package/dist/{registry-ssNoHzwD.d.ts.map → registry-B_yG-1jh.d.ts.map} +1 -1
  117. package/dist/render-Cs34y0iX.js.map +1 -1
  118. package/dist/reporting.d.ts +3 -3
  119. package/dist/{researcher-GU1tV4dn.d.ts → researcher-H0p44OYC.d.ts} +5 -5
  120. package/dist/{researcher-GU1tV4dn.d.ts.map → researcher-H0p44OYC.d.ts.map} +1 -1
  121. package/dist/{reward-DrZ71vnf.d.ts → reward-BeUgVVC_.d.ts} +2 -2
  122. package/dist/{reward-DrZ71vnf.d.ts.map → reward-BeUgVVC_.d.ts.map} +1 -1
  123. package/dist/{reward-hacking-E52wq86n.d.ts → reward-hacking-WTxUbtbb.d.ts} +2 -2
  124. package/dist/{reward-hacking-E52wq86n.d.ts.map → reward-hacking-WTxUbtbb.d.ts.map} +1 -1
  125. package/dist/rl.d.ts +8 -8
  126. package/dist/rollout/index.d.ts +3 -3
  127. package/dist/rollout-Dd1ucT_M.js.map +1 -1
  128. package/dist/{rubric-predictive-validity-DiwT8PbL.d.ts → rubric-predictive-validity-noY7uE03.d.ts} +2 -2
  129. package/dist/{rubric-predictive-validity-DiwT8PbL.d.ts.map → rubric-predictive-validity-noY7uE03.d.ts.map} +1 -1
  130. package/dist/{run-record-CaxbLq6Y.d.ts → run-record-C3awiXPf.d.ts} +2 -2
  131. package/dist/{run-record-CaxbLq6Y.d.ts.map → run-record-C3awiXPf.d.ts.map} +1 -1
  132. package/dist/schema-CvhfLjtZ.js.map +1 -1
  133. package/dist/search/index.d.ts +2 -2
  134. package/dist/search/index.js +2 -2
  135. package/dist/{search-kernel-p9JB3SmL.d.ts → search-kernel-Dloxd6gs.d.ts} +4 -4
  136. package/dist/{search-kernel-p9JB3SmL.d.ts.map → search-kernel-Dloxd6gs.d.ts.map} +1 -1
  137. package/dist/{search-ledger-ClpN4I3e.js → search-ledger-CZRqtFbW.js} +249 -242
  138. package/dist/search-ledger-CZRqtFbW.js.map +1 -0
  139. package/dist/{search-shipper-Bg2HW3Uv.js → search-shipper-Dn3NLsuH.js} +2 -2
  140. package/dist/{search-shipper-Bg2HW3Uv.js.map → search-shipper-Dn3NLsuH.js.map} +1 -1
  141. package/dist/{search-summary-CrBuvVnf.js → search-summary-D4bwYnAE.js} +2 -2
  142. package/dist/{search-summary-CrBuvVnf.js.map → search-summary-D4bwYnAE.js.map} +1 -1
  143. package/dist/{server-C6M0qR8h.js → server-8TS8XCvg.js} +3 -3
  144. package/dist/{server-C6M0qR8h.js.map → server-8TS8XCvg.js.map} +1 -1
  145. package/dist/{skillopt-optimization-method-5dlex7MJ.js → skillopt-optimization-method-DLKqdhcR.js} +106 -61
  146. package/dist/skillopt-optimization-method-DLKqdhcR.js.map +1 -0
  147. package/dist/{statistical-heldout-BHfLZHrm.d.ts → statistical-heldout-3z3I-kJN.d.ts} +3 -3
  148. package/dist/{statistical-heldout-BHfLZHrm.d.ts.map → statistical-heldout-3z3I-kJN.d.ts.map} +1 -1
  149. package/dist/{store-otlp-BteDAbHK.js → store-otlp-dyWE3wAG.js} +2 -2
  150. package/dist/{store-otlp-BteDAbHK.js.map → store-otlp-dyWE3wAG.js.map} +1 -1
  151. package/dist/{store-tool-spans-D5l_OfTJ.js → store-tool-spans-DIUgZbzu.js} +3 -3
  152. package/dist/{store-tool-spans-D5l_OfTJ.js.map → store-tool-spans-DIUgZbzu.js.map} +1 -1
  153. package/dist/{store-tool-spans-DZS0HWVf.d.ts → store-tool-spans-DPwXHQkH.d.ts} +7 -7
  154. package/dist/{store-tool-spans-DZS0HWVf.d.ts.map → store-tool-spans-DPwXHQkH.d.ts.map} +1 -1
  155. package/dist/{summary-report-CkbRcSuu.d.ts → summary-report-C8V49Jt3.d.ts} +3 -3
  156. package/dist/{summary-report-CkbRcSuu.d.ts.map → summary-report-C8V49Jt3.d.ts.map} +1 -1
  157. package/dist/{systemone-BMzDJb1i.d.ts → systemone-DVA7t76V.d.ts} +6 -6
  158. package/dist/{systemone-BMzDJb1i.d.ts.map → systemone-DVA7t76V.d.ts.map} +1 -1
  159. package/dist/{systemone-review-CwE6JGIp.d.ts → systemone-review-CUBMuTLi.d.ts} +2 -2
  160. package/dist/{systemone-review-CwE6JGIp.d.ts.map → systemone-review-CUBMuTLi.d.ts.map} +1 -1
  161. package/dist/systemone.d.ts +3 -3
  162. package/dist/{tool-groups-DyNz2JZ_.d.ts → tool-groups-LISbBWCg.d.ts} +3 -3
  163. package/dist/tool-groups-LISbBWCg.d.ts.map +1 -0
  164. package/dist/{trace-questions-xxwgsuMi.d.ts → trace-questions-C42fYEE4.d.ts} +4 -4
  165. package/dist/{trace-questions-xxwgsuMi.d.ts.map → trace-questions-C42fYEE4.d.ts.map} +1 -1
  166. package/dist/trace-repair/index.d.ts +3 -3
  167. package/dist/traces.d.ts +6 -6
  168. package/dist/traces.js +3 -3
  169. package/dist/trajectory-replay/index.js +24 -3
  170. package/dist/trajectory-replay/index.js.map +1 -1
  171. package/dist/{tree-B5e1uAeL.js → tree-DSqwJpL9.js} +3 -3
  172. package/dist/{tree-B5e1uAeL.js.map → tree-DSqwJpL9.js.map} +1 -1
  173. package/dist/{types-CW8ewATZ.d.ts → types-8yQzNm8u.d.ts} +4 -4
  174. package/dist/{types-CW8ewATZ.d.ts.map → types-8yQzNm8u.d.ts.map} +1 -1
  175. package/dist/{types-DtkO1G9L.d.ts → types-CHfn1zg5.d.ts} +69 -71
  176. package/dist/types-CHfn1zg5.d.ts.map +1 -0
  177. package/dist/{types-BKCzzmit.d.ts → types-D8GXK9Z5.d.ts} +3 -3
  178. package/dist/{types-BKCzzmit.d.ts.map → types-D8GXK9Z5.d.ts.map} +1 -1
  179. package/dist/{types-D-mgRzlh.d.ts → types-eMhdacVp.d.ts} +2 -2
  180. package/dist/{types-D-mgRzlh.d.ts.map → types-eMhdacVp.d.ts.map} +1 -1
  181. package/dist/wire/index.d.ts +3 -3
  182. package/dist/wire/index.js +1 -1
  183. package/docs/campaign-proposers.md +16 -0
  184. package/docs/concepts.md +31 -0
  185. package/docs/public-api.md +76 -75
  186. package/package.json +2 -5
  187. package/dist/campaign-CGvUTnmc.js.map +0 -1
  188. package/dist/cost-ledger-D0352Z4d.js.map +0 -1
  189. package/dist/cost-ledger-Jk62OBfF.d.ts.map +0 -1
  190. package/dist/index-B16FNdW5.d.ts.map +0 -1
  191. package/dist/index-CiRwLv2R.d.ts.map +0 -1
  192. package/dist/judge-gate-DsL0yFcb.d.ts.map +0 -1
  193. package/dist/llm-judge-Djxh-Uhs.js.map +0 -1
  194. package/dist/pareto-CTv8vozZ.js.map +0 -1
  195. package/dist/search-ledger-ClpN4I3e.js.map +0 -1
  196. package/dist/skillopt-optimization-method-5dlex7MJ.js.map +0 -1
  197. package/dist/tool-groups-DyNz2JZ_.d.ts.map +0 -1
  198. package/dist/types-DtkO1G9L.d.ts.map +0 -1
@@ -62,6 +62,173 @@ function compareStrings(a, b) {
62
62
  return a < b ? -1 : a > b ? 1 : 0;
63
63
  }
64
64
  //#endregion
65
+ //#region src/campaign/search-state-indexes.ts
66
+ var SearchLedgerIndexes = class {
67
+ tasks = /* @__PURE__ */ new Map();
68
+ nodes = /* @__PURE__ */ new Map();
69
+ nodeOrder = [];
70
+ nodeByDigest = /* @__PURE__ */ new Map();
71
+ edges = /* @__PURE__ */ new Map();
72
+ edgeOrder = [];
73
+ cells = /* @__PURE__ */ new Map();
74
+ operations = /* @__PURE__ */ new Map();
75
+ node(nodeId) {
76
+ const record = this.nodes.get(nodeId);
77
+ return record ? nodeView(record) : void 0;
78
+ }
79
+ nodeIds() {
80
+ return this.nodeOrder;
81
+ }
82
+ hasNode(nodeId) {
83
+ return this.nodes.has(nodeId);
84
+ }
85
+ nodeIdForDigest(artifactDigest) {
86
+ return this.nodeByDigest.get(artifactDigest);
87
+ }
88
+ edge(edgeId) {
89
+ return this.edges.get(edgeId);
90
+ }
91
+ edgeIds() {
92
+ return this.edgeOrder;
93
+ }
94
+ cell(cellId) {
95
+ const cell = this.cells.get(cellId);
96
+ return cell ? { ...cell } : void 0;
97
+ }
98
+ cellIds(nodeId) {
99
+ if (nodeId === void 0) return [...this.cells.keys()];
100
+ return this.nodes.get(nodeId)?.cellIds ?? [];
101
+ }
102
+ operation(operationId) {
103
+ const operation = this.operations.get(operationId);
104
+ return operation ? { ...operation } : void 0;
105
+ }
106
+ scoredCells(nodeId, split) {
107
+ const scored = [];
108
+ for (const cellId of this.nodes.get(nodeId)?.cellIds ?? []) {
109
+ const cell = this.cells.get(cellId);
110
+ if (cell.split === split && cell.score !== null) scored.push({
111
+ cellId,
112
+ unitId: cell.unitId,
113
+ attempt: cell.attempts,
114
+ score: cell.score
115
+ });
116
+ }
117
+ return scored;
118
+ }
119
+ };
120
+ function nodeView(record) {
121
+ const latest = record.decisions.at(-1)?.decision ?? null;
122
+ return {
123
+ nodeId: record.event.nodeId,
124
+ ordinal: record.ordinal,
125
+ artifactDigest: record.event.artifactDigest,
126
+ artifact: record.event.artifact,
127
+ surfaces: record.event.surfaces,
128
+ primaryParentId: record.primaryParentId,
129
+ depth: record.depth,
130
+ parents: [...record.parents],
131
+ children: [...record.children],
132
+ edgeIds: [...record.edgeIds],
133
+ status: latest?.status ?? null,
134
+ rung: latest?.status === "advanced" ? latest.rung : null,
135
+ finalist: record.finalist,
136
+ decisions: [...record.decisions],
137
+ cellCount: record.cellIds.length,
138
+ spend: { ...record.spend },
139
+ registeredSequence: record.registeredSequence,
140
+ updatedSequence: record.updatedSequence
141
+ };
142
+ }
143
+ //#endregion
144
+ //#region src/campaign/search-state-spend.ts
145
+ /** Spend bookkeeping of a search ledger: the running totals, holds and the claim reserve. */
146
+ /** A cell's share of the open reservations, its overspend, and what it draws
147
+ * from the claim reserve: the larger of its hold and its spend. */
148
+ function reservationParts(cell) {
149
+ const reserved = cell.reservation?.usd ?? 0;
150
+ const holding = !cell.final && cell.cancelled === null;
151
+ return {
152
+ open: holding ? Math.max(0, reserved - cell.spentUsd) : 0,
153
+ over: cell.reservation ? Math.max(0, cell.spentUsd - reserved) : 0,
154
+ used: holding ? Math.max(reserved, cell.spentUsd) : cell.spentUsd
155
+ };
156
+ }
157
+ /** Writes the spend, token and reservation totals of the audit it is given. */
158
+ var SearchSpendBook = class {
159
+ audit;
160
+ claimUsedUsd = 0;
161
+ constructor(audit) {
162
+ this.audit = audit;
163
+ }
164
+ /** Book an attempt's or operation's accounting into the running totals. An
165
+ * unknown cost counts as its proven floor. */
166
+ book(accounting) {
167
+ const { tokens, cost } = accounting;
168
+ if (tokens.status === "known") {
169
+ this.audit.tokens.inputTokens += tokens.inputTokens;
170
+ this.audit.tokens.outputTokens += tokens.outputTokens;
171
+ this.audit.tokens.cachedTokens += tokens.cachedTokens;
172
+ } else this.audit.tokens.unknownTokenAttempts += 1;
173
+ const usd = cost.status === "known" ? cost.usd : cost.knownLowerBoundUsd;
174
+ if (cost.status === "known") this.audit.spend.knownUsd += usd;
175
+ else this.audit.spend.floorUsd += usd;
176
+ this.audit.spend.committedUsd += usd;
177
+ return {
178
+ usd,
179
+ unknown: cost.status === "unknown"
180
+ };
181
+ }
182
+ /** A new claim cell's hold counts against the claim reserve. */
183
+ drawClaim(reservation) {
184
+ this.claimUsedUsd += reservation?.usd ?? 0;
185
+ }
186
+ /** Move the audit's open holds, overspend and claim draw from a cell's
187
+ * `before` shares to its current ones. */
188
+ shiftHolds(cell, before) {
189
+ const after = reservationParts(cell);
190
+ this.audit.spend.openReservationUsd += after.open - before.open;
191
+ this.audit.spend.overspendUsd += after.over - before.over;
192
+ if (cell.stage === "claim") this.claimUsedUsd += after.used - before.used;
193
+ }
194
+ /** The admission rule: committed spend, open holds, the unspent claim
195
+ * reserve and the new hold stay within the cap. A claim cell draws on the
196
+ * claim reserve instead of adding to it.
197
+ *
198
+ * The rule admits holds, not records. Spend above a hold is legal
199
+ * overspend, so committed spend can pass the cap; an event that holds
200
+ * nothing (the claim operation, a cell an external optimizer already ran)
201
+ * and a claim cell the unspent reserve covers are then still admitted. The
202
+ * reserve was set aside at the start and overspend elsewhere cannot take it
203
+ * back; refusing them would leave a search that can never close. */
204
+ admit(budget, reservation, claim, subject) {
205
+ const { maxUsd, reservedClaimUsd } = budget;
206
+ const hold = reservation?.usd ?? 0;
207
+ if (maxUsd === null || hold === 0) return;
208
+ const remainingClaim = Math.max(0, reservedClaimUsd - this.claimUsedUsd);
209
+ if (claim && hold <= remainingClaim + 1e-9) return;
210
+ if (this.audit.spend.committedUsd + this.audit.spend.openReservationUsd + (claim ? 0 : remainingClaim) + hold > maxUsd + 1e-9) throw integrity(`${subject} is not admissible: committed $${roundDecimal(this.audit.spend.committedUsd, 6)} + open $${roundDecimal(this.audit.spend.openReservationUsd, 6)} + claim reserve $${roundDecimal(remainingClaim, 6)} + hold $${roundDecimal(hold, 6)} exceeds the cap $${maxUsd}`);
211
+ }
212
+ /** The largest hold a new non-claim cell or operation may take, and the
213
+ * claim reserve still unspent. */
214
+ room(budget) {
215
+ const { maxUsd, reservedClaimUsd } = budget ?? {
216
+ maxUsd: null,
217
+ reservedClaimUsd: 0
218
+ };
219
+ const claimReserveUsd = Math.max(0, reservedClaimUsd - this.claimUsedUsd);
220
+ if (maxUsd === null) return {
221
+ headroomUsd: null,
222
+ claimReserveUsd
223
+ };
224
+ const used = this.audit.spend.committedUsd + this.audit.spend.openReservationUsd + claimReserveUsd;
225
+ return {
226
+ headroomUsd: Math.max(0, maxUsd - used),
227
+ claimReserveUsd
228
+ };
229
+ }
230
+ };
231
+ //#endregion
65
232
  //#region src/campaign/search-state-view.ts
66
233
  /** Position-bound search snapshots and deterministic scored-unit reads. */
67
234
  /**
@@ -115,12 +282,13 @@ var SearchStateView = class {
115
282
  this.state = state;
116
283
  this.version = version;
117
284
  this.searchId = state.searchId;
118
- this.header = state.readHeader();
119
- this.closed = state.readClosed();
120
- this.head = state.readHead();
121
- this.lastOccurredAt = state.readLastOccurredAt();
122
- this.audit = state.readAudit();
123
- this.budget = state.readBudget();
285
+ const position = state.readPosition();
286
+ this.header = position.header;
287
+ this.closed = position.closed;
288
+ this.head = position.head;
289
+ this.lastOccurredAt = position.lastOccurredAt;
290
+ this.audit = position.audit;
291
+ this.budget = position.budget;
124
292
  const reasons = state.incompleteReasons();
125
293
  if (!this.closed) reasons.unshift("search is open");
126
294
  this.completion = {
@@ -130,49 +298,49 @@ var SearchStateView = class {
130
298
  }
131
299
  /** The node the search started from: the first registered. */
132
300
  get rootNodeId() {
133
- return this.live().readNodeIds()[0] ?? null;
301
+ return this.live().nodeIds()[0] ?? null;
134
302
  }
135
303
  node(nodeId) {
136
- return this.live().readNode(nodeId);
304
+ return this.live().node(nodeId);
137
305
  }
138
306
  hasNode(nodeId) {
139
- return this.live().readHasNode(nodeId);
307
+ return this.live().hasNode(nodeId);
140
308
  }
141
309
  /** Node ids in registration order. */
142
310
  nodeIds() {
143
- return [...this.live().readNodeIds()];
311
+ return [...this.live().nodeIds()];
144
312
  }
145
313
  /** Nodes in registration order. */
146
314
  nodes() {
147
315
  const state = this.live();
148
- return state.readNodeIds().map((nodeId) => state.readNode(nodeId));
316
+ return state.nodeIds().map((nodeId) => state.node(nodeId));
149
317
  }
150
318
  nodeIdForDigest(artifactDigest) {
151
- return this.live().readNodeIdForDigest(artifactDigest);
319
+ return this.live().nodeIdForDigest(artifactDigest);
152
320
  }
153
321
  edge(edgeId) {
154
- return this.live().readEdge(edgeId);
322
+ return this.live().edge(edgeId);
155
323
  }
156
324
  /** Edges in ledger order. */
157
325
  edges() {
158
326
  const state = this.live();
159
- return state.readEdgeIds().map((edgeId) => state.readEdge(edgeId));
327
+ return state.edgeIds().map((edgeId) => state.edge(edgeId));
160
328
  }
161
329
  cell(cellId) {
162
- return this.live().readCell(cellId);
330
+ return this.live().cell(cellId);
163
331
  }
164
332
  /** Cells in allocation order, optionally of one node. */
165
333
  cells(filter = {}) {
166
334
  const state = this.live();
167
- return state.readCellIds(filter.nodeId).map((cellId) => state.readCell(cellId));
335
+ return state.cellIds(filter.nodeId).map((cellId) => state.cell(cellId));
168
336
  }
169
337
  operation(operationId) {
170
- return this.live().readOperation(operationId);
338
+ return this.live().operation(operationId);
171
339
  }
172
340
  /** A node's scored cells on one split, in allocation order. Unscored cells
173
341
  * (errored, cancelled or in flight) are absent, never zero. */
174
342
  scoredCells(nodeId, split) {
175
- return this.live().readScoredCells(nodeId, split);
343
+ return this.live().scoredCells(nodeId, split);
176
344
  }
177
345
  /** Per-unit score means of a node on one split, in unitId order. */
178
346
  unitScores(nodeId, split) {
@@ -182,10 +350,10 @@ var SearchStateView = class {
182
350
  lineage(coordinates) {
183
351
  const state = this.live();
184
352
  if (coordinates.searchId !== this.searchId) throw new SearchLedgerError(`coordinates name search ${coordinates.searchId}, not ${this.searchId}`);
185
- const cell = state.readCell(coordinates.cellId);
353
+ const cell = state.cell(coordinates.cellId);
186
354
  if (!cell || cell.nodeId !== coordinates.nodeId) throw new SearchLedgerError(`cell ${coordinates.cellId} of node ${coordinates.nodeId} is not in search ${this.searchId}`);
187
355
  if (coordinates.attempt < 1 || coordinates.attempt > cell.attempts) throw new SearchLedgerError(`cell ${coordinates.cellId} has ${cell.attempts} settled attempt(s), not attempt ${coordinates.attempt}`);
188
- const node = state.readNode(cell.nodeId);
356
+ const node = state.node(cell.nodeId);
189
357
  if (node.depth === null) throw new SearchLedgerError(`node ${node.nodeId} has unknown lineage, so its depth is unknown`);
190
358
  const containment = this.header?.containment ?? null;
191
359
  return {
@@ -200,32 +368,9 @@ var SearchStateView = class {
200
368
  }
201
369
  live() {
202
370
  if (!this.state.isAt(this.version)) throw new SearchLedgerError(`this SearchStateView is from sequence ${this.head?.sequence ?? -1}, and the ledger has moved on; read the ledger's state again`);
203
- return this.state;
371
+ return this.state.index;
204
372
  }
205
373
  };
206
- function nodeView(record) {
207
- const latest = record.decisions.at(-1)?.decision ?? null;
208
- return {
209
- nodeId: record.event.nodeId,
210
- ordinal: record.ordinal,
211
- artifactDigest: record.event.artifactDigest,
212
- artifact: record.event.artifact,
213
- surfaces: record.event.surfaces,
214
- primaryParentId: record.primaryParentId,
215
- depth: record.depth,
216
- parents: [...record.parents],
217
- children: [...record.children],
218
- edgeIds: [...record.edgeIds],
219
- status: latest?.status ?? null,
220
- rung: latest?.status === "advanced" ? latest.rung : null,
221
- finalist: record.finalist,
222
- decisions: [...record.decisions],
223
- cellCount: record.cellIds.length,
224
- spend: { ...record.spend },
225
- registeredSequence: record.registeredSequence,
226
- updatedSequence: record.updatedSequence
227
- };
228
- }
229
374
  //#endregion
230
375
  //#region src/campaign/search-state-projection.ts
231
376
  const SPLITS = [
@@ -246,21 +391,15 @@ var SearchState = class {
246
391
  head = null;
247
392
  lastOccurredAt = Number.NEGATIVE_INFINITY;
248
393
  lastOccurredAtText = null;
249
- tasks = /* @__PURE__ */ new Map();
250
- nodes = /* @__PURE__ */ new Map();
251
- nodeOrder = [];
252
- nodeByDigest = /* @__PURE__ */ new Map();
253
- edges = /* @__PURE__ */ new Map();
254
- edgeOrder = [];
255
- cells = /* @__PURE__ */ new Map();
256
- operations = /* @__PURE__ */ new Map();
257
394
  selectedNodeId = null;
258
- claimUsedUsd = 0;
259
395
  /** Nodes ever decided `finalist`. */
260
396
  finalists = /* @__PURE__ */ new Set();
261
397
  /** Claim cells allocated. Once one exists, the finalists are fixed. */
262
398
  claimCells = 0;
263
399
  audit;
400
+ spend;
401
+ /** @internal The live indexes a `SearchStateView` reads. */
402
+ index = new SearchLedgerIndexes();
264
403
  constructor(searchId) {
265
404
  this.searchId = searchId;
266
405
  this.audit = {
@@ -314,6 +453,7 @@ var SearchState = class {
314
453
  unknownTokenAttempts: 0
315
454
  }
316
455
  };
456
+ this.spend = new SearchSpendBook(this.audit);
317
457
  }
318
458
  apply(entry, index) {
319
459
  const event = entry.event;
@@ -388,9 +528,9 @@ var SearchState = class {
388
528
  const { tasks, taskSetDigest } = event.splits[split];
389
529
  if (searchTaskSetDigest(tasks) !== taskSetDigest) throw integrity(`the ${split} split's taskSetDigest does not match its tasks`);
390
530
  for (const task of tasks) {
391
- const owner = this.tasks.get(task.taskId);
531
+ const owner = this.index.tasks.get(task.taskId);
392
532
  if (owner) throw integrity(`task ${task.taskId} is in both the ${owner.split} and ${split} splits`);
393
- this.tasks.set(task.taskId, {
533
+ this.index.tasks.set(task.taskId, {
394
534
  split,
395
535
  unitId: task.unitId
396
536
  });
@@ -406,10 +546,10 @@ var SearchState = class {
406
546
  this.header = event;
407
547
  }
408
548
  startOperation(event) {
409
- if (this.operations.has(event.operationId)) throw integrity(`operation ${event.operationId} was started twice`);
549
+ if (this.index.operations.has(event.operationId)) throw integrity(`operation ${event.operationId} was started twice`);
410
550
  this.assertSearching(`operation ${event.operationId}`);
411
- this.admit(event.reservation, false, `operation ${event.operationId}`);
412
- this.operations.set(event.operationId, {
551
+ this.spend.admit(this.header.budget, event.reservation, false, `operation ${event.operationId}`);
552
+ this.index.operations.set(event.operationId, {
413
553
  operationId: event.operationId,
414
554
  operationKind: event.operationKind,
415
555
  reservation: event.reservation,
@@ -425,11 +565,11 @@ var SearchState = class {
425
565
  this.audit.spend.openReservationUsd += event.reservation?.usd ?? 0;
426
566
  }
427
567
  recordOperation(event) {
428
- const operation = this.operations.get(event.operationId);
568
+ const operation = this.index.operations.get(event.operationId);
429
569
  if (!operation) throw integrity(`operation ${event.operationId} was recorded before it started`);
430
570
  if (operation.recorded) throw integrity(`operation ${event.operationId} was recorded twice`);
431
571
  if (operation.operationKind !== event.operationKind) throw integrity(`operation ${event.operationId} started as ${operation.operationKind} but recorded as ${event.operationKind}`);
432
- const cost = this.book(event.accounting);
572
+ const cost = this.spend.book(event.accounting);
433
573
  if (cost.unknown) this.audit.spend.unknownCostOperations += 1;
434
574
  const reserved = operation.reservation?.usd ?? 0;
435
575
  this.audit.spend.openReservationUsd -= reserved;
@@ -446,14 +586,14 @@ var SearchState = class {
446
586
  registerNode(event, index) {
447
587
  const expected = searchNodeId(this.searchId, event.artifactDigest);
448
588
  if (event.nodeId !== expected) throw integrity(`node ${event.nodeId} does not match its artifact digest (expected ${expected})`);
449
- if (this.nodes.has(event.nodeId)) throw integrity(`node ${event.nodeId} was registered twice; a re-proposal is a second edge into it`);
589
+ if (this.index.nodes.has(event.nodeId)) throw integrity(`node ${event.nodeId} was registered twice; a re-proposal is a second edge into it`);
450
590
  this.assertSearching(`node ${event.nodeId}`);
451
591
  assertUnique(event.surfaces.map((surface) => surface.surfaceId), "surfaceId", event.eventId);
452
592
  const maxNodes = this.header.budget.maxNodes;
453
- if (maxNodes !== null && this.nodes.size + 1 > maxNodes) throw integrity(`node ${event.nodeId} exceeds the search's maxNodes ${maxNodes}`);
454
- this.nodes.set(event.nodeId, {
593
+ if (maxNodes !== null && this.index.nodes.size + 1 > maxNodes) throw integrity(`node ${event.nodeId} exceeds the search's maxNodes ${maxNodes}`);
594
+ this.index.nodes.set(event.nodeId, {
455
595
  event,
456
- ordinal: this.nodeOrder.length,
596
+ ordinal: this.index.nodeOrder.length,
457
597
  registeredSequence: index,
458
598
  updatedSequence: index,
459
599
  edgeIds: [],
@@ -473,15 +613,15 @@ var SearchState = class {
473
613
  boxMinutes: 0
474
614
  }
475
615
  });
476
- this.nodeOrder.push(event.nodeId);
477
- this.nodeByDigest.set(event.artifactDigest, event.nodeId);
616
+ this.index.nodeOrder.push(event.nodeId);
617
+ this.index.nodeByDigest.set(event.artifactDigest, event.nodeId);
478
618
  this.audit.nodes += 1;
479
619
  this.audit.nodesWithoutEdge += 1;
480
620
  this.audit.undecidedNodes += 1;
481
621
  }
482
622
  recordEdge(event, index) {
483
- if (this.edges.has(event.edgeId)) throw integrity(`edge ${event.edgeId} was recorded twice`);
484
- const child = this.nodes.get(event.childNodeId);
623
+ if (this.index.edges.has(event.edgeId)) throw integrity(`edge ${event.edgeId} was recorded twice`);
624
+ const child = this.index.nodes.get(event.childNodeId);
485
625
  if (!child) throw integrity(`edge ${event.edgeId} names unregistered child ${event.childNodeId}`);
486
626
  this.assertSearching(`edge ${event.edgeId}`);
487
627
  if (event.label.length > 200) throw integrity(`edge ${event.edgeId} label exceeds 200 characters`);
@@ -494,7 +634,7 @@ var SearchState = class {
494
634
  if (operator !== "seed" && attribution !== "unknown" && parents.length === 0) throw integrity(`${operator} edge ${event.edgeId} names no parent`);
495
635
  if (operator === "merge" && attribution !== "unknown" && parents.length < 2) throw integrity(`merge edge ${event.edgeId} needs at least two parents`);
496
636
  const operationId = event.proposer?.operationId ?? null;
497
- if (operationId !== null && !this.operations.has(operationId)) throw integrity(`edge ${event.edgeId} names operation ${operationId}, which never started`);
637
+ if (operationId !== null && !this.index.operations.has(operationId)) throw integrity(`edge ${event.edgeId} names operation ${operationId}, which never started`);
498
638
  const reproposal = child.edgeIds.length > 0;
499
639
  const inSearch = [];
500
640
  const later = /* @__PURE__ */ new Set();
@@ -505,7 +645,7 @@ var SearchState = class {
505
645
  continue;
506
646
  }
507
647
  if (operator === "derive") throw integrity(`derive edge ${event.edgeId} must name the parent in derivedFrom`);
508
- const record = this.nodes.get(parent.nodeId);
648
+ const record = this.index.nodes.get(parent.nodeId);
509
649
  if (!record) throw integrity(`edge ${event.edgeId} names unregistered parent ${parent.nodeId}`);
510
650
  if (record.ordinal >= child.ordinal && !reproposal) throw integrity(`edge ${event.edgeId}: parent ${parent.nodeId} registered after child ${child.event.nodeId}`);
511
651
  if (record.edgeIds.length === 0) throw integrity(`edge ${event.edgeId}: parent ${parent.nodeId} has no edge of its own yet`);
@@ -528,18 +668,18 @@ var SearchState = class {
528
668
  }
529
669
  for (const parent of inSearch) if (!parent.children.includes(child.event.nodeId)) parent.children.push(child.event.nodeId);
530
670
  child.updatedSequence = index;
531
- this.edges.set(event.edgeId, event);
532
- this.edgeOrder.push(event.edgeId);
671
+ this.index.edges.set(event.edgeId, event);
672
+ this.index.edgeOrder.push(event.edgeId);
533
673
  this.audit.edges[attribution] += 1;
534
674
  }
535
675
  allocateCell(event, index) {
536
676
  const expected = searchCellId(this.searchId, event.nodeId, event.taskId, event.split, event.rep);
537
677
  if (event.cellId !== expected) throw integrity(`cell ${event.cellId} does not match its coordinates (expected ${expected})`);
538
- if (this.cells.has(event.cellId)) throw integrity(`cell ${event.cellId} was allocated twice`);
539
- const node = this.nodes.get(event.nodeId);
678
+ if (this.index.cells.has(event.cellId)) throw integrity(`cell ${event.cellId} was allocated twice`);
679
+ const node = this.index.nodes.get(event.nodeId);
540
680
  if (!node) throw integrity(`cell ${event.cellId} names unregistered node ${event.nodeId}`);
541
681
  if (node.edgeIds.length === 0) throw integrity(`cell ${event.cellId}: node ${event.nodeId} has no edge yet`);
542
- const task = this.tasks.get(event.taskId);
682
+ const task = this.index.tasks.get(event.taskId);
543
683
  if (!task || task.split !== event.split) throw integrity(`cell ${event.cellId}: task ${event.taskId} is not in the ${event.split} split`);
544
684
  if (task.unitId !== event.unitId) throw integrity(`cell ${event.cellId}: task ${event.taskId} samples unit ${task.unitId}`);
545
685
  assertStageSplit(event.cellId, event.stage, event.split, node.ordinal === 0);
@@ -547,8 +687,8 @@ var SearchState = class {
547
687
  if (event.stage !== "claim") this.assertSearching(`cell ${event.cellId}`);
548
688
  const maxCells = this.header.budget.maxCells;
549
689
  if (maxCells !== null && this.audit.cells.allocated + 1 > maxCells) throw integrity(`cell ${event.cellId} exceeds the search's maxCells ${maxCells}`);
550
- this.admit(event.reservation, event.stage === "claim", `cell ${event.cellId}`);
551
- this.cells.set(event.cellId, {
690
+ this.spend.admit(this.header.budget, event.reservation, event.stage === "claim", `cell ${event.cellId}`);
691
+ this.index.cells.set(event.cellId, {
552
692
  cellId: event.cellId,
553
693
  nodeId: event.nodeId,
554
694
  taskId: event.taskId,
@@ -576,12 +716,12 @@ var SearchState = class {
576
716
  this.audit.cells.open += 1;
577
717
  this.audit.spend.openReservationUsd += event.reservation?.usd ?? 0;
578
718
  if (event.stage === "claim") {
579
- this.claimUsedUsd += event.reservation?.usd ?? 0;
719
+ this.spend.drawClaim(event.reservation);
580
720
  this.claimCells += 1;
581
721
  }
582
722
  }
583
723
  settleCell(event, index) {
584
- const cell = this.cells.get(event.cellId);
724
+ const cell = this.index.cells.get(event.cellId);
585
725
  if (!cell) throw integrity(`attempt ${event.eventId} names unallocated cell ${event.cellId}`);
586
726
  if (cell.cancelled) throw integrity(`cell ${event.cellId} was settled after it was cancelled`);
587
727
  if (cell.final) throw integrity(`cell ${event.cellId} was attempted again after a final ${cell.outcome} outcome; only a retryable error admits another attempt`);
@@ -591,13 +731,13 @@ var SearchState = class {
591
731
  attempt: event.attempt
592
732
  });
593
733
  if (event.runId !== runId) throw integrity(`cell ${event.cellId} attempt ${event.attempt} must have runId ${runId}`);
594
- const node = this.nodes.get(cell.nodeId);
734
+ const node = this.index.nodes.get(cell.nodeId);
595
735
  const declared = new Set(node.event.surfaces.map((surface) => surface.surfaceId));
596
736
  assertUnique(event.surfaceEvidence.map((evidence) => evidence.surfaceId), "surface evidence", event.eventId);
597
737
  for (const evidence of event.surfaceEvidence) if (!declared.has(evidence.surfaceId)) throw integrity(`attempt ${event.eventId} reports surface ${evidence.surfaceId}, which node ${cell.nodeId} does not declare`);
598
738
  if (event.artifacts.filter((artifact) => artifact.role === "run-record").length > 1) throw integrity(`attempt ${event.eventId} binds more than one RunRecord`);
599
- const before = this.reservationParts(cell);
600
- const cost = this.book(event.accounting);
739
+ const before = reservationParts(cell);
740
+ const cost = this.spend.book(event.accounting);
601
741
  if (cell.attempts === 0) {
602
742
  this.audit.cells.settled += 1;
603
743
  this.audit.cells.open -= 1;
@@ -620,10 +760,7 @@ var SearchState = class {
620
760
  cell.score = event.outcome.score;
621
761
  node.scoredCells += 1;
622
762
  }
623
- const after = this.reservationParts(cell);
624
- this.audit.spend.openReservationUsd += after.open - before.open;
625
- this.audit.spend.overspendUsd += after.over - before.over;
626
- if (cell.stage === "claim") this.claimUsedUsd += after.used - before.used;
763
+ this.spend.shiftHolds(cell, before);
627
764
  if (cost.unknown) node.spend.floorUsd += cost.usd;
628
765
  else node.spend.knownUsd += cost.usd;
629
766
  const boxMinutes = event.boxMinutes ?? 0;
@@ -634,25 +771,22 @@ var SearchState = class {
634
771
  this.audit.outcomes[event.outcome.status] += 1;
635
772
  }
636
773
  cancelCell(event, index) {
637
- const cell = this.cells.get(event.cellId);
774
+ const cell = this.index.cells.get(event.cellId);
638
775
  if (!cell) throw integrity(`cancellation ${event.eventId} names unallocated cell ${event.cellId}`);
639
776
  if (cell.cancelled) throw integrity(`cell ${event.cellId} was cancelled twice`);
640
777
  if (cell.final) throw integrity(`cell ${event.cellId} was cancelled after a final outcome`);
641
- const before = this.reservationParts(cell);
778
+ const before = reservationParts(cell);
642
779
  cell.cancelled = event.reason;
643
780
  cell.updatedSequence = index;
644
- const after = this.reservationParts(cell);
645
- this.audit.spend.openReservationUsd += after.open - before.open;
646
- this.audit.spend.overspendUsd += after.over - before.over;
647
- if (cell.stage === "claim") this.claimUsedUsd += after.used - before.used;
781
+ this.spend.shiftHolds(cell, before);
648
782
  if (cell.attempts === 0) {
649
783
  this.audit.cells.cancelled += 1;
650
784
  this.audit.cells.open -= 1;
651
785
  }
652
- this.nodes.get(cell.nodeId).updatedSequence = index;
786
+ this.index.nodes.get(cell.nodeId).updatedSequence = index;
653
787
  }
654
788
  decideNode(event, index) {
655
- const node = this.nodes.get(event.nodeId);
789
+ const node = this.index.nodes.get(event.nodeId);
656
790
  if (!node) throw integrity(`decision ${event.eventId} names unregistered node ${event.nodeId}`);
657
791
  if (node.edgeIds.length === 0) throw integrity(`decision ${event.eventId}: node ${event.nodeId} has no edge yet`);
658
792
  const status = event.decision.status;
@@ -697,7 +831,7 @@ var SearchState = class {
697
831
  assertUnique(named, "claim finalist", "search-closed");
698
832
  for (const nodeId of named) if (!this.finalists.has(nodeId)) throw integrity(`claim finalist ${nodeId} was never decided finalist`);
699
833
  for (const nodeId of this.finalists) if (!named.includes(nodeId)) throw integrity(`finalist ${nodeId} is missing from the claim`);
700
- const root = this.nodeOrder[0] ?? null;
834
+ const root = this.index.nodeOrder[0] ?? null;
701
835
  const perFinalist = 1 - (1 - claim.confidence) / Math.max(1, named.length);
702
836
  for (const finalist of claim.finalists) {
703
837
  const { estimate, test } = finalist;
@@ -713,13 +847,13 @@ var SearchState = class {
713
847
  if ("unknown" in this.header.objective.judge) throw integrity("a ship claim needs a pinned judge; with an unknown judge a resumed search could mix two judges' verdicts");
714
848
  const testUnits = new Set(this.header.splits.test.tasks.map((task) => task.unitId));
715
849
  for (const nodeId of [root, claim.selected]) {
716
- const scored = new Set(this.readScoredCells(nodeId, "test").map((cell) => cell.unitId));
850
+ const scored = new Set(this.index.scoredCells(nodeId, "test").map((cell) => cell.unitId));
717
851
  const missing = [...testUnits].filter((unit) => !scored.has(unit));
718
852
  if (missing.length > 0) throw integrity(`a ship claim needs every test unit scored by the root and the finalist; ${nodeId} has none on ${missing.join(", ")}`);
719
853
  }
720
854
  } else if (claim.selected !== null && claim.selected !== root) throw integrity(`a ${claim.decision} claim keeps the root; it cannot select another node`);
721
855
  if (claim.decision === "test-cannot-resolve") {
722
- for (const cell of this.cells.values()) if (cell.split === "test" && cell.attempts > 0) throw integrity("a test-cannot-resolve claim spends nothing on test cells");
856
+ for (const cell of this.index.cells.values()) if (cell.split === "test" && cell.attempts > 0) throw integrity("a test-cannot-resolve claim spends nothing on test cells");
723
857
  }
724
858
  }
725
859
  /** Once the first finalist is decided, the search only runs its claim:
@@ -737,154 +871,27 @@ var SearchState = class {
737
871
  if (this.audit.undecidedNodes > 0) reasons.push(count(this.audit.undecidedNodes, "node", "has no terminal decision"));
738
872
  return reasons;
739
873
  }
740
- /** Book an attempt's or operation's accounting into the running totals. An
741
- * unknown cost counts as its proven floor. */
742
- book(accounting) {
743
- const { tokens, cost } = accounting;
744
- if (tokens.status === "known") {
745
- this.audit.tokens.inputTokens += tokens.inputTokens;
746
- this.audit.tokens.outputTokens += tokens.outputTokens;
747
- this.audit.tokens.cachedTokens += tokens.cachedTokens;
748
- } else this.audit.tokens.unknownTokenAttempts += 1;
749
- const usd = cost.status === "known" ? cost.usd : cost.knownLowerBoundUsd;
750
- if (cost.status === "known") this.audit.spend.knownUsd += usd;
751
- else this.audit.spend.floorUsd += usd;
752
- this.audit.spend.committedUsd += usd;
753
- return {
754
- usd,
755
- unknown: cost.status === "unknown"
756
- };
757
- }
758
- /** A cell's share of the open reservations, its overspend, and what it draws
759
- * from the claim reserve: the larger of its hold and its spend. */
760
- reservationParts(cell) {
761
- const reserved = cell.reservation?.usd ?? 0;
762
- const holding = !cell.final && cell.cancelled === null;
763
- return {
764
- open: holding ? Math.max(0, reserved - cell.spentUsd) : 0,
765
- over: cell.reservation ? Math.max(0, cell.spentUsd - reserved) : 0,
766
- used: holding ? Math.max(reserved, cell.spentUsd) : cell.spentUsd
767
- };
768
- }
769
- /** The admission rule: committed spend, open holds, the unspent claim
770
- * reserve and the new hold stay within the cap. A claim cell draws on the
771
- * claim reserve instead of adding to it.
772
- *
773
- * The rule admits holds, not records. Spend above a hold is legal
774
- * overspend, so committed spend can pass the cap; an event that holds
775
- * nothing (the claim operation, a cell an external optimizer already ran)
776
- * and a claim cell the unspent reserve covers are then still admitted. The
777
- * reserve was set aside at the start and overspend elsewhere cannot take it
778
- * back; refusing them would leave a search that can never close. */
779
- admit(reservation, claim, subject) {
780
- const { maxUsd, reservedClaimUsd } = this.header.budget;
781
- const hold = reservation?.usd ?? 0;
782
- if (maxUsd === null || hold === 0) return;
783
- const remainingClaim = Math.max(0, reservedClaimUsd - this.claimUsedUsd);
784
- if (claim && hold <= remainingClaim + 1e-9) return;
785
- if (this.audit.spend.committedUsd + this.audit.spend.openReservationUsd + (claim ? 0 : remainingClaim) + hold > maxUsd + 1e-9) throw integrity(`${subject} is not admissible: committed $${roundDecimal(this.audit.spend.committedUsd, 6)} + open $${roundDecimal(this.audit.spend.openReservationUsd, 6)} + claim reserve $${roundDecimal(remainingClaim, 6)} + hold $${roundDecimal(hold, 6)} exceeds the cap $${maxUsd}`);
786
- }
787
- /** @internal The largest hold a new non-claim cell or operation may take,
788
- * and the claim reserve still unspent. */
789
- readBudget() {
790
- const { maxUsd, reservedClaimUsd } = this.header?.budget ?? {
791
- maxUsd: null,
792
- reservedClaimUsd: 0
793
- };
794
- const claimReserveUsd = Math.max(0, reservedClaimUsd - this.claimUsedUsd);
795
- if (maxUsd === null) return {
796
- headroomUsd: null,
797
- claimReserveUsd
798
- };
799
- const used = this.audit.spend.committedUsd + this.audit.spend.openReservationUsd + claimReserveUsd;
800
- return {
801
- headroomUsd: Math.max(0, maxUsd - used),
802
- claimReserveUsd
803
- };
804
- }
805
- /** @internal */
806
- readHeader() {
807
- return this.header;
808
- }
809
- /** @internal */
810
- readClosed() {
811
- return this.closed;
812
- }
813
- /** @internal */
814
- readHead() {
815
- return this.head;
816
- }
817
- /** @internal */
818
- readLastOccurredAt() {
819
- return this.lastOccurredAtText;
820
- }
821
- /** @internal */
822
- readAudit() {
874
+ /** @internal What a `SearchStateView` copies at construction: the header,
875
+ * close, head and last timestamp, a snapshot of the audit, and the budget room. */
876
+ readPosition() {
823
877
  const audit = this.audit;
824
878
  return {
825
- ...audit,
826
- edges: { ...audit.edges },
827
- cells: { ...audit.cells },
828
- outcomes: { ...audit.outcomes },
829
- operations: { ...audit.operations },
830
- spend: { ...audit.spend },
831
- tokens: { ...audit.tokens }
879
+ header: this.header,
880
+ closed: this.closed,
881
+ head: this.head,
882
+ lastOccurredAt: this.lastOccurredAtText,
883
+ audit: {
884
+ ...audit,
885
+ edges: { ...audit.edges },
886
+ cells: { ...audit.cells },
887
+ outcomes: { ...audit.outcomes },
888
+ operations: { ...audit.operations },
889
+ spend: { ...audit.spend },
890
+ tokens: { ...audit.tokens }
891
+ },
892
+ budget: this.spend.room(this.header?.budget)
832
893
  };
833
894
  }
834
- /** @internal */
835
- readNode(nodeId) {
836
- const record = this.nodes.get(nodeId);
837
- return record ? nodeView(record) : void 0;
838
- }
839
- /** @internal */
840
- readNodeIds() {
841
- return this.nodeOrder;
842
- }
843
- /** @internal */
844
- readHasNode(nodeId) {
845
- return this.nodes.has(nodeId);
846
- }
847
- /** @internal */
848
- readNodeIdForDigest(artifactDigest) {
849
- return this.nodeByDigest.get(artifactDigest);
850
- }
851
- /** @internal */
852
- readEdge(edgeId) {
853
- return this.edges.get(edgeId);
854
- }
855
- /** @internal */
856
- readEdgeIds() {
857
- return this.edgeOrder;
858
- }
859
- /** @internal */
860
- readCell(cellId) {
861
- const cell = this.cells.get(cellId);
862
- return cell ? { ...cell } : void 0;
863
- }
864
- /** @internal */
865
- readCellIds(nodeId) {
866
- if (nodeId === void 0) return [...this.cells.keys()];
867
- return this.nodes.get(nodeId)?.cellIds ?? [];
868
- }
869
- /** @internal */
870
- readOperation(operationId) {
871
- const operation = this.operations.get(operationId);
872
- return operation ? { ...operation } : void 0;
873
- }
874
- /** @internal */
875
- readScoredCells(nodeId, split) {
876
- const scored = [];
877
- for (const cellId of this.nodes.get(nodeId)?.cellIds ?? []) {
878
- const cell = this.cells.get(cellId);
879
- if (cell.split === split && cell.score !== null) scored.push({
880
- cellId,
881
- unitId: cell.unitId,
882
- attempt: cell.attempts,
883
- score: cell.score
884
- });
885
- }
886
- return scored;
887
- }
888
895
  };
889
896
  function count(value, noun, predicate) {
890
897
  const verb = predicate.replace(/^(has|is) /, (word) => value === 1 ? word : word === "has " ? "have " : "are ");
@@ -1582,4 +1589,4 @@ function formatZodError(error) {
1582
1589
  //#endregion
1583
1590
  export { validateSearchLedgerEvent as a, SearchStateView as c, searchNodeId as d, searchTaskSetDigest as f, replaySearchLedgerText as i, searchUnitScores as l, openSearchLedger as n, SEARCH_LEDGER_SCHEMA as o, USD_TOLERANCE as p, parseSearchLedgerLine as r, SearchState as s, FileSearchLedger as t, searchCellId as u };
1584
1591
 
1585
- //# sourceMappingURL=search-ledger-ClpN4I3e.js.map
1592
+ //# sourceMappingURL=search-ledger-CZRqtFbW.js.map