@tangle-network/agent-eval 0.143.0 → 0.144.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +21 -8
  3. package/dist/analyst/index.d.ts +4 -4
  4. package/dist/analyst/index.d.ts.map +1 -1
  5. package/dist/analyst/index.js +3 -3
  6. package/dist/{benchmark-command-CSddvlq7.js → benchmark-command-4c7N_rlw.js} +7 -6
  7. package/dist/benchmark-command-4c7N_rlw.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-05UGZ8sZ.js} +4 -4
  11. package/dist/{benchmarks-Dg0B56tK.js.map → benchmarks-05UGZ8sZ.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-BKOtvRAB.js} +11 -9
  15. package/dist/campaign-BKOtvRAB.js.map +1 -0
  16. package/dist/cli.js +1 -1
  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-DfHJEwYh.js} +2 -2
  20. package/dist/{default-registry-BupFSXSN.js.map → default-registry-DfHJEwYh.js.map} +1 -1
  21. package/dist/{dspy-rlm-engine-CJ4sN8Wx.js → dspy-rlm-engine-CBFwlyaY.js} +89 -7
  22. package/dist/dspy-rlm-engine-CBFwlyaY.js.map +1 -0
  23. package/dist/external-optimizer-contracts-iK0yu4AR.d.ts +128 -0
  24. package/dist/external-optimizer-contracts-iK0yu4AR.d.ts.map +1 -0
  25. package/dist/{index-BKjcUH13.d.ts → index-BZUe-ODI.d.ts} +41 -5
  26. package/dist/index-BZUe-ODI.d.ts.map +1 -0
  27. package/dist/{index-DY83M1WL.d.ts → index-DgbFl4cv.d.ts} +9 -8
  28. package/dist/{index-DY83M1WL.d.ts.map → index-DgbFl4cv.d.ts.map} +1 -1
  29. package/dist/{index-CaOjqCli.d.ts → index-DtMpBKVF.d.ts} +3 -3
  30. package/dist/{index-CaOjqCli.d.ts.map → index-DtMpBKVF.d.ts.map} +1 -1
  31. package/dist/index.d.ts +7 -7
  32. package/dist/index.js +9 -8
  33. package/dist/index.js.map +1 -1
  34. package/dist/{integrity-hruKe6oe.js → integrity-DY6tIbl0.js} +48 -18
  35. package/dist/integrity-DY6tIbl0.js.map +1 -0
  36. package/dist/openapi.json +1 -1
  37. package/dist/{single-run-lock-Do-8vBIJ.js → single-run-lock-t1si1ob7.js} +370 -152
  38. package/dist/single-run-lock-t1si1ob7.js.map +1 -0
  39. package/dist/{skill-usage-CRqaHi0R.d.ts → skill-usage-BiVEU0QY.d.ts} +3 -3
  40. package/dist/{skill-usage-CRqaHi0R.d.ts.map → skill-usage-BiVEU0QY.d.ts.map} +1 -1
  41. package/dist/{skillopt-optimization-method-CwRvi-rL.d.ts → skillopt-optimization-method-B7o01OdX.d.ts} +43 -9
  42. package/dist/skillopt-optimization-method-B7o01OdX.d.ts.map +1 -0
  43. package/dist/{skillopt-optimization-method-Di8Xkalt.js → skillopt-optimization-method-Ds8J1_K8.js} +289 -222
  44. package/dist/skillopt-optimization-method-Ds8J1_K8.js.map +1 -0
  45. package/dist/supervisor-run/index.d.ts +3 -3
  46. package/dist/supervisor-run/index.js +3 -3
  47. package/dist/{supervisor-run-BEns49Nx.js → supervisor-run-DiyQVczd.js} +301 -25
  48. package/dist/supervisor-run-DiyQVczd.js.map +1 -0
  49. package/dist/{types-Dea6tiVI.d.ts → types-yLK8gXE9.d.ts} +15 -2
  50. package/dist/types-yLK8gXE9.d.ts.map +1 -0
  51. package/docs/campaign-proposers.md +11 -7
  52. package/package.json +1 -1
  53. package/dist/benchmark-command-CSddvlq7.js.map +0 -1
  54. package/dist/campaign-C7ZGiZYG.js.map +0 -1
  55. package/dist/dspy-rlm-engine-CJ4sN8Wx.js.map +0 -1
  56. package/dist/external-optimizer-contracts-nb7c_WAR.d.ts +0 -42
  57. package/dist/external-optimizer-contracts-nb7c_WAR.d.ts.map +0 -1
  58. package/dist/index-BKjcUH13.d.ts.map +0 -1
  59. package/dist/integrity-hruKe6oe.js.map +0 -1
  60. package/dist/single-run-lock-Do-8vBIJ.js.map +0 -1
  61. package/dist/skillopt-optimization-method-CwRvi-rL.d.ts.map +0 -1
  62. package/dist/skillopt-optimization-method-Di8Xkalt.js.map +0 -1
  63. package/dist/supervisor-run-BEns49Nx.js.map +0 -1
  64. package/dist/types-Dea6tiVI.d.ts.map +0 -1
