@tangle-network/agent-eval 0.126.0 → 0.126.2

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.
@@ -16,8 +16,9 @@ import {
16
16
  } from "./chunk-UCLVDLCH.js";
17
17
  import {
18
18
  clamp01,
19
+ combineAbortSignals,
19
20
  mapConcurrent
20
- } from "./chunk-UI4YMIN2.js";
21
+ } from "./chunk-WGXIEX7P.js";
21
22
  import {
22
23
  detectRewardHacking
23
24
  } from "./chunk-ARU2PZFM.js";
@@ -1513,6 +1514,29 @@ function campaignMeanComposite(campaign) {
1513
1514
  }
1514
1515
  return composites.length === 0 ? 0 : composites.reduce((a, b) => a + b, 0) / composites.length;
1515
1516
  }
1517
+ function assertFiniteRankKey(key, label, expectedLength) {
1518
+ if (!Array.isArray(key) || key.length === 0) {
1519
+ throw new Error(`${label} must return a non-empty array`);
1520
+ }
1521
+ if (expectedLength !== void 0 && key.length !== expectedLength) {
1522
+ throw new Error(`${label} returned ${key.length} elements; expected ${expectedLength}`);
1523
+ }
1524
+ for (let index = 0; index < key.length; index++) {
1525
+ if (!Number.isFinite(key[index])) {
1526
+ throw new Error(`${label}[${index}] must be finite`);
1527
+ }
1528
+ }
1529
+ }
1530
+ function compareRankKeys(a, b) {
1531
+ assertFiniteRankKey(a, "rank key a");
1532
+ assertFiniteRankKey(b, "rank key b", a.length);
1533
+ for (let i = 0; i < a.length; i++) {
1534
+ const av = a[i];
1535
+ const bv = b[i];
1536
+ if (av !== bv) return av - bv;
1537
+ }
1538
+ return 0;
1539
+ }
1516
1540
  function campaignBreakdown(campaign) {
1517
1541
  const dimSums = {};
1518
1542
  const dimCounts = {};
@@ -1696,7 +1720,6 @@ ${describe(winnerSurface)}`;
1696
1720
  }
1697
1721
 
1698
1722
  // src/campaign/presets/compare-optimization-methods.ts
1699
- import { randomUUID } from "crypto";
1700
1723
  async function compareOptimizationMethods(opts) {
1701
1724
  assertOptimizationMethods(opts.methods);
1702
1725
  assertComparisonPartitions(opts);
@@ -1711,20 +1734,22 @@ async function compareOptimizationMethods(opts) {
1711
1734
  assertComparisonControls(opts, seed, resamples, confidence);
1712
1735
  const storage = opts.storage ?? fsCampaignStorage();
1713
1736
  const resolvedRunDir = resolveRunDir(opts.runDir, opts.repo);
1714
- const testCostPhase = `compareOptimizationMethods:test:${randomUUID()}`;
1715
- const testCostLedger = opts.costLedger ?? createRunCostLedger({
1737
+ const baselineSurface = structuredClone(opts.baselineSurface);
1738
+ const costLedger = opts.costLedger ?? createRunCostLedger({
1716
1739
  storage,
1717
- runDir: `${resolvedRunDir}/test/cost`,
1740
+ runDir: `${resolvedRunDir}/cost`,
1718
1741
  costCeilingUsd: opts.costCeiling
1719
1742
  });
1720
- const scoreOnTest = async (surface, tag) => {
1743
+ const scoreOnTest = async (surface, tag, costPhase) => {
1744
+ const measuredSurface = structuredClone(surface);
1721
1745
  const campaign = await runCampaign({
1722
1746
  ...opts,
1723
1747
  storage,
1724
- costLedger: testCostLedger,
1725
- costPhase: testCostPhase,
1748
+ costLedger,
1749
+ costPhase,
1726
1750
  scenarios: opts.testScenarios.map((scenario) => structuredClone(scenario)),
1727
- dispatch: (scenario, ctx) => opts.dispatchWithSurface(surface, scenario, ctx),
1751
+ dispatch: (scenario, ctx) => opts.dispatchWithSurface(structuredClone(measuredSurface), scenario, ctx),
1752
+ dispatchRef: finalDispatchRef(opts, measuredSurface),
1728
1753
  runDir: `${resolvedRunDir}/${tag}`
1729
1754
  });
1730
1755
  const byScenario = {};
@@ -1745,22 +1770,50 @@ async function compareOptimizationMethods(opts) {
1745
1770
  }
1746
1771
  return scenarioIds.map((id) => byScenario[id]);
1747
1772
  };
1773
+ const optimizationOwner = new AbortController();
1748
1774
  const optimized = await mapConcurrent(opts.methods, optimizationConcurrency, async (method) => {
1749
- const out = await method.optimize(
1750
- createOptimizationMethodInput(opts, method.name, resolvedRunDir, seed)
1751
- );
1752
- assertOptimizationResult(method.name, out);
1753
- const winnerSurface = structuredClone(out.winnerSurface);
1754
- return {
1755
- name: method.name,
1756
- winnerSurface,
1757
- cost: out.cost,
1758
- durationMs: out.durationMs,
1759
- provenance: out.provenance
1760
- };
1775
+ try {
1776
+ const out = await method.optimize(
1777
+ createOptimizationMethodInput(
1778
+ opts,
1779
+ method.name,
1780
+ resolvedRunDir,
1781
+ seed,
1782
+ baselineSurface,
1783
+ costLedger,
1784
+ optimizationOwner.signal
1785
+ )
1786
+ );
1787
+ assertOptimizationResult(method.name, out);
1788
+ const winnerSurface = structuredClone(out.winnerSurface);
1789
+ return {
1790
+ name: method.name,
1791
+ winnerSurface,
1792
+ cost: out.cost,
1793
+ durationMs: out.durationMs,
1794
+ provenance: out.provenance
1795
+ };
1796
+ } catch (error) {
1797
+ if (!optimizationOwner.signal.aborted) optimizationOwner.abort(error);
1798
+ throw error;
1799
+ }
1761
1800
  });
1762
- const baselineArr = align(await scoreOnTest(opts.baselineSurface, "test/baseline"), "baseline");
1763
- const testScoresBySurface = /* @__PURE__ */ new Map([[surfaceContentHash(opts.baselineSurface), baselineArr]]);
1801
+ assertReportedCostWithinCeiling(
1802
+ combineCosts(
1803
+ optimized.map((result) => ({
1804
+ label: `method '${result.name}'`,
1805
+ cost: result.cost
1806
+ }))
1807
+ ).totalCostUsd,
1808
+ opts.costCeiling,
1809
+ "optimization"
1810
+ );
1811
+ const testCostPhase = finalCostPhase(opts, baselineSurface, optimized, seed);
1812
+ const baselineArr = align(
1813
+ await scoreOnTest(baselineSurface, "test/baseline", testCostPhase),
1814
+ "baseline"
1815
+ );
1816
+ const testScoresBySurface = /* @__PURE__ */ new Map([[surfaceContentHash(baselineSurface), baselineArr]]);
1764
1817
  const winners = [];
1765
1818
  for (const winner of optimized) {
1766
1819
  const surfaceKey = surfaceContentHash(winner.winnerSurface);
@@ -1768,7 +1821,8 @@ async function compareOptimizationMethods(opts) {
1768
1821
  if (!arr) {
1769
1822
  const byScenario = await scoreOnTest(
1770
1823
  winner.winnerSurface,
1771
- `test/methods/${slug(winner.name)}`
1824
+ `test/methods/${slug(winner.name)}`,
1825
+ testCostPhase
1772
1826
  );
1773
1827
  arr = align(byScenario, `method "${winner.name}"`);
1774
1828
  testScoresBySurface.set(surfaceKey, arr);
@@ -1798,7 +1852,7 @@ async function compareOptimizationMethods(opts) {
1798
1852
  winnerComposite: w.arr[index],
1799
1853
  lift: w.arr[index] - baselineArr[index]
1800
1854
  })),
1801
- winnerSurface: w.winnerSurface,
1855
+ winnerSurface: structuredClone(w.winnerSurface),
1802
1856
  rank: 0
1803
1857
  };
1804
1858
  if (w.durationMs !== void 0) score.durationMs = w.durationMs;
@@ -1843,11 +1897,12 @@ async function compareOptimizationMethods(opts) {
1843
1897
  const optimizationCost = combineCosts(
1844
1898
  scores.map((score) => ({ label: `method '${score.name}'`, cost: score.optimizationCost }))
1845
1899
  );
1846
- const testCost = costFromLedgerSummary(testCostLedger.summary({ phase: testCostPhase }));
1900
+ const testCost = costFromLedgerSummary(costLedger.summary({ phase: testCostPhase }));
1847
1901
  const totalCost = combineCosts([
1848
1902
  { label: "optimization", cost: optimizationCost },
1849
1903
  { label: "final test", cost: testCost }
1850
1904
  ]);
1905
+ assertReportedCostWithinCeiling(totalCost.totalCostUsd, opts.costCeiling, "total");
1851
1906
  return {
1852
1907
  scores,
1853
1908
  best,
@@ -1864,6 +1919,14 @@ async function compareOptimizationMethods(opts) {
1864
1919
  reps: opts.reps ?? 1
1865
1920
  };
1866
1921
  }
1922
+ function assertReportedCostWithinCeiling(totalCostUsd, costCeiling, phase) {
1923
+ const tolerance = Number.EPSILON * Math.max(1, Math.abs(totalCostUsd), Math.abs(costCeiling ?? 0)) * 8;
1924
+ if (costCeiling !== void 0 && totalCostUsd > costCeiling + tolerance) {
1925
+ throw new Error(
1926
+ `compareOptimizationMethods: reported ${phase} cost ${totalCostUsd} exceeds costCeiling ${costCeiling}`
1927
+ );
1928
+ }
1929
+ }
1867
1930
  function assertOptimizationMethods(methods) {
1868
1931
  if (!Array.isArray(methods) || methods.length === 0) {
1869
1932
  throw new Error("compareOptimizationMethods: no methods to compare");
@@ -1961,12 +2024,22 @@ function assertOptimizationProvenance(methodName, value) {
1961
2024
  }
1962
2025
  }
1963
2026
  function assertComparisonControls(opts, seed, resamples, confidence) {
2027
+ if (opts.optimizationRunOptions && "costCeiling" in opts.optimizationRunOptions) {
2028
+ throw new Error(
2029
+ "compareOptimizationMethods: optimizationRunOptions.costCeiling is not supported; costCeiling covers optimization and final scoring"
2030
+ );
2031
+ }
1964
2032
  if (!opts.judges || opts.judges.length === 0) {
1965
2033
  throw new Error("compareOptimizationMethods: at least one judge is required");
1966
2034
  }
1967
2035
  if (typeof opts.dispatchWithSurface !== "function") {
1968
2036
  throw new Error("compareOptimizationMethods: dispatchWithSurface must be a function");
1969
2037
  }
2038
+ if (opts.dispatchRef !== void 0 && (typeof opts.dispatchRef !== "string" || opts.dispatchRef.trim().length === 0 || opts.dispatchRef.trim() !== opts.dispatchRef)) {
2039
+ throw new Error(
2040
+ "compareOptimizationMethods: dispatchRef must be trimmed and non-empty when provided"
2041
+ );
2042
+ }
1970
2043
  try {
1971
2044
  surfaceContentHash(opts.baselineSurface);
1972
2045
  } catch (cause) {
@@ -2112,9 +2185,8 @@ function minimumBootstrapResamples(confidence, comparisonCount) {
2112
2185
  const exact = 2 * comparisonCount / (1 - confidence);
2113
2186
  return Math.ceil(exact - Number.EPSILON * Math.max(1, exact) * 32);
2114
2187
  }
2115
- function createOptimizationMethodInput(opts, methodName, resolvedRunDir, seed) {
2188
+ function createOptimizationMethodInput(opts, methodName, resolvedRunDir, seed, baselineSurface, costLedger, optimizationSignal) {
2116
2189
  const methodRunDir = `${resolvedRunDir}/optimization/${slug(methodName)}`;
2117
- const storage = opts.storage ?? fsCampaignStorage();
2118
2190
  const cloneScenarios = (scenarios) => Object.freeze(scenarios.map((scenario) => structuredClone(scenario)));
2119
2191
  const judges = opts.judges.map(
2120
2192
  (judge) => Object.freeze({
@@ -2124,22 +2196,54 @@ function createOptimizationMethodInput(opts, methodName, resolvedRunDir, seed) {
2124
2196
  )
2125
2197
  })
2126
2198
  );
2199
+ const signal = combineAbortSignals(
2200
+ opts.signal,
2201
+ opts.optimizationRunOptions?.signal,
2202
+ optimizationSignal
2203
+ );
2127
2204
  return Object.freeze({
2128
- baselineSurface: structuredClone(opts.baselineSurface),
2205
+ baselineSurface: structuredClone(baselineSurface),
2129
2206
  trainScenarios: cloneScenarios(opts.trainScenarios),
2130
2207
  selectionScenarios: cloneScenarios(opts.selectionScenarios),
2131
2208
  dispatchWithSurface: opts.dispatchWithSurface,
2132
2209
  judges: Object.freeze(judges),
2133
2210
  runDir: methodRunDir,
2134
2211
  seed,
2135
- runOptions: Object.freeze({ ...opts.optimizationRunOptions ?? {} }),
2136
- costLedger: createRunCostLedger({
2137
- storage,
2138
- runDir: `${methodRunDir}/cost`,
2139
- costCeilingUsd: opts.optimizationRunOptions?.costCeiling
2140
- })
2212
+ runOptions: Object.freeze({
2213
+ ...opts.optimizationRunOptions ?? {},
2214
+ ...signal ? { signal } : {}
2215
+ }),
2216
+ costLedger
2141
2217
  });
2142
2218
  }
2219
+ function finalCostPhase(opts, baselineSurface, winners, seed) {
2220
+ const identity = contentHash({
2221
+ baseline: surfaceContentHash(baselineSurface),
2222
+ winners: winners.map((winner) => ({
2223
+ name: winner.name,
2224
+ surface: surfaceContentHash(winner.winnerSurface)
2225
+ })),
2226
+ testScenarios: opts.testScenarios,
2227
+ judges: opts.judges.map((judge) => ({
2228
+ name: judge.name,
2229
+ dimensions: judge.dimensions,
2230
+ version: judge.judgeVersion ?? contentHash({
2231
+ score: judge.score.toString(),
2232
+ appliesTo: judge.appliesTo?.toString() ?? null
2233
+ })
2234
+ })),
2235
+ dispatch: callerDispatchRef(opts),
2236
+ seed,
2237
+ reps: opts.reps ?? 1
2238
+ });
2239
+ return `compareOptimizationMethods.test:${identity}`;
2240
+ }
2241
+ function finalDispatchRef(opts, surface) {
2242
+ return `compareOptimizationMethods:${callerDispatchRef(opts)}:${surfaceContentHash(surface)}`;
2243
+ }
2244
+ function callerDispatchRef(opts) {
2245
+ return (opts.dispatchRef ?? opts.dispatchWithSurface.name) || "anonymous";
2246
+ }
2143
2247
  function costFromLedgerSummary(summary) {
2144
2248
  const cost = {
2145
2249
  totalCostUsd: summary.totalCostUsd,
@@ -2384,6 +2488,7 @@ function sendJson(response, status, body) {
2384
2488
  var MAX_CALLBACK_BODY_BYTES = 1e6;
2385
2489
  async function startExternalOptimizerCallback(args) {
2386
2490
  assertCallbackConfig(args);
2491
+ args.signal?.throwIfAborted();
2387
2492
  let evaluations = 0;
2388
2493
  let accepting = true;
2389
2494
  let closePromise;
@@ -2397,6 +2502,7 @@ async function startExternalOptimizerCallback(args) {
2397
2502
  const controller = new AbortController();
2398
2503
  const abortRequest = () => {
2399
2504
  request.destroy();
2505
+ response.destroy();
2400
2506
  };
2401
2507
  activeControllers.add(controller);
2402
2508
  controller.signal.addEventListener("abort", abortRequest, { once: true });
@@ -2419,16 +2525,23 @@ async function startExternalOptimizerCallback(args) {
2419
2525
  void handler.catch(() => void 0);
2420
2526
  });
2421
2527
  const port = await listenLocal(server);
2528
+ const close = () => {
2529
+ closePromise ??= closeCallbackServer();
2530
+ return closePromise;
2531
+ };
2532
+ const onAbort = () => {
2533
+ void close().catch(() => void 0);
2534
+ };
2535
+ args.signal?.addEventListener("abort", onAbort, { once: true });
2536
+ if (args.signal?.aborted) onAbort();
2422
2537
  return {
2423
2538
  url: `http://127.0.0.1:${port}/evaluate`,
2424
2539
  token: args.token,
2425
2540
  evaluations: () => evaluations,
2426
- close: () => {
2427
- closePromise ??= closeCallbackServer();
2428
- return closePromise;
2429
- }
2541
+ close
2430
2542
  };
