@tangle-network/agent-eval 0.53.0 → 0.55.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 (67) hide show
  1. package/dist/adapters/http.d.ts +1 -1
  2. package/dist/adapters/langchain.d.ts +1 -1
  3. package/dist/adapters/otel.d.ts +7 -6
  4. package/dist/{baseline-4R5deP0N.d.ts → baseline-DE36-Np7.d.ts} +1 -1
  5. package/dist/benchmarks/index.d.ts +3 -2
  6. package/dist/builder-eval/index.d.ts +4 -3
  7. package/dist/campaign/index.d.ts +9 -7
  8. package/dist/campaign/index.js +33 -4
  9. package/dist/campaign/index.js.map +1 -1
  10. package/dist/{chunk-L7XMNXLO.js → chunk-J4DIMSRK.js} +2 -2
  11. package/dist/{chunk-5KSDYBYH.js → chunk-LYL4SOKT.js} +3 -2
  12. package/dist/chunk-LYL4SOKT.js.map +1 -0
  13. package/dist/{chunk-BWZEGTES.js → chunk-NCK5QLGT.js} +1 -1
  14. package/dist/chunk-NCK5QLGT.js.map +1 -0
  15. package/dist/contract/index.d.ts +13 -12
  16. package/dist/contract/index.js +25 -0
  17. package/dist/contract/index.js.map +1 -1
  18. package/dist/{control-ojEWkMfJ.d.ts → control-DjEgwWNo.d.ts} +6 -5
  19. package/dist/{control-runtime-BZ_lVLYW.d.ts → control-runtime-DuFBYg7A.d.ts} +3 -2
  20. package/dist/control.d.ts +7 -6
  21. package/dist/control.js +2 -2
  22. package/dist/{emitter-DP_cSSiw.d.ts → emitter-DEZwY14K.d.ts} +2 -1
  23. package/dist/{failure-cluster-Cw65_5FY.d.ts → failure-cluster-CL7IVgkJ.d.ts} +2 -1
  24. package/dist/{feedback-trajectory-BSxqEpu7.d.ts → feedback-trajectory-DpUmE90J.d.ts} +1 -1
  25. package/dist/governance/index.d.ts +3 -2
  26. package/dist/hosted/index.d.ts +7 -6
  27. package/dist/{index-C7RhhEME.d.ts → index-D2nT6_KT.d.ts} +20 -2
  28. package/dist/{index-0pu_fBwZ.d.ts → index-wlaiph9Y.d.ts} +1 -1
  29. package/dist/index.d.ts +31 -29
  30. package/dist/index.js +3 -3
  31. package/dist/{integrity-CTDhR1Sg.d.ts → integrity-CfXjSqEv.d.ts} +1 -1
  32. package/dist/knowledge/index.d.ts +4 -3
  33. package/dist/meta-eval/index.d.ts +4 -3
  34. package/dist/openapi.json +1 -1
  35. package/dist/pipelines/index.d.ts +7 -6
  36. package/dist/prm/index.d.ts +5 -4
  37. package/dist/{query-DODUYdPg.d.ts → query-CqTxMwDw.d.ts} +2 -1
  38. package/dist/{red-team-30II1T4o.d.ts → red-team-CrC5MZYd.d.ts} +1 -1
  39. package/dist/{registry-8KAs18kY.d.ts → registry-BSWy0rvH.d.ts} +1 -1
  40. package/dist/{release-report-DSu0DWy8.d.ts → release-report-B6l5fi7T.d.ts} +2 -2
  41. package/dist/reporting.d.ts +7 -6
  42. package/dist/{researcher-LZD0qHEa.d.ts → researcher-JP8EvnLv.d.ts} +11 -6
  43. package/dist/rl.d.ts +11 -10
  44. package/dist/rl.js +2 -2
  45. package/dist/{rubric-D5tjHNJQ.d.ts → rubric-BOfxn4ja.d.ts} +3 -2
  46. package/dist/{rubric-predictive-validity-ByZEC3BX.d.ts → rubric-predictive-validity-B3qNa4aY.d.ts} +1 -1
  47. package/dist/{run-improvement-loop-Cc7oZlRP.d.ts → run-improvement-loop-BhfdjrMY.d.ts} +3 -3
  48. package/dist/{run-record-BGY6bHRh.d.ts → run-record-etiCMsUq.d.ts} +11 -3
  49. package/dist/{store-Db2Bv8Cf.d.ts → schema-m0gsnbt3.d.ts} +1 -99
  50. package/dist/store-CKUAgsJz.d.ts +101 -0
  51. package/dist/{summary-report-B7gNRX-r.d.ts → summary-report-DLxh4yWk.d.ts} +2 -2
  52. package/dist/{test-graded-scenario-B2kWEdh9.d.ts → test-graded-scenario-BdVaPyHT.d.ts} +3 -2
  53. package/dist/traces.d.ts +7 -6
  54. package/dist/{trajectory-CnoBo-JY.d.ts → trajectory-GEdXJCL5.d.ts} +2 -1
  55. package/dist/{types-Dbj5gu8n.d.ts → types-BgrxOJSf.d.ts} +31 -1
  56. package/dist/wire/index.d.ts +5 -4
  57. package/docs/pilot/README.md +62 -0
  58. package/docs/pilot/customer-checklist.md +90 -0
  59. package/docs/pilot/integration-foreign-stack.md +296 -0
  60. package/docs/pilot/integration-tangle-stack.md +248 -0
  61. package/docs/pilot/one-pager.md +161 -0
  62. package/docs/pilot/sample-insight-report.json +172 -0
  63. package/docs/research/research-roadmap.md +204 -0
  64. package/package.json +1 -1
  65. package/dist/chunk-5KSDYBYH.js.map +0 -1
  66. package/dist/chunk-BWZEGTES.js.map +0 -1
  67. /package/dist/{chunk-L7XMNXLO.js.map → chunk-J4DIMSRK.js.map} +0 -0
