@tangle-network/agent-eval 0.191.0 → 0.193.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 (122) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/adapters/http.d.ts +2 -2
  3. package/dist/analyst/index.d.ts +6 -6
  4. package/dist/analyst/index.js +3 -3
  5. package/dist/{analyze-runs-SCSWYpy0.js → analyze-runs-B6gv7vAT.js} +2 -2
  6. package/dist/{analyze-runs-SCSWYpy0.js.map → analyze-runs-B6gv7vAT.js.map} +1 -1
  7. package/dist/{benchmark-command-3tZJnD82.js → benchmark-command-DVhUPU7H.js} +7 -7
  8. package/dist/{benchmark-command-3tZJnD82.js.map → benchmark-command-DVhUPU7H.js.map} +1 -1
  9. package/dist/benchmarks/index.d.ts +3 -3
  10. package/dist/benchmarks/index.js +3 -3
  11. package/dist/campaign/index.d.ts +5 -5
  12. package/dist/campaign/index.js +12 -12
  13. package/dist/{campaign-C8iWEF0M.js → campaign-DdE6S80e.js} +15 -328
  14. package/dist/campaign-DdE6S80e.js.map +1 -0
  15. package/dist/{campaign-evidence-BqjlTM3P.js → campaign-evidence-DJc60F9C.js} +11 -46
  16. package/dist/campaign-evidence-DJc60F9C.js.map +1 -0
  17. package/dist/cli.js +47 -10
  18. package/dist/cli.js.map +1 -1
  19. package/dist/{client-wFyDisxi.d.ts → client-dMMKoQNS.d.ts} +2 -2
  20. package/dist/{client-wFyDisxi.d.ts.map → client-dMMKoQNS.d.ts.map} +1 -1
  21. package/dist/contract/index.d.ts +7 -7
  22. package/dist/contract/index.js +10 -10
  23. package/dist/{default-registry-DK_9aVJf.d.ts → default-registry-C3HTE08O.d.ts} +3 -3
  24. package/dist/{default-registry-DK_9aVJf.d.ts.map → default-registry-C3HTE08O.d.ts.map} +1 -1
  25. package/dist/{define-agent-eval-P28y7mI1.d.ts → define-agent-eval-DQJvVa1P.d.ts} +15 -24
  26. package/dist/{define-agent-eval-P28y7mI1.d.ts.map → define-agent-eval-DQJvVa1P.d.ts.map} +1 -1
  27. package/dist/{define-agent-eval-BE43YTm4.js → define-agent-eval-DkPbnJEY.js} +9 -10
  28. package/dist/define-agent-eval-DkPbnJEY.js.map +1 -0
  29. package/dist/diagnosis/index.js +1 -1
  30. package/dist/{dspy-rlm-engine-MfpwoEaj.js → dspy-rlm-engine-BX34vPVY.js} +2 -2
  31. package/dist/{dspy-rlm-engine-MfpwoEaj.js.map → dspy-rlm-engine-BX34vPVY.js.map} +1 -1
  32. package/dist/{engine-vNiM02Ub.d.ts → engine-DjRV55F9.d.ts} +2 -2
  33. package/dist/{engine-vNiM02Ub.d.ts.map → engine-DjRV55F9.d.ts.map} +1 -1
  34. package/dist/evaluation.d.ts +1 -1
  35. package/dist/experiment/index.d.ts +2 -2
  36. package/dist/experiment/index.js +4 -4
  37. package/dist/{external-optimizer-process-xFTliuiK.js → external-optimizer-process-D-Sab4v3.js} +3 -62
  38. package/dist/external-optimizer-process-D-Sab4v3.js.map +1 -0
  39. package/dist/{external-optimizer-subprocess-B234V9V0.js → external-optimizer-subprocess-kbqC-iWf.js} +64 -3
  40. package/dist/external-optimizer-subprocess-kbqC-iWf.js.map +1 -0
  41. package/dist/hosted/index.d.ts +2 -2
  42. package/dist/hosted/index.js +1 -1
  43. package/dist/{index-D-TN7ZRM.d.ts → index-0jEtpSM7.d.ts} +3 -3
  44. package/dist/{index-D-TN7ZRM.d.ts.map → index-0jEtpSM7.d.ts.map} +1 -1
  45. package/dist/{index-I4UJGRno.d.ts → index-Bj96W0jZ.d.ts} +28 -2
  46. package/dist/{index-I4UJGRno.d.ts.map → index-Bj96W0jZ.d.ts.map} +1 -1
  47. package/dist/{index-CzfnASj_.d.ts → index-DMPLG226.d.ts} +400 -374
  48. package/dist/index-DMPLG226.d.ts.map +1 -0
  49. package/dist/{index-DH-MWH5f.d.ts → index-DWQNDqPd.d.ts} +7 -7
  50. package/dist/{index-DH-MWH5f.d.ts.map → index-DWQNDqPd.d.ts.map} +1 -1
  51. package/dist/index.d.ts +9 -9
  52. package/dist/index.js +9 -292
  53. package/dist/index.js.map +1 -1
  54. package/dist/jev.d.ts +1 -1
  55. package/dist/{journal-c3qY0SDP.js → journal-DvKCvjty.js} +94 -2
  56. package/dist/{journal-c3qY0SDP.js.map → journal-DvKCvjty.js.map} +1 -1
  57. package/dist/ledger-core/index.d.ts +2 -2
  58. package/dist/ledger-core/index.js +2 -2
  59. package/dist/{llm-judge-NJOHM0iA.js → llm-judge-ChbWfeWl.js} +2653 -656
  60. package/dist/llm-judge-ChbWfeWl.js.map +1 -0
  61. package/dist/{matrix-CgqdFm5N.d.ts → matrix-CNu13gg8.d.ts} +2 -2
  62. package/dist/{matrix-CgqdFm5N.d.ts.map → matrix-CNu13gg8.d.ts.map} +1 -1
  63. package/dist/multishot/golden/index.d.ts +1 -1
  64. package/dist/multishot/index.d.ts +2 -2
  65. package/dist/openapi.json +1 -1
  66. package/dist/paired-delta-test-BF-FEP8V.js +78 -0
  67. package/dist/paired-delta-test-BF-FEP8V.js.map +1 -0
  68. package/dist/{paired-promotion-decision-6cn3bLc3.js → paired-promotion-decision-DTKl6-G8.js} +4 -77
  69. package/dist/paired-promotion-decision-DTKl6-G8.js.map +1 -0
  70. package/dist/{produced-state-CY-L7CqS.js → produced-state-BqeIaIp2.js} +3 -3
  71. package/dist/{produced-state-CY-L7CqS.js.map → produced-state-BqeIaIp2.js.map} +1 -1
  72. package/dist/{promotion-policy-CfMNUro2.js → promotion-policy-Cy5OV8BV.js} +3 -3
  73. package/dist/{promotion-policy-CfMNUro2.js.map → promotion-policy-Cy5OV8BV.js.map} +1 -1
  74. package/dist/rl.d.ts +1 -1
  75. package/dist/rl.js +1 -1
  76. package/dist/{search-ledger-CQ8Uy4FK.js → search-ledger-CJiAEU3j.js} +164 -36
  77. package/dist/search-ledger-CJiAEU3j.js.map +1 -0
  78. package/dist/{search-ledger-file-EvdDZRhv.js → search-ledger-file-DY0AXX3W.js} +2 -2
  79. package/dist/{search-ledger-file-EvdDZRhv.js.map → search-ledger-file-DY0AXX3W.js.map} +1 -1
  80. package/dist/{search-ledger-types-C9anU2DX.d.ts → search-ledger-types-B3El5xgS.d.ts} +95 -11
  81. package/dist/search-ledger-types-B3El5xgS.d.ts.map +1 -0
  82. package/dist/{search-shipper-C55V7HE3.js → search-shipper-DUlwlCsK.js} +3 -3
  83. package/dist/{search-shipper-C55V7HE3.js.map → search-shipper-DUlwlCsK.js.map} +1 -1
  84. package/dist/search-summary-BhY8p0Av.js +378 -0
  85. package/dist/search-summary-BhY8p0Av.js.map +1 -0
  86. package/dist/{semantic-concept-judge-Bt7bXAAo.js → semantic-concept-judge-Bg-560H0.js} +3 -3
  87. package/dist/{semantic-concept-judge-Bt7bXAAo.js.map → semantic-concept-judge-Bg-560H0.js.map} +1 -1
  88. package/dist/{sequential-lwJOTRON.js → sequential-C21_PP7F.js} +2 -2
  89. package/dist/{sequential-lwJOTRON.js.map → sequential-C21_PP7F.js.map} +1 -1
  90. package/dist/{skillopt-optimization-method-DwO3TOAV.js → skillopt-optimization-method-DXQdaBjx.js} +7 -7
  91. package/dist/{skillopt-optimization-method-DwO3TOAV.js.map → skillopt-optimization-method-DXQdaBjx.js.map} +1 -1
  92. package/dist/{statistical-heldout-BED3wBtX.d.ts → statistical-heldout-D-xBpbrd.d.ts} +2 -2
  93. package/dist/{statistical-heldout-BED3wBtX.d.ts.map → statistical-heldout-D-xBpbrd.d.ts.map} +1 -1
  94. package/dist/{statistical-heldout-GcLIPxk4.js → statistical-heldout-DANVmyug.js} +2 -2
  95. package/dist/{statistical-heldout-GcLIPxk4.js.map → statistical-heldout-DANVmyug.js.map} +1 -1
  96. package/dist/{store-tool-spans-Cfh9XUCm.d.ts → store-tool-spans-BxrfuU2t.d.ts} +4 -4
  97. package/dist/{store-tool-spans-Cfh9XUCm.d.ts.map → store-tool-spans-BxrfuU2t.d.ts.map} +1 -1
  98. package/dist/{tool-groups-D-mkeN0N.d.ts → tool-groups-BC9O7Epn.d.ts} +2 -2
  99. package/dist/tool-groups-BC9O7Epn.d.ts.map +1 -0
  100. package/dist/trace-repair/index.d.ts +1 -1
  101. package/dist/traces.d.ts +2 -2
  102. package/dist/{types-BmQRs661.d.ts → types-BWwRXofp.d.ts} +340 -17
  103. package/dist/types-BWwRXofp.d.ts.map +1 -0
  104. package/dist/{types-ClK-sHs8.d.ts → types-qoxUfKQg.d.ts} +2 -2
  105. package/dist/{types-ClK-sHs8.d.ts.map → types-qoxUfKQg.d.ts.map} +1 -1
  106. package/docs/campaign-proposers.md +14 -16
  107. package/docs/search-ledger.md +129 -3
  108. package/package.json +1 -1
  109. package/dist/campaign-C8iWEF0M.js.map +0 -1
  110. package/dist/campaign-evidence-BqjlTM3P.js.map +0 -1
  111. package/dist/claim-BGOUqoMq.d.ts +0 -38
  112. package/dist/claim-BGOUqoMq.d.ts.map +0 -1
  113. package/dist/define-agent-eval-BE43YTm4.js.map +0 -1
  114. package/dist/external-optimizer-process-xFTliuiK.js.map +0 -1
  115. package/dist/external-optimizer-subprocess-B234V9V0.js.map +0 -1
  116. package/dist/index-CzfnASj_.d.ts.map +0 -1
  117. package/dist/llm-judge-NJOHM0iA.js.map +0 -1
  118. package/dist/paired-promotion-decision-6cn3bLc3.js.map +0 -1
  119. package/dist/search-ledger-CQ8Uy4FK.js.map +0 -1
  120. package/dist/search-ledger-types-C9anU2DX.d.ts.map +0 -1
  121. package/dist/tool-groups-D-mkeN0N.d.ts.map +0 -1
  122. package/dist/types-BmQRs661.d.ts.map +0 -1
