@tangle-network/agent-eval 0.143.0 → 0.144.1

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 (79) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +21 -8
  3. package/dist/analyst/index.d.ts +82 -17
  4. package/dist/analyst/index.d.ts.map +1 -1
  5. package/dist/analyst/index.js +4 -4
  6. package/dist/{benchmark-command-CSddvlq7.js → benchmark-command-95Zqd1e8.js} +333 -111
  7. package/dist/benchmark-command-95Zqd1e8.js.map +1 -0
  8. package/dist/benchmarks/index.d.ts +1 -1
  9. package/dist/benchmarks/index.js +1 -1
  10. package/dist/{benchmarks-Dg0B56tK.js → benchmarks-CRT4ScEA.js} +4 -4
  11. package/dist/{benchmarks-Dg0B56tK.js.map → benchmarks-CRT4ScEA.js.map} +1 -1
  12. package/dist/campaign/index.d.ts +4 -3
  13. package/dist/campaign/index.js +4 -4
  14. package/dist/{campaign-C7ZGiZYG.js → campaign-CefP3fPS.js} +12 -10
  15. package/dist/campaign-CefP3fPS.js.map +1 -0
  16. package/dist/cli.js +2 -2
  17. package/dist/contract/index.d.ts +1 -1
  18. package/dist/contract/index.js +3 -3
  19. package/dist/{default-registry-BupFSXSN.js → default-registry-SOyHB6qG.js} +3 -3
  20. package/dist/{default-registry-BupFSXSN.js.map → default-registry-SOyHB6qG.js.map} +1 -1
  21. package/dist/{dspy-rlm-engine-CJ4sN8Wx.js → dspy-rlm-engine-BR7TtB0o.js} +69 -31
  22. package/dist/dspy-rlm-engine-BR7TtB0o.js.map +1 -0
  23. package/dist/{eval-campaign-YdkpWWoT.js → eval-campaign-lI7wB9DH.js} +2 -2
  24. package/dist/{eval-campaign-YdkpWWoT.js.map → eval-campaign-lI7wB9DH.js.map} +1 -1
  25. package/dist/external-optimizer-contracts-CdmX2K2S.d.ts +165 -0
  26. package/dist/external-optimizer-contracts-CdmX2K2S.d.ts.map +1 -0
  27. package/dist/{index-BKjcUH13.d.ts → index-BZUe-ODI.d.ts} +41 -5
  28. package/dist/index-BZUe-ODI.d.ts.map +1 -0
  29. package/dist/{index-DY83M1WL.d.ts → index-BrpxwELi.d.ts} +9 -8
  30. package/dist/{index-DY83M1WL.d.ts.map → index-BrpxwELi.d.ts.map} +1 -1
  31. package/dist/{index-CaOjqCli.d.ts → index-DRI1DU5z.d.ts} +3 -3
  32. package/dist/{index-CaOjqCli.d.ts.map → index-DRI1DU5z.d.ts.map} +1 -1
  33. package/dist/index.d.ts +7 -7
  34. package/dist/index.js +12 -11
  35. package/dist/index.js.map +1 -1
  36. package/dist/{integrity-hruKe6oe.js → integrity-DY6tIbl0.js} +48 -18
  37. package/dist/integrity-DY6tIbl0.js.map +1 -0
  38. package/dist/{llm-client-B3WXSH5Y.js → llm-client-D3EoChAU.js} +7 -7
  39. package/dist/llm-client-D3EoChAU.js.map +1 -0
  40. package/dist/openapi.json +1 -1
  41. package/dist/rl.js +1 -1
  42. package/dist/{semantic-concept-judge-DJQtFr95.js → semantic-concept-judge-l8Lb3Kqz.js} +2 -2
  43. package/dist/{semantic-concept-judge-DJQtFr95.js.map → semantic-concept-judge-l8Lb3Kqz.js.map} +1 -1
  44. package/dist/{server-Cu4M3NSO.js → server-Df00sdwz.js} +3 -3
  45. package/dist/{server-Cu4M3NSO.js.map → server-Df00sdwz.js.map} +1 -1
  46. package/dist/single-run-lock-B-cFnnNn.js +1799 -0
  47. package/dist/single-run-lock-B-cFnnNn.js.map +1 -0
  48. package/dist/{skill-usage-CRqaHi0R.d.ts → skill-usage-3vUWdZ9n.d.ts} +24 -5
  49. package/dist/skill-usage-3vUWdZ9n.d.ts.map +1 -0
  50. package/dist/{skillopt-optimization-method-Di8Xkalt.js → skillopt-optimization-method-DA5mJmFE.js} +225 -347
  51. package/dist/skillopt-optimization-method-DA5mJmFE.js.map +1 -0
  52. package/dist/{skillopt-optimization-method-CwRvi-rL.d.ts → skillopt-optimization-method-DNniakI5.d.ts} +47 -9
  53. package/dist/skillopt-optimization-method-DNniakI5.d.ts.map +1 -0
  54. package/dist/supervisor-run/index.d.ts +3 -3
  55. package/dist/supervisor-run/index.js +3 -3
  56. package/dist/{supervisor-run-BEns49Nx.js → supervisor-run-DiyQVczd.js} +301 -25
  57. package/dist/supervisor-run-DiyQVczd.js.map +1 -0
  58. package/dist/types-BjMFz88h.d.ts.map +1 -1
  59. package/dist/{types-Dea6tiVI.d.ts → types-yLK8gXE9.d.ts} +15 -2
  60. package/dist/types-yLK8gXE9.d.ts.map +1 -0
  61. package/dist/wire/index.js +1 -1
  62. package/docs/campaign-proposers.md +11 -7
  63. package/docs/trace-analysis.md +43 -32
  64. package/package.json +1 -1
  65. package/dist/benchmark-command-CSddvlq7.js.map +0 -1
  66. package/dist/campaign-C7ZGiZYG.js.map +0 -1
  67. package/dist/dspy-rlm-engine-CJ4sN8Wx.js.map +0 -1
  68. package/dist/external-optimizer-contracts-nb7c_WAR.d.ts +0 -42
  69. package/dist/external-optimizer-contracts-nb7c_WAR.d.ts.map +0 -1
  70. package/dist/index-BKjcUH13.d.ts.map +0 -1
  71. package/dist/integrity-hruKe6oe.js.map +0 -1
  72. package/dist/llm-client-B3WXSH5Y.js.map +0 -1
  73. package/dist/single-run-lock-Do-8vBIJ.js +0 -1049
  74. package/dist/single-run-lock-Do-8vBIJ.js.map +0 -1
  75. package/dist/skill-usage-CRqaHi0R.d.ts.map +0 -1
  76. package/dist/skillopt-optimization-method-CwRvi-rL.d.ts.map +0 -1
  77. package/dist/skillopt-optimization-method-Di8Xkalt.js.map +0 -1
  78. package/dist/supervisor-run-BEns49Nx.js.map +0 -1
  79. package/dist/types-Dea6tiVI.d.ts.map +0 -1
@@ -1,17 +1,16 @@
1
1
  import { c as ValidationError, i as JudgeError, t as AgentEvalError } from "./errors-D-LKuDhb.js";
2
2
  import { i as CostLedger, t as CostAccountingIncompleteError } from "./cost-ledger-DMFxsLKr.js";
3
- import { f as maximumChargeForLlmRequest, l as costReceiptFromLlm, m as stripFencedJson, u as costReceiptFromLlmError } from "./llm-client-B3WXSH5Y.js";
3
+ import { f as maximumChargeForLlmRequest, l as costReceiptFromLlm, m as stripFencedJson, u as costReceiptFromLlmError } from "./llm-client-D3EoChAU.js";
4
4
  import { a as clamp01, o as combineAbortSignals, t as assertProposalFindings } from "./proposal-findings-2GIUo1et.js";
