@tangle-network/agent-eval 0.193.2 → 0.195.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.
- package/CHANGELOG.md +33 -0
- package/dist/adapters/http.d.ts +2 -2
- package/dist/analyst/index.d.ts +5 -5
- package/dist/analyst/index.js +1 -1
- package/dist/{benchmark-command-N5r3GXMP.js → benchmark-command-D2kaOHat.js} +2 -2
- package/dist/{benchmark-command-N5r3GXMP.js.map → benchmark-command-D2kaOHat.js.map} +1 -1
- package/dist/benchmarks/index.d.ts +3 -3
- package/dist/benchmarks/index.js +2 -2
- package/dist/campaign/index.d.ts +4 -4
- package/dist/campaign/index.js +6 -6
- package/dist/{campaign-DnL0S4XL.js → campaign-DkMRR1Co.js} +6 -6
- package/dist/{campaign-DnL0S4XL.js.map → campaign-DkMRR1Co.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/contract/index.d.ts +6 -6
- package/dist/contract/index.js +3 -3
- package/dist/{default-registry-C3HTE08O.d.ts → default-registry-D44M_E4Q.d.ts} +3 -3
- package/dist/{default-registry-C3HTE08O.d.ts.map → default-registry-D44M_E4Q.d.ts.map} +1 -1
- package/dist/{define-agent-eval-qxAPjMSL.js → define-agent-eval-Ddut4mkS.js} +3 -3
- package/dist/{define-agent-eval-qxAPjMSL.js.map → define-agent-eval-Ddut4mkS.js.map} +1 -1
- package/dist/{define-agent-eval-DQJvVa1P.d.ts → define-agent-eval-Dl0tmtJU.d.ts} +4 -4
- package/dist/{define-agent-eval-DQJvVa1P.d.ts.map → define-agent-eval-Dl0tmtJU.d.ts.map} +1 -1
- package/dist/{engine-DjRV55F9.d.ts → engine-LkP-rfp1.d.ts} +2 -2
- package/dist/{engine-DjRV55F9.d.ts.map → engine-LkP-rfp1.d.ts.map} +1 -1
- package/dist/evaluation.d.ts +1 -1
- package/dist/experiment/index.d.ts +1 -1
- package/dist/hosted/index.js +1 -1
- package/dist/{index-DWQNDqPd.d.ts → index-CZYB36IQ.d.ts} +6 -6
- package/dist/{index-DWQNDqPd.d.ts.map → index-CZYB36IQ.d.ts.map} +1 -1
- package/dist/{index-DMPLG226.d.ts → index-CdHX0_c3.d.ts} +120 -15
- package/dist/index-CdHX0_c3.d.ts.map +1 -0
- package/dist/{index-0jEtpSM7.d.ts → index-cWnRlQLW.d.ts} +3 -3
- package/dist/{index-0jEtpSM7.d.ts.map → index-cWnRlQLW.d.ts.map} +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +3 -3
- package/dist/jev.d.ts +1 -1
- package/dist/{llm-judge-Cififl_V.js → llm-judge-CvKpaRKY.js} +459 -43
- package/dist/llm-judge-CvKpaRKY.js.map +1 -0
- package/dist/{matrix-CNu13gg8.d.ts → matrix-CUzY_7Ml.d.ts} +2 -2
- package/dist/{matrix-CNu13gg8.d.ts.map → matrix-CUzY_7Ml.d.ts.map} +1 -1
- package/dist/multishot/golden/index.d.ts +1 -1
- package/dist/multishot/index.d.ts +2 -2
- package/dist/openapi.json +1 -1
- package/dist/{produced-state-B2AhDRnq.js → produced-state-C8rk-GCK.js} +2 -2
- package/dist/{produced-state-B2AhDRnq.js.map → produced-state-C8rk-GCK.js.map} +1 -1
- package/dist/rl.d.ts +1 -1
- package/dist/{search-ledger-CJiAEU3j.js → search-ledger-BbKtG_WF.js} +12 -4
- package/dist/search-ledger-BbKtG_WF.js.map +1 -0
- package/dist/{search-shipper-DUlwlCsK.js → search-shipper-Cm4Wt9SC.js} +2 -2
- package/dist/{search-shipper-DUlwlCsK.js.map → search-shipper-Cm4Wt9SC.js.map} +1 -1
- package/dist/{search-summary-BhY8p0Av.js → search-summary-DnDGR702.js} +2 -2
- package/dist/{search-summary-BhY8p0Av.js.map → search-summary-DnDGR702.js.map} +1 -1
- package/dist/{skillopt-optimization-method-C6DbFKnw.js → skillopt-optimization-method-CIJqP8oS.js} +3 -3
- package/dist/{skillopt-optimization-method-C6DbFKnw.js.map → skillopt-optimization-method-CIJqP8oS.js.map} +1 -1
- package/dist/{statistical-heldout-D-xBpbrd.d.ts → statistical-heldout-CLfkCc_B.d.ts} +2 -2
- package/dist/{statistical-heldout-D-xBpbrd.d.ts.map → statistical-heldout-CLfkCc_B.d.ts.map} +1 -1
- package/dist/{store-tool-spans-BxrfuU2t.d.ts → store-tool-spans-E-mdWPAz.d.ts} +3 -3
- package/dist/{store-tool-spans-BxrfuU2t.d.ts.map → store-tool-spans-E-mdWPAz.d.ts.map} +1 -1
- package/dist/{tool-groups-BC9O7Epn.d.ts → tool-groups-CiFSpk6C.d.ts} +2 -2
- package/dist/tool-groups-CiFSpk6C.d.ts.map +1 -0
- package/dist/trace-repair/index.d.ts +1 -1
- package/dist/traces.d.ts +2 -2
- package/dist/{types-BWwRXofp.d.ts → types-DLtLe21j.d.ts} +9 -2
- package/dist/types-DLtLe21j.d.ts.map +1 -0
- package/dist/{types-qoxUfKQg.d.ts → types-M1Et9e6Z.d.ts} +2 -2
- package/dist/{types-qoxUfKQg.d.ts.map → types-M1Et9e6Z.d.ts.map} +1 -1
- package/docs/search-ledger.md +37 -9
- package/docs/trace-contracts.md +21 -2
- package/package.json +1 -1
- package/dist/index-DMPLG226.d.ts.map +0 -1
- package/dist/llm-judge-Cififl_V.js.map +0 -1
- package/dist/search-ledger-CJiAEU3j.js.map +0 -1
- package/dist/tool-groups-BC9O7Epn.d.ts.map +0 -1
- package/dist/types-BWwRXofp.d.ts.map +0 -1
|
@@ -3,6 +3,7 @@ import { a as hashCanonical, i as compareCodeUnits, r as canonicalString, t as L
|
|
|
3
3
|
import { t as mulberry32 } from "./random-Dn5fPWkt.js";
|
|
4
4
|
import { o as summarizeNumberSeries, s as weightedComposite, t as confidenceInterval } from "./descriptive-1V17A-qa.js";
|
|
5
5
|
import { v as wilson } from "./paired-arms-JSDcs2zx.js";
|
|
6
|
+
import { t as minimumPairsForPairedDeltaTest } from "./paired-delta-test-BF-FEP8V.js";
|
|
6
7
|
import { t as decidePairedPromotion } from "./paired-promotion-decision-DTKl6-G8.js";
|
|
7
8
|
import { a as heldoutSignificance, i as dimensionRegressions, o as pairHoldout } from "./statistical-heldout-DANVmyug.js";
|
|
8
9
|
import { i as modelHasSnapshot, l as searchCellRunId } from "./run-record-BRlM99rX.js";
|
|
@@ -17,8 +18,8 @@ import { r as summarizeEvaluationUnits, t as defineEvaluationClaim } from "./cla
|
|
|
17
18
|
import { f as redactText, n as REDACTION_VERSION, o as detectCredential, s as detectPersonalData } from "./redact-Dk1ZdJEm.js";
|
|
18
19
|
import { t as detectRewardHacking } from "./reward-hacking-D0XwhVWE.js";
|
|
19
20
|
import { n as assertProposalFindings, t as combineAbortSignals } from "./abort-signal-CtzAM_sJ.js";
|
|
20
|
-
import { a as validateSearchLedgerEvent, d as searchTaskSetDigest, i as replaySearchLedgerText, l as searchCellId, n as openSearchLedger, t as FileSearchLedger, u as searchNodeId } from "./search-ledger-
|
|
21
|
-
import { c as seedFromDigest, i as estimateNode, n as searchProposerView, s as searchPosterior, t as renderSearchSummary } from "./search-summary-
|
|
21
|
+
import { a as validateSearchLedgerEvent, d as searchTaskSetDigest, i as replaySearchLedgerText, l as searchCellId, n as openSearchLedger, t as FileSearchLedger, u as searchNodeId } from "./search-ledger-BbKtG_WF.js";
|
|
22
|
+
import { c as seedFromDigest, i as estimateNode, n as searchProposerView, s as searchPosterior, t as renderSearchSummary } from "./search-summary-DnDGR702.js";
|
|
22
23
|
import { d as stripFencedJson, o as costReceiptFromLlm, s as costReceiptFromLlmError, u as maximumChargeForLlmRequest } from "./llm-client-c_bKHTXz.js";
|
|
23
24
|
import { createHash } from "node:crypto";
|
|
24
25
|
import { writeFileSync } from "node:fs";
|
|
@@ -2537,14 +2538,25 @@ function paretoFrontierWithCrowding(candidates, objectives) {
|
|
|
2537
2538
|
//#endregion
|
|
2538
2539
|
//#region src/campaign/allocation.ts
|
|
2539
2540
|
/**
|
|
2541
|
+
* Allocation: where a search spends rollouts.
|
|
2542
|
+
*
|
|
2543
|
+
* An allocator says which cells a node needs given the evidence so far; the
|
|
2544
|
+
* kernel allocates the ones the ledger does not hold yet, so a resumed search
|
|
2545
|
+
* plans exactly what the interrupted one planned. It also decides which nodes
|
|
2546
|
+
* earn more measurement (`advanced`) and, at close, which were left waiting
|
|
2547
|
+
* (`pruned`). Those are rank decisions: they spend budget and claim nothing.
|
|
2548
|
+
* Expansion (which parent, which operator) belongs to the `SearchPolicy`; the
|
|
2549
|
+
* claim belongs to the claim step.
|
|
2550
|
+
*/
|
|
2551
|
+
/**
|
|
2540
2552
|
* Every node gets every train and selection task at every repeat: the root as
|
|
2541
2553
|
* `root` cells, every other node as `train` cells (the proposer's feedback) and
|
|
2542
2554
|
* `screen` cells (the policy's private evidence). This is the fixed plan the
|
|
2543
|
-
* generation loop ran and GEPA runs.
|
|
2555
|
+
* generation loop ran and GEPA runs. It has one rung, so it never advances or
|
|
2556
|
+
* prunes a node.
|
|
2544
2557
|
*/
|
|
2545
2558
|
function uniform(options = {}) {
|
|
2546
|
-
const reps = options.reps ?? 1;
|
|
2547
|
-
if (!Number.isSafeInteger(reps) || reps < 1) throw new TypeError(`uniform: reps must be a positive integer, got ${String(reps)}`);
|
|
2559
|
+
const reps = positiveInteger("uniform", "reps", options.reps ?? 1);
|
|
2548
2560
|
const cells = (state, root) => {
|
|
2549
2561
|
const header = state.header;
|
|
2550
2562
|
if (!header) return [];
|
|
@@ -2561,9 +2573,281 @@ function uniform(options = {}) {
|
|
|
2561
2573
|
name: reps === 1 ? "uniform" : `uniform(reps=${reps})`,
|
|
2562
2574
|
reps,
|
|
2563
2575
|
plan: (state, nodeId) => cells(state, nodeId === state.rootNodeId),
|
|
2564
|
-
screenSize: (state) => cells(state, false).length
|
|
2576
|
+
screenSize: (state) => cells(state, false).length,
|
|
2577
|
+
advance: () => [],
|
|
2578
|
+
prune: () => []
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
/**
|
|
2582
|
+
* Asynchronous successive halving (Li et al., 2020) over one seeded
|
|
2583
|
+
* permutation of the ranked split's units, fixed by the search's seed.
|
|
2584
|
+
*
|
|
2585
|
+
* Rung k is the first `units × 2^k` units of the permutation; the top rung is
|
|
2586
|
+
* every unit. The root runs every unit before anything else, so every rung
|
|
2587
|
+
* pairs against it. A new node screens on rung 0 plus `trainUnits` train
|
|
2588
|
+
* units. Every node at a rung runs the same units as its parent, the root and
|
|
2589
|
+
* its siblings, so every contrast pairs.
|
|
2590
|
+
*
|
|
2591
|
+
* A node that finished rung k advances once it ranks in the top
|
|
2592
|
+
* floor(n / eta) of the n nodes that finished rung k, the root included, by
|
|
2593
|
+
* mean over the rung's units; a node with an unscored unit ranks last and
|
|
2594
|
+
* never advances. There is no barrier: a node outside the top waits, because
|
|
2595
|
+
* the set grows as more nodes finish. At close a waiting node is pruned with
|
|
2596
|
+
* its rank. The ranked split is selection, or train when a search declares no
|
|
2597
|
+
* selection split, as the policy's.
|
|
2598
|
+
*/
|
|
2599
|
+
function asha(options = {}) {
|
|
2600
|
+
const firstRung = minimumPairsForPairedDeltaTest();
|
|
2601
|
+
const units = positiveInteger("asha", "units", options.units ?? firstRung);
|
|
2602
|
+
const eta = positiveInteger("asha", "eta", options.eta ?? 3);
|
|
2603
|
+
if (eta < 2) throw new TypeError(`asha: eta must be at least 2, got ${eta}`);
|
|
2604
|
+
const trainUnits = nonNegativeInteger("asha", "trainUnits", options.trainUnits ?? 2);
|
|
2605
|
+
const reps = positiveInteger("asha", "reps", options.reps ?? 1);
|
|
2606
|
+
const name = units === firstRung && eta === 3 && trainUnits === 2 && reps === 1 ? "asha" : `asha(units=${units},eta=${eta},train=${trainUnits},reps=${reps})`;
|
|
2607
|
+
const layouts = /* @__PURE__ */ new WeakMap();
|
|
2608
|
+
const layoutOf = (state) => {
|
|
2609
|
+
const header = state.header;
|
|
2610
|
+
if (!header) return null;
|
|
2611
|
+
let layout = layouts.get(header);
|
|
2612
|
+
if (!layout) {
|
|
2613
|
+
layout = ashaLayout(header, {
|
|
2614
|
+
units,
|
|
2615
|
+
trainUnits
|
|
2616
|
+
});
|
|
2617
|
+
layouts.set(header, layout);
|
|
2618
|
+
}
|
|
2619
|
+
return layout;
|
|
2620
|
+
};
|
|
2621
|
+
const cellsThrough = (layout, rung, root) => {
|
|
2622
|
+
const plan = [];
|
|
2623
|
+
const through = root ? layout.top : Math.min(rung, layout.top);
|
|
2624
|
+
layout.order.slice(0, layout.sizes[through]).forEach((unitId, position) => {
|
|
2625
|
+
const stage = root ? "root" : layout.split === "train" ? "train" : position < layout.sizes[0] ? "screen" : "rung";
|
|
2626
|
+
for (const task of layout.tasks.get(unitId)) for (let rep = 0; rep < reps; rep++) plan.push({
|
|
2627
|
+
taskId: task.taskId,
|
|
2628
|
+
split: layout.split,
|
|
2629
|
+
rep,
|
|
2630
|
+
stage
|
|
2631
|
+
});
|
|
2632
|
+
});
|
|
2633
|
+
for (const task of layout.feedback) for (let rep = 0; rep < reps; rep++) plan.push({
|
|
2634
|
+
taskId: task.taskId,
|
|
2635
|
+
split: "train",
|
|
2636
|
+
rep,
|
|
2637
|
+
stage: root ? "root" : "train"
|
|
2638
|
+
});
|
|
2639
|
+
return plan;
|
|
2640
|
+
};
|
|
2641
|
+
/**
|
|
2642
|
+
* Per node: the deepest rung it finished and its unit means on the ranked
|
|
2643
|
+
* split. A later decision (finalist, selected, pruned) does not unfinish a
|
|
2644
|
+
* rung, so every node but an invalid one keeps its place in the ranks; only
|
|
2645
|
+
* an undecided or advanced node is `open` to a new decision.
|
|
2646
|
+
*/
|
|
2647
|
+
const standings = (view, layout) => {
|
|
2648
|
+
const { state } = view;
|
|
2649
|
+
const rootId = state.rootNodeId;
|
|
2650
|
+
const standing = [];
|
|
2651
|
+
for (const node of state.nodes()) {
|
|
2652
|
+
if (node.status === "invalid") continue;
|
|
2653
|
+
const root = node.nodeId === rootId;
|
|
2654
|
+
let advancedTo = 0;
|
|
2655
|
+
for (const { decision } of node.decisions) if (decision.status === "advanced") advancedTo = Math.max(advancedTo, decision.rung);
|
|
2656
|
+
const rung = root ? layout.top : Math.min(advancedTo, layout.top);
|
|
2657
|
+
const allocated = /* @__PURE__ */ new Set();
|
|
2658
|
+
let cancelled = false;
|
|
2659
|
+
for (const cell of state.cells({ nodeId: node.nodeId })) {
|
|
2660
|
+
if (cell.split !== layout.split) continue;
|
|
2661
|
+
const position = layout.position.get(cell.unitId);
|
|
2662
|
+
if (position !== void 0 && position < layout.sizes[rung]) {
|
|
2663
|
+
allocated.add(`${cell.taskId}\u0000${cell.rep}`);
|
|
2664
|
+
if (cell.cancelled !== null) cancelled = true;
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
const covered = !cancelled && allocated.size === layout.cellsThrough[rung] * reps;
|
|
2668
|
+
const finished = view.idle(node.nodeId) && covered ? rung : root ? -1 : rung - 1;
|
|
2669
|
+
if (finished < 0) continue;
|
|
2670
|
+
const means = /* @__PURE__ */ new Map();
|
|
2671
|
+
for (const unit of state.unitScores(node.nodeId, layout.split)) means.set(unit.unitId, unit.mean);
|
|
2672
|
+
const open = node.status === null || node.status === "advanced";
|
|
2673
|
+
standing.push({
|
|
2674
|
+
nodeId: node.nodeId,
|
|
2675
|
+
ordinal: node.ordinal,
|
|
2676
|
+
root,
|
|
2677
|
+
open,
|
|
2678
|
+
rung,
|
|
2679
|
+
finished,
|
|
2680
|
+
means
|
|
2681
|
+
});
|
|
2682
|
+
}
|
|
2683
|
+
return standing;
|
|
2684
|
+
};
|
|
2685
|
+
/** The nodes that finished rung k, best first; an unscored unit ranks last. */
|
|
2686
|
+
const ranking = (state, layout, standing, k) => {
|
|
2687
|
+
const rungUnits = [...layout.order.slice(0, layout.sizes[k])].sort(compareCodeUnits);
|
|
2688
|
+
const sign = state.header.objective.direction === "maximize" ? 1 : -1;
|
|
2689
|
+
const ranked = [];
|
|
2690
|
+
for (const entry of standing) {
|
|
2691
|
+
if (entry.finished < k) continue;
|
|
2692
|
+
let sum = 0;
|
|
2693
|
+
let complete = true;
|
|
2694
|
+
for (const unitId of rungUnits) {
|
|
2695
|
+
const mean = entry.means.get(unitId);
|
|
2696
|
+
if (mean === void 0) {
|
|
2697
|
+
complete = false;
|
|
2698
|
+
break;
|
|
2699
|
+
}
|
|
2700
|
+
sum += mean;
|
|
2701
|
+
}
|
|
2702
|
+
ranked.push({
|
|
2703
|
+
...entry,
|
|
2704
|
+
mean: complete ? sum / rungUnits.length : null,
|
|
2705
|
+
sign
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2708
|
+
return ranked.sort(compareRanked);
|
|
2709
|
+
};
|
|
2710
|
+
const reason = (ranked, index, k, layout) => {
|
|
2711
|
+
const entry = ranked[index];
|
|
2712
|
+
const top = Math.floor(ranked.length / eta);
|
|
2713
|
+
const mean = entry.mean === null ? "an unscored unit" : `mean ${round$1(entry.mean)}`;
|
|
2714
|
+
return `rank ${index + 1} of ${ranked.length} on rung ${k} (${layout.sizes[k]} units, ${mean}); the top ${top} advance (eta ${eta})`;
|
|
2715
|
+
};
|
|
2716
|
+
return {
|
|
2717
|
+
name,
|
|
2718
|
+
reps,
|
|
2719
|
+
plan(state, nodeId, rung) {
|
|
2720
|
+
const layout = layoutOf(state);
|
|
2721
|
+
return layout ? cellsThrough(layout, rung, nodeId === state.rootNodeId) : [];
|
|
2722
|
+
},
|
|
2723
|
+
screenSize(state) {
|
|
2724
|
+
const layout = layoutOf(state);
|
|
2725
|
+
return layout ? cellsThrough(layout, 0, false).length : 0;
|
|
2726
|
+
},
|
|
2727
|
+
advance(view) {
|
|
2728
|
+
const { state } = view;
|
|
2729
|
+
const layout = layoutOf(state);
|
|
2730
|
+
const rootId = state.rootNodeId;
|
|
2731
|
+
if (!layout || layout.top === 0 || rootId === null) return [];
|
|
2732
|
+
const standing = standings(view, layout);
|
|
2733
|
+
const decisions = [];
|
|
2734
|
+
for (let k = layout.top - 1; k >= 0; k--) {
|
|
2735
|
+
const ranked = ranking(state, layout, standing, k);
|
|
2736
|
+
const top = Math.floor(ranked.length / eta);
|
|
2737
|
+
for (let index = 0; index < top; index++) {
|
|
2738
|
+
const entry = ranked[index];
|
|
2739
|
+
if (!entry.open || entry.root || entry.mean === null) continue;
|
|
2740
|
+
if (entry.finished !== k || entry.rung !== k) continue;
|
|
2741
|
+
decisions.push({
|
|
2742
|
+
nodeId: entry.nodeId,
|
|
2743
|
+
decision: {
|
|
2744
|
+
status: "advanced",
|
|
2745
|
+
rung: k + 1
|
|
2746
|
+
},
|
|
2747
|
+
basis: estimateNode(state, entry.nodeId, {
|
|
2748
|
+
against: rootId,
|
|
2749
|
+
split: layout.split
|
|
2750
|
+
}),
|
|
2751
|
+
rule: name,
|
|
2752
|
+
reason: reason(ranked, index, k, layout)
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
return decisions;
|
|
2757
|
+
},
|
|
2758
|
+
prune(view, keep) {
|
|
2759
|
+
const { state } = view;
|
|
2760
|
+
const layout = layoutOf(state);
|
|
2761
|
+
const rootId = state.rootNodeId;
|
|
2762
|
+
if (!layout || layout.top === 0 || rootId === null) return [];
|
|
2763
|
+
const standing = standings(view, layout);
|
|
2764
|
+
const decisions = [];
|
|
2765
|
+
for (let k = layout.top - 1; k >= 0; k--) {
|
|
2766
|
+
const ranked = ranking(state, layout, standing, k);
|
|
2767
|
+
const top = Math.floor(ranked.length / eta);
|
|
2768
|
+
ranked.forEach((entry, index) => {
|
|
2769
|
+
if (!entry.open || entry.root || entry.nodeId === keep) return;
|
|
2770
|
+
if (entry.finished !== k || entry.rung !== k || entry.means.size === 0) return;
|
|
2771
|
+
const why = index < top && entry.mean !== null ? `it earned rung ${k + 1}, but the cap, the cell limit or the deadline did not admit its cells` : "it waited outside the top when the search closed";
|
|
2772
|
+
decisions.push({
|
|
2773
|
+
nodeId: entry.nodeId,
|
|
2774
|
+
decision: { status: "pruned" },
|
|
2775
|
+
basis: estimateNode(state, entry.nodeId, {
|
|
2776
|
+
against: rootId,
|
|
2777
|
+
split: layout.split
|
|
2778
|
+
}),
|
|
2779
|
+
rule: name,
|
|
2780
|
+
reason: `${reason(ranked, index, k, layout)}; ${why}`
|
|
2781
|
+
});
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
return decisions;
|
|
2785
|
+
}
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2788
|
+
function compareRanked(left, right) {
|
|
2789
|
+
if (left.mean === null || right.mean === null) {
|
|
2790
|
+
if (left.mean !== right.mean) return left.mean === null ? 1 : -1;
|
|
2791
|
+
} else if (left.mean !== right.mean) return left.sign * (right.mean - left.mean);
|
|
2792
|
+
return left.ordinal - right.ordinal;
|
|
2793
|
+
}
|
|
2794
|
+
function ashaLayout(header, options) {
|
|
2795
|
+
const split = header.splits.selection.tasks.length > 0 ? "selection" : "train";
|
|
2796
|
+
const tasks = unitsOf(header.splits[split].tasks);
|
|
2797
|
+
const order = permute([...tasks.keys()], header.policy.seed, split);
|
|
2798
|
+
const sizes = [];
|
|
2799
|
+
for (let size = options.units;; size *= 2) {
|
|
2800
|
+
sizes.push(Math.min(size, order.length));
|
|
2801
|
+
if (size >= order.length) break;
|
|
2802
|
+
}
|
|
2803
|
+
const cellsThrough = sizes.map((size) => order.slice(0, size).reduce((sum, unitId) => sum + tasks.get(unitId).length, 0));
|
|
2804
|
+
let feedback = [];
|
|
2805
|
+
if (split === "selection") {
|
|
2806
|
+
const train = unitsOf(header.splits.train.tasks);
|
|
2807
|
+
feedback = permute([...train.keys()], header.policy.seed, "train").slice(0, options.trainUnits).flatMap((unitId) => train.get(unitId));
|
|
2808
|
+
}
|
|
2809
|
+
return {
|
|
2810
|
+
split,
|
|
2811
|
+
order,
|
|
2812
|
+
position: new Map(order.map((unitId, index) => [unitId, index])),
|
|
2813
|
+
tasks,
|
|
2814
|
+
sizes,
|
|
2815
|
+
cellsThrough,
|
|
2816
|
+
top: sizes.length - 1,
|
|
2817
|
+
feedback
|
|
2565
2818
|
};
|
|
2566
2819
|
}
|
|
2820
|
+
/** Tasks grouped by unit, units and tasks in code-unit order. */
|
|
2821
|
+
function unitsOf(tasks) {
|
|
2822
|
+
const byUnit = /* @__PURE__ */ new Map();
|
|
2823
|
+
for (const task of [...tasks].sort((left, right) => compareCodeUnits(left.taskId, right.taskId))) {
|
|
2824
|
+
const unit = byUnit.get(task.unitId);
|
|
2825
|
+
if (unit) unit.push(task);
|
|
2826
|
+
else byUnit.set(task.unitId, [task]);
|
|
2827
|
+
}
|
|
2828
|
+
return new Map([...byUnit.entries()].sort(([left], [right]) => compareCodeUnits(left, right)));
|
|
2829
|
+
}
|
|
2830
|
+
/** Fisher-Yates over units in code-unit order, from the search seed and the split. */
|
|
2831
|
+
function permute(unitIds, seed, split) {
|
|
2832
|
+
const rng = mulberry32(seed ^ (split === "train" ? 2050776593 : 731729763) | 0);
|
|
2833
|
+
const order = [...unitIds];
|
|
2834
|
+
for (let index = order.length - 1; index > 0; index--) {
|
|
2835
|
+
const other = Math.floor(rng() * (index + 1));
|
|
2836
|
+
[order[index], order[other]] = [order[other], order[index]];
|
|
2837
|
+
}
|
|
2838
|
+
return order;
|
|
2839
|
+
}
|
|
2840
|
+
function positiveInteger(owner, name, value) {
|
|
2841
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new TypeError(`${owner}: ${name} must be a positive integer, got ${String(value)}`);
|
|
2842
|
+
return value;
|
|
2843
|
+
}
|
|
2844
|
+
function nonNegativeInteger(owner, name, value) {
|
|
2845
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new TypeError(`${owner}: ${name} must be a non-negative integer, got ${String(value)}`);
|
|
2846
|
+
return value;
|
|
2847
|
+
}
|
|
2848
|
+
function round$1(value) {
|
|
2849
|
+
return Math.round(value * 1e4) / 1e4;
|
|
2850
|
+
}
|
|
2567
2851
|
//#endregion
|
|
2568
2852
|
//#region src/paired-promotion-power.ts
|
|
2569
2853
|
/**
|
|
@@ -3064,6 +3348,76 @@ function decideSearchClaim(state, plan) {
|
|
|
3064
3348
|
decisions
|
|
3065
3349
|
};
|
|
3066
3350
|
}
|
|
3351
|
+
/**
|
|
3352
|
+
* Make a closed search's claim again from its ledger alone and compare it with
|
|
3353
|
+
* the recorded one, byte for byte: the power check, the finalists, each
|
|
3354
|
+
* finalist's test estimate and deciding interval, the selection, the decision
|
|
3355
|
+
* and the reason. A producer's claim is not evidence until this holds; a store
|
|
3356
|
+
* keeps what this function derives, not what the producer wrote.
|
|
3357
|
+
*
|
|
3358
|
+
* It needs no blob, so it works on a store that keeps digests only. The
|
|
3359
|
+
* finalists are the nodes the ledger decided `finalist`, in decision order.
|
|
3360
|
+
* The one input the ledger cannot replay is whether the budget covered k
|
|
3361
|
+
* finalists' test cells; the claim cells that ran answer it (none ran: the
|
|
3362
|
+
* budget covered none). Test repeats come from the claim cells too; without
|
|
3363
|
+
* test cells, the recorded estimator stands for them (binary needs one repeat,
|
|
3364
|
+
* so any other count reproduces continuous).
|
|
3365
|
+
*
|
|
3366
|
+
* Returns null for a search that made no claim.
|
|
3367
|
+
*/
|
|
3368
|
+
function verifySearchClaim(state) {
|
|
3369
|
+
const closed = state.closed;
|
|
3370
|
+
if (!closed) throw new Error(`search ${state.searchId} is open; its claim is made when it closes`);
|
|
3371
|
+
const recorded = closed.claim;
|
|
3372
|
+
if (recorded === null) return null;
|
|
3373
|
+
if (recorded.rule.revision !== SEARCH_CLAIM_RULE.revision) return {
|
|
3374
|
+
status: "unknown",
|
|
3375
|
+
reason: `the claim names rule revision ${recorded.rule.revision}; this code implements ${SEARCH_CLAIM_RULE.revision}`
|
|
3376
|
+
};
|
|
3377
|
+
const claimCells = state.cells().filter((cell) => cell.stage === "claim");
|
|
3378
|
+
const decided = state.nodes().flatMap((node) => {
|
|
3379
|
+
const first = node.decisions.find((record) => record.decision.status === "finalist");
|
|
3380
|
+
return first ? [{
|
|
3381
|
+
nodeId: node.nodeId,
|
|
3382
|
+
sequence: first.sequence
|
|
3383
|
+
}] : [];
|
|
3384
|
+
}).sort((a, b) => a.sequence - b.sequence).map((entry) => entry.nodeId);
|
|
3385
|
+
const reps = claimCells.length > 0 ? 1 + Math.max(...claimCells.map((cell) => cell.rep)) : "adequate" in recorded.power && recorded.power.estimator.kind === "binary" ? 1 : 2;
|
|
3386
|
+
const plan = planSearchClaim(state, {
|
|
3387
|
+
stopReason: closed.reason,
|
|
3388
|
+
reps,
|
|
3389
|
+
affordable: (finalists) => claimCells.length > 0 && finalists <= decided.length
|
|
3390
|
+
});
|
|
3391
|
+
const differences = [];
|
|
3392
|
+
const planned = plan.finalists.map((finalist) => finalist.nodeId);
|
|
3393
|
+
if (canonicalString(planned) !== canonicalString(decided)) differences.push(`finalists: the rule picks [${planned.join(", ")}], the ledger decided [${decided.join(", ")}]`);
|
|
3394
|
+
if (plan.test === "run" !== claimCells.length > 0) differences.push(`test: the plan ${plan.test === "run" ? "runs" : "skips"} the test cells, and ${claimCells.length} ran`);
|
|
3395
|
+
const expected = decideSearchClaim(state, plan).claim;
|
|
3396
|
+
for (const key of [
|
|
3397
|
+
"decision",
|
|
3398
|
+
"selected",
|
|
3399
|
+
"confidence",
|
|
3400
|
+
"power",
|
|
3401
|
+
"reason"
|
|
3402
|
+
]) if (canonicalString(expected[key]) !== canonicalString(recorded[key])) differences.push(`${key}: the ledger supports ${canonicalString(expected[key])}, the claim records ${canonicalString(recorded[key])}`);
|
|
3403
|
+
const count = Math.max(expected.finalists.length, recorded.finalists.length);
|
|
3404
|
+
for (let index = 0; index < count; index++) {
|
|
3405
|
+
const want = expected.finalists[index];
|
|
3406
|
+
const have = recorded.finalists[index];
|
|
3407
|
+
if (canonicalString(want ?? null) === canonicalString(have ?? null)) continue;
|
|
3408
|
+
const fields = [
|
|
3409
|
+
"nodeId",
|
|
3410
|
+
"promote",
|
|
3411
|
+
"test",
|
|
3412
|
+
"estimate"
|
|
3413
|
+
].filter((field) => canonicalString(want?.[field] ?? null) !== canonicalString(have?.[field] ?? null));
|
|
3414
|
+
differences.push(`finalist ${index + 1} (${have?.nodeId ?? want?.nodeId}): ${fields.join(", ")} differ from what the ledger supports`);
|
|
3415
|
+
}
|
|
3416
|
+
return differences.length === 0 ? { status: "verified" } : {
|
|
3417
|
+
status: "mismatch",
|
|
3418
|
+
differences
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3067
3421
|
/** Per-finalist confidence for `k` finalists. */
|
|
3068
3422
|
function bonferroni(k) {
|
|
3069
3423
|
return 1 - (1 - FAMILY_CONFIDENCE) / k;
|
|
@@ -3321,17 +3675,19 @@ const SEARCH_KERNEL_SOURCE = {
|
|
|
3321
3675
|
revision: hashCanonical({
|
|
3322
3676
|
name: "tangle.search-kernel.2026-09",
|
|
3323
3677
|
dispatch: "claim, then rung and root, then screen, then train; first allocated first within a stage",
|
|
3324
|
-
expansion: "when no cell waits, fewer than twice the lanes capacity run,
|
|
3678
|
+
expansion: "when no cell waits, fewer than twice the lanes capacity run, the cap admits one proposal and its expected screens, and overspend has not taken the search past its cap",
|
|
3325
3679
|
reservation: {
|
|
3326
3680
|
hard: "the lane per-cell maximum",
|
|
3327
3681
|
estimate: "1.5 times the p99 of the lane settled cells once 20 settled, else the lane prior"
|
|
3328
3682
|
},
|
|
3329
3683
|
retries: "a retryable errored attempt runs again, up to maxAttempts",
|
|
3684
|
+
rungs: "when a node finishes its cells, the allocator names the nodes it advances, deepest rung first; each is recorded advanced and its rung allocated when the cap admits the rung",
|
|
3330
3685
|
resume: "an unrecorded operation is recorded failed at an unknown cost with floor 0; a recorded proposal is finished from its stored output; an unsettled cell is offered to adopt before it runs",
|
|
3331
3686
|
writers: "one kernel per ledger file on a host, by a pid lock beside the ledger",
|
|
3332
3687
|
divergence: "a node whose train mean over shared units rises above its parent while its descriptive or bootstrap selection interval against the parent lies wholly on the worse side is decided invalid when its cells finish",
|
|
3333
3688
|
claim: "with a test split: after expansion stops, the claim plan is stored on the claim operation, the finalists are decided, the root and finalist test cells run together, and the claim decides; the kernel refuses to start a claim without a selection split, a minimumEffect, a pinned judge, or (under a cap) a claim reserve for the root and 3 finalists plus a cap for one screening round",
|
|
3334
|
-
close: "
|
|
3689
|
+
close: "the allocator prunes the nodes left waiting on a rung, except the policy leader; then, with a test split, the claim decides; without one, the policy leader is selected when it has a scored cell; every other undecided node is rejected",
|
|
3690
|
+
complete: "a node leads only when it has a scored cell on the ranked split and no cell there ended unscored: a final unscored outcome, or an error with no attempt left"
|
|
3335
3691
|
})
|
|
3336
3692
|
};
|
|
3337
3693
|
const USD_TOLERANCE = 1e-9;
|
|
@@ -3527,6 +3883,7 @@ var SearchKernel = class {
|
|
|
3527
3883
|
for (const expansion of recorded) await this.finishProposal(expansion);
|
|
3528
3884
|
for (const node of this.state.nodes()) if (this.admitted.has(node.nodeId) && !isTerminal(node)) await this.allocateFor(node.nodeId);
|
|
3529
3885
|
for (const nodeId of this.state.nodeIds()) if (this.admitted.has(nodeId) && (this.pending.get(nodeId) ?? 0) === 0) await this.screenDone(nodeId);
|
|
3886
|
+
await this.advance();
|
|
3530
3887
|
}
|
|
3531
3888
|
/**
|
|
3532
3889
|
* A search that will claim must be able to: finalists come from a selection
|
|
@@ -3555,7 +3912,7 @@ var SearchKernel = class {
|
|
|
3555
3912
|
cellUsd
|
|
3556
3913
|
});
|
|
3557
3914
|
if (reservedClaimUsd + USD_TOLERANCE < need) refuse(`its claim reserve $${reservedClaimUsd} is below the $${need} the root and 3 finalists need on ${testTasks} test tasks at $${cellUsd} a cell`);
|
|
3558
|
-
const rootCells = this.options.allocation.plan(this.state, this.state.rootNodeId).length;
|
|
3915
|
+
const rootCells = this.options.allocation.plan(this.state, this.state.rootNodeId, 0).length;
|
|
3559
3916
|
const screens = (this.options.proposer.childrenPerProposal ?? 1) * this.options.allocation.screenSize(this.state);
|
|
3560
3917
|
const round = this.operationReservation().usd + (rootCells + screens) * cellUsd;
|
|
3561
3918
|
if (maxUsd - reservedClaimUsd + USD_TOLERANCE < round) refuse(`its cap $${maxUsd} less the claim reserve $${reservedClaimUsd} does not cover the root and one screening round ($${round})`);
|
|
@@ -3694,11 +4051,20 @@ var SearchKernel = class {
|
|
|
3694
4051
|
if (cell.stage === "claim") return;
|
|
3695
4052
|
const left = (this.pending.get(cell.nodeId) ?? 1) - 1;
|
|
3696
4053
|
this.pending.set(cell.nodeId, left);
|
|
3697
|
-
if (left === 0) await this.
|
|
4054
|
+
if (left === 0) await this.onNodeIdle(cell.nodeId);
|
|
3698
4055
|
}
|
|
3699
|
-
|
|
4056
|
+
/** Every cell allocated to the node is done: its screen, or a rung. */
|
|
4057
|
+
async onNodeIdle(nodeId) {
|
|
3700
4058
|
if (!await this.screenDone(nodeId)) return;
|
|
3701
|
-
|
|
4059
|
+
await this.advance();
|
|
4060
|
+
}
|
|
4061
|
+
/** Record the rank decisions the allocator now supports and allocate the
|
|
4062
|
+
* rungs they open. Past the deadline, once the claim began, or once the
|
|
4063
|
+
* search fails or is aborted, no rung opens; a search that stopped
|
|
4064
|
+
* expanding still measures the nodes that earned it. */
|
|
4065
|
+
async advance() {
|
|
4066
|
+
if (this.claimPlan || this.failure || this.options.signal?.aborted || this.pastDeadline()) return;
|
|
4067
|
+
for (const decision of this.options.allocation.advance(this.allocationView())) await this.allocateFor(decision.nodeId, decision);
|
|
3702
4068
|
}
|
|
3703
4069
|
/**
|
|
3704
4070
|
* A node's allocated cells all finished. A node that diverges (its train
|
|
@@ -3707,7 +4073,7 @@ var SearchKernel = class {
|
|
|
3707
4073
|
* becomes a parent; any other node is screened. Returns whether it was.
|
|
3708
4074
|
*/
|
|
3709
4075
|
async screenDone(nodeId) {
|
|
3710
|
-
if (!this.claimPlan && !this.screenedSet.has(nodeId)) {
|
|
4076
|
+
if (!this.claimPlan && !this.screenedSet.has(nodeId) && this.state.node(nodeId).status !== "advanced") {
|
|
3711
4077
|
const divergence = searchDivergence(this.state, nodeId);
|
|
3712
4078
|
if (divergence) {
|
|
3713
4079
|
await this.recorder.decideNode({
|
|
@@ -3737,10 +4103,19 @@ var SearchKernel = class {
|
|
|
3737
4103
|
...this.screened.slice(index)
|
|
3738
4104
|
];
|
|
3739
4105
|
}
|
|
3740
|
-
/**
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
4106
|
+
/**
|
|
4107
|
+
* Allocate the cells the allocator plans for a node through its rung and the
|
|
4108
|
+
* ledger lacks. With a rank decision, the rung is the decision's, and the
|
|
4109
|
+
* decision is recorded first, only once the cap admits the rung's cells.
|
|
4110
|
+
* Returns false when the cap or `maxCells` refused them.
|
|
4111
|
+
*/
|
|
4112
|
+
async allocateFor(nodeId, decision) {
|
|
4113
|
+
const rung = decision?.decision.status === "advanced" ? decision.decision.rung : this.state.node(nodeId).rung ?? 0;
|
|
4114
|
+
const plans = this.options.allocation.plan(this.state, nodeId, rung).filter((plan) => !this.state.cell(searchCellId(this.recorder.searchId, nodeId, plan.taskId, plan.split, plan.rep)));
|
|
4115
|
+
if (plans.length === 0) {
|
|
4116
|
+
if (decision) await this.decide(decision);
|
|
4117
|
+
return true;
|
|
4118
|
+
}
|
|
3744
4119
|
const placed = plans.map((plan) => {
|
|
3745
4120
|
const laneName = this.options.executor.place({
|
|
3746
4121
|
...plan,
|
|
@@ -3756,9 +4131,10 @@ var SearchKernel = class {
|
|
|
3756
4131
|
});
|
|
3757
4132
|
const hold = placed.reduce((sum, { reservation }) => sum + reservation.usd, 0);
|
|
3758
4133
|
const { headroomUsd } = this.state.budget;
|
|
3759
|
-
if (headroomUsd !== null && hold > headroomUsd + USD_TOLERANCE) return;
|
|
4134
|
+
if (headroomUsd !== null && hold > headroomUsd + USD_TOLERANCE) return false;
|
|
3760
4135
|
const { maxCells } = this.state.header.budget;
|
|
3761
|
-
if (maxCells !== null && this.state.audit.cells.allocated + placed.length > maxCells) return;
|
|
4136
|
+
if (maxCells !== null && this.state.audit.cells.allocated + placed.length > maxCells) return false;
|
|
4137
|
+
if (decision) await this.decide(decision);
|
|
3762
4138
|
for (const { plan, lane, reservation } of placed) await this.recorder.allocateCell({
|
|
3763
4139
|
nodeId,
|
|
3764
4140
|
taskId: plan.taskId,
|
|
@@ -3775,6 +4151,11 @@ var SearchKernel = class {
|
|
|
3775
4151
|
}
|
|
3776
4152
|
this.pending.set(nodeId, (this.pending.get(nodeId) ?? 0) + placed.length);
|
|
3777
4153
|
this.dispatch();
|
|
4154
|
+
return true;
|
|
4155
|
+
}
|
|
4156
|
+
async decide(decision) {
|
|
4157
|
+
await this.recorder.decideNode(decision);
|
|
4158
|
+
await this.refresh();
|
|
3778
4159
|
}
|
|
3779
4160
|
enqueue(cell) {
|
|
3780
4161
|
this.lanes.get(cell.lane).queues[STAGE_PRIORITY[cell.stage]].push(cell.cellId);
|
|
@@ -3942,7 +4323,7 @@ var SearchKernel = class {
|
|
|
3942
4323
|
const children = this.options.proposer.childrenPerProposal ?? 1;
|
|
3943
4324
|
const need = operation.usd + children * screen * this.maxCellReservation();
|
|
3944
4325
|
const { headroomUsd } = this.state.budget;
|
|
3945
|
-
if (headroomUsd !== null && need > headroomUsd + USD_TOLERANCE) return "budget";
|
|
4326
|
+
if (headroomUsd !== null && (need > headroomUsd + USD_TOLERANCE || this.overCap())) return "budget";
|
|
3946
4327
|
if (maxCells !== null && this.state.audit.cells.allocated + children * screen > maxCells) return "budget";
|
|
3947
4328
|
const expansion = policy.expand(view);
|
|
3948
4329
|
if (expansion === null) return "converged";
|
|
@@ -4093,6 +4474,8 @@ var SearchKernel = class {
|
|
|
4093
4474
|
}
|
|
4094
4475
|
async close() {
|
|
4095
4476
|
await this.refresh();
|
|
4477
|
+
const keep = this.options.policy.leader(this.policyView());
|
|
4478
|
+
for (const decision of this.options.allocation.prune(this.allocationView(), keep)) await this.decide(decision);
|
|
4096
4479
|
if (this.claimPlan) {
|
|
4097
4480
|
await this.closeWithClaim(this.claimPlan);
|
|
4098
4481
|
return;
|
|
@@ -4102,6 +4485,7 @@ var SearchKernel = class {
|
|
|
4102
4485
|
const { split } = view;
|
|
4103
4486
|
const root = this.state.rootNodeId;
|
|
4104
4487
|
const rule = this.options.policy.name;
|
|
4488
|
+
const leaderUnits = view.unitScores(leader).map((unit) => unit.unitId);
|
|
4105
4489
|
const decisions = this.state.nodes().filter((node) => !isTerminal(node)).map((node) => {
|
|
4106
4490
|
const scored = this.state.scoredCells(node.nodeId, split).length > 0;
|
|
4107
4491
|
const lead = node.nodeId === leader && scored;
|
|
@@ -4110,12 +4494,13 @@ var SearchKernel = class {
|
|
|
4110
4494
|
against,
|
|
4111
4495
|
split
|
|
4112
4496
|
});
|
|
4497
|
+
const units = new Set(view.unitScores(node.nodeId).map((unit) => unit.unitId));
|
|
4113
4498
|
return {
|
|
4114
4499
|
nodeId: node.nodeId,
|
|
4115
4500
|
decision: { status: lead ? "selected" : "rejected" },
|
|
4116
4501
|
basis,
|
|
4117
4502
|
rule,
|
|
4118
|
-
reason: lead ? `the leader when the search stopped (${this.stopReason}): a budget decision on the ${split} split, which claims nothing` : !this.screenedSet.has(node.nodeId) || !scored ? "the search stopped before this node was measured" : !view.complete(node.nodeId) ? "it left a unit
|
|
4503
|
+
reason: lead ? `the leader when the search stopped (${this.stopReason}): a budget decision on the ${split} split, which claims nothing` : !this.screenedSet.has(node.nodeId) || !scored ? "the search stopped before this node was measured" : !view.complete(node.nodeId) ? "it left a unit unscored, so it could not lead" : leaderUnits.some((unitId) => !units.has(unitId)) ? "the search stopped before it was measured on every unit the leader was" : "it did not beat the leader on the units they share"
|
|
4119
4504
|
};
|
|
4120
4505
|
});
|
|
4121
4506
|
for (const decision of decisions) await this.recorder.decideNode(decision);
|
|
@@ -4151,22 +4536,35 @@ var SearchKernel = class {
|
|
|
4151
4536
|
});
|
|
4152
4537
|
await this.refresh();
|
|
4153
4538
|
}
|
|
4539
|
+
/** The closed search, with its claim made again from the ledger alone. A
|
|
4540
|
+
* claim the ledger does not support throws: a tampered ledger, or a
|
|
4541
|
+
* producer and verifier that disagree. */
|
|
4154
4542
|
closedResult() {
|
|
4155
4543
|
const state = this.state;
|
|
4544
|
+
const claimVerification = verifySearchClaim(state);
|
|
4545
|
+
if (claimVerification?.status === "mismatch") throw new Error(`runSearch: search ${state.searchId} closed with a claim its ledger does not support: ${claimVerification.differences.join("; ")}`);
|
|
4156
4546
|
return {
|
|
4157
4547
|
state,
|
|
4158
4548
|
leader: state.audit.selectedNodeId ?? state.rootNodeId,
|
|
4159
4549
|
reason: state.closed.reason,
|
|
4160
|
-
claim: state.closed.claim
|
|
4550
|
+
claim: state.closed.claim,
|
|
4551
|
+
claimVerification
|
|
4161
4552
|
};
|
|
4162
4553
|
}
|
|
4163
4554
|
policyView() {
|
|
4164
4555
|
return searchPolicyView(this.state, {
|
|
4165
4556
|
screened: this.screened,
|
|
4166
4557
|
screening: this.admitted.size - this.screenedSet.size,
|
|
4167
|
-
expansions: this.completed.length
|
|
4558
|
+
expansions: this.completed.length,
|
|
4559
|
+
maxAttempts: this.maxAttempts
|
|
4168
4560
|
});
|
|
4169
4561
|
}
|
|
4562
|
+
allocationView() {
|
|
4563
|
+
return {
|
|
4564
|
+
state: this.state,
|
|
4565
|
+
idle: (nodeId) => this.admitted.has(nodeId) && (this.pending.get(nodeId) ?? 0) === 0
|
|
4566
|
+
};
|
|
4567
|
+
}
|
|
4170
4568
|
artifact(nodeId) {
|
|
4171
4569
|
const held = this.artifacts.get(nodeId);
|
|
4172
4570
|
if (held !== void 0) return held;
|
|
@@ -4203,6 +4601,14 @@ var SearchKernel = class {
|
|
|
4203
4601
|
usd: estimate(this.operationCosts, this.options.proposer.reservationUsd ?? 0)
|
|
4204
4602
|
};
|
|
4205
4603
|
}
|
|
4604
|
+
/** Overspend took committed spend, open holds and the unspent claim reserve
|
|
4605
|
+
* past the cap. Nothing new is proposed then, even at a zero-dollar prior. */
|
|
4606
|
+
overCap() {
|
|
4607
|
+
const { maxUsd } = this.state.header.budget;
|
|
4608
|
+
if (maxUsd === null) return false;
|
|
4609
|
+
const { committedUsd, openReservationUsd } = this.state.audit.spend;
|
|
4610
|
+
return committedUsd + openReservationUsd + this.state.budget.claimReserveUsd > maxUsd + USD_TOLERANCE;
|
|
4611
|
+
}
|
|
4206
4612
|
pastDeadline() {
|
|
4207
4613
|
const deadline = this.state.header.budget.deadline;
|
|
4208
4614
|
return deadline !== null && this.now() >= Date.parse(deadline);
|
|
@@ -4216,8 +4622,11 @@ var SearchKernel = class {
|
|
|
4216
4622
|
* finished, in registration order; `screening` counts the admitted nodes still
|
|
4217
4623
|
* being screened. Nodes are ranked on the selection split, or on train when
|
|
4218
4624
|
* the search declares no selection split. The view reads no test cell.
|
|
4625
|
+
* `maxAttempts` (default 3, the kernel's) says when an errored cell has no
|
|
4626
|
+
* attempt left, which makes its unit a dodge.
|
|
4219
4627
|
*/
|
|
4220
4628
|
function searchPolicyView(state, input) {
|
|
4629
|
+
const maxAttempts = input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
4221
4630
|
const header = state.header;
|
|
4222
4631
|
if (!header) throw new Error(`search ${state.searchId} has not been opened`);
|
|
4223
4632
|
const split = header.splits.selection.tasks.length > 0 ? "selection" : "train";
|
|
@@ -4231,8 +4640,13 @@ function searchPolicyView(state, input) {
|
|
|
4231
4640
|
screened: [...input.screened],
|
|
4232
4641
|
screening: input.screening,
|
|
4233
4642
|
complete: (nodeId) => {
|
|
4234
|
-
|
|
4235
|
-
|
|
4643
|
+
let scored = false;
|
|
4644
|
+
for (const cell of state.cells({ nodeId })) {
|
|
4645
|
+
if (cell.split !== split) continue;
|
|
4646
|
+
if (cell.score !== null) scored = true;
|
|
4647
|
+
else if (cell.final || cell.outcome === "errored" && cell.attempts >= maxAttempts) return false;
|
|
4648
|
+
}
|
|
4649
|
+
return scored;
|
|
4236
4650
|
},
|
|
4237
4651
|
unitScores: (nodeId) => state.unitScores(nodeId, split),
|
|
4238
4652
|
estimate: (nodeId, against) => estimateNode(state, nodeId, {
|
|
@@ -4880,12 +5294,14 @@ function canonicalHeader(header) {
|
|
|
4880
5294
|
* claim nothing; a claim comes only from the sealed test split.
|
|
4881
5295
|
*/
|
|
4882
5296
|
/**
|
|
4883
|
-
* The hill climb. The leader starts at the root; a screened node that
|
|
4884
|
-
*
|
|
4885
|
-
*
|
|
4886
|
-
*
|
|
4887
|
-
*
|
|
4888
|
-
*
|
|
5297
|
+
* The hill climb. The leader starts at the root; a screened node that dodged no
|
|
5298
|
+
* unit takes the lead when it scored every unit the leader scored and its mean
|
|
5299
|
+
* on them beats the leader's. A node measured on fewer units than the leader,
|
|
5300
|
+
* such as one an allocator has only screened, cannot take the lead on less
|
|
5301
|
+
* evidence than the leader holds. Nodes are taken in registration order, so
|
|
5302
|
+
* the leader is a pure function of the ledger. The policy expands the leader,
|
|
5303
|
+
* and only once every earlier child is screened, so each proposal sees every
|
|
5304
|
+
* result before it.
|
|
4889
5305
|
*/
|
|
4890
5306
|
function incumbent(options = {}) {
|
|
4891
5307
|
const { patience } = options;
|
|
@@ -4970,20 +5386,20 @@ function hillClimb(spec) {
|
|
|
4970
5386
|
}
|
|
4971
5387
|
};
|
|
4972
5388
|
}
|
|
4973
|
-
/** True when `challenger`
|
|
5389
|
+
/** True when `challenger` scored every unit `holder` scored and improves on
|
|
5390
|
+
* `holder` over them. */
|
|
4974
5391
|
function beats(view, challenger, holder) {
|
|
4975
|
-
const held =
|
|
5392
|
+
const held = view.unitScores(holder);
|
|
5393
|
+
if (held.length === 0) return false;
|
|
5394
|
+
const own = new Map(view.unitScores(challenger).map((unit) => [unit.unitId, unit.mean]));
|
|
4976
5395
|
let challengerSum = 0;
|
|
4977
5396
|
let holderSum = 0;
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
holderSum += other;
|
|
4984
|
-
shared += 1;
|
|
5397
|
+
for (const unit of held) {
|
|
5398
|
+
const mean = own.get(unit.unitId);
|
|
5399
|
+
if (mean === void 0) return false;
|
|
5400
|
+
challengerSum += mean;
|
|
5401
|
+
holderSum += unit.mean;
|
|
4985
5402
|
}
|
|
4986
|
-
if (shared === 0) return false;
|
|
4987
5403
|
return view.direction === "maximize" ? challengerSum > holderSum : challengerSum < holderSum;
|
|
4988
5404
|
}
|
|
4989
5405
|
/** Non-dominated complete screened nodes over the root's units, with crowding. */
|
|
@@ -7250,6 +7666,6 @@ function firstString(value) {
|
|
|
7250
7666
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
7251
7667
|
}
|
|
7252
7668
|
//#endregion
|
|
7253
|
-
export {
|
|
7669
|
+
export { defaultProductionGate as $, createSearchHistoryReceipt as A, decideSearchClaim as B, searchEdgeId as C, assertCompleteSearchHistory as D, SearchHistoryRequiredError as E, runSearch as F, requiredPairsForPairedPromotion as G, searchClaimReserveUsd as H, searchDivergence as I, dominates as J, asha as K, searchExpansionIndex as L, verifySearchHistoryArtifact as M, verifySearchHistoryReceipt as N, assertSearchHistoryAdmissionOptions as O, SEARCH_KERNEL_SOURCE as P, captureJudge as Q, searchPolicyView as R, developmentClaim as S, surfaceNode as T, verifySearchClaim as U, planSearchClaim as V, pairedPromotionPower as W, runFinalComparison as X, paretoFrontier as Y, openAutoPr as Z, isProposedCandidate as _, computeManifestHash as _t, transientDispatchFailure as a, assertIndependentEvaluationSplit as at, incumbent as b, compareOptimizationMethods as c, reserveFinalEvidence as ct, combineComparisonCosts as d, planCampaignRun as dt, DEFAULT_RED_TEAM_CORPUS as et, costFromLedgerSummary as f, resolveRunDir as ft, runOptimization as g, cellCachePath as gt, runImprovementLoop as h, buildCellSchedule as ht, quotaExhaustedUntil as i, runCanaries as it, searchHistoryCoverageRow as j, assertSearchHistoryMatchesState as k, optimizationTokenUsageFromSummary as l, runEval as lt, readGepaCandidatePopulationArtifact as m, readCachedCell as mt, JudgeParseError as n, redTeamReport as nt, aggregateRunScore as o, captureFinalEvidencePolicy as ot, assertGepaCandidatePopulationSummary as p, tangleTracesRoot as pt, uniform as q, isTransientTransportFailure as r, scoreRedTeamOutput as rt, clamp01 as s, evaluationUnitMap as st, llmJudge as t, redTeamDataset as tt, executeOptimizationMethod as u, runCampaign as ut, labelTrustRank as v, surfaceDiff as w, SearchRecorder as x, crowdedFrontierParent as y, SEARCH_CLAIM_RULE as z };
|
|
7254
7670
|
|
|
7255
|
-
//# sourceMappingURL=llm-judge-
|
|
7671
|
+
//# sourceMappingURL=llm-judge-CvKpaRKY.js.map
|