@@ -0,0 +1,378 @@
1
+ import { a as hashCanonical, i as compareCodeUnits } from "./canonical-BsZVgBhW.js";
2
+ import { n as DECISION_PAIRED_DELTA_STATISTIC } from "./paired-tests-C8iCsioC.js";
3
+ import { n as pairedDeltaTest, t as minimumPairsForPairedDeltaTest } from "./paired-delta-test-BF-FEP8V.js";
4
+ import { a as SearchLedgerError } from "./search-ledger-file-DY0AXX3W.js";
5
+ import { f as searchUnitScores } from "./search-ledger-CJiAEU3j.js";
6
+ //#region src/campaign/estimate-node.ts
7
+ /**
8
+ * Per-node statistics of a search.
9
+ *
10
+ * `estimateNode` is the one paired contrast of two nodes: it averages each
11
+ * node's cells inside their units, pairs the units both nodes scored, and runs
12
+ * `pairedDeltaTest` on the per-unit deltas. `estimateNodeFromCells` is the same
13
+ * computation over cells a caller already holds, such as rows a server read
14
+ * from its database. The bootstrap seed comes from `cellSetDigest`, the digest
15
+ * of exactly the cells used, so a producer and a verifier that hold the same
16
+ * cells report the same bits.
17
+ *
18
+ * `searchPosterior` gives every node a normal posterior on its improvement over
19
+ * the root, which expansion policies sample parents from. Its numbers steer
20
+ * spend and claim nothing.
21
+ */
22
+ const CONFIDENCE = .95;
23
+ const RESAMPLES = 2e3;
24
+ /** 6 at 95 %: the smallest sample an exact one-sided sign test can resolve. */
25
+ const DESCRIPTIVE_FROM$1 = minimumPairsForPairedDeltaTest(CONFIDENCE);
26
+ /** The estimator every `NodeEstimate` names. */
27
+ const SEARCH_ESTIMATOR = {
28
+ uri: "npm:@tangle-network/agent-eval#estimateNode",
29
+ revision: hashCanonical({
30
+ name: "tangle.estimate-node.2026-09",
31
+ unit: "mean of the unit’s scored cells, summed in cellId order",
32
+ pairing: "units both nodes scored, in unitId order",
33
+ statistic: DECISION_PAIRED_DELTA_STATISTIC,
34
+ interval: "percentile paired bootstrap",
35
+ confidence: CONFIDENCE,
36
+ resamples: RESAMPLES,
37
+ rng: "mulberry32",
38
+ seed: "the first 32 bits of cellSetDigest",
39
+ signTest: "exact, one-sided toward improvement in the objective direction",
40
+ methods: {
41
+ none: "below 2 pairs",
42
+ insufficient: `below ${DESCRIPTIVE_FROM$1} pairs`,
43
+ descriptive: `below 20 pairs`,
44
+ bootstrap: `from 20 pairs`
45
+ },
46
+ indeterminate: "every paired delta equal: no interval and no sign p"
47
+ })
48
+ };
49
+ /** The method a paired sample of `pairs` units supports. */
50
+ function searchEstimateMethod(pairs) {
51
+ if (pairs < 2) return "none";
52
+ if (pairs < DESCRIPTIVE_FROM$1) return "insufficient";
53
+ if (pairs < 20) return "descriptive";
54
+ return "bootstrap";
55
+ }
56
+ /**
57
+ * Digest of the cells an estimate reads, with the contrast they serve. Cells
58
+ * are listed in cellId order, so the digest names the set, not the order it
59
+ * was read in.
60
+ */
61
+ function searchCellSetDigest(input) {
62
+ const list = (cells) => [...cells].sort((left, right) => compareCodeUnits(left.cellId, right.cellId)).map((cell) => [
63
+ cell.cellId,
64
+ cell.unitId,
65
+ cell.attempt,
66
+ cell.score
67
+ ]);
68
+ return hashCanonical({
69
+ contrast: [input.nodeId, input.against],
70
+ split: input.split,
71
+ nodeCells: list(input.nodeCells),
72
+ againstCells: list(input.againstCells)
73
+ });
74
+ }
75
+ /**
76
+ * The paired contrast of `nodeId` against `against` on the units both scored.
77
+ *
78
+ * `delta` is the mean per-unit difference, node minus `against`, in the
79
+ * metric's own units; for a `minimize` objective an improvement is negative.
80
+ * The exact sign test is one-sided toward improvement in the objective's
81
+ * direction. Unscored cells (errored, cancelled or in flight) are absent from
82
+ * the inputs, never zero.
83
+ *
84
+ * - `none` (0 or 1 pair): no delta.
85
+ * - `insufficient` (2 to 5): the delta only.
86
+ * - `descriptive` (6 to 19): the bootstrap interval as spread, and the sign-test p.
87
+ * - `bootstrap` (20 or more): the decision-grade bootstrap interval.
88
+ *
89
+ * When every paired delta is equal the interval would have zero width, which
90
+ * is an absence of evidence; the estimate is then `indeterminate` and carries
91
+ * neither an interval nor a p-value.
92
+ */
93
+ function estimateNodeFromCells(input) {
94
+ const { nodeId, against, split, direction } = input;
95
+ if (nodeId === against) throw new SearchLedgerError(`node ${nodeId} cannot be estimated against itself`);
96
+ assertDistinctCells([...input.nodeCells, ...input.againstCells], nodeId, against);
97
+ const cellSetDigest = searchCellSetDigest(input);
98
+ const nodeUnits = searchUnitScores(input.nodeCells);
99
+ const { node, other } = pairUnits(nodeUnits, unitMeans(searchUnitScores(input.againstCells)));
100
+ const pairs = node.length;
101
+ const method = searchEstimateMethod(pairs);
102
+ const base = {
103
+ against,
104
+ split,
105
+ units: nodeUnits.length,
106
+ pairs,
107
+ method,
108
+ cellSetDigest,
109
+ estimator: SEARCH_ESTIMATOR
110
+ };
111
+ if (method === "none") return {
112
+ ...base,
113
+ delta: null,
114
+ interval: null,
115
+ exactSignP: null,
116
+ indeterminate: false
117
+ };
118
+ const maximize = direction === "maximize";
119
+ const test = pairedDeltaTest(maximize ? other : node, maximize ? node : other, {
120
+ statistic: DECISION_PAIRED_DELTA_STATISTIC,
121
+ confidence: CONFIDENCE,
122
+ resamples: RESAMPLES,
123
+ seed: seedFromDigest(cellSetDigest)
124
+ });
125
+ const { mean, low, high } = test.bootstrap;
126
+ const indeterminate = test.indeterminate;
127
+ const spread = (method === "descriptive" || method === "bootstrap") && !indeterminate;
128
+ return {
129
+ ...base,
130
+ delta: plain(maximize ? mean : -mean),
131
+ interval: spread ? maximize ? [plain(low), plain(high)] : [plain(-high), plain(-low)] : null,
132
+ exactSignP: method === "descriptive" && !indeterminate ? test.pValue : null,
133
+ indeterminate
134
+ };
135
+ }
136
+ /**
137
+ * `estimateNodeFromCells` over the cells a search state holds: `nodeId`
138
+ * against `against` on one split, in the direction the search's objective
139
+ * declares.
140
+ */
141
+ function estimateNode(state, nodeId, options) {
142
+ const header = state.header;
143
+ if (!header) throw new SearchLedgerError(`search ${state.searchId} has not been opened`);
144
+ for (const id of [nodeId, options.against]) if (!state.hasNode(id)) throw new SearchLedgerError(`node ${id} is not in search ${state.searchId}`);
145
+ return estimateNodeFromCells({
146
+ nodeId,
147
+ against: options.against,
148
+ split: options.split,
149
+ direction: header.objective.direction,
150
+ nodeCells: state.scoredCells(nodeId, options.split),
151
+ againstCells: state.scoredCells(options.against, options.split)
152
+ });
153
+ }
154
+ /**
155
+ * The posterior every node's parent-selection draw uses. Each node's mean is
156
+ * its improvement over the root on shared units; its variance is the search's
157
+ * pooled between-unit variance divided by its unit count, so a node measured
158
+ * on one unit has a wide posterior rather than none. The root is the reference
159
+ * and sits at exactly 0.
160
+ */
161
+ function searchPosterior(state, options = {}) {
162
+ const split = options.split ?? "selection";
163
+ const header = state.header;
164
+ if (!header) throw new SearchLedgerError(`search ${state.searchId} has not been opened`);
165
+ const rootNodeId = state.rootNodeId;
166
+ if (rootNodeId === null) throw new SearchLedgerError(`search ${state.searchId} has no root node yet`);
167
+ const sign = header.objective.direction === "maximize" ? 1 : -1;
168
+ const root = searchUnitScores(state.scoredCells(rootNodeId, split));
169
+ const rootMeans = unitMeans(root);
170
+ const nodes = [];
171
+ let squares = 0;
172
+ let degreesOfFreedom = 0;
173
+ for (const nodeId of state.nodeIds()) {
174
+ if (nodeId === rootNodeId) {
175
+ const measured = root.length > 0;
176
+ nodes.push({
177
+ nodeId,
178
+ pairs: root.length,
179
+ mean: measured ? 0 : null,
180
+ variance: measured ? 0 : null
181
+ });
182
+ continue;
183
+ }
184
+ const { node, other } = pairUnits(searchUnitScores(state.scoredCells(nodeId, split)), rootMeans);
185
+ const gains = node.map((value, index) => sign * (value - other[index]));
186
+ const mean = gains.length > 0 ? sum(gains) / gains.length : null;
187
+ if (mean !== null && gains.length >= 2) {
188
+ squares += sum(gains.map((gain) => (gain - mean) ** 2));
189
+ degreesOfFreedom += gains.length - 1;
190
+ }
191
+ nodes.push({
192
+ nodeId,
193
+ pairs: gains.length,
194
+ mean: mean === null ? null : plain(mean),
195
+ variance: null
196
+ });
197
+ }
198
+ const pooledVariance = degreesOfFreedom > 0 ? squares / degreesOfFreedom : null;
199
+ if (pooledVariance !== null) {
200
+ for (const posterior of nodes) if (posterior.nodeId !== rootNodeId && posterior.pairs > 0) posterior.variance = pooledVariance / posterior.pairs;
201
+ }
202
+ return {
203
+ split,
204
+ rootNodeId,
205
+ pooledVariance,
206
+ degreesOfFreedom,
207
+ nodes
208
+ };
209
+ }
210
+ function unitMeans(units) {
211
+ return new Map(units.map((unit) => [unit.unitId, unit.mean]));
212
+ }
213
+ /** Unit means of two nodes on the units both scored, aligned in the node's
214
+ * unitId order. */
215
+ function pairUnits(node, otherMeans) {
216
+ const paired = {
217
+ node: [],
218
+ other: []
219
+ };
220
+ for (const unit of node) {
221
+ const mean = otherMeans.get(unit.unitId);
222
+ if (mean === void 0) continue;
223
+ paired.node.push(unit.mean);
224
+ paired.other.push(mean);
225
+ }
226
+ return paired;
227
+ }
228
+ function assertDistinctCells(cells, nodeId, against) {
229
+ const seen = /* @__PURE__ */ new Set();
230
+ for (const cell of cells) {
231
+ if (seen.has(cell.cellId)) throw new SearchLedgerError(`cell ${cell.cellId} appears twice in the estimate of ${nodeId} against ${against}`);
232
+ if (!Number.isFinite(cell.score)) throw new SearchLedgerError(`cell ${cell.cellId} has a non-finite score ${cell.score}`);
233
+ seen.add(cell.cellId);
234
+ }
235
+ }
236
+ /** The first 32 bits of a `sha256:` digest, as a bootstrap's seed. */
237
+ function seedFromDigest(digest) {
238
+ return Number.parseInt(digest.slice(7, 15), 16) | 0;
239
+ }
240
+ function sum(values) {
241
+ let total = 0;
242
+ for (const value of values) total += value;
243
+ return total;
244
+ }
245
+ /** Canonical JSON has one zero; never hand it a negative one. */
246
+ function plain(value) {
247
+ return value === 0 ? 0 : value;
248
+ }
249
+ //#endregion
250
+ //#region src/campaign/search-summary.ts
251
+ /**
252
+ * The agent's view of a search: the same projection a human reads.
253
+ *
254
+ * `renderSearchSummary` turns a `SearchStateView` into compact text — the
255
+ * leading nodes with their estimates, the most recently discarded ideas with
256
+ * why they were discarded, and a log of recent proposals — as AIDE's journal
257
+ * summary does for its own search. `agent-eval search show <ledger>` prints
258
+ * the same text for a terminal; `runOptimization` puts it on every
259
+ * `ProposeContext` a proposer receives.
260
+ *
261
+ * `searchProposerView` is the train-only read `ProposeContext.train` carries.
262
+ * It has no parameter that can select another split, so a proposer built on
263
+ * it cannot reach the selection or test split even by mistake: the sealed
264
+ * splits (§6.6 of the search-tree design) are unreachable by construction,
265
+ * not by convention.
266
+ */
267
+ const DESCRIPTIVE_FROM = minimumPairsForPairedDeltaTest(.95);
268
+ /** The train-only view `ProposeContext.train` carries. */
269
+ function searchProposerView(state) {
270
+ const header = state.header;
271
+ if (!header) throw new SearchLedgerError(`search ${state.searchId} has not been opened`);
272
+ return {
273
+ searchId: state.searchId,
274
+ direction: header.objective.direction,
275
+ scoredCells: (nodeId) => state.scoredCells(nodeId, "train"),
276
+ unitScores: (nodeId) => state.unitScores(nodeId, "train")
277
+ };
278
+ }
279
+ /**
280
+ * `SearchState` as compact text: a header line with the search's shape and
281
+ * spend, the leading nodes against the root with their estimates, the most
282
+ * recently discarded nodes with the measurement that discarded them, and a
283
+ * log of recent proposals (operator and label — the ledger's inline,
284
+ * redacted summary of each rationale; the full rationale is a blob this
285
+ * function never reads, so it stays a pure, I/O-free projection of the
286
+ * state already in hand).
287
+ */
288
+ function renderSearchSummary(state, options) {
289
+ const header = state.header;
290
+ if (!header) return `search ${state.searchId}: not yet opened`;
291
+ const limit = options.limit ?? 5;
292
+ if (limit < 1 || !Number.isInteger(limit)) throw new SearchLedgerError(`renderSearchSummary: limit must be a positive integer, got ${limit}`);
293
+ const split = options.split;
294
+ const direction = header.objective.direction;
295
+ const root = state.rootNodeId;
296
+ const lines = [
297
+ headerLine(state),
298
+ spendLine(state),
299
+ statusLine(state)
300
+ ];
301
+ if (root === null) {
302
+ lines.push("", "No nodes yet.");
303
+ return lines.join("\n");
304
+ }
305
+ const others = state.nodes().filter((node) => node.nodeId !== root);
306
+ const leading = others.filter((node) => node.status !== "invalid").map((node) => ({
307
+ node,
308
+ estimate: estimateNode(state, node.nodeId, {
309
+ against: root,
310
+ split
311
+ })
312
+ })).filter(({ estimate }) => estimate.delta !== null).sort((a, b) => signed(b.estimate, direction) - signed(a.estimate, direction)).slice(0, limit);
313
+ lines.push("", `Leading nodes (vs root, ${split} split):`);
314
+ if (leading.length === 0) lines.push(" none measured yet");
315
+ for (const { node, estimate } of leading) lines.push(` ${nodeLine(node)}: ${formatEstimate(estimate)}`);
316
+ const discarded = others.filter((node) => node.status === "pruned" || node.status === "rejected" || node.status === "invalid").sort((a, b) => b.updatedSequence - a.updatedSequence).slice(0, limit);
317
+ lines.push("", "Recently discarded:");
318
+ if (discarded.length === 0) lines.push(" none yet");
319
+ for (const node of discarded) lines.push(` ${nodeLine(node)}: ${discardLine(node)}`);
320
+ const recent = [...state.edges()].reverse().slice(0, limit);
321
+ lines.push("", "Recent proposals:");
322
+ if (recent.length === 0) lines.push(" none yet");
323
+ for (const edge of recent) {
324
+ const childId = edge.childNodeId;
325
+ const label = edge.label.length > 0 ? edge.label : "(no label)";
326
+ lines.push(` ${edge.operator} → ${childId}: ${label}`);
327
+ }
328
+ return lines.join("\n");
329
+ }
330
+ function headerLine(state) {
331
+ const header = state.header;
332
+ return `search ${state.searchId} — ${header.subject} — ${header.objective.direction} ${header.objective.metric}`;
333
+ }
334
+ function spendLine(state) {
335
+ const { audit } = state;
336
+ const known = audit.spend.knownUsd.toFixed(2);
337
+ const floor = audit.spend.floorUsd.toFixed(2);
338
+ const unknown = audit.spend.unknownCostCells;
339
+ const floorPart = unknown > 0 ? ` + floor $${floor} across ${unknown} unknown-cost cell${unknown === 1 ? "" : "s"}` : "";
340
+ return ` ${audit.nodes} nodes · ${audit.cells.settled} settled cells (${audit.cells.open} open) · $${known} known${floorPart}`;
341
+ }
342
+ function statusLine(state) {
343
+ if (!state.closed) return " status: open";
344
+ const claim = state.closed.claim;
345
+ const claimText = claim === null ? "" : claim.decision === "ship" ? ` · claim: ship ${claim.selected}` : ` · claim: ${claim.decision}`;
346
+ return ` status: closed (${state.closed.reason})${claimText}`;
347
+ }
348
+ function nodeLine(node) {
349
+ const rung = node.status === "advanced" ? ` rung ${node.rung}` : "";
350
+ return `${node.nodeId} (${node.status ?? "undecided"}${rung})`;
351
+ }
352
+ function discardLine(node) {
353
+ const decision = node.decisions.at(-1);
354
+ if (!decision) return "no decision recorded";
355
+ return `${decision.basis === null ? "no measured basis" : formatEstimate(decision.basis)} — ${decision.rule}: ${decision.reason}`;
356
+ }
357
+ /** `estimate.delta` in the objective's own units; positive is always better. */
358
+ function signed(estimate, direction) {
359
+ if (estimate.delta === null) return Number.NEGATIVE_INFINITY;
360
+ return direction === "maximize" ? estimate.delta : -estimate.delta;
361
+ }
362
+ /** The display rules of search-tree-design §6.4: no whisker below 6 shared
363
+ * units, a dashed descriptive interval with its exact sign p from 6 to 19,
364
+ * a decision-grade whisker from `BOOTSTRAP_GATE_MIN_N`. */
365
+ function formatEstimate(estimate) {
366
+ const unit = (n) => `${n} unit${n === 1 ? "" : "s"}`;
367
+ if (estimate.method === "none") return `unknown (${unit(estimate.pairs)})`;
368
+ if (estimate.indeterminate) return `no measurable difference (indeterminate, ${unit(estimate.pairs)})`;
369
+ const delta = estimate.delta === null ? "null" : (estimate.delta >= 0 ? "+" : "") + estimate.delta.toFixed(4);
370
+ if (estimate.method === "insufficient") return `Δ=${delta} (${estimate.pairs} of ${DESCRIPTIVE_FROM} units, insufficient)`;
371
+ const interval = estimate.interval ? ` [${estimate.interval[0].toFixed(4)}, ${estimate.interval[1].toFixed(4)}]` : "";
372
+ if (estimate.method === "descriptive") return `Δ=${delta}${interval} dashed, sign p=${estimate.exactSignP === null ? "null" : estimate.exactSignP.toPrecision(3)} (${unit(estimate.pairs)})`;
373
+ return `Δ=${delta}${interval} (${unit(estimate.pairs)}, ≥20)`;
374
+ }
375
+ //#endregion
376
+ export { estimateNodeFromCells as a, seedFromDigest as c, estimateNode as i, searchProposerView as n, searchCellSetDigest as o, SEARCH_ESTIMATOR as r, searchPosterior as s, renderSearchSummary as t };
377
+
378
+ //# sourceMappingURL=search-summary-BhY8p0Av.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-summary-BhY8p0Av.js","names":["DESCRIPTIVE_FROM"],"sources":["../src/campaign/estimate-node.ts","../src/campaign/search-summary.ts"],"sourcesContent":["/**\n * Per-node statistics of a search.\n *\n * `estimateNode` is the one paired contrast of two nodes: it averages each\n * node's cells inside their units, pairs the units both nodes scored, and runs\n * `pairedDeltaTest` on the per-unit deltas. `estimateNodeFromCells` is the same\n * computation over cells a caller already holds, such as rows a server read\n * from its database. The bootstrap seed comes from `cellSetDigest`, the digest\n * of exactly the cells used, so a producer and a verifier that hold the same\n * cells report the same bits.\n *\n * `searchPosterior` gives every node a normal posterior on its improvement over\n * the root, which expansion policies sample parents from. Its numbers steer\n * spend and claim nothing.\n */\n\nimport { compareCodeUnits, hashCanonical } from '../ledger-core/canonical'\nimport { minimumPairsForPairedDeltaTest, pairedDeltaTest } from '../paired-delta-test'\nimport { BOOTSTRAP_GATE_MIN_N, DECISION_PAIRED_DELTA_STATISTIC } from '../statistics'\nimport { SearchLedgerError } from './search-ledger-errors'\nimport type {\n NodeEstimate,\n SearchEstimateMethod,\n SearchLedgerHash,\n SearchSourceRef,\n SearchSplit,\n} from './search-ledger-types'\nimport {\n type SearchScoredCell,\n type SearchStateView,\n type SearchUnitScore,\n searchUnitScores,\n} from './search-state'\n\nconst CONFIDENCE = 0.95\nconst RESAMPLES = 2000\n/** 6 at 95 %: the smallest sample an exact one-sided sign test can resolve. */\nconst DESCRIPTIVE_FROM = minimumPairsForPairedDeltaTest(CONFIDENCE)\n\n/**\n * Every choice the numbers depend on. Its digest is the estimator's revision:\n * a producer and a verifier that report the same revision and the same\n * `cellSetDigest` report the same estimate. Change a value here whenever the\n * computation changes, so the revision moves with it.\n */\nconst ESTIMATOR_DEFINITION = {\n name: 'tangle.estimate-node.2026-09',\n unit: 'mean of the unit’s scored cells, summed in cellId order',\n pairing: 'units both nodes scored, in unitId order',\n statistic: DECISION_PAIRED_DELTA_STATISTIC,\n interval: 'percentile paired bootstrap',\n confidence: CONFIDENCE,\n resamples: RESAMPLES,\n rng: 'mulberry32',\n seed: 'the first 32 bits of cellSetDigest',\n signTest: 'exact, one-sided toward improvement in the objective direction',\n methods: {\n none: 'below 2 pairs',\n insufficient: `below ${DESCRIPTIVE_FROM} pairs`,\n descriptive: `below ${BOOTSTRAP_GATE_MIN_N} pairs`,\n bootstrap: `from ${BOOTSTRAP_GATE_MIN_N} pairs`,\n },\n indeterminate: 'every paired delta equal: no interval and no sign p',\n} as const\n\n/** The estimator every `NodeEstimate` names. */\nexport const SEARCH_ESTIMATOR: SearchSourceRef = {\n uri: 'npm:@tangle-network/agent-eval#estimateNode',\n revision: hashCanonical(ESTIMATOR_DEFINITION),\n}\n\n/** The method a paired sample of `pairs` units supports. */\nfunction searchEstimateMethod(pairs: number): SearchEstimateMethod {\n if (pairs < 2) return 'none'\n if (pairs < DESCRIPTIVE_FROM) return 'insufficient'\n if (pairs < BOOTSTRAP_GATE_MIN_N) return 'descriptive'\n return 'bootstrap'\n}\n\nexport interface EstimateNodeCellsInput {\n nodeId: string\n against: string\n split: SearchSplit\n direction: 'maximize' | 'minimize'\n /** Every scored cell of `nodeId` on `split`. */\n nodeCells: readonly SearchScoredCell[]\n /** Every scored cell of `against` on `split`. */\n againstCells: readonly SearchScoredCell[]\n}\n\n/**\n * Digest of the cells an estimate reads, with the contrast they serve. Cells\n * are listed in cellId order, so the digest names the set, not the order it\n * was read in.\n */\nexport function searchCellSetDigest(\n input: Omit<EstimateNodeCellsInput, 'direction'>,\n): SearchLedgerHash {\n const list = (cells: readonly SearchScoredCell[]) =>\n [...cells]\n .sort((left, right) => compareCodeUnits(left.cellId, right.cellId))\n .map((cell) => [cell.cellId, cell.unitId, cell.attempt, cell.score])\n return hashCanonical({\n contrast: [input.nodeId, input.against],\n split: input.split,\n nodeCells: list(input.nodeCells),\n againstCells: list(input.againstCells),\n })\n}\n\n/**\n * The paired contrast of `nodeId` against `against` on the units both scored.\n *\n * `delta` is the mean per-unit difference, node minus `against`, in the\n * metric's own units; for a `minimize` objective an improvement is negative.\n * The exact sign test is one-sided toward improvement in the objective's\n * direction. Unscored cells (errored, cancelled or in flight) are absent from\n * the inputs, never zero.\n *\n * - `none` (0 or 1 pair): no delta.\n * - `insufficient` (2 to 5): the delta only.\n * - `descriptive` (6 to 19): the bootstrap interval as spread, and the sign-test p.\n * - `bootstrap` (20 or more): the decision-grade bootstrap interval.\n *\n * When every paired delta is equal the interval would have zero width, which\n * is an absence of evidence; the estimate is then `indeterminate` and carries\n * neither an interval nor a p-value.\n */\nexport function estimateNodeFromCells(input: EstimateNodeCellsInput): NodeEstimate {\n const { nodeId, against, split, direction } = input\n if (nodeId === against) {\n throw new SearchLedgerError(`node ${nodeId} cannot be estimated against itself`)\n }\n assertDistinctCells([...input.nodeCells, ...input.againstCells], nodeId, against)\n const cellSetDigest = searchCellSetDigest(input)\n const nodeUnits = searchUnitScores(input.nodeCells)\n const { node, other } = pairUnits(nodeUnits, unitMeans(searchUnitScores(input.againstCells)))\n const pairs = node.length\n const method = searchEstimateMethod(pairs)\n const base = {\n against,\n split,\n units: nodeUnits.length,\n pairs,\n method,\n cellSetDigest,\n estimator: SEARCH_ESTIMATOR,\n }\n if (method === 'none') {\n return { ...base, delta: null, interval: null, exactSignP: null, indeterminate: false }\n }\n // The test's `after - before` is the improvement: node minus against when\n // larger is better, against minus node when smaller is.\n const maximize = direction === 'maximize'\n const test = pairedDeltaTest(maximize ? other : node, maximize ? node : other, {\n statistic: DECISION_PAIRED_DELTA_STATISTIC,\n confidence: CONFIDENCE,\n resamples: RESAMPLES,\n seed: seedFromDigest(cellSetDigest),\n })\n const { mean, low, high } = test.bootstrap\n const indeterminate = test.indeterminate\n const spread = (method === 'descriptive' || method === 'bootstrap') && !indeterminate\n return {\n ...base,\n delta: plain(maximize ? mean : -mean),\n interval: spread ? (maximize ? [plain(low), plain(high)] : [plain(-high), plain(-low)]) : null,\n exactSignP: method === 'descriptive' && !indeterminate ? test.pValue : null,\n indeterminate,\n }\n}\n\n/**\n * `estimateNodeFromCells` over the cells a search state holds: `nodeId`\n * against `against` on one split, in the direction the search's objective\n * declares.\n */\nexport function estimateNode(\n state: SearchStateView,\n nodeId: string,\n options: { against: string; split: SearchSplit },\n): NodeEstimate {\n const header = state.header\n if (!header) throw new SearchLedgerError(`search ${state.searchId} has not been opened`)\n for (const id of [nodeId, options.against]) {\n if (!state.hasNode(id)) {\n throw new SearchLedgerError(`node ${id} is not in search ${state.searchId}`)\n }\n }\n return estimateNodeFromCells({\n nodeId,\n against: options.against,\n split: options.split,\n direction: header.objective.direction,\n nodeCells: state.scoredCells(nodeId, options.split),\n againstCells: state.scoredCells(options.against, options.split),\n })\n}\n\n/** A node's normal posterior on its improvement over the root. */\nexport interface NodePosterior {\n nodeId: string\n /** Units the node and the root both scored. */\n pairs: number\n /** Mean per-unit improvement over the root in the objective's direction,\n * so larger is better for either direction. Null without a shared unit. */\n mean: number | null\n /** The pooled variance divided by `pairs`. Null when either is unknown. */\n variance: number | null\n}\n\nexport interface SearchPosterior {\n split: SearchSplit\n rootNodeId: string\n /**\n * Between-unit variance of the per-unit improvements over the root, pooled\n * across every non-root node with at least 2 shared units. Null until some\n * node has 2.\n */\n pooledVariance: number | null\n /** Sum over the pooled nodes of their shared units minus one. */\n degreesOfFreedom: number\n /** Every node in registration order; the root first, at exactly 0. */\n nodes: NodePosterior[]\n}\n\n/**\n * The posterior every node's parent-selection draw uses. Each node's mean is\n * its improvement over the root on shared units; its variance is the search's\n * pooled between-unit variance divided by its unit count, so a node measured\n * on one unit has a wide posterior rather than none. The root is the reference\n * and sits at exactly 0.\n */\nexport function searchPosterior(\n state: SearchStateView,\n options: { split?: SearchSplit } = {},\n): SearchPosterior {\n const split = options.split ?? 'selection'\n const header = state.header\n if (!header) throw new SearchLedgerError(`search ${state.searchId} has not been opened`)\n const rootNodeId = state.rootNodeId\n if (rootNodeId === null) {\n throw new SearchLedgerError(`search ${state.searchId} has no root node yet`)\n }\n const sign = header.objective.direction === 'maximize' ? 1 : -1\n const root = searchUnitScores(state.scoredCells(rootNodeId, split))\n const rootMeans = unitMeans(root)\n const nodes: NodePosterior[] = []\n let squares = 0\n let degreesOfFreedom = 0\n for (const nodeId of state.nodeIds()) {\n if (nodeId === rootNodeId) {\n const measured = root.length > 0\n nodes.push({\n nodeId,\n pairs: root.length,\n mean: measured ? 0 : null,\n variance: measured ? 0 : null,\n })\n continue\n }\n const { node, other } = pairUnits(searchUnitScores(state.scoredCells(nodeId, split)), rootMeans)\n const gains = node.map((value, index) => sign * (value - other[index]!))\n const mean = gains.length > 0 ? sum(gains) / gains.length : null\n if (mean !== null && gains.length >= 2) {\n squares += sum(gains.map((gain) => (gain - mean) ** 2))\n degreesOfFreedom += gains.length - 1\n }\n nodes.push({\n nodeId,\n pairs: gains.length,\n mean: mean === null ? null : plain(mean),\n variance: null,\n })\n }\n const pooledVariance = degreesOfFreedom > 0 ? squares / degreesOfFreedom : null\n if (pooledVariance !== null) {\n for (const posterior of nodes) {\n if (posterior.nodeId !== rootNodeId && posterior.pairs > 0) {\n posterior.variance = pooledVariance / posterior.pairs\n }\n }\n }\n return { split, rootNodeId, pooledVariance, degreesOfFreedom, nodes }\n}\n\nfunction unitMeans(units: readonly SearchUnitScore[]): ReadonlyMap<string, number> {\n return new Map(units.map((unit) => [unit.unitId, unit.mean]))\n}\n\n/** Unit means of two nodes on the units both scored, aligned in the node's\n * unitId order. */\nfunction pairUnits(\n node: readonly SearchUnitScore[],\n otherMeans: ReadonlyMap<string, number>,\n): { node: number[]; other: number[] } {\n const paired = { node: [] as number[], other: [] as number[] }\n for (const unit of node) {\n const mean = otherMeans.get(unit.unitId)\n if (mean === undefined) continue\n paired.node.push(unit.mean)\n paired.other.push(mean)\n }\n return paired\n}\n\nfunction assertDistinctCells(\n cells: readonly SearchScoredCell[],\n nodeId: string,\n against: string,\n): void {\n const seen = new Set<string>()\n for (const cell of cells) {\n if (seen.has(cell.cellId)) {\n throw new SearchLedgerError(\n `cell ${cell.cellId} appears twice in the estimate of ${nodeId} against ${against}`,\n )\n }\n if (!Number.isFinite(cell.score)) {\n throw new SearchLedgerError(`cell ${cell.cellId} has a non-finite score ${cell.score}`)\n }\n seen.add(cell.cellId)\n }\n}\n\n/** The first 32 bits of a `sha256:` digest, as a bootstrap's seed. */\nexport function seedFromDigest(digest: SearchLedgerHash): number {\n return Number.parseInt(digest.slice('sha256:'.length, 'sha256:'.length + 8), 16) | 0\n}\n\nfunction sum(values: readonly number[]): number {\n let total = 0\n for (const value of values) total += value\n return total\n}\n\n/** Canonical JSON has one zero; never hand it a negative one. */\nfunction plain(value: number): number {\n return value === 0 ? 0 : value\n}\n","/**\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":";;;;;;;;;;;;;;;;;;;;;AAkCA,MAAM,aAAa;AACnB,MAAM,YAAY;;AAElB,MAAMA,qBAAmB,+BAA+B,UAAU;;AA6BlE,MAAa,mBAAoC;CAC/C,KAAK;CACL,UAAU,cAAc;EAtBxB,MAAM;EACN,MAAM;EACN,SAAS;EACT,WAAW;EACX,UAAU;EACV,YAAY;EACZ,WAAW;EACX,KAAK;EACL,MAAM;EACN,UAAU;EACV,SAAS;GACP,MAAM;GACN,cAAc,SAASA,mBAAiB;GACxC,aAAa;GACb,WAAW;EACb;EACA,eAAe;CAMS,CAAoB;AAC9C;;AAGA,SAAS,qBAAqB,OAAqC;CACjE,IAAI,QAAQ,GAAG,OAAO;CACtB,IAAI,QAAQA,oBAAkB,OAAO;CACrC,IAAI,QAAA,IAA8B,OAAO;CACzC,OAAO;AACT;;;;;;AAkBA,SAAgB,oBACd,OACkB;CAClB,MAAM,QAAQ,UACZ,CAAC,GAAG,KAAK,CAAC,CACP,MAAM,MAAM,UAAU,iBAAiB,KAAK,QAAQ,MAAM,MAAM,CAAC,CAAC,CAClE,KAAK,SAAS;EAAC,KAAK;EAAQ,KAAK;EAAQ,KAAK;EAAS,KAAK;CAAK,CAAC;CACvE,OAAO,cAAc;EACnB,UAAU,CAAC,MAAM,QAAQ,MAAM,OAAO;EACtC,OAAO,MAAM;EACb,WAAW,KAAK,MAAM,SAAS;EAC/B,cAAc,KAAK,MAAM,YAAY;CACvC,CAAC;AACH;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,sBAAsB,OAA6C;CACjF,MAAM,EAAE,QAAQ,SAAS,OAAO,cAAc;CAC9C,IAAI,WAAW,SACb,MAAM,IAAI,kBAAkB,QAAQ,OAAO,oCAAoC;CAEjF,oBAAoB,CAAC,GAAG,MAAM,WAAW,GAAG,MAAM,YAAY,GAAG,QAAQ,OAAO;CAChF,MAAM,gBAAgB,oBAAoB,KAAK;CAC/C,MAAM,YAAY,iBAAiB,MAAM,SAAS;CAClD,MAAM,EAAE,MAAM,UAAU,UAAU,WAAW,UAAU,iBAAiB,MAAM,YAAY,CAAC,CAAC;CAC5F,MAAM,QAAQ,KAAK;CACnB,MAAM,SAAS,qBAAqB,KAAK;CACzC,MAAM,OAAO;EACX;EACA;EACA,OAAO,UAAU;EACjB;EACA;EACA;EACA,WAAW;CACb;CACA,IAAI,WAAW,QACb,OAAO;EAAE,GAAG;EAAM,OAAO;EAAM,UAAU;EAAM,YAAY;EAAM,eAAe;CAAM;CAIxF,MAAM,WAAW,cAAc;CAC/B,MAAM,OAAO,gBAAgB,WAAW,QAAQ,MAAM,WAAW,OAAO,OAAO;EAC7E,WAAW;EACX,YAAY;EACZ,WAAW;EACX,MAAM,eAAe,aAAa;CACpC,CAAC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS,KAAK;CACjC,MAAM,gBAAgB,KAAK;CAC3B,MAAM,UAAU,WAAW,iBAAiB,WAAW,gBAAgB,CAAC;CACxE,OAAO;EACL,GAAG;EACH,OAAO,MAAM,WAAW,OAAO,CAAC,IAAI;EACpC,UAAU,SAAU,WAAW,CAAC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAK;EAC1F,YAAY,WAAW,iBAAiB,CAAC,gBAAgB,KAAK,SAAS;EACvE;CACF;AACF;;;;;;AAOA,SAAgB,aACd,OACA,QACA,SACc;CACd,MAAM,SAAS,MAAM;CACrB,IAAI,CAAC,QAAQ,MAAM,IAAI,kBAAkB,UAAU,MAAM,SAAS,qBAAqB;CACvF,KAAK,MAAM,MAAM,CAAC,QAAQ,QAAQ,OAAO,GACvC,IAAI,CAAC,MAAM,QAAQ,EAAE,GACnB,MAAM,IAAI,kBAAkB,QAAQ,GAAG,oBAAoB,MAAM,UAAU;CAG/E,OAAO,sBAAsB;EAC3B;EACA,SAAS,QAAQ;EACjB,OAAO,QAAQ;EACf,WAAW,OAAO,UAAU;EAC5B,WAAW,MAAM,YAAY,QAAQ,QAAQ,KAAK;EAClD,cAAc,MAAM,YAAY,QAAQ,SAAS,QAAQ,KAAK;CAChE,CAAC;AACH;;;;;;;;AAoCA,SAAgB,gBACd,OACA,UAAmC,CAAC,GACnB;CACjB,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,SAAS,MAAM;CACrB,IAAI,CAAC,QAAQ,MAAM,IAAI,kBAAkB,UAAU,MAAM,SAAS,qBAAqB;CACvF,MAAM,aAAa,MAAM;CACzB,IAAI,eAAe,MACjB,MAAM,IAAI,kBAAkB,UAAU,MAAM,SAAS,sBAAsB;CAE7E,MAAM,OAAO,OAAO,UAAU,cAAc,aAAa,IAAI;CAC7D,MAAM,OAAO,iBAAiB,MAAM,YAAY,YAAY,KAAK,CAAC;CAClE,MAAM,YAAY,UAAU,IAAI;CAChC,MAAM,QAAyB,CAAC;CAChC,IAAI,UAAU;CACd,IAAI,mBAAmB;CACvB,KAAK,MAAM,UAAU,MAAM,QAAQ,GAAG;EACpC,IAAI,WAAW,YAAY;GACzB,MAAM,WAAW,KAAK,SAAS;GAC/B,MAAM,KAAK;IACT;IACA,OAAO,KAAK;IACZ,MAAM,WAAW,IAAI;IACrB,UAAU,WAAW,IAAI;GAC3B,CAAC;GACD;EACF;EACA,MAAM,EAAE,MAAM,UAAU,UAAU,iBAAiB,MAAM,YAAY,QAAQ,KAAK,CAAC,GAAG,SAAS;EAC/F,MAAM,QAAQ,KAAK,KAAK,OAAO,UAAU,QAAQ,QAAQ,MAAM,OAAQ;EACvE,MAAM,OAAO,MAAM,SAAS,IAAI,IAAI,KAAK,IAAI,MAAM,SAAS;EAC5D,IAAI,SAAS,QAAQ,MAAM,UAAU,GAAG;GACtC,WAAW,IAAI,MAAM,KAAK,UAAU,OAAO,SAAS,CAAC,CAAC;GACtD,oBAAoB,MAAM,SAAS;EACrC;EACA,MAAM,KAAK;GACT;GACA,OAAO,MAAM;GACb,MAAM,SAAS,OAAO,OAAO,MAAM,IAAI;GACvC,UAAU;EACZ,CAAC;CACH;CACA,MAAM,iBAAiB,mBAAmB,IAAI,UAAU,mBAAmB;CAC3E,IAAI,mBAAmB,MAChB;OAAA,MAAM,aAAa,OACtB,IAAI,UAAU,WAAW,cAAc,UAAU,QAAQ,GACvD,UAAU,WAAW,iBAAiB,UAAU;CAAA;CAItD,OAAO;EAAE;EAAO;EAAY;EAAgB;EAAkB;CAAM;AACtE;AAEA,SAAS,UAAU,OAAgE;CACjF,OAAO,IAAI,IAAI,MAAM,KAAK,SAAS,CAAC,KAAK,QAAQ,KAAK,IAAI,CAAC,CAAC;AAC9D;;;AAIA,SAAS,UACP,MACA,YACqC;CACrC,MAAM,SAAS;EAAE,MAAM,CAAC;EAAe,OAAO,CAAC;CAAc;CAC7D,KAAK,MAAM,QAAQ,MAAM;EACvB,MAAM,OAAO,WAAW,IAAI,KAAK,MAAM;EACvC,IAAI,SAAS,KAAA,GAAW;EACxB,OAAO,KAAK,KAAK,KAAK,IAAI;EAC1B,OAAO,MAAM,KAAK,IAAI;CACxB;CACA,OAAO;AACT;AAEA,SAAS,oBACP,OACA,QACA,SACM;CACN,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,IAAI,KAAK,MAAM,GACtB,MAAM,IAAI,kBACR,QAAQ,KAAK,OAAO,oCAAoC,OAAO,WAAW,SAC5E;EAEF,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,GAC7B,MAAM,IAAI,kBAAkB,QAAQ,KAAK,OAAO,0BAA0B,KAAK,OAAO;EAExF,KAAK,IAAI,KAAK,MAAM;CACtB;AACF;;AAGA,SAAgB,eAAe,QAAkC;CAC/D,OAAO,OAAO,SAAS,OAAO,MAAM,GAAkB,EAAoB,GAAG,EAAE,IAAI;AACrF;AAEA,SAAS,IAAI,QAAmC;CAC9C,IAAI,QAAQ;CACZ,KAAK,MAAM,SAAS,QAAQ,SAAS;CACrC,OAAO;AACT;;AAGA,SAAS,MAAM,OAAuB;CACpC,OAAO,UAAU,IAAI,IAAI;AAC3B;;;;;;;;;;;;;;;;;;;AC3TA,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,9 +1,9 @@
1
1
  import { i as CostLedger } from "./cost-ledger-_IbkejCF.js";