5
- import { C as safePathComponent, S as removeCredentialEnvironment, _ as assertJsonValue, b as isExternalTextCandidate, d as runWithCleanup, f as startExternalOptimizerModelProxy, g as assertExternalOptimizerModelBudget, h as sendJson, l as runExternalOptimizerProcess, m as listenLocal, n as createRunCostLedger, p as closeServer, r as fsCampaignStorage, t as acquireSingleRunLock, u as closeExternalOptimizerResources, v as assertNoCredentialValues, x as isRecord, y as isCandidateText } from "./single-run-lock-Do-8vBIJ.js";
5
+ import { A as removeCredentialEnvironment, D as isCandidateText, E as assertNoCredentialValues, M as resolveExternalOptimizerProcessLimits, N as safePathComponent, O as isExternalTextCandidate, T as assertJsonValue, d as closeExternalOptimizerResources, f as runWithCleanup, j as resolveExternalOptimizerCallbackLimits, k as isRecord, l as startExternalOptimizerCallback, n as createRunCostLedger, p as startExternalOptimizerModelProxy, r as fsCampaignStorage, t as acquireSingleRunLock, u as runExternalOptimizerProcess, v as canonicalJson, w as assertExternalOptimizerModelBudget, y as contentHash } from "./single-run-lock-B-cFnnNn.js";
6
6
  import { p as mapConcurrent } from "./ledger-core-Dxz0Rkwa.js";
7
7
  import { E as pairedBootstrap, H as weightedComposite, M as pairedRiskDifferenceScore, N as pairedSignTest, O as pairedDeltaTieFraction, T as pairedBinaryScale, d as confidenceInterval, j as pairedRiskDifferenceExact } from "./statistics-ByxzSiOM.js";
8
8
  import { t as DEFAULT_REDACTION_RULES } from "./redact-7Aq1ukl-.js";
9
9
  import { a as campaignCellCostProvenance, o as campaignCellExecutionEvidence, t as detectRewardHacking, u as projectCampaignCellQuality } from "./reward-hacking-DjTi9HLb.js";
10
10
  import { z } from "zod";
11
- import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { writeFileSync } from "node:fs";
12
12
  import { basename, isAbsolute, join } from "node:path";
13
13
  import { createHash, randomBytes } from "node:crypto";
14
- import { createServer } from "node:http";
15
14
  import { execSync } from "node:child_process";
16
15
  import { mkdir } from "node:fs/promises";
17
16
  import { homedir, tmpdir } from "node:os";
@@ -656,160 +655,6 @@ function paretoFrontierWithCrowding(candidates, objectives) {
656
655
  return crowdingDistance(frontier, objectives).sort((a, b) => b.distance - a.distance);
657
656
  }
658
657
  //#endregion
659
- //#region src/verdict-cache.ts
660
- /**
661
- * Content-addressed judge-verdict caching.
662
- *
663
- * LAW: cache JUDGE VERDICTS only — judging the same artifact with the same
664
- * judge+rubric is pure. NEVER cache agent rollouts. (A router that cached
665
- * identical fanout prompts silently destroyed best-of-N diversity; rollout
666
- * caching reintroduces that failure class. Judging has no diversity to
667
- * destroy — same artifact + same rubric ⇒ same verdict is the desired
668
- * property, not a bug.)
669
- *
670
- * The cache key is a sha-256 over the canonical JSON of everything that can
671
- * change a verdict: the artifact content, the scenario id, the judge name,
672
- * the full dimension list (key + description — the description IS the rubric
673
- * text shown to the judge), and a caller-supplied `judgeVersion`.
674
- * `judgeVersion` is REQUIRED: a judge whose prompt/model/ensemble changes
675
- * without a version bump would otherwise silently serve stale verdicts.
676
- *
677
- * Strict canonicalization (`canonicalJson`) throws on undefined / function /
678
- * symbol / non-finite numbers — an artifact that cannot be unambiguously
679
- * serialized cannot be content-addressed, and coercing it would let two
680
- * different artifacts collide on one key.
681
- */
682
- function canonicalizeAt(value, path) {
683
- if (value === null) return "null";
684
- switch (typeof value) {
685
- case "boolean": return value ? "true" : "false";
686
- case "number":
687
- if (!Number.isFinite(value)) throw new Error(`canonicalJson: non-finite number (${value}) at ${path} — ambiguity is an error, not a coercion`);
688
- return JSON.stringify(value);
689
- case "string": return JSON.stringify(value);
690
- case "undefined":
691
- case "function":
692
- case "symbol": throw new Error(`canonicalJson: ${typeof value} at ${path} — ambiguity is an error, not a coercion`);
693
- case "bigint": throw new Error(`canonicalJson: bigint at ${path} — not representable in JSON`);
694
- case "object": break;
695
- }
696
- const obj = value;
697
- if (typeof obj.toJSON === "function") return canonicalizeAt(obj.toJSON(), path);
698
- if (Array.isArray(obj)) return `[${obj.map((item, i) => canonicalizeAt(item, `${path}[${i}]`)).join(",")}]`;
699
- if (obj instanceof Map || obj instanceof Set) throw new Error(`canonicalJson: ${obj instanceof Map ? "Map" : "Set"} at ${path} — would serialize as '{}'; convert to a plain object/array first`);
700
- return `{${Object.keys(obj).sort().map((k) => `${JSON.stringify(k)}:${canonicalizeAt(obj[k], `${path}.${k}`)}`).join(",")}}`;
701
- }
702
- /**
703
- * Stable JSON stringify: object keys sorted recursively, so two semantically
704
- * equal values produce byte-identical output regardless of key insertion
705
- * order. Throws on undefined / function / symbol / NaN / ±Infinity / bigint /
706
- * Map / Set — anything JSON.stringify would coerce or drop silently.
707
- *
708
- * Distinct from `pre-registration.ts`'s `canonicalize`/`hashJson`, which are
709
- * permissive (coercion allowed) and async (web-crypto). Use THIS pair when a
710
- * hash collision or silent coercion would corrupt a cache key or attestation.
711
- */
712
- function canonicalJson(value) {
713
- return canonicalizeAt(value, "$");
714
- }
715
- /** Hex sha-256 over `canonicalJson(value)`. The content address used by the
716
- * verdict cache and report attestation. */
717
- function contentHash(value) {
718
- return createHash("sha256").update(canonicalJson(value)).digest("hex");
719
- }
720
- /** Process-local Map-backed store. */
721
- function inMemoryVerdictCache() {
722
- const entries = /* @__PURE__ */ new Map();
723
- return {
724
- get: (key) => entries.get(key),
725
- set: (key, score) => {
726
- entries.set(key, score);
727
- }
728
- };
729
- }
730
- function parseCacheLine(line, path, lineNo) {
731
- let parsed;
732
- try {
733
- parsed = JSON.parse(line);
734
- } catch (err) {
735
- throw new Error(`fileVerdictCache: corrupt JSONL at ${path}:${lineNo} — ${err instanceof Error ? err.message : String(err)}`);
736
- }
737
- const rec = parsed;
738
- if (typeof rec !== "object" || rec === null || typeof rec.key !== "string" || typeof rec.score !== "object" || rec.score === null || typeof rec.score.composite !== "number" || typeof rec.score.dimensions !== "object") throw new Error(`fileVerdictCache: invalid record shape at ${path}:${lineNo} — expected {key, score:{dimensions, composite, notes}}`);
739
- return rec;
740
- }
741
- /**
742
- * JSONL-file-backed store: the full file is loaded into an in-memory index at
743
- * construction; every `set` appends one line synchronously (durable before
744
- * the verdict is returned). A corrupt or malformed line throws at load with
745
- * file:line — a skipped line would silently re-judge (cost) or, worse, mask
746
- * a half-written file that needs operator attention.
747
- */
748
- function fileVerdictCache(path) {
749
- const entries = /* @__PURE__ */ new Map();
750
- if (existsSync(path)) {
751
- const lines = readFileSync(path, "utf8").split("\n");
752
- for (let i = 0; i < lines.length; i++) {
753
- const line = lines[i];
754
- if (line === void 0 || line.trim() === "") continue;
755
- const rec = parseCacheLine(line, path, i + 1);
756
- entries.set(rec.key, rec.score);
757
- }
758
- }
759
- return {
760
- get: (key) => entries.get(key),
761
- set: (key, score) => {
762
- appendFileSync(path, `${JSON.stringify({
763
- key,
764
- score
765
- })}\n`, "utf8");
766
- entries.set(key, score);
767
- }
768
- };
769
- }
770
- /**
771
- * Wrap a `JudgeConfig` so repeat judgments of the same artifact are served
772
- * from the store instead of re-invoking `score()`. The wrapper is generic
773
- * over the judge's own type parameters and preserves `appliesTo` — it is a
774
- * drop-in replacement anywhere a `JudgeConfig` is accepted.
775
- *
776
- * A judge that throws is NOT cached: the error propagates and the next
777
- * attempt re-judges (caching a failure would pin a transient outage forever).
778
- */
779
- function cachedJudge(judge, store, options) {
780
- if (typeof options.judgeVersion !== "string" || options.judgeVersion.trim() === "") throw new Error("cachedJudge: judgeVersion is required and must be a non-empty string");
781
- const stats = {
782
- hits: 0,
783
- misses: 0
784
- };
785
- const wrapped = {
786
- name: judge.name,
787
- dimensions: judge.dimensions,
788
- judgeVersion: options.judgeVersion,
789
- async score(input) {
790
- const key = contentHash({
791
- artifact: canonicalJson(input.artifact),
792
- scenarioId: input.scenario.id,
793
- judgeName: judge.name,
794
- dimensions: judge.dimensions,
795
- judgeVersion: options.judgeVersion
796
- });
797
- const cached = await store.get(key);
798
- if (cached !== void 0) {
799
- stats.hits += 1;
800
- return cached;
801
- }
802
- const score = await judge.score(input);
803
- await store.set(key, score);
804
- stats.misses += 1;
805
- return score;
806
- },
807
- stats: () => ({ ...stats })
808
- };
809
- if (judge.appliesTo) wrapped.appliesTo = judge.appliesTo;
810
- return wrapped;
811
- }
812
- //#endregion
813
658
  //#region src/llm-judge.ts