@@ -2,13 +2,13 @@ import { c as ValidationError, i as JudgeError, t as AgentEvalError } from "./er
2
2
  import { i as CostLedger, t as CostAccountingIncompleteError } from "./cost-ledger-DMFxsLKr.js";
3
3
  import { f as maximumChargeForLlmRequest, l as costReceiptFromLlm, m as stripFencedJson, u as costReceiptFromLlmError } from "./llm-client-B3WXSH5Y.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 { C as assertNoCredentialValues, D as removeCredentialEnvironment, E as isRecord, O as safePathComponent, S as assertJsonValue, T as isExternalTextCandidate, _ as canonicalJson, d as runWithCleanup, f as startExternalOptimizerModelProxy, 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 contentHash, w as isCandidateText, x as assertExternalOptimizerModelBudget } from "./single-run-lock-t1si1ob7.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
14
  import { createServer } from "node:http";
@@ -656,160 +656,6 @@ function paretoFrontierWithCrowding(candidates, objectives) {
656
656
  return crowdingDistance(frontier, objectives).sort((a, b) => b.distance - a.distance);
657
657
  }
658
658
  //#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
659
  //#region src/llm-judge.ts
814
660
  /**
815
661
  * `llmJudge` — the single-LLM-call bridge that turns a rubric prompt into a
@@ -1813,6 +1659,8 @@ async function startExternalOptimizerCallback(args) {
1813
1659
  let closePromise;
1814
1660
  const activeControllers = /* @__PURE__ */ new Set();
1815
1661
  const activeHandlers = /* @__PURE__ */ new Set();