2431
2543
  async function closeCallbackServer() {
2544
+ args.signal?.removeEventListener("abort", onAbort);
2432
2545
  accepting = false;
2433
2546
  const closingServer = closeServer(server);
2434
2547
  server.closeIdleConnections?.();
@@ -2525,6 +2638,7 @@ import { createServer as createServer2 } from "http";
2525
2638
  var MODEL_PROXY_PATHS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
2526
2639
  async function startExternalOptimizerModelProxy(args) {
2527
2640
  assertModelProxyConfig(args);
2641
+ args.signal?.throwIfAborted();
2528
2642
  const token = randomLocalToken();
2529
2643
  const fetchImpl = args.fetchImpl ?? fetch;
2530
2644
  let requestCount = 0;
@@ -2544,6 +2658,7 @@ async function startExternalOptimizerModelProxy(args) {
2544
2658
  const controller = new AbortController();
2545
2659
  const abortRequest = () => {
2546
2660
  request.destroy();
2661
+ response.destroy();
2547
2662
  };
2548
2663
  activeControllers.add(controller);
2549
2664
  controller.signal.addEventListener("abort", abortRequest, { once: true });
@@ -2583,17 +2698,24 @@ async function startExternalOptimizerModelProxy(args) {
2583
2698
  void handler.catch(() => void 0);
2584
2699
  });
2585
2700
  const port = await listenLocal(server);
2701
+ const close = () => {
2702
+ closePromise ??= closeModelProxy();
2703
+ return closePromise;
2704
+ };
2705
+ const onAbort = () => {
2706
+ void close().catch(() => void 0);
2707
+ };
2708
+ args.signal?.addEventListener("abort", onAbort, { once: true });
2709
+ if (args.signal?.aborted) onAbort();
2586
2710
  return {
2587
2711
  baseUrl: `http://127.0.0.1:${port}/v1`,
2588
2712
  apiKey: token,
2589
2713
  requestAttempts: () => requestCount,
2590
2714
  successfulCompletions: () => successfulCompletionCount,
2591
- close: () => {
2592
- closePromise ??= closeModelProxy();
2593
- return closePromise;
2594
- }
2715
+ close
2595
2716
  };
2596
2717
  async function closeModelProxy() {
2718
+ args.signal?.removeEventListener("abort", onAbort);
2597
2719
  accepting = false;
2598
2720
  const closingServer = closeServer(server);
2599
2721
  server.closeIdleConnections?.();
@@ -3041,6 +3163,7 @@ async function runExternalOptimizerProcess(args) {
3041
3163
  `${args.label} requires POSIX process-group cleanup; run the optimizer through WSL or Linux`
3042
3164
  );
3043
3165
  }
3166
+ throwIfAborted(args.signal, args.label);
3044
3167
  const dir = await mkdtemp(join2(tmpdir2(), args.tempPrefix));
3045
3168
  const inputPath = join2(dir, "input.json");
3046
3169
  const outputPath = join2(dir, "output.json");
@@ -3074,11 +3197,14 @@ async function runExternalOptimizerProcess(args) {
3074
3197
  args: commandArgs,
3075
3198
  cwd: dir,
3076
3199
  env: args.runner?.env,
3077
- timeoutMs: args.timeoutMs
3200
+ timeoutMs: args.timeoutMs,
3201
+ signal: args.signal
3078
3202
  });
3203
+ throwIfAborted(args.signal, args.label);
3079
3204
  const raw = JSON.parse(
3080
3205
  await readBoundedTextFile(outputPath, MAX_PROCESS_RESULT_BYTES, `${args.label} output`)
3081
3206
  );
3207
+ throwIfAborted(args.signal, args.label);
3082
3208
  if (!isRecord(raw)) throw new Error(`${args.label} output must be a JSON object`);
3083
3209
  return raw;
3084
3210
  },
