@tangle-network/agent-eval 0.131.1 → 0.133.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 (133) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/analyst/index.d.ts +5 -5
  3. package/dist/analyst/index.js +3 -3
  4. package/dist/{analyze-runs-CBnCgfse.d.ts → analyze-runs-DZr7JW-m.d.ts} +5 -5
  5. package/dist/{analyze-runs-CBnCgfse.d.ts.map → analyze-runs-DZr7JW-m.d.ts.map} +1 -1
  6. package/dist/benchmarks/index.d.ts +1 -1
  7. package/dist/benchmarks/index.js +1 -1
  8. package/dist/{benchmarks-BDtRiOvE.js → benchmarks-DncrVrSr.js} +3 -3
  9. package/dist/{benchmarks-BDtRiOvE.js.map → benchmarks-DncrVrSr.js.map} +1 -1
  10. package/dist/campaign/index.d.ts +4 -4
  11. package/dist/campaign/index.js +2 -2
  12. package/dist/{campaign-CLG6y9jF.js → campaign-CyQMCCEU.js} +4 -4
  13. package/dist/{campaign-CLG6y9jF.js.map → campaign-CyQMCCEU.js.map} +1 -1
  14. package/dist/cli.js +1 -1
  15. package/dist/{client-w90OcvvR.d.ts → client-D4F9hdzR.d.ts} +4 -4
  16. package/dist/{client-w90OcvvR.d.ts.map → client-D4F9hdzR.d.ts.map} +1 -1
  17. package/dist/contract/index.d.ts +188 -22
  18. package/dist/contract/index.d.ts.map +1 -1
  19. package/dist/contract/index.js +1663 -65
  20. package/dist/contract/index.js.map +1 -1
  21. package/dist/control.d.ts +1 -1
  22. package/dist/{cost-ledger-DIgQUFZZ.js → cost-ledger-BrJxbrMy.js} +278 -9
  23. package/dist/cost-ledger-BrJxbrMy.js.map +1 -0
  24. package/dist/{cost-ledger-Dye6jCgg.d.ts → cost-ledger-fGS_u_O1.d.ts} +18 -2
  25. package/dist/cost-ledger-fGS_u_O1.d.ts.map +1 -0
  26. package/dist/{default-registry-Cj1oUpLN.d.ts → default-registry-Cl3pHo4n.d.ts} +4 -4
  27. package/dist/{default-registry-Cj1oUpLN.d.ts.map → default-registry-Cl3pHo4n.d.ts.map} +1 -1
  28. package/dist/{default-registry-C-vFCSEc.js → default-registry-D3T9XbuY.js} +4 -13
  29. package/dist/default-registry-D3T9XbuY.js.map +1 -0
  30. package/dist/{eval-campaign-C2k-m4aY.js → eval-campaign-CYkUME2T.js} +2 -2
  31. package/dist/{eval-campaign-C2k-m4aY.js.map → eval-campaign-CYkUME2T.js.map} +1 -1
  32. package/dist/{task-failure-attributes-CQZlB3et.js → extract-usage-2j25whHw.js} +154 -2
  33. package/dist/extract-usage-2j25whHw.js.map +1 -0
  34. package/dist/fuzz.d.ts +1 -1
  35. package/dist/fuzz.js +1 -1
  36. package/dist/hosted/index.d.ts +2 -2
  37. package/dist/{index-B11XCkdf2.d.ts → index-3cdlURSk.d.ts} +2 -2
  38. package/dist/index-3cdlURSk.d.ts.map +1 -0
  39. package/dist/{index-VTypFU3t.d.ts → index-B_eUhU9K.d.ts} +7 -7
  40. package/dist/{index-VTypFU3t.d.ts.map → index-B_eUhU9K.d.ts.map} +1 -1
  41. package/dist/{index-DNXoNaFF.d.ts → index-C2fkZhv_.d.ts} +2 -2
  42. package/dist/{index-DNXoNaFF.d.ts.map → index-C2fkZhv_.d.ts.map} +1 -1
  43. package/dist/{index-NPeSWD98.d.ts → index-CiP2DRCj.d.ts} +5 -5
  44. package/dist/{index-NPeSWD98.d.ts.map → index-CiP2DRCj.d.ts.map} +1 -1
  45. package/dist/index.d.ts +69 -23
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +100 -12
  48. package/dist/index.js.map +1 -1
  49. package/dist/{llm-client-B_nIBlYo.d.ts → llm-client-BiK4HW0u.d.ts} +2 -2
  50. package/dist/{llm-client-B_nIBlYo.d.ts.map → llm-client-BiK4HW0u.d.ts.map} +1 -1
  51. package/dist/{llm-client--GR4JbZE.js → llm-client-ClPW-dWB.js} +2 -2
  52. package/dist/{llm-client--GR4JbZE.js.map → llm-client-ClPW-dWB.js.map} +1 -1
  53. package/dist/meta-eval/index.d.ts +216 -3
  54. package/dist/{index-D_F6VAKe.d.ts.map → meta-eval/index.d.ts.map} +1 -1
  55. package/dist/meta-eval/index.js +91 -1
  56. package/dist/meta-eval/index.js.map +1 -1
  57. package/dist/multishot/index.d.ts +1 -1
  58. package/dist/openapi.json +1 -1
  59. package/dist/{release-report-Crg9oFJ0.d.ts → release-report-DfmKSIEE.d.ts} +3 -3
  60. package/dist/{release-report-Crg9oFJ0.d.ts.map → release-report-DfmKSIEE.d.ts.map} +1 -1
  61. package/dist/{replay-RE97Ckjl.d.ts → replay-BI6CVKkp.d.ts} +2 -2
  62. package/dist/{replay-RE97Ckjl.d.ts.map → replay-BI6CVKkp.d.ts.map} +1 -1
  63. package/dist/{replay-D18-pBAA.js → replay-BMR4TEYY.js} +2 -3
  64. package/dist/{replay-D18-pBAA.js.map → replay-BMR4TEYY.js.map} +1 -1
  65. package/dist/reporting.d.ts +4 -4
  66. package/dist/{researcher-Q5rpPqZY.d.ts → researcher-DMimgHtN.d.ts} +4 -4
  67. package/dist/{researcher-Q5rpPqZY.d.ts.map → researcher-DMimgHtN.d.ts.map} +1 -1
  68. package/dist/{reward-hacking-CW-3HN0n.d.ts → reward-hacking-D-QqXvg-.d.ts} +2 -2
  69. package/dist/{reward-hacking-CW-3HN0n.d.ts.map → reward-hacking-D-QqXvg-.d.ts.map} +1 -1
  70. package/dist/rl.d.ts +156 -6
  71. package/dist/rl.d.ts.map +1 -1
  72. package/dist/rl.js +217 -2
  73. package/dist/rl.js.map +1 -1
  74. package/dist/rollout/index.d.ts +1 -1
  75. package/dist/{rubric-predictive-validity-lXLmashy.d.ts → rubric-predictive-validity-C1dCLcvb.d.ts} +2 -2
  76. package/dist/{rubric-predictive-validity-lXLmashy.d.ts.map → rubric-predictive-validity-C1dCLcvb.d.ts.map} +1 -1
  77. package/dist/{run-evidence-oByzm-dE.d.ts → run-evidence-DokQtX0-.d.ts} +2 -2
  78. package/dist/{run-evidence-oByzm-dE.d.ts.map → run-evidence-DokQtX0-.d.ts.map} +1 -1
  79. package/dist/run-record-CN8Zd21B.js.map +1 -1
  80. package/dist/{run-record-BJnYdTxO.d.ts → run-record-DcObtIGh.d.ts} +4 -14
  81. package/dist/run-record-DcObtIGh.d.ts.map +1 -0
  82. package/dist/{semantic-concept-judge-b5m3irbR.js → semantic-concept-judge-BypLt6Fw.js} +4 -4
  83. package/dist/{semantic-concept-judge-b5m3irbR.js.map → semantic-concept-judge-BypLt6Fw.js.map} +1 -1
  84. package/dist/{server-m5D9cvnG.js → server-BPqlDBWK.js} +3 -3
  85. package/dist/{server-m5D9cvnG.js.map → server-BPqlDBWK.js.map} +1 -1
  86. package/dist/{skill-usage-D5mlWdAJ.d.ts → skill-usage-BaaxFSJR.d.ts} +5 -5
  87. package/dist/{skill-usage-D5mlWdAJ.d.ts.map → skill-usage-BaaxFSJR.d.ts.map} +1 -1
  88. package/dist/{skillopt-optimization-method-D0MVjJdP.d.ts → skillopt-optimization-method-CAASpcS3.d.ts} +9 -9
  89. package/dist/{skillopt-optimization-method-D0MVjJdP.d.ts.map → skillopt-optimization-method-CAASpcS3.d.ts.map} +1 -1
  90. package/dist/{skillopt-optimization-method-eOJL2570.js → skillopt-optimization-method-DgN7U9iR.js} +3 -3
  91. package/dist/{skillopt-optimization-method-eOJL2570.js.map → skillopt-optimization-method-DgN7U9iR.js.map} +1 -1
  92. package/dist/{statistics-Cmj6nynr.d.ts → statistics-DbvkkDPa.d.ts} +2 -2
  93. package/dist/{statistics-Cmj6nynr.d.ts.map → statistics-DbvkkDPa.d.ts.map} +1 -1
  94. package/dist/{summary-report-CWwB_LiV.d.ts → summary-report-DnUcjVpV.d.ts} +2 -2
  95. package/dist/{summary-report-CWwB_LiV.d.ts.map → summary-report-DnUcjVpV.d.ts.map} +1 -1
  96. package/dist/traces.d.ts +2 -2
  97. package/dist/traces.js +2 -2
  98. package/dist/{types-CsD5nTfV.d.ts → types-BokuXvOG.d.ts} +4 -4
  99. package/dist/{types-CsD5nTfV.d.ts.map → types-BokuXvOG.d.ts.map} +1 -1
  100. package/dist/{types-DGsxbAEd.d.ts → types-Cc3qbqzj.d.ts} +3 -3
  101. package/dist/{types-DGsxbAEd.d.ts.map → types-Cc3qbqzj.d.ts.map} +1 -1
  102. package/dist/wire/index.d.ts +2 -2
  103. package/dist/wire/index.js +1 -1
  104. package/package.json +3 -8
  105. package/dist/belief-state/index.d.ts +0 -622
  106. package/dist/belief-state/index.d.ts.map +0 -1
  107. package/dist/belief-state/index.js +0 -1819
  108. package/dist/belief-state/index.js.map +0 -1
  109. package/dist/calibration-CNWWA6K8.js +0 -94
  110. package/dist/calibration-CNWWA6K8.js.map +0 -1
  111. package/dist/code-agent-session-BjkMTQ7H.js +0 -1390
  112. package/dist/code-agent-session-BjkMTQ7H.js.map +0 -1
  113. package/dist/code-agent-session-aYa3SGKz.d.ts +0 -143
  114. package/dist/code-agent-session-aYa3SGKz.d.ts.map +0 -1
  115. package/dist/cost-ledger-DIgQUFZZ.js.map +0 -1
  116. package/dist/cost-ledger-Dye6jCgg.d.ts.map +0 -1
  117. package/dist/default-registry-C-vFCSEc.js.map +0 -1
  118. package/dist/extract-usage-BrQ8mCLX.js +0 -155
  119. package/dist/extract-usage-BrQ8mCLX.js.map +0 -1
  120. package/dist/index-B11XCkdf2.d.ts.map +0 -1
  121. package/dist/index-D_F6VAKe.d.ts +0 -217
  122. package/dist/metrics-C9YY1OcL.js +0 -239
  123. package/dist/metrics-C9YY1OcL.js.map +0 -1
  124. package/dist/off-policy-DvgzvtIx.js +0 -220
  125. package/dist/off-policy-DvgzvtIx.js.map +0 -1
  126. package/dist/off-policy-mskQw8Mb.d.ts +0 -153
  127. package/dist/off-policy-mskQw8Mb.d.ts.map +0 -1
  128. package/dist/run-record-BJnYdTxO.d.ts.map +0 -1
  129. package/dist/runtime-trajectory-1gyaTOoC.js +0 -93
  130. package/dist/runtime-trajectory-1gyaTOoC.js.map +0 -1
  131. package/dist/runtime-trajectory-BXxG4lyi.d.ts +0 -50
  132. package/dist/runtime-trajectory-BXxG4lyi.d.ts.map +0 -1
  133. package/dist/task-failure-attributes-CQZlB3et.js.map +0 -1
@@ -1,20 +1,21 @@
1
1
  import { s as ValidationError } from "../errors-8YnH8WlF.js";
2
- import { L as createChatClient, t as buildDefaultAnalystRegistry } from "../default-registry-C-vFCSEc.js";
2
+ import { L as createChatClient, t as buildDefaultAnalystRegistry } from "../default-registry-D3T9XbuY.js";
3
+ import { g as isModelPriced, i as CostLedger, m as estimateCost } from "../cost-ledger-BrJxbrMy.js";
3
4
  import { LLM_MODEL_ATTR_KEYS, SPAN_KIND_ATTR_KEYS } from "../trace-attributes.js";
4
5
  import { b as classifyOtlpSpanRole, x as isOtlpModelCall } from "../tools-BmuN627J.js";
5
6
  import { r as mapConcurrentRange } from "../concurrency-MUjT7VjM.js";
6
- import { B as surfaceContentHash, Ct as llmJudge, M as compareOptimizationMethods, O as composeGate, Q as inMemoryCampaignStorage, S as defaultProductionGate, T as heldoutSignificance, V as surfaceHash, X as createRunCostLedger, Y as runCampaign, Z as fsCampaignStorage, _ as buildEvidenceVector, a as emitLoopProvenance, b as powerPreflight, ct as campaignSplitDigest, d as runImprovementLoop, dt as REFERENCE_EQUIVALENCE_INPUT_LIMITS, ft as REFERENCE_EQUIVALENCE_JUDGE_VERSION, g as gepaOptimizationMethod, j as assertOptimizationResult, k as externalTextOptimizationMethod, mt as runReferenceEquivalenceJudge, o as loopProvenanceArgsFromResult, p as runEval, pt as createReferenceEquivalenceJudge, rt as resolveRunDir, t as skillOptOptimizationMethod, v as paretoPolicy, x as heldOutGate, y as paretoSignificanceGate } from "../skillopt-optimization-method-eOJL2570.js";
7
+ import { B as surfaceContentHash, Ct as llmJudge, M as compareOptimizationMethods, O as composeGate, Q as inMemoryCampaignStorage, S as defaultProductionGate, T as heldoutSignificance, V as surfaceHash, X as createRunCostLedger, Y as runCampaign, Z as fsCampaignStorage, _ as buildEvidenceVector, a as emitLoopProvenance, b as powerPreflight, ct as campaignSplitDigest, d as runImprovementLoop, dt as REFERENCE_EQUIVALENCE_INPUT_LIMITS, ft as REFERENCE_EQUIVALENCE_JUDGE_VERSION, g as gepaOptimizationMethod, j as assertOptimizationResult, k as externalTextOptimizationMethod, mt as runReferenceEquivalenceJudge, o as loopProvenanceArgsFromResult, p as runEval, pt as createReferenceEquivalenceJudge, rt as resolveRunDir, t as skillOptOptimizationMethod, v as paretoPolicy, x as heldOutGate, y as paretoSignificanceGate } from "../skillopt-optimization-method-DgN7U9iR.js";
7
8
  import { v as pairedBootstrap } from "../statistics-CnnxdpOg.js";