814
659
  /**
815
660
  * `llmJudge` — the single-LLM-call bridge that turns a rubric prompt into a
@@ -1803,135 +1648,6 @@ function designedCellIds(scenarios, reps) {
1803
1648
  return ids;
1804
1649
  }
1805
1650
  //#endregion
1806
- //#region src/campaign/external-optimizer-callback.ts
1807
- const MAX_CALLBACK_BODY_BYTES = 1e6;
1808
- async function startExternalOptimizerCallback(args) {
1809
- assertCallbackConfig(args);
1810
- args.signal?.throwIfAborted();
1811
- let evaluations = 0;
1812
- let accepting = true;
1813
- let closePromise;
1814
- const activeControllers = /* @__PURE__ */ new Set();
1815
- const activeHandlers = /* @__PURE__ */ new Set();
1816
- const server = createServer((request, response) => {
1817
- if (!accepting) {
1818
- sendJsonIfOpen(response, 503, { error: "external optimizer callback is closing" });
1819
- return;
1820
- }
1821
- const controller = new AbortController();
1822
- const abortRequest = () => {
1823
- request.destroy();
1824
- response.destroy();
1825
- };
1826
- activeControllers.add(controller);
1827
- controller.signal.addEventListener("abort", abortRequest, { once: true });
1828
- let handler;
1829
- handler = handleCallback(request, response, controller.signal, args, () => {
1830
- const accepted = args.acceptEvaluation ? args.acceptEvaluation() : evaluations + 1;
1831
- if (accepted === void 0) return void 0;
1832
- if (!Number.isSafeInteger(accepted) || accepted <= 0) throw new Error("external optimizer callback: invalid accepted evaluation count");
1833
- if (accepted > args.maxEvaluations) return void 0;
1834
- evaluations += 1;
1835
- return accepted;
1836
- }).finally(() => {
1837
- controller.signal.removeEventListener("abort", abortRequest);
1838
- activeControllers.delete(controller);
1839
- activeHandlers.delete(handler);
1840
- });
1841
- activeHandlers.add(handler);
1842
- handler.catch(() => void 0);
1843
- });
1844
- const port = await listenLocal(server);
1845
- const close = () => {
1846
- closePromise ??= closeCallbackServer();
1847
- return closePromise;
1848
- };
1849
- const onAbort = () => {
1850
- close().catch(() => void 0);
1851
- };
1852
- args.signal?.addEventListener("abort", onAbort, { once: true });
1853
- if (args.signal?.aborted) onAbort();
1854
- return {
1855
- url: `http://127.0.0.1:${port}/evaluate`,
1856
- token: args.token,
1857
- evaluations: () => evaluations,
1858
- close
1859
- };
1860
- async function closeCallbackServer() {
1861
- args.signal?.removeEventListener("abort", onAbort);
1862
- accepting = false;
1863
- const closingServer = closeServer(server);
1864
- server.closeIdleConnections?.();
1865
- for (const controller of activeControllers) controller.abort();
1866
- const [serverResult] = await Promise.allSettled([closingServer, waitForActiveHandlers(activeHandlers)]);
1867
- if (activeControllers.size !== 0 || activeHandlers.size !== 0) throw new Error("external optimizer callback closed with active request work");
1868
- if (serverResult?.status === "rejected") throw serverResult.reason;
1869
- }
1870
- }
1871
- async function handleCallback(request, response, signal, args, nextEvaluation) {
1872
- try {
1873
- if (request.method !== "POST" || request.url !== "/evaluate") {
1874
- sendJsonIfOpen(response, 404, { error: "not found" });
1875
- return;
1876
- }
1877
- if (request.headers.authorization !== `Bearer ${args.token}`) {
1878
- sendJsonIfOpen(response, 401, { error: "unauthorized" });
1879
- return;
1880
- }
1881
- const body = await readJson(request);
1882
- if (!isRecord(body) || !isExternalTextCandidate(body.candidate) || typeof body.exampleId !== "string") {
1883
- sendJsonIfOpen(response, 400, { error: "candidate and exampleId are required strings" });
1884
- return;
1885
- }
1886
- if (nextEvaluation() === void 0) {
1887
- sendJsonIfOpen(response, 429, { error: "evaluation limit reached" });
1888
- return;
1889
- }
1890
- sendJsonIfOpen(response, 200, await args.evaluate({
1891
- candidate: body.candidate,
1892
- exampleId: body.exampleId
1893
- }, signal));
1894
- } catch {
1895
- sendJsonIfOpen(response, 500, { error: "evaluation failed" });
1896
- }
1897
- }
1898
- async function waitForActiveHandlers(activeHandlers) {
1899
- while (activeHandlers.size > 0) await Promise.allSettled([...activeHandlers]);
1900
- }
1901
- function sendJsonIfOpen(response, status, body) {
1902
- if (response.destroyed || response.writableEnded) return;
1903
- sendJson(response, status, body);
1904
- }
1905
- function readJson(request) {
1906
- return new Promise((resolvePromise, reject) => {
1907
- let size = 0;
1908
- const chunks = [];
1909
- request.on("data", (chunk) => {
1910
- size += chunk.length;
1911
- if (size > MAX_CALLBACK_BODY_BYTES) {
1912
- reject(/* @__PURE__ */ new Error("callback body too large"));
1913
- request.destroy();
1914
- return;
1915
- }
1916
- chunks.push(chunk);
1917
- });
1918
- request.on("error", reject);
1919
- request.on("end", () => {
1920
- try {
1921
- resolvePromise(JSON.parse(Buffer.concat(chunks).toString("utf8")));
1922
- } catch (error) {
1923
- reject(error);
1924
- }
1925
- });
1926
- });
1927
- }
1928
- function assertCallbackConfig(args) {
1929
- if (typeof args.token !== "string" || !args.token.trim()) throw new Error("external optimizer callback: token must be non-empty");
1930
- if (!Number.isSafeInteger(args.maxEvaluations) || args.maxEvaluations <= 0) throw new Error("external optimizer callback: maxEvaluations must be a positive safe integer");
1931
- if (args.acceptEvaluation !== void 0 && typeof args.acceptEvaluation !== "function") throw new Error("external optimizer callback: acceptEvaluation must be a function");
1932
- if (typeof args.evaluate !== "function") throw new Error("external optimizer callback: evaluate must be a function");
1933
- }
1934
- //#endregion
1935
1651
  //#region src/campaign/external-optimizer-run-budget.ts
1936
1652
  function externalOptimizerRunKey(input) {
1937
1653
  if (!input.attemptId.trim() || input.attemptId.trim() !== input.attemptId) throw new Error("external optimizer attemptId must be trimmed and non-empty");
@@ -3492,7 +3208,7 @@ async function compareOptimizationMethods(opts) {
3492
3208
  throw error;
3493
3209
  }
3494
3210
  });