@@ -3106,6 +3232,7 @@ async function readBoundedTextFile(path, maxBytes, label) {
3106
3232
  }
3107
3233
  }
3108
3234
  function runProcess(args) {
3235
+ throwIfAborted(args.signal, args.label);
3109
3236
  return new Promise((resolvePromise, reject) => {
3110
3237
  const child = spawn(args.command, args.args, {
3111
3238
  cwd: args.cwd,
@@ -3118,10 +3245,12 @@ function runProcess(args) {
3118
3245
  const stderr = createProcessOutputCapture();
3119
3246
  let settled = false;
3120
3247
  let timeout;
3248
+ const onAbort = () => finish(abortReason(args.signal, args.label));
3121
3249
  const finish = (error) => {
3122
3250
  if (settled) return;
3123
3251
  settled = true;
3124
3252
  if (timeout) clearTimeout(timeout);
3253
+ args.signal?.removeEventListener("abort", onAbort);
3125
3254
  void terminateProcessTree(child).then(
3126
3255
  () => {
3127
3256
  if (error) reject(error);
@@ -3149,6 +3278,8 @@ function runProcess(args) {
3149
3278
  timeout = setTimeout(() => {
3150
3279
  finish(new Error(`${args.label} exceeded ${args.timeoutMs}ms`));
3151
3280
  }, args.timeoutMs);
3281
+ args.signal?.addEventListener("abort", onAbort, { once: true });
3282
+ if (args.signal?.aborted) onAbort();
3152
3283
  child.stdout.on("data", (chunk) => {
3153
3284
  appendProcessOutput(stdout, chunk);
3154
3285
  });
@@ -3171,6 +3302,12 @@ function runProcess(args) {
3171
3302
  });
3172
3303
  });
3173
3304
  }
3305
+ function throwIfAborted(signal, label) {
3306
+ if (signal?.aborted) throw abortReason(signal, label);
3307
+ }
3308
+ function abortReason(signal, label) {
3309
+ return signal.reason instanceof Error ? signal.reason : new Error(`${label} aborted`, { cause: signal.reason });
3310
+ }
3174
3311
  function safeProcessEnvironment() {
3175
3312
  const allowed = [
3176
3313
  "PATH",
@@ -4912,7 +5049,8 @@ async function inspectExternalOptimizerRuntime(args) {
4912
5049
  engineModules: [...args.engineModules ?? []]
4913
5050
  },
4914
5051
  ...args.runner ? { runner: args.runner } : {},
4915
- timeoutMs: args.timeoutMs
5052
+ timeoutMs: args.timeoutMs,
5053
+ ...args.signal ? { signal: args.signal } : {}
4916
5054
  });
4917
5055
  assertExternalOptimizerRuntimeIdentity(result.runtime, args.package, args.label);
4918
5056
  return result.runtime;
@@ -5311,6 +5449,8 @@ function gepaOptimizationMethod(config) {
5311
5449
  return {
5312
5450
  name,
5313
5451
  async optimize(input) {
5452
+ const signal = input.runOptions.signal;
5453
+ signal?.throwIfAborted();
5314
5454
  if (typeof input.baselineSurface !== "string" && input.baselineSurface.kind !== "components") {
5315
5455
  throw new Error(`${name}: GEPA bridge supports text and component surfaces`);
5316
5456
  }
@@ -5337,7 +5477,8 @@ function gepaOptimizationMethod(config) {
5337
5477
  module: "agent_eval_rpc.gepa_bridge",
5338
5478
  engineModules: config.engineModules,
5339
5479
  ...bridgeRunner ? { runner: bridgeRunner } : {},
5340
- timeoutMs: config.timeoutMs ?? GEPA_DEFAULT_TIMEOUT_MS
5480
+ timeoutMs: config.timeoutMs ?? GEPA_DEFAULT_TIMEOUT_MS,
5481
+ ...signal ? { signal } : {}
5341
5482
  });
5342
5483
  const seedCandidate = encodeExternalTextCandidate(input.baselineSurface);
5343
5484
  const trainSet = input.trainScenarios.map(
@@ -5408,7 +5549,8 @@ function gepaOptimizationMethod(config) {
5408
5549
  token: randomBytes3(32).toString("hex"),
5409
5550
  maxEvaluations: evaluationLimit,
5410
5551
  acceptEvaluation: () => runBudget.acceptEvaluation(),
5411
- evaluate
5552
+ evaluate,
5553
+ ...signal ? { signal } : {}
5412
5554
  });
5413
5555
  const runnerEnv = bridgeRunner?.env ?? {};
5414
5556
  let modelProxy;
@@ -5438,7 +5580,8 @@ function gepaOptimizationMethod(config) {
5438
5580
  initialUsage: {
5439
5581
  requests: priorOptimizerUsage.totalCalls,
5440
5582
  costUsd: priorOptimizerUsage.totalCostUsd
5441
- }
5583
+ },
5584
+ ...signal ? { signal } : {}
5442
5585
  });
5443
5586
  }
5444
5587
  const outputDir2 = `${runDir}/external`;
@@ -5481,12 +5624,14 @@ function gepaOptimizationMethod(config) {
5481
5624
  ...bridgeRunner,
5482
5625
  env: removeCredentialEnvironment(runnerEnv)
5483
5626
  } : bridgeRunner,
5484
- timeoutMs: config.timeoutMs ?? GEPA_DEFAULT_TIMEOUT_MS
5627
+ timeoutMs: config.timeoutMs ?? GEPA_DEFAULT_TIMEOUT_MS,
5628
+ ...signal ? { signal } : {}
5485
5629
  });
5486
5630
  return { result: result2, outputDir: outputDir2 };
5487
5631
  },
5488
5632
  cleanup: closeResources
5489
5633
  });