8
9
  import { i as parseRunRecordSafe, r as modelHasSnapshot } from "../run-record-CN8Zd21B.js";
9
- import { i as summarizeTraceErrors, n as recordAggregateMeasurements, r as summarizeExecutionMeasurements, t as readTaskFailureLabels } from "../task-failure-attributes-CQZlB3et.js";
10
+ import { a as recordAggregateMeasurements, i as readTaskFailureLabels, o as summarizeExecutionMeasurements, s as summarizeTraceErrors, t as extractUsage } from "../extract-usage-2j25whHw.js";
10
11
  import { n as summarizeExecution, t as analyzeRuns } from "../analyze-runs-BjPn_fOS.js";
11
12
  import { a as campaignCellExecutionEvidence, c as campaignCellToRunRecord, o as campaignCellJudgeDimensions, s as campaignCellTaskScore } from "../reward-hacking-BEvjdUtD.js";
12
13
  import { n as InMemoryOutcomeStore, t as FileSystemOutcomeStore } from "../outcome-store-ChBKlTd_.js";
13
- import { a as fromPiSession, c as observeCodeAgentSession, i as fromOpenCodeSession, n as fromCodexSession, o as fromPigraphSession, r as fromKimiCodeSession, s as parseCodeAgentJsonl, t as fromClaudeCodeSession } from "../code-agent-session-BjkMTQ7H.js";
14
14
  import { t as createHostedClient } from "../client-CYzbdJOZ.js";
15
15
  import { dirname, join } from "node:path";
16
+ import { createHash } from "node:crypto";
16
17
  import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
17
- import { agentCandidateBenchmarkSuiteSchema, agentCandidateBenchmarkTaskSchema, agentCandidateBundleSchema, agentCandidateEvaluationPolicySchema, agentCandidateExperimentSchema, agentImprovementMeasuredComparisonSchema, agentProfileImprovementExperimentSchema, agentProfileImprovementMeasuredComparisonSchema, agentProfileImprovementRunCellSchema, agentProfileImprovementRunReceiptSchema, agentProfileImprovementSuiteInputsSchema, agentProfileImprovementSuiteSchema, agentProfileImprovementTaskSchema, candidateExecutionEvidenceSchema, canonicalCandidateDigest, canonicalCandidateJson, omitTopLevelDigest } from "@tangle-network/agent-interface";
18
+ import { agentCandidateBenchmarkSuiteSchema, agentCandidateBenchmarkTaskSchema, agentCandidateBundleSchema, agentCandidateEvaluationPolicySchema, agentCandidateExperimentSchema, agentImprovementMeasuredComparisonSchema, agentProfileImprovementExperimentSchema, agentProfileImprovementMeasuredComparisonSchema, agentProfileImprovementRunCellSchema, agentProfileImprovementRunReceiptSchema, agentProfileImprovementSuiteInputsSchema, agentProfileImprovementSuiteSchema, agentProfileImprovementTaskSchema, candidateExecutionEvidenceSchema, canonicalCandidateDigest, canonicalCandidateJson, numbersApproximatelyEqual, omitTopLevelDigest } from "@tangle-network/agent-interface";
18
19
  //#region src/contract/self-improve.ts
19
20
  /**
20
21
  * Run one complete improvement job.
@@ -417,7 +418,7 @@ function averageComposite(campaign) {
417
418
  const aggs = Object.values(campaign.aggregates.byScenario);
418
419
  return aggs.length === 0 ? 0 : aggs.reduce((s, a) => s + a.meanComposite, 0) / aggs.length;
419
420
  }
420
- function hashString(s) {
421
+ function hashString$1(s) {
421
422
  let h = 2166136261;
422
423
  for (let i = 0; i < s.length; i++) {
423
424
  h ^= s.charCodeAt(i);
@@ -447,7 +448,7 @@ function cellsToRunRecords(cells, candidateId, runId, surface, splitTag, fallbac
447
448
  runId: `${runId}::${candidateId}::${cell.cellId}`,
448
449
  experimentId: runId,
449
450
  candidateId,
450
- seed: cell.rep * 1e6 + hashString(cell.scenarioId).slice(0, 6).split("").reduce((a, c) => a * 31 + c.charCodeAt(0) >>> 0, 0),
451
+ seed: cell.rep * 1e6 + hashString$1(cell.scenarioId).slice(0, 6).split("").reduce((a, c) => a * 31 + c.charCodeAt(0) >>> 0, 0),
451
452
  model,
452
453
  promptHash,
453
454
  configHash,
@@ -546,6 +547,19 @@ function requirePositiveInteger(value, field) {
546
547
  return value;
547
548
  }
548
549
  //#endregion
550
+ //#region src/contract/fixed-spend.ts
551
+ function addFixedSpend(left, right) {
552
+ return {
553
+ inputTokens: left.inputTokens + right.inputTokens,
554
+ outputTokens: left.outputTokens + right.outputTokens,
555
+ cachedInputTokens: left.cachedInputTokens + right.cachedInputTokens,
556
+ reasoningTokens: left.reasoningTokens + right.reasoningTokens,
557
+ modelCalls: left.modelCalls + right.modelCalls,
558
+ costUsdNanos: left.costUsdNanos + right.costUsdNanos,
559
+ costProvenance: left.costProvenance === "observed" && right.costProvenance === "observed" ? "observed" : "estimated"
560
+ };
561
+ }
562
+ //#endregion
549
563
  //#region src/contract/concurrent-map.ts
550
564
  /** Map both arms while bounding the actual executions, not the number of pairs. */
551
565
  async function mapPairedConcurrent(options) {
@@ -568,6 +582,62 @@ async function mapPairedConcurrent(options) {
568
582
  }));
569
583
  }
570
584
  //#endregion
585
+ //#region src/contract/paid-paired-measurement.ts
586
+ /**
587
+ * Reserve the complete signed suite before its first cell starts.
588
+ *
589
+ * The host executor receives each task's signed cap through `execute`; this
590
+ * batch reservation prevents a complete comparison from turning into a partial
591
+ * one after earlier analysis or search has used the remaining budget.
592
+ */
593
+ async function runPaidPairedMeasurement(options) {
594
+ assertNonNegativeFinite(options.maximumCostUsd, "maximumCostUsd");
595
+ const costLedger = resolveCostLedger(options);
596
+ const startedAt = performance.now();
597
+ const paid = await costLedger.runPaidCall({
598
+ callId: options.call.callId,
599
+ channel: options.call.channel,
600
+ phase: options.call.phase,
601
+ actor: options.call.actor,
602
+ model: options.call.model,
603
+ maximumCharge: { externallyEnforcedMaximumUsd: options.maximumCostUsd },
604
+ ...options.call.tags ? { tags: options.call.tags } : {},
605
+ ...options.signal ? { signal: options.signal } : {},
606
+ execute: async (signal) => await mapPairedConcurrent({
607
+ count: options.count,
608
+ maxConcurrency: options.maxConcurrency,
609
+ label: options.label,
610
+ signal,
611
+ map: options.execute
612
+ }),
613
+ receipt: options.receipt
614
+ });
615
+ const wallDurationMs = performance.now() - startedAt;
616
+ if (!paid.succeeded) throw paid.error;
617
+ if (paid.receipt.costUnknown) throw new Error(`${options.label} did not capture the complete measurement cost`);
618
+ return {
619
+ measurements: paid.value,
620
+ wallDurationMs,
621
+ cost: paid.receipt.actualCostUsd === void 0 ? {
622
+ kind: "estimated",
623
+ usd: paid.receipt.costUsd
624
+ } : {
625
+ kind: "observed",
626
+ usd: paid.receipt.costUsd
627
+ }
628
+ };
629
+ }
630
+ function resolveCostLedger(options) {
631
+ if (options.budgetUsd === void 0) return options.costLedger ?? new CostLedger();
632
+ assertNonNegativeFinite(options.budgetUsd, "budgetUsd");
633
+ if (!options.costLedger) throw new Error(`${options.label} with a policy budget requires one shared CostLedger`);
634
+ if (options.costLedger.costCeilingUsd !== options.budgetUsd) throw new Error(`${options.label} CostLedger ceiling must equal the frozen policy budget`);
635
+ return options.costLedger;
636
+ }
637
+ function assertNonNegativeFinite(value, label) {
638
+ if (!Number.isFinite(value) || value < 0) throw new Error(`${label} must be a non-negative finite number`);
639
+ }
640
+ //#endregion
571
641
  //#region src/contract/measured-comparison.ts
572
642
  /** Content-address one task before any measured execution can see it. */