3495
- assertReportedCostWithinCeiling(combineCosts(optimized.map((result) => ({
3211
+ assertReportedCostWithinCeiling(combineComparisonCosts(optimized.map((result) => ({
3496
3212
  label: `method '${result.name}'`,
3497
3213
  cost: result.cost
3498
3214
  }))).totalCostUsd, opts.costCeiling, "optimization");
@@ -3577,12 +3293,12 @@ async function compareOptimizationMethods(opts) {
3577
3293
  favored
3578
3294
  };
3579
3295
  });
3580
- const optimizationCost = combineCosts(scores.map((score) => ({
3296
+ const optimizationCost = combineComparisonCosts(scores.map((score) => ({
3581
3297
  label: `method '${score.name}'`,
3582
3298
  cost: score.optimizationCost
3583
3299
  })));
3584
3300
  const testCost = costFromLedgerSummary(costLedger.summary({ phase: testCostPhase }));
3585
- const totalCost = combineCosts([{
3301
+ const totalCost = combineComparisonCosts([{
3586
3302
  label: "optimization",
3587
3303
  cost: optimizationCost
3588
3304
  }, {
@@ -3645,6 +3361,7 @@ function assertOptimizationProvenance(methodName, value) {
3645
3361
  for (const [field, entry] of [["sourceUrl", value.source.sourceUrl], ["revision", value.source.revision]]) if (entry !== void 0 && (typeof entry !== "string" || !entry.trim())) fail(`source.${field}`);
3646
3362
  if (typeof value.runId !== "string" || !value.runId.trim()) fail("runId");
3647
3363
  if (value.optimizerModel !== void 0 && (typeof value.optimizerModel !== "string" || !value.optimizerModel.trim() || value.optimizerModel.trim() !== value.optimizerModel)) fail("optimizerModel");
3364
+ if (value.optimizerCallRef !== void 0 && (typeof value.optimizerCallRef !== "string" || !value.optimizerCallRef.trim() || value.optimizerCallRef.trim() !== value.optimizerCallRef)) fail("optimizerCallRef");
3648
3365
  if (typeof value.resumed !== "boolean") fail("resumed");
3649
3366
  if (!Number.isSafeInteger(value.evaluationCount) || value.evaluationCount < 0) fail("evaluationCount");
3650
3367
  if (typeof value.artifactDir !== "string" || !value.artifactDir.trim()) fail("artifactDir");
@@ -3667,6 +3384,24 @@ function assertOptimizationProvenance(methodName, value) {
3667
3384
  if (value.tokenUsage.reasoningTokens !== void 0 && value.tokenUsage.reasoningTokens > value.tokenUsage.outputTokens) fail("tokenUsage.reasoningTokens");
3668
3385
  if (value.tokenUsage.totalTokens !== value.tokenUsage.inputTokens + value.tokenUsage.outputTokens) fail("tokenUsage.totalTokens");
3669
3386
  }
3387
+ if (value.observations !== void 0) {
3388
+ if (value.observations.scope !== "callback-submitted-candidates" || typeof value.observations.path !== "string" || !value.observations.path.trim() || typeof value.observations.sha256 !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.observations.sha256)) fail("observations");
3389
+ for (const field of [
3390
+ "submittedCandidates",
3391
+ "evaluations",
3392
+ "refusals"
3393
+ ]) if (!Number.isSafeInteger(value.observations[field]) || value.observations[field] < 0) fail(`observations.${field}`);
3394
+ }
3395
+ if (value.modelExecutions !== void 0) {
3396
+ if (value.modelExecutions.scope !== "runtime-model-calls" || typeof value.modelExecutions.path !== "string" || !value.modelExecutions.path.trim() || typeof value.modelExecutions.sha256 !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.modelExecutions.sha256)) fail("modelExecutions");
3397
+ for (const field of [
3398
+ "calls",
3399
+ "succeeded",
3400
+ "failed"
3401
+ ]) if (!Number.isSafeInteger(value.modelExecutions[field]) || value.modelExecutions[field] < 0) fail(`modelExecutions.${field}`);
3402
+ if (value.modelExecutions.calls !== value.modelExecutions.succeeded + value.modelExecutions.failed) fail("modelExecutions.calls");
3403
+ }
3404
+ if (value.optimizerModel === void 0 !== (value.optimizerCallRef === void 0) || value.optimizerModel === void 0 !== (value.modelExecutions === void 0)) fail("optimizerModel execution provenance");
3670
3405
  }
3671
3406
  function assertComparisonControls(opts, seed, resamples, confidence) {
3672
3407
  if (opts.optimizationRunOptions && "costCeiling" in opts.optimizationRunOptions) throw new Error("compareOptimizationMethods: optimizationRunOptions.costCeiling is not supported; costCeiling covers optimization and final scoring");
@@ -3810,6 +3545,7 @@ function callerDispatchRef(opts) {
3810
3545
  function costFromLedgerSummary(summary) {
3811
3546
  const cost = {
3812
3547
  totalCostUsd: summary.totalCostUsd,
3548
+ costProvenance: structuredClone(summary.costProvenance),
3813
3549
  accountingComplete: summary.accountingComplete,
3814
3550
  incompleteReasons: [...summary.incompleteReasons]
3815
3551
  };
@@ -3836,19 +3572,37 @@ function optimizationTokenUsageFromSummary(summary, receipts) {
3836
3572
  calls: summary.totalCalls
3837
3573
  };
3838
3574
  }
3839
- function combineCosts(entries) {
3840
- return {
3841
- totalCostUsd: entries.reduce((total, entry) => total + entry.cost.totalCostUsd, 0),
3575
+ /** Combine method costs without turning one unknown bill into a known total. */
3576
+ function combineComparisonCosts(entries) {
3577
+ const totalCostUsd = entries.reduce((total, entry) => total + entry.cost.totalCostUsd, 0);
3578
+ const cost = {
3579
+ totalCostUsd,
3580
+ costProvenance: entries.some((entry) => entry.cost.costProvenance.kind === "uncaptured") ? {
3581
+ kind: "uncaptured",
3582
+ usd: null
3583
+ } : entries.every((entry) => entry.cost.costProvenance.kind === "observed") ? {
3584
+ kind: "observed",
3585
+ usd: totalCostUsd
3586
+ } : {
3587
+ kind: "estimated",
3588
+ usd: totalCostUsd
3589
+ },
3842
3590
  accountingComplete: entries.every((entry) => entry.cost.accountingComplete),
3843
3591
  incompleteReasons: entries.flatMap((entry) => entry.cost.incompleteReasons.map((reason) => `${entry.label}: ${reason}`))
3844
3592
  };
3593
+ assertComparisonCost(cost, "combined cost");
3594
+ return cost;
3845
3595
  }
3846
3596
  function assertComparisonCost(cost, label) {
3847
3597
  if (!cost || typeof cost !== "object") throw new Error(`compareOptimizationMethods: ${label} returned no cost`);
3848
3598
  if (!Number.isFinite(cost.totalCostUsd) || cost.totalCostUsd < 0) throw new Error(`compareOptimizationMethods: ${label} returned an invalid totalCostUsd`);
3599
+ const provenance = cost.costProvenance;
3600
+ if (!provenance || typeof provenance !== "object" || provenance.kind !== "observed" && provenance.kind !== "estimated" && provenance.kind !== "uncaptured" || (provenance.kind === "uncaptured" ? provenance.usd !== null : !Number.isFinite(provenance.usd) || provenance.usd < 0)) throw new Error(`compareOptimizationMethods: ${label} returned invalid costProvenance`);
3601
+ if (provenance.kind !== "uncaptured" && provenance.usd !== cost.totalCostUsd) throw new Error(`compareOptimizationMethods: ${label} returned costProvenance inconsistent with totalCostUsd`);
3849
3602
  if (typeof cost.accountingComplete !== "boolean") throw new Error(`compareOptimizationMethods: ${label} returned invalid accountingComplete`);
3850
3603
  if (!Array.isArray(cost.incompleteReasons) || cost.incompleteReasons.some((reason) => typeof reason !== "string" || reason.trim().length === 0)) throw new Error(`compareOptimizationMethods: ${label} returned invalid incompleteReasons`);
3851
3604
  if (cost.accountingComplete !== (cost.incompleteReasons.length === 0)) throw new Error(`compareOptimizationMethods: ${label} returned inconsistent cost completeness and reasons`);
3605
+ if (cost.accountingComplete && provenance.kind === "uncaptured") throw new Error(`compareOptimizationMethods: ${label} cannot mark uncaptured cost as complete`);
3852
3606
  }
3853
3607
  //#endregion
3854
3608
  //#region src/campaign/external-text-optimization.ts
@@ -4097,16 +3851,23 @@ function externalTextOptimizationMethod(config) {
4097
3851
  status: "completed"
4098
3852
  }
4099
3853
  });
3854
+ const measuredCost = combineComparisonCosts([{
3855
+ label: "evaluation",
3856
+ cost: evaluationCost
3857
+ }, {
3858
+ label: "optimizer",
3859
+ cost: optimizerCost
3860
+ }]);
4100
3861
  return {
4101
3862
  winnerSurface: decodeExternalTextCandidate(result.bestCandidate),
4102
3863
  cost: {
4103
- totalCostUsd: evaluationCost.totalCostUsd + optimizerCost.totalCostUsd,
4104
- accountingComplete: evaluationCost.accountingComplete && optimizerCost.accountingComplete && externalCostReason === void 0,
4105
- incompleteReasons: [
4106
- ...evaluationCost.incompleteReasons.map((reason) => `evaluation: ${reason}`),
4107
- ...optimizerCost.incompleteReasons.map((reason) => `optimizer: ${reason}`),
4108
- ...externalCostReason ? [`optimizer: external spend is not observed: ${externalCostReason}`] : []
4109
- ]
3864
+ ...measuredCost,
3865
+ ...externalCostReason ? { costProvenance: {
3866
+ kind: "uncaptured",
3867
+ usd: null
3868
+ } } : {},
3869
+ accountingComplete: measuredCost.accountingComplete && externalCostReason === void 0,
3870
+ incompleteReasons: [...measuredCost.incompleteReasons, ...externalCostReason ? [`optimizer: external spend is not observed: ${externalCostReason}`] : []]
4110
3871
  },
4111
3872
  durationMs: Date.now() - started,
4112
3873
  provenance: {
@@ -5339,8 +5100,8 @@ const TOKEN_USAGE_FIELDS = [
5339
5100
  "calls"
5340
5101
  ];
5341
5102
  function assertPriorExternalOptimizerUsage(summary, budget, name) {
5342
- if (!summary.accountingComplete || !summary.usageComplete) throw new Error(`${name}: cannot resume optimizer-model work with incomplete prior cost or usage`);
5343
- if (summary.totalCalls > budget.maxRequests || summary.totalCostUsd > budget.maxCostUsd + Number.EPSILON) throw new Error(`${name}: prior optimizer-model usage exceeds the configured budget`);
5103
+ if (!summary.usageComplete || budget.maxCostUsd !== void 0 && !summary.accountingComplete) throw new Error(`${name}: cannot resume optimizer-model work with incomplete bounded usage`);
5104
+ if (summary.totalCalls > budget.maxRequests || budget.maxCostUsd !== void 0 && summary.totalCostUsd > budget.maxCostUsd + Number.EPSILON) throw new Error(`${name}: prior optimizer-model usage exceeds the configured budget`);
5344
5105
  }
5345
5106
  function assertExternalOptimizerTokenUsage(usage, name, optimizer) {
5346
5107
  if (usage === void 0) return;
@@ -5355,12 +5116,85 @@ function assertExternalOptimizerCompletionCount(upstream, requestAttempts, succe
5355
5116
  if (upstream.requestAttempts !== void 0 && upstream.requestAttempts !== requestAttempts) throw new Error(`${name}: ${optimizer} reported ${upstream.requestAttempts} model attempts but the proxy received ${requestAttempts}`);
5356
5117
  }
5357
5118
  //#endregion
5119
+ //#region src/campaign/external-optimizer-observations.ts
5120
+ /** Append-only observation log for one external-optimizer attempt. */
5121
+ function openExternalOptimizerObservationLog(input) {
5122
+ if (input.storage.read(input.path) !== void 0 || input.storage.exists(input.path)) throw new Error(`external optimizer observation log already exists at '${input.path}'`);
5123
+ input.storage.write(input.path, "");
5124
+ let revision = 0;
5125
+ const counts = {
5126
+ submittedCandidates: 0,
5127
+ evaluations: 0,
5128
+ refusals: 0
5129
+ };
5130
+ return {
5131
+ observe(observation) {
5132
+ const expectedSequence = counts.submittedCandidates + counts.evaluations + counts.refusals + 1;
5133
+ if (observation.sequence !== expectedSequence) throw new Error(`external optimizer observation log expected sequence ${expectedSequence}, got ${observation.sequence}`);
5134
+ const line = `${canonicalJson(observation)}\n`;
5135
+ const next = input.storage.append(input.path, line, revision);
5136
+ if (next === void 0) throw new Error(`external optimizer observation log changed concurrently at '${input.path}'`);
5137
+ revision = next;
5138
+ if (observation.kind === "proposal") counts.submittedCandidates += 1;
5139
+ else if (observation.kind === "evaluation") counts.evaluations += 1;
5140
+ else counts.refusals += 1;
5141
+ },
5142
+ summary() {
5143
+ const contents = input.storage.read(input.path) ?? "";
5144
+ const bytes = new TextEncoder().encode(contents);
5145
+ if (bytes.byteLength !== revision) throw new Error(`external optimizer observation log revision changed at '${input.path}'`);
5146
+ return {
5147
+ scope: "callback-submitted-candidates",
5148
+ path: input.path,
5149
+ sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
5150
+ ...counts
5151
+ };
5152
+ }
5153
+ };
5154
+ }
5155
+ /** Append-only opaque Runtime execution records for one optimizer attempt. */
5156
+ function openExternalOptimizerExecutionLog(input) {
5157
+ if (input.storage.read(input.path) !== void 0 || input.storage.exists(input.path)) throw new Error(`external optimizer execution log already exists at '${input.path}'`);
5158
+ input.storage.write(input.path, "");
5159
+ let revision = 0;
5160
+ const counts = {
5161
+ calls: 0,
5162
+ succeeded: 0,
5163
+ failed: 0
5164
+ };
5165
+ return {
5166
+ observe(observation) {
5167
+ if (observation.sequence !== counts.calls + 1) throw new Error(`external optimizer execution log expected sequence ${counts.calls + 1}, got ${observation.sequence}`);
5168
+ const line = `${canonicalJson(observation)}\n`;
5169
+ const next = input.storage.append(input.path, line, revision);
5170
+ if (next === void 0) throw new Error(`external optimizer execution log changed concurrently at '${input.path}'`);
5171
+ revision = next;
5172
+ counts.calls += 1;
5173
+ if (observation.succeeded) counts.succeeded += 1;
5174
+ else counts.failed += 1;
5175
+ },
5176
+ summary() {
5177
+ const contents = input.storage.read(input.path) ?? "";
5178
+ const bytes = new TextEncoder().encode(contents);
5179
+ if (bytes.byteLength !== revision) throw new Error(`external optimizer execution log revision changed at '${input.path}'`);
5180
+ if (counts.calls !== counts.succeeded + counts.failed) throw new Error(`external optimizer execution log counts disagree at '${input.path}'`);
5181
+ return {
5182
+ scope: "runtime-model-calls",
5183
+ path: input.path,
5184
+ sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
5185
+ ...counts
5186
+ };
5187
+ }
5188
+ };
5189
+ }
5190
+ //#endregion
5358
5191
  //#region src/campaign/external-optimizer-run-config.ts
5359
5192
  function externalOptimizerRunnerIdentity(runner, module) {
5360
5193
  return {
5361
5194
  command: runner?.command ?? "python",
5362
5195
  args: [...runner?.args ?? ["-m", module]],
5363
- environment: removeCredentialEnvironment(runner?.env ?? {})
5196
+ environment: removeCredentialEnvironment(runner?.env ?? {}),
5197
+ limits: resolveExternalOptimizerProcessLimits(runner?.limits)
5364
5198
  };
5365
5199
  }
5366
5200
  function snapshotExternalOptimizerRunner(runner) {
@@ -5368,7 +5202,8 @@ function snapshotExternalOptimizerRunner(runner) {
5368
5202
  return {
5369
5203
  ...runner,
5370
5204
  ...runner.args ? { args: [...runner.args] } : {},
5371
- ...runner.env ? { env: { ...runner.env } } : {}
5205
+ ...runner.env ? { env: { ...runner.env } } : {},
5206
+ ...runner.limits ? { limits: { ...runner.limits } } : {}
5372
5207
  };
5373
5208
  }
5374
5209
  function snapshotJson(value, label) {
@@ -5446,27 +5281,16 @@ function assertExternalOptimizerRuntimeIdentity(value, expectedPackage, label) {
5446
5281
  //#region src/campaign/optimizer-model.ts
5447
5282
  function assertOptimizerModel(value, label) {
5448
5283
  if (!value || typeof value !== "object") throw new Error(`${label} is required`);
5449
- for (const field of [
5450
- "model",
5451
- "baseUrl",
5452
- "apiKey"
5453
- ]) {
5454
- const item = value[field];
5455
- if (typeof item !== "string" || !item.trim() || item.trim() !== item) throw new Error(`${label}.${field} must be trimmed and non-empty`);
5456
- }
5457
- let url;
5458
- try {
5459
- url = new URL(value.baseUrl);
5460
- } catch {
5461
- throw new Error(`${label}.baseUrl must be an absolute HTTP URL`);
5462
- }
5463
- if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) throw new Error(`${label}.baseUrl must use HTTP or HTTPS without credentials, query, or fragment`);
5284
+ for (const [field, item] of [["model", value.model], ["callRef", value.callRef]]) if (typeof item !== "string" || !item.trim() || item.trim() !== item) throw new Error(`${label}.${field} must be trimmed and non-empty`);
5285
+ if (typeof value.call !== "function") throw new Error(`${label}.call must be a function`);
5464
5286
  assertExternalOptimizerModelBudget(value.budget, `${label}.budget`);
5465
5287
  }
5466
5288
  function snapshotOptimizerModel(value) {
5467
5289
  return {
5468
- ...value,
5469
- budget: structuredClone(value.budget)
5290
+ model: value.model,
5291
+ budget: structuredClone(value.budget),
5292
+ call: value.call,
5293
+ callRef: value.callRef
5470
5294
  };
5471
5295
  }
5472
5296
  const MAX_TIMER_DELAY_MS$1 = 2147483647;
@@ -5477,6 +5301,7 @@ function snapshotGepaOptimizationConfig(config) {
5477
5301
  recipe: structuredClone(config.recipe),
5478
5302
  ...config.engineModules ? { engineModules: [...config.engineModules] } : {},
5479
5303
  ...config.optimizer ? { optimizer: snapshotOptimizerModel(config.optimizer) } : {},
5304
+ ...config.evaluationCallbackLimits ? { evaluationCallbackLimits: { ...config.evaluationCallbackLimits } } : {},
5480
5305
  ...runner ? { runner } : {}
5481
5306
  };
5482
5307
  }
@@ -5495,6 +5320,7 @@ function assertGepaOptimizationConfig(config) {
5495
5320
  const evidenceLimit = config.maxEvidenceChars ?? 1e5;
5496
5321
  if (JSON.stringify(config.objective).length > evidenceLimit || JSON.stringify(config.background ?? "").length > evidenceLimit) throw new Error("gepaOptimizationMethod: objective and background must each fit maxEvidenceChars");
5497
5322
  assertEngineModules(config.engineModules);
5323
+ resolveExternalOptimizerCallbackLimits(config.evaluationCallbackLimits, "gepaOptimizationMethod: evaluationCallbackLimits");
5498
5324
  if (config.optimizer !== void 0) {
5499
5325
  assertOptimizerModel(config.optimizer, "gepaOptimizationMethod: optimizer");
5500
5326
  if (config.engineModules?.length) throw new Error("gepaOptimizationMethod: optimizer cannot be combined with engineModules because proxied reflection requires the built-in GEPA engine");
@@ -5581,7 +5407,7 @@ function assertEngineRun(run, label) {
5581
5407
  function assertEngineOptions(run, label) {
5582
5408
  if (!run || typeof run !== "object") throw new Error(`gepaOptimizationMethod: ${label} is required`);
5583
5409
  if (typeof run.engine !== "string" || !run.engine.trim() || run.engine.trim() !== run.engine) throw new Error(`gepaOptimizationMethod: ${label}.engine must be a trimmed non-empty string`);
5584
- if (!Number.isFinite(run.maxProposerCostUsd) || run.maxProposerCostUsd <= 0) throw new Error(`gepaOptimizationMethod: ${label}.maxProposerCostUsd must be a positive finite number`);
5410
+ if (run.maxProposerCostUsd !== void 0 && (!Number.isFinite(run.maxProposerCostUsd) || run.maxProposerCostUsd <= 0)) throw new Error(`gepaOptimizationMethod: ${label}.maxProposerCostUsd must be a positive finite number when supplied`);
5585
5411
  if (run.maxConcurrency !== void 0) assertPositiveSafeInteger$1(run.maxConcurrency, `${label}.maxConcurrency`);
5586
5412
  if (run.stopAtScore !== void 0 && !Number.isFinite(run.stopAtScore)) throw new Error(`gepaOptimizationMethod: ${label}.stopAtScore must be a finite number`);
5587
5413
  if (run.sandbox !== void 0 && typeof run.sandbox !== "boolean") throw new Error(`gepaOptimizationMethod: ${label}.sandbox must be a boolean`);
@@ -5706,9 +5532,10 @@ function gepaOptimizationMethod(config) {
5706
5532
  selectionSet,
5707
5533
  maxCandidateChars,
5708
5534
  maxEvidenceChars,
5535
+ evaluationCallbackLimits: resolveExternalOptimizerCallbackLimits(config.evaluationCallbackLimits),
5709
5536
  optimizerModel: config.optimizer ? {
5710
5537
  model: config.optimizer.model,
5711
- baseUrl: config.optimizer.baseUrl,
5538
+ callRef: config.optimizer.callRef,
5712
5539
  budget: config.optimizer.budget
5713
5540
  } : null,
5714
5541
  runner: externalOptimizerRunnerIdentity(bridgeRunner, "agent_eval_rpc.gepa_bridge"),
@@ -5727,6 +5554,14 @@ function gepaOptimizationMethod(config) {
5727
5554
  attemptId,
5728
5555
  maxEvaluations: evaluationLimit
5729
5556
  });
5557
+ const observationLog = openExternalOptimizerObservationLog({
5558
+ storage,
5559
+ path: `${runDir}/observations-${attemptId}.jsonl`
5560
+ });
5561
+ const executionLog = config.optimizer ? openExternalOptimizerExecutionLog({
5562
+ storage,
5563
+ path: `${runDir}/model-executions-${attemptId}.jsonl`
5564
+ }) : void 0;
5730
5565
  const scenarioById = mapExternalScenarios(input.trainScenarios, input.selectionScenarios, "GEPA bridge");
5731
5566
  const evaluate = createExternalTextEvaluator({
5732
5567
  input,
@@ -5746,6 +5581,8 @@ function gepaOptimizationMethod(config) {
5746
5581
  maxEvaluations: evaluationLimit,
5747
5582
  acceptEvaluation: () => runBudget.acceptEvaluation(),
5748
5583
  evaluate,
5584
+ observe: observationLog.observe,
5585
+ ...config.evaluationCallbackLimits ? { limits: config.evaluationCallbackLimits } : {},
5749
5586
  ...signal ? { signal } : {}
5750
5587
  });
5751
5588
  const runnerEnv = bridgeRunner?.env ?? {};
@@ -5765,8 +5602,9 @@ function gepaOptimizationMethod(config) {
5765
5602
  });
5766
5603
  assertPriorExternalOptimizerUsage(priorOptimizerUsage, config.optimizer.budget, name);
5767
5604
  modelProxy = await startExternalOptimizerModelProxy({
5768
- upstreamBaseUrl: config.optimizer.baseUrl,
5769
- upstreamApiKey: config.optimizer.apiKey,
5605
+ call: config.optimizer.call,
5606
+ callRef: config.optimizer.callRef,
5607
+ recordExecution: executionLog.observe,
5770
5608
  model: config.optimizer.model,
5771
5609
  budget: config.optimizer.budget,
5772
5610
  costLedger,
@@ -5775,7 +5613,7 @@ function gepaOptimizationMethod(config) {
5775
5613
  tags: { ...runBudget.attemptTags },
5776
5614
  initialUsage: {
5777
5615
  requests: priorOptimizerUsage.totalCalls,
5778
- costUsd: priorOptimizerUsage.totalCostUsd
5616
+ ...priorOptimizerUsage.costProvenance.kind === "uncaptured" ? {} : { costUsd: priorOptimizerUsage.totalCostUsd }
5779
5617
  },
5780
5618
  ...signal ? { signal } : {}
5781
5619
  });
@@ -5854,17 +5692,31 @@ function gepaOptimizationMethod(config) {
5854
5692
  });
5855
5693
  const optimizerCost = costFromLedgerSummary(optimizerSummary);
5856
5694
  const reportedProposerCost = result.proposerCostUsd ?? 0;
5857
- if (modelProxy) assertExternalOptimizerCompletionCount(result.tokenUsage, modelProxy.requestAttempts(), modelProxy.successfulCompletions(), name, "GEPA");
5695
+ if (modelProxy) {
5696
+ modelProxy.assertExecutionComplete();
5697
+ assertExternalOptimizerCompletionCount(result.tokenUsage, modelProxy.requestAttempts(), modelProxy.successfulCompletions(), name, "GEPA");
5698
+ }
5858
5699
  const tokenUsage = modelProxy ? optimizationTokenUsageFromSummary(optimizerSummary, optimizerReceipts) : void 0;
5859
5700
  const runtime = observedExternalOptimizerRuntime(runtimeIdentity);
5701
+ const meteredCost = modelProxy ? combineComparisonCosts([{
5702
+ label: "evaluation",
5703
+ cost: evaluationCost
5704
+ }, {
5705
+ label: "optimizer model",
5706
+ cost: optimizerCost
5707
+ }]) : void 0;
5708
+ const externalTotalCostUsd = evaluationCost.totalCostUsd + reportedProposerCost;
5860
5709
  return {
5861
5710
  winnerSurface: decodeExternalTextCandidate(result.bestCandidate),
5862
- cost: modelProxy ? {
5863
- totalCostUsd: evaluationCost.totalCostUsd + optimizerCost.totalCostUsd,
5864
- accountingComplete: evaluationCost.accountingComplete && optimizerCost.accountingComplete,
5865
- incompleteReasons: [...evaluationCost.incompleteReasons.map((reason) => `evaluation: ${reason}`), ...optimizerCost.incompleteReasons.map((reason) => `optimizer model: ${reason}`)]
5866
- } : {
5867
- totalCostUsd: evaluationCost.totalCostUsd + reportedProposerCost,
5711
+ cost: modelProxy ? meteredCost : {
5712
+ totalCostUsd: externalTotalCostUsd,
5713
+ costProvenance: result.proposerCostAccounting === "reported" && evaluationCost.costProvenance.kind !== "uncaptured" ? {
5714
+ kind: "estimated",
5715
+ usd: externalTotalCostUsd
5716
+ } : {
5717
+ kind: "uncaptured",
5718
+ usd: null
5719
+ },
5868
5720
  accountingComplete: false,
5869
5721
  incompleteReasons: [
5870
5722
  ...evaluationCost.incompleteReasons,
@@ -5875,13 +5727,18 @@ function gepaOptimizationMethod(config) {
5875
5727
  durationMs: Date.now() - started,
5876
5728
  provenance: {
5877
5729
  ...runtime,
5878
- ...config.optimizer ? { optimizerModel: config.optimizer.model } : {},
5730
+ ...config.optimizer ? {
5731
+ optimizerModel: config.optimizer.model,
5732
+ optimizerCallRef: config.optimizer.callRef
5733
+ } : {},
5879
5734
  compatibleRunId,
5880
5735
  runId,
5881
5736
  resumed: result.resumed,
5882
5737
  evaluationCount: runBudget.acceptedEvaluations(),
5883
5738
  artifactDir: outputDir,
5884
- ...tokenUsage ? { tokenUsage } : {}
5739
+ ...tokenUsage ? { tokenUsage } : {},
5740
+ observations: observationLog.summary(),
5741
+ ...executionLog ? { modelExecutions: executionLog.summary() } : {}
5885
5742
  }
5886
5743
  };
5887
5744
  }
@@ -6954,6 +6811,7 @@ function snapshotSkillOptOptimizationConfig(config) {
6954
6811
  ...config,
6955
6812
  trainer: structuredClone(config.trainer),
6956
6813
  optimizer: snapshotOptimizerModel(config.optimizer),
6814
+ ...config.evaluationCallbackLimits ? { evaluationCallbackLimits: { ...config.evaluationCallbackLimits } } : {},
6957
6815
  ...runner ? { runner } : {}
6958
6816
  };
6959
6817
  }
@@ -7006,6 +6864,7 @@ function assertSkillOptOptimizationConfig(config) {
7006
6864
  assertJsonValue(config.trainer.overrides ?? {}, "skillOptOptimizationMethod: trainer.overrides");
7007
6865
  assertNoCredentialValues(config.trainer.overrides ?? {}, "skillOptOptimizationMethod: trainer.overrides", "optimizer");
7008
6866
  assertOptimizerModel(config.optimizer, "skillOptOptimizationMethod: optimizer");
6867
+ resolveExternalOptimizerCallbackLimits(config.evaluationCallbackLimits, "skillOptOptimizationMethod: evaluationCallbackLimits");
7009
6868
  const evidenceLimit = config.maxEvidenceChars ?? 1e5;
7010
6869
  if (JSON.stringify(config.objective).length > evidenceLimit || JSON.stringify(config.background ?? "").length > evidenceLimit) throw new Error("skillOptOptimizationMethod: objective and background must each fit maxEvidenceChars");
7011
6870
  }
@@ -7076,7 +6935,7 @@ function skillOptOptimizationMethod(config) {
7076
6935
  background: config.background ?? "",
7077
6936
  optimizerModel: {
7078
6937
  model: config.optimizer.model,
7079
- baseUrl: config.optimizer.baseUrl,
6938
+ callRef: config.optimizer.callRef,
7080
6939
  budget: config.optimizer.budget
7081
6940
  },
7082
6941
  seedCandidate: input.baselineSurface,
@@ -7086,6 +6945,7 @@ function skillOptOptimizationMethod(config) {
7086
6945
  hardScoreThreshold: config.hardScoreThreshold ?? 1,
7087
6946
  maxCandidateChars,
7088
6947
  maxEvidenceChars,
6948
+ evaluationCallbackLimits: resolveExternalOptimizerCallbackLimits(config.evaluationCallbackLimits),
7089
6949
  runner: externalOptimizerRunnerIdentity(bridgeRunner, "agent_eval_rpc.skillopt_bridge")
7090
6950
  };
7091
6951
  const compatibleRunId = externalOptimizerCompatibleRunKey(runMaterial);
@@ -7101,6 +6961,14 @@ function skillOptOptimizationMethod(config) {
7101
6961
  attemptId,
7102
6962
  maxEvaluations: config.maxEvaluations
7103
6963
  });
6964
+ const observationLog = openExternalOptimizerObservationLog({
6965
+ storage,
6966
+ path: `${runDir}/observations-${attemptId}.jsonl`
6967
+ });
6968
+ const executionLog = openExternalOptimizerExecutionLog({
6969
+ storage,
6970
+ path: `${runDir}/model-executions-${attemptId}.jsonl`
6971
+ });
7104
6972
  const scenarioById = mapExternalScenarios(input.trainScenarios, input.selectionScenarios, "SkillOpt bridge");
7105
6973
  const evaluate = createExternalTextEvaluator({
7106
6974
  input,
@@ -7120,6 +6988,8 @@ function skillOptOptimizationMethod(config) {
7120
6988
  maxEvaluations: config.maxEvaluations,
7121
6989
  acceptEvaluation: () => runBudget.acceptEvaluation(),
7122
6990
  evaluate,
6991
+ observe: observationLog.observe,
6992
+ ...config.evaluationCallbackLimits ? { limits: config.evaluationCallbackLimits } : {},
7123
6993
  ...signal ? { signal } : {}
7124
6994
  });
7125
6995
  const runnerEnv = bridgeRunner?.env ?? {};
@@ -7138,8 +7008,9 @@ function skillOptOptimizationMethod(config) {
7138
7008
  });
7139
7009
  assertPriorExternalOptimizerUsage(priorOptimizerUsage, config.optimizer.budget, name);
7140
7010
  const modelProxy = await startExternalOptimizerModelProxy({
7141
- upstreamBaseUrl: config.optimizer.baseUrl,
7142
- upstreamApiKey: config.optimizer.apiKey,
7011
+ call: config.optimizer.call,
7012
+ callRef: config.optimizer.callRef,
7013
+ recordExecution: executionLog.observe,
7143
7014
  model: config.optimizer.model,
7144
7015
  budget: config.optimizer.budget,
7145
7016
  costLedger,
@@ -7148,7 +7019,7 @@ function skillOptOptimizationMethod(config) {
7148
7019
  tags: { ...runBudget.attemptTags },
7149
7020
  initialUsage: {
7150
7021
  requests: priorOptimizerUsage.totalCalls,
7151
- costUsd: priorOptimizerUsage.totalCostUsd
7022
+ ...priorOptimizerUsage.costProvenance.kind === "uncaptured" ? {} : { costUsd: priorOptimizerUsage.totalCostUsd }
7152
7023
  },
7153
7024
  ...signal ? { signal } : {}
7154
7025
  });
@@ -7237,32 +7108,39 @@ function skillOptOptimizationMethod(config) {
7237
7108
  tags: runBudget.runTags
7238
7109
  });
7239
7110
  const optimizerCost = costFromLedgerSummary(optimizerUsage);
7111
+ modelProxy.assertExecutionComplete();
7240
7112
  assertExternalOptimizerCompletionCount(result.tokenUsage, modelProxy.requestAttempts(), modelProxy.successfulCompletions(), name, "SkillOpt");
7241
7113
  const tokenUsage = optimizationTokenUsageFromSummary(optimizerUsage, optimizerReceipts);
7242
7114
  const runtime = observedExternalOptimizerRuntime(runtimeIdentity);
7115
+ const combinedCost = combineComparisonCosts([{
7116
+ label: "evaluation",
7117
+ cost: evaluationCost
7118
+ }, {
7119
+ label: "optimizer model",
7120
+ cost: optimizerCost
7121
+ }]);
7243
7122
  return {
7244
7123
  winnerSurface: result.bestCandidate,
7245
- cost: {
7246
- totalCostUsd: evaluationCost.totalCostUsd + optimizerCost.totalCostUsd,
7247
- accountingComplete: evaluationCost.accountingComplete && optimizerCost.accountingComplete,
7248
- incompleteReasons: [...evaluationCost.incompleteReasons.map((reason) => `evaluation: ${reason}`), ...optimizerCost.incompleteReasons.map((reason) => `optimizer model: ${reason}`)]
7249
- },
7124
+ cost: combinedCost,
7250
7125
  durationMs: Date.now() - started,
7251
7126
  provenance: {
7252
7127
  ...runtime,
7253
7128
  optimizerModel: config.optimizer.model,
7129
+ optimizerCallRef: config.optimizer.callRef,
7254
7130
  compatibleRunId,
7255
7131
  runId,
7256
7132
  resumed: result.resumed,
7257
7133
  evaluationCount: runBudget.acceptedEvaluations(),
7258
7134
  artifactDir: outputDir,
7259
- ...tokenUsage ? { tokenUsage } : {}
7135
+ ...tokenUsage ? { tokenUsage } : {},
7136
+ observations: observationLog.summary(),
7137
+ modelExecutions: executionLog.summary()
7260
7138
  }
7261
7139
  };
7262
7140
  }
7263
7141
  };
7264
7142
  }
7265
7143
  //#endregion
7266
- export { campaignScenarioIdentity as $, assertOptimizationResult as A, pairedDeltaTest as At, surfaceHash as B, detectScale as C, paretoFrontier as Ct, runCanaries as D, decidePairedPromotion as Dt, pairHoldout as E, recoverTruncatedJson as Et, assertComponentSurface as F, summarizeBackendIntegrity as Ft, buildReflectionPrompt as G, campaignMeanComposite as H, codeSurfaceIdentityMaterial as I, JudgeParseError as It, runCampaign as J, parseReflectionResponse as K, componentSurfaceIdentityMaterial as L, costFromLedgerSummary as M, assertRealAgentReceipts as Mt, optimizationTokenUsageFromSummary as N, assertRealBackend as Nt, composeGate as O, pairedDecisionShape as Ot, assertCodeSurfaceIdentity as P, summarizeAgentReceiptIntegrity as Pt, assertCampaignSplitIdentity as Q, renderSurfaceDiff as R, defaultProductionGate as S, dominates as St, heldoutSignificance as T, scalarScore as Tt, compareRankKeys as U, campaignBreakdown as V, DEFAULT_MUTATION_PRIMITIVES as W, tangleTracesRoot as X, resolveRunDir as Y, assertCampaignDesign as Z, buildEvidenceVector as _, canonicalJson as _t, emitLoopProvenance as a, createReferenceEquivalenceJudge as at, powerPreflight as b, inMemoryVerdictCache as bt, provenanceRecordPath as c, redTeamDataset as ct, runImprovementLoop as d, toolNamesForRun as dt, campaignSplitDigest as et, runOptimization as f, Dataset as ft, gepaOptimizationMethod as g, cachedJudge as gt, labelTrustRank as h, llmJudge as ht, canonicalDigest as i, REFERENCE_EQUIVALENCE_JUDGE_VERSION as it, compareOptimizationMethods as j, BackendIntegrityError as jt, externalTextOptimizationMethod as k, minimumPairsForPairedDeltaTest as kt, provenanceSpansPath as l, redTeamReport as lt, isProposedCandidate as m, hashScenarios as mt, buildLoopProvenanceRecord as n, openAutoPr as nt, loopProvenanceArgsFromResult as o, runReferenceEquivalenceJudge as ot, runEval as p, HoldoutLockedError as pt, planCampaignRun as q, campaignMeasurementDigest as r, REFERENCE_EQUIVALENCE_INPUT_LIMITS as rt, loopProvenanceSpans as s, DEFAULT_RED_TEAM_CORPUS as st, skillOptOptimizationMethod as t, campaignSplitDigestFromIdentities as tt, verifyLoopProvenanceRecord as u, scoreRedTeamOutput as ut, paretoPolicy as v, contentHash as vt, dimensionRegressions as w, paretoFrontierWithCrowding as wt, heldOutGate as x, crowdingDistance as xt, paretoSignificanceGate as y, fileVerdictCache as yt, surfaceContentHash as z };
7144
+ export { assertCampaignSplitIdentity as $, assertOptimizationResult as A, summarizeAgentReceiptIntegrity as At, surfaceContentHash as B, detectScale as C, decidePairedPromotion as Ct, runCanaries as D, BackendIntegrityError as Dt, pairHoldout as E, pairedDeltaTest as Et, assertCodeSurfaceIdentity as F, DEFAULT_MUTATION_PRIMITIVES as G, campaignBreakdown as H, assertComponentSurface as I, planCampaignRun as J, buildReflectionPrompt as K, codeSurfaceIdentityMaterial as L, compareOptimizationMethods as M, JudgeParseError as Mt, costFromLedgerSummary as N, composeGate as O, assertRealAgentReceipts as Ot, optimizationTokenUsageFromSummary as P, assertCampaignDesign as Q, componentSurfaceIdentityMaterial as R, defaultProductionGate as S, recoverTruncatedJson as St, heldoutSignificance as T, minimumPairsForPairedDeltaTest as Tt, campaignMeanComposite as U, surfaceHash as V, compareRankKeys as W, resolveRunDir as X, runCampaign as Y, tangleTracesRoot as Z, buildEvidenceVector as _, crowdingDistance as _t, emitLoopProvenance as a, REFERENCE_EQUIVALENCE_JUDGE_VERSION as at, powerPreflight as b, paretoFrontierWithCrowding as bt, provenanceRecordPath as c, DEFAULT_RED_TEAM_CORPUS as ct, runImprovementLoop as d, scoreRedTeamOutput as dt, campaignScenarioIdentity as et, runOptimization as f, toolNamesForRun as ft, gepaOptimizationMethod as g, llmJudge as gt, labelTrustRank as h, hashScenarios as ht, canonicalDigest as i, REFERENCE_EQUIVALENCE_INPUT_LIMITS as it, combineComparisonCosts as j, summarizeBackendIntegrity as jt, externalTextOptimizationMethod as k, assertRealBackend as kt, provenanceSpansPath as l, redTeamDataset as lt, isProposedCandidate as m, HoldoutLockedError as mt, buildLoopProvenanceRecord as n, campaignSplitDigestFromIdentities as nt, loopProvenanceArgsFromResult as o, createReferenceEquivalenceJudge as ot, runEval as p, Dataset as pt, parseReflectionResponse as q, campaignMeasurementDigest as r, openAutoPr as rt, loopProvenanceSpans as s, runReferenceEquivalenceJudge as st, skillOptOptimizationMethod as t, campaignSplitDigest as tt, verifyLoopProvenanceRecord as u, redTeamReport as ut, paretoPolicy as v, dominates as vt, dimensionRegressions as w, pairedDecisionShape as wt, heldOutGate as x, scalarScore as xt, paretoSignificanceGate as y, paretoFrontier as yt, renderSurfaceDiff as z };
7267
7145
 
7268
- //# sourceMappingURL=skillopt-optimization-method-Di8Xkalt.js.map
7146
+ //# sourceMappingURL=skillopt-optimization-method-DA5mJmFE.js.map