@tangle-network/agent-eval 0.123.1 → 0.123.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +151 -161
  3. package/dist/analyst/index.d.ts +9 -1
  4. package/dist/analyst/index.js +5 -5
  5. package/dist/authenticity/index.js +3 -2
  6. package/dist/authenticity/index.js.map +1 -1
  7. package/dist/benchmarks/index.d.ts +2 -1
  8. package/dist/benchmarks/index.js +6 -6
  9. package/dist/campaign/index.d.ts +29 -33
  10. package/dist/campaign/index.js +6 -6
  11. package/dist/{chunk-A5S77LSE.js → chunk-4SOQ4ND2.js} +2 -2
  12. package/dist/{chunk-VJ7T5WIO.js → chunk-5YMKIFYP.js} +3 -3
  13. package/dist/{chunk-U5CHZ5M3.js → chunk-DNVPOYUS.js} +4 -4
  14. package/dist/{chunk-6WX7CBAR.js → chunk-E3HAD4A3.js} +19 -8
  15. package/dist/chunk-E3HAD4A3.js.map +1 -0
  16. package/dist/{chunk-LBAHQOBI.js → chunk-EBDOTTZJ.js} +37 -11
  17. package/dist/chunk-EBDOTTZJ.js.map +1 -0
  18. package/dist/{chunk-XJYR7XFV.js → chunk-GC4ATIKK.js} +1 -1
  19. package/dist/chunk-GC4ATIKK.js.map +1 -0
  20. package/dist/{chunk-HZJF4IUO.js → chunk-HQY7LBV2.js} +3 -3
  21. package/dist/{chunk-NJC7U437.js → chunk-J7S4YM27.js} +6 -5
  22. package/dist/chunk-J7S4YM27.js.map +1 -0
  23. package/dist/{chunk-S3UZOQ5Y.js → chunk-LOW3U7JZ.js} +2 -2
  24. package/dist/{chunk-OYZAPX5G.js → chunk-R226UZOI.js} +2 -2
  25. package/dist/{chunk-GS3FJGUF.js → chunk-RQP5UTK5.js} +120 -14
  26. package/dist/chunk-RQP5UTK5.js.map +1 -0
  27. package/dist/{chunk-G2GPNLSX.js → chunk-WMJR67FX.js} +3 -3
  28. package/dist/{chunk-FC5NDO3E.js → chunk-WXQTVEKM.js} +3 -3
  29. package/dist/cli.js +100 -10
  30. package/dist/cli.js.map +1 -1
  31. package/dist/contract/index.d.ts +97 -5
  32. package/dist/contract/index.js +9 -7
  33. package/dist/contract/index.js.map +1 -1
  34. package/dist/control.js +3 -3
  35. package/dist/fuzz.js +3 -2
  36. package/dist/fuzz.js.map +1 -1
  37. package/dist/hosted/index.d.ts +8 -2
  38. package/dist/index.d.ts +10 -2
  39. package/dist/index.js +13 -13
  40. package/dist/index.js.map +1 -1
  41. package/dist/openapi.json +1 -1
  42. package/dist/rl.d.ts +9 -1
  43. package/dist/rl.js +4 -4
  44. package/dist/storyboard/index.js +1 -1
  45. package/dist/storyboard/index.js.map +1 -1
  46. package/dist/traces.js +3 -3
  47. package/dist/wire/index.d.ts +61 -4
  48. package/dist/wire/index.js +2 -2
  49. package/docs/adapters-observability.md +6 -6
  50. package/docs/building-doctrine.md +5 -5
  51. package/docs/concepts.md +29 -29
  52. package/docs/customer-journeys.md +80 -155
  53. package/docs/design/loop-taxonomy.md +26 -27
  54. package/docs/design.md +70 -0
  55. package/docs/distributed-driver.md +14 -14
  56. package/docs/eval-surface-map.md +11 -11
  57. package/docs/hosted-ingest-spec.md +4 -4
  58. package/docs/improvement-glossary.md +38 -38
  59. package/docs/insight-report.md +32 -27
  60. package/docs/multi-shot-optimization.md +8 -8
  61. package/docs/research-report-methodology.md +9 -9
  62. package/docs/self-improvement-map.md +13 -13
  63. package/docs/trace-analysis.md +2 -2
  64. package/docs/wire-protocol.md +16 -16
  65. package/package.json +2 -1
  66. package/dist/chunk-6WX7CBAR.js.map +0 -1
  67. package/dist/chunk-GS3FJGUF.js.map +0 -1
  68. package/dist/chunk-LBAHQOBI.js.map +0 -1
  69. package/dist/chunk-NJC7U437.js.map +0 -1
  70. package/dist/chunk-XJYR7XFV.js.map +0 -1
  71. package/docs/auto-research-loop-end-to-end.md +0 -186
  72. /package/dist/{chunk-A5S77LSE.js.map → chunk-4SOQ4ND2.js.map} +0 -0
  73. /package/dist/{chunk-VJ7T5WIO.js.map → chunk-5YMKIFYP.js.map} +0 -0
  74. /package/dist/{chunk-U5CHZ5M3.js.map → chunk-DNVPOYUS.js.map} +0 -0
  75. /package/dist/{chunk-HZJF4IUO.js.map → chunk-HQY7LBV2.js.map} +0 -0
  76. /package/dist/{chunk-S3UZOQ5Y.js.map → chunk-LOW3U7JZ.js.map} +0 -0
  77. /package/dist/{chunk-OYZAPX5G.js.map → chunk-R226UZOI.js.map} +0 -0
  78. /package/dist/{chunk-G2GPNLSX.js.map → chunk-WMJR67FX.js.map} +0 -0
  79. /package/dist/{chunk-FC5NDO3E.js.map → chunk-WXQTVEKM.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -8,6 +8,7 @@ All notable changes to `@tangle-network/agent-eval` and its sibling `agent-eval-
8
8
 
9
9
  ### Added
10
10
 
11
+ - `llmPolicyEditProposer({ redactCurrentSurfaceForModel })` can remove credentials and unrelated fields from the current surface sent to the model while applying validated edits to the complete original surface.
11
12
  - `CostLedger.listPending()` exposes immutable pending paid calls and distinguishes calls that are active, late after cancellation, or interrupted by a prior process so durable workflows can reconcile exact reservations before resuming.
12
13
  - `traceAnalystProposer()` accepts an opt-in `resolvePriorFindings` callback that forwards canonical prior findings into the existing analyst registry.
13
14
 
@@ -20,6 +21,10 @@ All notable changes to `@tangle-network/agent-eval` and its sibling `agent-eval-
20
21
 
21
22
  ### Fixed
22
23
 
24
+ - Include the complete required response shape in `llmPolicyEditProposer` model instructions, and add `LlmClientOptions.jsonSchemaTransport: 'json-object'` for providers that do not implement native JSON Schema enforcement.
25
+ - `compositeProposer` restores each member's original labels when replaying history, so stateful members do not repeat candidates whose labels were decorated for provenance.
26
+ - `InsightReport.interRater.kappa` now reports quadratic weighted kappa instead of Pearson correlation.
27
+ Read `interRater.pearson` for the previous correlation measure; `icc` and `spearman` are now reported separately.
23
28
  - Compute contextual-bandit doubly robust estimates with separate logged-action and target-policy value terms, expose how many rows use DR versus IPS or the deprecated scalar path, and carry both values through belief-state records.
24
29
 
25
30
  ## [0.122.2] — 2026-07-17 — premeasured optimization continuation
package/README.md CHANGED
@@ -1,202 +1,211 @@
1
1
  # `@tangle-network/agent-eval`
2
2
 
3
- Evaluate and improve AI agents from the runs they already produce.
4
-
5
- `agent-eval` turns agent outputs, traces, judge scores, and production feedback into a decision packet: did this change help, what failed, what should ship, and what needs more data?
3
+ A TypeScript library that measures whether your AI agent got better or worse, using the runs it already produces.
6
4
 
7
5
  [![npm](https://img.shields.io/npm/v/@tangle-network/agent-eval.svg)](https://www.npmjs.com/package/@tangle-network/agent-eval)
8
6
  [![pypi](https://img.shields.io/pypi/v/agent-eval-rpc.svg)](https://pypi.org/project/agent-eval-rpc/)
9
7
  [![tests](https://github.com/tangle-network/agent-eval/actions/workflows/ci.yml/badge.svg)](https://github.com/tangle-network/agent-eval/actions/workflows/ci.yml)
10
8
  [![license: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
11
9
 
10
+ You give it agent runs: outputs, traces, scores, and production feedback.
11
+ It gives you numbers you can act on: did the new prompt beat the old one, is the difference statistically real, what failed and why, and whether the change should ship.
12
+
12
13
  Use it when you need to:
13
14
 
14
- - compare a candidate agent/prompt/model against a baseline,
15
- - turn production traces or human feedback into eval results,
16
- - run a gated self-improvement loop,
17
- - explain failures by cluster, cost, judge disagreement, and release risk.
15
+ - compare a candidate prompt/model/config against a baseline, with confidence intervals instead of vibes,
16
+ - turn production traces or human feedback you already collect into eval results,
17
+ - run an automated improve-and-verify loop over a prompt, held to a promotion rule you choose,
18
+ - explain failures by cluster, cost, and judge disagreement.
18
19
 
19
- It is a library, not a SaaS requirement. TypeScript is first-class; Python can call the same wire protocol through `agent-eval-rpc`.
20
+ The deterministic evaluator runs in your process and makes no network calls.
21
+ Features that use a model send their inputs to the model client you pass.
22
+ Trace exporters and hosted ingestion are also opt-in.
23
+ Python can drive the same engine over HTTP via [`agent-eval-rpc`](./clients/python/README.md).
20
24
 
21
25
  ---
22
26
 
23
27
  ## Install
24
28
 
25
29
  ```sh
26
- pnpm add @tangle-network/agent-eval
30
+ pnpm add @tangle-network/agent-eval # or npm / yarn
27
31
  ```
28
32
 
29
- Python clients can use the RPC package:
30
-
31
33
  ```sh
32
- pip install agent-eval-rpc
34
+ pip install agent-eval-rpc # optional Python client
33
35
  ```
34
36
 
35
37
  ---
36
38
 
37
- ## Quick start
38
-
39
- ### 1. Analyze runs you already have
39
+ ## Quickstart
40
40
 
41
- Start here if you already have production logs, benchmark rows, human ratings, or agent run records.
41
+ Copy this into `quickstart.ts` and run `npx tsx quickstart.ts`.
42
+ It is fully offline; the "agent" and "judge" are plain functions you replace with your own.
42
43
 
43
44
  ```ts
44
- import { analyzeRuns } from '@tangle-network/agent-eval/contract'
45
+ import { defineAgentEval } from '@tangle-network/agent-eval/contract'
45
46
 
46
- const report = await analyzeRuns({
47
- runs, // RunRecord[]
48
- baselineRuns,
47
+ interface SupportScenario {
48
+ id: string
49
+ kind: 'support'
50
+ }
51
+
52
+ async function main() {
53
+ const scenarios: SupportScenario[] = [
54
+ { id: 'refund', kind: 'support' },
55
+ { id: 'shipping', kind: 'support' },
56
+ { id: 'cancel', kind: 'support' },
57
+ ]
58
+
59
+ const evalKit = defineAgentEval<SupportScenario, string>({
60
+ scenarios,
61
+ // Your agent takes the prompt under test and one scenario, then returns its output.
62
+ agent: async (prompt, scenario) =>
63
+ String(prompt).includes('ticket') ? `Re ${scenario.id}: on it.` : 'On it.',
64
+ // Your judge scores one output from 0 to 1. Swap in an LLM judge for real work.
65
+ judge: {
66
+ name: 'cites-ticket',
67
+ dimensions: [{ key: 'ticket_id', description: 'The answer includes the ticket id' }],
68
+ score: ({ artifact, scenario }) => {
69
+ const ticketId = artifact.includes(scenario.id) ? 1 : 0
70
+ return { dimensions: { ticket_id: ticketId }, composite: ticketId, notes: '' }
71
+ },
72
+ },
73
+ baselineSurface: 'Answer the customer politely.',
74
+ expectUsage: 'off',
75
+ })
76
+
77
+ console.log('baseline: ', (await evalKit.evaluate()).aggregates.byJudge)
78
+ const candidate = await evalKit.evaluate({ surface: 'Answer politely, cite the ticket id.' })
79
+ console.log('candidate:', candidate.aggregates.byJudge)
80
+ }
81
+
82
+ main().catch((error: unknown) => {
83
+ console.error(error)
84
+ process.exitCode = 1
49
85
  })
50
-
51
- console.log(report.recommendations)
52
- console.log(report.lift)
53
- console.log(report.failureClusters)
54
86
  ```
55
87
 
56
- The output includes score distributions, lift confidence intervals, failure modes, cost-quality tradeoffs, judge agreement, contamination checks, and release recommendations when the input supports them.
57
-
58
- ### 2. Define one eval, then score or improve
88
+ Output:
59
89
 
60
- Use this when you have scenarios, a runnable agent, and judges.
61
-
62
- ```ts
63
- import { defineAgentEval } from '@tangle-network/agent-eval/contract'
64
-
65
- const evalKit = defineAgentEval({
66
- scenarios,
67
- agent: async (surface, scenario, ctx) =>
68
- myAgent.run({ scenario, systemPrompt: String(surface), signal: ctx.signal }),
69
- judge: myJudge,
70
- baselineSurface: currentPrompt,
71
- })
90
+ ```
91
+ baseline: { 'cites-ticket': { mean: 0, stdev: 0, ci95: [ 0, 0 ], n: 3 } }
92
+ candidate: { 'cites-ticket': { mean: 1, stdev: 0, ci95: [ 1, 1 ], n: 3 } }
93
+ ```
72
94
 
73
- const baseline = await evalKit.evaluate()
74
- const result = await evalKit.improve({ budget: { generations: 2 } })
95
+ Each `evaluate()` call runs every scenario through the agent, scores each output with the judge, and returns per-judge score distributions.
96
+ The "surface" is the thing you are changing: here a system-prompt string, and in general any prompt or config value.
97
+ From the same definition, `evalKit.improve()` proposes candidate prompts, measures each one, and checks the winner against a held-back scenario set before recommending it.
98
+ The default candidate generator calls a model, so pass `llm: { baseUrl, apiKey, model }` to `.improve()` or provide your own `proposer`.
75
99
 
76
- console.log(baseline.aggregates.byJudge)
77
- console.log(result.gateDecision)
78
- console.log(result.winner.surface)
79
- console.log(result.insight.recommendations)
80
- ```
100
+ Already have run data and no runnable agent? Skip the loop and call [`analyzeRuns()`](./docs/concepts.md#the-top-level-functions) on your existing records instead.
81
101
 
82
- `defineAgentEval()` is a small wrapper over `runEval()` and `selfImprove()`. It lets you define scenarios, agent, judge, and baseline once, then either score one surface with `.evaluate()` or run the gated loop with `.improve()`.
102
+ ### Use A Model Judge
83
103
 
84
- ### 3. Adapt existing data
104
+ `llmJudge()` converts one model call into the same `JudgeConfig` used above:
85
105
 
86
106
  ```ts
87
- import { analyzeRuns, fromFeedbackTable, fromOtelSpans } from '@tangle-network/agent-eval/contract'
107
+ import { createChatClient, llmJudge } from '@tangle-network/agent-eval/contract'
88
108
 
89
- const { runs, raterScores } = fromFeedbackTable({
90
- ratings: parseYourFeedbackTable(),
91
- })
109
+ const apiKey = process.env.OPENAI_API_KEY
110
+ if (!apiKey) throw new Error('OPENAI_API_KEY is required')
92
111
 
93
- const traceRuns = fromOtelSpans({ spans: yourOtelSpans })
112
+ const chat = createChatClient({
113
+ transport: 'direct-provider',
114
+ baseUrl: process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1',
115
+ apiKey,
116
+ defaultModel: 'gpt-4.1-mini',
117
+ })
94
118
 
95
- await analyzeRuns({ runs: [...runs, ...traceRuns], raterScores })
119
+ const judge = llmJudge<string, SupportScenario>(
120
+ 'support-quality',
121
+ 'Score whether the response resolves the request using only supported facts.',
122
+ {
123
+ chat,
124
+ dimensions: [
125
+ { key: 'correct', description: 'The answer is factually correct' },
126
+ { key: 'complete', description: 'The answer addresses the whole request' },
127
+ ],
128
+ },
129
+ )
96
130
  ```
97
131
 
98
- ---
132
+ Pass `judge` to `defineAgentEval()` in place of the offline judge.
133
+ The model receives the scenario, artifact, scoring prompt, and dimension descriptions.
99
134
 
100
- ## Core concepts
135
+ ---
101
136
 
102
- - **RunRecord**: the durable row for one agent run: model, prompt/config hashes, split, cost, tokens, outcome.
103
- - **Scenario**: one task or case the agent attempts.
104
- - **Judge**: a scoring function, rule-based or model-based.
105
- - **Surface**: the thing being changed, usually a prompt string or config object.
106
- - **InsightReport**: the decision packet returned by `analyzeRuns()` and embedded in `selfImprove()`.
107
- - **Gate**: the policy that decides `ship`, `hold`, or `need_more_work`.
137
+ ## What's in the box
108
138
 
109
- ## Examples
139
+ One-line tour of the primitives. All of these are plain functions and interfaces you compose; start from `/contract` and pull in more only when you need it.
110
140
 
111
- | Journey | Example | Who it's for |
112
- |---|---|---|
113
- | **Closed loop** improve a prompt under statistical confidence | [`examples/selfimprove-quickstart/`](./examples/selfimprove-quickstart/) | Teams with scenarios + judges + agent in hand |
114
- | **Multi-rater feedback corpus** turn Obsidian/Sheets/CSV ratings into actionable insights | [`examples/customer-feedback-loop/`](./examples/customer-feedback-loop/) | Teams reviewing AI outputs by hand who want to compress that taste into per-member LLM judges + close the loop |
115
- | **Production OTel traces** analyze logs you already have, no closed loop required | [`examples/customer-otel-traces/`](./examples/customer-otel-traces/) | Teams running agents in prod with observability, no eval discipline yet |
141
+ | Primitive | What it does |
142
+ |---|---|
143
+ | **Evaluation** (`runEval`, `runCampaign`) | Run agent × scenarios × repetitions, score every run, and record the result. |
144
+ | **Scoring** (`JudgeConfig`, `llmJudge`, calibration) | Score one output on weighted dimensions with code or a model, then compare model scores against human ratings. |
145
+ | **Release rules** (`heldOutGate`, `paretoSignificanceGate`, `composeGate`, …) | Decide whether a candidate ships, such as requiring an improvement on scenarios that candidate generation never saw. |
146
+ | **Candidate generation** (`gepaProposer`, `evolutionaryProposer`, …) | Generate candidate prompts or configs from prior failures. |
147
+ | **Run analysis** (`analyzeRuns`, `diffRuns`) | Turn any set of `RunRecord`s into a report: score distributions, baseline-vs-candidate lift with confidence intervals, failure clusters, cost breakdown, recommendations. |
148
+ | **Intake adapters** (`fromFeedbackTable`, `fromOtelSpans`) | Convert data you already have, such as human ratings tables and OpenTelemetry spans, into `RunRecord`s. |
149
+ | **Cost tracking** | Attribute every model call's tokens and dollars to the run, phase, and judge that spent them, including interrupted calls. |
150
+ | **Human feedback storage** | Persist runs with approved, rejected, or edited labels so review activity becomes training and eval data. |
151
+ | **Statistics** (`pairedBootstrap`, `benjaminiHochberg`, sequential tests) | The release-decision math, usable standalone. |
152
+ | **Trace tools** (`/traces`, `/analyst`) | Store and replay structured run traces; cluster failures with an LLM analyst panel. |
153
+ | **HTTP and RPC** (`/wire`) | Expose judging and ingestion to non-TypeScript stacks, including the Python client. |
154
+
155
+ Our own experiments with these primitives live in [`examples/`](./examples/README.md); they are demonstrations, not part of the API.
156
+
157
+ | Runnable example | Shows |
158
+ |---|---|
159
+ | [`examples/selfimprove-quickstart/`](./examples/selfimprove-quickstart/) | The closed improve-and-verify loop, fully offline |
160
+ | [`examples/customer-feedback-loop/`](./examples/customer-feedback-loop/) | Multi-rater human feedback (CSV/Sheets/Obsidian) → per-rater judges → report |
161
+ | [`examples/customer-otel-traces/`](./examples/customer-otel-traces/) | Production OpenTelemetry traces → report, no closed loop required |
116
162
 
117
- Each example: `README.md` + a single `index.ts` runnable via `pnpm tsx`. Prints the resulting `InsightReport` to stdout.
163
+ Each is a single `index.ts` you run with `pnpm tsx`.
118
164
 
119
165
  ---
120
166
 
121
- ## Subpath entry points
167
+ ## Entry points
168
+
169
+ Import from `@tangle-network/agent-eval/<subpath>`. Every row below is verified importable from the published package.
122
170
 
123
171
  | Subpath | What it gives you |
124
172
  |---|---|
125
- | `…/contract` | **The headline, frozen surface — new code starts here.** `defineAgentEval`, `selfImprove`, `analyzeRuns`, `runEval`, `runCampaign`, `runImprovementLoop`, `diffRuns`; intake adapters (`fromFeedbackTable`, `fromOtelSpans`); proposers (`gepaProposer`, `evolutionaryProposer`); gates (`defaultProductionGate`, `heldOutGate`, `paretoSignificanceGate`, `neutralizationGate`, `composeGate`); the deployment-outcome store; storage; and the five core types `Scenario` / `Dispatch` / `JudgeConfig` / `SurfaceProposer` / `Gate`. |
126
- | `…/hosted` | `createHostedClient` / `hostedClientFromEnv` + the wire types to ship eval-run events + trace spans to a hosted orchestrator (ours or your own implementation of the spec) |
127
- | `…/adapters/otel` | `createOtelBridge` forwards OpenTelemetry-shape spans into the hosted-tier ingest, no `@opentelemetry/*` dependency |
128
- | `…/adapters/langchain` | Wrap any LangChain `Runnable` as a `Dispatch` (or `JudgeConfig`), no `@langchain/core` peer dep |
129
- | `…/adapters/http` | `httpDispatch` + `runDispatchServer` run a campaign's worker on another machine (multi-region, remote worker execution) |
130
- | `…/campaign` | **The measurement + improvement engine**: `runProfileMatrix`, `compareProposers`, `analyzeCrossSurfaceInteractions`, every surface proposer (`gepaProposer`, `fapoProposer`, `parameterSweepProposer`, `haloProposer`, `skillOptProposer`, `aceProposer`, `memoryCurationProposer`, …), the gates, storage backends, and loop provenance. `/contract` re-exports the app-facing subset. |
131
- | `…/rl` | Bridge from eval artifacts to training signal: verifiable rewards, preferences, OPE, tournaments, contamination, compute curves, trainer-format exporters, process rewards, plus the durable corpus + `buildRlDataset` / datasheet bundle |
132
- | `…/reporting` | Release-decision statistics: `pairedBootstrap`, `benjaminiHochberg`, anytime-valid sequential e-values, `evaluateReleaseConfidence`, and the report renderers |
133
- | `…/analyst` | The trace-analyst surface: `AnalystRegistry` + `buildDefaultAnalystRegistry` (run the failure-clustering panel), `FindingsStore`, and the LLM chat transports |
134
- | `…/traces` | Trace stores + emitters, OTLP-JSONL deterministic replay, `analyzeTraces`, and the `traceAnalystOnRunComplete` hook |
135
- | `…/control` | Agent control loop: `runAgentControlLoop` (observe validate decide → act), action policy, propose/review |
136
- | `…/matrix` | `runAgentMatrix` an N-axis cartesian over caller-supplied substrate values, per-axis pass/score/cost/duration |
137
- | `…/multishot` | N-shot persona × shot matrix runner (`runMultishot` / `runMultishotMatrix`) |
138
- | `…/wire` | The cross-language HTTP/RPC server + Zod schemas (the source-of-truth protocol the Python client speaks) + the built-in rubric registry |
139
- | `…/benchmarks` | `BenchmarkAdapter` contract, `runBenchmarkAdapter`, `calibrateBenchmarkMetric`, standard retrieval parsers + ranked retrieval metrics, `deterministicSplit`, and the bundled `routing` reference benchmark |
140
-
141
- **Specialized surfaces** (subpath-only): `…/prm` (process-reward grading + best-of-N), `…/meta-eval` (judge calibration + the deployment-outcome store), `…/belief-state` (decision-point extraction + selective-policy reports), `…/pipelines` (trace-diagnostic views: budget breach, failure cluster, stuck loop, …), `…/governance` (EU AI Act / NIST AI RMF / SOC2 reports), `…/knowledge` (knowledge-readiness gating before a run), `…/builder-eval` (code-generator three-layer eval), `…/storyboard` (trace → watchable replay), `…/authenticity` (anti-Goodhart "real or convincing BS" scorer over produced files), `…/workflow` (workflow-trace eval + partner export), `…/telemetry` (Workers-safe telemetry client), `…/testing` (test-only reset helpers).
142
-
143
- The root export remains broad for compatibility; new code should prefer the focused subpaths above — `/contract` first.
144
-
145
- ---
146
-
147
- ## Composition with the stack
148
-
149
- agent-eval is the bottom of the layering: consumers depend on it, it depends on none of them.
150
-
151
- ```
152
- agent-runtime Runs agents (chat turns, one-shot tasks, multi-attempt loops), captures every
153
- run as a trace, and calls optimizePrompt / runImprovementLoop. Produces the
154
- RunRecords + traces agent-eval scores. Depends on agent-eval.
155
-
156
- agent-eval selfImprove, analyzeRuns, runCampaign + surface proposers (GEPA proposer, …), the gates
157
- (this repo) (heldOutGate, defaultProductionGate, paretoSignificanceGate), the InsightReport
158
- decision packet, the RL bridge, the wire protocol. Depends on neither consumer.
159
-
160
- agent-knowledge proposeKnowledgeWrites / applyKnowledgeWriteBlocks. agent-eval's analyst findings
161
- feed it; the knowledge gate consumes them. Depends on agent-eval.
162
-
163
- sandbox Sandbox.create, streamPrompt. One execution surface the runtime's
164
- loops run on; agent-eval scores what comes back.
165
- ```
166
-
167
- The rule: **agent-eval has zero upward dependencies on a consumer.** A concept that makes sense *without* a running agent loop — a verdict, a run record, a scenario, a judge score — is substrate and lives here. Runtime execution details (a validation context with an abort signal, a concrete sandbox session) live in agent-runtime or sandbox. Agent profile shape is the shared `@tangle-network/agent-interface` contract.
173
+ | `/contract` | **Start here.** Stable APIs for defining an eval, running it, improving a prompt, judging outputs, analyzing existing runs, and storing results. |
174
+ | `/campaign` | Lower-level control over candidate generation, release rules, storage, and comparisons. |
175
+ | `/reporting` | Statistical comparisons and report renderers. |
176
+ | `/analyst` | Model-based failure clustering and stored findings. |
177
+ | `/traces` | Trace stores, emitters, deterministic replay, trace analysis. |
178
+ | `/rl` | Export eval artifacts as training signal: rewards, preferences, trainer-format datasets. |
179
+ | `/benchmarks` | Benchmark adapter contract + retrieval metrics + a bundled reference benchmark. |
180
+ | `/wire` | The HTTP/RPC server and Zod schemas (what the Python client speaks). |
181
+ | `/hosted` | Client for shipping eval-run events to a remote orchestrator (see below). |
182
+ | `/control` | A generic observe validate decide act agent loop with eval-backed stopping rules. |
183
+ | `/matrix`, `/multishot` | N-axis configuration sweeps; multi-turn persona × turn-count runners. |
184
+ | `/meta-eval`, `/belief-state`, `/builder-eval`, `/pipelines`, `/storyboard`, `/authenticity`, `/fuzz`, `/trace-attributes` | Specialized surfaces: judge calibration, decision-point extraction, code-generator grading, trace diagnostics, run replay rendering, anti-gaming output checks, input fuzzing, trace attribute vocabulary. |
185
+
186
+ The root export (`@tangle-network/agent-eval`) remains broad for compatibility; prefer the subpaths for new code.
168
187
 
169
188
  ---
170
189
 
171
- ## Concepts + design
172
-
173
- - [`docs/concepts.md`](./docs/concepts.md) — the top-level entry points, the layering rule, and the wire-protocol contract (the five core contract types are documented in the `/contract` barrel itself)
174
- - [`docs/campaign-proposers.md`](./docs/campaign-proposers.md) — ELI5 proposer inputs/outputs, when to use each proposer, and the FAPO escalation policy
175
- - [`docs/insight-report.md`](./docs/insight-report.md) — annotated walkthrough of every section of the decision packet
176
- - [`docs/customer-journeys.md`](./docs/customer-journeys.md) — three end-to-end journeys with code + expected output
177
- - [`docs/adapters-observability.md`](./docs/adapters-observability.md) — composing agent-eval with LangSmith, Langfuse, Phoenix, OpenLLMetry, TraceAI
178
- - [`docs/wire-protocol.md`](./docs/wire-protocol.md) — the HTTP/RPC contract Python (and any future language) speaks
179
- - [`docs/hosted-ingest-spec.md`](./docs/hosted-ingest-spec.md) — the hosted-tier wire format, frozen at `2026-05-26.v1`
180
- - [`docs/design/loop-taxonomy.md`](./docs/design/loop-taxonomy.md) — plain-language vocabulary for execution drivers, workers, measurements, and proposers
190
+ ## Documentation
181
191
 
182
- The `.claude/skills/agent-eval/SKILL.md` skill ships embedded directives so LLM agents writing integration code don't reintroduce historical bug classes.
192
+ - [`docs/concepts.md`](./docs/concepts.md): the mental model for runs, judges, verifiers, traces, and the top-level functions (5-minute read)
193
+ - [`docs/customer-journeys.md`](./docs/customer-journeys.md): three complete adoption paths with code
194
+ - [`docs/insight-report.md`](./docs/insight-report.md): annotated walkthrough of every section of the `analyzeRuns()` report
195
+ - [`docs/campaign-proposers.md`](./docs/campaign-proposers.md): which proposer to use and when
196
+ - [`docs/adapters-observability.md`](./docs/adapters-observability.md): composing with LangSmith, Langfuse, Phoenix, and OpenLLMetry
197
+ - [`docs/wire-protocol.md`](./docs/wire-protocol.md): the HTTP/RPC contract for other languages
198
+ - [`docs/design.md`](./docs/design.md): how this package relates to the rest of the Tangle agent stack, and the dependency rules that keep it reusable
199
+ - [`CHANGELOG.md`](./CHANGELOG.md): every release, with additive and breaking changes identified
183
200
 
184
201
  ---
185
202
 
186
- ## Hosted tier
203
+ ## Optional hosted tier
187
204
 
188
- Wire your loop to a hosted orchestrator (ours, or your own implementation of the spec) with one config:
205
+ The library is complete without it.
206
+ If you want a dashboard over many loops, point any run at our remote orchestrator or your own implementation of the [open ingest spec](./docs/hosted-ingest-spec.md):
189
207
 
190
208
  ```ts
191
- import { defineAgentEval } from '@tangle-network/agent-eval/contract'
192
-
193
- const evalKit = defineAgentEval({
194
- scenarios,
195
- agent,
196
- judge,
197
- baselineSurface,
198
- })
199
-
200
209
  await evalKit.improve({
201
210
  hostedTenant: {
202
211
  endpoint: 'https://intelligence.tangle.tools',
@@ -206,42 +215,23 @@ await evalKit.improve({
206
215
  })
207
216
  ```
208
217
 
209
- The substrate runs the loop in your process. Only the eval-run events + (optional) trace spans go to the orchestrator. Your scenarios, your judges, your raw data — never sent. Spec at [`docs/hosted-ingest-spec.md`](./docs/hosted-ingest-spec.md); reference receiver at [`examples/hosted-ingest-server/`](./examples/hosted-ingest-server/).
218
+ The loop still runs in your process.
219
+ Hosted ingest sends run identifiers and paths, scenario IDs, candidate surfaces, scores, errors, costs, summaries, and trace attributes.
220
+ Review the [wire format](./docs/hosted-ingest-spec.md) before enabling it for sensitive inputs.
221
+ A reference receiver you can self-host is at [`examples/hosted-ingest-server/`](./examples/hosted-ingest-server/).
210
222
 
211
223
  ---
212
224
 
213
225
  ## Development
214
226
 
215
- Run an example:
216
-
217
- ```sh
218
- pnpm tsx examples/selfimprove-quickstart/index.ts
219
- pnpm tsx examples/customer-feedback-loop/index.ts
220
- pnpm tsx examples/customer-otel-traces/index.ts
221
- ```
222
-
223
- Run the test suite:
224
-
225
227
  ```sh
226
228
  pnpm install
227
229
  pnpm build
228
- pnpm test
230
+ pnpm test # vitest, ~3300 tests
231
+ pnpm typecheck
229
232
  ```
230
233
 
231
- ---
232
-
233
- ## Public API
234
-
235
- The `/contract` surface is the **stability contract**: its barrel freezes the API — a `0.x` minor only *adds*; nothing there changes shape or disappears. Start there for app code.
236
-
237
- | Surface | Meaning |
238
- |---|---|
239
- | `/contract` | Frozen app-facing API. Prefer this first. |
240
- | Named subpaths | Public capability areas such as `/campaign`, `/rl`, `/prm`, `/meta-eval`, `/belief-state`, `/wire`, `/reporting`, `/traces`, and `/analyst`. |
241
- | `/testing` | Test-only helpers. Do not import from production code. |
242
- | Unexported source paths | Not public API. Open an issue if you need one promoted. |
243
-
244
- [`CHANGELOG.md`](./CHANGELOG.md) tracks every release with what's new / additive / breaking.
234
+ Run any example: `pnpm tsx examples/selfimprove-quickstart/index.ts`
245
235
 
246
236
  ---
247
237
 
@@ -224,9 +224,10 @@ declare class ValidationError extends AgentEvalError {
224
224
  }
225
225
 
226
226
  type AgentProfileCellSchemaVersion = 'agent-profile-cell/v1';
227
- type AgentProfileJson = string | number | boolean | null | AgentProfileJson[] | {
227
+ type AgentProfileJsonObject = {
228
228
  [key: string]: AgentProfileJson;
229
229
  };
230
+ type AgentProfileJson = string | number | boolean | null | AgentProfileJson[] | AgentProfileJsonObject;
230
231
  type AgentProfileDimensionValue = string | number | boolean | null;
231
232
  interface AgentProfileSource {
232
233
  /** Runtime/profile contract being fingerprinted, e.g. `agent-interface-profile`. */
@@ -1100,6 +1101,13 @@ interface LlmClientOptions {
1100
1101
  deadlineMs?: number;
1101
1102
  /** Total provider attempts. Legacy option name; default 3 (1 initial + 2 retries). */
1102
1103
  maxRetries?: number;
1104
+ /**
1105
+ * Transport for requests that declare `jsonSchema`. `native` sends
1106
+ * `response_format: json_schema`; `json-object` sends the broadly supported
1107
+ * JSON mode and relies on the caller to include the schema in model-visible
1108
+ * instructions. Default: `native`.
1109
+ */
1110
+ jsonSchemaTransport?: 'native' | 'json-object';
1103
1111
  /** Fetch implementation — defaults to global `fetch`. Override for custom transport (e.g. tests). */
1104
1112
  fetch?: typeof fetch;
1105
1113
  /**
@@ -9,13 +9,13 @@ import {
9
9
  diffFindings,
10
10
  emitSkillUsageFindings,
11
11
  runSemanticConceptJudge
12
- } from "../chunk-G2GPNLSX.js";
12
+ } from "../chunk-WMJR67FX.js";
13
13
  import {
14
14
  behavioralAnalyst,
15
15
  buildDefaultAnalystRegistry,
16
16
  createChatClient,
17
17
  deriveEfficiencyFindings
18
- } from "../chunk-VJ7T5WIO.js";
18
+ } from "../chunk-5YMKIFYP.js";
19
19
  import "../chunk-HHWE3POT.js";
20
20
  import {
21
21
  ANALYST_SEVERITIES,
@@ -71,15 +71,15 @@ import {
71
71
  validatePolicyEdit,
72
72
  validatePolicyEditCandidateRecord,
73
73
  validateUsageSettlementTimeout
74
- } from "../chunk-FC5NDO3E.js";
75
- import "../chunk-NJC7U437.js";
74
+ } from "../chunk-WXQTVEKM.js";
75
+ import "../chunk-J7S4YM27.js";
76
76
  import {
77
77
  CostLedger
78
78
  } from "../chunk-BGVTIE2C.js";
79
79
  import "../chunk-VI2UW6B6.js";
80
80
  import "../chunk-IR3KBHOY.js";
81
81
  import "../chunk-PC4UYEBM.js";
82
- import "../chunk-XJYR7XFV.js";
82
+ import "../chunk-GC4ATIKK.js";
83
83
  import "../chunk-VSMTAMNK.js";
84
84
  import "../chunk-ONWEPEDO.js";
85
85
  import "../chunk-K4DBDHLK.js";
@@ -11,10 +11,11 @@ function escapeRe(s) {
11
11
  function declaredNames(content) {
12
12
  const names = /* @__PURE__ */ new Set();
13
13
  const re = /\b(?:contract|library|interface|abstract\s+contract|class|enum|struct|module|package)\s+([A-Za-z_]\w*)/g;
14
- let m;
15
- while (m = re.exec(content)) {
14
+ let m = re.exec(content);
15
+ while (m) {
16
16
  const name = m[1];
17
17
  if (name && name.length >= 4) names.add(name);
18
+ m = re.exec(content);
18
19
  }
19
20
  return [...names];
20
21
  }