2
- import { f as Mutex } from "./journal-c3qY0SDP.js";
2
+ import { p as Mutex } from "./journal-DvKCvjty.js";
3
3
  import { N as RawAnalystFindingSchema } from "./kind-factory-CdPPHtGZ.js";
4
4
  import { c as extractJsonPayload } from "./llm-client-KYQBh9C8.js";
5
5
  import { n as paidJsonChat, t as paidChat } from "./chat-json-call-CXHBvUz8.js";
6
- import { n as decodeRawFindingArray } from "./dspy-rlm-engine-MfpwoEaj.js";
6
+ import { n as decodeRawFindingArray } from "./dspy-rlm-engine-BX34vPVY.js";
7
7
  import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
8
8
  import { z } from "zod";
9
9
  import { dirname } from "node:path";
@@ -777,4 +777,4 @@ async function runSemanticConceptJudge(input, options) {
777
777
  //#endregion
778
778
  export { diffFindings as a, defaultIsMaterial as i, runSemanticConceptJudge as n, createChatTraceEngine as o, FindingsStore as r, SEMANTIC_CONCEPT_JUDGE_VERSION as t };
779
779
 
780
- //# sourceMappingURL=semantic-concept-judge-Bt7bXAAo.js.map
780
+ //# sourceMappingURL=semantic-concept-judge-Bg-560H0.js.map