@tangle-network/agent-eval 1.0.17 → 1.0.19

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 (109) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/adapters/http.d.ts +2 -2
  3. package/dist/analyst/index.d.ts +3 -3
  4. package/dist/analyst/index.js +7 -7
  5. package/dist/app-grade/index.d.ts +1042 -0
  6. package/dist/app-grade/index.d.ts.map +1 -0
  7. package/dist/app-grade/index.js +3077 -0
  8. package/dist/app-grade/index.js.map +1 -0
  9. package/dist/{benchmark-command-BRRhjX2k.js → benchmark-command-DSJQeYom.js} +9 -9
  10. package/dist/{benchmark-command-BRRhjX2k.js.map → benchmark-command-DSJQeYom.js.map} +1 -1
  11. package/dist/benchmarks/index.d.ts +4 -4
  12. package/dist/benchmarks/index.js +3 -3
  13. package/dist/campaign/index.d.ts +4 -4
  14. package/dist/campaign/index.js +10 -10
  15. package/dist/{campaign-DzLlT0So.js → campaign-BABipFel.js} +10 -10
  16. package/dist/{campaign-DzLlT0So.js.map → campaign-BABipFel.js.map} +1 -1
  17. package/dist/{campaign-evidence-CcA7a67m.js → campaign-evidence-DONEWvVY.js} +2 -2
  18. package/dist/{campaign-evidence-CcA7a67m.js.map → campaign-evidence-DONEWvVY.js.map} +1 -1
  19. package/dist/cli.js +6 -6
  20. package/dist/contract/index.d.ts +7 -7
  21. package/dist/contract/index.js +6 -6
  22. package/dist/{default-registry-BNv7eB1C.d.ts → default-registry-BbXr18AT.d.ts} +2 -2
  23. package/dist/{default-registry-BNv7eB1C.d.ts.map → default-registry-BbXr18AT.d.ts.map} +1 -1
  24. package/dist/{define-agent-eval-DKVYlm-T.d.ts → define-agent-eval-D_DQMH04.d.ts} +5 -5
  25. package/dist/{define-agent-eval-DKVYlm-T.d.ts.map → define-agent-eval-D_DQMH04.d.ts.map} +1 -1
  26. package/dist/{define-agent-eval-CjF2nTA3.js → define-agent-eval-Q4FdXGj7.js} +5 -5
  27. package/dist/{define-agent-eval-CjF2nTA3.js.map → define-agent-eval-Q4FdXGj7.js.map} +1 -1
  28. package/dist/{dspy-rlm-engine-_MEe2jIO.js → dspy-rlm-engine-CqQc0R2l.js} +2 -2
  29. package/dist/{dspy-rlm-engine-_MEe2jIO.js.map → dspy-rlm-engine-CqQc0R2l.js.map} +1 -1
  30. package/dist/{evaluation-DYAIkPQB.d.ts → evaluation-SDXKL6Fx.d.ts} +2 -2
  31. package/dist/{evaluation-DYAIkPQB.d.ts.map → evaluation-SDXKL6Fx.d.ts.map} +1 -1
  32. package/dist/evaluation.d.ts +1 -1
  33. package/dist/experiment/index.d.ts +1 -1
  34. package/dist/experiment/index.js +1 -1
  35. package/dist/{external-optimizer-process-za-SsIap.js → external-optimizer-process-CfrY-nJT.js} +2 -2
  36. package/dist/{external-optimizer-process-za-SsIap.js.map → external-optimizer-process-CfrY-nJT.js.map} +1 -1
  37. package/dist/{external-optimizer-subprocess-B44kOfZb.js → external-optimizer-subprocess-BtJk8UcU.js} +2 -2
  38. package/dist/{external-optimizer-subprocess-B44kOfZb.js.map → external-optimizer-subprocess-BtJk8UcU.js.map} +1 -1
  39. package/dist/{findings-store-DWiBvJVM.js → findings-store-BnYN57-h.js} +3 -3
  40. package/dist/{findings-store-DWiBvJVM.js.map → findings-store-BnYN57-h.js.map} +1 -1
  41. package/dist/hosted/index.js +1 -1
  42. package/dist/{index-Dt6E4BHX.d.ts → index-AmfM3usI.d.ts} +2 -2
  43. package/dist/{index-Dt6E4BHX.d.ts.map → index-AmfM3usI.d.ts.map} +1 -1
  44. package/dist/{index-BJM4rwP2.d.ts → index-BPmj-_ta.d.ts} +4 -4
  45. package/dist/{index-BJM4rwP2.d.ts.map → index-BPmj-_ta.d.ts.map} +1 -1
  46. package/dist/index-D4lT7aFu.d.ts +3649 -0
  47. package/dist/index-D4lT7aFu.d.ts.map +1 -0
  48. package/dist/{index-chNyKroE.d.ts → index-DLVbfss9.d.ts} +6 -6
  49. package/dist/{index-chNyKroE.d.ts.map → index-DLVbfss9.d.ts.map} +1 -1
  50. package/dist/{index-f09MX_H_.d.ts → index-HuSMvqR2.d.ts} +2 -2
  51. package/dist/{index-f09MX_H_.d.ts.map → index-HuSMvqR2.d.ts.map} +1 -1
  52. package/dist/{index-B1Bm9oJA.d.ts → index-lRKqfyg4.d.ts} +3 -7
  53. package/dist/index-lRKqfyg4.d.ts.map +1 -0
  54. package/dist/index.d.ts +15 -3629
  55. package/dist/index.js +6 -6
  56. package/dist/{journal-C1uLJi0w.js → journal-CTX5uK-n.js} +36 -26
  57. package/dist/journal-CTX5uK-n.js.map +1 -0
  58. package/dist/ledger-core/index.d.ts +1 -1
  59. package/dist/ledger-core/index.js +2 -2
  60. package/dist/{ledger-core-CfT-FzPf.js → ledger-core-24IxvmVY.js} +2 -2
  61. package/dist/{ledger-core-CfT-FzPf.js.map → ledger-core-24IxvmVY.js.map} +1 -1
  62. package/dist/{llm-judge-CHLvjbVR.js → llm-judge-VD_MI6kd.js} +8 -8
  63. package/dist/{llm-judge-CHLvjbVR.js.map → llm-judge-VD_MI6kd.js.map} +1 -1
  64. package/dist/{matrix-Cy3oy7Rq.d.ts → matrix-Yg1lHIGI.d.ts} +2 -2
  65. package/dist/{matrix-Cy3oy7Rq.d.ts.map → matrix-Yg1lHIGI.d.ts.map} +1 -1
  66. package/dist/multishot/golden/index.d.ts +1 -1
  67. package/dist/multishot/index.d.ts +2 -2
  68. package/dist/openapi.json +1 -1
  69. package/dist/{pareto-B34N-vnn.js → pareto-a3EOmjfe.js} +4 -4
  70. package/dist/{pareto-B34N-vnn.js.map → pareto-a3EOmjfe.js.map} +1 -1
  71. package/dist/pipelines/index.d.ts +1 -1
  72. package/dist/{proposal-findings-CZJkS_GY.js → proposal-findings-BfzzZ_lN.js} +2 -2
  73. package/dist/{proposal-findings-CZJkS_GY.js.map → proposal-findings-BfzzZ_lN.js.map} +1 -1
  74. package/dist/rl.d.ts +3 -3
  75. package/dist/rollout/index.d.ts +2 -2
  76. package/dist/search/index.d.ts +2 -2
  77. package/dist/search/index.js +2 -2
  78. package/dist/{search-kernel-kwO-sgCq.d.ts → search-kernel-DjqaPJqL.d.ts} +3 -3
  79. package/dist/{search-kernel-kwO-sgCq.d.ts.map → search-kernel-DjqaPJqL.d.ts.map} +1 -1
  80. package/dist/{search-ledger-CV_bQQed.js → search-ledger-Dk-yk5Li.js} +4 -4
  81. package/dist/{search-ledger-CV_bQQed.js.map → search-ledger-Dk-yk5Li.js.map} +1 -1
  82. package/dist/{search-ledger-file-DTBD6X-y.js → search-ledger-file-BhOdgYIt.js} +3 -3
  83. package/dist/{search-ledger-file-DTBD6X-y.js.map → search-ledger-file-BhOdgYIt.js.map} +1 -1
  84. package/dist/{search-shipper-CA3ooBbC.js → search-shipper-Dm-_Dtvy.js} +3 -3
  85. package/dist/{search-shipper-CA3ooBbC.js.map → search-shipper-Dm-_Dtvy.js.map} +1 -1
  86. package/dist/{search-summary-DBMjdecs.js → search-summary-RJ5zXjHN.js} +3 -3
  87. package/dist/{search-summary-DBMjdecs.js.map → search-summary-RJ5zXjHN.js.map} +1 -1
  88. package/dist/{single-run-lock-BGlW3OSk.js → single-run-lock-CUj2tjxT.js} +2 -2
  89. package/dist/{single-run-lock-BGlW3OSk.js.map → single-run-lock-CUj2tjxT.js.map} +1 -1
  90. package/dist/{skillopt-optimization-method-D72XZZfR.js → skillopt-optimization-method-D9Hlmhjg.js} +8 -8
  91. package/dist/{skillopt-optimization-method-D72XZZfR.js.map → skillopt-optimization-method-D9Hlmhjg.js.map} +1 -1
  92. package/dist/{statistical-heldout-_dXihc9T.d.ts → statistical-heldout-C-tgpUuu.d.ts} +2 -2
  93. package/dist/{statistical-heldout-_dXihc9T.d.ts.map → statistical-heldout-C-tgpUuu.d.ts.map} +1 -1
  94. package/dist/{systemone-CBvL1gx-.d.ts → systemone-CpDppX8s.d.ts} +3 -3
  95. package/dist/{systemone-CBvL1gx-.d.ts.map → systemone-CpDppX8s.d.ts.map} +1 -1
  96. package/dist/systemone.d.ts +2 -2
  97. package/dist/traces.d.ts +1 -1
  98. package/dist/{tree-DPwgM2LX.js → tree-DIHp6UQ0.js} +3 -3
  99. package/dist/{tree-DPwgM2LX.js.map → tree-DIHp6UQ0.js.map} +1 -1
  100. package/dist/{types-D91tnhzf.d.ts → types-DsuOcYsE.d.ts} +2 -2
  101. package/dist/{types-D91tnhzf.d.ts.map → types-DsuOcYsE.d.ts.map} +1 -1
  102. package/dist/{types-CBkDo7ja.d.ts → types-wDBk14lj.d.ts} +2 -2
  103. package/dist/{types-CBkDo7ja.d.ts.map → types-wDBk14lj.d.ts.map} +1 -1
  104. package/dist/wire/index.d.ts +2 -2
  105. package/docs/app-grade.md +79 -0
  106. package/package.json +15 -1
  107. package/dist/index-B1Bm9oJA.d.ts.map +0 -1
  108. package/dist/index.d.ts.map +0 -1
  109. package/dist/journal-C1uLJi0w.js.map +0 -1