@@ -0,0 +1,161 @@
1
+ # Statistical self-improvement for your agent — one-pager
2
+
3
+ **For:** teams running an agent on the Tangle stack (sandbox + tcloud), OR any agent emitting OTel traces, OR LangChain / LlamaIndex / Anthropic SDK / OpenAI Assistants / OpenRouter / custom — we meet you where you are.
4
+ **The pitch:** every week, get a statistically-rigorous answer to *"did my last change help?"* + a closed loop that proposes the next improvement + a held-out gate that refuses to ship regressions.
5
+
6
+ ## What you get
7
+
8
+ | Deliverable | Cadence | LLM cost |
9
+ |---|---|---|
10
+ | **Decision packet** — composite distribution, per-dimension judges, cost-quality Pareto, failure clusters, named worst-N runs, ranked recommendations | Whenever you want it. Hosted runs on a 15-min schedule by default. | $0 (deterministic) |
11
+ | **Prior-period comparison** — Welch CI on composite / cost / duration / per-dimension deltas vs your prior week, with regressed + improved metrics named | Same cadence | $0 |
12
+ | **Closed-loop improvement** — `selfImprove()` proposes prompt edits, runs scenarios, gates on paired-bootstrap CI, auto-PRs the winner | On-demand, opt-in | Real $; you set a `maxUsd` ceiling |
13
+
14
+ Every claim is falsifiable: `n=`, `CI95=[a, b]`, `p=`, `Cohen's d=`. No vibes, no "score went up." Where the data doesn't support a section, the report says so explicitly instead of inventing signal.
15
+
16
+ ## Why this is different
17
+
18
+ | | LangSmith / Braintrust / Phoenix | Hermes / Claude Code skills | **Tangle** |
19
+ |---|---|---|---|
20
+ | Trace ingest | proprietary | own runtime | universal (sandbox + tcloud + OTel + any custom) |
21
+ | Decision packet | scorecards (no CI) | none | **paired-bootstrap CI on every claim** |
22
+ | Closed loop | none | heuristic, no gate | **statistically-gated; refuses regressions** |
23
+ | Prior-period delta | none | none | **Welch CI on every metric** |
24
+ | Sample-size guidance | none | none | **MDE-aware** |
25
+ | Auto-PR promotion | none | none | **opt-in, on green gate only** |
26
+
27
+ ## Integration paths — pick your stack
28
+
29
+ | Your stack | Intake adapter | LLM provider for closed loop |
30
+ |---|---|---|
31
+ | **Tangle (sandbox + tcloud)** | `fromTangleSandbox` | tcloud (already wired) |
32
+ | Any OTel exporter (Datadog APM, Honeycomb, NewRelic, OpenInference) | `fromOtelSpans` | any OpenAI-compat |
33
+ | LangChain (LangSmith) | LangSmith → OTel export → `fromOtelSpans` today; `fromLangChain` queued 0.55.0 | OpenAI, Anthropic, OpenRouter, tcloud |
34
+ | LlamaIndex | `OpenInferenceCallbackHandler` → OTel → ingest | any OpenAI-compat |
35
+ | Anthropic SDK direct | OTel wrapping (~20 LOC) → `fromOtelSpans`; `fromAnthropicSDK` queued | Anthropic, OpenRouter |
36
+ | OpenAI Assistants API | Custom mapper (~20 LOC) today; `fromOpenAIAssistants` queued | OpenAI, OpenRouter |
37
+ | OpenRouter (any model on any path) | Whatever you already use for tracing | OpenRouter (OpenAI-compat baseUrl) |
38
+ | vLLM / Ollama / LMStudio / self-hosted | OTel wrap | Your local OpenAI-compat endpoint |
39
+ | Multi-rater human feedback (no automated judge yet) | `fromFeedbackTable` | n/a — gives you κ + disagreement triage |
40
+ | Custom logs / DB rows | ~20-line mapper to `RunRecord` | any OpenAI-compat |
41
+
42
+ Full integration walkthroughs:
43
+ - **Tangle stack** → [`integration-tangle-stack.md`](./integration-tangle-stack.md)
44
+ - **Everything else** → [`integration-foreign-stack.md`](./integration-foreign-stack.md)
45
+
46
+ ## Zero-setup demo first — 30 seconds
47
+
48
+ Before any integration, run the demo against synthetic data so you see the output shape live:
49
+
50
+ ```sh
51
+ npx @tangle-network/intelligence demo
52
+ ```
53
+
54
+ No install, no key, no data. Synthetic agent runs through synthetic scenarios; the CLI prints a real `InsightReport` with composite distribution + Pareto + prior-period delta + ranked recommendations. Same output shape you'll get on your real data once we integrate.
55
+
56
+ When you're ready to integrate, the same CLI scaffolds your repo:
57
+
58
+ ```sh
59
+ npx @tangle-network/intelligence init # creates eval/scenarios.json + judges.ts + pnpm scripts + .runs/
60
+ npx @tangle-network/intelligence report # renders InsightReport from your latest traces
61
+ npx @tangle-network/intelligence improve --max-usd 25 # runs selfImprove with cost ceiling, opens auto-PR on green gate
62
+ ```
63
+
64
+ Hosted equivalent: **[staging-intelligence.tangle.tools](https://staging-intelligence.tangle.tools)** — open in your browser, ingest your traces, see the dashboard render the same packet your CLI produces.
65
+
66
+ ## How you integrate (Tangle stack — 4 steps)
67
+
68
+ ```ts
69
+ import { fromTangleSandbox } from '@tangle-network/agent-eval/adapters/sandbox'
70
+ import { analyzeRuns, selfImprove, gepaDriver } from '@tangle-network/agent-eval/contract'
71
+
72
+ // 1. You already emit traces via @tangle-network/sandbox + tcloud.
73
+ // Pull them into canonical RunRecord[]:
74
+ const runs = fromTangleSandbox({ sessionId, sinceMs: lastReportTime })
75
+
76
+ // 2. Get the decision packet — no LLM cost.
77
+ const report = await analyzeRuns({ runs, baselineRuns: priorWeekRuns })
78
+ // → report.composite + .priorPeriodComparison + .recommendations
79
+
80
+ // 3. When you want to actually improve, run the closed loop.
81
+ const result = await selfImprove({
82
+ scenarios: yourScenarios, // we help you build these
83
+ agent: (surface, scenario) => runYourAgent(scenario, surface),
84
+ judge: yourJudge, // any function (artifact) → JudgeScore
85
+ baselineSurface: currentSystemPrompt,
86
+ driver: gepaDriver({ llm: tcloud, model: 'claude-sonnet-4.6', target: 'agent prompt' }),
87
+ budget: { generations: 3, populationSize: 4, holdoutFraction: 0.3, maxUsd: 25 },
88
+ })
89
+
90
+ // 4. Result is a verifiable diff with statistical evidence.
91
+ // Auto-PR if result.gateDecision === 'ship-substrate'.
92
+ ```
93
+
94
+ That's it. ~30 lines of integration code; the rest is your existing agent + tcloud setup.
95
+
96
+ ## What we need from you
97
+
98
+ - API key for tcloud (you already have this)
99
+ - Read access to your sandbox session traces
100
+ - A list of 20-50 representative scenarios your agent should handle
101
+ - A judge function — even a simple LLM-as-judge gets you 80% of the value
102
+ - An LLM-cost budget for the closed loop (default: $25/campaign)
103
+
104
+ ## What you ship back to your customers
105
+
106
+ The substrate produces a single JSON `InsightReport` your dashboard renders. Live demo embedded in the Tangle Intelligence dashboard. Example below — every section optional based on what your data supports.
107
+
108
+ ```json
109
+ {
110
+ "n": 36,
111
+ "composite": {
112
+ "mean": 0.823, "p50": 0.85, "p95": 0.96, "stddev": 0.11,
113
+ "tailRuns": [
114
+ { "runId": "scenario::checkout-bug", "score": 0.41 },
115
+ { "runId": "scenario::refund-policy", "score": 0.48 }
116
+ ]
117
+ },
118
+ "priorPeriodComparison": {
119
+ "baselineN": 34,
120
+ "currentN": 36,
121
+ "windowLabel": "vs prior 7 days",
122
+ "metrics": {
123
+ "composite": {
124
+ "current": 0.823, "baseline": 0.731, "delta": 0.092,
125
+ "ci95": [0.041, 0.143], "pValue": 0.0008,
126
+ "cohensD": 0.84, "significant": true
127
+ }
128
+ },
129
+ "improvedMetrics": ["composite"],
130
+ "regressedMetrics": []
131
+ },
132
+ "recommendations": [
133
+ {
134
+ "priority": "low",
135
+ "kind": "ship",
136
+ "title": "composite improved from 0.731 → 0.823 vs prior 7 days",
137
+ "detail": "Welch CI95=[0.041, 0.143], p=0.0008, Cohen's d=0.84 (n_current=36, n_baseline=34). Statistically significant improvement worth flagging."
138
+ },
139
+ {
140
+ "priority": "high",
141
+ "kind": "investigate",
142
+ "title": "Top failure cluster: refund-policy (12% of failures)",
143
+ "detail": "4 runs failed. Largest cluster groups by intent — agent missed compliance flag in 3 of 4."
144
+ }
145
+ ]
146
+ }
147
+ ```
148
+
149
+ ## Pricing for the pilot
150
+
151
+ - Free for the first 30 days
152
+ - Hosted decision-packet generation: included
153
+ - LLM cost on closed-loop campaigns: pass-through to your tcloud account
154
+ - Post-pilot: per-campaign pricing tied to budget cap + per-decision-packet billed monthly
155
+
156
+ ## Next step
157
+
158
+ Reply with: which agent + which week you want to start, and we'll set up the integration on a shared call. ~1 hour to first running report.
159
+
160
+
161
+ *Tangle Network · @tangle-network/agent-eval @0.53.0 · MIT · Self-hostable*
@@ -0,0 +1,172 @@
1
+ {
2
+ "n": 36,
3
+ "composite": {
4
+ "n": 36,
5
+ "mean": 0.823,
6
+ "p50": 0.85,
7
+ "p95": 0.96,
8
+ "stddev": 0.114,
9
+ "min": 0.41,
10
+ "max": 0.98,
11
+ "tailRuns": [
12
+ { "runId": "scenario::checkout-bug", "score": 0.41 },
13
+ { "runId": "scenario::refund-policy-edge", "score": 0.48 },
14
+ { "runId": "scenario::multi-tenant-isolation", "score": 0.52 },
15
+ { "runId": "scenario::stale-cache-invalidation", "score": 0.55 },
16
+ { "runId": "scenario::partial-payment", "score": 0.61 }
17
+ ],
18
+ "histogram": [
19
+ { "lo": 0.40, "hi": 0.46, "count": 1 },
20
+ { "lo": 0.46, "hi": 0.51, "count": 1 },
21
+ { "lo": 0.51, "hi": 0.57, "count": 1 },
22
+ { "lo": 0.57, "hi": 0.62, "count": 1 },
23
+ { "lo": 0.62, "hi": 0.68, "count": 2 },
24
+ { "lo": 0.68, "hi": 0.74, "count": 2 },
25
+ { "lo": 0.74, "hi": 0.80, "count": 4 },
26
+ { "lo": 0.80, "hi": 0.85, "count": 9 },
27
+ { "lo": 0.85, "hi": 0.91, "count": 8 },
28
+ { "lo": 0.91, "hi": 0.96, "count": 6 },
29
+ { "lo": 0.96, "hi": 1.0, "count": 1 }
30
+ ]
31
+ },
32
+ "perDimension": {
33
+ "intent-recognition": {
34
+ "n": 36, "mean": 0.89, "p50": 0.92, "p95": 0.98, "stddev": 0.08
35
+ },
36
+ "compliance-flagging": {
37
+ "n": 36, "mean": 0.71, "p50": 0.75, "p95": 0.92, "stddev": 0.18
38
+ },
39
+ "tone": {
40
+ "n": 36, "mean": 0.94, "p50": 0.95, "p95": 0.99, "stddev": 0.04
41
+ }
42
+ },
43
+ "costQuality": {
44
+ "cost": {
45
+ "n": 36, "mean": 0.087, "p50": 0.082, "p95": 0.124, "stddev": 0.021
46
+ },
47
+ "pareto": {
48
+ "kind": "pareto-cost-quality",
49
+ "split": "holdout",
50
+ "axes": { "x": "costUsd", "y": "score" },
51
+ "points": [
52
+ { "candidateId": "baseline-v3.1", "cost": 0.087, "quality": 0.823, "n": 36, "onFrontier": true }
53
+ ]
54
+ }
55
+ },
56
+ "judges": {
57
+ "claude-sonnet-4.6": {
58
+ "n": 36,
59
+ "meanScore": 0.831,
60
+ "calibration": null
61
+ }
62
+ },
63
+ "lift": null,
64
+ "failureClusters": {
65
+ "totalFailures": 4,
66
+ "clusters": [
67
+ {
68
+ "id": "cluster_refund_compliance",
69
+ "name": "refund-policy missed compliance flag",
70
+ "share": 0.75,
71
+ "exemplars": [
72
+ "scenario::refund-policy-edge",
73
+ "scenario::partial-payment",
74
+ "scenario::cross-border-refund"
75
+ ],
76
+ "suggestedFix": "Add explicit step to the addendum: when refund amount > $100 OR cross-border, surface compliance flag before responding."
77
+ }
78
+ ]
79
+ },
80
+ "priorPeriodComparison": {
81
+ "baselineN": 34,
82
+ "currentN": 36,
83
+ "windowLabel": "vs prior 7 days",
84
+ "metrics": {
85
+ "composite": {
86
+ "current": 0.823,
87
+ "baseline": 0.731,
88
+ "delta": 0.092,
89
+ "ci95": [0.041, 0.143],
90
+ "pValue": 0.0008,
91
+ "cohensD": 0.84,
92
+ "baselineN": 34,
93
+ "currentN": 36,
94
+ "significant": true
95
+ },
96
+ "cost": {
97
+ "current": 0.087,
98
+ "baseline": 0.082,
99
+ "delta": 0.005,
100
+ "ci95": [-0.003, 0.013],
101
+ "pValue": 0.21,
102
+ "cohensD": 0.23,
103
+ "baselineN": 34,
104
+ "currentN": 36,
105
+ "significant": false
106
+ },
107
+ "duration": {
108
+ "current": 4820,
109
+ "baseline": 5340,
110
+ "delta": -520,
111
+ "ci95": [-840, -200],
112
+ "pValue": 0.002,
113
+ "cohensD": -0.71,
114
+ "baselineN": 34,
115
+ "currentN": 36,
116
+ "significant": true
117
+ },
118
+ "dim.compliance-flagging": {
119
+ "current": 0.71,
120
+ "baseline": 0.58,
121
+ "delta": 0.13,
122
+ "ci95": [0.06, 0.20],
123
+ "pValue": 0.0004,
124
+ "cohensD": 0.79,
125
+ "baselineN": 34,
126
+ "currentN": 36,
127
+ "significant": true
128
+ }
129
+ },
130
+ "improvedMetrics": ["composite", "duration", "dim.compliance-flagging"],
131
+ "regressedMetrics": []
132
+ },
133
+ "release": {
134
+ "status": "pass",
135
+ "axes": [
136
+ { "name": "quality-lift", "status": "pass", "detail": "no candidate/baseline pair within campaign; relying on priorPeriodComparison" },
137
+ { "name": "contamination", "status": "pass", "detail": "no canaries supplied" },
138
+ { "name": "composite-distribution", "status": "pass", "detail": "mean=0.823, p50=0.85, p95=0.96 over n=36" }
139
+ ],
140
+ "issues": []
141
+ },
142
+ "recommendations": [
143
+ {
144
+ "priority": "low",
145
+ "kind": "ship",
146
+ "title": "composite improved from 0.731 → 0.823 vs prior 7 days",
147
+ "detail": "Welch CI95=[0.041, 0.143], p=0.0008, Cohen's d=0.84 (n_current=36, n_baseline=34). Statistically significant improvement worth flagging.",
148
+ "evidencePath": "priorPeriodComparison.metrics.composite"
149
+ },
150
+ {
151
+ "priority": "low",
152
+ "kind": "ship",
153
+ "title": "compliance-flagging dimension improved from 0.58 → 0.71",
154
+ "detail": "Welch CI95=[0.06, 0.20], p=0.0004, Cohen's d=0.79. The fix from last week's PR is statistically validated.",
155
+ "evidencePath": "priorPeriodComparison.metrics.dim.compliance-flagging"
156
+ },
157
+ {
158
+ "priority": "high",
159
+ "kind": "investigate",
160
+ "title": "Top failure cluster: refund-policy missed compliance flag (75% of failures)",
161
+ "detail": "3 of 4 failed runs cluster here. Suggested fix: add explicit step to addendum for refund > $100 OR cross-border → surface compliance flag.",
162
+ "evidencePath": "failureClusters.clusters[0]"
163
+ },
164
+ {
165
+ "priority": "low",
166
+ "kind": "ship",
167
+ "title": "duration improved from 5340ms → 4820ms vs prior 7 days",
168
+ "detail": "Welch CI95=[-840, -200]ms, p=0.002, Cohen's d=-0.71. Agent is meaningfully faster — worth keeping the optimization that drove this.",
169
+ "evidencePath": "priorPeriodComparison.metrics.duration"
170
+ }
171
+ ]
172
+ }
@@ -0,0 +1,204 @@
1
+ # Research Roadmap — Agent Self-Improvement as a Research Field
2
+
3
+ **Status:** Living. Separate from the product roadmap. Updated when a thesis formalizes, an experiment runs, or a draft posts.
4
+ **Tracking:** task #107.
5
+ **Audience:** Dario, Yann, Ilya, Sam, lab researchers, peer reviewers.
6
+ **Posture:** Honest about what we have, sharp about what we'd need.
7
+
8
+ ## One-sentence pitch
9
+
10
+ **Agent self-improvement is missing its statistical foundation, its formal model of two-writer state, and its standard benchmark. We claim all three.**
11
+
12
+ ## The three publishable theses
13
+
14
+ ### Thesis 1 — Branch-benchmark consensus for safe offline+online self-improvement
15
+
16
+ **Setup.** Two writers concurrently mutate an agent's behavior surface: an in-sandbox harness (per-turn, online) and an offline substrate (batch, statistically-gated). Both produce divergent versions from a common ancestor. Existing literature handles online RL (single writer = policy) and offline RL (no in-runtime writes) — nobody has formalized the *combined* regime where both writers coexist.
17
+
18
+ **Claim to prove.** Given common ancestor `P_anc`, harness branch `P_h`, substrate branch `P_s`, scenarios `S`, and judge `J`, a branch-benchmark consensus procedure produces a winner `P_w` with regret bounded by `max(R(P_h), R(P_s)) − ε` with probability `≥ 1−δ` under explicit assumptions about judge calibration + scenario coverage.
19
+
20
+ **Why it's publishable.**
21
+ - Genuinely novel regime — the combined offline+online assumption set is uncharted.
22
+ - Maps to a real customer pain point (Hermes-on-our-sandbox drift).
23
+ - Tractable proof structure: paired-bootstrap + Hoeffding + union bound across surface dimensions.
24
+ - Empirical validation: instrument Hermes-on-our-sandbox, measure consensus-vs-naïve-merge regret.
25
+
26
+ **Estimated effort.** ~3 months focused. Maps to product task #98 (profile-versioning architecture).
27
+
28
+ **Venue.** NeurIPS or ICLR main track. Or workshop at NeurIPS Foundation Models for Decision Making.
29
+
30
+ ---
31
+
32
+ ### Thesis 2 — Natural-language corrective feedback as a learnable gradient
33
+
34
+ **Setup.** RL provides scalar reward `r ∈ ℝ`. Natural-language feedback ("stop doing X", "you always Y", "this is too verbose") carries strictly more information per bit but no formal model says how to combine it with scalar reward to update policy or skill state. Hermes uses corrective feedback heuristically; GEPA paper claims language is a richer learning medium but doesn't formalize this specific signal. The gap is wide open.
35
+
36
+ **Claim to prove.**
37
+ 1. Information-theoretic: corrective utterances carry `H(c) > H(r)` bits of policy-relevant information under realistic distributions of user satisfaction.
38
+ 2. Algorithmic: an extraction+integration procedure exists that improves sample efficiency over scalar-only RL by `k×` (target k ≥ 5) on the proposed benchmark.
39
+ 3. Empirical: validation on multi-turn agent tasks with explicit user-corrective channels.
40
+
41
+ **Why it's publishable.**
42
+ - Connects to GEPA paper's central claim, makes it falsifiable for the corrective sub-class.
43
+ - Maps to product task #103 (`extractUserCorrections`).
44
+ - Distinctive — no observability or RL competitor formalizes corrective feedback as a gradient.
45
+
46
+ **Estimated effort.** ~4 months. Information-theoretic framing is delicate.
47
+
48
+ **Venue.** ICLR or main-track NeurIPS. Possibly EMNLP for the NLP angle.
49
+
50
+ ---
51
+
52
+ ### Thesis 3 — Sample-efficient self-improvement under a paired-bootstrap gate
53
+
54
+ **Setup.** GEPA paper claims ~35× fewer rollouts than GRPO. They use a binary improvement check. Our substrate uses paired-bootstrap CI + Cohen's d + MDE (strictly stricter gate). The trade-off between gate-strictness and rollout efficiency is unmodeled.
55
+
56
+ **Claim to prove.** Given a paired-bootstrap gate with significance level α and minimum detectable effect δ, `selfImprove` requires `O((σ/δ)² · log(1/α))` rollouts to detect a true ε-improvement with power `1-β`. Tight constants. Compare empirically to GRPO and to GEPA's simple-improvement gate on identical benchmarks.
57
+
58
+ **Why it's publishable.**
59
+ - Closes a gap GEPA left open (their efficiency claim has no power analysis).
60
+ - Maps to product task #101 (real GEPA Pareto + sample-size theory).
61
+ - Provides a tool — power calculator for the field's self-improvement runs.
62
+
63
+ **Estimated effort.** ~2 months — the cleanest of the three. Mostly classical sample-size theory + careful experiments.
64
+
65
+ **Venue.** ICML or AISTATS. The statistical framing fits both.
66
+
67
+ ## The fourth thesis (long-horizon, highest prestige)
68
+
69
+ ### Thesis 4 — A standardized benchmark for self-improvement
70
+
71
+ **Setup.** No standard benchmark exists for "did self-improvement help, robustly, across distribution shift?" GAIA + SWE-Bench + AgentBench measure agent capability; nothing measures self-improvement quality. The field is publishing self-improvement results on disparate ad-hoc setups; nobody compares.
72
+
73
+ **Claim to ship.** A benchmark with:
74
+ - 100+ scenarios spanning distinct distribution shifts (intra-domain, cross-domain, adversarial corruption)
75
+ - Held-out test split with strict contamination guards
76
+ - Reference baselines (no-driver / random / scalar-only-RL / GEPA / our substrate)
77
+ - Standard scorecard: lift CI, sample efficiency, distribution-shift robustness, cost
78
+ - Public leaderboard
79
+
80
+ **Why it matters.** Whoever owns the benchmark owns the measuring stick. ImageNet for vision, GLUE for NLP, GAIA for agent capability — the gap for *self-improvement quality* is open.
81
+
82
+ **Estimated effort.** 6 months. Real scenario authoring, contamination engineering, community outreach for leaderboard adoption.
83
+
84
+ **Venue.** Datasets + Benchmarks track at NeurIPS. Or workshop debut → main-track followup.
85
+
86
+ ## 12 open research questions, ranked by signal-to-noise
87
+
88
+ Each is a falsifiable claim or unanswered formal question. Each maps to publishable work.
89
+
90
+ 1. **Information content of corrective feedback.** What's the empirical mutual information `I(correction; preferred_policy)` across realistic agent deployments? Is it consistently `> H(scalar_reward)`?
91
+
92
+ 2. **Convergence of branch-benchmark consensus.** Under what assumptions on judge calibration does the symmetric-fork merge protocol converge to a global optimum vs a local one?
93
+
94
+ 3. **The cost of statistical strictness.** How much does a paired-bootstrap gate cost in rollouts vs a literal `>` gate (SkillOpt's choice), as a function of true effect size? Where's the crossover where strictness costs more than it saves?
95
+
96
+ 4. **Cross-surface attribution.** When `compositeDriver` ships a winner where N surfaces changed, which surface's change drove the lift? Shapley estimators on agent-profile surfaces — tractable? Required sample size?
97
+
98
+ 5. **Sample-efficient evaluation under distribution shift.** Given a held-out test slice and a known shift class (intra-domain / cross-domain / adversarial), how few held-out scenarios are needed to detect lift with target power? Is it a function of shift magnitude?
99
+
100
+ 6. **Diminishing returns of recursive self-improvement.** A substrate that optimizes its own SKILL.md against held-out tasks — does it converge or drift? At what point do recursive self-edits become net-negative on a true holdout? Map the loss landscape.
101
+
102
+ 7. **Skill semantic-duplicate detection.** Substrate's `summarize-pr` vs harness's `pr-summarizer`. What's the right embedding + threshold? Is human review for borderline cases unavoidable or can it be automated?
103
+
104
+ 8. **Reward-hacking under self-improvement.** When the optimizer can mutate the judge prompt (the recursive surface), what's the formal condition under which it learns to game the judge instead of solving the task? Connect to Goodhart + AIRP.
105
+
106
+ 9. **Cost-quality Pareto across drivers.** What's the empirical Pareto frontier when you trade off `gepaDriver` (high $/gen) vs `evolutionaryDriver` ($0/gen) vs heuristic mutations? Is it task-dependent or universal?
107
+
108
+ 10. **Online-offline merge regret.** When harness branch and substrate branch are merged, what's the regret of the merged policy vs the better-of-two? Bounded? Worst-case adversarial?
109
+
110
+ 11. **Universal trace ingest tax.** Cross-framework adapter coverage (LangChain / LlamaIndex / Anthropic / OpenAI) — how much signal loss is forced by the lowest-common-denominator RunRecord shape? Quantify in terms of recoverable lift CI.
111
+
112
+ 12. **Foundation-model-as-judge calibration drift.** When the judge LLM updates (Claude → Claude+1), what's the variance in judge scores on a fixed corpus? Is held-out gate validity preserved across judge versions? Empirical study, longitudinal.
113
+
114
+ ## The processes (how we actually do this)
115
+
116
+ **Cadence.**
117
+ - Daily: product work continues (Track A). Research is a separate 30%-time block.
118
+ - Weekly: research log + open-questions revision. One paper-quality paragraph per week.
119
+ - Monthly: experiment milestone — either proof attempt or empirical-run results.
120
+ - Quarterly: paper-draft milestone.
121
+
122
+ **Artifacts.**
123
+ - `docs/research/<thesis>/notes.md` — running research log, hypothesis, current status.
124
+ - `docs/research/<thesis>/experiments.md` — every run + numbers + analysis.
125
+ - `docs/research/<thesis>/paper-draft.md` — building toward arXiv submission.
126
+ - `.evolve/research/<thesis>/` — code + data + figures, version-controlled.
127
+
128
+ **Quality bar.**
129
+ - Every claim falsifiable. Every number has CI, p, and sample size.
130
+ - Every experiment reproducible — script + seed + commit hash + data hash.
131
+ - Every figure has an underlying CSV the reviewer can download.
132
+ - Every theorem has a proof in the doc, not just a citation.
133
+
134
+ **Review cadence.**
135
+ - Internal critique pass before any external sharing — find every weak spot.
136
+ - External review at 80%-draft: one peer in the field, one peer outside.
137
+ - ArXiv submission as the gating event for public claim.
138
+
139
+ ## What we explicitly will NOT do
140
+
141
+ - **Will not pretend product-grade engineering is research.** Architecture docs are not papers. Strategic framing is not contribution.
142
+ - **Will not chase trendy directions (RLHF variants, constitutional AI, scaling laws) where we have no edge.** Our edges are specific: two-writer state, corrective feedback as gradient, statistical strictness. Stay in lane.
143
+ - **Will not publish empirical results without proper baselines.** "Our substrate produces N% lift on dataset X" is meaningless without no-driver/random/GEPA/SkillOpt baselines on identical infrastructure.
144
+ - **Will not optimize for citation count over insight.** One paper that changes how the field thinks > five papers that move a benchmark by 2 points.
145
+
146
+ ## Where we are right now
147
+
148
+ **Track A (product) status as of 2026-05-27:**
149
+ - agent-eval shipped 0.47 → 0.53 in one session
150
+ - Six consumers on substrate 0.50+
151
+ - Honest spec docs landed
152
+ - Product roadmap 0.53 → 1.1 mapped
153
+
154
+ **Track B (research) status as of 2026-05-27:**
155
+ - This doc exists
156
+ - Zero experiments run on published benchmarks
157
+ - Zero papers drafted
158
+ - Three theses identified, none formalized
159
+ - Twelve open questions enumerated, none answered
160
+
161
+ We are at Track-B day 0. Honesty matters.
162
+
163
+ ## Deliverables — 12-month plan
164
+
165
+ **Q3 2026 — proof of life.**
166
+ - Run our drivers against AgentBench / SWE-Bench Verified / GAIA. Report numbers with CI.
167
+ - Pick one named partner customer who'd validate Thesis 1 with us on their real deployment.
168
+
169
+ **Q4 2026 — Thesis 3 paper draft.**
170
+ - Sample-efficient self-improvement is the cleanest claim — fastest to publish, sharpens our gate's edge.
171
+ - Target: arXiv pre-print + AISTATS submission.
172
+
173
+ **Q1 2027 — Thesis 1 paper draft.**
174
+ - Branch-benchmark consensus — the deepest claim, the one that needs forcing-function data from a Hermes-on-sandbox deployment.
175
+ - Target: NeurIPS / ICLR submission.
176
+
177
+ **Q2 2027 — Thesis 4 benchmark public release.**
178
+ - The benchmark + leaderboard is the highest-prestige play.
179
+ - Target: Datasets + Benchmarks track at NeurIPS 2027.
180
+
181
+ **Q3 2027 — Thesis 2 paper draft.**
182
+ - Corrective feedback as gradient — slowest to ripen, hardest to formalize.
183
+ - Target: ICLR submission.
184
+
185
+ ## How a lab lead would react to this doc
186
+
187
+ If you printed this and slid it across Dario's desk:
188
+
189
+ **The good.** Specific named theses with falsifiable claims. Honest about gap from product to research. Three publishable directions in clear scope. Twelve open questions readable as a research-program statement.
190
+
191
+ **The hostile-reviewer attack.** "Show me one number on one published benchmark from your existing infrastructure. You have a substrate with a paired-bootstrap gate that's never been compared to anything." That is correct. Q3 2026 deliverable is the answer.
192
+
193
+ **The deepest question they'd ask.** "Why does this matter for AGI / safety / capability? Why work on this instead of pretraining / alignment / interpretability?" Honest answer: agents that self-improve in production are a near-term reality. The work to make that *safe* and *measurable* is path-dependent on whether the field formalizes it or accepts ad-hoc product implementations. Our pitch is "be the lab that formalized it before it became a 1000-org engineering mess." That's a defensible answer if backed by the published work.
194
+
195
+ ## The one-sentence inspirational version per audience
196
+
197
+ - **For Dario:** "We're building the statistical foundation that turns 'agents that self-improve' from a marketing slogan into a measurable claim with calibrated error bars."
198
+ - **For Yann:** "Self-improvement is offline-RL with two writers — and nobody has formalized the consensus regime. We will."
199
+ - **For Ilya:** "What's the simplest formalism under which self-improving agents converge to a global optimum vs a local one? Branch-benchmark consensus is our hypothesis."
200
+ - **For Sam:** "We are going to ship the substrate that lets every customer's agent self-improve safely, then publish the science that proves it works. The product builds the data; the data writes the papers; the papers create the moat."
201
+
202
+ ## The harshest honest sentence
203
+
204
+ If we don't run a published benchmark by Q3 2026, this entire doc is fan-fiction. Build the empirical infrastructure first, formalize after, publish last.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-eval",
3
- "version": "0.53.0",
3
+ "version": "0.55.0",
4
4
  "description": "Substrate for self-improving agents: traces, verifiable rewards, preferences, GEPA / reflective mutation, auto-research, replay, sequential anytime-valid stats, and release gates.",
5
5
  "homepage": "https://github.com/tangle-network/agent-eval#readme",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/eval-campaign.ts"],"sourcesContent":["/**\n * EvalCampaign — opinionated matrix runner that wires the four\n * capture-integrity directives by construction.\n *\n * The canonical benchmark shape — matrix runner → for each\n * (variant, scenario, seed) → start a TraceEmitter → call LLMs → end the\n * run → analyze — has a bug class at the integration boundary: raw\n * events not captured, route silently wrong, integrity not asserted,\n * analyst never run. The directives in `SKILL.md § Capture integrity`\n * are the mitigations.\n *\n * `EvalCampaign` is the structural fix — consumers don't wire the\n * integrity surface themselves; the campaign owns it. Specifically:\n *\n * - calls `assertLlmRoute` once at preflight before any work runs\n * - constructs a per-run `TraceStore` and `RawProviderSink` via factories\n * - constructs the `TraceEmitter` with `onRunComplete: [analyst hook]`\n * - hands the runner an `LlmClientOptions` pre-wired with the sink and\n * trace context — the runner can't accidentally call an LLM without\n * capturing the raw HTTP envelope\n * - calls `assertRunCaptured` after every `endRun` and routes failures\n * through a configurable policy (`throw` / `mark_failed` / `log`)\n * - assembles per-run `RunRecord`s and runs `researchReport` at the end\n * so the campaign artifact is launch-decision-grade by default\n * - embeds the campaign fingerprint (a SHA-256 over the canonicalised\n * run set) and optional `preregistrationHash` in the report\n *\n * The runner contract is intentionally narrow: produce a `CampaignRunOutcome`\n * given a fully-wired `CampaignRunContext`. Everything orchestration-shaped\n * lives in the campaign. This is the inversion-of-control point — consumers\n * stop writing matrix runners and start writing scenario-runners.\n *\n * Out of scope for v1 (tracked in `docs/research-report-methodology.md`):\n *\n * - Distributed/cluster execution (concurrency is local async)\n * - Adaptive sampling / sequential interim looks\n * - Resume from partial state across crashes\n * - LLM-call retry beyond what `LlmClient` already does\n */\n\nimport {\n type AgentProfileCell,\n type AgentProfileCellInput,\n buildAgentProfileCell,\n verifyAgentProfileCell,\n} from './agent-profile-cell'\nimport { assertLlmRoute, type LlmClientOptions, type LlmRouteRequirements } from './llm-client'\nimport { canonicalize, hashJson } from './pre-registration'\nimport type {\n JudgeScoresRecord,\n RunJudgeMetadata,\n RunOutcome,\n RunRecord,\n RunSplitTag,\n RunTokenUsage,\n} from './run-record'\nimport { validateRunRecord } from './run-record'\nimport { type ResearchReport, type ResearchReportOptions, researchReport } from './summary-report'\nimport type { RunCompleteHook } from './trace/emitter'\nimport { TraceEmitter } from './trace/emitter'\nimport {\n assertRunCaptured,\n RunIntegrityError,\n type RunIntegrityExpectations,\n type RunIntegrityReport,\n} from './trace/integrity'\nimport { FileSystemRawProviderSink, type RawProviderSink } from './trace/raw-provider-sink'\nimport type { TraceStore } from './trace/store'\n\n// ── Public types ─────────────────────────────────────────────────────────\n\nexport interface CampaignVariant<V> {\n id: string\n payload: V\n}\n\nexport interface CampaignScenario {\n scenarioId: string\n /** Free-form metadata propagated to runs and reports. */\n tags?: Record<string, string>\n}\n\nexport interface CampaignRunContext<V> {\n /** Stable run id. The campaign generates this; the runner does not. */\n runId: string\n /** Logical experiment id (campaignId by default; overridable per-run via opts). */\n experimentId: string\n variant: V\n variantId: string\n scenarioId: string\n scenarioTags: Record<string, string>\n seed: number\n splitTag: RunSplitTag\n /**\n * The TraceEmitter for this run, with `onRunComplete` hooks pre-wired\n * (analyst auto-execution if configured, plus integrity check). The\n * runner MUST call `emitter.startRun` before doing any work and either\n * `emitter.endRun` or `emitter.abortRun` before returning.\n */\n emitter: TraceEmitter\n store: TraceStore\n rawSink: RawProviderSink\n /**\n * Pre-wired LLM client options — `rawSink` and `traceContext` are populated\n * so any `callLlm(req, ctx.llmOpts)` automatically captures raw HTTP. The\n * runner can spread additional fields if needed.\n */\n llmOpts: LlmClientOptions\n}\n\nexport interface CampaignRunOutcome {\n /** Did the run pass? Mirrors `RunOutcome.pass` semantics. */\n pass: boolean\n /** Score for the run on its split. Maps to `searchScore` or `holdoutScore`. */\n score: number\n /** Mandatory cost in USD. Use 0 + raw.cost_unknown=1 only if truly unknown. */\n costUsd: number\n tokenUsage: RunTokenUsage\n /** Snapshot model id (e.g. `claude-sonnet-4-6@2025-04-15`). */\n model: string\n /** sha256 of the effective prompt sent to the model. */\n promptHash: string\n /** sha256 of the effective config (model, temperature, tools, judges, splits). */\n configHash: string\n /** Optional extra numeric metrics to land in `outcome.raw`. */\n raw?: Record<string, number>\n /** Optional failure-taxonomy tag if the run failed. */\n failureMode?: string\n /** Optional judge metadata when a judge was used. */\n judgeMetadata?: RunJudgeMetadata\n /**\n * Optional per-judge / per-dim breakdown for ensemble-judged runs.\n * Propagated to `outcome.judgeScores` on the resulting `RunRecord`.\n * Single-judge or scalar-only runs leave this unset.\n */\n judgeScores?: JudgeScoresRecord\n /**\n * Agent profile cell observed by the runner. When supplied, it overrides\n * `EvalCampaignOptions.agentProfile` for this run and must match the\n * outcome's `model` and `promptHash`.\n */\n agentProfile?: AgentProfileCell | AgentProfileCellInput\n}\n\nexport type CampaignRunner<V> = (ctx: CampaignRunContext<V>) => Promise<CampaignRunOutcome>\n\nexport type CampaignIntegrityPolicy = 'throw' | 'mark_failed' | 'log'\n\nexport interface EvalCampaignOptions<V> {\n /**\n * Stable id for the campaign. Used as the default `experimentId` on\n * every run, and folded into the campaign fingerprint.\n */\n campaignId: string\n variants: CampaignVariant<V>[]\n scenarios: CampaignScenario[]\n /** Default `[0, 1, 2]`. */\n seeds?: number[]\n /** Default `'holdout'` — the split that anchors a launch decision. */\n splitTag?: RunSplitTag\n /** Git SHA the campaign is run against. Mandatory; `RunRecord` rejects unset. */\n commitSha: string\n /**\n * LLM client config. Augmented per-run with `rawSink` and `traceContext`\n * before being passed to the runner. The campaign asserts this config\n * matches `routeRequirements` once at preflight.\n */\n llmOpts: LlmClientOptions\n /**\n * Default `{ requireExplicitBaseUrl: true, requireAuth: true }` — fail\n * loud if the campaign would silently fall back to the public router or\n * run unauthenticated. Override with an empty object to disable.\n */\n routeRequirements?: LlmRouteRequirements\n /**\n * Per-run TraceStore factory. Common shape: a fresh store per run keyed\n * on `runId`. Implementations that share a store across the campaign\n * are valid — the campaign only writes through `emitter`.\n */\n storeFactory: (params: CampaignFactoryParams) => TraceStore\n /**\n * Per-run RawProviderSink factory. Defaults to `FileSystemRawProviderSink`\n * rooted at `${workDir}/raw-events/${runId}` if `workDir` is supplied;\n * otherwise required. Forensic capture is non-negotiable in a campaign\n * run — pass `NoopRawProviderSink` explicitly if you want to opt out.\n */\n rawSinkFactory?: (params: CampaignFactoryParams) => RawProviderSink\n /**\n * Filesystem root for default `rawSinkFactory`. Ignored if\n * `rawSinkFactory` is supplied.\n */\n workDir?: string\n /**\n * Extra `onRunComplete` hooks the campaign appends (after its own\n * integrity-check hook). Pass `traceAnalystOnRunComplete(...)` here.\n */\n onRunComplete?: RunCompleteHook[]\n /**\n * Per-run integrity expectations. Defaults to:\n * `{ llmSpansMin: 1, requireRawCoverageOfLlmSpans: true, requireOutcome: true }`.\n * Override (e.g. `{ llmSpansMin: 0 }`) for runs that don't call LLMs.\n */\n integrity?: RunIntegrityExpectations\n /** Behaviour when integrity fails. Default `'mark_failed'`. */\n onIntegrityFailure?: CampaignIntegrityPolicy\n /**\n * Per-run runner. Receives a fully-wired context; produces an outcome\n * the campaign converts into a `RunRecord`.\n */\n runner: CampaignRunner<V>\n /**\n * If set, the campaign computes `researchReport` at the end. `comparator`\n * is a `variantId`. Other fields are forwarded verbatim.\n */\n report?: { comparator?: string } & Omit<\n ResearchReportOptions,\n 'comparator' | 'preregistrationHash' | 'generatedAt'\n >\n /**\n * Hash of a signed `HypothesisManifest` (see `pre-registration.ts`).\n * Embedded in the campaign fingerprint and the research report.\n */\n preregistrationHash?: string\n /** Local concurrency. Default `1` (sequential). */\n concurrency?: number\n /**\n * Override the time source. Tests pass a mock to make wallMs deterministic.\n */\n now?: () => number\n /** Override the runId generator. Tests pin this. */\n runId?: (params: CampaignFactoryParams) => string\n /**\n * Agent profile cell for campaign runs. Static profiles can pass an object;\n * routers or variant-specific harnesses can pass a factory. The campaign\n * stamps the built cell onto every `RunRecord` and rejects profile/model or\n * profile/prompt contradictions.\n */\n agentProfile?:\n | AgentProfileCell\n | AgentProfileCellInput\n | ((\n params: CampaignFactoryParams & {\n variant: V\n scenarioTags: Record<string, string>\n },\n ) =>\n | AgentProfileCell\n | AgentProfileCellInput\n | Promise<AgentProfileCell | AgentProfileCellInput>)\n}\n\nexport interface CampaignFactoryParams {\n campaignId: string\n runId: string\n variantId: string\n scenarioId: string\n seed: number\n}\n\nexport interface FailedRun {\n runId: string\n variantId: string\n scenarioId: string\n seed: number\n reason: string\n error?: string\n}\n\nexport interface EvalCampaignResult {\n campaignId: string\n /** SHA-256 over canonicalised `(variantIds, scenarioIds, seeds, comparator, splitTag, baseUrl, provider, preregistrationHash)`. */\n campaignFingerprint: string\n preregistrationHash: string | null\n /** Successful runs only. Failed runs land in `failedRuns`. */\n runs: RunRecord[]\n /** Integrity reports for every successful run. */\n integrityReports: RunIntegrityReport[]\n failedRuns: FailedRun[]\n /** Computed when `report` is set on options. */\n report?: ResearchReport\n startedAt: string\n endedAt: string\n}\n\n// ── Implementation ───────────────────────────────────────────────────────\n\nconst DEFAULT_INTEGRITY: RunIntegrityExpectations = {\n llmSpansMin: 1,\n requireRawCoverageOfLlmSpans: true,\n requireOutcome: true,\n}\n\nconst DEFAULT_ROUTE: LlmRouteRequirements = {\n requireExplicitBaseUrl: true,\n requireAuth: true,\n}\n\nexport async function runEvalCampaign<V>(\n opts: EvalCampaignOptions<V>,\n): Promise<EvalCampaignResult> {\n // ── Preflight ──────────────────────────────────────────────────────\n assertLlmRoute(opts.llmOpts, opts.routeRequirements ?? DEFAULT_ROUTE)\n\n if (opts.variants.length === 0) {\n throw new Error('runEvalCampaign: variants must be non-empty.')\n }\n if (opts.scenarios.length === 0) {\n throw new Error('runEvalCampaign: scenarios must be non-empty.')\n }\n const variantIds = new Set<string>()\n for (const v of opts.variants) {\n if (variantIds.has(v.id)) {\n throw new Error(`runEvalCampaign: duplicate variant id \"${v.id}\".`)\n }\n variantIds.add(v.id)\n }\n const scenarioIds = new Set<string>()\n for (const s of opts.scenarios) {\n if (scenarioIds.has(s.scenarioId)) {\n throw new Error(`runEvalCampaign: duplicate scenarioId \"${s.scenarioId}\".`)\n }\n scenarioIds.add(s.scenarioId)\n }\n if (opts.report?.comparator && !variantIds.has(opts.report.comparator)) {\n throw new Error(\n `runEvalCampaign: report.comparator \"${opts.report.comparator}\" is not a configured variantId.`,\n )\n }\n if (!opts.commitSha) {\n throw new Error('runEvalCampaign: commitSha is required (every RunRecord needs it).')\n }\n\n const seeds = opts.seeds ?? [0, 1, 2]\n const splitTag: RunSplitTag = opts.splitTag ?? 'holdout'\n const concurrency = Math.max(1, opts.concurrency ?? 1)\n const integrity = { ...DEFAULT_INTEGRITY, ...(opts.integrity ?? {}) }\n const onIntegrityFailure: CampaignIntegrityPolicy = opts.onIntegrityFailure ?? 'mark_failed'\n const now = opts.now ?? (() => Date.now())\n const baseUrl = (opts.llmOpts.baseUrl ?? '').replace(/\\/+$/, '')\n const provider = opts.llmOpts.provider ?? null\n const preregistrationHash = opts.preregistrationHash ?? null\n\n const rawSinkFactory = opts.rawSinkFactory ?? defaultRawSinkFactory(opts.workDir)\n\n // ── Fingerprint ────────────────────────────────────────────────────\n const campaignFingerprint = await hashJson(\n canonicalize({\n campaignId: opts.campaignId,\n variants: opts.variants.map((v) => v.id).sort(),\n scenarios: opts.scenarios.map((s) => s.scenarioId).sort(),\n seeds: [...seeds].sort((a, b) => a - b),\n splitTag,\n comparator: opts.report?.comparator ?? null,\n baseUrl,\n provider,\n preregistrationHash,\n }),\n )\n\n // ── Plan the matrix ────────────────────────────────────────────────\n type Cell = { variant: CampaignVariant<V>; scenario: CampaignScenario; seed: number }\n const cells: Cell[] = []\n for (const variant of opts.variants) {\n for (const scenario of opts.scenarios) {\n for (const seed of seeds) {\n cells.push({ variant, scenario, seed })\n }\n }\n }\n\n const startedAt = new Date(now()).toISOString()\n const runs: RunRecord[] = []\n const integrityReports: RunIntegrityReport[] = []\n const failedRuns: FailedRun[] = []\n\n // ── Execute (bounded-concurrency worker pool) ──────────────────────\n let cursor = 0\n async function worker(): Promise<void> {\n while (true) {\n const i = cursor++\n if (i >= cells.length) return\n const cell = cells[i]!\n try {\n const result = await runOneCell(cell)\n runs.push(result.record)\n integrityReports.push(result.integrity)\n } catch (err) {\n if (err instanceof CellExecutionError) {\n failedRuns.push(err.failed)\n if (err.integrity) integrityReports.push(err.integrity)\n } else {\n // Genuine bug — not a runner failure, not an integrity failure.\n // Surface it; don't silently mask.\n throw err\n }\n }\n }\n }\n\n async function runOneCell(\n cell: Cell,\n ): Promise<{ record: RunRecord; integrity: RunIntegrityReport }> {\n const runId = (opts.runId ?? defaultRunId)({\n campaignId: opts.campaignId,\n runId: '', // unused by default generator\n variantId: cell.variant.id,\n scenarioId: cell.scenario.scenarioId,\n seed: cell.seed,\n })\n const factoryParams: CampaignFactoryParams = {\n campaignId: opts.campaignId,\n runId,\n variantId: cell.variant.id,\n scenarioId: cell.scenario.scenarioId,\n seed: cell.seed,\n }\n const store = opts.storeFactory(factoryParams)\n const rawSink = rawSinkFactory(factoryParams)\n\n const emitter = new TraceEmitter(store, {\n runId,\n now: opts.now,\n onRunComplete: opts.onRunComplete,\n })\n\n const llmOpts: LlmClientOptions = {\n ...opts.llmOpts,\n rawSink,\n traceContext: { runId },\n }\n\n const ctx: CampaignRunContext<V> = {\n runId,\n experimentId: opts.campaignId,\n variant: cell.variant.payload,\n variantId: cell.variant.id,\n scenarioId: cell.scenario.scenarioId,\n scenarioTags: cell.scenario.tags ?? {},\n seed: cell.seed,\n splitTag,\n emitter,\n store,\n rawSink,\n llmOpts,\n }\n\n const wallStart = now()\n let outcome: CampaignRunOutcome\n try {\n outcome = await opts.runner(ctx)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n // The runner threw mid-execution; give it a chance to have aborted.\n try {\n await emitter.abortRun(message)\n } catch {\n // Already aborted/ended; ignore.\n }\n throw new CellExecutionError({\n runId,\n variantId: cell.variant.id,\n scenarioId: cell.scenario.scenarioId,\n seed: cell.seed,\n reason: 'runner_threw',\n error: message,\n })\n }\n const wallMs = now() - wallStart\n\n const integrityReport = await assertRunCaptured(store, runId, { ...integrity, rawSink })\n if (!integrityReport.ok) {\n switch (onIntegrityFailure) {\n case 'throw':\n throw new RunIntegrityError(integrityReport)\n case 'mark_failed':\n throw new CellExecutionError(\n {\n runId,\n variantId: cell.variant.id,\n scenarioId: cell.scenario.scenarioId,\n seed: cell.seed,\n reason: 'integrity_failed',\n error: integrityReport.issues.map((i) => i.code).join(', '),\n },\n integrityReport,\n )\n case 'log':\n // Caller wants the run admitted with a flagged report; fall through.\n break\n }\n }\n\n const recordOutcome: RunOutcome = {\n raw: outcome.raw ?? {},\n }\n if (splitTag === 'holdout') recordOutcome.holdoutScore = outcome.score\n else recordOutcome.searchScore = outcome.score\n if (outcome.judgeScores !== undefined) recordOutcome.judgeScores = outcome.judgeScores\n\n const record: RunRecord = {\n runId,\n experimentId: opts.campaignId,\n candidateId: cell.variant.id,\n seed: cell.seed,\n model: outcome.model,\n promptHash: outcome.promptHash,\n configHash: outcome.configHash,\n commitSha: opts.commitSha,\n wallMs,\n costUsd: outcome.costUsd,\n tokenUsage: outcome.tokenUsage,\n judgeMetadata: outcome.judgeMetadata,\n outcome: recordOutcome,\n failureMode: outcome.failureMode,\n splitTag,\n scenarioId: cell.scenario.scenarioId,\n }\n const profileSource =\n outcome.agentProfile ??\n (typeof opts.agentProfile === 'function'\n ? await opts.agentProfile({\n campaignId: opts.campaignId,\n runId,\n variantId: cell.variant.id,\n scenarioId: cell.scenario.scenarioId,\n seed: cell.seed,\n variant: cell.variant.payload,\n scenarioTags: cell.scenario.tags ?? {},\n })\n : opts.agentProfile)\n if (profileSource !== undefined) {\n const agentProfile = await resolveAgentProfileCell(profileSource)\n assertAgentProfileMatchesRun(agentProfile, outcome.model, outcome.promptHash)\n record.agentProfile = agentProfile\n }\n return { record: validateRunRecord(record), integrity: integrityReport }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, cells.length) }, () => worker())\n await Promise.all(workers)\n\n // ── Optional research report ───────────────────────────────────────\n let report: ResearchReport | undefined\n if (opts.report) {\n const reportOpts: ResearchReportOptions = {\n ...opts.report,\n comparator: opts.report.comparator,\n split: splitTag === 'dev' ? 'search' : splitTag,\n generatedAt: new Date(now()).toISOString(),\n preregistrationHash: preregistrationHash ?? undefined,\n }\n report = await researchReport(runs, reportOpts)\n }\n\n const endedAt = new Date(now()).toISOString()\n\n return {\n campaignId: opts.campaignId,\n campaignFingerprint,\n preregistrationHash,\n runs,\n integrityReports,\n failedRuns,\n report,\n startedAt,\n endedAt,\n }\n}\n\n// ── Internal ─────────────────────────────────────────────────────────────\n\nclass CellExecutionError extends Error {\n readonly failed: FailedRun\n readonly integrity?: RunIntegrityReport\n constructor(failed: FailedRun, integrity?: RunIntegrityReport) {\n super(`cell ${failed.variantId}/${failed.scenarioId}@${failed.seed} failed: ${failed.reason}`)\n this.failed = failed\n this.integrity = integrity\n }\n}\n\nfunction defaultRawSinkFactory(workDir: string | undefined) {\n return (params: CampaignFactoryParams): RawProviderSink => {\n if (!workDir) {\n throw new Error(\n 'runEvalCampaign: rawSinkFactory not supplied and workDir not set. Pass either to enable raw provider capture, or pass `new NoopRawProviderSink()` via rawSinkFactory to opt out explicitly.',\n )\n }\n return new FileSystemRawProviderSink({\n dir: `${workDir}/raw-events/${params.runId}`,\n })\n }\n}\n\nasync function resolveAgentProfileCell(\n input: AgentProfileCell | AgentProfileCellInput,\n): Promise<AgentProfileCell> {\n if (isAgentProfileCell(input)) {\n if (!(await verifyAgentProfileCell(input))) {\n throw new Error(`runEvalCampaign: agentProfile.cellId does not match its content`)\n }\n return input\n }\n return buildAgentProfileCell(input)\n}\n\nfunction isAgentProfileCell(\n input: AgentProfileCell | AgentProfileCellInput,\n): input is AgentProfileCell {\n return 'schemaVersion' in input && 'cellId' in input\n}\n\nfunction assertAgentProfileMatchesRun(\n profile: AgentProfileCell,\n model: string,\n promptHash: string,\n): void {\n if (profile.model !== undefined && profile.model !== model) {\n throw new Error(\n `runEvalCampaign: agentProfile.model \"${profile.model}\" does not match outcome.model \"${model}\"`,\n )\n }\n if (profile.promptHash !== undefined && profile.promptHash !== promptHash) {\n throw new Error(\n `runEvalCampaign: agentProfile.promptHash \"${profile.promptHash}\" does not match outcome.promptHash \"${promptHash}\"`,\n )\n }\n}\n\nfunction defaultRunId(params: CampaignFactoryParams): string {\n // Stable across re-runs: fingerprint of (campaignId, variantId, scenarioId, seed).\n // Caller can override via opts.runId for non-deterministic IDs.\n const base = `${params.campaignId}::${params.variantId}::${params.scenarioId}::${params.seed}`\n // Lightweight hex: we don't need crypto-grade here, just stability + uniqueness.\n let h1 = 0x811c9dc5\n let h2 = 0x12345678\n for (let i = 0; i < base.length; i++) {\n const c = base.charCodeAt(i)\n h1 = Math.imul(h1 ^ c, 0x01000193) >>> 0\n h2 = Math.imul(h2 ^ c, 0x9e3779b1) >>> 0\n }\n return `run-${h1.toString(16).padStart(8, '0')}${h2.toString(16).padStart(8, '0')}`\n}\n"],"mappings":"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","names":[]}