573
643
  function sealCandidateBenchmarkTask(material) {
@@ -614,12 +684,23 @@ function verifyCandidateExperiment(input) {
614
684
  async function runCandidateExperiment(options) {
615
685
  const experiment = verifyCandidateExperiment(options.experiment);
616
686
  const { suite, tasks } = experiment.benchmark;
617
- return (await mapPairedConcurrent({
687
+ const run = await runPaidPairedMeasurement({
618
688
  count: suite.taskDigests.length * suite.reps,
619
689
  maxConcurrency: options.maxConcurrency ?? 2,
620
690
  label: "candidate experiment",
691
+ budgetUsd: experiment.policy.budgetUsd,
692
+ ...options.costLedger ? { costLedger: options.costLedger } : {},
693
+ maximumCostUsd: candidateMeasurementMaximumCostUsd(experiment),
694
+ call: {
695
+ callId: `candidate-measurement:${experiment.digest}`,
696
+ channel: "measurement",
697
+ phase: "heldout",
698
+ actor: experiment.digest,
699
+ model: candidateMeasurementModel(experiment),
700
+ tags: { experimentDigest: experiment.digest }
701
+ },
621
702
  ...options.signal ? { signal: options.signal } : {},
622
- async map(index, arm, signal) {
703
+ async execute(index, arm, signal) {
623
704
  const taskIndex = Math.floor(index / suite.reps);
624
705
  const repetition = index % suite.reps;
625
706
  const task = tasks[taskIndex];
@@ -630,7 +711,7 @@ async function runCandidateExperiment(options) {
630
711
  taskIndex,
631
712
  repetition
632
713
  };
633
- return options.execute({
714
+ return verifyExecutionEvidence(await options.execute({
634
715
  experiment,
635
716
  arm,
636
717
  bundle: experiment[arm],
@@ -638,9 +719,22 @@ async function runCandidateExperiment(options) {
638
719
  benchmarkCell,
639
720
  seed,
640
721
  signal
641
- });
722
+ }));
723
+ },
724
+ receipt(measurements) {
725
+ return candidateMeasurementCostReceipt(measurements, candidateMeasurementModel(experiment));
642
726
  }
643
- })).map((measurement, index) => verifyMeasurement(experiment, measurement, index));
727
+ });
728
+ return {
729
+ measurements: run.measurements.map((measurement, index) => verifyMeasurement(experiment, measurement, index)),
730
+ measurement: {
731
+ wallDurationMs: run.wallDurationMs,
732
+ cost: {
733
+ usd: run.cost.usd,
734
+ provenance: run.cost.kind
735
+ }
736
+ }
737
+ };
644
738
  }
645
739
  /**
646
740
  * Calculate the shared paired decision from any complete receipt shape.
@@ -651,8 +745,11 @@ async function runCandidateExperiment(options) {
651
745
  */
652
746
  function evaluatePairedMeasurements(options) {
653
747
  if (options.measurements.length === 0) throw new Error("paired measurement evaluation requires at least one paired cell");
654
- const additionalCostUsd = options.additionalCostUsd ?? 0;
655
- if (!Number.isFinite(additionalCostUsd) || additionalCostUsd < 0) throw new Error("paired measurement evaluation additionalCostUsd must be a non-negative number");
748
+ const preparationCost = options.preparationCost ?? {
749
+ usd: 0,
750
+ provenance: "observed"
751
+ };
752
+ if (!Number.isFinite(preparationCost.usd) || preparationCost.usd < 0) throw new Error("paired measurement evaluation preparation cost must be a non-negative number");
656
753
  if (typeof options.sharedScorerChannel !== "boolean") throw new Error("paired measurement evaluation sharedScorerChannel must be a boolean");
657
754
  const policy = agentCandidateEvaluationPolicySchema.parse(options.policy);
658
755
  const measurements = options.measurements.map((measurement, index) => projectPairedMeasurement(measurement, index, options.adapter));
@@ -738,12 +835,22 @@ function evaluatePairedMeasurements(options) {
738
835
  const guardedDimensions = new Set(criticalDimensions);
739
836
  const missingCriticalDimensions = criticalDimensions.filter((dimension) => !dimensions.includes(dimension));
740
837
  const regressions = objectives.filter((objective) => objective.kind === "dimension" && guardedDimensions.has(objective.name) && objective.availability === "measured" && objective.confidenceInterval.lower < -regressionTolerance);
741
- const executionCostUsd = measurements.reduce((sum, measurement) => sum + measurement.baseline.costUsd + measurement.candidate.costUsd, 0);
742
- const executionDurationMs = measurements.reduce((sum, measurement) => sum + measurement.baseline.latencyMs + measurement.candidate.latencyMs, 0);
838
+ const measurementCostUsd = measurements.reduce((sum, measurement) => sum + measurement.baseline.costUsd + measurement.candidate.costUsd, 0);
839
+ const measurementWorkDurationMs = measurements.reduce((sum, measurement) => sum + measurement.baseline.latencyMs + measurement.candidate.latencyMs, 0);
743
840
  const incompleteRuns = measurements.flatMap((measurement) => [measurement.baseline, measurement.candidate]).filter((run) => !run.completed);
744
841
  const failedCandidateResults = measurements.filter((measurement) => !measurement.candidate.passed);
745
- const totalCostUsd = executionCostUsd + additionalCostUsd;
746
- const budgetPassed = budgetUsd === void 0 || totalCostUsd <= budgetUsd;
842
+ const derivedMeasurementCost = {
843
+ usd: measurementCostUsd,
844
+ provenance: measurements.every((measurement) => measurement.baseline.costProvenance === "observed" && measurement.candidate.costProvenance === "observed") ? "observed" : "estimated"
845
+ };
846
+ const measurementCost = options.measurementCost ?? derivedMeasurementCost;
847
+ assertKnownCost(measurementCost, "paired measurement cost");
848
+ if (options.measurementCost !== void 0 && (measurementCost.provenance !== derivedMeasurementCost.provenance || !numbersApproximatelyEqual(measurementCost.usd, derivedMeasurementCost.usd))) throw new Error("paired measurement cost does not match its signed receipts");
849
+ const totalCost = {
850
+ usd: preparationCost.usd + measurementCost.usd,
851
+ provenance: preparationCost.provenance === "observed" && measurementCost.provenance === "observed" ? "observed" : "estimated"
852
+ };
853
+ const budgetPassed = budgetUsd === void 0 || totalCost.usd < budgetUsd || numbersApproximatelyEqual(totalCost.usd, budgetUsd);
747
854
  const checks = [
748
855
  {
749
856
  name: "paired-significance",
@@ -778,7 +885,7 @@ function evaluatePairedMeasurements(options) {
778
885
  ...missingCriticalDimensions.length === 0 ? [] : [`critical dimensions missing: ${missingCriticalDimensions.join(", ")}`],
779
886
  ...incompleteRuns.length === 0 ? [] : [`${incompleteRuns.length} benchmark executions did not exit successfully`],
780
887
  ...failedCandidateResults.length === 0 ? [] : [`candidate failed ${failedCandidateResults.length} benchmark tasks`],
781
- ...budgetPassed ? [] : [`total cost ${totalCostUsd} exceeded budget ${budgetUsd}`]
888
+ ...budgetPassed ? [] : [`total cost ${totalCost.usd} exceeded budget ${budgetUsd}`]
782
889
  ];
783
890
  return {
784
891
  overall: {
@@ -802,9 +909,9 @@ function evaluatePairedMeasurements(options) {
802
909
  sharedScorerChannel: options.sharedScorerChannel,
803
910
  reason: power.reason
804
911
  },
805
- executionCostUsd,
806
- totalCostUsd,
807
- executionDurationMs
912
+ measurementCost,
913
+ totalCost,
914
+ measurementWorkDurationMs
808
915
  };
809
916
  }
810
917
  /** Build the only publishable comparison: paired statistics over Runtime receipts. */
@@ -815,7 +922,8 @@ function measuredComparisonFromCandidateExperiment(options) {
815
922
  if (measurements.length !== expectedN) throw new Error(`candidate experiment is incomplete (${measurements.length}/${expectedN} paired cells)`);
816
923
  verifyStableProfileMaterialization(measurements);
817
924
  if (!options.runId.trim()) throw new Error("candidate experiment runId is required");
818
- const searchCostUsd = options.searchCostUsd ?? 0;
925
+ assertPhaseAccounting(options.preparation, "candidate preparation");
926
+ assertPhaseAccounting(options.measurement, "candidate measurement");
819
927
  const evaluation = evaluatePairedMeasurements({
820
928
  measurements: measurements.map((measurement, index) => ({
821
929
  cellId: cellIds(experiment)[index],
@@ -824,12 +932,10 @@ function measuredComparisonFromCandidateExperiment(options) {
824
932
  policy: experiment.policy,
825
933
  adapter: candidateExecutionEvidenceAdapter,
826
934
  sharedScorerChannel: true,
827
- additionalCostUsd: searchCostUsd
935
+ preparationCost: options.preparation.cost,
936
+ measurementCost: options.measurement.cost
828
937
  });
829
938
  const diff = deriveCandidateBundleDiff(experiment);
830
- const searchDurationMs = options.searchDurationMs ?? 0;
831
- const totalCostUsd = evaluation.totalCostUsd;
832
- const durationMs = evaluation.executionDurationMs + searchDurationMs;
833
939
  const provisional = agentImprovementMeasuredComparisonSchema.parse({
834
940
  kind: "agent-improvement-measured-comparison",
835
941
  experiment,
@@ -850,12 +956,16 @@ function measuredComparisonFromCandidateExperiment(options) {
850
956
  diff,
851
957
  evaluation: {
852
958
  generationsExplored: options.generationsExplored ?? 0,
853
- searchDurationMs,
854
- executionDurationMs: evaluation.executionDurationMs,
855
- durationMs,
856
- searchCostUsd,
857
- executionCostUsd: evaluation.executionCostUsd,
858
- totalCostUsd
959
+ preparation: options.preparation,
960
+ measurement: {
961
+ wallDurationMs: options.measurement.wallDurationMs,
962
+ workDurationMs: evaluation.measurementWorkDurationMs,
963
+ cost: evaluation.measurementCost
964
+ },
965
+ total: {
966
+ wallDurationMs: options.preparation.wallDurationMs + options.measurement.wallDurationMs,
967
+ cost: evaluation.totalCost
968
+ }
859
969
  },
860
970
  ...options.metadata ? { metadata: options.metadata } : {}
861
971
  });
@@ -880,12 +990,52 @@ function verifyCandidateExperimentComparison(input) {
880
990
  runId: comparison.provenance.runId,
881
991
  ...comparison.candidate ? { candidate: comparison.candidate } : {},
882
992
  generationsExplored: comparison.evaluation.generationsExplored,
883
- searchDurationMs: comparison.evaluation.searchDurationMs,
884
- searchCostUsd: comparison.evaluation.searchCostUsd,
993
+ preparation: comparison.evaluation.preparation,
994
+ measurement: {
995
+ wallDurationMs: comparison.evaluation.measurement.wallDurationMs,
996
+ cost: comparison.evaluation.measurement.cost
997
+ },
885
998
  ...comparison.metadata ? { metadata: comparison.metadata } : {}
886
999
  })) !== canonicalCandidateDigest(comparison)) throw new Error("candidate experiment comparison does not match its Runtime receipts");
887
1000
  return comparison;
888
1001
  }
1002
+ function candidateMeasurementMaximumCostUsd(experiment) {
1003
+ const maximum = experiment.benchmark.tasks.reduce((sum, task) => sum + task.limits.maxCostUsd * experiment.benchmark.suite.reps * 2, 0);
1004
+ if (!Number.isFinite(maximum) || maximum < 0) throw new Error("candidate measurement maximum cost is invalid");
1005
+ return maximum;
1006
+ }
1007
+ function candidateMeasurementModel(experiment) {
1008
+ const models = [...new Set(experiment.benchmark.tasks.map((task) => task.model.model))];
1009
+ return models.length === 1 ? models[0] : "multiple-models";
1010
+ }
1011
+ function candidateMeasurementCostReceipt(measurements, model) {
1012
+ const usage = measurements.reduce((sum, measurement) => addFixedSpend(addFixedSpend(sum, combinedUsage(measurement.baseline)), combinedUsage(measurement.candidate)), {
1013
+ inputTokens: 0,
1014
+ outputTokens: 0,
1015
+ cachedInputTokens: 0,
1016
+ reasoningTokens: 0,
1017
+ modelCalls: 0,
1018
+ costUsdNanos: 0,
1019
+ costProvenance: "observed"
1020
+ });
1021
+ const costUsd = usage.costUsdNanos / 1e9;
1022
+ return {
1023
+ model,
1024
+ inputTokens: usage.inputTokens,
1025
+ outputTokens: usage.outputTokens,
1026
+ cachedTokens: usage.cachedInputTokens,
1027
+ reasoningTokens: usage.reasoningTokens,
1028
+ ...usage.costProvenance === "observed" ? { actualCostUsd: costUsd } : { estimatedCostUsd: costUsd }
1029
+ };
1030
+ }
1031
+ function assertPhaseAccounting(accounting, label) {
1032
+ if (!Number.isFinite(accounting.wallDurationMs) || accounting.wallDurationMs < 0) throw new Error(`${label} wall duration must be a non-negative number`);
1033
+ assertKnownCost(accounting.cost, `${label} cost`);
1034
+ }
1035
+ function assertKnownCost(cost, label) {
1036
+ if (!Number.isFinite(cost.usd) || cost.usd < 0) throw new Error(`${label} must be a non-negative number`);
1037
+ if (cost.provenance !== "observed" && cost.provenance !== "estimated") throw new Error(`${label} provenance must be observed or estimated`);
1038
+ }
889
1039
  function deriveCandidateBundleDiff(experiment) {
890
1040
  const changed = [
891
1041
  "profile",
@@ -1061,10 +1211,13 @@ function projectRun(run, adapter, label) {
1061
1211
  const completed = adapter.completed(run);
1062
1212
  const passed = adapter.passed(run);
1063
1213
  if (typeof completed !== "boolean" || typeof passed !== "boolean") throw new Error(`${label} completion and pass values must be booleans`);
1214
+ const costProvenance = adapter.costProvenance(run);
1215
+ if (costProvenance !== "observed" && costProvenance !== "estimated") throw new Error(`${label} cost provenance must be observed or estimated`);
1064
1216
  return {
1065
1217
  score: finiteMeasurement(adapter.score(run), `${label} score`),
1066
1218
  dimensions,
1067
1219
  costUsd: nonNegativeMeasurement(adapter.costUsd(run), `${label} cost`),
1220
+ costProvenance,
1068
1221
  latencyMs: nonNegativeMeasurement(adapter.latencyMs(run), `${label} latency`),
1069
1222
  completed,
1070
1223
  passed
@@ -1149,6 +1302,7 @@ const candidateExecutionEvidenceAdapter = {
1149
1302
  score: (evidence) => evidence.receipt.benchmarkResult.material.score,
1150
1303
  dimensions: (evidence) => evidence.receipt.benchmarkResult.material.dimensions,
1151
1304
  costUsd: costFromEvidence,
1305
+ costProvenance: (evidence) => combinedUsage(evidence).costProvenance,
1152
1306
  latencyMs: latencyFromEvidence,
1153
1307
  completed: completedSuccessfully,
1154
1308
  passed: (evidence) => evidence.receipt.benchmarkResult.material.passed
@@ -1162,14 +1316,7 @@ function latencyFromEvidence(evidence) {
1162
1316
  function combinedUsage(evidence) {
1163
1317
  const candidate = evidence.receipt.modelSettlement.material.usage;
1164
1318
  const grader = evidence.receipt.benchmarkResult.material.grading.usage;
1165
- return {
1166
- inputTokens: candidate.inputTokens + grader.inputTokens,
1167
- outputTokens: candidate.outputTokens + grader.outputTokens,
1168
- cachedInputTokens: candidate.cachedInputTokens + grader.cachedInputTokens,
1169
- reasoningTokens: candidate.reasoningTokens + grader.reasoningTokens,
1170
- modelCalls: candidate.modelCalls + grader.modelCalls,
1171
- costUsdNanos: candidate.costUsdNanos + grader.costUsdNanos
1172
- };
1319
+ return addFixedSpend(candidate, grader);
1173
1320
  }
1174
1321
  function cellIds(experiment) {
1175
1322
  const { suite, tasks } = experiment.benchmark;
@@ -1235,15 +1382,45 @@ function verifyAgentProfileImprovementExperiment(input) {
1235
1382
  */
1236
1383
  async function runAgentProfileImprovementExperiment(options) {
1237
1384
  const experiment = verifyAgentProfileImprovementExperiment(options.experiment);
1238
- return (await mapPairedConcurrent({
1385
+ const run = await runPaidPairedMeasurement({
1239
1386
  count: experiment.benchmark.suite.taskDigests.length * experiment.benchmark.suite.reps,
1240
1387
  maxConcurrency: options.maxConcurrency ?? 2,
1241
1388
  label: "profile improvement experiment",
1389
+ budgetUsd: experiment.policy.budgetUsd,
1390
+ ...options.costLedger ? { costLedger: options.costLedger } : {},
1391
+ maximumCostUsd: profileMeasurementMaximumCostUsd(experiment),
1392
+ call: {
1393
+ callId: `profile-improvement-measurement:${experiment.digest}`,
1394
+ channel: "measurement",
1395
+ phase: "heldout",
1396
+ actor: experiment.executionRef.identity,
1397
+ model: profileMeasurementModel(experiment),
1398
+ tags: {
1399
+ experimentDigest: experiment.digest,
1400
+ executorDigest: experiment.executionRef.digest
1401
+ }
1402
+ },
1242
1403
  ...options.signal ? { signal: options.signal } : {},
1243
- map(index, arm, signal) {
1244
- return options.execute(profileExecutionInput(experiment, arm, index, signal));
1404
+ async execute(index, arm, signal) {
1405
+ const expected = profileExecutionInput(experiment, arm, index, signal);
1406
+ const receipt = agentProfileImprovementRunReceiptSchema.parse(await options.execute(expected));
1407
+ verifyProfileReceiptContract(receipt, expected, index);
1408
+ return receipt;
1409
+ },
1410
+ receipt(measurements) {
1411
+ return profileMeasurementCostReceipt(measurements, profileMeasurementModel(experiment));
1412
+ }
1413
+ });
1414
+ return {
1415
+ measurements: run.measurements.map((measurement, index) => verifyProfileMeasurement(experiment, measurement, index)),
1416
+ measurement: {
1417
+ wallDurationMs: run.wallDurationMs,
1418
+ cost: {
1419
+ usd: run.cost.usd,
1420
+ provenance: run.cost.kind
1421
+ }
1245
1422
  }
1246
- })).map((measurement, index) => verifyProfileMeasurement(experiment, measurement, index));
1423
+ };
1247
1424
  }
1248
1425
  /** Build the only publishable profile comparison from complete host receipts. */
1249
1426
  function measuredComparisonFromAgentProfileImprovementExperiment(options) {
@@ -1252,8 +1429,6 @@ function measuredComparisonFromAgentProfileImprovementExperiment(options) {
1252
1429
  const expectedCount = experiment.benchmark.suite.taskDigests.length * experiment.benchmark.suite.reps;
1253
1430
  if (measurements.length !== expectedCount) throw new Error(`profile improvement experiment is incomplete (${measurements.length}/${expectedCount} paired cells)`);
1254
1431
  if (!options.runId.trim()) throw new Error("profile improvement experiment runId is required");
1255
- const searchCostUsd = nonNegative(options.searchCostUsd ?? 0, "searchCostUsd");
1256
- const searchDurationMs = nonNegative(options.searchDurationMs ?? 0, "searchDurationMs");
1257
1432
  const evaluation = evaluatePairedMeasurements({
1258
1433
  measurements: measurements.map((measurement, index) => ({
1259
1434
  cellId: profileCellId(experiment, index),
@@ -1262,7 +1437,8 @@ function measuredComparisonFromAgentProfileImprovementExperiment(options) {
1262
1437
  policy: experiment.policy,
1263
1438
  adapter: profileReceiptAdapter,
1264
1439
  sharedScorerChannel: true,
1265
- additionalCostUsd: searchCostUsd
1440
+ preparationCost: options.preparation.cost,
1441
+ measurementCost: options.measurement.cost
1266
1442
  });
1267
1443
  const provisional = agentProfileImprovementMeasuredComparisonSchema.parse({
1268
1444
  kind: "agent-profile-improvement-measured-comparison",
@@ -1284,12 +1460,16 @@ function measuredComparisonFromAgentProfileImprovementExperiment(options) {
1284
1460
  diff: canonicalCandidateJson(experiment.change),
1285
1461
  evaluation: {
1286
1462
  generationsExplored: options.generationsExplored ?? 0,
1287
- searchDurationMs,
1288
- executionDurationMs: evaluation.executionDurationMs,
1289
- durationMs: evaluation.executionDurationMs + searchDurationMs,
1290
- searchCostUsd,
1291
- executionCostUsd: evaluation.executionCostUsd,
1292
- totalCostUsd: evaluation.totalCostUsd
1463
+ preparation: options.preparation,
1464
+ measurement: {
1465
+ wallDurationMs: options.measurement.wallDurationMs,
1466
+ workDurationMs: evaluation.measurementWorkDurationMs,
1467
+ cost: evaluation.measurementCost
1468
+ },
1469
+ total: {
1470
+ wallDurationMs: options.preparation.wallDurationMs + options.measurement.wallDurationMs,
1471
+ cost: evaluation.totalCost
1472
+ }
1293
1473
  },
1294
1474
  ...options.metadata ? { metadata: options.metadata } : {}
1295
1475
  });
@@ -1314,12 +1494,48 @@ function verifyAgentProfileImprovementExperimentComparison(input) {
1314
1494
  runId: comparison.provenance.runId,
1315
1495
  ...comparison.candidate ? { candidate: comparison.candidate } : {},
1316
1496
  generationsExplored: comparison.evaluation.generationsExplored,
1317
- searchDurationMs: comparison.evaluation.searchDurationMs,
1318
- searchCostUsd: comparison.evaluation.searchCostUsd,
1497
+ preparation: comparison.evaluation.preparation,
1498
+ measurement: {
1499
+ wallDurationMs: comparison.evaluation.measurement.wallDurationMs,
1500
+ cost: comparison.evaluation.measurement.cost
1501
+ },
1319
1502
  ...comparison.metadata ? { metadata: comparison.metadata } : {}
1320
1503
  })) !== canonicalCandidateDigest(comparison)) throw new Error("profile improvement comparison does not match its Runtime receipts");
1321
1504
  return comparison;
1322
1505
  }
1506
+ function profileMeasurementMaximumCostUsd(experiment) {
1507
+ const pairs = experiment.benchmark.suite.reps;
1508
+ const maximum = experiment.benchmark.tasks.reduce((sum, task) => sum + task.limits.maxCostUsd * pairs * 2, 0);
1509
+ if (!Number.isFinite(maximum) || maximum < 0) throw new Error("profile improvement measurement maximum cost is invalid");
1510
+ return maximum;
1511
+ }
1512
+ function profileMeasurementModel(experiment) {
1513
+ const models = [...new Set(experiment.benchmark.tasks.map((task) => task.model.model))];
1514
+ return models.length === 1 ? models[0] : "multiple-models";
1515
+ }
1516
+ function profileMeasurementCostReceipt(measurements, model) {
1517
+ const usage = measurements.reduce((sum, measurement) => addFixedSpend(addFixedSpend(sum, combinedProfileUsage(measurement.baseline)), combinedProfileUsage(measurement.candidate)), {
1518
+ inputTokens: 0,
1519
+ outputTokens: 0,
1520
+ cachedInputTokens: 0,
1521
+ reasoningTokens: 0,
1522
+ modelCalls: 0,
1523
+ costUsdNanos: 0,
1524
+ costProvenance: "observed"
1525
+ });
1526
+ const costUsd = usage.costUsdNanos / 1e9;
1527
+ return {
1528
+ model,
1529
+ inputTokens: usage.inputTokens,
1530
+ outputTokens: usage.outputTokens,
1531
+ cachedTokens: usage.cachedInputTokens,
1532
+ reasoningTokens: usage.reasoningTokens,
1533
+ ...usage.costProvenance === "observed" ? { actualCostUsd: costUsd } : { estimatedCostUsd: costUsd }
1534
+ };
1535
+ }
1536
+ function combinedProfileUsage(receipt) {
1537
+ return addFixedSpend(receipt.usage, receipt.grading.usage);
1538
+ }
1323
1539
  function profileExecutionInput(experiment, arm, index, signal) {
1324
1540
  const { task, taskIndex, repetition, seed } = profileCell(experiment, index);
1325
1541
  const stateDigest = experiment[arm].stateDigest;
@@ -1356,17 +1572,18 @@ function verifyProfileMeasurement(experiment, input, index) {
1356
1572
  const baseline = agentProfileImprovementRunReceiptSchema.parse(material.baseline);
1357
1573
  const candidate = agentProfileImprovementRunReceiptSchema.parse(material.candidate);
1358
1574
  if (baseline.runCell.digest !== expectedBaseline.runCell.digest || candidate.runCell.digest !== expectedCandidate.runCell.digest) throw new Error(`profile improvement measurement ${index} substituted a measured arm`);
1359
- verifyProfileReceiptTaskContract(baseline, expectedBaseline, index);
1360
- verifyProfileReceiptTaskContract(candidate, expectedCandidate, index);
1575
+ verifyProfileReceiptContract(baseline, expectedBaseline, index);
1576
+ verifyProfileReceiptContract(candidate, expectedCandidate, index);
1361
1577
  if (baseline.executionId === candidate.executionId || baseline.digest === candidate.digest) throw new Error(`profile improvement measurement ${index} reused one execution across arms`);
1362
1578
  return {
1363
1579
  baseline,
1364
1580
  candidate
1365
1581
  };
1366
1582
  }
1367
- function verifyProfileReceiptTaskContract(receipt, expected, index) {
1583
+ function verifyProfileReceiptContract(receipt, expected, index) {
1368
1584
  const task = expected.task;
1369
1585
  if (canonicalCandidateDigest(receipt.resolvedModel) !== canonicalCandidateDigest(task.model) || canonicalCandidateDigest(receipt.limits) !== canonicalCandidateDigest(task.limits) || canonicalCandidateDigest(receipt.grading.grader) !== canonicalCandidateDigest(task.grader)) throw new Error(`profile improvement measurement ${index} substituted its ${expected.arm} task contract`);
1586
+ if (canonicalCandidateDigest(receipt.executionRef) !== canonicalCandidateDigest(expected.experiment.executionRef)) throw new Error(`profile improvement measurement ${index} substituted its ${expected.arm} executor`);
1370
1587
  }
1371
1588
  function profileCell(experiment, index) {
1372
1589
  const { suite, tasks } = experiment.benchmark;
@@ -1389,14 +1606,11 @@ const profileReceiptAdapter = {
1389
1606
  score: (receipt) => receipt.grading.score,
1390
1607
  dimensions: (receipt) => receipt.grading.dimensions,
1391
1608
  costUsd: (receipt) => (receipt.usage.costUsdNanos + receipt.grading.usage.costUsdNanos) / 1e9,
1609
+ costProvenance: (receipt) => receipt.usage.costProvenance === "observed" && receipt.grading.usage.costProvenance === "observed" ? "observed" : "estimated",
1392
1610
  latencyMs: (receipt) => receipt.timing.durationMs + receipt.grading.timing.durationMs,
1393
1611
  completed: (receipt) => receipt.outcome.status === "succeeded",
1394
1612
  passed: (receipt) => receipt.grading.passed
1395
1613
  };
1396
- function nonNegative(value, label) {
1397
- if (!Number.isFinite(value) || value < 0) throw new Error(`${label} must be a non-negative number`);
1398
- return value;
1399
- }
1400
1614
  //#endregion
1401
1615
  //#region src/contract/intake/run-record-dir.ts
1402
1616
  /**
@@ -1784,6 +1998,1390 @@ function partitionRunsByAuthoringModel(runs, index) {
1784
1998
  };
1785
1999
  }
1786
2000
  //#endregion
2001
+ //#region src/contract/intake/code-agent-observation.ts
2002
+ /**
2003
+ * Project one provider session into the exact user-visible answer and a
2004
+ * provider-neutral action stream. Raw prompts, tool inputs, and tool outputs
2005
+ * stay out of this projection; callers retain the source JSONL as evidence.
2006
+ */
2007
+ function observeCodeAgentSession(options) {
2008
+ const entries = options.entries.filter(isRecord$1);
2009
+ const projection = projectionFor(options.source, entries);
2010
+ const terminal = terminalWithExecution(projection, options.execution);
2011
+ return {
2012
+ source: options.source,
2013
+ sessionId: sessionIdFromEntries$1(options.source, entries) ?? fallbackSessionId$1(options.source, options.sourcePath),
2014
+ finalText: projection.finalText,
2015
+ terminal,
2016
+ actions: projection.actions
2017
+ };
2018
+ }
2019
+ function projectionFor(source, entries) {
2020
+ switch (source) {
2021
+ case "codex": return codexProjection(entries);
2022
+ case "claude-code": return claudeProjection(entries);
2023
+ case "opencode": return openCodeProjection(entries);
2024
+ case "kimi-code": return kimiProjection(entries);
2025
+ case "pi": return piProjection(entries);
2026
+ }
2027
+ }
2028
+ function codexProjection(entries) {
2029
+ const actions = [];
2030
+ const calls = /* @__PURE__ */ new Map();
2031
+ let finalText = null;
2032
+ let terminal = "unknown";
2033
+ let explicitTerminal = false;
2034
+ for (const entry of entries) {
2035
+ const entryType = stringField$1(entry, "type");
2036
+ const payload = record$1(entry.payload) ?? {};
2037
+ const payloadType = stringField$1(payload, "type");
2038
+ const timestampMs = timestamp(entry.timestamp);
2039
+ const item = record$1(entry.item);
2040
+ if (entryType === "item.started" && item) {
2041
+ const itemType = stringField$1(item, "type");
2042
+ if (isCodexActionItem(itemType)) {
2043
+ const id = stringField$1(item, "id") ?? `item-${actions.length}`;
2044
+ const action = actionFor({
2045
+ id,
2046
+ stepIndex: actions.length,
2047
+ kind: itemType === "file_change" ? "patch" : "tool",
2048
+ surface: codexItemSurface(itemType, item),
2049
+ name: codexItemName(itemType, item),
2050
+ status: "started",
2051
+ timestampMs,
2052
+ metadata: compactMetadata({
2053
+ sourceEventType: entryType,
2054
+ itemType
2055
+ })
2056
+ });
2057
+ calls.set(id, action);
2058
+ actions.push(action);
2059
+ }
2060
+ }
2061
+ if (entryType === "item.completed" && item) {
2062
+ const itemType = stringField$1(item, "type");
2063
+ if (itemType === "agent_message" || itemType === "message") finalText = nonEmpty(stringField$1(item, "text")) ?? finalText;
2064
+ if (isCodexActionItem(itemType)) {
2065
+ const id = stringField$1(item, "id") ?? `item-${actions.length}`;
2066
+ const status = codexItemStatus(itemType, item);
2067
+ const existing = calls.get(id);
2068
+ if (existing) existing.status = status;
2069
+ else {
2070
+ const action = actionFor({
2071
+ id,
2072
+ stepIndex: actions.length,
2073
+ kind: itemType === "file_change" ? "patch" : "tool",
2074
+ surface: codexItemSurface(itemType, item),
2075
+ name: codexItemName(itemType, item),
2076
+ status,
2077
+ timestampMs,
2078
+ metadata: compactMetadata({
2079
+ sourceEventType: entryType,
2080
+ itemType
2081
+ })
2082
+ });
2083
+ calls.set(id, action);
2084
+ actions.push(action);
2085
+ }
2086
+ }
2087
+ }
2088
+ if (entryType === "response_item") {
2089
+ if (payloadType === "message" || payloadType === "agent_message") finalText = textFromMessagePayload(payload) ?? finalText;
2090
+ if (payloadType === "function_call" || payloadType === "custom_tool_call") {
2091
+ const id = stringField$1(payload, "call_id") ?? stringField$1(payload, "id") ?? `call-${actions.length}`;
2092
+ const name = stringField$1(payload, "name") ?? payloadType;
2093
+ const action = actionFor({
2094
+ id,
2095
+ stepIndex: actions.length,
2096
+ kind: "tool",
2097
+ surface: surfaceForTool(name),
2098
+ name,
2099
+ status: "started",
2100
+ timestampMs,
2101
+ metadata: compactMetadata({
2102
+ sourceEventType: entryType,
2103
+ payloadType
2104
+ })
2105
+ });
2106
+ calls.set(id, action);
2107
+ actions.push(action);
2108
+ }
2109
+ if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
2110
+ const id = stringField$1(payload, "call_id") ?? stringField$1(payload, "id");
2111
+ const existing = id ? calls.get(id) : void 0;
2112
+ if (existing) existing.status = looksLikeError(payload.output) ? "failed" : "completed";
2113
+ }
2114
+ }
2115
+ if (entryType === "event_msg") {
2116
+ if (payloadType === "agent_message") finalText = nonEmpty(stringField$1(payload, "message")) ?? nonEmpty(stringField$1(payload, "text")) ?? finalText;
2117
+ if (payloadType === "patch_apply_end") {
2118
+ const patchStatus = typeof payload.success === "boolean" ? payload.success ? "completed" : "failed" : "unknown";
2119
+ actions.push(actionFor({
2120
+ id: stringField$1(payload, "call_id") ?? `patch-${actions.length}`,
2121
+ stepIndex: actions.length,
2122
+ kind: "patch",
2123
+ surface: "code",
2124
+ name: "patch",
2125
+ status: patchStatus,
2126
+ timestampMs,
2127
+ metadata: compactMetadata({
2128
+ sourceEventType: entryType,
2129
+ payloadType
2130
+ })
2131
+ }));
2132
+ }
2133
+ if (payloadType === "sub_agent_activity") {
2134
+ const name = stringField$1(payload, "kind") ?? "subagent";
2135
+ actions.push(actionFor({
2136
+ id: stringField$1(payload, "event_id") ?? `subagent-${actions.length}`,
2137
+ stepIndex: actions.length,
2138
+ kind: "tool",
2139
+ surface: "subagent",
2140
+ name,
2141
+ status: statusFrom(payload),
2142
+ timestampMs,
2143
+ metadata: compactMetadata({
2144
+ sourceEventType: entryType,
2145
+ payloadType
2146
+ })
2147
+ }));
2148
+ }
2149
+ if (payloadType === "task_complete" || payloadType === "turn_aborted") {
2150
+ const completed = payloadType === "task_complete";
2151
+ terminal = completed ? "completed" : "failed";
2152
+ explicitTerminal = true;
2153
+ actions.push(terminalAction(actions.length, payloadType, completed, timestampMs));
2154
+ }
2155
+ }
2156
+ if (entryType === "turn.completed" || entryType === "exec_done") {
2157
+ const completed = entry.success !== false;
2158
+ terminal = completed ? "completed" : "failed";
2159
+ explicitTerminal = true;
2160
+ finalText = nonEmpty(stringField$1(entry, "message")) ?? finalText;
2161
+ actions.push(terminalAction(actions.length, entryType, completed, timestampMs));
2162
+ }
2163
+ if (entryType === "turn.failed" || entryType === "error") {
2164
+ terminal = "failed";
2165
+ explicitTerminal = true;
2166
+ actions.push(terminalAction(actions.length, entryType, false, timestampMs));
2167
+ }
2168
+ }
2169
+ return {
2170
+ finalText,
2171
+ terminal,
2172
+ explicitTerminal,
2173
+ actions
2174
+ };
2175
+ }
2176
+ function claudeProjection(entries) {
2177
+ const actions = [];
2178
+ const calls = /* @__PURE__ */ new Map();
2179
+ let finalText = null;
2180
+ let resultText = null;
2181
+ let terminal = "unknown";
2182
+ let explicitTerminal = false;
2183
+ for (const entry of entries) {
2184
+ const entryType = stringField$1(entry, "type");
2185
+ const timestampMs = timestamp(entry.timestamp);
2186
+ const message = record$1(entry.message);
2187
+ const content = Array.isArray(message?.content) ? message.content : [];
2188
+ if (entryType === "assistant") {
2189
+ const text = textBlocks(content);
2190
+ if (text !== null) finalText = text;
2191
+ for (const value of content) {
2192
+ const part = record$1(value);
2193
+ if (!part) continue;
2194
+ const partType = stringField$1(part, "type");
2195
+ if (partType === "tool_use") {
2196
+ const id = stringField$1(part, "id") ?? `tool-${actions.length}`;
2197
+ const name = stringField$1(part, "name") ?? "tool";
2198
+ const action = actionFor({
2199
+ id,
2200
+ stepIndex: actions.length,
2201
+ kind: "tool",
2202
+ surface: surfaceForTool(name),
2203
+ name,
2204
+ status: "started",
2205
+ timestampMs,
2206
+ metadata: compactMetadata({
2207
+ sourceEventType: entryType,
2208
+ partType
2209
+ })
2210
+ });
2211
+ calls.set(id, action);
2212
+ actions.push(action);
2213
+ }
2214
+ }
2215
+ completeClaudeTools(content, calls);
2216
+ const stopReason = stringField$1(message ?? {}, "stop_reason");
2217
+ if (stopReason === "end_turn") {
2218
+ terminal = "completed";
2219
+ explicitTerminal = true;
2220
+ actions.push(terminalAction(actions.length, stopReason, true, timestampMs));
2221
+ }
2222
+ }
2223
+ if (entryType === "user") completeClaudeTools(content, calls);
2224
+ if (entryType === "tool_use") {
2225
+ const id = stringField$1(entry, "id") ?? `tool-${actions.length}`;
2226
+ const name = stringField$1(entry, "name") ?? "tool";
2227
+ const action = actionFor({
2228
+ id,
2229
+ stepIndex: actions.length,
2230
+ kind: "tool",
2231
+ surface: surfaceForTool(name),
2232
+ name,
2233
+ status: "started",
2234
+ timestampMs,
2235
+ metadata: { sourceEventType: entryType }
2236
+ });
2237
+ calls.set(id, action);
2238
+ actions.push(action);
2239
+ }
2240
+ if (entryType === "tool_result") {
2241
+ const id = stringField$1(entry, "tool_use_id");
2242
+ const action = id ? calls.get(id) : void 0;
2243
+ if (action) action.status = entry.is_error === true ? "failed" : "completed";
2244
+ }
2245
+ if (entryType === "system") {
2246
+ const subtype = stringField$1(entry, "subtype");
2247
+ const hookName = stringField$1(entry, "hook_name") ?? stringField$1(entry, "hookName");
2248
+ if (subtype?.toLowerCase().includes("hook") || hookName) actions.push(actionFor({
2249
+ id: stringField$1(entry, "uuid") ?? `hook-${actions.length}`,
2250
+ stepIndex: actions.length,
2251
+ kind: "tool",
2252
+ surface: "hook",
2253
+ name: hookName ?? subtype ?? "hook",
2254
+ status: statusFrom(entry),
2255
+ timestampMs,
2256
+ metadata: compactMetadata({
2257
+ sourceEventType: entryType,
2258
+ subtype
2259
+ })
2260
+ }));
2261
+ }
2262
+ if (entryType === "pr-link") actions.push(terminalAction(actions.length, entryType, true, timestampMs));
2263
+ if (entryType === "result") {
2264
+ resultText = nonEmpty(stringField$1(entry, "result")) ?? resultText;
2265
+ const subtype = stringField$1(entry, "subtype");
2266
+ const completed = entry.is_error !== true && !subtype?.toLowerCase().includes("error");
2267
+ terminal = completed ? "completed" : "failed";
2268
+ explicitTerminal = true;
2269
+ actions.push(terminalAction(actions.length, subtype ?? entryType, completed, timestampMs));
2270
+ }
2271
+ if (entryType === "error") {
2272
+ terminal = "failed";
2273
+ explicitTerminal = true;
2274
+ actions.push(terminalAction(actions.length, entryType, false, timestampMs));
2275
+ }
2276
+ }
2277
+ return {
2278
+ finalText: resultText ?? finalText,
2279
+ terminal,
2280
+ explicitTerminal,
2281
+ actions
2282
+ };
2283
+ }
2284
+ function openCodeProjection(entries) {
2285
+ const actions = [];
2286
+ const assistantMessageIds = new Set(entries.filter((entry) => stringField$1(entry, "role") === "assistant").map((entry) => stringField$1(entry, "id")).filter((id) => id !== void 0));
2287
+ let finalText = null;
2288
+ let terminal = "unknown";
2289
+ let explicitTerminal = false;
2290
+ for (const entry of entries) {
2291
+ const entryType = stringField$1(entry, "type");
2292
+ const part = record$1(entry.part) ?? entry;
2293
+ const partType = stringField$1(part, "type");
2294
+ const timestampMs = timestamp(entry.timestamp) ?? timestamp(record$1(entry.time)?.created);
2295
+ if (entryType === "text" && record$1(entry.part)) finalText = nonEmpty(stringField$1(part, "text")) ?? finalText;
2296
+ else if (partType === "text" && (assistantMessageIds.size === 0 || assistantMessageIds.has(stringField$1(part, "messageID") ?? ""))) finalText = nonEmpty(stringField$1(part, "text")) ?? finalText;
2297
+ if (entryType === "tool_use" || partType === "tool") {
2298
+ const state = record$1(part.state) ?? {};
2299
+ const name = stringField$1(part, "tool") ?? "tool";
2300
+ actions.push(actionFor({
2301
+ id: stringField$1(part, "callID") ?? stringField$1(part, "id") ?? `tool-${actions.length}`,
2302
+ stepIndex: actions.length,
2303
+ kind: "tool",
2304
+ surface: surfaceForTool(name),
2305
+ name,
2306
+ status: statusFrom(state),
2307
+ timestampMs,
2308
+ metadata: compactMetadata({
2309
+ sourceEventType: entryType,
2310
+ partType
2311
+ })
2312
+ }));
2313
+ }
2314
+ if (partType === "patch") actions.push(actionFor({
2315
+ id: stringField$1(part, "id") ?? `patch-${actions.length}`,
2316
+ stepIndex: actions.length,
2317
+ kind: "patch",
2318
+ surface: "code",
2319
+ name: "patch",
2320
+ status: "completed",
2321
+ timestampMs,
2322
+ metadata: compactMetadata({
2323
+ sourceEventType: entryType,
2324
+ partType
2325
+ })
2326
+ }));
2327
+ if (stringField$1(entry, "role") === "assistant") {
2328
+ const finish = stringField$1(entry, "finish");
2329
+ if (finish === "stop" || finish === "error") {
2330
+ const completed = finish === "stop";
2331
+ terminal = completed ? "completed" : "failed";
2332
+ explicitTerminal = true;
2333
+ actions.push(terminalAction(actions.length, finish, completed, timestampMs));
2334
+ }
2335
+ }
2336
+ if (entryType === "error") {
2337
+ terminal = "failed";
2338
+ explicitTerminal = true;
2339
+ actions.push(terminalAction(actions.length, entryType, false, timestampMs));
2340
+ }
2341
+ }
2342
+ return {
2343
+ finalText,
2344
+ terminal,
2345
+ explicitTerminal,
2346
+ actions
2347
+ };
2348
+ }
2349
+ function kimiProjection(entries) {
2350
+ const actions = [];
2351
+ const calls = /* @__PURE__ */ new Map();
2352
+ let finalText = null;
2353
+ let terminal = "unknown";
2354
+ let explicitTerminal = false;
2355
+ for (const entry of entries) {
2356
+ const timestampMs = timestamp(entry.timestamp);
2357
+ const message = record$1(entry.message);
2358
+ const messageType = stringField$1(message ?? {}, "type");
2359
+ const payload = record$1(message?.payload) ?? {};
2360
+ if (messageType === "ContentPart" && stringField$1(payload, "type") === "text") finalText = nonEmpty(stringField$1(payload, "text")) ?? nonEmpty(stringField$1(payload, "content")) ?? finalText;
2361
+ if (messageType === "ToolCall") {
2362
+ const fn = record$1(payload.function) ?? {};
2363
+ const id = stringField$1(payload, "id") ?? `tool-${actions.length}`;
2364
+ const name = stringField$1(fn, "name") ?? "tool";
2365
+ const action = actionFor({
2366
+ id,
2367
+ stepIndex: actions.length,
2368
+ kind: "tool",
2369
+ surface: surfaceForTool(name),
2370
+ name,
2371
+ status: "started",
2372
+ timestampMs,
2373
+ metadata: { sourceEventType: messageType }
2374
+ });
2375
+ calls.set(id, action);
2376
+ actions.push(action);
2377
+ }
2378
+ if (messageType === "ToolResult") {
2379
+ const id = stringField$1(payload, "tool_call_id");
2380
+ const action = id ? calls.get(id) : void 0;
2381
+ if (action) action.status = record$1(payload.return_value)?.is_error === true ? "failed" : "completed";
2382
+ }
2383
+ if (messageType === "TurnEnd" || messageType === "StepInterrupted") {
2384
+ const completed = messageType === "TurnEnd";
2385
+ terminal = completed ? "completed" : "failed";
2386
+ explicitTerminal = true;
2387
+ actions.push(terminalAction(actions.length, messageType, completed, timestampMs));
2388
+ }
2389
+ }
2390
+ return {
2391
+ finalText,
2392
+ terminal,
2393
+ explicitTerminal,
2394
+ actions
2395
+ };
2396
+ }
2397
+ function piProjection(entries) {
2398
+ const actions = [];
2399
+ let terminal = "unknown";
2400
+ let explicitTerminal = false;
2401
+ for (const entry of entries) {
2402
+ const nodes = Array.isArray(entry.nodes) ? entry.nodes : [];
2403
+ for (const value of nodes) {
2404
+ const node = record$1(value);
2405
+ const ir = record$1(node?.ir) ?? node;
2406
+ const kind = stringField$1(ir ?? {}, "kind");
2407
+ if (kind === "ToolInvocation") actions.push(actionFor({
2408
+ id: stringField$1(ir ?? {}, "id") ?? stringField$1(node ?? {}, "id") ?? `tool-${actions.length}`,
2409
+ stepIndex: actions.length,
2410
+ kind: "tool",
2411
+ surface: "tool",
2412
+ name: stringField$1(ir ?? {}, "name") ?? "graph-tool",
2413
+ status: "started",
2414
+ timestampMs: timestamp(ir?.createdAt),
2415
+ metadata: {
2416
+ sourceEventType: "graph-node",
2417
+ graphKind: kind
2418
+ }
2419
+ }));
2420
+ if (kind === "ToolResult") {
2421
+ const prior = [...actions].reverse().find((action) => action.kind === "tool");
2422
+ if (prior) prior.status = "completed";
2423
+ }
2424
+ if (kind === "CompletionDecision") {
2425
+ terminal = "completed";
2426
+ explicitTerminal = true;
2427
+ actions.push(actionFor({
2428
+ id: stringField$1(ir ?? {}, "id") ?? stringField$1(node ?? {}, "id") ?? `completion-${actions.length}`,
2429
+ stepIndex: actions.length,
2430
+ kind: "graph-completion",
2431
+ surface: "tool",
2432
+ name: "complete",
2433
+ status: "completed",
2434
+ timestampMs: timestamp(ir?.createdAt),
2435
+ metadata: {
2436
+ sourceEventType: "graph-node",
2437
+ graphKind: kind
2438
+ }
2439
+ }));
2440
+ }
2441
+ }
2442
+ }
2443
+ return {
2444
+ finalText: null,
2445
+ terminal,
2446
+ explicitTerminal,
2447
+ actions
2448
+ };
2449
+ }
2450
+ function completeClaudeTools(content, calls) {
2451
+ for (const value of content) {
2452
+ const part = record$1(value);
2453
+ if (!part || stringField$1(part, "type") !== "tool_result") continue;
2454
+ const id = stringField$1(part, "tool_use_id");
2455
+ const action = id ? calls.get(id) : void 0;
2456
+ if (action) action.status = part.is_error === true ? "failed" : "completed";
2457
+ }
2458
+ }
2459
+ function isCodexActionItem(itemType) {
2460
+ return itemType === "command_execution" || itemType === "mcp_tool_call" || itemType === "collab_tool_call" || itemType === "web_search" || itemType === "file_change";
2461
+ }
2462
+ function codexItemSurface(itemType, item) {
2463
+ if (itemType === "mcp_tool_call") return "mcp";
2464
+ if (itemType === "collab_tool_call") return "subagent";
2465
+ if (itemType === "web_search") return "web";
2466
+ if (itemType === "file_change") return "code";
2467
+ return surfaceForTool(stringField$1(item, "name") ?? itemType ?? "tool");
2468
+ }
2469
+ function codexItemName(itemType, item) {
2470
+ if (itemType === "mcp_tool_call") return [stringField$1(item, "server"), stringField$1(item, "tool")].filter(Boolean).join("/") || "mcp";
2471
+ if (itemType === "collab_tool_call") return stringField$1(item, "tool") ?? "subagent";
2472
+ if (itemType === "web_search") return "web_search";
2473
+ if (itemType === "file_change") return "file_change";
2474
+ return stringField$1(item, "name") ?? itemType ?? "tool";
2475
+ }
2476
+ function codexItemStatus(itemType, item) {
2477
+ const status = statusFrom(item);
2478
+ if (status === "failed") return status;
2479
+ if (itemType === "command_execution") {
2480
+ const exitCode = numberField$1(item, "exit_code");
2481
+ if (exitCode !== void 0) return exitCode === 0 ? "completed" : "failed";
2482
+ }
2483
+ if (itemType === "mcp_tool_call" && record$1(item.error)) return "failed";
2484
+ return status === "unknown" || status === "started" ? "completed" : status;
2485
+ }
2486
+ function textFromMessagePayload(payload) {
2487
+ const direct = nonEmpty(stringField$1(payload, "text"));
2488
+ if (direct) return direct;
2489
+ const content = payload.content;
2490
+ if (typeof content === "string") return nonEmpty(content);
2491
+ if (Array.isArray(content)) return textBlocks(content);
2492
+ return null;
2493
+ }
2494
+ function textBlocks(values) {
2495
+ return nonEmpty(values.map((value) => record$1(value)).filter((value) => value !== null).filter((value) => stringField$1(value, "type") === "text").map((value) => stringField$1(value, "text")).filter((value) => value !== void 0).join("\n"));
2496
+ }
2497
+ function terminalWithExecution(projection, execution) {
2498
+ if (!execution) return {
2499
+ status: projection.terminal,
2500
+ explicit: projection.explicitTerminal
2501
+ };
2502
+ assertExecutionReceipt(execution);
2503
+ return {
2504
+ status: execution.exitCode !== 0 || projection.terminal === "failed" ? "failed" : "completed",
2505
+ explicit: true
2506
+ };
2507
+ }
2508
+ function assertExecutionReceipt(receipt) {
2509
+ if (!Number.isSafeInteger(receipt.exitCode)) throw new Error("code-agent execution exitCode must be a safe integer");
2510
+ for (const [name, value] of [["startedAtMs", receipt.startedAtMs], ["completedAtMs", receipt.completedAtMs]]) if (value !== void 0 && (!Number.isFinite(value) || value < 0)) throw new Error(`code-agent execution ${name} must be a non-negative finite number`);
2511
+ if (receipt.startedAtMs !== void 0 && receipt.completedAtMs !== void 0 && receipt.completedAtMs < receipt.startedAtMs) throw new Error("code-agent execution completedAtMs precedes startedAtMs");
2512
+ }
2513
+ function terminalAction(stepIndex, name, completed, timestampMs) {
2514
+ return actionFor({
2515
+ id: `terminal-${stepIndex}`,
2516
+ stepIndex,
2517
+ kind: "terminal",
2518
+ surface: "tool",
2519
+ name,
2520
+ status: completed ? "completed" : "failed",
2521
+ timestampMs,
2522
+ metadata: { sourceEventType: name }
2523
+ });
2524
+ }
2525
+ function actionFor(input) {
2526
+ return input;
2527
+ }
2528
+ function surfaceForTool(name) {
2529
+ const normalized = name.toLowerCase();
2530
+ if (normalized.startsWith("mcp__") || normalized.includes("mcp_tool")) return "mcp";
2531
+ if (normalized === "task" || normalized === "agent" || normalized.includes("subagent") || normalized.includes("spawn_agent") || normalized.includes("collab") || normalized.startsWith("multi_agent")) return "subagent";
2532
+ if (normalized === "skill" || normalized.includes("skill")) return "skill";
2533
+ if (normalized.includes("hook")) return "hook";
2534
+ if (normalized.includes("web_search") || normalized.includes("webfetch") || normalized === "web") return "web";
2535
+ if (normalized === "edit" || normalized === "write" || normalized.includes("patch") || normalized.includes("file_change")) return "code";
2536
+ return "tool";
2537
+ }
2538
+ function statusFrom(value) {
2539
+ const status = (stringField$1(value, "status") ?? "").toLowerCase();
2540
+ if (status === "completed" || status === "success" || status === "succeeded") return "completed";
2541
+ if (status === "error" || status === "failed" || status === "declined") return "failed";
2542
+ if (status === "running" || status === "started" || status === "in_progress") return "started";
2543
+ return "unknown";
2544
+ }
2545
+ function compactMetadata(input) {
2546
+ return Object.fromEntries(Object.entries(input).filter((entry) => entry[1] !== void 0));
2547
+ }
2548
+ function sessionIdFromEntries$1(source, entries) {
2549
+ for (const entry of entries) {
2550
+ if (source === "codex") {
2551
+ const threadId = stringField$1(entry, "thread_id");
2552
+ if (threadId) return threadId;
2553
+ const payload = record$1(entry.payload);
2554
+ const id = payload ? stringField$1(payload, "id") : void 0;
2555
+ if (id) return id;
2556
+ }
2557
+ const payload = record$1(record$1(entry.message)?.payload);
2558
+ const sessionId = stringField$1(entry, "sessionID") ?? stringField$1(entry, "sessionId") ?? stringField$1(entry, "session_id") ?? stringField$1(payload ?? {}, "session_id");
2559
+ if (sessionId) return sessionId;
2560
+ }
2561
+ }
2562
+ function fallbackSessionId$1(source, sourcePath) {
2563
+ return `${source}:${sourcePath ?? "unknown-session"}`;
2564
+ }
2565
+ function looksLikeError(value) {
2566
+ if (isRecord$1(value)) {
2567
+ if (value.success === false || value.is_error === true) return true;
2568
+ const exitCode = numberField$1(value, "exit_code");
2569
+ if (exitCode !== void 0 && exitCode !== 0) return true;
2570
+ }
2571
+ const text = typeof value === "string" ? value : JSON.stringify(value ?? "");
2572
+ return /\b(?:error|failed|failure)\b|exit(?:_| )code["\s:]+[1-9]/i.test(text);
2573
+ }
2574
+ function nonEmpty(value) {
2575
+ return value !== void 0 && value.length > 0 ? value : null;
2576
+ }
2577
+ function isRecord$1(value) {
2578
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2579
+ }
2580
+ function record$1(value) {
2581
+ return isRecord$1(value) ? value : null;
2582
+ }
2583
+ function stringField$1(value, key) {
2584
+ const field = value[key];
2585
+ return typeof field === "string" && field.length > 0 ? field : void 0;
2586
+ }
2587
+ function numberField$1(value, key) {
2588
+ const field = value[key];
2589
+ return typeof field === "number" && Number.isFinite(field) ? field : void 0;
2590
+ }
2591
+ function timestamp(value) {
2592
+ if (typeof value === "number" && Number.isFinite(value)) return value > 0xe8d4a51000 ? value : value * 1e3;
2593
+ if (typeof value === "string" && value.length > 0) {
2594
+ const parsed = Date.parse(value);
2595
+ return Number.isFinite(parsed) ? parsed : void 0;
2596
+ }
2597
+ }
2598
+ //#endregion
2599
+ //#region src/contract/intake/code-agent-session.ts
2600
+ function parseCodeAgentJsonl(jsonl) {
2601
+ const entries = [];
2602
+ let malformedLines = 0;
2603
+ for (const line of jsonl.split("\n")) {
2604
+ const trimmed = line.trim();
2605
+ if (!trimmed) continue;
2606
+ try {
2607
+ entries.push(JSON.parse(trimmed));
2608
+ } catch {
2609
+ malformedLines += 1;
2610
+ }
2611
+ }
2612
+ return {
2613
+ entries,
2614
+ malformedLines
2615
+ };
2616
+ }
2617
+ function fromCodexSession(options) {
2618
+ return fromCodeAgentSession("codex", options);
2619
+ }
2620
+ function fromClaudeCodeSession(options) {
2621
+ return fromCodeAgentSession("claude-code", options);
2622
+ }
2623
+ function fromOpenCodeSession(options) {
2624
+ return fromCodeAgentSession("opencode", options);
2625
+ }
2626
+ function fromKimiCodeSession(options) {
2627
+ return fromCodeAgentSession("kimi-code", options);
2628
+ }
2629
+ function fromPiSession(options) {
2630
+ return fromCodeAgentSession("pi", options);
2631
+ }
2632
+ const fromPigraphSession = fromPiSession;
2633
+ function fromCodeAgentSession(source, options) {
2634
+ const entries = options.entries.filter(isRecord);
2635
+ if (entries.length === 0) return {
2636
+ runs: [],
2637
+ diagnostics: [{
2638
+ source,
2639
+ sessionId: fallbackSessionId(source, options.sourcePath),
2640
+ sourcePath: options.sourcePath,
2641
+ entries: 0,
2642
+ malformedLines: options.malformedLines ?? 0,
2643
+ hasExplicitTerminalSignal: false,
2644
+ hasFinalOutput: false,
2645
+ hasQualityLabel: false,
2646
+ hasTokenUsage: false,
2647
+ hasCost: false,
2648
+ costKind: "uncaptured",
2649
+ warnings: ["no parseable session entries"]
2650
+ }],
2651
+ metrics: [],
2652
+ observations: []
2653
+ };
2654
+ const metrics = metricsFor(source, entries);
2655
+ const sessionId = sessionIdFromEntries(source, entries) ?? fallbackSessionId(source, options.sourcePath);
2656
+ const observation = observeCodeAgentSession({
2657
+ source,
2658
+ entries,
2659
+ sourcePath: options.sourcePath,
2660
+ execution: options.execution
2661
+ });
2662
+ applyObservedSurfaceMetrics(metrics, observation);
2663
+ const model = withSnapshot(options.model ?? modelFromEntries(source, entries) ?? source);
2664
+ const tokenUsage = {
2665
+ input: metrics.inputTokens,
2666
+ output: metrics.outputTokens,
2667
+ ...metrics.reasoningTokens > 0 ? { reasoning: metrics.reasoningTokens } : {},
2668
+ ...metrics.cachedTokens > 0 ? { cached: metrics.cachedTokens } : {},
2669
+ ...metrics.cacheWriteTokens > 0 ? { cacheWrite: metrics.cacheWriteTokens } : {}
2670
+ };
2671
+ const { costUsd, costProvenance } = resolveSessionCost(options.costProvenance, metrics, model);
2672
+ const qualityScore = finiteScore(options.score);
2673
+ const invalidQualityScore = options.score !== void 0 && qualityScore === void 0;
2674
+ const promptHash = options.promptHash ?? hashString(`prompt:${source}:${firstUserText(entries) ?? ""}`);
2675
+ const configHash = options.configHash ?? hashJson({
2676
+ source,
2677
+ model,
2678
+ sourcePath: options.sourcePath,
2679
+ cwd: cwdFromEntries(entries),
2680
+ entryCount: entries.length
2681
+ });
2682
+ const explicitTerminal = observation.terminal.explicit;
2683
+ const warnings = diagnosticsFor(metrics, {
2684
+ model,
2685
+ explicitTerminal,
2686
+ malformedLines: options.malformedLines ?? 0,
2687
+ hasQualityLabel: qualityScore !== void 0,
2688
+ invalidQualityScore,
2689
+ costKind: costProvenance.kind
2690
+ });
2691
+ const terminalOutcome = terminalOutcomeFromSession(observation.terminal);
2692
+ const processExitCode = options.execution?.exitCode;
2693
+ const splitTag = options.splitTag ?? "holdout";
2694
+ return {
2695
+ runs: [{
2696
+ runId: `${source}:${sessionId}`,
2697
+ experimentId: options.experimentId ?? `${source}-local-sessions`,
2698
+ candidateId: options.candidateId ?? model,
2699
+ seed: options.seed ?? stableSeed(sessionId),
2700
+ model,
2701
+ promptHash,
2702
+ configHash,
2703
+ commitSha: options.commitSha ?? "local-session",
2704
+ wallMs: metrics.wallMs,
2705
+ costUsd,
2706
+ costProvenance,
2707
+ tokenUsage,
2708
+ terminalOutcome,
2709
+ ...terminalOutcome === "failed" ? { terminalFailureReason: processExitCode !== void 0 && processExitCode !== 0 ? `process exited with code ${processExitCode}` : metrics.turnsAborted > 0 ? "turn aborted" : "session reported terminal failure" } : {},
2710
+ outcome: {
2711
+ ...qualityScore === void 0 ? {} : splitTag === "holdout" ? { holdoutScore: qualityScore } : { searchScore: qualityScore },
2712
+ raw: {
2713
+ entries: metrics.entries,
2714
+ user_messages: metrics.userMessages,
2715
+ assistant_messages: metrics.assistantMessages,
2716
+ reasoning_items: metrics.reasoningItems,
2717
+ tool_calls: metrics.toolCalls,
2718
+ tool_outputs: metrics.toolOutputs,
2719
+ tool_errors: metrics.toolErrors,
2720
+ execution_error_count: metrics.toolErrors,
2721
+ process_error_count: terminalOutcome === "failed" ? 1 : 0,
2722
+ unclassified_error_count: metrics.unclassifiedErrors,
2723
+ ...processExitCode !== void 0 ? { process_exit_code: processExitCode } : {},
2724
+ patch_attempts: metrics.patchAttempts,
2725
+ patch_successes: metrics.patchSuccesses,
2726
+ patch_failures: metrics.patchFailures,
2727
+ turns_started: metrics.turnsStarted,
2728
+ turns_completed: metrics.turnsCompleted,
2729
+ turns_aborted: metrics.turnsAborted,
2730
+ context_compactions: metrics.contextCompactions,
2731
+ mcp_calls: metrics.mcpCalls,
2732
+ subagent_calls: metrics.subagentCalls,
2733
+ skill_calls: metrics.skillCalls,
2734
+ hook_calls: metrics.hookCalls,
2735
+ web_calls: metrics.webCalls,
2736
+ code_actions: metrics.codeActions,
2737
+ pr_links: metrics.prLinks,
2738
+ file_snapshots: metrics.fileSnapshots,
2739
+ graph_nodes: metrics.graphNodes,
2740
+ graph_edges: metrics.graphEdges,
2741
+ action_candidates: metrics.actionCandidates,
2742
+ verification_reports: metrics.verificationReports,
2743
+ completion_decisions: metrics.completionDecisions,
2744
+ reliability_rows: metrics.reliabilityRows,
2745
+ reliability_lift: metrics.reliabilityLift,
2746
+ input_tokens: metrics.inputTokens,
2747
+ output_tokens: metrics.outputTokens,
2748
+ reasoning_tokens: metrics.reasoningTokens,
2749
+ cached_tokens: metrics.cachedTokens,
2750
+ cache_write_tokens: metrics.cacheWriteTokens,
2751
+ observed_cost_usd: metrics.observedCostUsd,
2752
+ observed_cost_captured: metrics.observedCostCaptured ? 1 : 0,
2753
+ estimated_cost_usd: costProvenance.kind === "estimated" ? costProvenance.usd : 0,
2754
+ process_score: metrics.processScore,
2755
+ explicit_terminal_signal: explicitTerminal ? 1 : 0,
2756
+ quality_label_present: qualityScore !== void 0 ? 1 : 0,
2757
+ cost_observed: costProvenance.kind === "observed" ? 1 : 0,
2758
+ cost_estimated: costProvenance.kind === "estimated" ? 1 : 0,
2759
+ cost_uncaptured: costProvenance.kind === "uncaptured" ? 1 : 0,
2760
+ cost_unknown: costProvenance.kind === "uncaptured" ? 1 : 0
2761
+ }
2762
+ },
2763
+ splitTag,
2764
+ scenarioId: options.scenarioId ?? sessionId
2765
+ }],
2766
+ diagnostics: [{
2767
+ source,
2768
+ sessionId,
2769
+ sourcePath: options.sourcePath,
2770
+ entries: entries.length,
2771
+ malformedLines: options.malformedLines ?? 0,
2772
+ hasExplicitTerminalSignal: explicitTerminal,
2773
+ hasFinalOutput: observation.finalText !== null,
2774
+ hasQualityLabel: qualityScore !== void 0,
2775
+ hasTokenUsage: metrics.inputTokens > 0 || metrics.outputTokens > 0 || metrics.reasoningTokens > 0 || metrics.cachedTokens > 0 || metrics.cacheWriteTokens > 0,
2776
+ hasCost: costProvenance.kind !== "uncaptured",
2777
+ costKind: costProvenance.kind,
2778
+ warnings
2779
+ }],
2780
+ metrics: [metrics],
2781
+ observations: [{
2782
+ ...observation,
2783
+ sessionId
2784
+ }]
2785
+ };
2786
+ }
2787
+ function terminalOutcomeFromSession(terminal) {
2788
+ if (!terminal.explicit) return "unknown";
2789
+ if (terminal.status === "completed") return "succeeded";
2790
+ if (terminal.status === "failed") return "failed";
2791
+ return "unknown";
2792
+ }
2793
+ function metricsFor(source, entries) {
2794
+ switch (source) {
2795
+ case "codex": return codexMetrics(entries);
2796
+ case "claude-code": return claudeCodeMetrics(entries);
2797
+ case "opencode": return openCodeMetrics(entries);
2798
+ case "kimi-code": return kimiCodeMetrics(entries);
2799
+ case "pi": return piMetrics(entries);
2800
+ }
2801
+ }
2802
+ function codexMetrics(entries) {
2803
+ const metrics = emptyMetrics(entries.length);
2804
+ const startedToolIds = /* @__PURE__ */ new Set();
2805
+ let startedAt;
2806
+ let completedAt;
2807
+ for (const entry of entries) {
2808
+ const payload = record(entry.payload) ?? {};
2809
+ const entryType = stringField(entry, "type");
2810
+ const payloadType = stringField(payload, "type");
2811
+ const timestamp = timestampMs(entry.timestamp);
2812
+ if (timestamp !== void 0) {
2813
+ startedAt = startedAt === void 0 ? timestamp : Math.min(startedAt, timestamp);
2814
+ completedAt = completedAt === void 0 ? timestamp : Math.max(completedAt, timestamp);
2815
+ }
2816
+ if (entryType === "turn.started") metrics.turnsStarted += 1;
2817
+ if (entryType === "turn.completed") {
2818
+ metrics.turnsCompleted += 1;
2819
+ addCodexExecUsage(metrics, record(entry.usage));
2820
+ }
2821
+ if (entryType === "turn.failed") metrics.turnsAborted += 1;
2822
+ if (entryType === "error") metrics.unclassifiedErrors += 1;
2823
+ const item = record(entry.item);
2824
+ if (item && (entryType === "item.started" || entryType === "item.completed")) addCodexExecItem(metrics, startedToolIds, entryType, item);
2825
+ if (entryType === "response_item") {
2826
+ if (payloadType === "function_call" || payloadType === "custom_tool_call") metrics.toolCalls += 1;
2827
+ if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") metrics.toolOutputs += 1;
2828
+ if (payloadType === "reasoning") metrics.reasoningItems += 1;
2829
+ if (payloadType === "message" && stringField(payload, "role") === "assistant") metrics.assistantMessages += 1;
2830
+ }
2831
+ if (entryType === "event_msg") {
2832
+ if (payloadType === "user_message") metrics.userMessages += 1;
2833
+ if (payloadType === "agent_message") metrics.assistantMessages += 1;
2834
+ if (payloadType === "task_started") {
2835
+ metrics.turnsStarted += 1;
2836
+ const started = timestampMs(payload.started_at);
2837
+ if (started !== void 0) startedAt = startedAt === void 0 ? started : Math.min(startedAt, started);
2838
+ }
2839
+ if (payloadType === "task_complete") {
2840
+ metrics.turnsCompleted += 1;
2841
+ const completed = timestampMs(payload.completed_at);
2842
+ if (completed !== void 0) completedAt = completedAt === void 0 ? completed : Math.max(completedAt, completed);
2843
+ const duration = numberField(payload, "duration_ms");
2844
+ if (duration !== void 0) metrics.wallMs = Math.max(metrics.wallMs, duration);
2845
+ }
2846
+ if (payloadType === "turn_aborted") {
2847
+ metrics.turnsAborted += 1;
2848
+ const completed = timestampMs(payload.completed_at);
2849
+ if (completed !== void 0) completedAt = completedAt === void 0 ? completed : Math.max(completedAt, completed);
2850
+ }
2851
+ if (payloadType === "context_compacted") metrics.contextCompactions += 1;
2852
+ if (payloadType === "patch_apply_end") {
2853
+ metrics.patchAttempts += 1;
2854
+ if (payload.success === true) metrics.patchSuccesses += 1;
2855
+ else {
2856
+ metrics.patchFailures += 1;
2857
+ metrics.toolErrors += 1;
2858
+ }
2859
+ }
2860
+ if (payloadType === "token_count") setCodexCumulativeUsage(metrics, record(record(payload.info)?.total_token_usage));
2861
+ const result = record(payload.result);
2862
+ if (result && "Err" in result) metrics.unclassifiedErrors += 1;
2863
+ }
2864
+ }
2865
+ if (metrics.wallMs === 0 && startedAt !== void 0 && completedAt !== void 0) metrics.wallMs = Math.max(0, completedAt - startedAt);
2866
+ metrics.processScore = codexProcessScore(metrics);
2867
+ return metrics;
2868
+ }
2869
+ function addCodexExecItem(metrics, startedToolIds, eventType, item) {
2870
+ const itemType = stringField(item, "type");
2871
+ const itemId = stringField(item, "id");
2872
+ const isTool = itemType === "command_execution" || itemType === "mcp_tool_call" || itemType === "collab_tool_call" || itemType === "web_search";
2873
+ if (eventType === "item.started" && isTool) {
2874
+ metrics.toolCalls += 1;
2875
+ if (itemId) startedToolIds.add(itemId);
2876
+ }
2877
+ if (eventType === "item.completed" && isTool) {
2878
+ if (!itemId || !startedToolIds.has(itemId)) metrics.toolCalls += 1;
2879
+ metrics.toolOutputs += 1;
2880
+ }
2881
+ if (eventType !== "item.completed") return;
2882
+ if (itemType === "agent_message" || itemType === "message") metrics.assistantMessages += 1;
2883
+ if (itemType === "reasoning") metrics.reasoningItems += 1;
2884
+ if (itemType === "file_change") {
2885
+ metrics.patchAttempts += 1;
2886
+ const status = stringField(item, "status");
2887
+ if (status === "completed") metrics.patchSuccesses += 1;
2888
+ if (status === "failed") {
2889
+ metrics.patchFailures += 1;
2890
+ metrics.toolErrors += 1;
2891
+ }
2892
+ }
2893
+ if (itemType === "error") metrics.unclassifiedErrors += 1;
2894
+ else if (codexExecToolFailed(itemType, item)) metrics.toolErrors += 1;
2895
+ }
2896
+ function codexExecToolFailed(itemType, item) {
2897
+ const status = stringField(item, "status");
2898
+ if (itemType === "command_execution") {
2899
+ const exitCode = numberField(item, "exit_code");
2900
+ return status === "failed" || status === "declined" || exitCode !== void 0 && exitCode !== 0;
2901
+ }
2902
+ if (itemType === "mcp_tool_call") return status === "failed" || record(item.error) !== null;
2903
+ return itemType === "collab_tool_call" && status === "failed";
2904
+ }
2905
+ function addCodexExecUsage(metrics, usage) {
2906
+ const parsed = readUsage(usage);
2907
+ metrics.inputTokens += parsed.input;
2908
+ metrics.outputTokens += parsed.output;
2909
+ metrics.reasoningTokens += parsed.reasoning;
2910
+ metrics.cachedTokens += parsed.cached;
2911
+ metrics.cacheWriteTokens += parsed.cacheWrite;
2912
+ }
2913
+ function setCodexCumulativeUsage(metrics, usage) {
2914
+ const parsed = readUsage(usage);
2915
+ metrics.inputTokens = Math.max(metrics.inputTokens, parsed.input);
2916
+ metrics.outputTokens = Math.max(metrics.outputTokens, parsed.output);
2917
+ metrics.reasoningTokens = Math.max(metrics.reasoningTokens, parsed.reasoning);
2918
+ metrics.cachedTokens = Math.max(metrics.cachedTokens, parsed.cached);
2919
+ metrics.cacheWriteTokens = Math.max(metrics.cacheWriteTokens, parsed.cacheWrite);
2920
+ }
2921
+ function claudeCodeMetrics(entries) {
2922
+ const metrics = emptyMetrics(entries.length);
2923
+ let startedAt;
2924
+ let completedAt;
2925
+ for (const entry of entries) {
2926
+ const type = stringField(entry, "type");
2927
+ const timestamp = timestampMs(entry.timestamp);
2928
+ if (timestamp !== void 0) {
2929
+ startedAt = startedAt === void 0 ? timestamp : Math.min(startedAt, timestamp);
2930
+ completedAt = completedAt === void 0 ? timestamp : Math.max(completedAt, timestamp);
2931
+ }
2932
+ if (type === "user") metrics.userMessages += 1;
2933
+ if (type === "assistant") metrics.assistantMessages += 1;
2934
+ if (type === "pr-link") metrics.prLinks += 1;
2935
+ if (type === "file-history-snapshot") metrics.fileSnapshots += 1;
2936
+ const message = record(entry.message);
2937
+ if (message) addUsage(metrics, record(message.usage));
2938
+ const content = Array.isArray(message?.content) ? message.content : [];
2939
+ for (const item of content) {
2940
+ const part = record(item);
2941
+ if (!part) continue;
2942
+ const partType = stringField(part, "type");
2943
+ if (partType === "thinking") metrics.reasoningItems += 1;
2944
+ if (partType === "tool_use") metrics.toolCalls += 1;
2945
+ if (partType === "tool_result") {
2946
+ metrics.toolOutputs += 1;
2947
+ if (part.is_error === true) metrics.toolErrors += 1;
2948
+ }
2949
+ }
2950
+ }
2951
+ if (startedAt !== void 0 && completedAt !== void 0) metrics.wallMs = Math.max(0, completedAt - startedAt);
2952
+ metrics.processScore = claudeProcessScore(metrics);
2953
+ return metrics;
2954
+ }
2955
+ function openCodeMetrics(entries) {
2956
+ const metrics = emptyMetrics(entries.length);
2957
+ const messageUsage = emptyTokenTotals();
2958
+ const partUsage = emptyTokenTotals();
2959
+ let messageCost = 0;
2960
+ let partCost = 0;
2961
+ let messageCostCaptured = false;
2962
+ let partCostCaptured = false;
2963
+ let startedAt;
2964
+ let completedAt;
2965
+ for (const entry of entries) {
2966
+ const time = record(entry.time);
2967
+ const created = timestampMs(time?.created);
2968
+ const completed = timestampMs(time?.completed);
2969
+ if (created !== void 0) startedAt = startedAt === void 0 ? created : Math.min(startedAt, created);
2970
+ if (completed !== void 0) completedAt = completedAt === void 0 ? completed : Math.max(completedAt, completed);
2971
+ const role = stringField(entry, "role");
2972
+ if (role === "user") metrics.userMessages += 1;
2973
+ if (role === "assistant") {
2974
+ metrics.assistantMessages += 1;
2975
+ const finish = stringField(entry, "finish");
2976
+ if (finish === "stop") metrics.turnsCompleted += 1;
2977
+ if (finish === "error") metrics.turnsAborted += 1;
2978
+ }
2979
+ const type = stringField(entry, "type");
2980
+ if (type === "reasoning") metrics.reasoningItems += 1;
2981
+ if (type === "tool") {
2982
+ metrics.toolCalls += 1;
2983
+ const status = stringField(record(entry.state) ?? {}, "status");
2984
+ if (status === "completed") metrics.toolOutputs += 1;
2985
+ if (status === "error") {
2986
+ metrics.toolOutputs += 1;
2987
+ metrics.toolErrors += 1;
2988
+ }
2989
+ }
2990
+ if (type === "patch") {
2991
+ metrics.patchAttempts += 1;
2992
+ metrics.patchSuccesses += 1;
2993
+ }
2994
+ const cost = numberField(entry, "cost");
2995
+ if (record(entry.tokens) && role) {
2996
+ addUsageTo(messageUsage, record(entry.tokens));
2997
+ if (cost !== void 0) {
2998
+ messageCost += cost;
2999
+ messageCostCaptured = true;
3000
+ }
3001
+ } else if (record(entry.tokens)) {
3002
+ addUsageTo(partUsage, record(entry.tokens));
3003
+ if (cost !== void 0) {
3004
+ partCost += cost;
3005
+ partCostCaptured = true;
3006
+ }
3007
+ }
3008
+ }
3009
+ const usage = hasTokenTotals(messageUsage) ? messageUsage : partUsage;
3010
+ metrics.inputTokens = usage.input;
3011
+ metrics.outputTokens = usage.output;
3012
+ metrics.reasoningTokens = usage.reasoning;
3013
+ metrics.cachedTokens = usage.cached;
3014
+ metrics.cacheWriteTokens = usage.cacheWrite;
3015
+ metrics.observedCostUsd = messageCostCaptured ? messageCost : partCost;
3016
+ metrics.observedCostCaptured = messageCostCaptured || partCostCaptured;
3017
+ if (metrics.wallMs === 0 && startedAt !== void 0 && completedAt !== void 0) metrics.wallMs = Math.max(0, completedAt - startedAt);
3018
+ metrics.processScore = terminalProcessScore(metrics);
3019
+ return metrics;
3020
+ }
3021
+ function kimiCodeMetrics(entries) {
3022
+ const metrics = emptyMetrics(entries.length);
3023
+ let startedAt;
3024
+ let completedAt;
3025
+ for (const entry of entries) {
3026
+ const timestamp = timestampMs(entry.timestamp);
3027
+ if (timestamp !== void 0) {
3028
+ startedAt = startedAt === void 0 ? timestamp : Math.min(startedAt, timestamp);
3029
+ completedAt = completedAt === void 0 ? timestamp : Math.max(completedAt, timestamp);
3030
+ }
3031
+ const role = stringField(entry, "role");
3032
+ if (role === "user") metrics.userMessages += 1;
3033
+ if (role === "assistant") metrics.assistantMessages += 1;
3034
+ if (role === "_usage") addUsage(metrics, entry);
3035
+ const message = record(entry.message);
3036
+ const messageType = message ? stringField(message, "type") : void 0;
3037
+ const payload = record(message?.payload) ?? {};
3038
+ if (messageType === "TurnBegin") {
3039
+ metrics.userMessages += 1;
3040
+ metrics.turnsStarted += 1;
3041
+ }
3042
+ if (messageType === "TurnEnd") metrics.turnsCompleted += 1;
3043
+ if (messageType === "StepInterrupted") metrics.turnsAborted += 1;
3044
+ if (messageType === "StepBegin") metrics.reasoningItems += 1;
3045
+ if (messageType === "ContentPart") {
3046
+ const payloadType = stringField(payload, "type");
3047
+ if (payloadType === "think") metrics.reasoningItems += 1;
3048
+ if (payloadType === "text") metrics.assistantMessages += 1;
3049
+ }
3050
+ if (messageType === "ToolCall") metrics.toolCalls += 1;
3051
+ if (messageType === "ToolResult") {
3052
+ metrics.toolOutputs += 1;
3053
+ if (record(payload.return_value)?.is_error === true) metrics.toolErrors += 1;
3054
+ }
3055
+ if (messageType === "StatusUpdate") addUsage(metrics, record(payload.token_usage));
3056
+ if (messageType === "Notification") {
3057
+ const notification = record(payload.payload);
3058
+ const exitCode = numberField(notification ?? {}, "exit_code");
3059
+ if (notification?.timed_out === true || exitCode !== void 0 && exitCode !== 0) metrics.toolErrors += 1;
3060
+ }
3061
+ }
3062
+ if (startedAt !== void 0 && completedAt !== void 0) metrics.wallMs = Math.max(0, completedAt - startedAt);
3063
+ metrics.processScore = terminalProcessScore(metrics);
3064
+ return metrics;
3065
+ }
3066
+ function piMetrics(entries) {
3067
+ const metrics = emptyMetrics(entries.length);
3068
+ let bestReliabilityScore;
3069
+ for (const entry of entries) {
3070
+ const nodes = Array.isArray(entry.nodes) ? entry.nodes : [];
3071
+ const edges = Array.isArray(entry.edges) ? entry.edges : [];
3072
+ metrics.graphNodes += nodes.length;
3073
+ metrics.graphEdges += edges.length;
3074
+ for (const node of nodes) {
3075
+ const obj = record(node);
3076
+ const kind = stringField(record(obj?.ir) ?? obj ?? {}, "kind");
3077
+ if (kind === "ActionCandidate") metrics.actionCandidates += 1;
3078
+ if (kind === "ToolInvocation") metrics.toolCalls += 1;
3079
+ if (kind === "ToolResult") metrics.toolOutputs += 1;
3080
+ if (kind === "VerificationReport") metrics.verificationReports += 1;
3081
+ if (kind === "CompletionDecision") {
3082
+ metrics.completionDecisions += 1;
3083
+ metrics.turnsCompleted += 1;
3084
+ }
3085
+ }
3086
+ const averageReliability = numberField(entry, "averageNodeReliability");
3087
+ if (averageReliability !== void 0) bestReliabilityScore = maxOptional(bestReliabilityScore, averageReliability);
3088
+ const pessimisticPath = numberField(entry, "pessimisticPathEstimate");
3089
+ if (pessimisticPath !== void 0) bestReliabilityScore = maxOptional(bestReliabilityScore, pessimisticPath);
3090
+ const rows = Array.isArray(entry.rows) ? entry.rows : [];
3091
+ metrics.reliabilityRows += rows.length;
3092
+ for (const row of rows) {
3093
+ const obj = record(row);
3094
+ if (!obj) continue;
3095
+ const lift = numberField(obj, "lift");
3096
+ if (lift !== void 0) metrics.reliabilityLift = Math.max(metrics.reliabilityLift, lift);
3097
+ const validated = numberField(obj, "validatedSuccessEstimate");
3098
+ if (validated !== void 0) bestReliabilityScore = maxOptional(bestReliabilityScore, validated);
3099
+ }
3100
+ }
3101
+ metrics.processScore = bestReliabilityScore !== void 0 ? clamp01(bestReliabilityScore) : metrics.completionDecisions > 0 ? 1 : 0;
3102
+ return metrics;
3103
+ }
3104
+ function emptyMetrics(entries) {
3105
+ return {
3106
+ entries,
3107
+ userMessages: 0,
3108
+ assistantMessages: 0,
3109
+ reasoningItems: 0,
3110
+ toolCalls: 0,
3111
+ toolOutputs: 0,
3112
+ toolErrors: 0,
3113
+ unclassifiedErrors: 0,
3114
+ patchAttempts: 0,
3115
+ patchSuccesses: 0,
3116
+ patchFailures: 0,
3117
+ turnsStarted: 0,
3118
+ turnsCompleted: 0,
3119
+ turnsAborted: 0,
3120
+ contextCompactions: 0,
3121
+ mcpCalls: 0,
3122
+ subagentCalls: 0,
3123
+ skillCalls: 0,
3124
+ hookCalls: 0,
3125
+ webCalls: 0,
3126
+ codeActions: 0,
3127
+ prLinks: 0,
3128
+ fileSnapshots: 0,
3129
+ graphNodes: 0,
3130
+ graphEdges: 0,
3131
+ actionCandidates: 0,
3132
+ verificationReports: 0,
3133
+ completionDecisions: 0,
3134
+ reliabilityRows: 0,
3135
+ reliabilityLift: 0,
3136
+ inputTokens: 0,
3137
+ outputTokens: 0,
3138
+ reasoningTokens: 0,
3139
+ cachedTokens: 0,
3140
+ cacheWriteTokens: 0,
3141
+ observedCostUsd: 0,
3142
+ observedCostCaptured: false,
3143
+ wallMs: 0,
3144
+ processScore: 0
3145
+ };
3146
+ }
3147
+ function codexProcessScore(metrics) {
3148
+ const terminalTurns = metrics.turnsCompleted + metrics.turnsAborted;
3149
+ return penalizeErrors(terminalTurns > 0 ? metrics.turnsCompleted / terminalTurns : metrics.assistantMessages > 0 ? .5 : 0, metrics);
3150
+ }
3151
+ function claudeProcessScore(metrics) {
3152
+ return penalizeErrors(metrics.prLinks > 0 ? 1 : metrics.assistantMessages > 0 ? .75 : 0, metrics);
3153
+ }
3154
+ function terminalProcessScore(metrics) {
3155
+ const terminalTurns = metrics.turnsCompleted + metrics.turnsAborted;
3156
+ return penalizeErrors(terminalTurns > 0 ? metrics.turnsCompleted / terminalTurns : metrics.assistantMessages > 0 ? .75 : metrics.toolCalls > 0 ? .5 : 0, metrics);
3157
+ }
3158
+ function penalizeErrors(base, metrics) {
3159
+ const operations = Math.max(1, metrics.toolCalls + metrics.patchAttempts);
3160
+ return clamp01(base * (1 - .5 * Math.min(1, (metrics.toolErrors + metrics.patchFailures) / operations)));
3161
+ }
3162
+ function emptyTokenTotals() {
3163
+ return {
3164
+ input: 0,
3165
+ output: 0,
3166
+ reasoning: 0,
3167
+ cached: 0,
3168
+ cacheWrite: 0
3169
+ };
3170
+ }
3171
+ function hasTokenTotals(usage) {
3172
+ return usage.input > 0 || usage.output > 0 || usage.reasoning > 0 || usage.cached > 0 || usage.cacheWrite > 0;
3173
+ }
3174
+ function addUsage(metrics, usage) {
3175
+ const parsed = readUsage(usage);
3176
+ metrics.inputTokens += parsed.input;
3177
+ metrics.outputTokens += parsed.output;
3178
+ metrics.reasoningTokens += parsed.reasoning;
3179
+ metrics.cachedTokens += parsed.cached;
3180
+ metrics.cacheWriteTokens += parsed.cacheWrite;
3181
+ }
3182
+ function addUsageTo(totals, usage) {
3183
+ const parsed = readUsage(usage);
3184
+ totals.input += parsed.input;
3185
+ totals.output += parsed.output;
3186
+ totals.reasoning += parsed.reasoning;
3187
+ totals.cached += parsed.cached;
3188
+ totals.cacheWrite += parsed.cacheWrite;
3189
+ }
3190
+ function readUsage(usage) {
3191
+ const parsed = extractUsage(usage);
3192
+ return {
3193
+ input: parsed?.input ?? 0,
3194
+ output: parsed?.output ?? 0,
3195
+ reasoning: parsed?.reasoning ?? 0,
3196
+ cached: parsed?.cached ?? 0,
3197
+ cacheWrite: parsed?.cacheWrite ?? 0
3198
+ };
3199
+ }
3200
+ function resolveSessionCost(explicit, metrics, model) {
3201
+ if (explicit) {
3202
+ if (explicit.kind === "uncaptured") {
3203
+ if (explicit.usd !== null) throw new Error("uncaptured cost must have usd: null");
3204
+ return {
3205
+ costUsd: null,
3206
+ costProvenance: explicit
3207
+ };
3208
+ }
3209
+ if (!Number.isFinite(explicit.usd) || explicit.usd < 0) throw new Error(`${explicit.kind} cost must be a finite, non-negative USD amount`);
3210
+ return {
3211
+ costUsd: explicit.usd,
3212
+ costProvenance: explicit
3213
+ };
3214
+ }
3215
+ if (metrics.observedCostCaptured) {
3216
+ const costProvenance = {
3217
+ kind: "observed",
3218
+ usd: metrics.observedCostUsd
3219
+ };
3220
+ return {
3221
+ costUsd: metrics.observedCostUsd,
3222
+ costProvenance
3223
+ };
3224
+ }
3225
+ if ((metrics.inputTokens > 0 || metrics.outputTokens > 0) && isModelPriced(model)) {
3226
+ const costUsd = estimateCost(metrics.inputTokens, metrics.outputTokens, model);
3227
+ return {
3228
+ costUsd,
3229
+ costProvenance: {
3230
+ kind: "estimated",
3231
+ usd: costUsd
3232
+ }
3233
+ };
3234
+ }
3235
+ return {
3236
+ costUsd: null,
3237
+ costProvenance: {
3238
+ kind: "uncaptured",
3239
+ usd: null
3240
+ }
3241
+ };
3242
+ }
3243
+ function diagnosticsFor(metrics, options) {
3244
+ const warnings = [];
3245
+ if (options.invalidQualityScore) warnings.push("non-finite task quality score omitted");
3246
+ else if (!options.hasQualityLabel) warnings.push("task quality score not supplied");
3247
+ if (!options.explicitTerminal) warnings.push("no explicit terminal success/failure signal");
3248
+ if (metrics.inputTokens === 0 && metrics.outputTokens === 0 && metrics.reasoningTokens === 0 && metrics.cachedTokens === 0 && metrics.cacheWriteTokens === 0) warnings.push("missing token usage");
3249
+ if (options.costKind === "estimated") warnings.push("USD cost estimated from token usage and model pricing");
3250
+ if (options.costKind === "uncaptured") warnings.push("USD cost uncaptured");
3251
+ if (options.costKind === "uncaptured" && !isModelPriced(options.model)) warnings.push("model pricing unknown");
3252
+ if (options.malformedLines > 0) warnings.push(`${options.malformedLines} malformed JSONL lines skipped`);
3253
+ return warnings;
3254
+ }
3255
+ function applyObservedSurfaceMetrics(metrics, observation) {
3256
+ const actions = observation.actions.filter((action) => action.kind === "tool" || action.kind === "patch");
3257
+ metrics.mcpCalls = actions.filter((action) => action.surface === "mcp").length;
3258
+ metrics.subagentCalls = actions.filter((action) => action.surface === "subagent").length;
3259
+ metrics.skillCalls = actions.filter((action) => action.surface === "skill").length;
3260
+ metrics.hookCalls = actions.filter((action) => action.surface === "hook").length;
3261
+ metrics.webCalls = actions.filter((action) => action.surface === "web").length;
3262
+ metrics.codeActions = actions.filter((action) => action.surface === "code").length;
3263
+ }
3264
+ function sessionIdFromEntries(source, entries) {
3265
+ for (const entry of entries) {
3266
+ if (source === "codex") {
3267
+ const threadId = stringField(entry, "thread_id");
3268
+ if (threadId) return threadId;
3269
+ const payload = record(entry.payload);
3270
+ const id = payload ? stringField(payload, "id") : void 0;
3271
+ if (id) return id;
3272
+ }
3273
+ const payload = record(record(entry.message)?.payload);
3274
+ const sessionId = stringField(entry, "sessionID") ?? stringField(entry, "sessionId") ?? stringField(entry, "session_id") ?? stringField(payload ?? {}, "session_id");
3275
+ if (sessionId) return sessionId;
3276
+ }
3277
+ }
3278
+ function modelFromEntries(source, entries) {
3279
+ let providerModel;
3280
+ for (const entry of entries) {
3281
+ if (source === "codex") {
3282
+ const payload = record(entry.payload);
3283
+ const model = payload ? stringField(payload, "model") : void 0;
3284
+ if (model) return model;
3285
+ providerModel = providerModel ?? (payload ? stringField(payload, "model_provider") : void 0);
3286
+ }
3287
+ const openCodeModel = stringField(entry, "modelID");
3288
+ if (openCodeModel) {
3289
+ const provider = stringField(entry, "providerID");
3290
+ return provider ? `${provider}/${openCodeModel}` : openCodeModel;
3291
+ }
3292
+ const modelObject = record(entry.model);
3293
+ const nestedModel = stringField(modelObject ?? {}, "modelID");
3294
+ if (nestedModel) {
3295
+ const provider = stringField(modelObject ?? {}, "providerID");
3296
+ return provider ? `${provider}/${nestedModel}` : nestedModel;
3297
+ }
3298
+ const message = record(entry.message);
3299
+ const model = message ? stringField(message, "model") : void 0;
3300
+ if (model) return model;
3301
+ }
3302
+ return providerModel;
3303
+ }
3304
+ function cwdFromEntries(entries) {
3305
+ for (const entry of entries) {
3306
+ const path = record(entry.path);
3307
+ const cwd = stringField(entry, "cwd") ?? stringField(path ?? {}, "cwd") ?? stringField(path ?? {}, "root") ?? (record(entry.payload) ? stringField(record(entry.payload), "cwd") : void 0);
3308
+ if (cwd) return cwd;
3309
+ }
3310
+ }
3311
+ function firstUserText(entries) {
3312
+ for (const entry of entries) {
3313
+ const payload = record(entry.payload);
3314
+ if ((payload ? stringField(payload, "type") : void 0) === "user_message") return stringField(payload, "message");
3315
+ if (stringField(entry, "role") === "user") {
3316
+ const content = entry.content;
3317
+ if (typeof content === "string") return content;
3318
+ }
3319
+ const wirePayload = record(record(entry.message)?.payload);
3320
+ const userInput = wirePayload ? stringField(wirePayload, "user_input") : void 0;
3321
+ if (userInput) return userInput;
3322
+ const message = record(entry.message);
3323
+ if (message && stringField(message, "role") === "user") {
3324
+ const content = message.content;
3325
+ if (typeof content === "string") return content;
3326
+ if (Array.isArray(content)) {
3327
+ const text = content.map((part) => {
3328
+ const obj = record(part);
3329
+ return obj ? stringField(obj, "text") : void 0;
3330
+ }).filter((part) => part !== void 0).join("\n");
3331
+ if (text) return text;
3332
+ }
3333
+ }
3334
+ }
3335
+ }
3336
+ function fallbackSessionId(source, sourcePath) {
3337
+ return hashString(`${source}:${sourcePath ?? "unknown-session"}`).slice(7, 27);
3338
+ }
3339
+ function withSnapshot(model) {
3340
+ const trimmed = model.trim() || "unknown";
3341
+ if (trimmed.includes("@") || /-\d{8}$/.test(trimmed) || /-\d{4}-\d{2}-\d{2}$/.test(trimmed)) return trimmed;
3342
+ return `${trimmed}@observed-local`;
3343
+ }
3344
+ function hashJson(value) {
3345
+ return hashString(JSON.stringify(value));
3346
+ }
3347
+ function hashString(value) {
3348
+ return `sha256:${createHash("sha256").update(value).digest("hex")}`;
3349
+ }
3350
+ function stableSeed(value) {
3351
+ return createHash("sha256").update(value).digest().readUInt32BE(0);
3352
+ }
3353
+ function maxOptional(current, next) {
3354
+ return current === void 0 ? next : Math.max(current, next);
3355
+ }
3356
+ function clamp01(value) {
3357
+ if (!Number.isFinite(value)) return 0;
3358
+ return Math.max(0, Math.min(1, value));
3359
+ }
3360
+ function finiteScore(value) {
3361
+ return value !== void 0 && Number.isFinite(value) ? clamp01(value) : void 0;
3362
+ }
3363
+ function isRecord(value) {
3364
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3365
+ }
3366
+ function record(value) {
3367
+ return isRecord(value) ? value : null;
3368
+ }
3369
+ function stringField(obj, key) {
3370
+ const value = obj[key];
3371
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3372
+ }
3373
+ function numberField(obj, key) {
3374
+ const value = obj[key];
3375
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3376
+ }
3377
+ function timestampMs(value) {
3378
+ if (typeof value === "number" && Number.isFinite(value)) return value > 0xe8d4a51000 ? value : value * 1e3;
3379
+ if (typeof value === "string" && value.length > 0) {
3380
+ const parsed = Date.parse(value);
3381
+ return Number.isFinite(parsed) ? parsed : void 0;
3382
+ }
3383
+ }
3384
+ //#endregion
1787
3385
  //#region src/contract/intake/feedback-table.ts
1788
3386
  function fromFeedbackTable(opts) {
1789
3387
  const { ratings, meta = [], scale, emitRaterScores = true } = opts;