@@ -0,0 +1,3649 @@
1
+ import { f as CostReceiptInput, h as MaximumCharge, m as CustomTokenPricing, o as CostLedgerHandle, p as CostUsage } from "./cost-ledger-Czro9wmo.js";
2
+ import { c as ValidationError, t as AgentEvalError } from "./errors-E9I2hldP.js";
3
+ import { l as AgentProfileJson, r as AgentProfileCell } from "./agent-profile-cell-3kYxUsK2.js";
4
+ import { h as RunStatus, o as FailureClass, r as BudgetSpec } from "./schema-CkIvnmLU.js";
5
+ import { a as RunRecord, c as RunSplitTag } from "./run-record-D2BFMPGl.js";
6
+ import { i as AnalystFinding, m as EvidenceRef } from "./types-B9s9Xo5_.js";
7
+ import { H as LlmUsage, M as LlmCallRequest, N as LlmCallResult, P as LlmChargeBounds, c as JudgeFn, f as ProductClientConfig, nt as ServedModelCheck, x as ChatClient } from "./types-0KglnQ9o.js";
8
+ import "./statistical-heldout-C-tgpUuu.js";
9
+ import { l as PairedBootstrapOptions, o as PairedPromotionDecisionOptions, t as PairedDecisionMethod, u as PairedBootstrapResult } from "./paired-promotion-decision-BDnqepZ1.js";
10
+ import "./promotion-gate-DOkgJcLW.js";
11
+ import "./index-BPmj-_ta.js";
12
+ import { S as JudgeConfig, w as JudgeScore } from "./types-wDBk14lj.js";
13
+ import { s as TraceStore } from "./store-CMs5wGXP.js";
14
+ import { t as AgentProfile } from "./agent-profile-CPv4tENR.js";
15
+ import { r as DatasetScenario, t as Dataset } from "./dataset-DThC1S6G.js";
16
+ import { t as DefaultVerdict } from "./verdict-D_qiBQ6_.js";
17
+ import { n as Layer } from "./multi-layer-verifier-lM4aVN78.js";
18
+ import "./summary-report-0La5vejY.js";
19
+ import "./analyze-runs-pJ4fhnYg.js";
20
+ import { o as TraceEmitter } from "./emitter-mAXC5PSN.js";
21
+ import { t as HarnessAssertion } from "./sandbox-harness-K3LuyORm.js";
22
+ import "./define-agent-eval-D_DQMH04.js";
23
+ import "./default-registry-BbXr18AT.js";
24
+ import "./exact-types-CaLLU1Pz.js";
25
+ import "./systemone-CpDppX8s.js";
26
+ import "./engine-rfV8Ff8V.js";
27
+ import "./registry-Ck7w-rr5.js";
28
+ import "./index-AmfM3usI.js";
29
+ import { d as Trajectory, f as TrajectoryStep } from "./tool-waste-CUASoTtk.js";
30
+ import "./types-DsuOcYsE.js";
31
+ import { B as ControlRuntimeConfig, H as ControlSeverity, R as ControlEvalResult, a as FeedbackLabel, v as ProposedSideEffect, z as ControlRunResult } from "./feedback-trajectory-P37ju-gW.js";
32
+ import "./store-tool-spans-BgwVHXma.js";
33
+ import "./researcher-CLnNJ7Or.js";
34
+ import "./index-HuSMvqR2.js";
35
+ import { SpanKind } from "@tangle-network/agent-trace-contract";
36
+ declare class CostCeilingReachedError extends ValidationError {
37
+ constructor(ceilingUsd: number, committedUsd: number, requestedUsd: number, phase: string, actor: string);
38
+ }
39
+ declare class CostAccountingIncompleteError extends ValidationError {}
40
+ interface CostResult {
41
+ costUsd: number;
42
+ costUnknown: boolean;
43
+ }
44
+ declare function costForUsage(model: string, usage: CostUsage): CostResult;
45
+ /** Price token counts with caller-supplied per-million rates. */
46
+ declare function costForTokenPricing(pricing: CustomTokenPricing, usage: Pick<CostUsage, 'inputTokens' | 'outputTokens' | 'cachedTokens' | 'cacheWriteTokens'>): number;
47
+ declare function maximumChargeForLlmRequest(request: Pick<LlmCallRequest, 'model' | 'messages' | 'jsonSchema' | 'tools' | 'toolChoice' | 'maxTokens' | 'thinking'>, options?: LlmChargeBounds): MaximumCharge | undefined;
48
+ /** Convert a provider result into the canonical paid-call receipt input.
49
+ * The receipt is JSON-clean: absent optional fields are omitted, never carried
50
+ * as explicit-undefined keys. Receipts cross JSON boundaries (the external
51
+ * optimizer's loopback proxy validates them with `assertJsonValue`), where an
52
+ * explicit-undefined value is rejected as non-serializable. */
53
+ declare function costReceiptFromLlm(result: LlmCallResult, customTokenPricing?: CustomTokenPricing): CostReceiptInput;
54
+ /** Structured-response failures retain their completed provider receipt. */
55
+ declare function costReceiptFromLlmError(error: Error, customTokenPricing?: CustomTokenPricing): CostReceiptInput | undefined;
56
+ declare class LlmCallError extends AgentEvalError {
57
+ readonly status: number;
58
+ readonly body: string;
59
+ readonly model: string;
60
+ constructor(message: string, status: number, body: string, model: string);
61
+ }
62
+ /** A provider response completed and incurred measurable usage, but its content
63
+ * could not satisfy the caller's response contract. The response envelope is
64
+ * retained so accounting can commit the receipt before the error propagates. */
65
+ declare class LlmResponseError extends AgentEvalError {
66
+ readonly result: LlmCallResult;
67
+ constructor(message: string, result: LlmCallResult, options?: {
68
+ cause?: unknown;
69
+ });
70
+ }
71
+ /**
72
+ * Strip a ```json / ``` code fence if the model emitted one.
73
+ * Idempotent for naked JSON. Some models (claude-code via router, certain
74
+ * deepseek models) wrap output even under json_object.
75
+ */
76
+ declare function stripFencedJson(raw: string): string;
77
+ /**
78
+ * True when an error is a transient transport/network fault worth retrying,
79
+ * as opposed to a deterministic failure (4xx schema reject, JSON parse) that
80
+ * a retry cannot fix. Inspects `LlmCallError.status`, then the error's
81
+ * name/message/code, then recurses into `error.cause` — undici nests the
82
+ * real socket fault one or more levels under `.cause`.
83
+ *
84
+ * This is the retry classifier for the package: `callLlm` and
85
+ * `withJudgeRetry` both route through it, so connection failures are treated
86
+ * consistently across transports.
87
+ */
88
+ declare function isTransientLlmError(err: unknown): boolean;
89
+ /** Mann–Whitney ROC area: ties count half; no observed member of either class means unknown. */
90
+ declare function rocAuc(scores: number[], labels: number[]): number | null;
91
+ /**
92
+ * Nearest-rank distribution summary for measurement paths (gates, dashboards,
93
+ * load harnesses): min/p50/p90/p95/p99/max + mean + sample stddev + a 95%
94
+ * Student-t CI on the mean. Consolidates the former @repo/shared
95
+ * distribution-stats: identical nearest-rank percentiles, with the CI now the
96
+ * exact t critical value for any df (the retired implementation capped its
97
+ * table at df 30 and fell back to 1.96).
98
+ */
99
+ interface SampleDistribution {
100
+ min: number;
101
+ p50: number;
102
+ p90: number;
103
+ p95: number;
104
+ p99: number;
105
+ max: number;
106
+ mean: number;
107
+ stddev?: number;
108
+ ci95Low?: number;
109
+ ci95High?: number;
110
+ n: number;
111
+ values: number[];
112
+ }
113
+ interface DistributionSummaryOptions {
114
+ lowerBound?: number;
115
+ }
116
+ /**
117
+ * Nearest-rank percentile on an arbitrary sample, `percentile` on a 0-100
118
+ * scale — the same rule `summarizeDistribution` applies, exposed for a single
119
+ * percentile the summary struct does not carry (e.g. p99.9). Returns NaN for
120
+ * an empty sample; callers that need a numeric floor guard it.
121
+ */
122
+ declare function percentileNearestRank(values: number[], percentile: number): number;
123
+ declare function summarizeDistribution(values: number[], options?: DistributionSummaryOptions): SampleDistribution;
124
+ /** Standardized effect sizes; undefined effects remain null. */
125
+ /** Independent Cohen's d, oriented b − a; null below two samples or for an unbounded effect. */
126
+ declare function cohensD(a: number[], b: number[]): number | null;
127
+ /** Paired Cohen's dz; constant nonzero deltas have no finite standardized effect. */
128
+ declare function pairedCohensDz(before: number[], after: number[]): number | null;
129
+ type CliffsMagnitude = 'negligible' | 'small' | 'medium' | 'large';
130
+ /** Cliff's delta, oriented after − before; empty samples return zero. */
131
+ declare function cliffsDelta(before: number[], after: number[]): number;
132
+ /** Romano magnitude thresholds for the absolute Cliff's delta. */
133
+ declare function interpretCliffs(delta: number): CliffsMagnitude;
134
+ /** Two-sided normal tail, evaluated without subtracting a near-one CDF. */
135
+ declare function normalTwoSidedPValue(z: number): number;
136
+ /** Two-sided Student-t tail, preserving small tail probabilities. */
137
+ declare function studentTTwoSidedPValue(t: number, df: number): number;
138
+ /** Pearson statistic for a 2x2 table; empty or fixed margins have statistic zero. */
139
+ declare function chiSquared(a: number, b: number, c: number, d: number): number;
140
+ /** One-degree-of-freedom chi-squared survival probability. */
141
+ declare function chiSquaredPValue(statistic: number): number;
142
+ /** Fisher two-sided probability: sum all fixed-margin tables no likelier than the observed table. */
143
+ declare function fisherExactTwoSided(a: number, b: number, c: number, d: number): number;
144
+ /** Fisher when any expected count is below five; otherwise Pearson's chi-squared tail. */
145
+ declare function twoByTwoPValue(a: number, b: number, c: number, d: number): number;
146
+ interface LinearRegressionResult {
147
+ slope: number;
148
+ rSquared: number;
149
+ slopePValue: number | null;
150
+ }
151
+ /** Centered least-squares slope; null p-value means no slope test was possible. */
152
+ declare function linearRegressionSlope(points: readonly {
153
+ x: number;
154
+ y: number;
155
+ }[]): LinearRegressionResult;
156
+ interface SampleSummaryOptions {
157
+ confidence?: number;
158
+ seed?: number;
159
+ resamples?: number;
160
+ }
161
+ interface SampleSummary {
162
+ n: number;
163
+ median: number;
164
+ mean: number;
165
+ stddev: number;
166
+ q1: number;
167
+ q3: number;
168
+ iqr: number;
169
+ min: number;
170
+ max: number;
171
+ range: number;
172
+ cv: number | null;
173
+ skewness: number | null;
174
+ kurtosis: number | null;
175
+ mad: number;
176
+ outliers: number[];
177
+ medianInterval: {
178
+ low: number;
179
+ high: number;
180
+ confidence: number;
181
+ resamples: number;
182
+ } | null;
183
+ }
184
+ /** Interpolated sample median; an empty population has no measured median. */
185
+ declare function sampleMedian(values: readonly number[]): number | null;
186
+ /** Descriptive facts, not stability policy or a significance test. */
187
+ declare function summarizeSample(values: readonly number[], options?: SampleSummaryOptions): SampleSummary | null;
188
+ type PolicyEditSchemaVersion = 'policy-edit/v1';
189
+ declare const POLICY_EDIT_AXES: readonly ["carrier", "representation", "budget", "sampling", "output_contract", "tool_contract", "routing", "memory", "agent_profile", "deployment_target"];
190
+ type PolicyEditAxis = (typeof POLICY_EDIT_AXES)[number];
191
+ declare const POLICY_EDIT_TARGET_SURFACES: readonly ["prompt", "tool-contract", "runtime-config", "memory", "agent-profile", "code", "deployment"];
192
+ type PolicyEditTargetSurface = (typeof POLICY_EDIT_TARGET_SURFACES)[number];
193
+ type PolicyEditRisk = 'low' | 'medium' | 'high' | 'unknown';
194
+ type PolicyEditGainDirection = 'increase' | 'decrease';
195
+ type PolicyEditGainUnit = 'absolute' | 'relative' | 'percent' | 'score';
196
+ interface PolicyEditTarget {
197
+ surface: PolicyEditTargetSurface;
198
+ /** Stable path inside the target surface, for example `system-prompt:tools`
199
+ * or `budget.maxTurns`. */
200
+ path?: string;
201
+ /** Optional canonical deployment identity. Store the existing cell, not a
202
+ * local profile shape. */
203
+ agentProfileCell?: AgentProfileCell;
204
+ /** Human label when the path is not enough for a readable audit trail. */
205
+ label?: string;
206
+ }
207
+ type PolicyEditChange = {
208
+ kind: 'text';
209
+ mode: 'append' | 'prepend' | 'replace';
210
+ value: string;
211
+ /** Required when `mode === 'replace'`; exact match only. */
212
+ find?: string;
213
+ } | {
214
+ kind: 'json';
215
+ mode: 'set' | 'merge' | 'remove';
216
+ path: string;
217
+ value?: AgentProfileJson;
218
+ };
219
+ interface PolicyEditExpectedGain {
220
+ /** Metric this edit is expected to move, e.g. `holdout.composite`. */
221
+ metric: string;
222
+ direction: PolicyEditGainDirection;
223
+ /** Positive magnitude in the metric's native units. */
224
+ amount: number;
225
+ unit?: PolicyEditGainUnit;
226
+ rationale?: string;
227
+ }
228
+ interface PolicyEditSource {
229
+ findingIds: string[];
230
+ analystIds: string[];
231
+ evidenceRefs: EvidenceRef[];
232
+ /** Mirrors `AnalystFinding.derived_from_judge`; admission rejects it. */
233
+ derivedFromJudge?: boolean;
234
+ }
235
+ interface PolicyEdit {
236
+ schemaVersion: PolicyEditSchemaVersion;
237
+ editId: string;
238
+ axis: PolicyEditAxis;
239
+ target: PolicyEditTarget;
240
+ change: PolicyEditChange;
241
+ claim: string;
242
+ expectedGain: PolicyEditExpectedGain;
243
+ confidence: number;
244
+ risk: PolicyEditRisk;
245
+ source: PolicyEditSource;
246
+ rationale?: string;
247
+ validationPlan?: string;
248
+ metadata?: Record<string, unknown>;
249
+ }
250
+ declare const POLICY_EDIT_CANDIDATE_RECORD_SCHEMA: "tangle.policy-edit-candidate.v1";
251
+ /** JSON-safe attribution carried with a measured candidate and its scores. */
252
+ interface PolicyEditCandidateRecord {
253
+ schema: typeof POLICY_EDIT_CANDIDATE_RECORD_SCHEMA;
254
+ policyEdit: PolicyEdit;
255
+ }
256
+ type PolicyEditInit = Omit<PolicyEdit, 'schemaVersion' | 'editId'> & {
257
+ schemaVersion?: PolicyEditSchemaVersion;
258
+ editId?: string;
259
+ };
260
+ interface FindingToPolicyEditOptions {
261
+ expectedGain?: PolicyEditExpectedGain | ((finding: AnalystFinding) => PolicyEditExpectedGain | null | undefined);
262
+ risk?: PolicyEditRisk | ((finding: AnalystFinding) => PolicyEditRisk);
263
+ defaultAxis?: PolicyEditAxis;
264
+ defaultTargetSurface?: PolicyEditTargetSurface;
265
+ }
266
+ interface PolicyEditAdmissionOptions {
267
+ minScore?: number;
268
+ minExpectedGain?: number;
269
+ allowHighRisk?: boolean;
270
+ requireEvidence?: boolean;
271
+ }
272
+ interface PolicyEditAdmission {
273
+ edit: PolicyEdit;
274
+ decision: 'admit' | 'reject';
275
+ score: number;
276
+ reasons: string[];
277
+ }
278
+ declare function makePolicyEditCandidateRecord(edit: PolicyEdit): PolicyEditCandidateRecord;
279
+ /**
280
+ * Capability-headroom gate — "can this task set even SEE the capability
281
+ * you're about to A/B?"
282
+ *
283
+ * A capability A/B (grant one arm a tool, a skill, extra context — anything)
284
+ * can only detect the capability on tasks the capability-ABSENT baseline
285
+ * FAILS. A task the baseline already passes is saturated: the treatment arm
286
+ * has no headroom to demonstrate anything, and averaging saturated tasks into
287
+ * the comparison dilutes a real effect toward a false null. Calibrate before
288
+ * measuring: run the baseline arm first, classify every task's headroom, and
289
+ * refuse to run the comparison when no task has a gap.
290
+ *
291
+ * Fail-closed on unknown outcomes: a baseline run whose outcome could not be
292
+ * determined (infra loss, unverified, missing) NEVER counts as a failure —
293
+ * treating "couldn't tell" as "failed" would manufacture headroom out of
294
+ * telemetry gaps and launder infra noise into a treatment effect.
295
+ *
296
+ * Domain-free by construction: the capability under test never appears here.
297
+ * Callers decide what the baseline arm lacks; this module only classifies
298
+ * baseline outcomes per task.
299
+ */
300
+ /** One baseline-arm (capability-absent) observation of one task. */
301
+ interface HeadroomInput {
302
+ /** Stable task identity; multiple rows per task are multiple baseline reps. */
303
+ taskId: string;
304
+ /** Baseline outcome. 'unknown' = the outcome could not be determined —
305
+ * distinct from 'fail' and never counted as one. */
306
+ baselineOutcome: 'pass' | 'fail' | 'unknown';
307
+ }
308
+ /** Headroom classification for one task.
309
+ * - 'gap': the baseline fails it — the comparison can see the capability here.
310
+ * - 'saturated': the baseline already passes it — no headroom.
311
+ * - 'unknown': every baseline outcome is unknown — fail-closed, not a gap. */
312
+ type HeadroomClass = 'gap' | 'saturated' | 'unknown';
313
+ interface TaskHeadroom {
314
+ taskId: string;
315
+ /** Total baseline observations for this task (known + unknown). */
316
+ n: number;
317
+ /** Observations with a known ('pass' | 'fail') outcome — the evidence the
318
+ * classification actually rests on. A large `n` with a small `nKnown`
319
+ * means the headroom verdict is thin, not well-replicated. */
320
+ nKnown: number;
321
+ /** Pass rate over KNOWN outcomes only; NaN when every outcome is unknown
322
+ * (no data ≠ measured zero). */
323
+ baselinePassRate: number;
324
+ headroom: HeadroomClass;
325
+ }
326
+ interface CapabilityHeadroomOptions {
327
+ /** A task counts as 'gap' when its baseline pass rate is ≤ this. Default 0
328
+ * (the baseline must fail every known rep). Must be in [0, 1) — a
329
+ * threshold of 1 would classify a fully-saturated task as a gap. */
330
+ keepThreshold?: number;
331
+ }
332
+ interface CapabilityHeadroomResult {
333
+ /** Per-task classification, sorted by `taskId`. */
334
+ tasks: TaskHeadroom[];
335
+ summary: {
336
+ tasksWithGap: number;
337
+ tasksSaturated: number;
338
+ tasksUnknown: number;
339
+ /** Total unknown-outcome reps across ALL tasks (including tasks that
340
+ * still classified via their known reps) — the size of the telemetry
341
+ * hole behind the classifications. */
342
+ repsUnknown: number;
343
+ };
344
+ }
345
+ /**
346
+ * Classify every task's baseline headroom from capability-absent baseline
347
+ * outcomes.
348
+ *
349
+ * A task is 'gap' when it has ≥ 1 known outcome and its pass rate over known
350
+ * outcomes is ≤ `keepThreshold`; 'saturated' when the rate exceeds the
351
+ * threshold; 'unknown' when every outcome is unknown. Unknown reps never
352
+ * count toward the rate in either direction (fail-closed — see the module
353
+ * doc), and evidence strength stays visible: each task reports `nKnown`
354
+ * alongside `n`, and the summary totals the unknown reps in `repsUnknown`.
355
+ * Throws on empty input, an out-of-range `keepThreshold`, or an
356
+ * unrecognized outcome value (rows are often projected from untyped records).
357
+ */
358
+ declare function capabilityHeadroom(rows: readonly HeadroomInput[], opts?: CapabilityHeadroomOptions): CapabilityHeadroomResult;
359
+ interface AssertCapabilityHeadroomOptions {
360
+ /** Minimum tasks classified 'gap' for the comparison to proceed. Default 1. */
361
+ minTasksWithGap?: number;
362
+ }
363
+ /**
364
+ * The go/no-go guard: throws when fewer than `minTasksWithGap` tasks have
365
+ * baseline headroom — i.e. the benchmark cannot see the capability, so any
366
+ * comparison run on it would measure noise. Call between the baseline
367
+ * calibration run and the treatment run.
368
+ */
369
+ declare function assertCapabilityHeadroom(result: CapabilityHeadroomResult, opts?: AssertCapabilityHeadroomOptions): void;
370
+ /**
371
+ * Cost tracker — token + USD accounting per scenario and per run.
372
+ *
373
+ * Adapted from generic usage-event accounting. Every
374
+ * optimizer needs to know "is the quality gain worth the cost delta?",
375
+ * and every dashboard needs dollars-per-completed-task. MODEL_PRICING
376
+ * from metrics.ts stays authoritative for estimate math; this module
377
+ * adds the aggregation + per-scenario roll-up that was duplicated
378
+ * across 4 verticals.
379
+ */
380
+ interface TokenSpec {
381
+ inputTokens: number;
382
+ outputTokens: number;
383
+ cachedTokens?: number;
384
+ reasoningTokens?: number;
385
+ }
386
+ interface CostEntry extends TokenSpec {
387
+ scenarioId: string;
388
+ model: string;
389
+ /** Override estimate with an observed cost (e.g. from provider response). */
390
+ actualCostUsd?: number;
391
+ timestamp: number;
392
+ /** Free-form tags (variant id, round #, etc.). */
393
+ tags?: Record<string, string>;
394
+ }
395
+ interface ScenarioCost {
396
+ scenarioId: string;
397
+ entries: CostEntry[];
398
+ totalInputTokens: number;
399
+ totalOutputTokens: number;
400
+ totalCachedTokens: number;
401
+ totalCostUsd: number;
402
+ /** Pass flag — set by consumer via markOutcome; used for cost-per-completed-task. */
403
+ completed?: boolean;
404
+ }
405
+ declare class CostTracker {
406
+ private byScenario;
407
+ record(entry: Omit<CostEntry, 'timestamp'> & {
408
+ timestamp?: number;
409
+ }): CostEntry;
410
+ markOutcome(scenarioId: string, completed: boolean): void;
411
+ /**
412
+ * Convenience: record + markOutcome in one call from a
413
+ * `{ usage, verdict }`-shaped response (starter-foundry's
414
+ * `invokeMetaJudge` returns this shape; consumers that wrap any
415
+ * judge/critic can follow the same convention).
416
+ *
417
+ * `usage.model` must be present in `MODEL_PRICING` for cost math to
418
+ * populate; otherwise totalCostUsd stays at 0 for the entry but
419
+ * tokens still aggregate.
420
+ */
421
+ recordVerdict(verdict: {
422
+ usage?: {
423
+ inputTokens: number;
424
+ outputTokens: number;
425
+ model: string;
426
+ cachedTokens?: number;
427
+ reasoningTokens?: number;
428
+ };
429
+ verdict?: 'pass' | 'fail' | 'borderline' | string;
430
+ }, scenarioId: string, tags?: Record<string, string>): CostEntry | null;
431
+ get(scenarioId: string): ScenarioCost | undefined;
432
+ list(): ScenarioCost[];
433
+ summary(): CostSummary;
434
+ }
435
+ interface CostSummary {
436
+ scenarioCount: number;
437
+ completedCount: number;
438
+ totalInputTokens: number;
439
+ totalOutputTokens: number;
440
+ totalCostUsd: number;
441
+ avgCostPerScenarioUsd: number;
442
+ /** Total USD / completed scenarios — null when nothing completed. */
443
+ costPerCompletedTaskUsd: number | null;
444
+ }
445
+ type DetectorSeverity = 'info' | 'warn' | 'critical';
446
+ /** A normalized step a detector folds over. Fields are optional; each detector reads only what it
447
+ * needs (repeated-action → `actionFingerprint`; no-progress → `stateFingerprint` + `score`;
448
+ * error-streak → `status`). */
449
+ interface DetectorEvent {
450
+ /** Fingerprint of the action/tool-call this step took (caller pre-hashes). */
451
+ readonly actionFingerprint?: string;
452
+ /** Fingerprint of observable state AFTER this step. */
453
+ readonly stateFingerprint?: string;
454
+ /** Score after this step (the score-flat half of no-progress). */
455
+ readonly score?: number;
456
+ /** Whether this step errored. */
457
+ readonly status?: 'ok' | 'error';
458
+ /** Free-form label carried into the signal evidence (e.g. the tool name). */
459
+ readonly label?: string;
460
+ }
461
+ interface DetectorSignal {
462
+ readonly detector: string;
463
+ readonly severity: DetectorSeverity;
464
+ readonly failureClass?: FailureClass;
465
+ readonly reason: string;
466
+ /** Consecutive matching steps at the moment the signal fired. */
467
+ readonly streak: number;
468
+ readonly evidence?: Record<string, unknown>;
469
+ }
470
+ interface StreamingDetector {
471
+ readonly id: string;
472
+ /** Current streak (consecutive matching steps) — for telemetry/introspection between observes. */
473
+ readonly streak: number;
474
+ /** Fold one event; return a signal when the threshold trips, else null. */
475
+ observe(event: DetectorEvent): DetectorSignal | null;
476
+ /** Clear all state. */
477
+ reset(): void;
478
+ }
479
+ interface RepeatedActionOptions {
480
+ /** Signal once the same action fingerprint repeats this many CONSECUTIVE steps (default 3).
481
+ * `<= 0` disables the signal (the streak is still tracked, for telemetry). */
482
+ readonly maxRepeated?: number;
483
+ readonly severity?: DetectorSeverity;
484
+ /** Failure class to stamp on the signal (default `tool_recovery_failure`). */
485
+ readonly failureClass?: FailureClass;
486
+ }
487
+ /** Same action fingerprint N consecutive steps = a stuck loop (the #1 long-horizon failure mode). */
488
+ declare function repeatedActionDetector(opts?: RepeatedActionOptions): StreamingDetector;
489
+ interface ErrorStreakOptions {
490
+ /** Signal once this many CONSECUTIVE steps errored (default 3). `<= 0` disables. */
491
+ readonly maxErrors?: number;
492
+ readonly severity?: DetectorSeverity;
493
+ readonly failureClass?: FailureClass;
494
+ }
495
+ /** N consecutive tool errors = the worker is hammering a broken approach. */
496
+ declare function errorStreakDetector(opts?: ErrorStreakOptions): StreamingDetector;
497
+ /** Fold one event through many detectors at once; returns every signal that fired this step. The
498
+ * natural shape for an online pipe watching with a whole panel of detectors. */
499
+ declare function observeAll(detectors: ReadonlyArray<StreamingDetector>, event: DetectorEvent): DetectorSignal[];
500
+ /**
501
+ * Toolchain error-count extractor.
502
+ *
503
+ * Given stderr/stdout from a compiler or test runner, count the number
504
+ * of reported errors/failures. Patterns are deliberately narrow —
505
+ * unknown stderr returns `null` rather than zero so callers can
506
+ * distinguish "no errors" from "different toolchain, couldn't parse".
507
+ *
508
+ * All patterns are anchored to the start of a line and use bounded
509
+ * character classes to avoid catastrophic backtracking on pathological
510
+ * inputs.
511
+ *
512
+ * Add new toolchains by appending to {@link ERROR_COUNT_PATTERNS};
513
+ * order matters only in the sense that the first matching pattern wins.
514
+ */
515
+ interface ErrorCountPattern {
516
+ /** Stable identifier for logging + tests. */
517
+ name: string;
518
+ /** Must be global (`g` flag) — the extractor counts matches. */
519
+ regex: RegExp;
520
+ /** Optional post-processing to extract a count from a single captured match. */
521
+ transform?: (match: RegExpMatchArray) => number;
522
+ }
523
+ declare const ERROR_COUNT_PATTERNS: ErrorCountPattern[];
524
+ interface ExtractOptions {
525
+ /** Restrict to named patterns — default: all patterns. */
526
+ only?: string[];
527
+ /** Additional patterns to consider BEFORE the built-in list. */
528
+ extra?: ErrorCountPattern[];
529
+ }
530
+ interface ExtractResult {
531
+ /** Total count of matched errors, or null when no pattern matched. */
532
+ count: number | null;
533
+ /** Name of the pattern that matched, or null. */
534
+ matched: string | null;
535
+ /** Original matches for callers that want to surface specifics. */
536
+ samples: string[];
537
+ }
538
+ /**
539
+ * Try each pattern in order; return the first with matches.
540
+ *
541
+ * Returning `null` (instead of zero) on no-match is deliberate — a
542
+ * callsite that greps for "typescript errors" on cargo output should
543
+ * NOT treat that as "zero TS errors" because the toolchain is wrong.
544
+ */
545
+ declare function extractErrorCount(text: string, opts?: ExtractOptions): ExtractResult;
546
+ type KnowledgeRequirementCategory = 'user_specific' | 'company_specific' | 'domain_specific' | 'codebase_specific' | 'market_specific' | 'regulatory' | 'tool_api' | 'credential_or_secret' | 'runtime_environment' | 'preference' | 'historical_context';
547
+ type KnowledgeAcquisitionMode = 'ask_user' | 'search_web' | 'query_connector' | 'inspect_repo' | 'run_command' | 'infer_low_confidence' | 'not_available';
548
+ type KnowledgeImportance = 'blocking' | 'high' | 'medium' | 'low';
549
+ type KnowledgeFreshness = 'static' | 'monthly' | 'weekly' | 'daily' | 'realtime';
550
+ type KnowledgeSensitivity = 'public' | 'private' | 'secret';
551
+ type KnowledgeFallbackPolicy = 'block' | 'ask' | 'continue_with_caveat' | 'use_default';
552
+ interface KnowledgeRequirement {
553
+ id: string;
554
+ description: string;
555
+ requiredFor: string[];
556
+ category: KnowledgeRequirementCategory;
557
+ acquisitionMode: KnowledgeAcquisitionMode;
558
+ importance: KnowledgeImportance;
559
+ freshness: KnowledgeFreshness;
560
+ sensitivity: KnowledgeSensitivity;
561
+ confidenceNeeded: number;
562
+ currentConfidence: number;
563
+ evidenceIds: string[];
564
+ fallbackPolicy: KnowledgeFallbackPolicy;
565
+ /**
566
+ * ISO timestamp after which this requirement must be treated as stale.
567
+ * Stale requirements score as missing even when they still have evidence.
568
+ */
569
+ validUntil?: string;
570
+ /** ISO timestamp for the last source-grounding or human verification pass. */
571
+ lastVerifiedAt?: string;
572
+ metadata?: Record<string, unknown>;
573
+ }
574
+ interface KnowledgeBundle {
575
+ taskId: string;
576
+ requirements: KnowledgeRequirement[];
577
+ evidenceIds: string[];
578
+ claimIds: string[];
579
+ wikiPageIds: string[];
580
+ userAnswers: Record<string, string>;
581
+ missing: KnowledgeRequirement[];
582
+ readinessScore: number;
583
+ metadata?: Record<string, unknown>;
584
+ }
585
+ type KnowledgeRecommendedAction = 'run_agent' | 'ask_user' | 'collect_web_data' | 'query_connectors' | 'inspect_repo' | 'build_domain_wiki' | 'continue_with_caveat' | 'abort_or_rescope';
586
+ interface KnowledgeReadinessReport {
587
+ taskId: string;
588
+ readinessScore: number;
589
+ blockingMissingRequirements: KnowledgeRequirement[];
590
+ nonBlockingGaps: KnowledgeRequirement[];
591
+ recommendedAction: KnowledgeRecommendedAction;
592
+ bundle: KnowledgeBundle;
593
+ severity: ControlSeverity;
594
+ reason: string;
595
+ }
596
+ interface UserQuestion {
597
+ id: string;
598
+ question: string;
599
+ reason: string;
600
+ requirementId: string;
601
+ importance: KnowledgeImportance;
602
+ answerType: 'free_text' | 'select_one' | 'multi_select' | 'file_upload' | 'credential' | 'url';
603
+ defaultIfSkipped?: string;
604
+ impactIfUnknown: string;
605
+ options?: string[];
606
+ metadata?: Record<string, unknown>;
607
+ }
608
+ interface DataAcquisitionPlan {
609
+ id: string;
610
+ requirementIds: string[];
611
+ mode: Exclude<KnowledgeAcquisitionMode, 'not_available' | 'infer_low_confidence'> | 'build_domain_wiki';
612
+ description: string;
613
+ priority: KnowledgeImportance;
614
+ expectedEvidenceIds?: string[];
615
+ questions?: UserQuestion[];
616
+ metadata?: Record<string, unknown>;
617
+ }
618
+ interface LeaderboardRow {
619
+ /** Group key — the agent-profile cellId by default, else a caller dimension. */
620
+ key: string;
621
+ /** Display label: `harness · model` when the profile carries them, else the key. */
622
+ label: string;
623
+ harness?: string;
624
+ model?: string;
625
+ rank: number;
626
+ /** Measured runs in this group. */
627
+ n: number;
628
+ passRate: number;
629
+ passRateCi95: [number, number];
630
+ /** Observability rollup — means over the group; null when no run reported it. */
631
+ meanCostUsd: number | null;
632
+ meanTokensIn: number | null;
633
+ meanTokensOut: number | null;
634
+ meanWallMs: number | null;
635
+ }
636
+ interface LeaderboardOptions {
637
+ /** Per-run pass predicate — the benchmark defines what "passed" means (real-build
638
+ * hit-rate, test-pass, etc.). The leaderboard owns only the grouping + stats. */
639
+ passed: (record: RunRecord) => boolean;
640
+ /** Group key. Default: the agent-profile cellId (harness × model × dimensions),
641
+ * so persona/harness/model sweeps separate without parsing labels. */
642
+ groupBy?: (record: RunRecord) => string;
643
+ }
644
+ /**
645
+ * Group `records`, compute pass-rate + Wilson CI + cost/token/latency means per
646
+ * group, and rank by pass-rate (cost as the tiebreaker — cheaper wins a tie).
647
+ * Pure projection: no I/O, deterministic, safe to call on any RunRecord[].
648
+ */
649
+ declare function leaderboard(records: RunRecord[], opts: LeaderboardOptions): LeaderboardRow[];
650
+ interface PairedDeltaTestOptions extends PairedBootstrapOptions {
651
+ /** Smallest candidate-minus-baseline delta that counts as improvement. Default 0. */
652
+ threshold?: number;
653
+ /** Caller-required paired observations. The exact test may impose a higher minimum. */
654
+ minPairs?: number;
655
+ }
656
+ interface PairedDeltaTestResult {
657
+ bootstrap: PairedBootstrapResult;
658
+ method: 'bootstrap-ci' | 'exact-sign';
659
+ /** Exact one-sided p-value below the bootstrap minimum; otherwise null. */
660
+ pValue: number | null;
661
+ /** Effective observation minimum after accounting for confidence. */
662
+ minimumPairs: number;
663
+ sufficient: boolean;
664
+ /**
665
+ * The bootstrap interval has zero width (or is non-finite), so it carries no
666
+ * information about how far the estimate could be wrong and cannot support a
667
+ * decision in either direction. See {@link pairedDeltaTest}.
668
+ */
669
+ indeterminate: boolean;
670
+ significant: boolean;
671
+ }
672
+ /** Smallest all-positive sample that can clear a one-sided exact sign test. */
673
+ declare function minimumPairsForPairedDeltaTest(confidence?: number): number;
674
+ /**
675
+ * Tests whether a paired candidate-minus-baseline delta clears a threshold.
676
+ *
677
+ * At 20 or more pairs, the percentile bootstrap lower bound carries the
678
+ * decision. Below that point the interval is descriptive only, so the function
679
+ * switches to a pre-registered one-sided exact sign test. The exact path is
680
+ * deliberately conservative: it requires both a point estimate above the
681
+ * threshold and enough consistently positive paired differences.
682
+ *
683
+ * ## A zero-width interval is never significant
684
+ *
685
+ * When every paired delta is identical the resample distribution is a point
686
+ * mass and the interval collapses: `[0, 0]` when all pairs tie, `[g, g]` on n
687
+ * identical deltas of g. Neither says the effect is certain — both say the
688
+ * sample carries no information about how far the estimate could be wrong, and
689
+ * `low > threshold` then answers on the point estimate alone. It fails in both
690
+ * directions: `[0, 0]` clears every NEGATIVE threshold, which is how a
691
+ * tie-dominated pass/fail comparison laundered a regression into a
692
+ * noninferiority pass, and `[g, g]` clears every threshold below g with no
693
+ * spread behind it. Under a bounded asymmetric null whose true mean paired
694
+ * delta is exactly 0 — 2 % of pairs dropping by 1.0, the rest gaining 0.0204 —
695
+ * every sample that misses the drop is exactly that shape, and deciding on
696
+ * `low > 0` promoted 65.65 % of samples at n = 20 against a nominal 5 %.
697
+ *
698
+ * So `indeterminate` is reported and `significant` is false whenever the
699
+ * interval has zero width, on BOTH paths: at small n the exact sign test is a
700
+ * test of the MEDIAN and a zero-spread sample is precisely where it stops
701
+ * saying anything about the mean the caller is thresholding.
702
+ *
703
+ * `threshold` may be negative — that is a noninferiority margin, and it is the
704
+ * regime the zero-width hole is worst in. For a two-point (pass/fail) outcome
705
+ * the percentile bootstrap is not a valid interval at a nonzero margin at all;
706
+ * use {@link decidePairedPromotion}, which routes those to Tango's score
707
+ * interval, rather than thresholding this function's bootstrap directly.
708
+ */
709
+ declare function pairedDeltaTest(before: number[], after: number[], options?: PairedDeltaTestOptions): PairedDeltaTestResult;
710
+ /**
711
+ * Deterministic held-out partitioning over stable ids.
712
+ *
713
+ * A promotion gate's paired-delta math needs a fixed partition: the same
714
+ * scenario id lands in the same bucket forever, regardless of when it ran or
715
+ * which loop emitted the record — otherwise the gate has nothing paired to
716
+ * compare against. Picking one global split per run is wrong: every run then
717
+ * stamps either ALL search or ALL holdout, and no id is ever observed on both
718
+ * sides. Per-id deterministic hashing fixes that.
719
+ *
720
+ * Two consumers hand-roll this (agent-builder's `deterministicSplit` and the
721
+ * frontier persona-splitter). The substrate already has a 3-way benchmark
722
+ * `deterministicSplit` in `./benchmarks`; this is the generic, validated batch
723
+ * partitioner: it takes a list of ids, splits them by a stable FNV-1a hash, and
724
+ * fails loud when the inputs can't support a trustworthy held-out comparison
725
+ * (duplicate ids, or a holdout set below the significance floor).
726
+ */
727
+ interface PartitionHeldOutOptions {
728
+ /** Partition seed. Bumping it reshuffles every assignment — do that only when
729
+ * the corpus/policy changes meaningfully so old and new records can't pair.
730
+ * Default 'held-out-v1'. */
731
+ seed?: string;
732
+ /** Fraction routed to the held-out bucket, in (0, 1). Default 0.5. */
733
+ holdoutFraction?: number;
734
+ /**
735
+ * Minimum held-out ids required for the split to be admissible. Below this,
736
+ * a paired comparison has too few observations to be significant, so the
737
+ * partition throws rather than hand back an underpowered holdout. Default 1
738
+ * (only the empty-holdout degenerate case is rejected). Raise it to enforce
739
+ * real statistical power. */
740
+ minHoldout?: number;
741
+ /** Same floor for the search side. Default 1. */
742
+ minSearch?: number;
743
+ }
744
+ interface HeldOutPartition {
745
+ /** Ids the optimizer is allowed to read/optimize against. */
746
+ search: string[];
747
+ /** Ids reserved for gated, held-out evaluation. */
748
+ holdout: string[];
749
+ seed: string;
750
+ holdoutFraction: number;
751
+ }
752
+ /**
753
+ * Partition a list of stable ids into disjoint `search` / `holdout` sets by a
754
+ * deterministic hash. Fail-loud:
755
+ * - empty input throws,
756
+ * - duplicate ids throw (a dup would be observed on both sides, double-counting),
757
+ * - a holdout (or search) set below its floor throws (underpowered comparison).
758
+ *
759
+ * Order within each bucket follows the input order, so the result is stable and
760
+ * reproducible.
761
+ */
762
+ declare function partitionHeldOut(ids: string[], options?: PartitionHeldOutOptions): HeldOutPartition;
763
+ interface TrainSelectionTestOptions<T> {
764
+ /** Partition seed. Bumping it reshuffles every unit. Default 'train-selection-test-v1'. */
765
+ seed?: string;
766
+ /** Share of units routed to selection, in (0, 1). Default 0.25. */
767
+ selectionFraction?: number;
768
+ /** Share of units routed to test, in (0, 1). Default 0.25. */
769
+ testFraction?: number;
770
+ /**
771
+ * The independent unit an item samples, such as the task a fresh run repeats.
772
+ * Every item of one unit lands in the same partition, so a final test never
773
+ * scores a unit the optimizer read. Default: the item id.
774
+ */
775
+ unitOf?: (item: T) => string;
776
+ /** Minimum items per partition. Defaults 1, 1 and 1. */
777
+ minTrain?: number;
778
+ minSelection?: number;
779
+ minTest?: number;
780
+ }
781
+ interface TrainSelectionTestPartition<T> {
782
+ /** Items an optimizer may learn from. */
783
+ train: T[];
784
+ /** Items an optimizer may use to accept candidates and stop. */
785
+ selection: T[];
786
+ /** Items reserved for the final comparison. */
787
+ test: T[];
788
+ seed: string;
789
+ selectionFraction: number;
790
+ testFraction: number;
791
+ }
792
+ interface ProductBenchmarkExportOptions {
793
+ /** Source eval run directories, each containing a `records.jsonl` of RunRecord rows. */
794
+ readonly runDirs: readonly string[];
795
+ /** Destination directory for the bundle (manifest + records + materialized source runs). */
796
+ readonly outDir: string;
797
+ readonly projectId: string;
798
+ readonly benchmarkId: string;
799
+ /** Repo-relative path of the product's canonical agent profile source. */
800
+ readonly agentProfilePath: string;
801
+ /** Pass threshold applied when a row carries no explicit `outcome.raw.pass`. Default 0.7. */
802
+ readonly passThreshold?: number;
803
+ /**
804
+ * First scenario tag. Defaults to `projectId` with a trailing `-agent`
805
+ * stripped (`tax-agent` → `tax`), matching the product exporters.
806
+ */
807
+ readonly scenarioTagPrefix?: string;
808
+ /** Profile id used when a row has no `agentProfile.profileId`. Defaults to the row's arm id. */
809
+ readonly fallbackProfileId?: string;
810
+ /** Arm mutable surfaces recorded in the manifest. Defaults to the full superset. */
811
+ readonly mutableSurfaces?: readonly string[];
812
+ /**
813
+ * Copy each run dir into `<outDir>/source-runs/` and record
814
+ * bundle-relative artifact paths (portable, self-contained). When false,
815
+ * artifacts keep absolute paths into the original run dirs. Default true.
816
+ */
817
+ readonly materializeSourceRuns?: boolean;
818
+ /**
819
+ * Override split classification for a row. Return undefined to fall back
820
+ * to the default (`outcome.raw.safety === 1` → safety, then splitTag).
821
+ */
822
+ readonly classifySplit?: (record: RunRecord) => ProductBenchmarkSplit | undefined;
823
+ /** Recovers a tool-call count when the row's raw bag carries none (e.g. from turn artifacts). */
824
+ readonly toolCallFallback?: (record: RunRecord, runDir: string) => number;
825
+ /** Backend version recorded per row. Defaults to the cwd package.json's `@tangle-network/sandbox` range. */
826
+ readonly backendVersion?: string;
827
+ /**
828
+ * Explicit substrate versions for the manifest, merged over what the cwd
829
+ * package.json / node_modules resolve. Use when a substrate package is not
830
+ * installed where the export runs — the validator refuses an `'unknown'`
831
+ * version, so provide the real one rather than shipping the sentinel.
832
+ */
833
+ readonly substrate?: Partial<ProductBenchmarkManifest['substrate']>;
834
+ }
835
+ interface ProductBenchmarkSingleRunExportOptions extends Omit<ProductBenchmarkExportOptions, 'runDirs'> {
836
+ readonly runDir: string;
837
+ }
838
+ interface ProductBenchmarkExportResult {
839
+ readonly manifestPath: string;
840
+ readonly recordsPath: string;
841
+ readonly records: number;
842
+ }
843
+ /** Repo identity from the exporting process's cwd. `'unknown'` values are flagged by `validateProductBenchmarkRun`. */
844
+ declare function productBenchmarkRepoIdentity(): ProductBenchmarkManifest['repo'];
845
+ /** Single-run convenience wrapper over `exportProductBenchmarkRuns`. */
846
+ declare function exportProductBenchmark(options: ProductBenchmarkSingleRunExportOptions): ProductBenchmarkExportResult;
847
+ /**
848
+ * Export one or more product eval run dirs into a validated product
849
+ * benchmark bundle at `outDir`. Both the manifest and every record are
850
+ * run through the contract validators before anything is written.
851
+ */
852
+ declare function exportProductBenchmarkRuns(options: ProductBenchmarkExportOptions): ProductBenchmarkExportResult;
853
+ declare const productBenchmarkSplits: readonly ["practice", "dev", "holdout", "safety", "sentinel"];
854
+ type ProductBenchmarkSplit = (typeof productBenchmarkSplits)[number];
855
+ interface ProductBenchmarkRepoRef {
856
+ readonly url: string;
857
+ readonly commit: string;
858
+ readonly branch: string;
859
+ }
860
+ interface ProductBenchmarkSubstrateVersions {
861
+ readonly agentEval: string;
862
+ readonly agentRuntime: string;
863
+ readonly agentInterface: string;
864
+ readonly sandbox: string;
865
+ readonly agentBench?: string;
866
+ }
867
+ interface ProductBenchmarkProfileRef {
868
+ readonly id: string;
869
+ readonly profileHash: string;
870
+ readonly agentProfilePath: string;
871
+ }
872
+ interface ProductBenchmarkArm {
873
+ readonly id: string;
874
+ readonly profileId: string;
875
+ readonly mutableSurfaces: readonly string[];
876
+ readonly policyAxes: Record<string, unknown>;
877
+ }
878
+ interface ProductBenchmarkScenario {
879
+ readonly id: string;
880
+ readonly split: ProductBenchmarkSplit;
881
+ readonly tags: readonly string[];
882
+ readonly sourceAllowedForSynthesis: boolean;
883
+ }
884
+ interface ProductBenchmarkBudgets {
885
+ readonly maxUsd: number;
886
+ readonly maxCells: number;
887
+ readonly maxWallMs: number;
888
+ }
889
+ interface ProductBenchmarkManifest {
890
+ readonly schemaVersion: 1;
891
+ readonly projectId: string;
892
+ readonly benchmarkId: string;
893
+ readonly repo: ProductBenchmarkRepoRef;
894
+ readonly substrate: ProductBenchmarkSubstrateVersions;
895
+ readonly profiles: readonly ProductBenchmarkProfileRef[];
896
+ readonly arms: readonly ProductBenchmarkArm[];
897
+ readonly scenarios: readonly ProductBenchmarkScenario[];
898
+ readonly budgets: ProductBenchmarkBudgets;
899
+ readonly expectedArtifactDir: string;
900
+ }
901
+ interface ProductBenchmarkValidationReport {
902
+ readonly manifestPath: string;
903
+ readonly recordsPath: string;
904
+ readonly records: number;
905
+ /** Manifest repo fields that are empty or the `'unknown'` export sentinel. */
906
+ readonly repoFailures: readonly string[];
907
+ /** Manifest substrate versions that are empty or the `'unknown'` export
908
+ * sentinel — a bundle without substrate identity is not reproducible. */
909
+ readonly substrateFailures: readonly string[];
910
+ readonly projects: readonly string[];
911
+ readonly benchmarks: readonly string[];
912
+ readonly arms: readonly string[];
913
+ readonly scenarios: readonly string[];
914
+ readonly passed: number;
915
+ readonly failed: number;
916
+ readonly inputTokens: number;
917
+ readonly outputTokens: number;
918
+ readonly costUsd: number;
919
+ readonly wallMs: number;
920
+ readonly integrityFailures: readonly string[];
921
+ readonly missingArtifacts: readonly string[];
922
+ }
923
+ declare function readProductBenchmarkManifest(path: string): ProductBenchmarkManifest;
924
+ /**
925
+ * Fail-loud gate over a bundle directory: locates the manifest + records,
926
+ * runs `validateProductBenchmarkRun`, and throws with every repo,
927
+ * integrity, and artifact failure listed. Returns the report when clean.
928
+ */
929
+ declare function assertProductBenchmarkRun(runDir: string): ProductBenchmarkValidationReport;
930
+ interface RuntimeTrajectoryHookEvent {
931
+ id: string;
932
+ runId: string;
933
+ scenarioId?: string;
934
+ target: string;
935
+ phase: string;
936
+ timestamp: number;
937
+ stepIndex?: number;
938
+ parentId?: string;
939
+ payload?: unknown;
940
+ metadata?: Record<string, unknown>;
941
+ }
942
+ interface RuntimeTrajectoryRecord {
943
+ id?: string;
944
+ scenarioId?: string;
945
+ splitTag?: RunSplitTag;
946
+ runtimeEvents?: unknown;
947
+ [key: string]: unknown;
948
+ }
949
+ interface RuntimeTrajectoryRunRecord {
950
+ runId: string;
951
+ scenarioId?: string;
952
+ splitTag: RunSplitTag;
953
+ }
954
+ interface RuntimeTrajectoryEvidenceSummary {
955
+ recordCount: number;
956
+ recordWithRuntimeEventsCount: number;
957
+ runtimeRunCount: number;
958
+ lifecycleEventCount: number;
959
+ defaultedSplitCount: number;
960
+ }
961
+ interface RuntimeTrajectoryEvidenceProjection {
962
+ runs: RuntimeTrajectoryRunRecord[];
963
+ events: RuntimeTrajectoryHookEvent[];
964
+ summary: RuntimeTrajectoryEvidenceSummary;
965
+ diagnostics: string[];
966
+ }
967
+ interface ProjectRuntimeTrajectoryEvidenceOptions<TRecord extends RuntimeTrajectoryRecord = RuntimeTrajectoryRecord> {
968
+ records: TRecord[];
969
+ defaultSplitTag?: RunSplitTag;
970
+ recordIdOf?: (record: TRecord, index: number) => string | undefined;
971
+ scenarioIdOf?: (record: TRecord, index: number) => string | undefined;
972
+ }
973
+ declare function projectRuntimeTrajectoryEvidence<TRecord extends RuntimeTrajectoryRecord>(options: ProjectRuntimeTrajectoryEvidenceOptions<TRecord>): RuntimeTrajectoryEvidenceProjection;
974
+ type SynthesisReason = 'high-variance' | 'undersampled' | 'failure-cluster' | 'difficulty-gap';
975
+ interface SynthesisTarget {
976
+ reason: SynthesisReason;
977
+ description: string;
978
+ /** Existing scenarios that are closest to the gap; caller feeds these to
979
+ * their LLM proposer as few-shot examples. */
980
+ neighbors: DatasetScenario[];
981
+ /** Suggested direction — e.g. "harder variants", "edge cases of X", "failure class Y". */
982
+ direction: string;
983
+ /** Priority score — higher = more information-dense gap. 0..1. */
984
+ priority: number;
985
+ }
986
+ interface ActiveLearningOptions {
987
+ /** Minimum scenarios per difficulty band to count as "covered". */
988
+ minPerBand?: number;
989
+ /** Variance threshold above which a scenario's dimension is "unstable". */
990
+ varianceThreshold?: number;
991
+ /** Max synthesis targets returned. */
992
+ topK?: number;
993
+ }
994
+ declare function proposeSynthesisTargets(dataset: Dataset, traceStore: TraceStore, options?: ActiveLearningOptions): Promise<SynthesisTarget[]>;
995
+ /**
996
+ * `runBoundedProcess` — run one command under a wall-clock bound and return
997
+ * what it produced, leaving no descendant behind.
998
+ *
999
+ * The child leads its own process group (`detached` on every non-win32
1000
+ * platform), so a deadline or an abort kills the group with
1001
+ * `process.kill(-pid, 'SIGKILL')` rather than only the shell that was
1002
+ * spawned. That distinction is the whole point of this module: `sh -c
1003
+ * 'solver & wait'` puts the real work in a grandchild, and a kill aimed at
1004
+ * the shell alone leaves that grandchild running, holding the stdout and
1005
+ * stderr pipes open. `close` then never fires, so the deadline produces no
1006
+ * result the caller can observe and the descendant outlives the run.
1007
+ *
1008
+ * Two further guarantees the caller depends on:
1009
+ *
1010
+ * - A killed run never reports success. A SIGKILLed child can still close
1011
+ * with exit code 0, so a run killed by the deadline or by an abort has
1012
+ * its exit code forced non-zero.
1013
+ * - A run always resolves. If a grandchild escaped the group and still
1014
+ * holds the pipes, the result is delivered {@link KILL_DRAIN_MS} after
1015
+ * the kill instead of waiting on a `close` that will not come.
1016
+ *
1017
+ * The captured output is bounded. Once the cap is reached the streams are
1018
+ * still drained — the child needs its pipes read to reach `close` — but the
1019
+ * bytes are discarded and `outputTruncated` is set.
1020
+ */
1021
+ /** Wall-clock bound applied when the caller names none. */
1022
+ declare const DEFAULT_BOUNDED_PROCESS_TIMEOUT_MS: number;
1023
+ interface BoundedProcessInput {
1024
+ /**
1025
+ * The command line, interpreted by a shell unless `shell` is `false`. When
1026
+ * {@link BoundedProcessInput.args} is present this is a program path
1027
+ * instead, and no shell reads it.
1028
+ */
1029
+ command: string;
1030
+ /**
1031
+ * Argument vector for `command`, delivered to the program as separate
1032
+ * argv entries with no shell between them.
1033
+ *
1034
+ * This is the form to use whenever an argument carries text the caller did
1035
+ * not author — a script body, a path, a pattern. Without it, `interpreter
1036
+ * -flag <script>` can only be expressed by quoting the script into a
1037
+ * shell string, and a quoting bug there is a command injection rather than
1038
+ * a wrong answer. Each entry arrives verbatim, so there is nothing to
1039
+ * quote: `{ command: 'bash', args: ['-n', '-c', body] }` parses `body`
1040
+ * whatever shell metacharacters it holds.
1041
+ *
1042
+ * Two limits are the platform's, not this module's. A NUL byte in an entry
1043
+ * cannot be passed to a process at all, and is reported as a `runnerError`
1044
+ * rather than thrown. A lone surrogate is encoded to UTF-8 as U+FFFD, and a
1045
+ * non-string entry from an untypechecked caller is coerced with `String()`,
1046
+ * so an entry is verbatim exactly when it is a string a UTF-8 argv can hold.
1047
+ *
1048
+ * A shell cannot interpret an argument vector, so `args` and a truthy
1049
+ * `shell` are contradictory. Passing both spawns nothing and reports
1050
+ * `runnerError`.
1051
+ */
1052
+ args?: string[];
1053
+ /**
1054
+ * Text written to the child's standard input. The stream is closed after
1055
+ * the write, so a command that reads until end-of-file sees exactly this
1056
+ * payload and then EOF.
1057
+ *
1058
+ * The child's stdin is closed whether or not this field is present. A pipe
1059
+ * nobody closes is a hang: a command that reads stdin waits for bytes that
1060
+ * never arrive until the deadline kills it, which reads downstream as a
1061
+ * slow command rather than as a caller that forgot to send input.
1062
+ *
1063
+ * The payload is written as UTF-8 and is otherwise verbatim: a NUL byte,
1064
+ * a newline, a quote and a backslash all arrive as themselves, because
1065
+ * nothing parses this text. The one platform limit is the one `args`
1066
+ * already states — a lone surrogate is encoded as U+FFFD, since no UTF-8
1067
+ * stream can carry it.
1068
+ *
1069
+ * A child that exits before reading breaks the pipe. That is the child's
1070
+ * behaviour, not a runner failure, so the run still settles on the child's
1071
+ * own exit code, and the undelivered payload is reported in `runnerError`
1072
+ * with the `stdin not delivered:` prefix. Whether a SMALL payload to a
1073
+ * command that ignores stdin reports this is a race — the write may land in
1074
+ * the pipe buffer before the child exits — so the flag is a statement about
1075
+ * delivery, never about the command. It is not a race for the case that
1076
+ * matters: a payload larger than the pipe buffer that the child never
1077
+ * drains cannot be delivered, and reports every time.
1078
+ */
1079
+ stdin?: string;
1080
+ /**
1081
+ * Shell that interprets `command`. `true` (the default) uses the platform
1082
+ * default — `/bin/sh` on POSIX, `cmd.exe` on Windows. A string names the
1083
+ * shell binary, e.g. `'bash'`, which is required for a bash-only construct
1084
+ * such as `[[ ]]` or `$BASH_VERSION`. `false` executes `command` directly
1085
+ * as a program path with no arguments. Ignored — and refused when truthy —
1086
+ * once {@link BoundedProcessInput.args} is present.
1087
+ */
1088
+ shell?: boolean | string;
1089
+ /** Working directory. The parent's cwd is inherited when omitted. */
1090
+ cwd?: string;
1091
+ /**
1092
+ * Environment for the child. How it combines with the parent environment
1093
+ * is decided by {@link BoundedProcessInput.envMode}.
1094
+ */
1095
+ env?: NodeJS.ProcessEnv;
1096
+ /**
1097
+ * `'merge'` (the default) layers `env` on top of `process.env`, so the
1098
+ * child sees every variable this process has plus the named ones.
1099
+ * `'replace'` passes exactly `env` and nothing else, which is what a
1100
+ * grader needs when the claim under test is about the variables the
1101
+ * command could read. `PATH` is not re-added under `'replace'`, and a
1102
+ * command that must find a binary on `PATH` names `PATH` itself.
1103
+ *
1104
+ * With no `PATH` in the child environment the two forms do NOT search the
1105
+ * same directories, so a caller under `'replace'` that names no `PATH` gets
1106
+ * a resolution that depends on which form it picked. The shell form execs
1107
+ * the shell, which applies its own compiled-in default; the argv form
1108
+ * reaches `execvp`, which falls back to `confstr(_CS_PATH)`. Measured on
1109
+ * Linux/glibc: `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`
1110
+ * against `/bin:/usr/bin`, so a binary in `/usr/local/bin` resolves on the
1111
+ * shell form and reports ENOENT on the argv form. Name `PATH` and both
1112
+ * forms search exactly what was named.
1113
+ */
1114
+ envMode?: 'merge' | 'replace';
1115
+ /** Wall-clock bound in ms. Defaults to 10 minutes. */
1116
+ timeoutMs?: number;
1117
+ /**
1118
+ * Cap on captured stdout+stderr. Reaching it truncates the captured text
1119
+ * and sets `outputTruncated`; it never kills the process. Defaults to
1120
+ * 16 MiB.
1121
+ */
1122
+ maxOutputBytes?: number;
1123
+ /**
1124
+ * Kills the process group when it aborts, the same way the deadline does.
1125
+ * A signal that is already aborted when `runBoundedProcess` is called
1126
+ * prevents the spawn entirely — nothing runs.
1127
+ */
1128
+ signal?: AbortSignal;
1129
+ }
1130
+ interface BoundedProcessResult {
1131
+ /**
1132
+ * The process actually started. False when nothing ran at all: an
1133
+ * already-aborted signal, `args` with a truthy `shell`, an argument the
1134
+ * platform refuses, or a program that does not exist.
1135
+ *
1136
+ * This is the field to branch on, never the text of `runnerError`. A caller
1137
+ * deciding whether a run produced an outcome at all is asking exactly this,
1138
+ * and asking it here is type-checked rather than held by a wording
1139
+ * convention.
1140
+ */
1141
+ spawned: boolean;
1142
+ /**
1143
+ * The `stdin` payload reached the child in full. True whenever the caller
1144
+ * supplied none, because nothing could fail to arrive.
1145
+ *
1146
+ * False means the command ran on partial input, which is a fact about the
1147
+ * answer it produced: a checksum over a truncated payload is a wrong answer,
1148
+ * not a failed run. `spawned` stays true and `exitCode` stays the child's.
1149
+ */
1150
+ stdinDelivered: boolean;
1151
+ /**
1152
+ * Exit code. Forced non-zero whenever `killedByTimeout` or
1153
+ * `killedBySignal` is set, because a SIGKILLed child can close with 0 and
1154
+ * a killed run must never read as a pass.
1155
+ */
1156
+ exitCode: number;
1157
+ /** Captured stdout, truncated at `maxOutputBytes`. */
1158
+ stdout: string;
1159
+ /**
1160
+ * Captured stderr, truncated at `maxOutputBytes`. A runner-side failure is
1161
+ * reported in `runnerError`, not appended here; the caller decides whether
1162
+ * the command's own stderr and the runner's diagnosis belong together.
1163
+ */
1164
+ stderr: string;
1165
+ /** Wall-clock duration in ms, measured across the whole call. */
1166
+ wallMs: number;
1167
+ /** The wall-clock bound was reached and the process group was killed. */
1168
+ killedByTimeout: boolean;
1169
+ /** `signal` aborted and the process group was killed. */
1170
+ killedBySignal: boolean;
1171
+ /** `maxOutputBytes` was reached and later output was discarded. */
1172
+ outputTruncated: boolean;
1173
+ /**
1174
+ * Human-readable diagnosis of a runner-side failure, in one of three shapes:
1175
+ *
1176
+ * - `not spawned:` — the caller's own bug, such as `args` with a truthy
1177
+ * `shell`, or an already-aborted signal. No retry helps.
1178
+ * - `stdin not delivered:` — the command ran and exited on its own, and
1179
+ * the stdin payload did not reach it in full because the child closed
1180
+ * the pipe first.
1181
+ * - anything else — the process could not be spawned (a missing binary, a
1182
+ * NUL byte in an argument).
1183
+ *
1184
+ * Absent when the command ran and read everything it was sent, however it
1185
+ * exited. The prefixes are for a person reading a log; code reads `spawned`
1186
+ * and `stdinDelivered`, which say the same things and are type-checked.
1187
+ */
1188
+ runnerError?: string;
1189
+ }
1190
+ declare function runBoundedProcess(input: BoundedProcessInput): Promise<BoundedProcessResult>;
1191
+ /**
1192
+ * CommandRunner — abstract subprocess execution surface.
1193
+ *
1194
+ * Layers in a {@link MultiLayerVerifier} that need to invoke external
1195
+ * tools (compilers, test runners, package managers) call out via this
1196
+ * interface rather than directly using `child_process`. Two reasons:
1197
+ *
1198
+ * 1. **Sandbox interchangeability.** A run that targets a sandbox box
1199
+ * (via SDK-specific Box.exec) and a run that targets the host both
1200
+ * satisfy this same contract. The harness doesn't care which.
1201
+ * 2. **Testability.** Tests inject a fake runner and assert on calls
1202
+ * without spawning real subprocesses.
1203
+ *
1204
+ * agent-eval ships only the local implementation (host-process). Sandbox
1205
+ * implementations live with their consumer because they depend on
1206
+ * SDK-specific Box / Sandbox types that don't belong in this package.
1207
+ */
1208
+ interface RunCommandInput {
1209
+ /** Executable name, looked up via PATH unless absolute. */
1210
+ cmd: string;
1211
+ /** Argument vector, NOT shell-interpolated. Each element passed to argv. */
1212
+ argv: string[];
1213
+ /** Working directory. Defaults to runner's notion of cwd if omitted. */
1214
+ cwd?: string;
1215
+ /**
1216
+ * Wall-clock cap in ms. The runner SHOULD return `timedOut: true` when
1217
+ * exceeded; callers MAY treat status null + timedOut as "killed."
1218
+ *
1219
+ * Omitting it does NOT mean unbounded: {@link localCommandRunner} then
1220
+ * applies {@link LOCAL_COMMAND_RUNNER_DEFAULT_CAP_MS}. A command that
1221
+ * legitimately runs longer than that names its own `capMs`; it is never
1222
+ * killed silently, because a run stopped by the bound comes back with
1223
+ * `timedOut: true` and `status: null`.
1224
+ */
1225
+ capMs?: number;
1226
+ /** Env overrides merged on top of the runner's base environment. */
1227
+ env?: Record<string, string>;
1228
+ /** Optional stdin payload. */
1229
+ stdin?: string;
1230
+ }
1231
+ interface RunCommandResult {
1232
+ /** Exit code, or null when the process couldn't start / was killed. */
1233
+ status: number | null;
1234
+ stdout: string;
1235
+ stderr: string;
1236
+ durationMs: number;
1237
+ timedOut: boolean;
1238
+ /** Non-fatal runner-side error (binary missing, signal, etc.). */
1239
+ runnerError?: string;
1240
+ }
1241
+ interface DirEntry {
1242
+ name: string;
1243
+ isDirectory: boolean;
1244
+ isFile: boolean;
1245
+ /** File size in bytes. `null` for directories (not stat'd). */
1246
+ sizeBytes: number | null;
1247
+ }
1248
+ interface CommandRunner {
1249
+ /** Identifier for telemetry + logs. Open-ended literal-union for new runners. */
1250
+ readonly name: string;
1251
+ /** Execute a command in the runner's environment. */
1252
+ run(input: RunCommandInput): Promise<RunCommandResult>;
1253
+ /** True iff `<name>` resolves on the runner's PATH. */
1254
+ hasBin(name: string): Promise<boolean>;
1255
+ /** True iff the given path exists in the runner's filesystem. */
1256
+ fileExists(path: string): Promise<boolean>;
1257
+ /** Read a file. Returns `null` if missing or unreadable. */
1258
+ readFile(path: string): Promise<string | null>;
1259
+ /** List a directory. Returns `[]` if unreadable / missing. */
1260
+ readDir(path: string): Promise<DirEntry[]>;
1261
+ }
1262
+ declare const localCommandRunner: CommandRunner;
1263
+ /**
1264
+ * Walk a personas directory and return every file matching the convention
1265
+ * `NN-slug.{yaml,yml,json,md}`. Sorted by filename so the numeric prefix
1266
+ * gives stable persona ordering for reproducibility. Consumers filter
1267
+ * through `include` / `exclude`.
1268
+ */
1269
+ interface DiscoverPersonasOptions {
1270
+ /**
1271
+ * Regex applied to filenames. Files that don't match are skipped.
1272
+ * Default: `^[0-9]{2}-.+\.(yaml|yml|json|md)$`.
1273
+ */
1274
+ pattern?: RegExp;
1275
+ /**
1276
+ * Filenames (or basenames) to skip. Use this to exclude WIP / archived
1277
+ * personas without removing the file.
1278
+ */
1279
+ exclude?: readonly string[];
1280
+ /**
1281
+ * If set, return only personas whose basename contains one of these
1282
+ * substrings (post-pattern filter).
1283
+ */
1284
+ include?: readonly string[];
1285
+ /** Recurse into subdirectories. Default false. */
1286
+ recursive?: boolean;
1287
+ }
1288
+ interface DiscoveredPersona {
1289
+ /** Absolute file path. */
1290
+ path: string;
1291
+ /** Filename without directory. */
1292
+ filename: string;
1293
+ /** Filename without extension — the conventional persona id. */
1294
+ id: string;
1295
+ }
1296
+ declare function discoverPersonas(dir: string, opts?: DiscoverPersonasOptions): Promise<DiscoveredPersona[]>;
1297
+ /** Verdict for one candidate relative to its parent. ITERATE is the neutral
1298
+ * "keep collecting reps / no parent to compare against" state. */
1299
+ type ExperimentVerdict = 'KEEP' | 'ITERATE' | 'NOISE' | 'REGRESSION';
1300
+ /** A single repetition of a candidate run, carrying the score the verdict is
1301
+ * computed on plus any free-form per-rep metrics the consumer wants kept. */
1302
+ interface ExperimentRep {
1303
+ /** 0-indexed repetition number. */
1304
+ rep: number;
1305
+ /** The score this rep is judged on (same scale as the thresholds). */
1306
+ score: number;
1307
+ /** ISO timestamp the rep completed. */
1308
+ timestamp: string;
1309
+ /** Stable execution/run identity that produced this score. */
1310
+ runId?: string;
1311
+ /** Mechanically resolvable trace, artifact, metric, or finding evidence. */
1312
+ evidence?: EvidenceRef[];
1313
+ /** Whether this rep passed the consumer's own gate — folded into `passRate`. */
1314
+ passed?: boolean;
1315
+ /** Free-form numeric metrics retained for later analysis. */
1316
+ metrics?: Record<string, number>;
1317
+ }
1318
+ interface ExperimentStats {
1319
+ median: number;
1320
+ mean: number;
1321
+ min: number;
1322
+ max: number;
1323
+ /** Inter-quartile range of the rep scores. */
1324
+ iqr: number;
1325
+ /** Population standard deviation of the rep scores. */
1326
+ stddev: number;
1327
+ /** Fraction of reps with `passed === true`, over reps that set `passed`.
1328
+ * null when no rep declared a pass/fail outcome. */
1329
+ passRate: number | null;
1330
+ /** Number of reps. */
1331
+ n: number;
1332
+ /** True when the sample is tight enough to trust for a verdict. */
1333
+ stable: boolean;
1334
+ }
1335
+ interface ImprovementThresholds {
1336
+ /** medianDelta strictly above this ⇒ KEEP. Default 5. */
1337
+ keepThreshold?: number;
1338
+ /** medianDelta strictly below the negative of this ⇒ REGRESSION. Default 5. */
1339
+ regressionThreshold?: number;
1340
+ /** iqr at or above this ⇒ unstable. Default 10. */
1341
+ iqrUnstableAbove?: number;
1342
+ /** stddev at or above this ⇒ unstable. Default Infinity (iqr-only stability). */
1343
+ stddevUnstableAbove?: number;
1344
+ /** Reps required on BOTH candidate and parent before a verdict is rendered.
1345
+ * Default 3. */
1346
+ minRepsForVerdict?: number;
1347
+ }
1348
+ interface ImprovementVerdictResult {
1349
+ verdict: ExperimentVerdict;
1350
+ /** candidate.median − parent.median; null when no parent or insufficient reps. */
1351
+ medianDelta: number | null;
1352
+ /** Human-readable reason for the verdict — for dashboards and logs. */
1353
+ reason: string;
1354
+ }
1355
+ interface Verification {
1356
+ pass: boolean;
1357
+ score?: number;
1358
+ failingLayers?: string[];
1359
+ details?: unknown;
1360
+ }
1361
+ interface Review {
1362
+ observations: string;
1363
+ diagnosis: string;
1364
+ nextShotInstruction: string;
1365
+ shouldContinue: boolean;
1366
+ confidence: number;
1367
+ }
1368
+ interface ReviewMemoryEntry extends Review {
1369
+ shot: number;
1370
+ timestamp: number;
1371
+ verification: {
1372
+ pass: boolean;
1373
+ score?: number;
1374
+ failingLayers?: string[];
1375
+ };
1376
+ }
1377
+ interface ProposeInput<State> {
1378
+ shot: number;
1379
+ goal: string;
1380
+ state: State;
1381
+ priorReview: Review | null;
1382
+ abortSignal: AbortSignal;
1383
+ emitter?: TraceEmitter;
1384
+ }
1385
+ interface ProposeOutput<State, Summary = unknown> {
1386
+ state: State;
1387
+ traceSummary?: Summary;
1388
+ }
1389
+ interface ReviewInput<State, Summary = unknown> {
1390
+ shot: number;
1391
+ goal: string;
1392
+ state: State;
1393
+ verification: Verification;
1394
+ traceSummary: Summary | undefined;
1395
+ memory: ReviewMemoryEntry[];
1396
+ }
1397
+ type ProposeFn<State, Summary = unknown> = (input: ProposeInput<State>) => Promise<ProposeOutput<State, Summary>>;
1398
+ type VerifyFn<State> = (state: State) => Promise<Verification>;
1399
+ type ReviewFn<State, Summary = unknown> = (input: ReviewInput<State, Summary>) => Promise<Review>;
1400
+ interface ReviewMemoryStore {
1401
+ load(): Promise<ReviewMemoryEntry[]>;
1402
+ append(entry: ReviewMemoryEntry): Promise<void>;
1403
+ }
1404
+ interface ProposeReviewConfig<State, Summary = unknown> {
1405
+ goal: string;
1406
+ initialState: State;
1407
+ propose: ProposeFn<State, Summary>;
1408
+ verify: VerifyFn<State>;
1409
+ review: ReviewFn<State, Summary>;
1410
+ /** Hard shot cap. Default 10. */
1411
+ maxShots?: number;
1412
+ /** Wall-clock cap in ms. Default 10 min. */
1413
+ maxWallMs?: number;
1414
+ /**
1415
+ * If the reviewer returns confidence ≤ floor on `confidenceFloorWindow`
1416
+ * consecutive shots, terminate early. Default floor 0.3, window 2.
1417
+ * Set window to 0 or floor to <0 to disable.
1418
+ */
1419
+ confidenceFloor?: number;
1420
+ confidenceFloorWindow?: number;
1421
+ /** Defaults to an in-memory store if omitted. */
1422
+ memory?: ReviewMemoryStore;
1423
+ /** If provided, emit a Run + per-shot spans. */
1424
+ store?: TraceStore;
1425
+ scenarioId?: string;
1426
+ projectId?: string;
1427
+ variantId?: string;
1428
+ /**
1429
+ * Used when the reviewer soft-fails on shot 1 (no prior instruction to
1430
+ * fall back to). Default is a generic "inspect failures and fix".
1431
+ */
1432
+ fallbackInstruction?: string;
1433
+ }
1434
+ interface ProposeReviewShot<State, Summary = unknown> {
1435
+ shot: number;
1436
+ state: State;
1437
+ verification: Verification;
1438
+ traceSummary: Summary | undefined;
1439
+ review: Review;
1440
+ reviewAvailable: boolean;
1441
+ reviewError?: string;
1442
+ durationMs: number;
1443
+ }
1444
+ interface ProposeReviewReport<State, Summary = unknown> {
1445
+ runId: string | null;
1446
+ completed: boolean;
1447
+ shots: ProposeReviewShot<State, Summary>[];
1448
+ finalState: State;
1449
+ finalVerification: Verification;
1450
+ failureClass?: FailureClass;
1451
+ wallMs: number;
1452
+ score: number;
1453
+ }
1454
+ declare function inMemoryReviewStore(initial?: ReviewMemoryEntry[]): ReviewMemoryStore;
1455
+ declare function jsonlReviewStore(path: string): ReviewMemoryStore;
1456
+ declare function runProposeReview<State, Summary = unknown>(config: ProposeReviewConfig<State, Summary>): Promise<ProposeReviewReport<State, Summary>>;
1457
+ type LlmJsonCall = (req: {
1458
+ system: string;
1459
+ user: string;
1460
+ }) => Promise<unknown>;
1461
+ interface LlmReviewerConfig<State, Summary = unknown> {
1462
+ callJson: LlmJsonCall;
1463
+ renderState?: (state: State) => string;
1464
+ renderTraceSummary?: (summary: Summary | undefined) => string;
1465
+ /** Appended to the default system prompt. */
1466
+ systemPromptAddendum?: string;
1467
+ }
1468
+ declare function createLlmReviewer<State, Summary = unknown>(cfg: LlmReviewerConfig<State, Summary>): ReviewFn<State, Summary>;
1469
+ interface TrialTrace {
1470
+ /** Stable id for the trial — surfaces in the prompt for grounding. */
1471
+ id: string;
1472
+ /** Score the trial received on its primary metric. */
1473
+ score: number;
1474
+ /** Candidate inputs the agent was given (e.g., the fixture or scenario). */
1475
+ inputName?: string;
1476
+ /**
1477
+ * Goldens / expectations this trial was tested against, with whether each
1478
+ * was matched. The reflection prompt quotes the missed ones specifically.
1479
+ */
1480
+ expectations?: Array<{
1481
+ id: string;
1482
+ phrase: string;
1483
+ matched: boolean;
1484
+ }>;
1485
+ /** Free-form text — what the agent actually emitted (e.g., findings, plan). */
1486
+ emitted?: string;
1487
+ /** The judge's free-form note on WHY this trial scored low — the failure
1488
+ * evidence the reflection targets. Should be a generalizable pattern (which
1489
+ * checks/lines/dimensions failed, and how), never case-specific ground
1490
+ * truth (that would teach memorization, not capability). */
1491
+ failureNote?: string;
1492
+ /** Optional structured metrics (recall, precision, cost, latency). */
1493
+ metrics?: Record<string, number>;
1494
+ }
1495
+ interface ReflectionContext {
1496
+ /** What is being mutated — appears in the system prompt for orientation. */
1497
+ target: string;
1498
+ /** Current variant's payload — JSON-serialised for the prompt. */
1499
+ parentPayload: unknown;
1500
+ /** Best-performing trials this generation. */
1501
+ topTrials: TrialTrace[];
1502
+ /** Worst-performing trials this generation — the missed-golden source. */
1503
+ bottomTrials: TrialTrace[];
1504
+ /** How many children the mutator should propose. */
1505
+ childCount: number;
1506
+ /** Optional: domain-specific mutation primitives the model can pick from. */
1507
+ mutationPrimitives?: string[];
1508
+ }
1509
+ /**
1510
+ * Build the LLM-ready reflection prompt. Output is plain text — pass it as
1511
+ * the user message. The system message should be small and stable (e.g.
1512
+ * "Output ONLY a JSON object matching the schema below.").
1513
+ */
1514
+ declare function buildReflectionPrompt(ctx: ReflectionContext): string;
1515
+ interface ReflectionProposal {
1516
+ label: string;
1517
+ rationale: string;
1518
+ payload: unknown;
1519
+ }
1520
+ /**
1521
+ * Parse the model's JSON response back into proposals. Tolerates markdown
1522
+ * fences and surrounding prose. Returns at most `maxProposals`.
1523
+ */
1524
+ declare function parseReflectionResponse(raw: string, maxProposals?: number): ReflectionProposal[];
1525
+ /**
1526
+ * Backing persistence for `EvalTraceStore`. The in-memory store is the default;
1527
+ * the JSONL file store appends one validated `RunRecord` per line. Both keep an
1528
+ * append order so `getBest` / `compareRuns` are reproducible.
1529
+ */
1530
+ interface RunRecordBackend {
1531
+ append(record: RunRecord): Promise<void>;
1532
+ load(): Promise<RunRecord[]>;
1533
+ }
1534
+ /**
1535
+ * JSONL-backed store at `path`, one `RunRecord` per line. Malformed lines fail
1536
+ * loud on load (a corrupt corpus must not silently shrink the analysis set);
1537
+ * pass `skipInvalid` only for forensics on a known-bad file.
1538
+ */
1539
+ declare function jsonlRunRecordBackend(path: string, opts?: {
1540
+ skipInvalid?: boolean;
1541
+ }): RunRecordBackend;
1542
+ interface ModelPreflight {
1543
+ /** The model id as supplied by the caller. */
1544
+ model: string;
1545
+ /** Membership in the `{baseUrl}/models` served set. */
1546
+ listed: boolean;
1547
+ /**
1548
+ * The router reached a provider for this model. `null` when `probe` was not
1549
+ * requested. True on a 2xx, and also when the provider answered by
1550
+ * exhausting its reasoning budget — that proves reachability while leaving
1551
+ * identity unproven (see `budgetExhausted`).
1552
+ */
1553
+ served: boolean | null;
1554
+ /** HTTP status of the probe. `null` when not probed. */
1555
+ status: number | null;
1556
+ /** Probe body's `error.message` when present, else `null`. */
1557
+ detail: string | null;
1558
+ /**
1559
+ * The probe died on the token budget rather than on the model. Identity is
1560
+ * unproven, not refuted: raise `probeMaxTokens` to resolve it.
1561
+ */
1562
+ budgetExhausted: boolean;
1563
+ /**
1564
+ * Identity of the model that answered the probe, compared against `model`.
1565
+ * `null` when the model was not probed or the probe failed.
1566
+ */
1567
+ substitution: ServedModelCheck | null;
1568
+ }
1569
+ /** One check Agent Eval asks the caller's endpoint to perform. */
1570
+ type ModelEndpointCheck = {
1571
+ readonly kind: 'list-models';
1572
+ } | {
1573
+ readonly kind: 'probe';
1574
+ readonly model: string;
1575
+ /** Output-token budget the probe completion may bill. */
1576
+ readonly maxOutputTokens: number;
1577
+ };
1578
+ /**
1579
+ * Caller-owned request into the model endpoint. The caller binds the base URL
1580
+ * and the credential and returns the raw `Response`; a `list-models` request
1581
+ * answers with an OpenAI-compatible `{ data: [{ id }] }` body, and a `probe`
1582
+ * request answers with one minimal chat completion for `model`.
1583
+ */
1584
+ type ModelEndpointRequest = (check: ModelEndpointCheck) => Promise<Response>;
1585
+ interface PreflightModelsOptions {
1586
+ /** Caller-owned endpoint request. Agent Eval issues no provider HTTP itself. */
1587
+ request: ModelEndpointRequest;
1588
+ /** Model ids to check. */
1589
+ models: string[];
1590
+ /** When true, additionally spend a small chat probe per model. Default false. */
1591
+ probe?: boolean;
1592
+ /**
1593
+ * Output-token budget per probe. Default `PROBE_MAX_TOKENS`. Lowering it
1594
+ * below the reasoning floor makes healthy reasoning models report
1595
+ * `budgetExhausted` instead of proving their identity.
1596
+ */
1597
+ probeMaxTokens?: number;
1598
+ }
1599
+ interface PreflightOutcome {
1600
+ succeeded: boolean;
1601
+ value: ModelPreflight[] | null;
1602
+ error: string | null;
1603
+ }
1604
+ /**
1605
+ * Check that `models` are reachable on the router. Returns a typed outcome —
1606
+ * a network failure yields `{ succeeded: false, error }`, never a throw and
1607
+ * never a partial result silently reported as success. No retries, no
1608
+ * fallbacks.
1609
+ *
1610
+ * The membership check (one GET) always runs. When `probe` is true, each model
1611
+ * additionally gets a small chat probe so a model that is listed but
1612
+ * unconfigured (a 401 `model_not_found` from the router) is caught.
1613
+ */
1614
+ declare function preflightModels(opts: PreflightModelsOptions): Promise<PreflightOutcome>;
1615
+ /**
1616
+ * ModelSeats — the program's model seating chart.
1617
+ *
1618
+ * One object names which model fills each role in an eval program: the worker
1619
+ * under evaluation, the judge panel, the analyst, the reflection/driver model,
1620
+ * and the verifier. Re-tiering an entire program (economy ↔ frontier) is one
1621
+ * swapped object instead of a hunt through call sites.
1622
+ *
1623
+ * Wiring points — consumers thread seats; this module implements none of them
1624
+ * (those files belong to other surfaces):
1625
+ * - `judges` → `ensembleJudge({ models: seats.judges, … })` (src/judge-panel.ts)
1626
+ * and the `JudgeConfig`s handed to `makeEvalTools({ judges })`
1627
+ * (src/eval-tools.ts).
1628
+ * - `reflection` → the model configured by a custom `SurfaceProposer` or
1629
+ * external optimization engine.
1630
+ * - `worker` → the dispatch model the agent itself calls — the model an
1631
+ * `AgentProfile` declares.
1632
+ * - `analyst` → the LLM behind `analyzeRuns` / analyst-registry kinds.
1633
+ * - `verifier` → completion-verifier / objective-checker model.
1634
+ * - campaign cells thread `judges` + driver models the same way; that wiring
1635
+ * lands with the campaign surface, not here.
1636
+ *
1637
+ * `resolveSeat` is the only read path: an unset seat with no explicit fallback
1638
+ * throws — a model id is a budget decision, never a silent default.
1639
+ */
1640
+ interface ModelSeats {
1641
+ /** The model under evaluation — what the agent itself dispatches with. */
1642
+ worker?: string;
1643
+ /** Judge-panel model ids — thread into `ensembleJudge({ models })`. */
1644
+ judges?: string[];
1645
+ /** Analyst model — `analyzeRuns` / analyst-registry LLM calls. */
1646
+ analyst?: string;
1647
+ /** Reflection or candidate-generation model. */
1648
+ reflection?: string;
1649
+ /** Verifier model — completion/objective checking. */
1650
+ verifier?: string;
1651
+ }
1652
+ type SeatName = keyof ModelSeats;
1653
+ /**
1654
+ * Tier presets — plain data, swap or spread freely.
1655
+ *
1656
+ * A preset names REQUESTED ids, and a requested id is NOT a guarantee of
1657
+ * family, of provider, or of liveness. A routing gateway can accept any id
1658
+ * below and answer from a different model on HTTP 200, with only the response
1659
+ * body's `model` field betraying the swap. The served assertion is the
1660
+ * guarantee: gate a run on `assertModelsServed({ probe: true })` and assert
1661
+ * `assertServedModel` per call. `assertCrossFamily` over these ids proves the
1662
+ * configuration is diverse; only `assertCrossFamilyServed` over the ids that
1663
+ * ANSWERED proves the run was.
1664
+ *
1665
+ * `economy` names ids a live router probe answered from the provider their
1666
+ * name implies, so the judge trio spanned three provider families (deepseek /
1667
+ * zhipu / google) as configured and, at that probe, as served. A preset is
1668
+ * only as good as its last probe: ids go dead and start resolving elsewhere
1669
+ * without notice, so re-probe rather than trusting this list.
1670
+ *
1671
+ * `frontier` is deliberately EMPTY: entitled frontier ids vary per router
1672
+ * account, and a hardcoded claude/gpt-5 id 401s on keys that lack it. Supply
1673
+ * your own: `{ ...seatPresets.frontier, worker: '<your-frontier-id>', … }` —
1674
+ * `resolveSeat` throws on every seat you haven't filled.
1675
+ */
1676
+ declare const seatPresets: Record<'economy' | 'frontier', ModelSeats>;
1677
+ /**
1678
+ * Read one seat. Blank strings and empty arrays count as unset (env-var
1679
+ * plumbing produces them); malformed values (non-string seat, non-array or
1680
+ * blank-entry `judges`) throw `ValidationError`. When the seat is unset, an
1681
+ * explicit `fallback` is returned (`[fallback]` for `judges` — a one-model
1682
+ * panel); without one, `SeatUnsetError`.
1683
+ */
1684
+ declare function resolveSeat(seats: ModelSeats, seat: 'judges', fallback?: string): string[];
1685
+ declare function resolveSeat(seats: ModelSeats, seat: Exclude<SeatName, 'judges'>, fallback?: string): string;
1686
+ declare function resolveSeat(seats: ModelSeats, seat: SeatName, fallback?: string): string | string[];
1687
+ /**
1688
+ * Versioned prompt registry.
1689
+ *
1690
+ * Every prompt used in an eval run is registered with an explicit version.
1691
+ * Reports include the content hash so A/B compares are rigorous: if the
1692
+ * hash changes between two reports, the prompt actually changed; if it
1693
+ * matches, the variance is elsewhere.
1694
+ *
1695
+ * Hash is SHA-256(content), truncated to 12 hex chars for readability.
1696
+ * Uses the Web Crypto API (works in Workers, Node 22+, browsers).
1697
+ */
1698
+ interface PromptHandle {
1699
+ /** Stable human-readable id, e.g. 'browser.system' */
1700
+ id: string;
1701
+ /** Caller-chosen version string, e.g. 'v3' or '2026-04-20' */
1702
+ version: string;
1703
+ /** SHA-256 of content, 12-hex-char prefix */
1704
+ hash: string;
1705
+ /** Full prompt body */
1706
+ content: string;
1707
+ }
1708
+ declare class PromptRegistry {
1709
+ private readonly entries;
1710
+ /**
1711
+ * Register a prompt. Re-registering the same id+version with DIFFERENT
1712
+ * content throws — versions are immutable. Re-registering with the SAME
1713
+ * content is a no-op (idempotent).
1714
+ */
1715
+ register(id: string, version: string, content: string): Promise<PromptHandle>;
1716
+ /** Look up a registered prompt. Throws if unknown — no implicit defaults. */
1717
+ get(id: string, version: string): PromptHandle;
1718
+ /** Return all versions of an id, newest-first (lex-descending on version). */
1719
+ listVersions(id: string): PromptHandle[];
1720
+ /** Snapshot the whole registry — useful for including in reports. */
1721
+ list(): PromptHandle[];
1722
+ /** Verify a hash against registered content. Returns null if not found. */
1723
+ verifyHash(id: string, version: string, expectedHash: string): boolean | null;
1724
+ }
1725
+ /** SHA-256(content) → first 12 hex chars. Stable across runtimes. */
1726
+ declare function hashContent(content: string): Promise<string>;
1727
+ /**
1728
+ * Reference replay — score an agent against withheld historical outcomes.
1729
+ *
1730
+ * This is the generic version of the public-audit replay pattern:
1731
+ * run a candidate system on an old task, keep the reference answers hidden
1732
+ * until after execution, then score recall/precision and gate promotion
1733
+ * across train/dev/test/holdout splits.
1734
+ */
1735
+ type ReferenceReplaySplit = 'train' | 'dev' | 'test' | 'holdout';
1736
+ interface ReferenceReplayItem {
1737
+ id: string;
1738
+ title: string;
1739
+ description?: string;
1740
+ severity?: string;
1741
+ tags?: string[];
1742
+ weight?: number;
1743
+ }
1744
+ interface ReferenceReplayCandidate {
1745
+ id: string;
1746
+ title: string;
1747
+ description?: string;
1748
+ severity?: string;
1749
+ tags?: string[];
1750
+ metadata?: Record<string, unknown>;
1751
+ }
1752
+ interface ReferenceReplayMatch {
1753
+ scenarioId: string;
1754
+ referenceId: string;
1755
+ candidateId: string | null;
1756
+ score: number;
1757
+ matched: boolean;
1758
+ weight: number;
1759
+ reason: string;
1760
+ }
1761
+ interface ReferenceReplayScenarioScore {
1762
+ scenarioId: string;
1763
+ split: ReferenceReplaySplit;
1764
+ matched: number;
1765
+ total: number;
1766
+ falsePositives: number;
1767
+ matchedWeight: number;
1768
+ totalWeight: number;
1769
+ precision: number;
1770
+ recall: number;
1771
+ f1: number;
1772
+ matches: ReferenceReplayMatch[];
1773
+ }
1774
+ interface ReferenceReplayAggregate {
1775
+ matched: number;
1776
+ total: number;
1777
+ falsePositives: number;
1778
+ matchedWeight: number;
1779
+ totalWeight: number;
1780
+ precision: number;
1781
+ recall: number;
1782
+ f1: number;
1783
+ weightedRecall: number;
1784
+ }
1785
+ interface ReferenceReplayScore {
1786
+ scenarios: ReferenceReplayScenarioScore[];
1787
+ aggregate: ReferenceReplayAggregate;
1788
+ bySplit: Partial<Record<ReferenceReplaySplit, ReferenceReplayAggregate>>;
1789
+ }
1790
+ interface ReferenceReplayCaseRun<Input = unknown> {
1791
+ caseId: string;
1792
+ split: ReferenceReplaySplit;
1793
+ input: Input;
1794
+ metadata?: Record<string, unknown>;
1795
+ references: ReferenceReplayItem[];
1796
+ candidates: ReferenceReplayCandidate[];
1797
+ score: ReferenceReplayScenarioScore;
1798
+ durationMs: number;
1799
+ error?: string;
1800
+ }
1801
+ interface ReferenceReplayRun<Input = unknown> {
1802
+ id: string;
1803
+ variantId?: string;
1804
+ startedAt: number;
1805
+ completedAt: number;
1806
+ durationMs: number;
1807
+ cases: ReferenceReplayCaseRun<Input>[];
1808
+ score: ReferenceReplayScore;
1809
+ metadata?: Record<string, unknown>;
1810
+ }
1811
+ interface SteeringRolePrompt {
1812
+ system?: string;
1813
+ append?: string;
1814
+ }
1815
+ interface SteeringBundle {
1816
+ id: string;
1817
+ coderPrompt?: string;
1818
+ continuePrompt?: string;
1819
+ reviewerPrompts?: Record<string, string>;
1820
+ skills?: string[];
1821
+ rolePrompts?: Record<string, SteeringRolePrompt>;
1822
+ metadata?: Record<string, unknown>;
1823
+ }
1824
+ interface RunScore {
1825
+ success: number;
1826
+ goalProgress: number;
1827
+ repoGroundedness: number;
1828
+ driftPenalty: number;
1829
+ toolUseQuality: number;
1830
+ patchQuality: number;
1831
+ testReality: number;
1832
+ finalGate: number;
1833
+ reviewerBlockers: number;
1834
+ costUsd: number;
1835
+ wallSeconds: number;
1836
+ notes?: string[];
1837
+ }
1838
+ type RunScoreWeights = Omit<RunScore, 'notes'>;
1839
+ declare function aggregateRunScore(score: RunScore, weights?: Partial<RunScoreWeights>): number;
1840
+ declare function clamp01(value: number): number;
1841
+ type SteeringOptimizerBackend = 'pairwise';
1842
+ interface SteeringOptimizationRow {
1843
+ variantId: string;
1844
+ scenarioId: string;
1845
+ bundle: SteeringBundle;
1846
+ score: RunScore;
1847
+ metadata?: Record<string, unknown>;
1848
+ }
1849
+ interface SteeringOptimizationSelector {
1850
+ backend: SteeringOptimizerBackend;
1851
+ signature?: string;
1852
+ labels?: string[];
1853
+ rationale?: string;
1854
+ }
1855
+ interface SteeringOptimizationResult {
1856
+ backend: SteeringOptimizerBackend;
1857
+ recommendedVariantId: string;
1858
+ rationale: string;
1859
+ rankings: Array<{
1860
+ variantId: string;
1861
+ mean: number;
1862
+ runs: number;
1863
+ }>;
1864
+ selector?: SteeringOptimizationSelector;
1865
+ skipped?: boolean;
1866
+ }
1867
+ interface SteeringOptimizerConfig {
1868
+ weights?: Partial<RunScoreWeights>;
1869
+ }
1870
+ /**
1871
+ * Rank already-evaluated steering variants.
1872
+ *
1873
+ * Model-backed candidate generation belongs to the official GEPA campaign
1874
+ * integration. This class only performs the deterministic selection it owns.
1875
+ */
1876
+ declare class PairwiseSteeringOptimizer {
1877
+ optimize(rows: SteeringOptimizationRow[], config?: SteeringOptimizerConfig): SteeringOptimizationResult;
1878
+ }
1879
+ type CounterfactualMutation = {
1880
+ kind: 'swap-model';
1881
+ at: number;
1882
+ newModel: string;
1883
+ } | {
1884
+ kind: 'swap-tool-result';
1885
+ at: number;
1886
+ newResult: unknown;
1887
+ } | {
1888
+ kind: 'truncate-after';
1889
+ at: number;
1890
+ } | {
1891
+ kind: 'inject-system-message';
1892
+ at: number;
1893
+ content: string;
1894
+ } | {
1895
+ kind: 'custom';
1896
+ at: number;
1897
+ describe: string;
1898
+ apply: (step: TrajectoryStep) => TrajectoryStep;
1899
+ };
1900
+ interface CounterfactualContext {
1901
+ originalRunId: string;
1902
+ originalTrajectory: Trajectory;
1903
+ /** Steps up to (but not including) the mutation point — the prefix the
1904
+ * replayed agent inherits as its prior conversation/tool history. */
1905
+ prefix: TrajectoryStep[];
1906
+ mutation: CounterfactualMutation;
1907
+ /** Pre-applied mutation on the step at `mutation.at`. Consumers use this
1908
+ * as the FIRST step the replayed agent emits (they decide whether to
1909
+ * re-emit it or continue from there). */
1910
+ mutatedStep: TrajectoryStep;
1911
+ }
1912
+ interface CounterfactualResult {
1913
+ counterfactualRunId: string;
1914
+ originalRunId: string;
1915
+ mutation: CounterfactualMutation;
1916
+ /** Structured delta summary — caller can extend via scoring. */
1917
+ delta: {
1918
+ originalOutcomeScore: number | null;
1919
+ counterfactualOutcomeScore: number | null;
1920
+ deltaScore: number | null;
1921
+ };
1922
+ }
1923
+ interface CounterfactualRunner {
1924
+ /**
1925
+ * Execute the agent from `ctx.prefix` with the mutation applied.
1926
+ * MUST emit spans into the provided emitter so they become part of
1927
+ * the counterfactual run. MUST call emitter.endRun() with a verdict.
1928
+ */
1929
+ executeFrom: (ctx: CounterfactualContext, emitter: TraceEmitter) => Promise<void>;
1930
+ }
1931
+ /** One commit's measurement of one (scenario, profile) cell. */
1932
+ interface ScorecardEntry {
1933
+ commitSha: string;
1934
+ /** ISO timestamp the entry was recorded. */
1935
+ timestamp: string;
1936
+ /** Per-seed (or per-rep) scores for this cell at this commit. */
1937
+ scores: number[];
1938
+ /** Median of `scores` — the cell's headline score for the commit. */
1939
+ composite: number;
1940
+ /** Per-dimension means, when the runs carried a judge breakdown. */
1941
+ perDimension?: Record<string, number>;
1942
+ /** RunRecord ids folded into this entry — provenance. */
1943
+ runIds: string[];
1944
+ }
1945
+ /** A (scenario, profile) cell and its commit-ordered score timeline. */
1946
+ interface ScorecardCell {
1947
+ scenarioId: string;
1948
+ profileHash: string;
1949
+ /** Model id — denormalised from the profile for readable filtering. */
1950
+ model: string;
1951
+ timeline: ScorecardEntry[];
1952
+ }
1953
+ /** The folded scorecard: every cell, plus the profile definitions by hash. */
1954
+ interface Scorecard {
1955
+ cells: ScorecardCell[];
1956
+ /** Profile definitions seen — keeps the scorecard self-describing. */
1957
+ profiles: Record<string, AgentProfile>;
1958
+ }
1959
+ /** One append-only log line — a single cell's entry for a single commit. */
1960
+ interface ScorecardLogLine {
1961
+ scenarioId: string;
1962
+ profileHash: string;
1963
+ model: string;
1964
+ profile: AgentProfile;
1965
+ entry: ScorecardEntry;
1966
+ }
1967
+ interface RecordRunsOptions {
1968
+ /** The profile that produced these runs — keys the cell. */
1969
+ profile: AgentProfile;
1970
+ commitSha: string;
1971
+ /** Defaults to `new Date().toISOString()`. */
1972
+ timestamp?: string;
1973
+ }
1974
+ /**
1975
+ * Fold a benchmark's `RunRecord[]` into per-cell scorecard log lines — one
1976
+ * line per scenario the runs cover. All runs are attributed to the single
1977
+ * `profile` in `opts` (the harness ran them under it); the cell key is
1978
+ * `(scenarioId, agentProfileHash(profile))`.
1979
+ */
1980
+ declare function recordRuns(runs: RunRecord[], opts: RecordRunsOptions): ScorecardLogLine[];
1981
+ /** Record runs and append them to the log in one call. Returns the lines. */
1982
+ declare function recordRunsToScorecard(logPath: string, runs: RunRecord[], opts: RecordRunsOptions): ScorecardLogLine[];
1983
+ /**
1984
+ * Fold the JSONL log into a queryable `Scorecard`. A missing file yields an
1985
+ * empty scorecard; a malformed line is skipped — a corrupt append never
1986
+ * breaks the read. Each cell's timeline is sorted chronologically.
1987
+ */
1988
+ declare function loadScorecard(logPath: string): Scorecard;
1989
+ type CellVerdict = 'improved' | 'regressed' | 'flat' | 'new';
1990
+ interface ScorecardCellDiff {
1991
+ scenarioId: string;
1992
+ profileHash: string;
1993
+ model: string;
1994
+ verdict: CellVerdict;
1995
+ /** Composite of the latest entry. */
1996
+ current: number;
1997
+ /** Composite of the comparison entry — `null` when `verdict === 'new'`. */
1998
+ baseline: number | null;
1999
+ /** `current − baseline` — `null` when new. */
2000
+ delta: number | null;
2001
+ /** Cohen's d of current vs baseline samples — `null` when new or n < 2. */
2002
+ cohensD: number | null;
2003
+ /** Welch's t-test p-value — `null` when new or n < 2. */
2004
+ pValue: number | null;
2005
+ currentCommit: string;
2006
+ baselineCommit: string | null;
2007
+ }
2008
+ interface ScorecardDiff {
2009
+ cells: ScorecardCellDiff[];
2010
+ summary: {
2011
+ improved: number;
2012
+ regressed: number;
2013
+ flat: number;
2014
+ new: number;
2015
+ };
2016
+ }
2017
+ interface DiffScorecardOptions {
2018
+ /** Compare each cell against this commit instead of its immediate predecessor. */
2019
+ baselineCommit?: string;
2020
+ /** |Cohen's d| at/above which a move counts as real. Default 0.5. */
2021
+ minEffect?: number;
2022
+ /** p-value at/below which a move is significant. Default 0.05. */
2023
+ maxP?: number;
2024
+ /**
2025
+ * |delta| at/above which a move counts when statistics are unavailable
2026
+ * (a cell with fewer than 2 samples on either side). Default 0.05.
2027
+ */
2028
+ minDelta?: number;
2029
+ }
2030
+ /**
2031
+ * Compare the latest entry of every cell against its predecessor (or against
2032
+ * `baselineCommit`) and classify the move. A move is `improved`/`regressed`
2033
+ * only when it clears both the effect-size and significance gates; otherwise
2034
+ * `flat`. Cells with no prior entry are `new`.
2035
+ */
2036
+ declare function diffScorecard(scorecard: Scorecard, opts?: DiffScorecardOptions): ScorecardDiff;
2037
+ /**
2038
+ * Render a scorecard diff as a human-readable report — the block a feature
2039
+ * PR prints. Regressions are listed first; flat cells are summarised, not
2040
+ * enumerated.
2041
+ */
2042
+ declare function formatScorecardDiff(diff: ScorecardDiff): string;
2043
+ /**
2044
+ * Minimal structural span the checker reads. The eval-side `Span`
2045
+ * (trace/schema) and the output of {@link contractSpansFromOtlp} satisfy it.
2046
+ */
2047
+ interface ContractSpan {
2048
+ spanId?: string;
2049
+ parentSpanId?: string | null;
2050
+ name?: string;
2051
+ /** Eval kinds (`tool`, `llm`, ...) or contract kinds (`TOOL`, `LLM`, ...). */
2052
+ kind?: string;
2053
+ startedAt?: number;
2054
+ endedAt?: number;
2055
+ status?: string;
2056
+ error?: string;
2057
+ /** Typed field on eval-side ToolSpans; OTLP flattenings drop it (see
2058
+ * {@link contractSpanToolName}). */
2059
+ toolName?: string;
2060
+ /** Typed field on eval-side LlmSpans. */
2061
+ model?: string;
2062
+ inputTokens?: number;
2063
+ outputTokens?: number;
2064
+ /** Typed field on eval-side ToolSpans. */
2065
+ args?: unknown;
2066
+ /** False when the source observed the call but did not capture its arguments. */
2067
+ argsCaptured?: boolean;
2068
+ attributes?: Record<string, unknown>;
2069
+ }
2070
+ /**
2071
+ * The run a `run` rule reads. The eval-side `Run` satisfies it. When no run is
2072
+ * passed, the checker derives it from the trace's single root span; a trace
2073
+ * with no unique root leaves the run unknown and every `run` rule fails.
2074
+ */
2075
+ interface ContractRun {
2076
+ status?: string;
2077
+ startedAt?: number;
2078
+ endedAt?: number;
2079
+ }
2080
+ /** JSON-safe RegExp form — what the builder normalizes RegExp matchers to. */
2081
+ interface SerializedRegex {
2082
+ $regex: string;
2083
+ flags: string;
2084
+ }
2085
+ /** Matches when the value equals one of the listed strings. */
2086
+ interface OneOfMatcher {
2087
+ oneOf: string[];
2088
+ }
2089
+ type TextMatcher = string | RegExp | SerializedRegex | OneOfMatcher;
2090
+ /**
2091
+ * Proposition over one span. All specified fields must match (AND); `not`
2092
+ * must NOT match. At least one field is required — an empty predicate would
2093
+ * match every span and is rejected.
2094
+ *
2095
+ * `kind` compares against the span's contract kind (`TOOL`, `LLM`, `AGENT`,
2096
+ * ...), resolved by agent-trace-contract's classifier, so eval kinds
2097
+ * (`tool`, `llm`) and OTLP spans that declare no kind are read the same way.
2098
+ *
2099
+ * `tool` resolves through {@link contractSpanToolName}; `model` through the
2100
+ * typed `model` field and the gen_ai model attributes.
2101
+ *
2102
+ * `attr` values match by strict equality, or regex-test when the value is a
2103
+ * RegExp/SerializedRegex and the attribute is a string. Structured attribute
2104
+ * values need `custom`, or an `argument` rule for tool arguments.
2105
+ */
2106
+ interface SpanPredicate {
2107
+ name?: TextMatcher;
2108
+ tool?: TextMatcher;
2109
+ model?: TextMatcher;
2110
+ kind?: SpanKind;
2111
+ attr?: Record<string, unknown>;
2112
+ not?: SpanPredicate;
2113
+ custom?: (span: ContractSpan) => boolean;
2114
+ /** Stamped by the builder when `custom` is present. Survives JSON while
2115
+ * the function does not, so evaluation of a deserialized contract throws
2116
+ * instead of silently dropping the check. */
2117
+ requiresCustom?: true;
2118
+ }
2119
+ declare const ORDER_MODES: readonly ["start-order", "finish-before-start", "all-occurrences"];
2120
+ type OrderMode = (typeof ORDER_MODES)[number];
2121
+ declare const ARGUMENT_OCCURRENCES: readonly ["first", "last", "any", "all"];
2122
+ type ArgumentOccurrence = (typeof ARGUMENT_OCCURRENCES)[number];
2123
+ declare const ARGUMENT_TYPES: readonly ["string", "number", "boolean", "object", "array", "null"];
2124
+ type ArgumentType = (typeof ARGUMENT_TYPES)[number];
2125
+ type ArgumentCheck = {
2126
+ op: 'exists';
2127
+ } | {
2128
+ op: 'equals';
2129
+ value: unknown;
2130
+ } | {
2131
+ op: 'oneOf';
2132
+ values: unknown[];
2133
+ } | {
2134
+ op: 'type';
2135
+ type: ArgumentType;
2136
+ };
2137
+ /** A tool whose calls change state outside the run. */
2138
+ interface RetryWrite {
2139
+ tool: string;
2140
+ /** RFC 6901 JSON Pointer to the idempotency key in the call's arguments.
2141
+ * Without it, a repeated call of this tool always fails. */
2142
+ idempotencyKey?: string;
2143
+ }
2144
+ interface RunChecks {
2145
+ /** The run's status is `completed` and its end time is recorded. A failed
2146
+ * or aborted run fails this check. */
2147
+ requireCompleted?: boolean;
2148
+ /** The run's status must be one of these. */
2149
+ allowedStatuses?: RunStatus[];
2150
+ /** End minus start, in milliseconds. An unknown duration fails. */
2151
+ maxDurationMs?: number;
2152
+ }
2153
+ type ContractRule = {
2154
+ kind: 'always' | 'never' | 'eventually';
2155
+ label: string;
2156
+ p: SpanPredicate;
2157
+ } | {
2158
+ kind: 'precedes';
2159
+ label: string;
2160
+ a: SpanPredicate;
2161
+ b: SpanPredicate;
2162
+ order?: OrderMode;
2163
+ } | {
2164
+ kind: 'neverUnless';
2165
+ label: string;
2166
+ p: SpanPredicate;
2167
+ prior: SpanPredicate;
2168
+ order?: OrderMode;
2169
+ } | {
2170
+ kind: 'atMost';
2171
+ label: string;
2172
+ p: SpanPredicate;
2173
+ max: number;
2174
+ } | {
2175
+ kind: 'tokensAtMost';
2176
+ label: string;
2177
+ p: SpanPredicate;
2178
+ max: number;
2179
+ } | ({
2180
+ kind: 'run';
2181
+ label: string;
2182
+ } & RunChecks) | {
2183
+ kind: 'argument';
2184
+ label: string;
2185
+ p: SpanPredicate;
2186
+ /** RFC 6901 JSON Pointer into the call's arguments; `''` is the whole value. */
2187
+ pointer: string;
2188
+ check: ArgumentCheck;
2189
+ /** Which matching calls must satisfy the check. Default `all`. */
2190
+ occurrence?: ArgumentOccurrence;
2191
+ } | {
2192
+ /**
2193
+ * The tools the harness offered the model (the OTel GenAI
2194
+ * `gen_ai.tool.definitions` attribute) are recorded, every tool call is
2195
+ * one of them, and, when `declared` is set, every offered tool is
2196
+ * declared. A trace that records no offered tools fails: what the
2197
+ * harness enforced is unknown.
2198
+ */
2199
+ kind: 'toolsOffered';
2200
+ label: string;
2201
+ declared?: string[];
2202
+ } | {
2203
+ /**
2204
+ * A tool called again with the same arguments repeats its side effect.
2205
+ * A repeated `reads` call passes; a repeated `writes` call passes only
2206
+ * when every call carries the same idempotency key; a repeated call of
2207
+ * any other tool fails, because its side effect is unknown.
2208
+ */
2209
+ kind: 'retrySafe';
2210
+ label: string;
2211
+ reads?: string[];
2212
+ writes?: RetryWrite[];
2213
+ };
2214
+ /** One legitimate alternate path. The contract passes only when the base
2215
+ * rules pass and at least one alternative passes completely. */
2216
+ interface ContractAlternative {
2217
+ id: string;
2218
+ rules: ContractRule[];
2219
+ }
2220
+ /** Serializable plain object — `traceContract(name)....build()` output. */
2221
+ interface TraceContract {
2222
+ name: string;
2223
+ /** What the contract protects, for reports and `explainTraceContract`. */
2224
+ description?: string;
2225
+ /** Evaluate only the span matching `root` and its descendants. Exactly one
2226
+ * span must match; zero or several make every rule an error. */
2227
+ scope?: {
2228
+ root: SpanPredicate;
2229
+ };
2230
+ rules: ContractRule[];
2231
+ alternatives?: ContractAlternative[];
2232
+ }
2233
+ type ContractStatus = 'pass' | 'fail' | 'error';
2234
+ interface ContractViolation {
2235
+ rule: string;
2236
+ spanId?: string;
2237
+ detail: string;
2238
+ }
2239
+ /** What happened to one rule. `error` means it could not be evaluated. */
2240
+ interface ContractRuleExecution {
2241
+ rule: string;
2242
+ /** Set for rules inside `alternatives`. */
2243
+ alternative?: string;
2244
+ status: ContractStatus;
2245
+ violations: number;
2246
+ error?: string;
2247
+ }
2248
+ interface ContractVerdict extends DefaultVerdict {
2249
+ /** Contract name — keys this verdict in multi-contract reports. */
2250
+ contract: string;
2251
+ status: ContractStatus;
2252
+ /** `status === 'pass'`. */
2253
+ valid: boolean;
2254
+ /** Fraction of rules that passed, in [0, 1]. An errored rule is not a pass. */
2255
+ score: number;
2256
+ /** 0|1 per evaluated base rule, keyed by label, plus `anyOf` when the
2257
+ * contract has alternatives. Errored rules are absent, never 0. */
2258
+ scores: Record<string, number>;
2259
+ violations: ContractViolation[];
2260
+ ruleExecutions: ContractRuleExecution[];
2261
+ /** Why rules errored, one line per errored rule or scope failure. */
2262
+ errors: string[];
2263
+ }
2264
+ interface ContractCheckResult {
2265
+ verdicts: ContractVerdict[];
2266
+ /** `error` if any verdict errored, else `fail` if any failed, else `pass`. */
2267
+ status: ContractStatus;
2268
+ allValid: boolean;
2269
+ }
2270
+ interface EvaluateTraceContractOptions {
2271
+ /** The run `run` rules read. Ignored under `scope`, where the scope root is the run. */
2272
+ run?: ContractRun;
2273
+ }
2274
+ interface OrderOptions {
2275
+ order?: OrderMode;
2276
+ }
2277
+ interface ArgumentRuleOptions {
2278
+ pointer: string;
2279
+ check: ArgumentCheck;
2280
+ occurrence?: ArgumentOccurrence;
2281
+ }
2282
+ declare class TraceContractBuilder {
2283
+ private readonly name;
2284
+ private readonly rules;
2285
+ private readonly alternativeList;
2286
+ private scopeRoot;
2287
+ constructor(name: string);
2288
+ /** Every span in the trace must satisfy `p`. */
2289
+ always(p: SpanPredicate, label?: string): this;
2290
+ /** No span in the trace may satisfy `p`. */
2291
+ never(p: SpanPredicate, label?: string): this;
2292
+ /** At least one span in the trace must satisfy `p`. */
2293
+ eventually(p: SpanPredicate, label?: string): this;
2294
+ /** Both `a` and `b` must occur, with `a` first under `options.order`
2295
+ * (default `start-order`). For "every `b` needs an earlier `a`, and a run
2296
+ * without `b` passes", use {@link neverUnless}. */
2297
+ precedes(a: SpanPredicate, b: SpanPredicate, label?: string, options?: OrderOptions): this;
2298
+ /** Every `p`-match needs an earlier `prior`-match; a run without `p` passes. */
2299
+ neverUnless(p: SpanPredicate, prior: SpanPredicate, label?: string, options?: OrderOptions): this;
2300
+ /** At most `max` spans may satisfy `p`. */
2301
+ atMost(p: SpanPredicate, max: number, label?: string): this;
2302
+ /** Spans satisfying `p` may use at most `max` input+output tokens in total. */
2303
+ tokensAtMost(p: SpanPredicate, max: number, label?: string): this;
2304
+ /** Terminal status and duration of the run. */
2305
+ run(checks: RunChecks, label?: string): this;
2306
+ /** A JSON Pointer check on the arguments of calls matching `p`. */
2307
+ argument(p: SpanPredicate, options: ArgumentRuleOptions, label?: string): this;
2308
+ /** The trace records the tools offered to the model, every call is one of
2309
+ * them, and, with `declared`, every offered tool is declared. */
2310
+ toolsOffered(declared?: string[], label?: string): this;
2311
+ /** No call repeats a side effect that is not proven safe to repeat. */
2312
+ retrySafe(options: {
2313
+ reads?: string[];
2314
+ writes?: RetryWrite[];
2315
+ }, label?: string): this;
2316
+ /** Evaluate only the subtree rooted at the one span matching `root`. */
2317
+ scope(root: SpanPredicate): this;
2318
+ /** A named alternate path; at least one alternative must pass completely. */
2319
+ alternative(id: string, define: (branch: TraceContractBuilder) => TraceContractBuilder): this;
2320
+ build(): TraceContract;
2321
+ private where;
2322
+ private add;
2323
+ }
2324
+ declare function traceContract(name: string): TraceContractBuilder;
2325
+ /**
2326
+ * Evaluate one contract over a span sequence. Pure and synchronous — works on
2327
+ * `Span[]` from a TraceStore, `contractSpansFromOtlp` output, or any array
2328
+ * satisfying `ContractSpan`. Throws
2329
+ * `ValidationError` for a malformed contract; a rule that cannot be evaluated
2330
+ * is reported as `error` in the verdict instead.
2331
+ */
2332
+ declare function evaluateTraceContract(contract: TraceContract, spans: readonly ContractSpan[], options?: EvaluateTraceContractOptions): ContractVerdict;
2333
+ /**
2334
+ * Evaluate many contracts over one span sequence. Throws on an empty
2335
+ * contract list — `allValid: true` over zero contracts is a silent pass.
2336
+ */
2337
+ declare function checkTraceContracts(spans: readonly ContractSpan[], contracts: readonly TraceContract[], options?: EvaluateTraceContractOptions): ContractCheckResult;
2338
+ /** Plain-language statement of everything a contract checks, one rule per line. */
2339
+ declare function explainTraceContract(contract: TraceContract): string;
2340
+ interface ToolArgumentSpec {
2341
+ tool: string;
2342
+ /** RFC 6901 JSON Pointer into the call's arguments; `''` is the whole value. */
2343
+ pointer: string;
2344
+ /** Which calls must satisfy the check. Default `all`. */
2345
+ occurrence?: ArgumentOccurrence;
2346
+ /** Exactly one of `exists`, `equals`, `oneOf`, `type`. */
2347
+ exists?: true;
2348
+ equals?: unknown;
2349
+ oneOf?: unknown[];
2350
+ type?: ArgumentType;
2351
+ }
2352
+ interface ToolsSpec {
2353
+ /** Each must be called at least once. */
2354
+ required?: string[];
2355
+ /** None may be called. */
2356
+ forbidden?: string[];
2357
+ /** Every tool call must name one of these; a call without a name fails. */
2358
+ allowed?: string[];
2359
+ /** Ceiling on tool calls of any name. */
2360
+ maxCalls?: number;
2361
+ /** Ceiling per tool name. */
2362
+ maxCallsPerTool?: Record<string, number>;
2363
+ /** Each tool is called, and each comes before the next under `orderMode`. */
2364
+ requiredOrder?: string[];
2365
+ /** Default `start-order`. */
2366
+ orderMode?: OrderMode;
2367
+ arguments?: ToolArgumentSpec[];
2368
+ /** The trace records the tools the harness offered the model
2369
+ * (`gen_ai.tool.definitions`), every call is one of them, and, with
2370
+ * `allowed`, every offered tool is allowed. */
2371
+ enforced?: true;
2372
+ }
2373
+ interface RetriesSpec {
2374
+ /** Tools that change nothing outside the run: repeating a call is safe. */
2375
+ reads?: string[];
2376
+ /** Tools that change state outside the run: a repeated call must carry one
2377
+ * idempotency key. Any other tool called twice with the same arguments fails. */
2378
+ writes?: RetryWrite[];
2379
+ }
2380
+ interface LlmSpec {
2381
+ maxCalls?: number;
2382
+ /** Input plus output tokens across LLM calls; an unrecorded count fails. */
2383
+ maxTotalTokens?: number;
2384
+ /** Every LLM call must name one of these models; a call without one fails. */
2385
+ allowedModels?: string[];
2386
+ }
2387
+ /** The checks one path carries: the base contract or one alternative. */
2388
+ interface PathSpec {
2389
+ run?: RunChecks;
2390
+ tools?: ToolsSpec;
2391
+ llm?: LlmSpec;
2392
+ retries?: RetriesSpec;
2393
+ /** Low-level rules in their serialized form. */
2394
+ rules?: ContractRule[];
2395
+ }
2396
+ interface AlternativeSpec extends PathSpec {
2397
+ id: string;
2398
+ }
2399
+ interface TraceContractSpec extends PathSpec {
2400
+ name: string;
2401
+ description?: string;
2402
+ /** Check only the one span matching `root` and its descendants: one
2403
+ * sub-agent (`{ "kind": "AGENT", "name": "researcher" }`) or one search
2404
+ * node (`{ "attr": { "agent.branch.id": "n7" } }`). */
2405
+ scope?: {
2406
+ root: SpanPredicate;
2407
+ };
2408
+ /** At least one alternative must pass completely, as well as the base checks. */
2409
+ alternatives?: {
2410
+ anyOf: AlternativeSpec[];
2411
+ };
2412
+ }
2413
+ /**
2414
+ * Parse and compile a declarative contract. Throws `ValidationError` naming
2415
+ * the path of the first problem: an unknown key, a wrong type, an unknown
2416
+ * status, or a contract that checks nothing.
2417
+ */
2418
+ declare function compileTraceContractSpec(input: unknown): TraceContract;
2419
+ interface ContractLintFinding {
2420
+ /** `error`: the contract can never pass, or contradicts itself.
2421
+ * `warning`: the contract works but likely does not say what was meant. */
2422
+ level: 'error' | 'warning';
2423
+ code: string;
2424
+ path: string;
2425
+ message: string;
2426
+ }
2427
+ /**
2428
+ * Find contradictions and likely mistakes in a declarative contract. Parses
2429
+ * strictly first, so a malformed spec throws instead of linting.
2430
+ */
2431
+ declare function lintTraceContractSpec(input: unknown): ContractLintFinding[];
2432
+ interface ActionExecutionPolicy {
2433
+ allowedTypes?: string[];
2434
+ blockedTypes?: string[];
2435
+ alwaysRequireApprovalTypes?: string[];
2436
+ autoApproveTypes?: string[];
2437
+ requireApprovalForExternalSideEffects?: boolean;
2438
+ requireApprovalAboveCostUsd?: number;
2439
+ maxActionCostUsd?: number;
2440
+ remainingBudgetUsd?: number;
2441
+ expectedOutcomeRequired?: boolean;
2442
+ killCriteriaRequired?: boolean;
2443
+ }
2444
+ interface ActionPolicyDecision {
2445
+ allowed: boolean;
2446
+ blocked: boolean;
2447
+ requiresApproval: boolean;
2448
+ reasons: string[];
2449
+ label?: FeedbackLabel;
2450
+ }
2451
+ declare function evaluateActionPolicy(action: ProposedSideEffect, policy?: ActionExecutionPolicy, options?: {
2452
+ createdAt?: string;
2453
+ }): ActionPolicyDecision;
2454
+ interface CanaryLeak {
2455
+ scenarioId: string;
2456
+ canary: string;
2457
+ runId?: string;
2458
+ evidence: string;
2459
+ }
2460
+ declare function checkCanaries(output: string, scenarios: DatasetScenario[]): CanaryLeak[];
2461
+ type DeployFamily = 'frontend-static' | 'nextjs' | 'remix' | 'fullstack-ts';
2462
+ interface DeployRunResult {
2463
+ ok: boolean;
2464
+ /** Stdout/stderr tail surfaced as evidence. Bounded in caller. */
2465
+ output?: string;
2466
+ /** Wall-clock duration of the build command. */
2467
+ durationMs?: number;
2468
+ /** Path to artifact directory the runner expects (dist/, .next/, build/, etc). */
2469
+ artifactDir?: string;
2470
+ /** True iff artifactDir contains the family's expected entry point. */
2471
+ artifactValid?: boolean;
2472
+ }
2473
+ interface DeployRunner {
2474
+ /** Run the production build. The runner owns command + cwd. */
2475
+ run(): Promise<DeployRunResult>;
2476
+ }
2477
+ interface DeployGateLayerInput {
2478
+ /** Build the runner per call. */
2479
+ runner: () => DeployRunner | Promise<DeployRunner>;
2480
+ /** Family hint — for logging, surfaced in diagnostics. */
2481
+ family?: DeployFamily;
2482
+ /** Layer name. Default `deploy`. */
2483
+ name?: string;
2484
+ /** Layer dependencies — default `['build']`. */
2485
+ dependsOn?: string[];
2486
+ /** Weight in blendedScore. Default 1. */
2487
+ weight?: number;
2488
+ /** Cap (ms). Default 120s — prod builds are slower than dev. */
2489
+ capMs?: number;
2490
+ /** When true, treat artifactValid=false as a fail (default true). */
2491
+ requireArtifact?: boolean;
2492
+ }
2493
+ /**
2494
+ * Build a deploy gate layer that runs the production build and verifies
2495
+ * the artifact. Pass: ok && artifactValid. Score: 1.0 (pass) or 0 (fail).
2496
+ *
2497
+ * For families where artifact-validation isn't applicable (e.g. a
2498
+ * server-rendered build that prints a manifest), set `requireArtifact:
2499
+ * false` and rely on the runner's own ok signal.
2500
+ */
2501
+ declare function deployGateLayer<Env = unknown>(input: DeployGateLayerInput): Layer<Env>;
2502
+ interface ViteDeployRunnerInput {
2503
+ /** Workdir to build. The runner cd's here. */
2504
+ workdir: string;
2505
+ /**
2506
+ * Function to run a shell command in `workdir`. Same shape as
2507
+ * agent-eval's CommandRunner.run for compositional reuse.
2508
+ */
2509
+ exec: (cmd: string, opts?: {
2510
+ cwd?: string;
2511
+ timeoutMs?: number;
2512
+ }) => Promise<{
2513
+ stdout: string;
2514
+ stderr: string;
2515
+ exitCode: number;
2516
+ }>;
2517
+ /**
2518
+ * Function to test whether a path exists in the workdir. Inject
2519
+ * `(p) => existsSync(join(workdir, p))` for host runs.
2520
+ */
2521
+ exists: (relativePath: string) => boolean | Promise<boolean>;
2522
+ /** Build command. Default `npm run build`. */
2523
+ buildCommand?: string;
2524
+ /** Artifact directory to validate. Default `dist`. */
2525
+ artifactDir?: string;
2526
+ /** Entry-point file under artifactDir. Default `index.html`. */
2527
+ artifactEntry?: string;
2528
+ /** Per-build cap (ms). Default 90s. */
2529
+ timeoutMs?: number;
2530
+ }
2531
+ /**
2532
+ * Canonical runner for `frontend-static` family — runs the build script,
2533
+ * validates `<artifactDir>/<artifactEntry>` exists. Use as the `runner:`
2534
+ * factory for {@link deployGateLayer}.
2535
+ */
2536
+ declare function viteDeployRunner(input: ViteDeployRunnerInput): DeployRunner;
2537
+ interface WranglerDeployRunnerInput {
2538
+ workdir: string;
2539
+ exec: (cmd: string, opts?: {
2540
+ cwd?: string;
2541
+ timeoutMs?: number;
2542
+ }) => Promise<{
2543
+ stdout: string;
2544
+ stderr: string;
2545
+ exitCode: number;
2546
+ }>;
2547
+ exists: (relativePath: string) => boolean | Promise<boolean>;
2548
+ /** Build command. Default `npm run build`. */
2549
+ buildCommand?: string;
2550
+ /** Wrangler dry-run command. Default `npx wrangler deploy --dry-run --outdir dist`. */
2551
+ dryRunCommand?: string;
2552
+ /** Per-step cap (ms). Default 120s. */
2553
+ timeoutMs?: number;
2554
+ }
2555
+ /**
2556
+ * Canonical runner for the `fullstack-ts` family on Cloudflare Workers
2557
+ * (Remix / React Router v7 / Hono on Workers). Detects wrangler.toml or
2558
+ * wrangler.jsonc in the workdir, builds, then `wrangler deploy --dry-run`
2559
+ * to catch missing bindings, syntax errors in wrangler config, and
2560
+ * import-time crashes that don't surface in `tsc`.
2561
+ *
2562
+ * No wrangler config = skip with "no wrangler" evidence (not a failure
2563
+ * — the gate caller decides whether to require deploy validation).
2564
+ */
2565
+ declare function wranglerDeployRunner(input: WranglerDeployRunnerInput): DeployRunner;
2566
+ /**
2567
+ * Hidden-criteria grading firewall — grade an agent on criteria it never saw.
2568
+ *
2569
+ * A trustworthy benchmark splits every scenario's data by WHERE each field is
2570
+ * allowed to flow, then proves the held-out / judge-only fields never reach the
2571
+ * agent during the run. The coding bench expresses this with four destinations
2572
+ * (prompt / develop-against / held-out suite / rubric); this module lifts the
2573
+ * domain-FREE core out of it so research, legal, tax, content — any domain —
2574
+ * can declare the same routing and get the same firewall enforcement and the
2575
+ * same held-out-weighted composite, plugging in its OWN grader.
2576
+ *
2577
+ * Two reusable pieces, both domain-agnostic:
2578
+ *
2579
+ * 1. FIELD ROUTING BY DESTINATION. A scenario declares each field's
2580
+ * `FieldDestination`; `assertNoHiddenLeak` is a pure checker that throws if
2581
+ * a grading-only or judge-only field's value appears in what reaches the
2582
+ * agent. The domain decides which fields exist and where they go — the
2583
+ * substrate only enforces "hidden stays hidden".
2584
+ *
2585
+ * 2. HIDDEN-CRITERIA GRADING. The domain supplies its own grader
2586
+ * `(artifact, hiddenCriteria) => { passRate, total }` — the coding
2587
+ * node-test executor is ONE such grader a consumer plugs in; the substrate
2588
+ * bakes in NO node/test/TS/exec/regex. `gradeOnHidden` runs that grader
2589
+ * behind the firewall and `blendHeldout` composes its pass rate with a
2590
+ * judge score into the final number the leaderboard ranks on.
2591
+ *
2592
+ * Shape mirrors `treatment-gate`/`authenticity`: pure predicates and pure
2593
+ * composition over already-computed values, fail-loud, with the
2594
+ * "which field / which weight / which grader" decisions left as parameters and
2595
+ * no domain literal anywhere in the module.
2596
+ *
2597
+ * Lives next to `test-graded-scenario` and `partition-held-out` — it is a
2598
+ * scorecard/grading concept that makes sense without a running agent loop.
2599
+ */
2600
+ /**
2601
+ * Where one scenario field is allowed to flow. The firewall guarantee is keyed
2602
+ * on this tag, not on a field name — a domain can have any number of fields per
2603
+ * destination.
2604
+ *
2605
+ * - `agent-visible` reaches the agent's context during the run (the prompt,
2606
+ * the task statement — what the agent reads to act).
2607
+ * - `develop-against` seeded into the agent's environment during the run so it
2608
+ * can iterate (a visible example/test/reference). The
2609
+ * agent MAY read it — that is intentional (real TDD). Not
2610
+ * a leak: it is example-grade, not the grading criteria.
2611
+ * - `grading-only` the hidden criteria. Used ONLY at grading, after the run
2612
+ * — the held-out suite / answer key / hidden requirements.
2613
+ * Must NEVER reach the agent context. This is what makes a
2614
+ * good score un-memorizable.
2615
+ * - `judge-only` grading context for the judge only (rubric anchors,
2616
+ * design intent). Lives with the judge, never in the agent
2617
+ * context.
2618
+ */
2619
+ type FieldDestination = 'agent-visible' | 'develop-against' | 'grading-only' | 'judge-only';
2620
+ /**
2621
+ * A scenario's fields routed by destination. The domain owns the field set
2622
+ * (`TFields` — a record of its named fields to their string-renderable values)
2623
+ * and declares one `FieldDestination` per field. `routeFields` builds this from
2624
+ * a domain's `(value, destination)` map; the firewall reads it.
2625
+ */
2626
+ interface RoutedField {
2627
+ /** The field's name — for diagnostics only. */
2628
+ name: string;
2629
+ /** The field's value as it would be rendered into text. The firewall compares
2630
+ * this against the agent context, so a domain that ships structured data
2631
+ * passes a stable string projection (e.g. JSON) of the hidden value. */
2632
+ value: string;
2633
+ destination: FieldDestination;
2634
+ }
2635
+ /** A single detected leak: a hidden field whose value appears in the agent context. */
2636
+ interface HiddenLeak {
2637
+ field: string;
2638
+ destination: FieldDestination;
2639
+ }
2640
+ interface NoLeakOptions {
2641
+ /** Minimum hidden-value length to check. A hidden value shorter than this is
2642
+ * skipped — a one-word or empty hidden field would substring-match innocuous
2643
+ * prose and is not meaningful evidence of a leak. Default 12. */
2644
+ minMatchLength?: number;
2645
+ }
2646
+ /**
2647
+ * The FIREWALL. Throws `ValidationError` if any `grading-only`/`judge-only`
2648
+ * field's value is found inside `agentContext` — the exact text that reaches the
2649
+ * agent during the run (its prompt, its seeded files concatenated, whatever the
2650
+ * caller assembled). `agent-visible` and `develop-against` fields are never
2651
+ * checked: they are meant to be there.
2652
+ *
2653
+ * Substring containment is the check: it is domain-free and catches the failure
2654
+ * that matters — a hidden answer key, held-out case, or rubric anchor pasted
2655
+ * into the prompt. Returns the routed fields on success so a caller can chain.
2656
+ */
2657
+ declare function assertNoHiddenLeak(fields: readonly RoutedField[], agentContext: string, opts?: NoLeakOptions): readonly RoutedField[];
2658
+ /** What a hidden-criteria grader reports. `passRate = passed / total` over the
2659
+ * hidden checks; `total === 0` means the criteria never ran (e.g. the artifact
2660
+ * did not even load) — an honest zero, never a spurious pass. */
2661
+ interface HiddenGradeResult {
2662
+ /** Hidden checks that passed. */
2663
+ passed: number;
2664
+ /** Total hidden checks attempted. 0 when the criteria could not run at all. */
2665
+ total: number;
2666
+ /** `passed / total`, or 0 when `total === 0`. The PRIMARY correctness score. */
2667
+ passRate: number;
2668
+ /** Free-form provenance the caller may record (runner output, reason for 0). */
2669
+ notes?: string;
2670
+ }
2671
+ /**
2672
+ * The domain's grader: given the agent's artifact and the HIDDEN criteria,
2673
+ * return a pass rate. This is the ONE seam a non-coding domain implements — the
2674
+ * coding node-test executor is a single implementation of it; a legal grader
2675
+ * checks the brief against hidden required holdings, a research grader checks an
2676
+ * answer against held-out facts, a tax grader runs hidden return assertions.
2677
+ * The substrate calls it ONLY at grading time, behind the firewall.
2678
+ *
2679
+ * `THidden` is the domain's hidden-criteria payload (the held-out suite, the
2680
+ * answer key, the hidden requirements) — opaque to the substrate.
2681
+ */
2682
+ type HiddenCriteriaGrader<TArtifact, THidden> = (artifact: TArtifact, hiddenCriteria: THidden, signal?: AbortSignal) => Promise<HiddenGradeResult> | HiddenGradeResult;
2683
+ /** Weights for the held-out / judge blend. Must be finite and non-negative;
2684
+ * they are renormalized to sum to 1 so a caller can pass any positive ratio. */
2685
+ interface BlendWeights {
2686
+ /** Weight on the hidden-criteria pass rate (the primary, ungameable score). */
2687
+ heldout: number;
2688
+ /** Weight on the judge's quality composite (the secondary style/quality score). */
2689
+ judge: number;
2690
+ }
2691
+ /** The input shape a judge's `score` receives — exactly `JudgeConfig.score`'s
2692
+ * argument: the artifact, plus any scenario/signal fields the judge carries.
2693
+ * `withHeldoutBlend` only reads `artifact`; the rest rides through. */
2694
+ interface JudgeScoreInput<TArtifact> {
2695
+ artifact: TArtifact;
2696
+ /** Pass-through for the judge's extra input fields (scenario, signal). */
2697
+ [key: string]: unknown;
2698
+ }
2699
+ /**
2700
+ * Compose the PRIMARY hidden-criteria pass rate with the SECONDARY judge
2701
+ * composite into the single score the leaderboard ranks on. Weights are
2702
+ * renormalized, so a solution that fails the hidden criteria is capped low no
2703
+ * matter how the judge felt about its style, while a stylistically-mediocre but
2704
+ * CORRECT solution still earns the bulk of the points. Both inputs are clamped
2705
+ * to [0,1] — a judge on a non-unit scale must be normalized by the caller first.
2706
+ */
2707
+ declare function blendHeldout(heldoutPassRate: number, judgeScore: number, weights?: BlendWeights): number;
2708
+ /**
2709
+ * Minimal backend-config shape the assertion reads. Consumers may pass richer
2710
+ * types — only these five fields are inspected.
2711
+ */
2712
+ interface BackendDescriptor {
2713
+ /** Backend route — e.g. `'tcloud' | 'cli-bridge' | 'sandbox' | 'direct-provider'`;
2714
+ * free-form for consumer extensibility. */
2715
+ kind: string;
2716
+ /** Resolved base URL. Compared lexically (trailing slash stripped). */
2717
+ baseUrl: string;
2718
+ /** Model id (with snapshot suffix). Compared lexically. */
2719
+ model: string;
2720
+ /** Optional provider override. Compared when both set; flagged when only
2721
+ * one side sets it. */
2722
+ provider?: string;
2723
+ /** Bearer token. Values are NEVER compared (security) — only that EITHER
2724
+ * both are set OR both are empty. Mismatched presence is a divergence. */
2725
+ apiKey?: string;
2726
+ }
2727
+ interface AssertSingleBackendOptions {
2728
+ /** When true, ANY field divergence fails. When false (default), only
2729
+ * `kind` / `baseUrl` / `provider` / `apiKeyPresence` divergence throws —
2730
+ * a different judge `model` on the same route is allowed (the legal
2731
+ * pattern: a cheaper judge model). */
2732
+ strict?: boolean;
2733
+ agentLabel?: string;
2734
+ judgeLabel?: string;
2735
+ }
2736
+ type SingleBackendField = 'kind' | 'baseUrl' | 'model' | 'provider' | 'apiKeyPresence';
2737
+ interface SingleBackendDivergence {
2738
+ field: SingleBackendField;
2739
+ agent: string | undefined;
2740
+ judge: string | undefined;
2741
+ }
2742
+ interface SingleBackendReport {
2743
+ /** True when agent + judge agree per the configured strictness. */
2744
+ ok: boolean;
2745
+ /** Every divergence detected (includes `model` even when non-blocking). */
2746
+ divergences: ReadonlyArray<SingleBackendDivergence>;
2747
+ }
2748
+ /**
2749
+ * Throw `SingleBackendError` when the agent and judge backends diverge in a
2750
+ * way that would re-route the judge through a different backend than the
2751
+ * agent. Returns the report so callers can log it in either case.
2752
+ */
2753
+ declare function assertSingleBackend(agent: BackendDescriptor, judge: BackendDescriptor, opts?: AssertSingleBackendOptions): SingleBackendReport;
2754
+ interface IntentMatchInput {
2755
+ /** The full natural-language prompt the agent was handed. */
2756
+ userRequest: string;
2757
+ /** Top-level source files from the agent's workdir. */
2758
+ sourceFiles: Array<{
2759
+ path: string;
2760
+ content: string;
2761
+ }>;
2762
+ /** Rendered HTML the preview returned, when available. */
2763
+ servedHtml?: string;
2764
+ /** Optional metadata to inject (id, vertical, difficulty). */
2765
+ artifactLabel?: string;
2766
+ artifactDescription?: string;
2767
+ }
2768
+ interface IntentMatchResult {
2769
+ kind: 'intent-match';
2770
+ version: string;
2771
+ /** 0..1 — 1 = unmistakably the right app, 0 = unrelated to the brief. */
2772
+ score: number;
2773
+ /** One-sentence rationale citing concrete evidence (file or HTML). */
2774
+ evidence: string;
2775
+ durationMs: number;
2776
+ costUsd: number | null;
2777
+ available: boolean;
2778
+ error?: string;
2779
+ }
2780
+ interface IntentMatchOptions {
2781
+ /** Caller-owned transport. Required: agent-eval executes no paid model. */
2782
+ chat: ChatClient;
2783
+ model?: string;
2784
+ timeoutMs?: number;
2785
+ maxTokens?: number;
2786
+ maxSourceChars?: number;
2787
+ maxPerFileChars?: number;
2788
+ maxHtmlChars?: number;
2789
+ /** Endpoint rates used when the transport reports no billed amount. */
2790
+ pricing?: CustomTokenPricing;
2791
+ costLedger?: CostLedgerHandle;
2792
+ costPhase?: string;
2793
+ costTags?: Record<string, string>;
2794
+ signal?: AbortSignal;
2795
+ }
2796
+ /**
2797
+ * Run the intent-match judge. Soft-fails to available=false on error.
2798
+ */
2799
+ declare function runIntentMatchJudge(input: IntentMatchInput, options: IntentMatchOptions): Promise<IntentMatchResult>;
2800
+ /** One judge's verdict. `perDimension: null` ⇒ that judge call failed (threw or
2801
+ * returned malformed output) — recorded, never folded into a zero. */
2802
+ interface JudgeVerdict<D extends string = string> {
2803
+ /** Judge identity (typically the model id) — the `perJudge` / `failedJudges` key. */
2804
+ model: string;
2805
+ /** Per-dimension scores, or `null` when the judge failed. */
2806
+ perDimension: Record<D, number> | null;
2807
+ /** Optional one-line rationale; the first non-empty one becomes the aggregate's. */
2808
+ rationale?: string;
2809
+ /** Optional reported cost, committed by the caller's CostLedger. */
2810
+ costUsd?: number;
2811
+ /** Optional provider usage, committed by the caller's CostLedger. */
2812
+ usage?: LlmUsage;
2813
+ /** Optional per-dimension reasoning/evidence. Carried through to
2814
+ * `EnsembleAggregate.verdicts` verbatim — never folded into the math. */
2815
+ detail?: Partial<Record<D, {
2816
+ reasoning?: string;
2817
+ evidence?: string;
2818
+ }>>;
2819
+ }
2820
+ /** The aggregated ensemble result. */
2821
+ interface EnsembleAggregate<D extends string = string> {
2822
+ /** Mean over SURVIVING judges, per dimension (absent-everywhere ⇒ 0). */
2823
+ perDimension: Record<D, number>;
2824
+ /** Weighted mean of `perDimension` (uniform unless `weights` given). */
2825
+ composite: number;
2826
+ /** Each surviving judge's clamped per-dimension scores, for trace drill-down. */
2827
+ perJudge: Record<string, Record<D, number>>;
2828
+ /** Max over dimensions of (max − min) across survivors — the inter-rater signal. */
2829
+ maxDisagreement: number;
2830
+ /** Models whose verdict was null (failed). */
2831
+ failedJudges: string[];
2832
+ /** First non-empty survivor rationale, or `'llm-judge'`. */
2833
+ rationale: string;
2834
+ /** The input verdicts, verbatim — drill-down to raw scores, `detail`
2835
+ * reasoning/evidence, and per-verdict cost without re-running judges. */
2836
+ verdicts: JudgeVerdict<D>[];
2837
+ }
2838
+ /**
2839
+ * Reduce per-judge verdicts to one aggregate. Generic over the rubric: pass the
2840
+ * stable-ordered `dimensionKeys` the judges scored.
2841
+ *
2842
+ * - Per-dimension score = mean over surviving judges (out-of-range clamped to [0,1]).
2843
+ * - `composite` = `weightedComposite` of `perDimension`. No `weights` ⇒ uniform
2844
+ * over every dimension. A partial `weights` map selects AND weights exactly the
2845
+ * named dimensions (others excluded from the composite) — the substrate's
2846
+ * sum-normalized weighting, not re-implemented here.
2847
+ * - Throws if `verdicts` or `dimensionKeys` is empty, or if every judge failed.
2848
+ */
2849
+ declare function aggregateJudgeVerdicts<D extends string>(verdicts: readonly JudgeVerdict<D>[], dimensionKeys: readonly D[], weights?: Partial<Record<D, number>>): EnsembleAggregate<D>;
2850
+ /**
2851
+ * Wrap a single judge LLM call with retry, optional fallback-model
2852
+ * rotation, exponential backoff, and a typed `JudgeRetryOutcome`. Callers
2853
+ * MUST inspect `succeeded` before using `value`; on failure the library
2854
+ * returns `value: null` rather than substituting a default, so a judge
2855
+ * abort cannot silently corrupt a downstream composite.
2856
+ *
2857
+ * Reporting contract: callers ship `TrialResult.judgeSucceeded = succeeded`
2858
+ * and `TrialResult.judgeAttempts = attempts` so `aggregateTrialsByMode`
2859
+ * with `mode: 'exclude-failed'` drops the trial.
2860
+ */
2861
+ /** Retry policy for judge LLM calls. */
2862
+ interface JudgeRetryPolicy {
2863
+ /** Max attempts per model. Default 3 (one initial + two retries). */
2864
+ maxAttempts?: number;
2865
+ /** Per-attempt timeout in ms. Default 300_000. */
2866
+ timeoutMs?: number;
2867
+ /**
2868
+ * Models to try, in order. The first model is the primary; subsequent
2869
+ * models are fallbacks invoked only when ALL retries on the previous
2870
+ * model have been exhausted. Example: `['claude-code/sonnet', 'kimi-code/k2p6']`
2871
+ * runs claude-code up to maxAttempts times, then falls back to kimi.
2872
+ * If omitted, the caller's judge function controls model selection and
2873
+ * the retries apply to that single model.
2874
+ */
2875
+ models?: readonly string[];
2876
+ /** Exponential backoff function, default `attempt → min(500 * 2^attempt, 16_000)`. */
2877
+ backoffMs?: (attempt: number) => number;
2878
+ /**
2879
+ * Predicate deciding whether an error should trigger a retry. Defaults to
2880
+ * `isTransientLlmError` — the package-wide classifier shared with
2881
+ * `callLlm` — which retries aborts/timeouts, network faults, HTTP/2
2882
+ * transport faults, and any `LlmCallError` with status in {429,502,503,504}.
2883
+ * JSON-parse and schema-rejection errors are NOT retriable (the model
2884
+ * needs prompt adjustment, not another shot).
2885
+ */
2886
+ isRetryable?: (err: unknown) => boolean;
2887
+ }
2888
+ /** Outcome of a wrapped judge invocation. */
2889
+ interface JudgeRetryOutcome<T> {
2890
+ /** The judge's returned value when `succeeded === true`. */
2891
+ value: T | null;
2892
+ /** True iff one of the attempts completed without throwing. */
2893
+ succeeded: boolean;
2894
+ /** Total attempts made across all models. */
2895
+ attempts: number;
2896
+ /** Which model the successful attempt used (when succeeded). */
2897
+ modelUsed?: string;
2898
+ /** Last error captured when `succeeded === false`. */
2899
+ error?: Error;
2900
+ /** Per-attempt error log for forensics. */
2901
+ attemptErrors: Array<{
2902
+ attempt: number;
2903
+ model: string;
2904
+ error: string;
2905
+ }>;
2906
+ }
2907
+ /**
2908
+ * Wrap a judge call with retry + fallback-model + typed outcome semantics.
2909
+ *
2910
+ * The `judgeFn` signature is `(model: string, signal: AbortSignal) => Promise<T>`.
2911
+ * The signal will be aborted at `timeoutMs`. Callers should pass the signal
2912
+ * to their underlying fetch/SDK call so the abort actually fires.
2913
+ *
2914
+ * Returns a typed outcome — callers MUST inspect `succeeded` before using
2915
+ * `value`. The library refuses to default to a silent zero score because a
2916
+ * synthetic zero is indistinguishable from a real low score downstream.
2917
+ */
2918
+ declare function withJudgeRetry<T>(judgeFn: (model: string, signal: AbortSignal) => Promise<T>, policy?: JudgeRetryPolicy): Promise<JudgeRetryOutcome<T>>;
2919
+ interface EnsembleJudgeOptions<D extends string> {
2920
+ /** Judge name — becomes the returned `JudgeConfig.name`. */
2921
+ name: string;
2922
+ /** Rubric dimensions every model scores. Keys of the verdict's `perDimension`. */
2923
+ dimensions: D[];
2924
+ /** Judge model ids — one `scoreWith` call per entry. List a model twice to
2925
+ * sample it twice (votes are suffix-keyed `model#2` so none overwrite). */
2926
+ models: string[];
2927
+ /** Explicit scoring revision for closure state not visible in the static panel config. */
2928
+ judgeVersion?: string;
2929
+ /**
2930
+ * Score the artifact with one model. Throw (or reject) on failure — the
2931
+ * panel records that model as a failed judge; it is never folded into a
2932
+ * zero. Verdict scores are clamped to [0,1] by the reducer.
2933
+ */
2934
+ scoreWith: (model: string, input: {
2935
+ artifact: unknown;
2936
+ scenario?: unknown;
2937
+ signal: AbortSignal;
2938
+ }) => Promise<JudgeVerdict<D>>;
2939
+ /** Recover usage from a failed provider response when its error retains one. */
2940
+ receiptFromError?: (error: Error, model: string) => CostReceiptInput | undefined;
2941
+ /** Used by direct score calls; campaigns supply their run ledger in score(). */
2942
+ costLedger?: CostLedgerHandle;
2943
+ /** Required per model when the shared ledger has a dollar cap. */
2944
+ maximumCharge?: MaximumCharge | ((model: string) => MaximumCharge);
2945
+ /**
2946
+ * Per-model retry policy, applied via `withJudgeRetry`. The panel's
2947
+ * `models` list drives the fan-out, so `retry.models` (the fallback
2948
+ * rotation) is overridden to each panel model in turn.
2949
+ */
2950
+ retry?: JudgeRetryPolicy;
2951
+ /** Enforce `assertCrossFamily` over `models` at construction. Default true.
2952
+ * Opt out only for deliberate single-family panels (e.g. self-consistency
2953
+ * sampling of one model).
2954
+ *
2955
+ * Construction-time only, over the REQUESTED ids: it cannot see a gateway
2956
+ * that answers several ids from one provider. `scoreWith` owns the served
2957
+ * side — check the id the provider echoed with `assertServedModel`
2958
+ * (./integrity/served-model) inside your judge call. */
2959
+ crossFamily?: boolean;
2960
+ /** Composite weights forwarded to `aggregateJudgeVerdicts`: a partial map
2961
+ * selects AND weights exactly the named dimensions. Omit for uniform. */
2962
+ weights?: Partial<Record<D, number>>;
2963
+ }
2964
+ /**
2965
+ * Build a campaign-shaped `JudgeConfig` whose `score()` runs every panel
2966
+ * model in parallel and reduces the surviving verdicts to one canonical
2967
+ * `JudgeScore` in [0,1].
2968
+ *
2969
+ * Failure semantics: a model whose `scoreWith` throws (or exhausts `retry`)
2970
+ * lands in `failedJudges` and is excluded from the means. When EVERY model
2971
+ * fails, `aggregateJudgeVerdicts` throws — the campaign engine records a
2972
+ * failed cell instead of averaging a fabricated zero.
2973
+ */
2974
+ declare function ensembleJudge<D extends string>(opts: EnsembleJudgeOptions<D>): JudgeConfig<unknown>;
2975
+ /**
2976
+ * Keyword-coverage judge — baseline complement to the semantic concept
2977
+ * judge.
2978
+ *
2979
+ * Where {@link runSemanticConceptJudge} uses an LLM to read source code
2980
+ * and decide whether a concept is REALLY implemented (not just
2981
+ * keyword-mentioned), this judge does the cheap, deterministic version:
2982
+ * fetch the served preview, concatenate every linked CSS/JS asset, and
2983
+ * substring-match each expected concept's keywords against the
2984
+ * concatenated haystack. Optional `requiredElement` selector adds a
2985
+ * structural gate so "supply counter" can require an actual `<input>` or
2986
+ * `<table>`, not just a comment containing the word.
2987
+ *
2988
+ * Use both judges. Keyword coverage is a fast 0-cost gate — a stub page
2989
+ * with the right keywords passes here, fails the semantic judge. Score
2990
+ * divergence between the two is itself a signal: high keyword coverage
2991
+ * + low semantic = "the agent slapped the right words on the right
2992
+ * scaffold but didn't wire any of it up."
2993
+ *
2994
+ * Pure functions, soft-fail on fetch error, no LLM dependency.
2995
+ */
2996
+ interface KeywordConceptSpec {
2997
+ name: string;
2998
+ keywords: string[];
2999
+ /**
3000
+ * Optional CSS selector that must match in the HTML for the concept
3001
+ * to count as present. Tiny subset:
3002
+ * - `tag` (e.g. `form`)
3003
+ * - `tag[attr="value"]` (e.g. `input[type="number"]`)
3004
+ * - `tag[attr]` (presence only)
3005
+ * Anything more complex is rejected with `null` (treated as
3006
+ * "unenforced", not "failed").
3007
+ */
3008
+ requiredElement?: string;
3009
+ }
3010
+ interface KeywordCoverageFinding {
3011
+ concept: string;
3012
+ found: boolean;
3013
+ matchedKeywords: string[];
3014
+ /** True iff the optional requiredElement selector matched; null when no selector. */
3015
+ requiredElementPresent: boolean | null;
3016
+ }
3017
+ interface KeywordCoverageResult {
3018
+ /** 0..1 share of concepts satisfied. */
3019
+ score: number;
3020
+ presentCount: number;
3021
+ totalCount: number;
3022
+ findings: KeywordCoverageFinding[];
3023
+ durationMs: number;
3024
+ /** Total bytes assembled across html + linked assets. */
3025
+ totalAssembledBytes: number;
3026
+ /** Soft-failure reason if the audit couldn't run. */
3027
+ error?: string;
3028
+ }
3029
+ interface KeywordCoverageOptions {
3030
+ /** Override fetch implementation — for tests. */
3031
+ fetch?: typeof fetch;
3032
+ /** Per-asset fetch timeout (default 3s). */
3033
+ assetTimeoutMs?: number;
3034
+ /** Initial-HTML fetch timeout (default 5s). */
3035
+ htmlTimeoutMs?: number;
3036
+ }
3037
+ /**
3038
+ * Score expected concepts against an already-fetched HTML payload + any
3039
+ * pre-fetched CSS/JS assets. Use when the runner has the bytes in hand
3040
+ * and doesn't want a fresh HTTP round-trip — e.g. sandbox runtime where
3041
+ * the preview content was fetched via curl from inside the container.
3042
+ */
3043
+ declare function runKeywordCoverageJudge(html: string, expectedConcepts: ReadonlyArray<KeywordConceptSpec>, assets?: ReadonlyArray<string>): KeywordCoverageResult;
3044
+ /**
3045
+ * URL-fetch flavor — GET the preview, parallel-fetch every linked
3046
+ * stylesheet + script (with bounded timeouts, soft-fail individually),
3047
+ * then score via {@link runKeywordCoverageJudge}.
3048
+ */
3049
+ declare function runKeywordCoverageJudgeUrl(previewUrl: string, expectedConcepts: ReadonlyArray<KeywordConceptSpec>, options?: KeywordCoverageOptions): Promise<KeywordCoverageResult>;
3050
+ interface OracleObservation {
3051
+ /** Final observable text output from the agent (response, page snapshot, stdout). */
3052
+ text?: string;
3053
+ /** Final URL — for browser-style scenarios. */
3054
+ url?: string;
3055
+ /** Any structured JSON the agent produced. */
3056
+ json?: unknown;
3057
+ /** Free-form context used by custom oracles. */
3058
+ context?: Record<string, unknown>;
3059
+ }
3060
+ interface OracleResult {
3061
+ id: string;
3062
+ pass: boolean;
3063
+ detail: string;
3064
+ evidence?: string;
3065
+ }
3066
+ interface Oracle {
3067
+ id: string;
3068
+ check(obs: OracleObservation): OracleResult;
3069
+ }
3070
+ declare function jsonShape(expected: Record<string, unknown>): Oracle;
3071
+ /** Extends the substrate verdict spine: `valid` = every oracle passed (and
3072
+ * at least one ran); `score` = ratio of oracles passed; `scores` = per-
3073
+ * oracle 0|1 keyed by oracle id. */
3074
+ interface OracleReport extends DefaultVerdict {
3075
+ results: OracleResult[];
3076
+ passCount: number;
3077
+ failCount: number;
3078
+ }
3079
+ /** Run all oracles against one observation and aggregate. */
3080
+ declare function evaluateOracles(obs: OracleObservation, oracles: Oracle[]): OracleReport;
3081
+ /** A serializable law for one scalar draw. */
3082
+ type PairedValueLaw = {
3083
+ kind: 'point';
3084
+ value: number;
3085
+ } | {
3086
+ kind: 'atoms';
3087
+ atoms: ReadonlyArray<{
3088
+ value: number;
3089
+ probability: number;
3090
+ }>;
3091
+ } | {
3092
+ kind: 'normal';
3093
+ mean: number;
3094
+ sd: number;
3095
+ };
3096
+ interface PairedPromotionAlternativeCell {
3097
+ /** Probability of this cell. The cells' probabilities sum to 1. */
3098
+ probability: number;
3099
+ /** Pass/fail outcome of each arm in this cell. Required by every call whose
3100
+ * `outcome` is `'pass'`; ignored otherwise. */
3101
+ pass?: {
3102
+ control: boolean;
3103
+ treatment: boolean;
3104
+ };
3105
+ /** Law of the candidate-minus-baseline delta on the continuous scale, given
3106
+ * this cell. A tie is `{ kind: 'point', value: 0 }`. */
3107
+ delta: PairedValueLaw;
3108
+ }
3109
+ interface PairedPromotionAlternative {
3110
+ /** The registered joint law: the cell is drawn first, then the delta given
3111
+ * the cell, so pass/fail and delta are dependent exactly as registered. */
3112
+ cells: ReadonlyArray<PairedPromotionAlternativeCell>;
3113
+ /**
3114
+ * Law of the control arm's value on the continuous scale; the candidate is
3115
+ * control plus delta. Default `{ kind: 'point', value: 0 }`. The mean and
3116
+ * median bootstraps read only the deltas, so the baseline changes nothing
3117
+ * there; it matters only when a `'delta'` call infers its shape from the
3118
+ * observed values (see `continuous` on {@link PairedPromotionDecisionOptions}).
3119
+ */
3120
+ baseline?: PairedValueLaw;
3121
+ }
3122
+ interface PairedPromotionPowerCall {
3123
+ /** What the call reads from each simulated pair: `'delta'` gives it
3124
+ * `(control, control + delta)` on the continuous scale, `'pass'` gives it
3125
+ * each arm's pass/fail as `0` or `options.binaryScale ?? 1`. */
3126
+ outcome: 'delta' | 'pass';
3127
+ /** The call's options, exactly as the caller will run it. `minPairs`
3128
+ * defaults to the simulated n, the value a gate seals; a caller-supplied
3129
+ * value is kept, and one above n refuses every simulation. A `'pass'` call
3130
+ * must declare `binaryScale`: the simulation never declares one on the
3131
+ * caller's behalf, because a declared scale routes an all-zero sample to
3132
+ * the score interval where an inferred one refuses it. */
3133
+ options: PairedPromotionDecisionOptions;
3134
+ }
3135
+ interface PairedPromotionPowerOptions {
3136
+ /** Total paired episodes, ties included. */
3137
+ n: number;
3138
+ alternative: PairedPromotionAlternative;
3139
+ /** Every call must return `promote: true` for a simulation to count as a
3140
+ * promotion. At least one call. */
3141
+ calls: ReadonlyArray<PairedPromotionPowerCall>;
3142
+ /** Simulated data sets. Default 1000. */
3143
+ simulations?: number;
3144
+ /** PRNG seed for the simulated draws (not the bootstrap's seed, which each
3145
+ * call carries in its own options). Default 1. */
3146
+ seed?: number;
3147
+ }
3148
+ interface PairedPromotionCallRates {
3149
+ outcome: 'delta' | 'pass';
3150
+ /** Fraction of simulations in which this call alone promoted. */
3151
+ promote: number;
3152
+ /** Fractions of simulations in which each refusal channel fired. */
3153
+ insufficient: number;
3154
+ indeterminate: number;
3155
+ exactTestVetoes: number;
3156
+ /** Fraction in which the call was sufficient, determinate, not vetoed, and
3157
+ * still did not clear its threshold. */
3158
+ hold: number;
3159
+ /** Fraction of simulations decided by each method; the regime the call
3160
+ * actually ran in at this n. */
3161
+ methods: Partial<Record<PairedDecisionMethod, number>>;
3162
+ }
3163
+ interface PairedPromotionPowerResult {
3164
+ n: number;
3165
+ simulations: number;
3166
+ seed: number;
3167
+ /** Fraction of simulations in which every call promoted: the joint power. */
3168
+ power: number;
3169
+ /** Monte Carlo standard error of `power`. */
3170
+ standardError: number;
3171
+ /** Wilson 95% interval on `power`. */
3172
+ low: number;
3173
+ high: number;
3174
+ /** Fraction in which at least one call refused (insufficient, indeterminate
3175
+ * or vetoed); the rest of the non-promotions are holds. */
3176
+ refuse: number;
3177
+ hold: number;
3178
+ /** Per-call rates, in the order the calls were supplied. */
3179
+ calls: PairedPromotionCallRates[];
3180
+ }
3181
+ interface RequiredPairsForPairedPromotionOptions {
3182
+ /** Target joint power. Default 0.8. */
3183
+ target?: number;
3184
+ alternative: PairedPromotionAlternative;
3185
+ calls: ReadonlyArray<PairedPromotionPowerCall>;
3186
+ /** Smallest n to consider. Default 1. */
3187
+ minPairs?: number;
3188
+ /** Largest n to consider; the scan stops there with `n: null` if the target
3189
+ * is never reached. Default 200. */
3190
+ maxPairs?: number;
3191
+ simulations?: number;
3192
+ seed?: number;
3193
+ }
3194
+ interface RequiredPairsForPairedPromotionResult {
3195
+ target: number;
3196
+ /** First n scanned whose estimated joint power reaches the target, or null
3197
+ * when no n up to `maxPairs` does. Descriptive only: power is not monotone
3198
+ * in n on the exact and discrete routes, the scan reads one noisy estimate
3199
+ * per n, and the first crossing of many is biased low. Read the curve. */
3200
+ n: number | null;
3201
+ /**
3202
+ * The conservative choice, which a preregistration should prefer: the first
3203
+ * scanned n whose Wilson lower bound reaches the target AND whose
3204
+ * independent confirmation (a second simulation at that n on a fresh seed,
3205
+ * `confirmation` below) also has its lower bound at or above the target. The
3206
+ * confirmation is what keeps the 95 % coverage: a scan over many n gives
3207
+ * Monte Carlo noise many chances to push one bound across, so the scan's own
3208
+ * bound selects a candidate and never certifies it. Never set while `n` is
3209
+ * null. Null when not reached by `maxPairs`.
3210
+ */
3211
+ nAtLowerBound: number | null;
3212
+ /** The independent simulation at `nAtLowerBound`; null when not reached. */
3213
+ confirmation: PairedPromotionPowerResult | null;
3214
+ /** Every n scanned, in order, with its scan estimate; candidates whose
3215
+ * confirmation failed are in `rejected`. The scan stops at `nAtLowerBound`
3216
+ * when that is reached, else at `maxPairs`. */
3217
+ curve: PairedPromotionPowerResult[];
3218
+ /** Candidates the scan's bound selected and the confirmation refused. */
3219
+ rejected: PairedPromotionPowerResult[];
3220
+ }
3221
+ /**
3222
+ * Joint power of the configured calls at `n` under the registered alternative:
3223
+ * the fraction of simulated data sets on which every call promotes.
3224
+ */
3225
+ declare function pairedPromotionPower(options: PairedPromotionPowerOptions): PairedPromotionPowerResult;
3226
+ /**
3227
+ * First n at which {@link pairedPromotionPower} reaches the target, scanning
3228
+ * every n from `minPairs` upward. Both the point-estimate answer and the
3229
+ * confirmed lower-bound answer are returned; the scan runs until the latter is
3230
+ * reached (so the curve past `n` is retained) or `maxPairs` is exhausted. A
3231
+ * candidate selected by the scan's bound is certified only by an independent
3232
+ * simulation at that n on a different seed; a candidate that fails it is
3233
+ * recorded and the scan continues.
3234
+ */
3235
+ declare function requiredPairsForPairedPromotion(options: RequiredPairsForPairedPromotionOptions): RequiredPairsForPairedPromotionResult;
3236
+ /**
3237
+ * Treatment-applied gate — "did the treatment's tool actually fire this run?"
3238
+ *
3239
+ * A tool-treatment A/B (mount a search provider, a browser, a code-exec
3240
+ * sandbox, a retrieval MCP — anything one arm gets and the other doesn't) only
3241
+ * means something on the treatment arm if that arm actually EXERCISED the tool.
3242
+ * A treatment run that never called the mounted tool is not a weak data point
3243
+ * — it is a NON-data-point: the manipulation never happened, so the run cannot
3244
+ * speak to the treatment and must be excluded from the objective, the same way
3245
+ * an infra-tainted run is excluded.
3246
+ *
3247
+ * This is the manipulation/validity precondition that protects the paired A/B
3248
+ * tests downstream (`mcnemar`, `pairedRiskDifference`): they must only ever see
3249
+ * runs where the treatment was applied, or they measure noise and report it as
3250
+ * an effect.
3251
+ *
3252
+ * Shape mirrors `authenticity`'s `gateRealness`: a pure predicate over already-
3253
+ * computed signals, with explicit fail-open/fail-closed discipline. It is not a
3254
+ * trace parser — the tool-call telemetry it reads is the deterministic
3255
+ * `toolHistogram` that `computeTraceMetrics(spans)` already produces (OTLP
3256
+ * tool-name extraction via `TOOL_NAME_ATTR_KEYS`), or the `ToolSpan[]` a
3257
+ * `toolSpans(store, runId)` query returns. The gate never re-derives either.
3258
+ *
3259
+ * General by construction:
3260
+ * - The "which tool counts as the treatment" decision is a `matches` PARAMETER
3261
+ * — a `(toolName: string) => boolean`. There is no `search`/`web`/`fetch`
3262
+ * literal anywhere in this module; a search A/B passes its own matcher.
3263
+ * - The "is THIS run a treatment arm (vs a control)" decision is the caller's
3264
+ * policy and stays at the call site. The gate only answers the narrower,
3265
+ * domain-free question: "given this run's telemetry, did a matching tool
3266
+ * fire?" — so it generalizes to any tool-treatment, not search.
3267
+ *
3268
+ * Fail-open on telemetry absence: when NO tool calls were captured at all
3269
+ * (`sum(toolHistogram) === 0`), the gate cannot distinguish "the agent used no
3270
+ * tools" from "this harness's tool calls weren't recorded". Quarantining on a
3271
+ * telemetry gap would silently delete real runs, so the default is
3272
+ * applied=true / gated=false. Pass `failOpenWhenNoTelemetry: false` to flip to
3273
+ * fail-closed for harnesses where tool capture is guaranteed.
3274
+ */
3275
+ /** A tool-name matcher: does this tool name belong to the treatment under test?
3276
+ * The caller supplies it — the substrate ships no `search` (or any) literal. */
3277
+ type ToolMatcher = (toolName: string) => boolean;
3278
+ interface TreatmentGateInput {
3279
+ /** Tool-call counts by tool name — exactly `computeTraceMetrics(spans).toolHistogram`.
3280
+ * The gate consumes this; it does not parse spans itself. */
3281
+ toolHistogram: Readonly<Record<string, number>>;
3282
+ /** Which tool names count as the treatment firing. A parameter, never baked in. */
3283
+ matches: ToolMatcher;
3284
+ }
3285
+ interface TreatmentGateOptions {
3286
+ /** When the histogram is empty (no tool telemetry captured), treat the run as
3287
+ * applied rather than quarantining it. Default true — a telemetry gap must
3288
+ * never be mistaken for "treatment not applied". Set false only when tool
3289
+ * capture is guaranteed for this harness. */
3290
+ failOpenWhenNoTelemetry?: boolean;
3291
+ }
3292
+ interface TreatmentGate {
3293
+ /** True iff the treatment's tool is considered to have fired this run. False
3294
+ * ONLY when telemetry was present and no matching call appears. */
3295
+ applied: boolean;
3296
+ /** Objective-exclusion flag, parallel to `outcome.realness.gated`: a gated run
3297
+ * is dropped from the objective denominator and reported as
3298
+ * treatment-not-applied (like infra-loss). `gated === !applied`. */
3299
+ gated: boolean;
3300
+ reason?: string;
3301
+ /** How many recorded tool calls matched the treatment matcher. */
3302
+ matchedCalls: number;
3303
+ /** Total recorded tool calls (`sum(toolHistogram)`) — 0 means no telemetry. */
3304
+ observedTools: number;
3305
+ }
3306
+ /**
3307
+ * Core predicate. Gated (treatment-not-applied) ONLY when telemetry was
3308
+ * captured AND zero matching calls appear; an empty histogram fails open.
3309
+ */
3310
+ declare function gateTreatmentApplied(input: TreatmentGateInput, opts?: TreatmentGateOptions): TreatmentGate;
3311
+ /**
3312
+ * Canonical JSON (RFC 8785): object keys sorted recursively, so two
3313
+ * semantically equal values produce byte-identical output regardless of key
3314
+ * insertion order. Throws `LedgerCanonicalizationError` on any value with no
3315
+ * faithful JSON form — see `ledger-core/canonical`.
3316
+ */
3317
+ declare function canonicalJson(value: unknown): string;
3318
+ /** Hex sha-256 over `canonicalJson(value)`. The content address used by the
3319
+ * verdict cache and report attestation. Equals `hashCanonical(value)` with
3320
+ * the `sha256:` prefix removed. */
3321
+ declare function contentHash(value: unknown): string;
3322
+ /** Pluggable verdict store. Sync or async on both legs — `cachedJudge`
3323
+ * awaits the results either way. */
3324
+ interface VerdictCacheStore {
3325
+ get(key: string): Promise<JudgeScore | undefined> | JudgeScore | undefined;
3326
+ set(key: string, score: JudgeScore): Promise<void> | void;
3327
+ }
3328
+ /**
3329
+ * JSONL-file-backed store: the full file is loaded into an in-memory index at
3330
+ * construction; every `set` appends one line synchronously (durable before
3331
+ * the verdict is returned). A corrupt or malformed line throws at load with
3332
+ * file:line — a skipped line would silently re-judge (cost) or, worse, mask
3333
+ * a half-written file that needs operator attention.
3334
+ */
3335
+ declare function fileVerdictCache(path: string): VerdictCacheStore;
3336
+ /**
3337
+ * Strong, generically-useful baseline ROLES — the top zone of an `AgentProfile`
3338
+ * before any domain layer. A product composes one of these with its own
3339
+ * environment description (its sandbox) and its domain guidance, which lives in
3340
+ * the product repo (not here): a profile is `<baseline role> + <environment> +
3341
+ * <domain sections>`. Domain strength is NOT generalized into the substrate —
3342
+ * it is lifted here only once ≥2 products genuinely reuse it.
3343
+ *
3344
+ * Three roles cover the common shapes:
3345
+ * - `engineerRole` — builds + verifies real artifacts in a sandbox. Distilled
3346
+ * from agent-runtime's `coderProfile` doctrine (minimal
3347
+ * correct change, run the checks, fix the root cause,
3348
+ * never weaken a test or hide an error).
3349
+ * - `researcherRole` — gathers from real sources and grounds every claim
3350
+ * (cite or mark inferred; never fabricate).
3351
+ * - `generalistRole` — a strong default: helpful, grounded, verify-before-assert.
3352
+ *
3353
+ * All three are verification-first and domain-agnostic. They describe HOW a
3354
+ * capable IC operates, not WHAT domain it works in.
3355
+ */
3356
+ /** Senior-IC engineer operating in a sandbox — produces real artifacts and
3357
+ * verifies them before declaring done. The shared operator foundation for any
3358
+ * file-producing, tool-using product agent. */
3359
+ declare const engineerRole: string;
3360
+ /** Researcher who grounds every claim in real sources — gathers, cites, and
3361
+ * distinguishes verified fact from inference. */
3362
+ declare const researcherRole: string;
3363
+ /** Strong general-purpose default — helpful, grounded, verifies before asserting. */
3364
+ declare const generalistRole: string;
3365
+ /** The named baseline roles, for selection by key (e.g. from a product config). */
3366
+ declare const BASELINE_ROLES: {
3367
+ readonly engineer: string;
3368
+ readonly researcher: string;
3369
+ readonly generalist: string;
3370
+ };
3371
+ type BaselineRoleKey = keyof typeof BASELINE_ROLES;
3372
+ declare namespace index_d_exports {
3373
+ export { AgentProfileSection, BASELINE_ROLES, BaselineRoleKey, ProfileSkill, PromptProfile, applyDomainPatch, baselineProfile, baselineProfileFromRole, engineerRole, generalistRole, prodProfile, profileToSurface, renderProfile, researcherRole, sectionHash };
3374
+ }
3375
+ /** A named, addressable region of the system prompt. `evolvable` marks whether
3376
+ * the self-improvement loop is allowed to patch its body; fixed scaffolding
3377
+ * (e.g. a compliance preamble) sets `evolvable: false`. */
3378
+ interface AgentProfileSection {
3379
+ id: string;
3380
+ title: string;
3381
+ body: string;
3382
+ evolvable: boolean;
3383
+ }
3384
+ /** A skill the agent can invoke. `triggers` are the phrases/conditions that
3385
+ * should activate it; `scriptsDir` points at its executable scripts when the
3386
+ * skill ships code (mirrors Harvey's `skills/<name>/scripts/`). */
3387
+ interface ProfileSkill {
3388
+ name: string;
3389
+ description: string;
3390
+ triggers: string[];
3391
+ scriptsDir?: string;
3392
+ }
3393
+ /** The structured system prompt. The first four fields are fixed scaffolding;
3394
+ * `domain` is the evolvable surface the loop optimizes. */
3395
+ interface PromptProfile {
3396
+ role: string;
3397
+ environment: string;
3398
+ toolConventions: string;
3399
+ skills: ProfileSkill[];
3400
+ domain: AgentProfileSection[];
3401
+ }
3402
+ /**
3403
+ * Emit the sectioned system prompt in fixed order: role text, then
3404
+ * `## Environment`, `## Tool conventions`, `## Skills` (each skill as
3405
+ * `### <name>` + description + triggers), `## Domain guidance` (each section as
3406
+ * `### <title>` + body). The order is load-bearing — the loop diffs rendered
3407
+ * text, and reordering would make every candidate look like a full rewrite.
3408
+ */
3409
+ declare function renderProfile(p: PromptProfile): string;
3410
+ /** The string `MutableSurface` the self-improvement loop scores — a profile
3411
+ * renders to exactly the text a candidate is graded on. */
3412
+ declare function profileToSurface(p: PromptProfile): string;
3413
+ /**
3414
+ * The fixed-scaffolding baseline: a `role`/`environment`/`toolConventions`
3415
+ * profile with the stock skill roster and NO domain guidance. The
3416
+ * production profile is this plus shipped domain sections.
3417
+ */
3418
+ declare function baselineProfile(args: {
3419
+ role: string;
3420
+ environment?: string;
3421
+ toolConventions?: string;
3422
+ skills?: ProfileSkill[];
3423
+ }): PromptProfile;
3424
+ /**
3425
+ * Baseline from one of the strong generic roles (`'engineer' | 'researcher' |
3426
+ * 'generalist'`) — the common case: pick a role foundation, optionally override
3427
+ * the environment to describe THIS product's sandbox, then layer domain via
3428
+ * `prodProfile`. Domain guidance stays in the product repo; this only supplies
3429
+ * the generically-useful role + stock scaffolding.
3430
+ */
3431
+ declare function baselineProfileFromRole(role: BaselineRoleKey, args?: {
3432
+ environment?: string;
3433
+ toolConventions?: string;
3434
+ skills?: ProfileSkill[];
3435
+ }): PromptProfile;
3436
+ /** The production profile: the baseline scaffolding plus the domain sections
3437
+ * shipped after self-improvement. Differs from the baseline ONLY in `domain`
3438
+ * (and any skills the caller layered into the baseline) — the role,
3439
+ * environment, and tool conventions are carried through unchanged. */
3440
+ declare function prodProfile(baseline: PromptProfile, shipped: AgentProfileSection[]): PromptProfile;
3441
+ /**
3442
+ * Section-scoped edit — replace the body of ONE domain section by id, leaving
3443
+ * every other section byte-identical. This is how the loop targets a single
3444
+ * evolvable surface. Throws on an unknown section id (a patch that hits no
3445
+ * section is a silent no-op otherwise — fail loud). Throws when the targeted
3446
+ * section is `evolvable: false`.
3447
+ */
3448
+ declare function applyDomainPatch(p: PromptProfile, sectionId: string, newBody: string): PromptProfile;
3449
+ /**
3450
+ * Content hash of a single section — its loop identity. Delegates to the
3451
+ * campaign provenance `surfaceContentHash` (the same helper the loop's
3452
+ * provenance record uses for full surfaces), so a section hash is byte-for-byte
3453
+ * comparable with the `sha256:`-prefixed content hashes that record carries.
3454
+ * Two sections that render identically (same title + body) share a hash; the
3455
+ * `id` and `evolvable` flag are excluded — they are routing metadata, not
3456
+ * content.
3457
+ */
3458
+ declare function sectionHash(section: AgentProfileSection): string;
3459
+ declare class BudgetBreachError extends AgentEvalError {
3460
+ dimension: keyof BudgetSpec;
3461
+ limit: number;
3462
+ attempted: number;
3463
+ constructor(dimension: keyof BudgetSpec, limit: number, attempted: number);
3464
+ }
3465
+ declare class BudgetGuard {
3466
+ private consumed;
3467
+ private emitter;
3468
+ private budget;
3469
+ private startedAt;
3470
+ constructor(emitter: TraceEmitter, budget: BudgetSpec, now?: () => number);
3471
+ /** Record consumption. Throws `BudgetBreachError` if any dimension exceeds its cap. */
3472
+ charge(delta: Partial<Record<keyof BudgetSpec, number>>, spanId?: string): Promise<void>;
3473
+ /** Convenience: advance wall-clock budget based on elapsed wall time. */
3474
+ tickWall(nowMs: number, spanId?: string): Promise<void>;
3475
+ get state(): Record<keyof BudgetSpec, number>;
3476
+ }
3477
+ /**
3478
+ * ProductClient — configurable HTTP client for exercising any agent's APIs.
3479
+ *
3480
+ * Routes are config, not hardcoded. Each agent provides its own RouteMap.
3481
+ *
3482
+ * @deprecated A one-product REST client does not belong in the generic
3483
+ * substrate. Moves to the product repo in the next major
3484
+ * (tangle-network/agent-runtime#694); vendor it if you construct it directly.
3485
+ */
3486
+ declare class ProductClient {
3487
+ private baseUrl;
3488
+ private routes;
3489
+ private cookies;
3490
+ private timeoutMs;
3491
+ constructor(config: ProductClientConfig);
3492
+ private route;
3493
+ /**
3494
+ * Single HTTP boundary for every JSON request. Aborts after `timeoutMs`,
3495
+ * checks `res.ok` BEFORE parsing, and throws `${method} ${path} failed:
3496
+ * HTTP ${status} — ${body}` on a non-ok response so a 4xx/5xx error body
3497
+ * can never be parsed as a success shape. Callers inspect the resolved
3498
+ * value only after a successful return.
3499
+ */
3500
+ private request;
3501
+ /**
3502
+ * Read a required collection field. A missing/non-array field is a contract
3503
+ * violation (wrong route, drift, partial body) — surface it loud instead of
3504
+ * masking it as a healthy empty set. A genuinely empty `[]` passes through.
3505
+ */
3506
+ private requireArray;
3507
+ signup(name: string, email: string, password: string): Promise<{
3508
+ userId: string;
3509
+ }>;
3510
+ login(email: string, password: string): Promise<void>;
3511
+ createWorkspace(name: string, type?: string): Promise<string>;
3512
+ createThread(workspaceId: string): Promise<string>;
3513
+ chat(workspaceId: string, threadId: string, content: string, _opts?: {
3514
+ blockPatterns?: RegExp[];
3515
+ }): Promise<{
3516
+ text: string;
3517
+ blocks: {
3518
+ type: string;
3519
+ title: string;
3520
+ }[];
3521
+ }>;
3522
+ getTasks(workspaceId: string): Promise<{
3523
+ id: string;
3524
+ title: string;
3525
+ status: string;
3526
+ priority: string;
3527
+ }[]>;
3528
+ getEvents(workspaceId: string): Promise<{
3529
+ id: string;
3530
+ title: string;
3531
+ type: string;
3532
+ }[]>;
3533
+ getApprovals(workspaceId: string): Promise<{
3534
+ id: string;
3535
+ title: string;
3536
+ status: string;
3537
+ type: string;
3538
+ }[]>;
3539
+ getVaultTree(workspaceId: string): Promise<string[]>;
3540
+ approveAction(workspaceId: string, id: string): Promise<void>;
3541
+ rejectAction(workspaceId: string, id: string, reason: string): Promise<void>;
3542
+ getGenerations(workspaceId: string): Promise<{
3543
+ id: string;
3544
+ type: string;
3545
+ prompt: string;
3546
+ }[]>;
3547
+ /** Generic GET for custom routes */
3548
+ get(path: string): Promise<Record<string, unknown>>;
3549
+ /** Generic POST for custom routes */
3550
+ post(path: string, body: Record<string, unknown>): Promise<Record<string, unknown>>;
3551
+ /** Generic PATCH for custom routes */
3552
+ patch(path: string, body: Record<string, unknown>): Promise<Record<string, unknown>>;
3553
+ }
3554
+ declare function runAgentControlLoop<TState, TAction, TActionResult, TEval extends ControlEvalResult = ControlEvalResult>(config: ControlRuntimeConfig<TState, TAction, TActionResult, TEval>): Promise<ControlRunResult<TState, TAction, TActionResult, TEval>>;
3555
+ declare function objectiveEval(input: Omit<ControlEvalResult, 'objective'>): ControlEvalResult;
3556
+ declare function subjectiveEval(input: Omit<ControlEvalResult, 'objective'>): ControlEvalResult;
3557
+ interface ScoreKnowledgeReadinessOptions {
3558
+ taskId: string;
3559
+ requirements: KnowledgeRequirement[];
3560
+ evidenceIds?: string[];
3561
+ claimIds?: string[];
3562
+ wikiPageIds?: string[];
3563
+ userAnswers?: Record<string, string>;
3564
+ metadata?: Record<string, unknown>;
3565
+ now?: Date;
3566
+ }
3567
+ declare function scoreKnowledgeReadiness(options: ScoreKnowledgeReadinessOptions): KnowledgeReadinessReport;
3568
+ declare function blockingKnowledgeEval(report: KnowledgeReadinessReport, options?: {
3569
+ id?: string;
3570
+ minimumScore?: number;
3571
+ emitter?: TraceEmitter;
3572
+ }): ControlEvalResult;
3573
+ declare function knowledgeReadinessTracePayload(report: KnowledgeReadinessReport, options?: {
3574
+ passed?: boolean;
3575
+ minimumScore?: number;
3576
+ }): Record<string, unknown>;
3577
+ declare function userQuestionsForKnowledgeGaps(gaps: KnowledgeRequirement[]): UserQuestion[];
3578
+ declare function acquisitionPlansForKnowledgeGaps(gaps: KnowledgeRequirement[]): DataAcquisitionPlan[];
3579
+ interface TokenPrice {
3580
+ input: number;
3581
+ output: number;
3582
+ }
3583
+ /** Per-1K token pricing for exact model ids. */
3584
+ declare const MODEL_PRICING: Record<string, TokenPrice>;
3585
+ /** Resolve pricing for a model id: exact table, then family fallback.
3586
+ * Returns null when the id matches nothing (caller decides — never a
3587
+ * silent-zero masquerading as a real $0 cost). */
3588
+ declare function resolveModelPricing(model: string): TokenPrice | null;
3589
+ /** True when `model` has known pricing (exact or family). Lets cost-aware
3590
+ * callers distinguish a real $0 from an unpriced model. */
3591
+ declare function isModelPriced(model: string): boolean;
3592
+ /** Estimate token count from string length (chars / 4 approximation) */
3593
+ declare function estimateTokens(text: string): number;
3594
+ /** Calculate cost in USD from token counts and model. Unknown models warn
3595
+ * once (not a silent zero) and return 0 so callers that ignore pricing keep
3596
+ * working; cost-sensitive callers should gate on {@link isModelPriced}. */
3597
+ declare function estimateCost(inputTokens: number, outputTokens: number, model: string): number;
3598
+ interface AntiSlopConfig {
3599
+ /** Domain label — appears in the DimensionJudgeScore output */
3600
+ domain?: string;
3601
+ /** Case-insensitive substrings that must not appear. Each occurrence = penalty. */
3602
+ bannedPhrases?: string[];
3603
+ /** Regexes matching opening formulas to penalize (e.g. /^great question/i). */
3604
+ bannedOpenings?: RegExp[];
3605
+ /** Regexes matching hedges (e.g. /i could be wrong/i). Ratio of hedged sentences drives score. */
3606
+ hedgingPatterns?: RegExp[];
3607
+ /** Regexes matching apology padding. */
3608
+ apologyPatterns?: RegExp[];
3609
+ /** Fraction of sentences that can be duplicates before penalty (default 0.15 = 15%). */
3610
+ repetitionThreshold?: number;
3611
+ /** Min output length in chars; below this the turn is deemed too terse. */
3612
+ minLength?: number;
3613
+ /** Max output length in chars; above this the turn is deemed too verbose. */
3614
+ maxLength?: number;
3615
+ /** How heavily each violation class reduces the score (default 1). */
3616
+ penaltyWeights?: Partial<Record<SlopCategory, number>>;
3617
+ }
3618
+ type SlopCategory = 'banned_phrase' | 'banned_opening' | 'hedging' | 'apology' | 'repetition' | 'length';
3619
+ /** Create a reusable Judge function from an anti-slop config. */
3620
+ declare function createAntiSlopJudge(config?: AntiSlopConfig): JudgeFn;
3621
+ interface AntiSlopIssue {
3622
+ category: SlopCategory;
3623
+ detail: string;
3624
+ example?: string;
3625
+ }
3626
+ interface AntiSlopReport {
3627
+ /** 0–10 score; 10 is clean, lower values mean more slop. */
3628
+ score: number;
3629
+ issues: AntiSlopIssue[];
3630
+ /** Count of each category for programmatic aggregation. */
3631
+ counts: Record<SlopCategory, number>;
3632
+ }
3633
+ /**
3634
+ * Pure function — analyze one or more outputs against the config. Exposed
3635
+ * separately so consumers can build their own reporters on top.
3636
+ */
3637
+ declare function analyzeAntiSlop(outputs: string[], config: Omit<Required<AntiSlopConfig>, 'domain'> & {
3638
+ penaltyWeights: Record<SlopCategory, number>;
3639
+ }): AntiSlopReport;
3640
+ declare function assertHttpOk(name: string, res: Response): HarnessAssertion;
3641
+ declare function assertStatus(name: string, actual: string, expected: string | string[]): HarnessAssertion;
3642
+ declare function assertExists(name: string, value: unknown): HarnessAssertion;
3643
+ declare function assertLatencyUnder(name: string, actualMs: number, targetMs: number): HarnessAssertion;
3644
+ declare function assertContains(name: string, actual: string, expected: string): HarnessAssertion;
3645
+ declare function assertNotContains(name: string, actual: string, forbidden: string): HarnessAssertion;
3646
+ declare function assertEventCount(name: string, count: number, min: number, max?: number): HarnessAssertion;
3647
+ declare function assertHasField(name: string, obj: Record<string, unknown>, field: string): HarnessAssertion;
3648
+ export { KeywordConceptSpec as $, PolicyEditCandidateRecord as $i, TrialTrace as $n, exportProductBenchmark as $r, ArgumentCheck as $t, contentHash as A, summarizeDistribution as Aa, DetectorSignal as Ai, SteeringOptimizationResult as An, localCommandRunner as Ar, assertNoHiddenLeak as At, PairedPromotionPowerOptions as B, costForTokenPricing as Ba, ScenarioCost as Bi, PromptHandle as Bn, RuntimeTrajectoryEvidenceSummary as Br, CanaryLeak as Bt, runAgentControlLoop as C, CliffsMagnitude as Ca, UserQuestion as Ci, recordRuns as Cn, DiscoverPersonasOptions as Cr, FieldDestination as Ct, index_d_exports as D, pairedCohensDz as Da, ExtractResult as Di, CounterfactualResult as Dn, DirEntry as Dr, JudgeScoreInput as Dt, BudgetGuard as E, interpretCliffs as Ea, ExtractOptions as Ei, CounterfactualMutation as En, CommandRunner as Er, HiddenLeak as Et, gateTreatmentApplied as F, LlmResponseError as Fa, observeAll as Fi, clamp01 as Fn, ActiveLearningOptions as Fr, ViteDeployRunnerInput as Ft, pairedPromotionPower as G, TaskHeadroom as Gi, seatPresets as Gn, ProductBenchmarkManifest as Gr, ContractLintFinding as Gt, PairedValueLaw as H, CostAccountingIncompleteError as Ha, CapabilityHeadroomOptions as Hi, hashContent as Hn, RuntimeTrajectoryRecord as Hr, ActionExecutionPolicy as Ht, PairedPromotionAlternative as I, costReceiptFromLlm as Ia, repeatedActionDetector as Ii, SteeringBundle as In, SynthesisTarget as Ir, WranglerDeployRunnerInput as It, OracleObservation as J, FindingToPolicyEditOptions as Ji, preflightModels as Jn, assertProductBenchmarkRun as Jr, lintTraceContractSpec as Jt, requiredPairsForPairedPromotion as K, assertCapabilityHeadroom as Ki, ModelEndpointCheck as Kn, ProductBenchmarkSplit as Kr, TraceContractSpec as Kt, PairedPromotionAlternativeCell as L, costReceiptFromLlmError as La, CostEntry as Li, ReferenceReplayCaseRun as Ln, proposeSynthesisTargets as Lr, deployGateLayer as Lt, TreatmentGate as M, isTransientLlmError as Ma, RepeatedActionOptions as Mi, RunScore as Mn, BoundedProcessResult as Mr, DeployGateLayerInput as Mt, TreatmentGateInput as N, stripFencedJson as Na, StreamingDetector as Ni, RunScoreWeights as Nn, DEFAULT_BOUNDED_PROCESS_TIMEOUT_MS as Nr, DeployRunResult as Nt, VerdictCacheStore as O, SampleDistribution as Oa, extractErrorCount as Oi, CounterfactualRunner as On, RunCommandInput as Or, NoLeakOptions as Ot, TreatmentGateOptions as P, LlmCallError as Pa, errorStreakDetector as Pi, aggregateRunScore as Pn, runBoundedProcess as Pr, DeployRunner as Pt, jsonShape as Q, PolicyEditAxis as Qi, ReflectionProposal as Qn, ProductBenchmarkSingleRunExportOptions as Qr, traceContract as Qt, PairedPromotionCallRates as R, maximumChargeForLlmRequest as Ra, CostSummary as Ri, ReferenceReplayRun as Rn, ProjectRuntimeTrajectoryEvidenceOptions as Rr, viteDeployRunner as Rt, subjectiveEval as S, twoByTwoPValue as Sa, KnowledgeSensitivity as Si, loadScorecard as Sn, ImprovementVerdictResult as Sr, BlendWeights as St, BudgetBreachError as T, cohensD as Ta, ErrorCountPattern as Ti, CounterfactualContext as Tn, discoverPersonas as Tr, HiddenGradeResult as Tt, RequiredPairsForPairedPromotionOptions as U, CostCeilingReachedError as Ua, CapabilityHeadroomResult as Ui, ModelSeats as Un, RuntimeTrajectoryRunRecord as Ur, ActionPolicyDecision as Ut, PairedPromotionPowerResult as V, costForUsage as Va, AssertCapabilityHeadroomOptions as Vi, PromptRegistry as Vn, RuntimeTrajectoryHookEvent as Vr, checkCanaries as Vt, RequiredPairsForPairedPromotionResult as W, HeadroomInput as Wi, resolveSeat as Wn, projectRuntimeTrajectoryEvidence as Wr, evaluateActionPolicy as Wt, OracleResult as X, PolicyEditAdmission as Xi, jsonlRunRecordBackend as Xn, ProductBenchmarkExportOptions as Xr, checkTraceContracts as Xt, OracleReport as Y, PolicyEdit as Yi, RunRecordBackend as Yn, readProductBenchmarkManifest as Yr, explainTraceContract as Yt, evaluateOracles as Z, PolicyEditAdmissionOptions as Zi, ReflectionContext as Zn, ProductBenchmarkExportResult as Zr, evaluateTraceContract as Zt, blockingKnowledgeEval as _, chiSquaredPValue as _a, KnowledgeFreshness as _i, ScorecardDiff as _n, jsonlReviewStore as _r, AssertSingleBackendOptions as _t, assertHttpOk as a, PolicyEditRisk as aa, TrainSelectionTestPartition as ai, ContractStatus as an, ProposeOutput as ar, EnsembleJudgeOptions as at, userQuestionsForKnowledgeGaps as b, normalTwoSidedPValue as ba, KnowledgeRequirement as bi, diffScorecard as bn, ExperimentStats as br, SingleBackendReport as bt, assertStatus as c, PolicyEditTarget as ca, PairedDeltaTestResult as ci, RetryWrite as cn, Review as cr, JudgeRetryPolicy as ct, MODEL_PRICING as d, SampleSummary as da, LeaderboardOptions as di, CellVerdict as dn, ReviewMemoryEntry as dr, JudgeVerdict as dt, PolicyEditChange as ea, exportProductBenchmarkRuns as ei, ContractCheckResult as en, buildReflectionPrompt as er, KeywordCoverageFinding as et, estimateCost as f, SampleSummaryOptions as fa, LeaderboardRow as fi, DiffScorecardOptions as fn, ReviewMemoryStore as fr, aggregateJudgeVerdicts as ft, acquisitionPlansForKnowledgeGaps as g, chiSquared as ga, KnowledgeBundle as gi, ScorecardCellDiff as gn, inMemoryReviewStore as gr, runIntentMatchJudge as gt, resolveModelPricing as h, LinearRegressionResult as ha, KnowledgeAcquisitionMode as hi, ScorecardCell as hn, createLlmReviewer as hr, IntentMatchResult as ht, assertHasField as i, PolicyEditInit as ia, TrainSelectionTestOptions as ii, ContractSpan as in, ProposeInput as ir, runKeywordCoverageJudgeUrl as it, fileVerdictCache as j, rocAuc as ja, ErrorStreakOptions as ji, SteeringOptimizationRow as jn, BoundedProcessInput as jr, blendHeldout as jt, canonicalJson as k, percentileNearestRank as ka, DetectorEvent as ki, PairwiseSteeringOptimizer as kn, RunCommandResult as kr, RoutedField as kt, analyzeAntiSlop as l, PolicyEditTargetSurface as la, minimumPairsForPairedDeltaTest as li, SpanPredicate as ln, ReviewFn as lr, withJudgeRetry as lt, isModelPriced as m, summarizeSample as ma, DataAcquisitionPlan as mi, Scorecard as mn, VerifyFn as mr, IntentMatchOptions as mt, assertEventCount as n, PolicyEditGainDirection as na, HeldOutPartition as ni, ContractRuleExecution as nn, LlmReviewerConfig as nr, KeywordCoverageResult as nt, assertLatencyUnder as o, PolicyEditSchemaVersion as oa, partitionHeldOut as oi, ContractVerdict as on, ProposeReviewConfig as or, ensembleJudge as ot, estimateTokens as p, sampleMedian as pa, leaderboard as pi, RecordRunsOptions as pn, Verification as pr, IntentMatchInput as pt, Oracle as q, capabilityHeadroom as qi, ModelEndpointRequest as qn, ProductBenchmarkValidationReport as qr, compileTraceContractSpec as qt, assertExists as r, PolicyEditGainUnit as ra, PartitionHeldOutOptions as ri, ContractRun as rn, ProposeFn as rr, runKeywordCoverageJudge as rt, assertNotContains as s, PolicyEditSource as sa, PairedDeltaTestOptions as si, OrderMode as sn, ProposeReviewReport as sr, JudgeRetryOutcome as st, assertContains as t, PolicyEditExpectedGain as ta, productBenchmarkRepoIdentity as ti, ContractRule as tn, parseReflectionResponse as tr, KeywordCoverageOptions as tt, createAntiSlopJudge as u, makePolicyEditCandidateRecord as ua, pairedDeltaTest as ui, TraceContract as un, ReviewInput as ur, EnsembleAggregate as ut, knowledgeReadinessTracePayload as v, fisherExactTwoSided as va, KnowledgeImportance as vi, ScorecardEntry as vn, runProposeReview as vr, BackendDescriptor as vt, ProductClient as w, cliffsDelta as wa, ERROR_COUNT_PATTERNS as wi, recordRunsToScorecard as wn, DiscoveredPersona as wr, HiddenCriteriaGrader as wt, objectiveEval as x, studentTTwoSidedPValue as xa, KnowledgeRequirementCategory as xi, formatScorecardDiff as xn, ImprovementThresholds as xr, assertSingleBackend as xt, scoreKnowledgeReadiness as y, linearRegressionSlope as ya, KnowledgeReadinessReport as yi, ScorecardLogLine as yn, ExperimentRep as yr, SingleBackendDivergence as yt, PairedPromotionPowerCall as z, CostResult as za, CostTracker as zi, ReferenceReplaySplit as zn, RuntimeTrajectoryEvidenceProjection as zr, wranglerDeployRunner as zt };
3649
+ //# sourceMappingURL=index-D4lT7aFu.d.ts.map