5634
+ signal?.throwIfAborted();
5490
5635
  assertGepaBridgeOutput(
5491
5636
  result,
5492
5637
  name,
@@ -5636,9 +5781,12 @@ async function runOptimization(opts) {
5636
5781
  const generations = [];
5637
5782
  const history = [];
5638
5783
  let currentFindings = opts.findings ?? [];
5784
+ const selectionRankKey = opts.selectionRankKey ?? ((campaign) => [campaignMeanComposite(campaign)]);
5639
5785
  let winnerSurface = opts.baselineSurface;
5640
5786
  let winnerSurfaceHash = surfaceHash(opts.baselineSurface);
5641
5787
  let winnerComposite = campaignMeanComposite(baselineCampaign);
5788
+ let winnerRankKey = selectionRankKey(baselineCampaign);
5789
+ assertFiniteRankKey(winnerRankKey, "selectionRankKey for baseline");
5642
5790
  const baselineOutcome = toScoredSurfaceOutcome(
5643
5791
  winnerSurfaceHash,
5644
5792
  baselineCampaign,
@@ -5705,6 +5853,12 @@ async function runOptimization(opts) {
5705
5853
  runDir: `${opts.runDir}/gen-${gen}/candidate-${i}`
5706
5854
  });
5707
5855
  const composite = campaignMeanComposite(campaign);
5856
+ const rankKey = selectionRankKey(campaign);
5857
+ assertFiniteRankKey(
5858
+ rankKey,
5859
+ `selectionRankKey for generation ${gen} candidate ${i}`,
5860
+ winnerRankKey.length
5861
+ );
5708
5862
  const coverage = campaignCoverage(
5709
5863
  campaign.cells,
5710
5864
  opts.scenarios,
@@ -5718,6 +5872,7 @@ async function runOptimization(opts) {
5718
5872
  rationale,
5719
5873
  campaign,
5720
5874
  composite,
5875
+ rankKey,
5721
5876
  coverage
5722
5877
  };
5723
5878
  }
@@ -5732,16 +5887,17 @@ async function runOptimization(opts) {
5732
5887
  }
5733
5888
  surfaceResults.sort((a, b) => {
5734
5889
  if (a.coverage.complete !== b.coverage.complete) return a.coverage.complete ? -1 : 1;
5735
- return b.composite - a.composite;
5890
+ return compareRankKeys(b.rankKey, a.rankKey);
5736
5891
  });
5737
5892
  const eligibleResults = surfaceResults.filter((result) => result.coverage.complete);
5738
5893
  const top = eligibleResults[0];
5739
- const promoted = top && top.composite > winnerComposite ? [top] : [];
5894
+ const promoted = top && compareRankKeys(top.rankKey, winnerRankKey) > 0 ? [top] : [];
5740
5895
  if (promoted[0]) {
5741
5896
  const top2 = promoted[0];
5742
5897
  winnerSurface = top2.surface;
5743
5898
  winnerSurfaceHash = top2.surfaceHash;
5744
5899
  winnerComposite = top2.composite;
5900
+ winnerRankKey = top2.rankKey;
5745
5901
  winnerOutcome = toScoredSurfaceOutcome(top2.surfaceHash, top2.campaign, top2.coverage, gen);
5746
5902
  winnerLabel = top2.label || void 0;
5747
5903
  winnerRationale = top2.rationale || void 0;
@@ -6840,6 +6996,8 @@ function skillOptOptimizationMethod(config) {
6840
6996
  return {
6841
6997
  name,
6842
6998
  async optimize(input) {
6999
+ const signal = input.runOptions.signal;
7000
+ signal?.throwIfAborted();
6843
7001
  if (typeof input.baselineSurface !== "string") {
6844
7002
  throw new Error(`${name}: SkillOpt requires a string baselineSurface`);
6845
7003
  }
@@ -6861,7 +7019,8 @@ function skillOptOptimizationMethod(config) {
6861
7019
  package: "skillopt",
6862
7020
  module: "agent_eval_rpc.skillopt_bridge",
6863
7021
  ...bridgeRunner ? { runner: bridgeRunner } : {},
6864
- timeoutMs: config.timeoutMs ?? SKILLOPT_DEFAULT_TIMEOUT_MS
7022
+ timeoutMs: config.timeoutMs ?? SKILLOPT_DEFAULT_TIMEOUT_MS,
7023
+ ...signal ? { signal } : {}
6865
7024
  });
6866
7025
  const trainSet = input.trainScenarios.map(
6867
7026
  (scenario) => describeExternalScenario(scenario, "SkillOpt", maxEvidenceChars, config.describeScenario)
@@ -6927,7 +7086,8 @@ function skillOptOptimizationMethod(config) {
6927
7086
  token: randomBytes4(32).toString("hex"),
6928
7087
  maxEvaluations: config.maxEvaluations,
6929
7088
  acceptEvaluation: () => runBudget.acceptEvaluation(),
6930
- evaluate
7089
+ evaluate,
7090
+ ...signal ? { signal } : {}
6931
7091
  });
6932
7092
  const runnerEnv = bridgeRunner?.env ?? {};
6933
7093
  let activeModelProxy;
@@ -6956,7 +7116,8 @@ function skillOptOptimizationMethod(config) {
6956
7116
  initialUsage: {
6957
7117
  requests: priorOptimizerUsage.totalCalls,
6958
7118
  costUsd: priorOptimizerUsage.totalCostUsd
6959
- }
7119
+ },
7120
+ ...signal ? { signal } : {}
6960
7121
  });
6961
7122
  activeModelProxy = modelProxy2;
6962
7123
  const outputDir2 = `${runDir}/external`;
@@ -6998,15 +7159,29 @@ function skillOptOptimizationMethod(config) {
6998
7159
  OPTIMIZER_OPENAI_COMPATIBLE_BASE_URL: modelProxy2.baseUrl,
6999
7160
  OPTIMIZER_OPENAI_COMPATIBLE_API_KEY: modelProxy2.apiKey,
7000
7161
  TARGET_OPENAI_COMPATIBLE_BASE_URL: modelProxy2.baseUrl,
7001
- TARGET_OPENAI_COMPATIBLE_API_KEY: modelProxy2.apiKey
7162
+ TARGET_OPENAI_COMPATIBLE_API_KEY: modelProxy2.apiKey,
7163
+ OPENAI_COMPATIBLE_MODEL: config.optimizer.model,
7164
+ OPENAI_COMPATIBLE_MAX_TOKENS: String(
7165
+ config.optimizer.budget.maxOutputTokensPerRequest
7166
+ ),
7167
+ OPTIMIZER_OPENAI_COMPATIBLE_MODEL: config.optimizer.model,
7168
+ OPTIMIZER_OPENAI_COMPATIBLE_MAX_TOKENS: String(
7169
+ config.optimizer.budget.maxOutputTokensPerRequest
7170
+ ),
7171
+ TARGET_OPENAI_COMPATIBLE_MODEL: config.optimizer.model,
7172
+ TARGET_OPENAI_COMPATIBLE_MAX_TOKENS: String(
7173
+ config.optimizer.budget.maxOutputTokensPerRequest
7174
+ )
7002
7175
  }
7003
7176
  },
7004
- timeoutMs: config.timeoutMs ?? SKILLOPT_DEFAULT_TIMEOUT_MS
7177
+ timeoutMs: config.timeoutMs ?? SKILLOPT_DEFAULT_TIMEOUT_MS,
7178
+ ...signal ? { signal } : {}
7005
7179
  });
7006
7180
  return { result: result2, outputDir: outputDir2, modelProxy: modelProxy2 };
7007
7181
  },
7008
7182
  cleanup: closeResources
7009
7183
  });
7184
+ signal?.throwIfAborted();
7010
7185
  assertSkillOptBridgeOutput(result, name, maxCandidateChars, config.maxEvaluations);
7011
7186
  assertExternalOptimizerRunBinding({
7012
7187
  label: name,
@@ -7106,6 +7281,7 @@ export {
7106
7281
  buildReflectionPrompt,
7107
7282
  parseReflectionResponse,
7108
7283
  campaignMeanComposite,
7284
+ compareRankKeys,
7109
7285
  campaignBreakdown,
7110
7286
  assertCodeSurfaceIdentity,
7111
7287
  assertComponentSurface,
@@ -7149,4 +7325,4 @@ export {
7149
7325
  emitLoopProvenance,
7150
7326
  skillOptOptimizationMethod
7151
7327
  };
7152
- //# sourceMappingURL=chunk-NTOV7RU5.js.map
7328
+ //# sourceMappingURL=chunk-7AN2E7BU.js.map