1662
+ const proposedCandidateHashes = /* @__PURE__ */ new Set();
1663
+ let observationSequence = 0;
1816
1664
  const server = createServer((request, response) => {
1817
1665
  if (!accepting) {
1818
1666
  sendJsonIfOpen(response, 503, { error: "external optimizer callback is closing" });
@@ -1833,7 +1681,13 @@ async function startExternalOptimizerCallback(args) {
1833
1681
  if (accepted > args.maxEvaluations) return void 0;
1834
1682
  evaluations += 1;
1835
1683
  return accepted;
1836
- }).finally(() => {
1684
+ }, (observation) => {
1685
+ if (!args.observe) return;
1686
+ args.observe({
1687
+ ...observation,
1688
+ sequence: ++observationSequence
1689
+ });
1690
+ }, proposedCandidateHashes).finally(() => {
1837
1691
  controller.signal.removeEventListener("abort", abortRequest);
1838
1692
  activeControllers.delete(controller);
1839
1693
  activeHandlers.delete(handler);
@@ -1868,7 +1722,7 @@ async function startExternalOptimizerCallback(args) {
1868
1722
  if (serverResult?.status === "rejected") throw serverResult.reason;
1869
1723
  }
1870
1724
  }
1871
- async function handleCallback(request, response, signal, args, nextEvaluation) {
1725
+ async function handleCallback(request, response, signal, args, nextEvaluation, observe, proposedCandidateHashes) {
1872
1726
  try {
1873
1727
  if (request.method !== "POST" || request.url !== "/evaluate") {
1874
1728
  sendJsonIfOpen(response, 404, { error: "not found" });
@@ -1878,19 +1732,76 @@ async function handleCallback(request, response, signal, args, nextEvaluation) {
1878
1732
  sendJsonIfOpen(response, 401, { error: "unauthorized" });
1879
1733
  return;
1880
1734
  }
1881
- const body = await readJson(request);
1735
+ let body;
1736
+ try {
1737
+ body = await readJson(request);
1738
+ } catch {
1739
+ observe({
1740
+ kind: "refusal",
1741
+ reason: "invalid-request"
1742
+ });
1743
+ sendJsonIfOpen(response, 400, { error: "request body must be bounded valid JSON" });
1744
+ return;
1745
+ }
1882
1746
  if (!isRecord(body) || !isExternalTextCandidate(body.candidate) || typeof body.exampleId !== "string") {
1747
+ observe({
1748
+ kind: "refusal",
1749
+ reason: "invalid-request"
1750
+ });
1883
1751
  sendJsonIfOpen(response, 400, { error: "candidate and exampleId are required strings" });
1884
1752
  return;
1885
1753
  }
1886
- if (nextEvaluation() === void 0) {
1754
+ const candidate = cloneCandidate(body.candidate);
1755
+ const candidateHash = contentHash({
1756
+ kind: "external-text-candidate",
1757
+ candidate
1758
+ });
1759
+ if (!proposedCandidateHashes.has(candidateHash)) {
1760
+ observe({
1761
+ kind: "proposal",
1762
+ candidate,
1763
+ candidateHash
1764
+ });
1765
+ proposedCandidateHashes.add(candidateHash);
1766
+ }
1767
+ const count = nextEvaluation();
1768
+ if (count === void 0) {
1769
+ observe({
1770
+ kind: "refusal",
1771
+ reason: "evaluation-limit",
1772
+ candidate,
1773
+ candidateHash,
1774
+ exampleId: body.exampleId
1775
+ });
1887
1776
  sendJsonIfOpen(response, 429, { error: "evaluation limit reached" });
1888
1777
  return;
1889
1778
  }
1890
- sendJsonIfOpen(response, 200, await args.evaluate({
1891
- candidate: body.candidate,
1892
- exampleId: body.exampleId
1893
- }, signal));
1779
+ let result;
1780
+ try {
1781
+ result = await args.evaluate({
1782
+ candidate,
1783
+ exampleId: body.exampleId
1784
+ }, signal);
1785
+ } catch {
1786
+ observe({
1787
+ kind: "refusal",
1788
+ reason: "evaluation-failed",
1789
+ candidate,
1790
+ candidateHash,
1791
+ exampleId: body.exampleId
1792
+ });
1793
+ sendJsonIfOpen(response, 500, { error: "evaluation failed" });
1794
+ return;
1795
+ }
1796
+ observe({
1797
+ kind: "evaluation",
1798
+ candidate,
1799
+ candidateHash,
1800
+ exampleId: body.exampleId,
1801
+ evaluationNumber: count,
1802
+ response: structuredClone(result)
1803
+ });
1804
+ sendJsonIfOpen(response, 200, result);
1894
1805
  } catch {
1895
1806
  sendJsonIfOpen(response, 500, { error: "evaluation failed" });
1896
1807
  }
@@ -1930,6 +1841,10 @@ function assertCallbackConfig(args) {
1930
1841
  if (!Number.isSafeInteger(args.maxEvaluations) || args.maxEvaluations <= 0) throw new Error("external optimizer callback: maxEvaluations must be a positive safe integer");
1931
1842
  if (args.acceptEvaluation !== void 0 && typeof args.acceptEvaluation !== "function") throw new Error("external optimizer callback: acceptEvaluation must be a function");
1932
1843
  if (typeof args.evaluate !== "function") throw new Error("external optimizer callback: evaluate must be a function");
1844
+ if (args.observe !== void 0 && typeof args.observe !== "function") throw new Error("external optimizer callback: observe must be a function");
1845
+ }
1846
+ function cloneCandidate(candidate) {
1847
+ return typeof candidate === "string" ? candidate : { ...candidate };
1933
1848
  }
1934
1849
  //#endregion
1935
1850
  //#region src/campaign/external-optimizer-run-budget.ts
@@ -3492,7 +3407,7 @@ async function compareOptimizationMethods(opts) {
3492
3407
  throw error;
3493
3408
  }
3494
3409
  });
3495
- assertReportedCostWithinCeiling(combineCosts(optimized.map((result) => ({
3410
+ assertReportedCostWithinCeiling(combineComparisonCosts(optimized.map((result) => ({
3496
3411
  label: `method '${result.name}'`,
3497
3412
  cost: result.cost
3498
3413
  }))).totalCostUsd, opts.costCeiling, "optimization");
@@ -3577,12 +3492,12 @@ async function compareOptimizationMethods(opts) {
3577
3492
  favored
3578
3493
  };
3579
3494
  });
3580
- const optimizationCost = combineCosts(scores.map((score) => ({
3495
+ const optimizationCost = combineComparisonCosts(scores.map((score) => ({
3581
3496
  label: `method '${score.name}'`,
3582
3497
  cost: score.optimizationCost
3583
3498
  })));
3584
3499
  const testCost = costFromLedgerSummary(costLedger.summary({ phase: testCostPhase }));
3585
- const totalCost = combineCosts([{
3500
+ const totalCost = combineComparisonCosts([{
3586
3501
  label: "optimization",
3587
3502
  cost: optimizationCost
3588
3503
  }, {
@@ -3645,6 +3560,7 @@ function assertOptimizationProvenance(methodName, value) {
3645
3560
  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
3561
  if (typeof value.runId !== "string" || !value.runId.trim()) fail("runId");
3647
3562
  if (value.optimizerModel !== void 0 && (typeof value.optimizerModel !== "string" || !value.optimizerModel.trim() || value.optimizerModel.trim() !== value.optimizerModel)) fail("optimizerModel");
3563
+ if (value.optimizerCallRef !== void 0 && (typeof value.optimizerCallRef !== "string" || !value.optimizerCallRef.trim() || value.optimizerCallRef.trim() !== value.optimizerCallRef)) fail("optimizerCallRef");
3648
3564
  if (typeof value.resumed !== "boolean") fail("resumed");
3649
3565
  if (!Number.isSafeInteger(value.evaluationCount) || value.evaluationCount < 0) fail("evaluationCount");
3650
3566
  if (typeof value.artifactDir !== "string" || !value.artifactDir.trim()) fail("artifactDir");
@@ -3667,6 +3583,24 @@ function assertOptimizationProvenance(methodName, value) {
3667
3583
  if (value.tokenUsage.reasoningTokens !== void 0 && value.tokenUsage.reasoningTokens > value.tokenUsage.outputTokens) fail("tokenUsage.reasoningTokens");
3668
3584
  if (value.tokenUsage.totalTokens !== value.tokenUsage.inputTokens + value.tokenUsage.outputTokens) fail("tokenUsage.totalTokens");
3669
3585
  }
3586
+ if (value.observations !== void 0) {
3587
+ 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");
3588
+ for (const field of [
3589
+ "submittedCandidates",
3590
+ "evaluations",
3591
+ "refusals"
3592
+ ]) if (!Number.isSafeInteger(value.observations[field]) || value.observations[field] < 0) fail(`observations.${field}`);
3593
+ }
3594
+ if (value.modelExecutions !== void 0) {
3595
+ 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");
3596
+ for (const field of [
3597
+ "calls",
3598
+ "succeeded",
3599
+ "failed"
3600
+ ]) if (!Number.isSafeInteger(value.modelExecutions[field]) || value.modelExecutions[field] < 0) fail(`modelExecutions.${field}`);
3601
+ if (value.modelExecutions.calls !== value.modelExecutions.succeeded + value.modelExecutions.failed) fail("modelExecutions.calls");
3602
+ }
3603
+ if (value.optimizerModel === void 0 !== (value.optimizerCallRef === void 0) || value.optimizerModel === void 0 !== (value.modelExecutions === void 0)) fail("optimizerModel execution provenance");
3670
3604
  }
3671
3605
  function assertComparisonControls(opts, seed, resamples, confidence) {
3672
3606
  if (opts.optimizationRunOptions && "costCeiling" in opts.optimizationRunOptions) throw new Error("compareOptimizationMethods: optimizationRunOptions.costCeiling is not supported; costCeiling covers optimization and final scoring");
@@ -3810,6 +3744,7 @@ function callerDispatchRef(opts) {
3810
3744
  function costFromLedgerSummary(summary) {
3811
3745
  const cost = {
3812
3746
  totalCostUsd: summary.totalCostUsd,
3747
+ costProvenance: structuredClone(summary.costProvenance),
3813
3748
  accountingComplete: summary.accountingComplete,
3814
3749
  incompleteReasons: [...summary.incompleteReasons]
3815
3750
  };
@@ -3836,19 +3771,37 @@ function optimizationTokenUsageFromSummary(summary, receipts) {
3836
3771
  calls: summary.totalCalls
3837
3772
  };
3838
3773
  }
3839
- function combineCosts(entries) {
3840
- return {
3841
- totalCostUsd: entries.reduce((total, entry) => total + entry.cost.totalCostUsd, 0),
3774
+ /** Combine method costs without turning one unknown bill into a known total. */
3775
+ function combineComparisonCosts(entries) {
3776
+ const totalCostUsd = entries.reduce((total, entry) => total + entry.cost.totalCostUsd, 0);
3777
+ const cost = {
3778
+ totalCostUsd,
3779
+ costProvenance: entries.some((entry) => entry.cost.costProvenance.kind === "uncaptured") ? {
3780
+ kind: "uncaptured",
3781
+ usd: null
3782
+ } : entries.every((entry) => entry.cost.costProvenance.kind === "observed") ? {
3783
+ kind: "observed",
3784
+ usd: totalCostUsd
3785
+ } : {
3786
+ kind: "estimated",
3787
+ usd: totalCostUsd
3788
+ },
3842
3789
  accountingComplete: entries.every((entry) => entry.cost.accountingComplete),
3843
3790
  incompleteReasons: entries.flatMap((entry) => entry.cost.incompleteReasons.map((reason) => `${entry.label}: ${reason}`))
3844
3791
  };
3792
+ assertComparisonCost(cost, "combined cost");
3793
+ return cost;
3845
3794
  }
3846
3795
  function assertComparisonCost(cost, label) {
3847
3796
  if (!cost || typeof cost !== "object") throw new Error(`compareOptimizationMethods: ${label} returned no cost`);
3848
3797
  if (!Number.isFinite(cost.totalCostUsd) || cost.totalCostUsd < 0) throw new Error(`compareOptimizationMethods: ${label} returned an invalid totalCostUsd`);
3798
+ const provenance = cost.costProvenance;
3799
+ 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`);
3800
+ if (provenance.kind !== "uncaptured" && provenance.usd !== cost.totalCostUsd) throw new Error(`compareOptimizationMethods: ${label} returned costProvenance inconsistent with totalCostUsd`);
3849
3801
  if (typeof cost.accountingComplete !== "boolean") throw new Error(`compareOptimizationMethods: ${label} returned invalid accountingComplete`);
3850
3802
  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
3803
  if (cost.accountingComplete !== (cost.incompleteReasons.length === 0)) throw new Error(`compareOptimizationMethods: ${label} returned inconsistent cost completeness and reasons`);
3804
+ if (cost.accountingComplete && provenance.kind === "uncaptured") throw new Error(`compareOptimizationMethods: ${label} cannot mark uncaptured cost as complete`);
3852
3805
  }
3853
3806
  //#endregion
3854
3807
  //#region src/campaign/external-text-optimization.ts
@@ -4097,16 +4050,23 @@ function externalTextOptimizationMethod(config) {
4097
4050
  status: "completed"
4098
4051
  }
4099
4052
  });
4053
+ const measuredCost = combineComparisonCosts([{
4054
+ label: "evaluation",
4055
+ cost: evaluationCost
4056
+ }, {
4057
+ label: "optimizer",
4058
+ cost: optimizerCost
4059
+ }]);
4100
4060
  return {
4101
4061
  winnerSurface: decodeExternalTextCandidate(result.bestCandidate),
4102
4062
  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
- ]
4063
+ ...measuredCost,
4064
+ ...externalCostReason ? { costProvenance: {
4065
+ kind: "uncaptured",
4066
+ usd: null
4067
+ } } : {},
4068
+ accountingComplete: measuredCost.accountingComplete && externalCostReason === void 0,
4069
+ incompleteReasons: [...measuredCost.incompleteReasons, ...externalCostReason ? [`optimizer: external spend is not observed: ${externalCostReason}`] : []]
4110
4070
  },
4111
4071
  durationMs: Date.now() - started,
4112
4072
  provenance: {
@@ -5339,8 +5299,8 @@ const TOKEN_USAGE_FIELDS = [
5339
5299
  "calls"
5340
5300
  ];
5341
5301
  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`);
5302
+ if (!summary.usageComplete || budget.maxCostUsd !== void 0 && !summary.accountingComplete) throw new Error(`${name}: cannot resume optimizer-model work with incomplete bounded usage`);
5303
+ 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
5304
  }
5345
5305
  function assertExternalOptimizerTokenUsage(usage, name, optimizer) {
5346
5306
  if (usage === void 0) return;
@@ -5355,6 +5315,78 @@ function assertExternalOptimizerCompletionCount(upstream, requestAttempts, succe
5355
5315
  if (upstream.requestAttempts !== void 0 && upstream.requestAttempts !== requestAttempts) throw new Error(`${name}: ${optimizer} reported ${upstream.requestAttempts} model attempts but the proxy received ${requestAttempts}`);
5356
5316
  }
5357
5317
  //#endregion
5318
+ //#region src/campaign/external-optimizer-observations.ts
5319
+ /** Append-only observation log for one external-optimizer attempt. */
5320
+ function openExternalOptimizerObservationLog(input) {
5321
+ 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}'`);
5322
+ input.storage.write(input.path, "");
5323
+ let revision = 0;
5324
+ const counts = {
5325
+ submittedCandidates: 0,
5326
+ evaluations: 0,
5327
+ refusals: 0
5328
+ };
5329
+ return {
5330
+ observe(observation) {
5331
+ const expectedSequence = counts.submittedCandidates + counts.evaluations + counts.refusals + 1;
5332
+ if (observation.sequence !== expectedSequence) throw new Error(`external optimizer observation log expected sequence ${expectedSequence}, got ${observation.sequence}`);
5333
+ const line = `${canonicalJson(observation)}\n`;
5334
+ const next = input.storage.append(input.path, line, revision);
5335
+ if (next === void 0) throw new Error(`external optimizer observation log changed concurrently at '${input.path}'`);
5336
+ revision = next;
5337
+ if (observation.kind === "proposal") counts.submittedCandidates += 1;
5338
+ else if (observation.kind === "evaluation") counts.evaluations += 1;
5339
+ else counts.refusals += 1;
5340
+ },
5341
+ summary() {
5342
+ const contents = input.storage.read(input.path) ?? "";
5343
+ const bytes = new TextEncoder().encode(contents);
5344
+ if (bytes.byteLength !== revision) throw new Error(`external optimizer observation log revision changed at '${input.path}'`);
5345
+ return {
5346
+ scope: "callback-submitted-candidates",
5347
+ path: input.path,
5348
+ sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
5349
+ ...counts
5350
+ };
5351
+ }
5352
+ };
5353
+ }
5354
+ /** Append-only opaque Runtime execution records for one optimizer attempt. */
5355
+ function openExternalOptimizerExecutionLog(input) {
5356
+ 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}'`);
5357
+ input.storage.write(input.path, "");
5358
+ let revision = 0;
5359
+ const counts = {
5360
+ calls: 0,
5361
+ succeeded: 0,
5362
+ failed: 0
5363
+ };
5364
+ return {
5365
+ observe(observation) {
5366
+ if (observation.sequence !== counts.calls + 1) throw new Error(`external optimizer execution log expected sequence ${counts.calls + 1}, got ${observation.sequence}`);
5367
+ const line = `${canonicalJson(observation)}\n`;
5368
+ const next = input.storage.append(input.path, line, revision);
5369
+ if (next === void 0) throw new Error(`external optimizer execution log changed concurrently at '${input.path}'`);
5370
+ revision = next;
5371
+ counts.calls += 1;
5372
+ if (observation.succeeded) counts.succeeded += 1;
5373
+ else counts.failed += 1;
5374
+ },
5375
+ summary() {
5376
+ const contents = input.storage.read(input.path) ?? "";
5377
+ const bytes = new TextEncoder().encode(contents);
5378
+ if (bytes.byteLength !== revision) throw new Error(`external optimizer execution log revision changed at '${input.path}'`);
5379
+ if (counts.calls !== counts.succeeded + counts.failed) throw new Error(`external optimizer execution log counts disagree at '${input.path}'`);
5380
+ return {
5381
+ scope: "runtime-model-calls",
5382
+ path: input.path,
5383
+ sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
5384
+ ...counts
5385
+ };
5386
+ }
5387
+ };
5388
+ }
5389
+ //#endregion
5358
5390
  //#region src/campaign/external-optimizer-run-config.ts
5359
5391
  function externalOptimizerRunnerIdentity(runner, module) {
5360
5392
  return {
@@ -5446,27 +5478,16 @@ function assertExternalOptimizerRuntimeIdentity(value, expectedPackage, label) {
5446
5478
  //#region src/campaign/optimizer-model.ts
5447
5479
  function assertOptimizerModel(value, label) {
5448
5480
  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`);
5481
+ 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`);
5482
+ if (typeof value.call !== "function") throw new Error(`${label}.call must be a function`);
5464
5483
  assertExternalOptimizerModelBudget(value.budget, `${label}.budget`);
5465
5484
  }
5466
5485
  function snapshotOptimizerModel(value) {
5467
5486
  return {
5468
- ...value,
5469
- budget: structuredClone(value.budget)
5487
+ model: value.model,
5488
+ budget: structuredClone(value.budget),
5489
+ call: value.call,
5490
+ callRef: value.callRef
5470
5491
  };
5471
5492
  }
5472
5493
  const MAX_TIMER_DELAY_MS$1 = 2147483647;
@@ -5581,7 +5602,7 @@ function assertEngineRun(run, label) {
5581
5602
  function assertEngineOptions(run, label) {
5582
5603
  if (!run || typeof run !== "object") throw new Error(`gepaOptimizationMethod: ${label} is required`);
5583
5604
  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`);
5605
+ 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
5606
  if (run.maxConcurrency !== void 0) assertPositiveSafeInteger$1(run.maxConcurrency, `${label}.maxConcurrency`);
5586
5607
  if (run.stopAtScore !== void 0 && !Number.isFinite(run.stopAtScore)) throw new Error(`gepaOptimizationMethod: ${label}.stopAtScore must be a finite number`);
5587
5608
  if (run.sandbox !== void 0 && typeof run.sandbox !== "boolean") throw new Error(`gepaOptimizationMethod: ${label}.sandbox must be a boolean`);
@@ -5708,7 +5729,7 @@ function gepaOptimizationMethod(config) {
5708
5729
  maxEvidenceChars,
5709
5730
  optimizerModel: config.optimizer ? {
5710
5731
  model: config.optimizer.model,
5711
- baseUrl: config.optimizer.baseUrl,
5732
+ callRef: config.optimizer.callRef,
5712
5733
  budget: config.optimizer.budget
5713
5734
  } : null,
5714
5735
  runner: externalOptimizerRunnerIdentity(bridgeRunner, "agent_eval_rpc.gepa_bridge"),
@@ -5727,6 +5748,14 @@ function gepaOptimizationMethod(config) {
5727
5748
  attemptId,
5728
5749
  maxEvaluations: evaluationLimit
5729
5750
  });
5751
+ const observationLog = openExternalOptimizerObservationLog({
5752
+ storage,
5753
+ path: `${runDir}/observations-${attemptId}.jsonl`
5754
+ });
5755
+ const executionLog = config.optimizer ? openExternalOptimizerExecutionLog({
5756
+ storage,
5757
+ path: `${runDir}/model-executions-${attemptId}.jsonl`
5758
+ }) : void 0;
5730
5759
  const scenarioById = mapExternalScenarios(input.trainScenarios, input.selectionScenarios, "GEPA bridge");
5731
5760
  const evaluate = createExternalTextEvaluator({
5732
5761
  input,
@@ -5746,6 +5775,7 @@ function gepaOptimizationMethod(config) {
5746
5775
  maxEvaluations: evaluationLimit,
5747
5776
  acceptEvaluation: () => runBudget.acceptEvaluation(),
5748
5777
  evaluate,
5778
+ observe: observationLog.observe,
5749
5779
  ...signal ? { signal } : {}
5750
5780
  });
5751
5781
  const runnerEnv = bridgeRunner?.env ?? {};
@@ -5765,8 +5795,9 @@ function gepaOptimizationMethod(config) {
5765
5795
  });
5766
5796
  assertPriorExternalOptimizerUsage(priorOptimizerUsage, config.optimizer.budget, name);
5767
5797
  modelProxy = await startExternalOptimizerModelProxy({
5768
- upstreamBaseUrl: config.optimizer.baseUrl,
5769
- upstreamApiKey: config.optimizer.apiKey,
5798
+ call: config.optimizer.call,
5799
+ callRef: config.optimizer.callRef,
5800
+ recordExecution: executionLog.observe,
5770
5801
  model: config.optimizer.model,
5771
5802
  budget: config.optimizer.budget,
5772
5803
  costLedger,
@@ -5775,7 +5806,7 @@ function gepaOptimizationMethod(config) {
5775
5806
  tags: { ...runBudget.attemptTags },
5776
5807
  initialUsage: {
5777
5808
  requests: priorOptimizerUsage.totalCalls,
5778
- costUsd: priorOptimizerUsage.totalCostUsd
5809
+ ...priorOptimizerUsage.costProvenance.kind === "uncaptured" ? {} : { costUsd: priorOptimizerUsage.totalCostUsd }
5779
5810
  },
5780
5811
  ...signal ? { signal } : {}
5781
5812
  });
@@ -5854,17 +5885,31 @@ function gepaOptimizationMethod(config) {
5854
5885
  });
5855
5886
  const optimizerCost = costFromLedgerSummary(optimizerSummary);
5856
5887
  const reportedProposerCost = result.proposerCostUsd ?? 0;
5857
- if (modelProxy) assertExternalOptimizerCompletionCount(result.tokenUsage, modelProxy.requestAttempts(), modelProxy.successfulCompletions(), name, "GEPA");
5888
+ if (modelProxy) {
5889
+ modelProxy.assertExecutionComplete();
5890
+ assertExternalOptimizerCompletionCount(result.tokenUsage, modelProxy.requestAttempts(), modelProxy.successfulCompletions(), name, "GEPA");
5891
+ }
5858
5892
  const tokenUsage = modelProxy ? optimizationTokenUsageFromSummary(optimizerSummary, optimizerReceipts) : void 0;
5859
5893
  const runtime = observedExternalOptimizerRuntime(runtimeIdentity);
5894
+ const meteredCost = modelProxy ? combineComparisonCosts([{
5895
+ label: "evaluation",
5896
+ cost: evaluationCost
5897
+ }, {
5898
+ label: "optimizer model",
5899
+ cost: optimizerCost
5900
+ }]) : void 0;
5901
+ const externalTotalCostUsd = evaluationCost.totalCostUsd + reportedProposerCost;
5860
5902
  return {
5861
5903
  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,
5904
+ cost: modelProxy ? meteredCost : {
5905
+ totalCostUsd: externalTotalCostUsd,
5906
+ costProvenance: result.proposerCostAccounting === "reported" && evaluationCost.costProvenance.kind !== "uncaptured" ? {
5907
+ kind: "estimated",
5908
+ usd: externalTotalCostUsd
5909
+ } : {
5910
+ kind: "uncaptured",
5911
+ usd: null
5912
+ },
5868
5913
  accountingComplete: false,
5869
5914
  incompleteReasons: [
5870
5915
  ...evaluationCost.incompleteReasons,
@@ -5875,13 +5920,18 @@ function gepaOptimizationMethod(config) {
5875
5920
  durationMs: Date.now() - started,
5876
5921
  provenance: {
5877
5922
  ...runtime,
5878
- ...config.optimizer ? { optimizerModel: config.optimizer.model } : {},
5923
+ ...config.optimizer ? {
5924
+ optimizerModel: config.optimizer.model,
5925
+ optimizerCallRef: config.optimizer.callRef
5926
+ } : {},
5879
5927
  compatibleRunId,
5880
5928
  runId,
5881
5929
  resumed: result.resumed,
5882
5930
  evaluationCount: runBudget.acceptedEvaluations(),
5883
5931
  artifactDir: outputDir,
5884
- ...tokenUsage ? { tokenUsage } : {}
5932
+ ...tokenUsage ? { tokenUsage } : {},
5933
+ observations: observationLog.summary(),
5934
+ ...executionLog ? { modelExecutions: executionLog.summary() } : {}
5885
5935
  }
5886
5936
  };
5887
5937
  }
@@ -7076,7 +7126,7 @@ function skillOptOptimizationMethod(config) {
7076
7126
  background: config.background ?? "",
7077
7127
  optimizerModel: {
7078
7128
  model: config.optimizer.model,
7079
- baseUrl: config.optimizer.baseUrl,
7129
+ callRef: config.optimizer.callRef,
7080
7130
  budget: config.optimizer.budget
7081
7131
  },
7082
7132
  seedCandidate: input.baselineSurface,
@@ -7101,6 +7151,14 @@ function skillOptOptimizationMethod(config) {
7101
7151
  attemptId,
7102
7152
  maxEvaluations: config.maxEvaluations
7103
7153
  });
7154
+ const observationLog = openExternalOptimizerObservationLog({
7155
+ storage,
7156
+ path: `${runDir}/observations-${attemptId}.jsonl`
7157
+ });
7158
+ const executionLog = openExternalOptimizerExecutionLog({
7159
+ storage,
7160
+ path: `${runDir}/model-executions-${attemptId}.jsonl`
7161
+ });
7104
7162
  const scenarioById = mapExternalScenarios(input.trainScenarios, input.selectionScenarios, "SkillOpt bridge");
7105
7163
  const evaluate = createExternalTextEvaluator({
7106
7164
  input,
@@ -7120,6 +7178,7 @@ function skillOptOptimizationMethod(config) {
7120
7178
  maxEvaluations: config.maxEvaluations,
7121
7179
  acceptEvaluation: () => runBudget.acceptEvaluation(),
7122
7180
  evaluate,
7181
+ observe: observationLog.observe,
7123
7182
  ...signal ? { signal } : {}
7124
7183
  });
7125
7184
  const runnerEnv = bridgeRunner?.env ?? {};
@@ -7138,8 +7197,9 @@ function skillOptOptimizationMethod(config) {
7138
7197
  });
7139
7198
  assertPriorExternalOptimizerUsage(priorOptimizerUsage, config.optimizer.budget, name);
7140
7199
  const modelProxy = await startExternalOptimizerModelProxy({
7141
- upstreamBaseUrl: config.optimizer.baseUrl,
7142
- upstreamApiKey: config.optimizer.apiKey,
7200
+ call: config.optimizer.call,
7201
+ callRef: config.optimizer.callRef,
7202
+ recordExecution: executionLog.observe,
7143
7203
  model: config.optimizer.model,
7144
7204
  budget: config.optimizer.budget,
7145
7205
  costLedger,
@@ -7148,7 +7208,7 @@ function skillOptOptimizationMethod(config) {
7148
7208
  tags: { ...runBudget.attemptTags },
7149
7209
  initialUsage: {
7150
7210
  requests: priorOptimizerUsage.totalCalls,
7151
- costUsd: priorOptimizerUsage.totalCostUsd
7211
+ ...priorOptimizerUsage.costProvenance.kind === "uncaptured" ? {} : { costUsd: priorOptimizerUsage.totalCostUsd }
7152
7212
  },
7153
7213
  ...signal ? { signal } : {}
7154
7214
  });
@@ -7237,32 +7297,39 @@ function skillOptOptimizationMethod(config) {
7237
7297
  tags: runBudget.runTags
7238
7298
  });
7239
7299
  const optimizerCost = costFromLedgerSummary(optimizerUsage);
7300
+ modelProxy.assertExecutionComplete();
7240
7301
  assertExternalOptimizerCompletionCount(result.tokenUsage, modelProxy.requestAttempts(), modelProxy.successfulCompletions(), name, "SkillOpt");
7241
7302
  const tokenUsage = optimizationTokenUsageFromSummary(optimizerUsage, optimizerReceipts);
7242
7303
  const runtime = observedExternalOptimizerRuntime(runtimeIdentity);
7304
+ const combinedCost = combineComparisonCosts([{
7305
+ label: "evaluation",
7306
+ cost: evaluationCost
7307
+ }, {
7308
+ label: "optimizer model",
7309
+ cost: optimizerCost
7310
+ }]);
7243
7311
  return {
7244
7312
  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
- },
7313
+ cost: combinedCost,
7250
7314
  durationMs: Date.now() - started,
7251
7315
  provenance: {
7252
7316
  ...runtime,
7253
7317
  optimizerModel: config.optimizer.model,
7318
+ optimizerCallRef: config.optimizer.callRef,
7254
7319
  compatibleRunId,
7255
7320
  runId,
7256
7321
  resumed: result.resumed,
7257
7322
  evaluationCount: runBudget.acceptedEvaluations(),
7258
7323
  artifactDir: outputDir,
7259
- ...tokenUsage ? { tokenUsage } : {}
7324
+ ...tokenUsage ? { tokenUsage } : {},
7325
+ observations: observationLog.summary(),
7326
+ modelExecutions: executionLog.summary()
7260
7327
  }
7261
7328
  };
7262
7329
  }
7263
7330
  };
7264
7331
  }
7265
7332
  //#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 };
7333
+ 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
7334
 
7268
- //# sourceMappingURL=skillopt-optimization-method-Di8Xkalt.js.map
7335
+ //# sourceMappingURL=skillopt-optimization-method-Ds8J1_K8.js.map