@tangle-network/agent-eval 0.117.1 → 0.118.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/analyst/index.d.ts +2772 -21
- package/dist/analyst/index.js +7 -6
- package/dist/analyst/index.js.map +1 -1
- package/dist/belief-state/index.d.ts +706 -10
- package/dist/belief-state/index.js +2 -1
- package/dist/belief-state/index.js.map +1 -1
- package/dist/benchmarks/index.d.ts +958 -14
- package/dist/benchmarks/index.js +12 -10
- package/dist/builder-eval/index.d.ts +449 -4
- package/dist/builder-eval/index.js +4 -3
- package/dist/builder-eval/index.js.map +1 -1
- package/dist/campaign/index.d.ts +4275 -73
- package/dist/campaign/index.js +12 -10
- package/dist/{chunk-VF3XSYTI.js → chunk-33JA4TFA.js} +6 -6
- package/dist/{chunk-4JLWXDYA.js → chunk-3EHHMC6E.js} +2 -2
- package/dist/{chunk-CCZIVI3F.js → chunk-BFW56GTT.js} +2 -2
- package/dist/{chunk-YZPO4UHR.js → chunk-DP3BBMYJ.js} +100 -143
- package/dist/chunk-DP3BBMYJ.js.map +1 -0
- package/dist/{chunk-E4BUPP7Z.js → chunk-HKUCJ437.js} +38 -66
- package/dist/chunk-HKUCJ437.js.map +1 -0
- package/dist/{chunk-HZHNRYHK.js → chunk-K6N6XJJX.js} +2 -2
- package/dist/chunk-KSDQVPLR.js +286 -0
- package/dist/chunk-KSDQVPLR.js.map +1 -0
- package/dist/chunk-MA6HLL3S.js +65 -0
- package/dist/chunk-MA6HLL3S.js.map +1 -0
- package/dist/{chunk-MGEHEHSN.js → chunk-OIIMMLRB.js} +11 -11
- package/dist/{chunk-DXZRATT5.js → chunk-OYZAPX5G.js} +3 -3
- package/dist/chunk-PXE2VKMX.js +140 -0
- package/dist/chunk-PXE2VKMX.js.map +1 -0
- package/dist/{chunk-JSJZ4PJ6.js → chunk-Q442S5AS.js} +17 -17
- package/dist/{chunk-ODVOOEWQ.js → chunk-QBRSJK47.js} +2 -2
- package/dist/{chunk-S2F4J57L.js → chunk-QKEGNI5B.js} +77 -32
- package/dist/chunk-QKEGNI5B.js.map +1 -0
- package/dist/{chunk-5UF54T55.js → chunk-S3UZOQ5Y.js} +34 -7
- package/dist/chunk-S3UZOQ5Y.js.map +1 -0
- package/dist/{chunk-FQNLDL4D.js → chunk-SVH2ANFD.js} +136 -4
- package/dist/chunk-SVH2ANFD.js.map +1 -0
- package/dist/{chunk-GQCZRZ7L.js → chunk-U5CHZ5M3.js} +9 -9
- package/dist/{chunk-TVVP3ZZQ.js → chunk-VQMK5FMP.js} +2 -1
- package/dist/{chunk-TVVP3ZZQ.js.map → chunk-VQMK5FMP.js.map} +1 -1
- package/dist/chunk-WDHBCA3M.js +31 -0
- package/dist/chunk-WDHBCA3M.js.map +1 -0
- package/dist/{chunk-HQPHZGL6.js → chunk-YLMUS4MM.js} +9 -9
- package/dist/{chunk-LQUTGLOZ.js → chunk-ZET2UAYW.js} +15 -65
- package/dist/chunk-ZET2UAYW.js.map +1 -0
- package/dist/contract/index.d.ts +4012 -38
- package/dist/contract/index.js +56 -29
- package/dist/contract/index.js.map +1 -1
- package/dist/control.d.ts +1013 -9
- package/dist/control.js +4 -3
- package/dist/fuzz.d.ts +194 -4
- package/dist/fuzz.js +3 -3
- package/dist/hosted/index.d.ts +498 -17
- package/dist/index.d.ts +10982 -1286
- package/dist/index.js +97 -80
- package/dist/index.js.map +1 -1
- package/dist/matrix/index.d.ts +139 -4
- package/dist/meta-eval/index.d.ts +862 -15
- package/dist/meta-eval/index.js +2 -1
- package/dist/meta-eval/index.js.map +1 -1
- package/dist/multishot/index.d.ts +214 -14
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.d.ts +392 -7
- package/dist/pipelines/index.js +5 -3
- package/dist/pipelines/index.js.map +1 -1
- package/dist/reporting.d.ts +1277 -17
- package/dist/rl.d.ts +2359 -28
- package/dist/rl.js +6 -5
- package/dist/rl.js.map +1 -1
- package/dist/storyboard/index.d.ts +86 -1
- package/dist/trace-attributes.d.ts +16 -0
- package/dist/trace-attributes.js +32 -0
- package/dist/trace-attributes.js.map +1 -0
- package/dist/traces.d.ts +1970 -697
- package/dist/traces.js +49 -32
- package/dist/wire/index.d.ts +655 -9
- package/docs/insight-report.md +44 -0
- package/package.json +9 -3
- package/dist/adversarial-B7loGVVX.d.ts +0 -19
- package/dist/analyst-C8HHvfJp.d.ts +0 -88
- package/dist/analyze-runs--2x39HZ7.d.ts +0 -81
- package/dist/baseline-DKq3gJpP.d.ts +0 -141
- package/dist/calibration-C8MTS7cw.d.ts +0 -101
- package/dist/chunk-5UF54T55.js.map +0 -1
- package/dist/chunk-E4BUPP7Z.js.map +0 -1
- package/dist/chunk-FQNLDL4D.js.map +0 -1
- package/dist/chunk-LQUTGLOZ.js.map +0 -1
- package/dist/chunk-S2F4J57L.js.map +0 -1
- package/dist/chunk-YZPO4UHR.js.map +0 -1
- package/dist/code-agent-session-CjZsVd19.d.ts +0 -87
- package/dist/control-6vuGfmDH.d.ts +0 -258
- package/dist/cost-ledger-DWy3XdJc.d.ts +0 -183
- package/dist/dataset-NENEzRgk.d.ts +0 -115
- package/dist/default-registry-DaK8b3fv.d.ts +0 -155
- package/dist/emitter-CjD7vUwv.d.ts +0 -122
- package/dist/errors-oeQrLqXC.d.ts +0 -74
- package/dist/failure-cluster-DOAcSJ87.d.ts +0 -76
- package/dist/feedback-trajectory-BUnM58xL.d.ts +0 -348
- package/dist/gepa-eESocoDi.d.ts +0 -642
- package/dist/index-PdX4VnPA.d.ts +0 -423
- package/dist/insight-report-DY4nDW9Q.d.ts +0 -310
- package/dist/integrity-DqlBiLyK.d.ts +0 -81
- package/dist/judge-calibration-7C-IDmKr.d.ts +0 -145
- package/dist/kind-factory-ClZmO25A.d.ts +0 -171
- package/dist/llm-client-qoDd18Qz.d.ts +0 -289
- package/dist/multi-layer-verifier-BsqKuLyN.d.ts +0 -150
- package/dist/off-policy-DiwuKKg7.d.ts +0 -132
- package/dist/outcome-store-rnXLEqSn.d.ts +0 -63
- package/dist/policy-edit-wG9uFEFm.d.ts +0 -455
- package/dist/pre-registration-BWQhJ3vz.d.ts +0 -761
- package/dist/provenance-DpjwyseI.d.ts +0 -541
- package/dist/query-CF7PG61p.d.ts +0 -35
- package/dist/raw-provider-sink-C46HDghv.d.ts +0 -132
- package/dist/release-report-C8G2i5Xi.d.ts +0 -236
- package/dist/researcher-C8XyxQsu.d.ts +0 -387
- package/dist/rubric-predictive-validity-p49lLVrE.d.ts +0 -105
- package/dist/run-record-BDH49H2E.d.ts +0 -360
- package/dist/runtime-trajectory-DGBIUt4B.d.ts +0 -49
- package/dist/schema-B3Q3l9Z_.d.ts +0 -201
- package/dist/semantic-concept-judge-CXnPEJbf.d.ts +0 -723
- package/dist/sequential-5iSVfzl2.d.ts +0 -139
- package/dist/series-convergence-D5OWMBg6.d.ts +0 -33
- package/dist/statistics-KUnG73jH.d.ts +0 -494
- package/dist/storage-DrX3v_5B.d.ts +0 -50
- package/dist/store-C1YxJDEK.d.ts +0 -248
- package/dist/store-DGqD0Pyo.d.ts +0 -116
- package/dist/summary-report-C5bKFfm-.d.ts +0 -445
- package/dist/test-graded-scenario-B0ybnPY7.d.ts +0 -166
- package/dist/types-BSw1rOUB.d.ts +0 -634
- package/dist/types-BUxNaJ8c.d.ts +0 -108
- package/dist/types-BkfcQnxV.d.ts +0 -313
- package/dist/verdict-C9MlYujm.d.ts +0 -35
- /package/dist/{chunk-VF3XSYTI.js.map → chunk-33JA4TFA.js.map} +0 -0
- /package/dist/{chunk-4JLWXDYA.js.map → chunk-3EHHMC6E.js.map} +0 -0
- /package/dist/{chunk-CCZIVI3F.js.map → chunk-BFW56GTT.js.map} +0 -0
- /package/dist/{chunk-HZHNRYHK.js.map → chunk-K6N6XJJX.js.map} +0 -0
- /package/dist/{chunk-MGEHEHSN.js.map → chunk-OIIMMLRB.js.map} +0 -0
- /package/dist/{chunk-DXZRATT5.js.map → chunk-OYZAPX5G.js.map} +0 -0
- /package/dist/{chunk-JSJZ4PJ6.js.map → chunk-Q442S5AS.js.map} +0 -0
- /package/dist/{chunk-ODVOOEWQ.js.map → chunk-QBRSJK47.js.map} +0 -0
- /package/dist/{chunk-GQCZRZ7L.js.map → chunk-U5CHZ5M3.js.map} +0 -0
- /package/dist/{chunk-HQPHZGL6.js.map → chunk-YLMUS4MM.js.map} +0 -0
package/dist/campaign/index.d.ts
CHANGED
|
@@ -1,37 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { P as PairedArmsComparison, S as SignedManifest, B as BackendIntegrityReport, C as CompletionRequirement, R as RuntimeEventLike, a as CompletionVerdict, b as ProducedState, c as CorrectnessChecker } from '../pre-registration-BWQhJ3vz.js';
|
|
4
|
-
export { L as LlmJudgeDimension, d as LlmJudgeOptions, l as llmJudge } from '../pre-registration-BWQhJ3vz.js';
|
|
5
|
-
import { C as CampaignRunPlan, P as PlanCampaignRunOptions, h as RunCampaignOptions, i as RunImprovementLoopOptions } from '../gepa-eESocoDi.js';
|
|
6
|
-
export { o as CampaignRunPlanCell, p as GepaProposerConstraints, G as GepaProposerOptions, O as OpenAutoPrOptions, q as OpenAutoPrResult, e as ReferenceEquivalenceJudgeOptions, g as ReferenceEquivalenceScenario, a as RunImprovementLoopResult, R as RunOptimizationOptions, s as RunOptimizationResult, t as countSentenceEdits, j as createReferenceEquivalenceJudge, u as defaultRenderDiff, v as extractH2Sections, k as gepaProposer, w as openAutoPr, x as planCampaignRun, r as runCampaign, l as runImprovementLoop, y as runOptimization } from '../gepa-eESocoDi.js';
|
|
7
|
-
import { A as AnalyzeTracesOptions, a as AnalyzeTracesInput, b as AnalyzeTracesResult } from '../analyst-C8HHvfJp.js';
|
|
8
|
-
import { S as Scenario, M as MutableSurface, D as DispatchContext, b as JudgeConfig, G as Gate, o as GenerationRecord, J as JudgeScore, L as LabeledScenarioStore, s as LabeledScenarioWrite, t as LabeledScenarioSampleArgs, u as LabeledScenarioRecord, v as LabelTrust, c as SurfaceProposer, w as ProposedCandidate, x as ProposeContext, j as CodeSurface, y as LabeledScenarioSource, C as CampaignResult } from '../types-BSw1rOUB.js';
|
|
9
|
-
export { e as CampaignAggregates, f as CampaignArtifactWriter, g as CampaignCellResult, h as CampaignCostMeter, z as CampaignTokenUsage, i as CampaignTraceWriter, k as DispatchFn, l as GateContext, d as GateDecision, m as GateResult, n as GenerationCandidate, A as JudgeAggregate, a as JudgeDimension, p as Mutator, O as OptimizationProposer, q as OptimizerConfig, P as ParetoParent, R as RedactionStatus, B as ScenarioAggregate, E as ScoredSurfaceOutcome, r as SessionScript, T as TraceSpan, F as isProposedCandidate, H as labelTrustRank } from '../types-BSw1rOUB.js';
|
|
10
|
-
import { a as PairedBootstrapResult, E as EProcessState } from '../statistics-KUnG73jH.js';
|
|
11
|
-
import { C as CampaignStorage } from '../storage-DrX3v_5B.js';
|
|
12
|
-
export { c as createRunCostLedger, f as fsCampaignStorage, i as inMemoryCampaignStorage } from '../storage-DrX3v_5B.js';
|
|
13
|
-
export { A as AxisEvidence, a as AxisVerdict, B as BuildEvidenceVectorOptions, l as BuildLoopProvenanceArgs, D as DefaultProductionGateOptions, m as EmitLoopProvenanceArgs, n as EmitLoopProvenanceResult, E as EvidenceVector, b as EvolutionaryProposerOptions, H as HeldOutGateOptions, o as LoopProvenanceBackend, q as LoopProvenanceCandidate, L as LoopProvenanceRecord, O as ObjectiveSource, c as ParetoSignificanceGateOptions, P as PowerPreflight, s as PowerPreflightOptions, d as PromotionObjective, e as PromotionPolicy, R as RunEvalOptions, f as buildEvidenceVector, t as buildLoopProvenanceRecord, g as composeGate, h as defaultProductionGate, u as emitLoopProvenance, i as evolutionaryProposer, j as heldOutGate, v as loopProvenanceSpans, p as paretoPolicy, k as paretoSignificanceGate, w as powerPreflight, x as provenanceRecordPath, y as provenanceSpansPath, r as runEval } from '../provenance-DpjwyseI.js';
|
|
14
|
-
import { a as LlmClientOptions } from '../llm-client-qoDd18Qz.js';
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AxAIService, AxFunction } from '@ax-llm/ax';
|
|
15
3
|
import { AgentProfile } from '@tangle-network/agent-interface';
|
|
16
|
-
import { A as AgentEvalError, V as ValidationError } from '../errors-oeQrLqXC.js';
|
|
17
|
-
import { a as RunSplitTag, R as RunRecord } from '../run-record-BDH49H2E.js';
|
|
18
|
-
import { C as CostLedger, b as CostLedgerSummary, M as MaximumCharge, c as CostReceiptInput } from '../cost-ledger-DWy3XdJc.js';
|
|
19
|
-
import { T as TraceAnalystKindSpec } from '../kind-factory-ClZmO25A.js';
|
|
20
|
-
import '../store-C1YxJDEK.js';
|
|
21
|
-
import '../types-BkfcQnxV.js';
|
|
22
|
-
import '@tangle-network/tcloud';
|
|
23
|
-
import 'zod';
|
|
24
|
-
import '../raw-provider-sink-C46HDghv.js';
|
|
25
|
-
import '../verdict-C9MlYujm.js';
|
|
26
|
-
import '../dataset-NENEzRgk.js';
|
|
27
|
-
import '../store-DGqD0Pyo.js';
|
|
28
|
-
import '../schema-B3Q3l9Z_.js';
|
|
29
|
-
import '@ax-llm/ax';
|
|
30
|
-
import '../judge-calibration-7C-IDmKr.js';
|
|
31
|
-
import '../hosted/index.js';
|
|
32
|
-
import '../insight-report-DY4nDW9Q.js';
|
|
33
|
-
import '../summary-report-C5bKFfm-.js';
|
|
34
|
-
import '../failure-cluster-DOAcSJ87.js';
|
|
35
4
|
|
|
36
5
|
/**
|
|
37
6
|
* Lineage DAG — a git-graph of improvement candidates.
|
|
@@ -270,6 +239,1941 @@ interface RunLineageResult {
|
|
|
270
239
|
* every node. Honors `prune`: a pruned track is never extended or branched again. */
|
|
271
240
|
declare function runLineage(opts: RunLineageOptions): Promise<RunLineageResult>;
|
|
272
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Error taxonomy for `@tangle-network/agent-eval`.
|
|
244
|
+
*
|
|
245
|
+
* Every error this package throws as part of its *public contract* extends
|
|
246
|
+
* `AgentEvalError`. Consumers can pattern-match by `instanceof <Subclass>` or
|
|
247
|
+
* by the stable string `code` carried on the base class.
|
|
248
|
+
*
|
|
249
|
+
* The codes are stable across minor versions; new codes can be added, but
|
|
250
|
+
* existing codes never change meaning. New subclasses are non-breaking.
|
|
251
|
+
*
|
|
252
|
+
* Internal invariant guards (`throw new Error('this should never happen')`)
|
|
253
|
+
* remain plain `Error`s on purpose — they're programmer-mistake assertions,
|
|
254
|
+
* not consumer-catchable contract failures.
|
|
255
|
+
*/
|
|
256
|
+
type AgentEvalErrorCode = 'validation' | 'not_found' | 'config' | 'capture_integrity' | 'judge' | 'verification' | 'replay' | 'backend_integrity' | 'profile_matrix';
|
|
257
|
+
/**
|
|
258
|
+
* Base class for every contract error this package throws — carries the stable
|
|
259
|
+
* string `code` taxonomy so consumers can `instanceof`-match or switch on `code`.
|
|
260
|
+
*/
|
|
261
|
+
declare class AgentEvalError extends Error {
|
|
262
|
+
/** Stable string code. Survives minification; safe to switch on. */
|
|
263
|
+
readonly code: AgentEvalErrorCode;
|
|
264
|
+
constructor(code: AgentEvalErrorCode, message: string, options?: {
|
|
265
|
+
cause?: unknown;
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/** Caller passed invalid arguments (out of range, mutually-exclusive options, bad shape). */
|
|
269
|
+
declare class ValidationError extends AgentEvalError {
|
|
270
|
+
constructor(message: string, options?: {
|
|
271
|
+
cause?: unknown;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
type AgentProfileCellSchemaVersion = 'agent-profile-cell/v1';
|
|
276
|
+
type AgentProfileJson = string | number | boolean | null | AgentProfileJson[] | {
|
|
277
|
+
[key: string]: AgentProfileJson;
|
|
278
|
+
};
|
|
279
|
+
type AgentProfileDimensionValue = string | number | boolean | null;
|
|
280
|
+
interface AgentProfileSource {
|
|
281
|
+
/** Runtime/profile contract being fingerprinted, e.g. `agent-interface-profile`. */
|
|
282
|
+
kind: string;
|
|
283
|
+
/** sha256 over the canonical source profile object. */
|
|
284
|
+
hash: string;
|
|
285
|
+
}
|
|
286
|
+
interface AgentProfileHarness {
|
|
287
|
+
id: string;
|
|
288
|
+
version?: string;
|
|
289
|
+
hash?: string;
|
|
290
|
+
}
|
|
291
|
+
interface AgentProfileCell {
|
|
292
|
+
schemaVersion: AgentProfileCellSchemaVersion;
|
|
293
|
+
cellId: string;
|
|
294
|
+
profileId: string;
|
|
295
|
+
sourceProfile: AgentProfileSource;
|
|
296
|
+
harness?: AgentProfileHarness;
|
|
297
|
+
model?: string;
|
|
298
|
+
promptHash?: string;
|
|
299
|
+
dimensions?: Record<string, AgentProfileDimensionValue>;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
type FailureClass = 'success' | 'reasoning_error' | 'tool_selection_error' | 'tool_argument_error' | 'tool_recovery_failure' | 'hallucination' | 'instruction_following' | 'safety_refusal_miss' | 'policy_violation' | 'budget_exceeded' | 'format_drift' | 'permission_escalation' | 'pii_leak' | 'cost_overrun' | 'timeout' | 'sandbox_failure' | 'missing_user_data' | 'missing_domain_data' | 'missing_codebase_context' | 'missing_runtime_context' | 'missing_credentials' | 'missing_integration_connection' | 'missing_integration_scope' | 'integration_approval_required' | 'integration_auth_expired' | 'integration_provider_failure' | 'bad_integration_manifest' | 'unsafe_integration_write_denied' | 'stale_external_data' | 'bad_retrieval' | 'insufficient_evidence' | 'contradictory_evidence' | 'ambiguous_user_intent' | 'knowledge_readiness_blocked' | 'unknown';
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Paper-grade RunRecord schema + runtime validator.
|
|
306
|
+
*
|
|
307
|
+
* Every run that participates in a promotion gate, paper table, or
|
|
308
|
+
* researcher loop SHOULD be recorded as a `RunRecord`. The mandatory
|
|
309
|
+
* fields are exactly those the paper "Two Loops, Three Roles" requires
|
|
310
|
+
* for reproducibility: who/what/when/cost/seed/hash, plus the search vs
|
|
311
|
+
* holdout split tag and either a `searchScore` or a `holdoutScore`.
|
|
312
|
+
*
|
|
313
|
+
* This is intentionally NOT a replacement for the rich `Run` /
|
|
314
|
+
* `ProposeReviewReport` / `ScenarioResult` types already in the
|
|
315
|
+
* package. Those are runtime structures with full provenance. A
|
|
316
|
+
* `RunRecord` is the analysis-time projection — the JSON-friendly
|
|
317
|
+
* row you'd put in a parquet file or paste into a notebook.
|
|
318
|
+
*
|
|
319
|
+
* Validate at the boundary:
|
|
320
|
+
*
|
|
321
|
+
* const rec = validateRunRecord(rawJson) // throws on missing
|
|
322
|
+
* const ok = isRunRecord(rawJson) // boolean check
|
|
323
|
+
* const rec = parseRunRecordSafe(rawJson) // { ok, value | error }
|
|
324
|
+
*
|
|
325
|
+
* The validator runs in pure TS — zod is intentionally NOT a
|
|
326
|
+
* dependency. Round-trip tested in `tests/run-record.test.ts`.
|
|
327
|
+
*/
|
|
328
|
+
|
|
329
|
+
/** Search/dev/holdout split tag. 'search' is the paper-grade alias for the
|
|
330
|
+
* combined train+test pool that the optimizer is allowed to read. */
|
|
331
|
+
type RunSplitTag = 'search' | 'dev' | 'holdout';
|
|
332
|
+
interface RunTokenUsage {
|
|
333
|
+
input: number;
|
|
334
|
+
/** All generated tokens charged as output, including reasoning tokens. */
|
|
335
|
+
output: number;
|
|
336
|
+
/** Reasoning-token subset of `output`, when the provider reports it. */
|
|
337
|
+
reasoning?: number;
|
|
338
|
+
/** Prompt tokens served from a provider cache. */
|
|
339
|
+
cached?: number;
|
|
340
|
+
/** Prompt tokens written into a provider cache. */
|
|
341
|
+
cacheWrite?: number;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* How a run's USD amount was obtained.
|
|
345
|
+
*
|
|
346
|
+
* `costUsd` remains mandatory for wire compatibility. New producers should
|
|
347
|
+
* always populate this discriminated union so a missing bill is never
|
|
348
|
+
* mistaken for an observed zero-dollar run. For `uncaptured`, `costUsd` uses
|
|
349
|
+
* the legacy `0` sentinel while this field carries the truthful null.
|
|
350
|
+
*/
|
|
351
|
+
type RunCostProvenance = {
|
|
352
|
+
kind: 'observed';
|
|
353
|
+
usd: number;
|
|
354
|
+
} | {
|
|
355
|
+
kind: 'estimated';
|
|
356
|
+
usd: number;
|
|
357
|
+
} | {
|
|
358
|
+
kind: 'uncaptured';
|
|
359
|
+
usd: null;
|
|
360
|
+
};
|
|
361
|
+
interface RunJudgeMetadata {
|
|
362
|
+
model: string;
|
|
363
|
+
promptVersion: string;
|
|
364
|
+
/** [0,1] confidence the judge declared. Constant judge confidence
|
|
365
|
+
* across many runs is a fallback signal (see `canary.ts`). */
|
|
366
|
+
confidence: number;
|
|
367
|
+
/** True if the judge degraded to a fallback path (rules-only,
|
|
368
|
+
* prior-call cache, etc.). The canary uses this to alert. */
|
|
369
|
+
fallback: boolean;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Per-judge / per-dimension breakdown for runs scored by an ensemble of
|
|
373
|
+
* judges over a multi-dimensional rubric.
|
|
374
|
+
*
|
|
375
|
+
* The collapsed `outcome.searchScore` / `holdoutScore` carries the
|
|
376
|
+
* composite the gate uses. The full breakdown belongs here so consumers
|
|
377
|
+
* can answer "which judge disagreed?", "which dimension dragged the
|
|
378
|
+
* composite down?", and "did half the panel fail?" without re-running.
|
|
379
|
+
*
|
|
380
|
+
* `perJudge[judgeId][dim]` is the canonical source; `perDimMean` and
|
|
381
|
+
* `composite` are convenience projections — derivable but precomputed so
|
|
382
|
+
* downstream IRR primitives (`interRaterReliability`,
|
|
383
|
+
* `corpusInterRaterAgreement`) and reporters don't pay the same
|
|
384
|
+
* aggregation twice.
|
|
385
|
+
*
|
|
386
|
+
* Fail-loud discipline: judges that errored out land in `failedJudges`
|
|
387
|
+
* by id. A missing key in `perJudge` is ambiguous (silent zero vs not
|
|
388
|
+
* run); the explicit list makes a partial-failure recorded as such.
|
|
389
|
+
*/
|
|
390
|
+
interface JudgeScoresRecord {
|
|
391
|
+
/** Per-judge per-dimension scores. `{ "kimi-k2.6": { helpfulness: 0.8, clarity: 0.7 }, ... }`. */
|
|
392
|
+
perJudge: Record<string, Record<string, number>>;
|
|
393
|
+
/** Per-dim mean across judges. Convenience — derivable from `perJudge`. */
|
|
394
|
+
perDimMean: Record<string, number>;
|
|
395
|
+
/** Composite mean across all dims and judges. Mirrors the score
|
|
396
|
+
* the gate sees on `outcome.searchScore` / `holdoutScore`. */
|
|
397
|
+
composite: number;
|
|
398
|
+
/** Judges that errored or returned an unparseable verdict. Recorded
|
|
399
|
+
* by id (e.g. `['glm-5.1']`) so a partial-failure case is explicit,
|
|
400
|
+
* not inferred from missing keys in `perJudge`. */
|
|
401
|
+
failedJudges?: string[];
|
|
402
|
+
/** Free-form notes the judges emitted (joined across judges or
|
|
403
|
+
* first-judge only — consumer's choice). */
|
|
404
|
+
notes?: string;
|
|
405
|
+
}
|
|
406
|
+
interface RunOutcome {
|
|
407
|
+
/** Score on the search/optimization split. Optional because a
|
|
408
|
+
* holdout-only evaluation only fills `holdoutScore`. */
|
|
409
|
+
searchScore?: number;
|
|
410
|
+
/** Score on the held-out split. Optional because a search-only run
|
|
411
|
+
* only fills `searchScore`. At least one must be present. */
|
|
412
|
+
holdoutScore?: number;
|
|
413
|
+
/** Bag of any other metric the run produced — judge dimensions,
|
|
414
|
+
* pass/fail counters, latency stats, etc. Numeric only — keeps
|
|
415
|
+
* reporters honest. */
|
|
416
|
+
raw: Record<string, number>;
|
|
417
|
+
/** Per-judge / per-dim breakdown. Consumers writing ensemble
|
|
418
|
+
* judgements populate this; substrate primitives like
|
|
419
|
+
* `interRaterReliability` and `corpusInterRaterAgreement` accept
|
|
420
|
+
* these records as input. Optional — single-judge or scalar-only
|
|
421
|
+
* runs leave it unset. */
|
|
422
|
+
judgeScores?: JudgeScoresRecord;
|
|
423
|
+
/** Authenticity / realness verdict — did the run build the REAL thing on the
|
|
424
|
+
* intended infra, or fake it (see `./authenticity`)? Optional: only domains
|
|
425
|
+
* with an authenticity config populate it. Carried in the corpus so the
|
|
426
|
+
* flywheel / off-policy learning can optimize for real completion, not gamed
|
|
427
|
+
* pass-rate. `score` is 0-1; `gated` is the anti-Goodhart flag — a gated run
|
|
428
|
+
* must not count as a real success regardless of `score`. */
|
|
429
|
+
realness?: {
|
|
430
|
+
score: number;
|
|
431
|
+
gated: boolean;
|
|
432
|
+
reason?: string;
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Mandatory paper-grade fields for a single evaluation run. Optional
|
|
437
|
+
* fields are extension points; mandatory fields throw if missing.
|
|
438
|
+
*
|
|
439
|
+
* Hash discipline:
|
|
440
|
+
* - `promptHash` is the sha256 of the EFFECTIVE prompt sent to the
|
|
441
|
+
* model (after any steering bundle merge).
|
|
442
|
+
* - `configHash` is the sha256 of the effective run config (model,
|
|
443
|
+
* temperature, tools, judges, splits). The pair (promptHash,
|
|
444
|
+
* configHash) uniquely identifies an experiment cell.
|
|
445
|
+
*
|
|
446
|
+
* Model snapshot discipline:
|
|
447
|
+
* - `model` MUST encode a snapshot version. Bare aliases like
|
|
448
|
+
* `claude-sonnet-4` or `gpt-4o` are banned — they remap silently.
|
|
449
|
+
* Use `claude-sonnet-4-6@2025-04-15` or `gpt-4o-2024-11-20`.
|
|
450
|
+
*/
|
|
451
|
+
interface RunRecord {
|
|
452
|
+
/** UUID for the run. */
|
|
453
|
+
runId: string;
|
|
454
|
+
/** Logical experiment grouping (a treatment vs a baseline within
|
|
455
|
+
* the same sweep should share `experimentId`). */
|
|
456
|
+
experimentId: string;
|
|
457
|
+
/** Stable identifier for the candidate (variant) being run. The
|
|
458
|
+
* promotion gate compares two `candidateId`s on matched items. */
|
|
459
|
+
candidateId: string;
|
|
460
|
+
/** RNG seed for the run. Always recorded — silent re-seeding is
|
|
461
|
+
* the most common cause of non-reproducible numbers. */
|
|
462
|
+
seed: number;
|
|
463
|
+
/** Model identifier WITH snapshot version. */
|
|
464
|
+
model: string;
|
|
465
|
+
/** sha256 of the effective prompt (post-steering). */
|
|
466
|
+
promptHash: string;
|
|
467
|
+
/** sha256 of the effective config. */
|
|
468
|
+
configHash: string;
|
|
469
|
+
/** Git SHA the harness was run from. */
|
|
470
|
+
commitSha: string;
|
|
471
|
+
/** End-to-end wall-clock duration in milliseconds. */
|
|
472
|
+
wallMs: number;
|
|
473
|
+
/** Time spent queued before execution started, if known. */
|
|
474
|
+
queueMs?: number;
|
|
475
|
+
/** Total USD cost. Mandatory — runs without a cost number are
|
|
476
|
+
* unbounded by definition and must not be admitted into the gate.
|
|
477
|
+
* `0` is retained as the compatibility sentinel for an uncaptured amount;
|
|
478
|
+
* inspect `costProvenance` before treating it as observed. */
|
|
479
|
+
costUsd: number;
|
|
480
|
+
/** Observed, model-priced estimate, or genuinely uncaptured USD amount.
|
|
481
|
+
* Optional only so existing serialized RunRecords remain valid. */
|
|
482
|
+
costProvenance?: RunCostProvenance;
|
|
483
|
+
/** Token usage breakdown. */
|
|
484
|
+
tokenUsage: RunTokenUsage;
|
|
485
|
+
/** Judge-side metadata, if a judge was used. */
|
|
486
|
+
judgeMetadata?: RunJudgeMetadata;
|
|
487
|
+
/** Per-split scores + raw bag. */
|
|
488
|
+
outcome: RunOutcome;
|
|
489
|
+
/** Canonical, cross-agent failure class drawn from the shared
|
|
490
|
+
* `FAILURE_CLASSES` taxonomy. This is the aggregation key that makes
|
|
491
|
+
* "which failure dominates across the whole fleet" answerable in ONE
|
|
492
|
+
* vocabulary — every agent classifies against the same enum. Producers
|
|
493
|
+
* set it via the substrate classifier; leave unset only when the failure
|
|
494
|
+
* genuinely can't be classified. */
|
|
495
|
+
failureClass?: FailureClass;
|
|
496
|
+
/** Free-form domain-specific failure detail, scoped UNDER `failureClass`
|
|
497
|
+
* (e.g. failureClass='tool_recovery_failure', failureMode='forge_build_unsatisfied').
|
|
498
|
+
* The within-agent drill-down; `failureClass` is the cross-agent key. */
|
|
499
|
+
failureMode?: string;
|
|
500
|
+
/** Which split this run was drawn from. */
|
|
501
|
+
splitTag: RunSplitTag;
|
|
502
|
+
/**
|
|
503
|
+
* Stable scenario identifier the run was scored against. Optional for
|
|
504
|
+
* backwards compatibility, but **strongly recommended**: every primitive
|
|
505
|
+
* that pairs runs by scenario (preferences, paired stats, BT tournament)
|
|
506
|
+
* keys on this. The campaign artifact populates it canonically; legacy
|
|
507
|
+
* runs without it fall back to inference from `outcome.raw.scenario_id`
|
|
508
|
+
* or `experimentId`.
|
|
509
|
+
*/
|
|
510
|
+
scenarioId?: string;
|
|
511
|
+
/**
|
|
512
|
+
* Canonical identity for the agent profile cell that produced this row:
|
|
513
|
+
* profile artifact hash plus optional harness/model/prompt/reporting
|
|
514
|
+
* dimensions. Use `agentProfile.cellId` to group persona sweeps and
|
|
515
|
+
* longitudinal reports by the complete source profile, not by a loose
|
|
516
|
+
* candidate label or opaque config hash.
|
|
517
|
+
*/
|
|
518
|
+
agentProfile?: AgentProfileCell;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Shared types for the trace-analyst module.
|
|
523
|
+
*
|
|
524
|
+
* Wire format. The store interface speaks `OtlpSpanLike` rows — one JSONL
|
|
525
|
+
* line per span, OTLP-shaped. We do NOT depend on a specific tracing
|
|
526
|
+
* vendor at the type level. Adapter
|
|
527
|
+
* layers map upstream shapes onto this interface.
|
|
528
|
+
*
|
|
529
|
+
* Design constraint. Every read operation that can return arbitrary
|
|
530
|
+
* payload must carry a byte budget so the agent's tool result stays
|
|
531
|
+
* bounded regardless of input trace size. Oversized responses
|
|
532
|
+
* substitute a deterministic summary instead of bytes — see
|
|
533
|
+
* `ViewTraceOversized`.
|
|
534
|
+
*/
|
|
535
|
+
/** OTLP span kind (subset we actually use). */
|
|
536
|
+
type TraceAnalystSpanKind = 'AGENT' | 'LLM' | 'TOOL' | 'CHAIN' | 'GUARDRAIL' | 'SPAN' | 'UNKNOWN';
|
|
537
|
+
type TraceAnalystSpanStatus = 'OK' | 'ERROR' | 'UNSET';
|
|
538
|
+
/** Subset of OTLP span fields the analyst exposes to the agent. The
|
|
539
|
+
* store's job is to project upstream's full span shape down to this
|
|
540
|
+
* view — the analyst never sees vendor extensions directly. */
|
|
541
|
+
interface TraceAnalystSpan {
|
|
542
|
+
trace_id: string;
|
|
543
|
+
span_id: string;
|
|
544
|
+
parent_span_id: string | null;
|
|
545
|
+
name: string;
|
|
546
|
+
kind: TraceAnalystSpanKind;
|
|
547
|
+
start_time: string;
|
|
548
|
+
end_time: string;
|
|
549
|
+
duration_ms: number;
|
|
550
|
+
status: TraceAnalystSpanStatus;
|
|
551
|
+
status_message?: string;
|
|
552
|
+
service_name: string | null;
|
|
553
|
+
agent_name: string | null;
|
|
554
|
+
model_name: string | null;
|
|
555
|
+
tool_name: string | null;
|
|
556
|
+
/** Raw JSON-serialisable attribute map. May contain large strings;
|
|
557
|
+
* callers must respect the per-attribute byte cap. */
|
|
558
|
+
attributes: Record<string, unknown>;
|
|
559
|
+
}
|
|
560
|
+
interface TraceAnalystTraceSummary {
|
|
561
|
+
trace_id: string;
|
|
562
|
+
service_name: string | null;
|
|
563
|
+
agent_name: string | null;
|
|
564
|
+
span_count: number;
|
|
565
|
+
has_errors: boolean;
|
|
566
|
+
start_time: string;
|
|
567
|
+
end_time: string;
|
|
568
|
+
duration_ms: number;
|
|
569
|
+
raw_jsonl_bytes: number;
|
|
570
|
+
models: string[];
|
|
571
|
+
tools: string[];
|
|
572
|
+
}
|
|
573
|
+
interface TraceAnalystFilters {
|
|
574
|
+
/** Restrict to traces that contain at least one error span. */
|
|
575
|
+
has_errors?: boolean;
|
|
576
|
+
/** Match if any span's `service.name` is in this list. */
|
|
577
|
+
service_names?: string[];
|
|
578
|
+
/** Match if any span's `agent.name` is in this list. */
|
|
579
|
+
agent_names?: string[];
|
|
580
|
+
/** Match if any LLM span's `llm.model_name` is in this list. */
|
|
581
|
+
model_names?: string[];
|
|
582
|
+
/** Match if any tool span's `tool.name` is in this list. */
|
|
583
|
+
tool_names?: string[];
|
|
584
|
+
/** ISO-8601 lower bound on the trace's earliest start time. */
|
|
585
|
+
start_time_after?: string;
|
|
586
|
+
/** ISO-8601 upper bound on the trace's earliest start time. */
|
|
587
|
+
start_time_before?: string;
|
|
588
|
+
/** Single regex applied to raw JSONL bytes for the trace. Opt-in;
|
|
589
|
+
* expensive on large datasets. Use the indexed filters above first. */
|
|
590
|
+
regex_pattern?: string;
|
|
591
|
+
}
|
|
592
|
+
/** One distinct error signature across the dataset — the deterministic unit of
|
|
593
|
+
* failure coverage. Signatures normalize volatile tokens (digits, hex/uuids,
|
|
594
|
+
* paths, durations) out of the span `status_message` so semantically identical
|
|
595
|
+
* failures collapse into one cluster. An analyst that accounts for every
|
|
596
|
+
* cluster has, by construction, covered every distinct failure mode. */
|
|
597
|
+
interface ErrorCluster {
|
|
598
|
+
/** Normalized status_message — the cluster key. */
|
|
599
|
+
signature: string;
|
|
600
|
+
/** A verbatim, un-normalized exemplar message (for exact-string citation). */
|
|
601
|
+
status_message_sample: string;
|
|
602
|
+
/** The span name that most often carries this signature, if any. */
|
|
603
|
+
span_name: string | null;
|
|
604
|
+
/** The tool that most often carries this signature, if any. */
|
|
605
|
+
tool_name: string | null;
|
|
606
|
+
trace_count: number;
|
|
607
|
+
span_count: number;
|
|
608
|
+
/** trace_count / total error traces in the matched set (0..1). */
|
|
609
|
+
prevalence: number;
|
|
610
|
+
/** Real trace ids carrying this signature (capped), passable to view/search. */
|
|
611
|
+
exemplar_trace_ids: string[];
|
|
612
|
+
/** Real span ids carrying this signature (capped). */
|
|
613
|
+
exemplar_span_ids: string[];
|
|
614
|
+
}
|
|
615
|
+
interface DatasetOverview {
|
|
616
|
+
total_traces: number;
|
|
617
|
+
raw_jsonl_bytes: number;
|
|
618
|
+
services: string[];
|
|
619
|
+
agents: string[];
|
|
620
|
+
models: string[];
|
|
621
|
+
tool_names: string[];
|
|
622
|
+
/** Up to 20 real trace ids the agent may pass to view/search tools. */
|
|
623
|
+
sample_trace_ids: string[];
|
|
624
|
+
errors: {
|
|
625
|
+
trace_count: number;
|
|
626
|
+
span_count: number;
|
|
627
|
+
};
|
|
628
|
+
/** The COMPLETE deterministic error-signature population, sorted by
|
|
629
|
+
* trace_count desc. This is the failure-coverage checklist: an analysis is
|
|
630
|
+
* complete only when every cluster here is accounted for. Empty when the
|
|
631
|
+
* matched set has no error spans. */
|
|
632
|
+
error_clusters: ErrorCluster[];
|
|
633
|
+
time_range: {
|
|
634
|
+
earliest: string;
|
|
635
|
+
latest: string;
|
|
636
|
+
} | null;
|
|
637
|
+
}
|
|
638
|
+
interface QueryTracesPage {
|
|
639
|
+
traces: TraceAnalystTraceSummary[];
|
|
640
|
+
total: number;
|
|
641
|
+
has_more: boolean;
|
|
642
|
+
}
|
|
643
|
+
/** Full-trace view. When the response would exceed the per-call byte
|
|
644
|
+
* budget, `oversized` is populated INSTEAD of `spans` so the agent
|
|
645
|
+
* knows to switch to `searchTrace` / `viewSpans`. */
|
|
646
|
+
interface ViewTraceResult {
|
|
647
|
+
trace_id: string;
|
|
648
|
+
spans?: TraceAnalystSpan[];
|
|
649
|
+
oversized?: ViewTraceOversized;
|
|
650
|
+
}
|
|
651
|
+
interface ViewTraceOversized {
|
|
652
|
+
span_count: number;
|
|
653
|
+
/** Names with their counts, sorted desc. Capped at 20 entries. */
|
|
654
|
+
top_span_names: Array<[string, number]>;
|
|
655
|
+
/** Largest single span body (bytes after attribute-cap projection). */
|
|
656
|
+
span_response_bytes_max: number;
|
|
657
|
+
error_span_count: number;
|
|
658
|
+
}
|
|
659
|
+
interface ViewSpansResult {
|
|
660
|
+
trace_id: string;
|
|
661
|
+
spans: TraceAnalystSpan[];
|
|
662
|
+
/** Number of requested span ids that were not found in the trace. */
|
|
663
|
+
missing_span_ids: string[];
|
|
664
|
+
/** Number of attribute fields truncated to fit the per-attribute cap. */
|
|
665
|
+
truncated_attribute_count: number;
|
|
666
|
+
}
|
|
667
|
+
interface SpanMatchRecord {
|
|
668
|
+
trace_id: string;
|
|
669
|
+
span_id: string;
|
|
670
|
+
span_name: string;
|
|
671
|
+
span_kind: TraceAnalystSpanKind;
|
|
672
|
+
/** JSON pointer-style path to the matched value, e.g.
|
|
673
|
+
* `attributes."llm.input_messages"[2].content`. */
|
|
674
|
+
attribute_path: string;
|
|
675
|
+
matched_text: string;
|
|
676
|
+
context_before: string;
|
|
677
|
+
context_after: string;
|
|
678
|
+
match_offset: number;
|
|
679
|
+
}
|
|
680
|
+
interface SearchTraceResult {
|
|
681
|
+
trace_id: string;
|
|
682
|
+
hits: SpanMatchRecord[];
|
|
683
|
+
total_matches: number;
|
|
684
|
+
has_more: boolean;
|
|
685
|
+
}
|
|
686
|
+
interface SearchSpanResult {
|
|
687
|
+
trace_id: string;
|
|
688
|
+
span_id: string;
|
|
689
|
+
hits: SpanMatchRecord[];
|
|
690
|
+
total_matches: number;
|
|
691
|
+
has_more: boolean;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* `TraceAnalysisStore` — read-side interface the trace-analyst calls
|
|
696
|
+
* through. Six operations, all bounded:
|
|
697
|
+
*
|
|
698
|
+
* - `getOverview(filters?)` — dataset rollup + sample trace ids.
|
|
699
|
+
* - `queryTraces(filters?, limit, offset)` — paginated summaries.
|
|
700
|
+
* - `countTraces(filters?)` — cheap count without materialisation.
|
|
701
|
+
* - `viewTrace(trace_id, perAttrCap)` — full span list, oversized → summary.
|
|
702
|
+
* - `viewSpans(trace_id, span_ids, perAttrCap)` — surgical span fetch.
|
|
703
|
+
* - `searchTrace(trace_id, regex, max_matches)` — bounded regex hits.
|
|
704
|
+
* - `searchSpan(trace_id, span_id, regex, max_matches)` — single-span search.
|
|
705
|
+
*
|
|
706
|
+
* Multiple implementations ship in the core (`OtlpFileTraceStore`).
|
|
707
|
+
* Downstream callers can supply their own — e.g. a DuckDB-backed
|
|
708
|
+
* adapter or an in-memory adapter for tests — by implementing this
|
|
709
|
+
* interface.
|
|
710
|
+
*
|
|
711
|
+
* Filters compose with AND semantics. Empty/undefined fields impose
|
|
712
|
+
* no constraint. `regex_pattern` is the only opt-in raw-bytes scan —
|
|
713
|
+
* implementations may skip it via `count`/`overview` when not set.
|
|
714
|
+
*/
|
|
715
|
+
|
|
716
|
+
interface TraceAnalysisStore {
|
|
717
|
+
getOverview(filters?: TraceAnalystFilters): Promise<DatasetOverview>;
|
|
718
|
+
queryTraces(opts: {
|
|
719
|
+
filters?: TraceAnalystFilters;
|
|
720
|
+
limit: number;
|
|
721
|
+
offset?: number;
|
|
722
|
+
}): Promise<QueryTracesPage>;
|
|
723
|
+
countTraces(filters?: TraceAnalystFilters): Promise<number>;
|
|
724
|
+
viewTrace(opts: {
|
|
725
|
+
trace_id: string;
|
|
726
|
+
/** Override per-attribute byte cap. Defaults to discovery budget. */
|
|
727
|
+
per_attribute_byte_cap?: number;
|
|
728
|
+
}): Promise<ViewTraceResult>;
|
|
729
|
+
viewSpans(opts: {
|
|
730
|
+
trace_id: string;
|
|
731
|
+
span_ids: readonly string[];
|
|
732
|
+
/** Override per-attribute byte cap. Defaults to surgical budget. */
|
|
733
|
+
per_attribute_byte_cap?: number;
|
|
734
|
+
}): Promise<ViewSpansResult>;
|
|
735
|
+
searchTrace(opts: {
|
|
736
|
+
trace_id: string;
|
|
737
|
+
regex_pattern: string;
|
|
738
|
+
/** Hard cap on matches returned. Default 50. */
|
|
739
|
+
max_matches?: number;
|
|
740
|
+
}): Promise<SearchTraceResult>;
|
|
741
|
+
searchSpan(opts: {
|
|
742
|
+
trace_id: string;
|
|
743
|
+
span_id: string;
|
|
744
|
+
regex_pattern: string;
|
|
745
|
+
max_matches?: number;
|
|
746
|
+
}): Promise<SearchSpanResult>;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
type CostChannel = 'agent' | 'judge' | 'verifier' | 'analyst' | 'driver' | (string & {});
|
|
750
|
+
interface CostUsage {
|
|
751
|
+
inputTokens: number;
|
|
752
|
+
outputTokens: number;
|
|
753
|
+
cachedTokens?: number;
|
|
754
|
+
}
|
|
755
|
+
interface CostCallBase {
|
|
756
|
+
callId: string;
|
|
757
|
+
channel: CostChannel;
|
|
758
|
+
phase: string;
|
|
759
|
+
actor: string;
|
|
760
|
+
model: string;
|
|
761
|
+
maximumCostUsd?: number;
|
|
762
|
+
tags?: Record<string, string>;
|
|
763
|
+
timestamp: number;
|
|
764
|
+
}
|
|
765
|
+
interface CostReceipt extends CostCallBase, CostUsage {
|
|
766
|
+
status: 'settled';
|
|
767
|
+
costUsd: number;
|
|
768
|
+
costUnknown: boolean;
|
|
769
|
+
usageUnknown?: boolean;
|
|
770
|
+
pricing?: {
|
|
771
|
+
inputUsdPerThousand: number;
|
|
772
|
+
outputUsdPerThousand: number;
|
|
773
|
+
};
|
|
774
|
+
actualCostUsd?: number;
|
|
775
|
+
error?: string;
|
|
776
|
+
}
|
|
777
|
+
interface CostReceiptInput extends CostUsage {
|
|
778
|
+
model: string;
|
|
779
|
+
actualCostUsd?: number;
|
|
780
|
+
costUnknown?: boolean;
|
|
781
|
+
usageUnknown?: boolean;
|
|
782
|
+
}
|
|
783
|
+
type MaximumCharge = {
|
|
784
|
+
externallyEnforcedMaximumUsd: number;
|
|
785
|
+
} | ({
|
|
786
|
+
model: string;
|
|
787
|
+
} & CostUsage);
|
|
788
|
+
interface RunPaidCallInput<T> {
|
|
789
|
+
callId?: string;
|
|
790
|
+
channel: CostChannel;
|
|
791
|
+
phase: string;
|
|
792
|
+
actor: string;
|
|
793
|
+
/** Used before a provider receipt exists and on failures without one. */
|
|
794
|
+
model?: string;
|
|
795
|
+
tags?: Record<string, string>;
|
|
796
|
+
signal?: AbortSignal;
|
|
797
|
+
/** Provider-enforced dollar maximum, or maximum priced token usage. Required when capped. */
|
|
798
|
+
maximumCharge?: MaximumCharge;
|
|
799
|
+
/** `callId` can be forwarded as the provider's idempotency key. */
|
|
800
|
+
execute(signal: AbortSignal, callId: string): Promise<T>;
|
|
801
|
+
receipt(value: T): CostReceiptInput;
|
|
802
|
+
receiptFromError?(error: Error): CostReceiptInput | undefined;
|
|
803
|
+
}
|
|
804
|
+
type PaidCallResult<T> = {
|
|
805
|
+
succeeded: true;
|
|
806
|
+
callId: string;
|
|
807
|
+
value: T;
|
|
808
|
+
receipt: CostReceipt;
|
|
809
|
+
} | {
|
|
810
|
+
succeeded: false;
|
|
811
|
+
callId?: string;
|
|
812
|
+
error: Error;
|
|
813
|
+
receipt?: CostReceipt;
|
|
814
|
+
};
|
|
815
|
+
interface ChannelRollup {
|
|
816
|
+
channel: CostChannel;
|
|
817
|
+
calls: number;
|
|
818
|
+
inputTokens: number;
|
|
819
|
+
outputTokens: number;
|
|
820
|
+
cachedTokens: number;
|
|
821
|
+
costUsd: number;
|
|
822
|
+
unpricedCalls: number;
|
|
823
|
+
unknownUsageCalls: number;
|
|
824
|
+
}
|
|
825
|
+
interface CostLedgerSummary {
|
|
826
|
+
totalCalls: number;
|
|
827
|
+
pendingCalls: number;
|
|
828
|
+
unresolvedCalls: number;
|
|
829
|
+
reservedCostUsd: number;
|
|
830
|
+
inputTokens: number;
|
|
831
|
+
outputTokens: number;
|
|
832
|
+
cachedTokens: number;
|
|
833
|
+
totalCostUsd: number;
|
|
834
|
+
byChannel: ChannelRollup[];
|
|
835
|
+
unpricedModels: string[];
|
|
836
|
+
fullyPriced: boolean;
|
|
837
|
+
usageComplete: boolean;
|
|
838
|
+
accountingComplete: boolean;
|
|
839
|
+
incompleteReasons: string[];
|
|
840
|
+
}
|
|
841
|
+
interface CostLedgerFilter {
|
|
842
|
+
channel?: CostChannel;
|
|
843
|
+
phase?: string;
|
|
844
|
+
tags?: Record<string, string>;
|
|
845
|
+
}
|
|
846
|
+
/** Append-only storage. `append` must atomically reject stale revisions. */
|
|
847
|
+
interface CostLedgerPersistence {
|
|
848
|
+
read(): {
|
|
849
|
+
revision: string;
|
|
850
|
+
events: string;
|
|
851
|
+
};
|
|
852
|
+
append(expectedRevision: string, event: string): string | undefined;
|
|
853
|
+
}
|
|
854
|
+
interface CostLedgerOptions {
|
|
855
|
+
costCeilingUsd?: number;
|
|
856
|
+
persistence?: CostLedgerPersistence;
|
|
857
|
+
/** Import already-settled receipts without admitting new paid work. */
|
|
858
|
+
receipts?: readonly CostReceipt[];
|
|
859
|
+
}
|
|
860
|
+
/** Run-wide paid-call admission, durable call state, receipts, and summaries. */
|
|
861
|
+
declare class CostLedger {
|
|
862
|
+
private readonly records;
|
|
863
|
+
private readonly activeCallIds;
|
|
864
|
+
private readonly lateCallIds;
|
|
865
|
+
private completedTasks;
|
|
866
|
+
private revision;
|
|
867
|
+
private costLimitPersisted;
|
|
868
|
+
readonly costCeilingUsd?: number;
|
|
869
|
+
private readonly persistence?;
|
|
870
|
+
constructor(input?: number | CostLedgerOptions);
|
|
871
|
+
runPaidCall<T>(input: RunPaidCallInput<T>): Promise<PaidCallResult<T>>;
|
|
872
|
+
/** Settle a call left pending by a crashed process after reconciling with the provider. */
|
|
873
|
+
reconcile(callId: string, observed: CostReceiptInput, options?: {
|
|
874
|
+
error?: string;
|
|
875
|
+
}): CostReceipt;
|
|
876
|
+
list(filter?: CostLedgerFilter): CostReceipt[];
|
|
877
|
+
summary(filter?: CostLedgerFilter): CostLedgerSummary;
|
|
878
|
+
markCompleted(count?: number): void;
|
|
879
|
+
costPerCompletedTask(): number | null;
|
|
880
|
+
private execute;
|
|
881
|
+
private captureLateOutcome;
|
|
882
|
+
private commitOutcome;
|
|
883
|
+
private captureFailure;
|
|
884
|
+
private commitReceipt;
|
|
885
|
+
private resolveMaximum;
|
|
886
|
+
private hasIncompleteSettledCall;
|
|
887
|
+
private appendRecord;
|
|
888
|
+
private ensureCostLimitPersisted;
|
|
889
|
+
private appendEvent;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* RawProviderSink — first-class persistence for the actual HTTP-level
|
|
894
|
+
* request/response bodies of every LLM provider call.
|
|
895
|
+
*
|
|
896
|
+
* Why this is a separate sink from the structured `LlmSpan`:
|
|
897
|
+
*
|
|
898
|
+
* - `LlmSpan` records the *intent* — model name, messages, output text,
|
|
899
|
+
* usage. It's what dashboards read; it's NOT enough for forensics.
|
|
900
|
+
* - When a downstream consumer reports "the verifier used the wrong route"
|
|
901
|
+
* or "tokens look right but reasoning was missing," the only way to
|
|
902
|
+
* answer is the raw HTTP body. Span fields can lie (a proxy can echo
|
|
903
|
+
* a different `model` value than what actually answered); the raw
|
|
904
|
+
* response is ground truth.
|
|
905
|
+
*
|
|
906
|
+
* Default behaviour: opt-in. Pass `rawSink` to `LlmClientOptions` (or the
|
|
907
|
+
* matrix runner / BuilderSession sets it up automatically) and every
|
|
908
|
+
* request, response, and error is recorded — including retries, with the
|
|
909
|
+
* attempt index attached so a flaky call's full event chain is recoverable.
|
|
910
|
+
*
|
|
911
|
+
* Redaction is enforced at sink time. The default redactor strips
|
|
912
|
+
* `Authorization`, `X-Api-Key`, `X-Auth-Token`, `Cookie` headers and any
|
|
913
|
+
* payload field whose key matches `apiKey | api_key | bearer | password |
|
|
914
|
+
* secret | token` (case-insensitive). Override via the sink constructor or
|
|
915
|
+
* the per-call `redactor`. The `redactedFields` array on the persisted
|
|
916
|
+
* event lets a reviewer see what was stripped without exposing the values.
|
|
917
|
+
*/
|
|
918
|
+
type RawProviderDirection = 'request' | 'response' | 'error';
|
|
919
|
+
interface RawProviderEvent {
|
|
920
|
+
/** Stable id. Generated by the sink if omitted. */
|
|
921
|
+
eventId: string;
|
|
922
|
+
/** Trace context populated by `LlmClient` when the call is wrapped in a span. */
|
|
923
|
+
runId?: string;
|
|
924
|
+
spanId?: string;
|
|
925
|
+
/**
|
|
926
|
+
* Logical provider name. Free-form so callers can use whatever id matches
|
|
927
|
+
* their topology (`'openai'`, `'anthropic'`, `'tangle-router'`, …). When
|
|
928
|
+
* omitted, derived from `baseUrl` in `LlmClientOptions`.
|
|
929
|
+
*/
|
|
930
|
+
provider: string;
|
|
931
|
+
model: string;
|
|
932
|
+
/** Endpoint path, e.g. `'/v1/chat/completions'`. */
|
|
933
|
+
endpoint: string;
|
|
934
|
+
/** Base URL used for the call (already-normalised — no trailing slash). */
|
|
935
|
+
baseUrl: string;
|
|
936
|
+
/** 0-indexed retry attempt. The first attempt is 0; a retried call gets 1, 2, … */
|
|
937
|
+
attemptIndex: number;
|
|
938
|
+
direction: RawProviderDirection;
|
|
939
|
+
/** Unix ms. */
|
|
940
|
+
timestamp: number;
|
|
941
|
+
/** Wall-clock duration of the call leg. Set on `response` and `error` events; null on `request`. */
|
|
942
|
+
durationMs?: number;
|
|
943
|
+
statusCode?: number;
|
|
944
|
+
requestHeaders?: Record<string, string>;
|
|
945
|
+
requestBody?: unknown;
|
|
946
|
+
responseHeaders?: Record<string, string>;
|
|
947
|
+
responseBody?: unknown;
|
|
948
|
+
/** Set on `direction: 'error'` events. */
|
|
949
|
+
errorMessage?: string;
|
|
950
|
+
/** Field paths the redactor stripped from this event ('header:Authorization', 'body.apiKey', …). */
|
|
951
|
+
redactedFields: string[];
|
|
952
|
+
}
|
|
953
|
+
interface RawProviderSinkFilter {
|
|
954
|
+
runId?: string;
|
|
955
|
+
spanId?: string;
|
|
956
|
+
direction?: RawProviderDirection;
|
|
957
|
+
attemptIndex?: number;
|
|
958
|
+
}
|
|
959
|
+
interface RawProviderSink {
|
|
960
|
+
record(event: RawProviderEvent): Promise<void>;
|
|
961
|
+
/** Optional listing — implementations that durably persist (file, db) should support this. */
|
|
962
|
+
list?(filter?: RawProviderSinkFilter): Promise<RawProviderEvent[]>;
|
|
963
|
+
/** Optional teardown for backed implementations. */
|
|
964
|
+
close?(): Promise<void>;
|
|
965
|
+
}
|
|
966
|
+
type ProviderRedactor = (event: RawProviderEvent) => RawProviderEvent;
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* LLM client with graceful degrade.
|
|
970
|
+
*
|
|
971
|
+
* OpenAI-compatible `/v1/chat/completions` client with:
|
|
972
|
+
* - Exponential-backoff retry on 429 + 5xx gateway errors (502/503/504).
|
|
973
|
+
* - Retry on transient network errors (fetch failed, AbortError, ECONNRESET).
|
|
974
|
+
* - Graceful json_schema → json_object degrade on 400 with schema-reject body.
|
|
975
|
+
* - Fenced-JSON stripping (```json ... ```) for models that wrap structured output.
|
|
976
|
+
* - Configurable base URL + api key / bearer, works with LiteLLM proxies, OpenAI
|
|
977
|
+
* directly, cli-bridge subscriptions, and any router that speaks the spec.
|
|
978
|
+
*
|
|
979
|
+
* Usage:
|
|
980
|
+
* const { value, result } = await callLlmJson<MyType>(
|
|
981
|
+
* { model: 'gpt-4o', messages: [...], jsonSchema: { name: 'x', schema: {...} } },
|
|
982
|
+
* { baseUrl: 'https://router.tangle.tools/v1', apiKey: process.env.KEY },
|
|
983
|
+
* )
|
|
984
|
+
*
|
|
985
|
+
* This is THE llm-calling seam for agent-eval primitives that need structured
|
|
986
|
+
* output (semantic concept judge, reviewer directives, critic scores). Primitives
|
|
987
|
+
* that need free-form text use `callLlm` and parse output themselves.
|
|
988
|
+
*/
|
|
989
|
+
|
|
990
|
+
interface LlmMessage {
|
|
991
|
+
role: 'system' | 'user' | 'assistant';
|
|
992
|
+
/**
|
|
993
|
+
* Either a plain text content string OR a multimodal content array
|
|
994
|
+
* (text + image_url parts) for vision-capable models.
|
|
995
|
+
*/
|
|
996
|
+
content: string | Array<{
|
|
997
|
+
type: 'text';
|
|
998
|
+
text: string;
|
|
999
|
+
} | {
|
|
1000
|
+
type: 'image_url';
|
|
1001
|
+
image_url: {
|
|
1002
|
+
url: string;
|
|
1003
|
+
detail?: 'auto' | 'low' | 'high';
|
|
1004
|
+
};
|
|
1005
|
+
}>;
|
|
1006
|
+
}
|
|
1007
|
+
interface LlmCallRequest {
|
|
1008
|
+
model: string;
|
|
1009
|
+
messages: LlmMessage[];
|
|
1010
|
+
/** Optional JSON-mode response format (response_format: json_object). */
|
|
1011
|
+
jsonMode?: boolean;
|
|
1012
|
+
/** Optional structured output via JSON Schema. Falls back to json_object on 400. */
|
|
1013
|
+
jsonSchema?: {
|
|
1014
|
+
name: string;
|
|
1015
|
+
schema: Record<string, unknown>;
|
|
1016
|
+
};
|
|
1017
|
+
temperature?: number;
|
|
1018
|
+
maxTokens?: number;
|
|
1019
|
+
/** Per-call timeout, default 300s. */
|
|
1020
|
+
timeoutMs?: number;
|
|
1021
|
+
}
|
|
1022
|
+
interface LlmUsage {
|
|
1023
|
+
promptTokens: number;
|
|
1024
|
+
completionTokens: number;
|
|
1025
|
+
totalTokens: number;
|
|
1026
|
+
/** False when the provider omitted or malformed prompt/completion usage. */
|
|
1027
|
+
captured?: boolean;
|
|
1028
|
+
/** Proxies populate this when prompt caching is on. */
|
|
1029
|
+
cachedPromptTokens?: number;
|
|
1030
|
+
}
|
|
1031
|
+
interface LlmCallResult {
|
|
1032
|
+
/** The text content of the first choice. Empty string if none. */
|
|
1033
|
+
content: string;
|
|
1034
|
+
usage: LlmUsage;
|
|
1035
|
+
/**
|
|
1036
|
+
* Cost in USD. Pulled from proxy's `_response_cost` field when present;
|
|
1037
|
+
* `null` when neither the proxy nor the caller can derive it.
|
|
1038
|
+
*/
|
|
1039
|
+
costUsd: number | null;
|
|
1040
|
+
/** Model name actually used (echoed from response). */
|
|
1041
|
+
model: string;
|
|
1042
|
+
/** Wall-clock duration of the HTTP call (last attempt, if retried). */
|
|
1043
|
+
durationMs: number;
|
|
1044
|
+
/**
|
|
1045
|
+
* `finish_reason` echoed from the first choice (`stop`, `length`,
|
|
1046
|
+
* `content_filter`, `tool_calls`, ...). `null` when the provider omits it.
|
|
1047
|
+
* Exposed so a free-form `callLlm` caller CAN detect a truncated answer
|
|
1048
|
+
* (`length`) instead of treating a cut-off completion as complete. Note:
|
|
1049
|
+
* `callLlm` does not itself reject on it — acting on this signal is the
|
|
1050
|
+
* caller's responsibility (in-repo free-form drivers do not yet enforce it).
|
|
1051
|
+
*/
|
|
1052
|
+
finishReason?: string | null;
|
|
1053
|
+
/**
|
|
1054
|
+
* True when `content.trim()` is empty. An empty completion is a silent zero
|
|
1055
|
+
* for free-form `callLlm` callers; this flag is the signal a caller can
|
|
1056
|
+
* inspect to fail loud rather than proceed on an empty string. `callLlm`
|
|
1057
|
+
* surfaces it but does not throw on it.
|
|
1058
|
+
*/
|
|
1059
|
+
contentEmpty?: boolean;
|
|
1060
|
+
/** Raw response body. */
|
|
1061
|
+
raw: Record<string, unknown>;
|
|
1062
|
+
}
|
|
1063
|
+
type LlmCallMetadata = Pick<LlmCallResult, 'usage' | 'costUsd' | 'model' | 'durationMs'>;
|
|
1064
|
+
interface LlmClientOptions {
|
|
1065
|
+
/** Base URL (without trailing slash). Must end at the `/v1` prefix. */
|
|
1066
|
+
baseUrl?: string;
|
|
1067
|
+
/** Bearer token — either `apiKey` or `bearer` populates `Authorization: Bearer ...`. */
|
|
1068
|
+
apiKey?: string;
|
|
1069
|
+
bearer?: string;
|
|
1070
|
+
/** Override for the `Authorization` header (e.g. `X-Auth: ...`). Takes precedence over apiKey/bearer. */
|
|
1071
|
+
authHeader?: {
|
|
1072
|
+
name: string;
|
|
1073
|
+
value: string;
|
|
1074
|
+
};
|
|
1075
|
+
/** Stable provider idempotency key, reused across retries of this logical call. */
|
|
1076
|
+
idempotencyKey?: string;
|
|
1077
|
+
/** Default timeout in ms. Per-call can override. */
|
|
1078
|
+
defaultTimeoutMs?: number;
|
|
1079
|
+
/**
|
|
1080
|
+
* Caller-supplied abort signal — e.g. a campaign-wide cancel. Linked to
|
|
1081
|
+
* each attempt's per-attempt timeout controller, so aborting it cancels
|
|
1082
|
+
* the in-flight fetch. A caller abort is FATAL: it is not retried even
|
|
1083
|
+
* though an AbortError otherwise matches the transient patterns.
|
|
1084
|
+
*/
|
|
1085
|
+
signal?: AbortSignal;
|
|
1086
|
+
/**
|
|
1087
|
+
* Cross-attempt wall-clock budget in ms, measured from the first attempt.
|
|
1088
|
+
* Before launching each attempt the loop checks the remaining budget and
|
|
1089
|
+
* stops retrying once it is exhausted, rather than waiting the full
|
|
1090
|
+
* per-attempt timeout on every retry. Bounds total time independent of
|
|
1091
|
+
* total attempts × `timeoutMs`.
|
|
1092
|
+
*/
|
|
1093
|
+
deadlineMs?: number;
|
|
1094
|
+
/** Total provider attempts. Legacy option name; default 3 (1 initial + 2 retries). */
|
|
1095
|
+
maxRetries?: number;
|
|
1096
|
+
/** Fetch implementation — defaults to global `fetch`. Override for custom transport (e.g. tests). */
|
|
1097
|
+
fetch?: typeof fetch;
|
|
1098
|
+
/**
|
|
1099
|
+
* Optional raw HTTP capture sink. When provided, every request, response,
|
|
1100
|
+
* and error (across all retry attempts) is recorded to the sink, with auth
|
|
1101
|
+
* headers and credential-shaped body fields redacted by default. This is
|
|
1102
|
+
* the layer-1 forensics primitive: structured `LlmSpan`s record intent,
|
|
1103
|
+
* raw events record what actually crossed the wire.
|
|
1104
|
+
*/
|
|
1105
|
+
rawSink?: RawProviderSink;
|
|
1106
|
+
/**
|
|
1107
|
+
* Logical provider id attached to raw events. When omitted, derived from
|
|
1108
|
+
* `baseUrl` via `providerFromBaseUrl`.
|
|
1109
|
+
*/
|
|
1110
|
+
provider?: string;
|
|
1111
|
+
/** Trace context attached to raw events; populated by emitter-aware callers. */
|
|
1112
|
+
traceContext?: {
|
|
1113
|
+
runId?: string;
|
|
1114
|
+
spanId?: string;
|
|
1115
|
+
};
|
|
1116
|
+
/** Override the redaction strategy for this call. Defaults to `defaultProviderRedactor`. */
|
|
1117
|
+
redactor?: ProviderRedactor;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* ChatClient — the single LLM abstraction analysts call.
|
|
1122
|
+
*
|
|
1123
|
+
* agent-eval already ships an `LlmClient` (OpenAI-compatible, retry,
|
|
1124
|
+
* graceful JSON-schema degrade) and judges that talk to `TCloud`. Two
|
|
1125
|
+
* mixed patterns force every analyst author to pick a transport, which
|
|
1126
|
+
* couples analyst code to runtime concerns (cli-bridge vs router vs
|
|
1127
|
+
* sandbox-sdk) it shouldn't know about.
|
|
1128
|
+
*
|
|
1129
|
+
* `ChatClient` is one interface every analyst takes via `AnalystContext.chat`.
|
|
1130
|
+
* The operator decides at the registry boundary which transport binds
|
|
1131
|
+
* to it. Analyst code stays transport-agnostic; swapping production
|
|
1132
|
+
* (sandbox-sdk) for local dev (cli-bridge) or tests (mock) is a one-
|
|
1133
|
+
* line factory call.
|
|
1134
|
+
*
|
|
1135
|
+
* Designed to coexist: existing `LlmClient` callers and existing
|
|
1136
|
+
* `TCloud`-based judges keep working untouched. New analyst code uses
|
|
1137
|
+
* `ChatClient`. When old call sites migrate, they pick up budgeting,
|
|
1138
|
+
* cancellation, and unified telemetry for free.
|
|
1139
|
+
*/
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* Unified chat interface. Mirrors LlmCallRequest/Result so the OpenAI-
|
|
1143
|
+
* compatible mental model stays. Two methods: a one-shot `chat()` and
|
|
1144
|
+
* an `streamChat()` for future agentic loops (not yet exposed).
|
|
1145
|
+
*/
|
|
1146
|
+
interface ChatClient {
|
|
1147
|
+
/** Display name of the bound transport — included in telemetry. */
|
|
1148
|
+
readonly transport: ChatTransport;
|
|
1149
|
+
/** Default model when caller omits — operators bind this per environment. */
|
|
1150
|
+
readonly defaultModel?: string;
|
|
1151
|
+
/** Total provider attempts this transport can make for one chat call. */
|
|
1152
|
+
readonly maximumAttempts?: number;
|
|
1153
|
+
/** Implementations must enforce `req.maxTokens` when it is present. */
|
|
1154
|
+
chat(req: ChatRequest, opts?: ChatCallOpts): Promise<ChatResponse>;
|
|
1155
|
+
}
|
|
1156
|
+
type ChatTransport = 'router' | 'sandbox-sdk' | 'cli-bridge' | 'direct-provider' | 'mock';
|
|
1157
|
+
interface ChatRequest extends Omit<LlmCallRequest, 'model'> {
|
|
1158
|
+
/** Optional — falls back to ChatClient.defaultModel. */
|
|
1159
|
+
model?: string;
|
|
1160
|
+
}
|
|
1161
|
+
type ChatResponse = LlmCallResult;
|
|
1162
|
+
interface ChatCallOpts {
|
|
1163
|
+
/** Cancel the in-flight request. */
|
|
1164
|
+
signal?: AbortSignal;
|
|
1165
|
+
/** Hard USD ceiling for this single call (informational; the underlying transport may not enforce). */
|
|
1166
|
+
maxCostUsd?: number;
|
|
1167
|
+
/** Correlation tag carried into request headers when the transport allows. */
|
|
1168
|
+
correlationId?: string;
|
|
1169
|
+
/** Stable provider idempotency key for retries/redrives of one paid call. */
|
|
1170
|
+
idempotencyKey?: string;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* Analyst contract — the missing orchestration layer over agent-eval's
|
|
1175
|
+
* existing analyzers (analyzeTraces, MultiLayerVerifier, RunCritic,
|
|
1176
|
+
* SemanticConceptJudge, JudgeFn, ...).
|
|
1177
|
+
*
|
|
1178
|
+
* Each existing primitive returns its own output shape. The Analyst
|
|
1179
|
+
* contract is the single envelope every primitive lifts into, so a
|
|
1180
|
+
* registry can run N analysts against a run and a single renderer can
|
|
1181
|
+
* compose findings without knowing which analyzer produced them.
|
|
1182
|
+
*
|
|
1183
|
+
* The contract is intentionally domain-agnostic: nothing here knows
|
|
1184
|
+
* about code, voice, RAG, or any particular agent stack. Analysts
|
|
1185
|
+
* declare what INPUT KIND they need (a trace store, an artifact dir,
|
|
1186
|
+
* a RunRecord, a JudgeInput, or `custom`), and the registry routes
|
|
1187
|
+
* the matching input from `AnalystRunInputs`.
|
|
1188
|
+
*/
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* Unified envelope every analyst emits. Schema-versioned so renderers
|
|
1192
|
+
* and time-series diffs survive future field additions.
|
|
1193
|
+
*/
|
|
1194
|
+
interface AnalystFinding {
|
|
1195
|
+
schema_version: '1.0.0';
|
|
1196
|
+
/**
|
|
1197
|
+
* Stable hash over identity-defining fields (analyst_id + canonical
|
|
1198
|
+
* claim + area + optional subject). Two findings from two runs that
|
|
1199
|
+
* "are the same finding" share this id — that's what `diffFindings`
|
|
1200
|
+
* uses to compute appeared/disappeared sets across runs.
|
|
1201
|
+
*/
|
|
1202
|
+
finding_id: string;
|
|
1203
|
+
analyst_id: string;
|
|
1204
|
+
produced_at: string;
|
|
1205
|
+
severity: AnalystSeverity;
|
|
1206
|
+
/**
|
|
1207
|
+
* Coarse classification. Renderers group by this. Free-form so
|
|
1208
|
+
* domain-specific analysts can introduce categories without a
|
|
1209
|
+
* schema change ('agent-reasoning', 'verification', 'cost',
|
|
1210
|
+
* 'tool-use', 'safety', 'latency', 'data-quality', ...).
|
|
1211
|
+
*/
|
|
1212
|
+
area: string;
|
|
1213
|
+
claim: string;
|
|
1214
|
+
rationale?: string;
|
|
1215
|
+
evidence_refs: EvidenceRef[];
|
|
1216
|
+
recommended_action?: string;
|
|
1217
|
+
validation_plan?: string;
|
|
1218
|
+
/** 0..1 — the analyst's own confidence. Not calibrated across analysts. */
|
|
1219
|
+
confidence: number;
|
|
1220
|
+
/**
|
|
1221
|
+
* Optional subject the finding is about — leaf id, agent id, request
|
|
1222
|
+
* id. Included in finding_id when present so per-subject findings
|
|
1223
|
+
* diff cleanly across runs.
|
|
1224
|
+
*/
|
|
1225
|
+
subject?: string;
|
|
1226
|
+
/** FIREWALL provenance (docs/learning-flywheel.md): true iff this finding was
|
|
1227
|
+
* lifted from a JUDGE verdict (an acceptance score), not OBSERVED from the
|
|
1228
|
+
* agent's behavior. A judge-derived finding must NEVER be admitted as a
|
|
1229
|
+
* steering input — that is the held-out judge leaking into the loop. Set at
|
|
1230
|
+
* the lift site (createJudgeAdapter); checked by `assertNoJudgeVerdict`.
|
|
1231
|
+
* Provenance, not evidence presence, is the correct discriminator: an
|
|
1232
|
+
* evidence-less trace-analyst observation legitimately steers, while a judge
|
|
1233
|
+
* verdict that happens to cite an artifact must not. */
|
|
1234
|
+
derived_from_judge?: boolean;
|
|
1235
|
+
/** Analyst-private extras; renderers ignore unless they know the analyst. */
|
|
1236
|
+
metadata?: Record<string, unknown>;
|
|
1237
|
+
}
|
|
1238
|
+
type AnalystSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
1239
|
+
interface EvidenceRef {
|
|
1240
|
+
/**
|
|
1241
|
+
* Where the evidence lives. `span` and `event` refer to OTLP trace
|
|
1242
|
+
* elements; `artifact` to a file inside the run's artifact tree;
|
|
1243
|
+
* `finding` to another AnalystFinding (cross-analyst chaining);
|
|
1244
|
+
* `metric` to a named scalar reading the renderer knows how to read.
|
|
1245
|
+
*/
|
|
1246
|
+
kind: 'span' | 'event' | 'artifact' | 'finding' | 'metric';
|
|
1247
|
+
uri: string;
|
|
1248
|
+
excerpt?: string;
|
|
1249
|
+
}
|
|
1250
|
+
interface AnalystCost {
|
|
1251
|
+
/** `deterministic` analysts MUST NOT call the LLM. */
|
|
1252
|
+
kind: 'deterministic' | 'llm';
|
|
1253
|
+
/** Optional declared upper bound; the registry can enforce a budget. */
|
|
1254
|
+
est_usd_per_run?: number;
|
|
1255
|
+
/** Models the analyst expects to use (informational). */
|
|
1256
|
+
models?: string[];
|
|
1257
|
+
}
|
|
1258
|
+
interface AnalystContext {
|
|
1259
|
+
runId: string;
|
|
1260
|
+
/** Stable correlation id so logs from a single registry.run() share a tag. */
|
|
1261
|
+
correlationId: string;
|
|
1262
|
+
/** Wall-clock deadline (epoch ms). Analysts SHOULD honor for graceful cancel. */
|
|
1263
|
+
deadlineMs?: number;
|
|
1264
|
+
/** Per-analyst USD budget. Analysts MAY check before issuing LLM calls. */
|
|
1265
|
+
budgetUsd?: number;
|
|
1266
|
+
/**
|
|
1267
|
+
* Shared chat client. Analysts that call an LLM go through this so
|
|
1268
|
+
* the operator picks transport (sandbox-sdk | router | cli-bridge |
|
|
1269
|
+
* direct-provider | mock) at the registry boundary without touching
|
|
1270
|
+
* analyst code.
|
|
1271
|
+
*/
|
|
1272
|
+
chat?: ChatClient;
|
|
1273
|
+
/**
|
|
1274
|
+
* Findings from a prior run the operator wants the analyst to see as
|
|
1275
|
+
* retrieval context. Kinds that take advantage of cross-run memory
|
|
1276
|
+
* (failure-mode "I saw this cluster last run", knowledge-gap "the wiki
|
|
1277
|
+
* page I asked for is still missing") render these into the actor's
|
|
1278
|
+
* working set. Filtering is the operator's job: pass the slice that
|
|
1279
|
+
* matches the analyst's id, or pass everything and let the kind
|
|
1280
|
+
* filter. Empty / absent means no cross-run context.
|
|
1281
|
+
*/
|
|
1282
|
+
priorFindings?: ReadonlyArray<AnalystFinding>;
|
|
1283
|
+
/** Free-form runtime tags (env, host, op). Findings can echo these into metadata. */
|
|
1284
|
+
tags?: Record<string, string>;
|
|
1285
|
+
/** Logger callback — analysts SHOULD prefer this over console.* for testability. */
|
|
1286
|
+
log?: (msg: string, fields?: Record<string, unknown>) => void;
|
|
1287
|
+
/** Optional abort signal. Analysts SHOULD pass it through to LLM calls. */
|
|
1288
|
+
signal?: AbortSignal;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
type PolicyEditSchemaVersion = 'policy-edit/v1';
|
|
1292
|
+
declare const POLICY_EDIT_AXES: readonly ["carrier", "representation", "budget", "sampling", "output_contract", "tool_contract", "routing", "memory", "agent_profile", "deployment_target"];
|
|
1293
|
+
type PolicyEditAxis = (typeof POLICY_EDIT_AXES)[number];
|
|
1294
|
+
declare const POLICY_EDIT_TARGET_SURFACES: readonly ["prompt", "tool-contract", "runtime-config", "memory", "agent-profile", "code", "deployment"];
|
|
1295
|
+
type PolicyEditTargetSurface = (typeof POLICY_EDIT_TARGET_SURFACES)[number];
|
|
1296
|
+
type PolicyEditRisk = 'low' | 'medium' | 'high' | 'unknown';
|
|
1297
|
+
type PolicyEditGainDirection = 'increase' | 'decrease';
|
|
1298
|
+
type PolicyEditGainUnit = 'absolute' | 'relative' | 'percent' | 'score';
|
|
1299
|
+
interface PolicyEditTarget {
|
|
1300
|
+
surface: PolicyEditTargetSurface;
|
|
1301
|
+
/** Stable path inside the target surface, for example `system-prompt:tools`
|
|
1302
|
+
* or `budget.maxTurns`. */
|
|
1303
|
+
path?: string;
|
|
1304
|
+
/** Optional canonical deployment identity. Store the existing cell, not a
|
|
1305
|
+
* local profile shape. */
|
|
1306
|
+
agentProfileCell?: AgentProfileCell;
|
|
1307
|
+
/** Human label when the path is not enough for a readable audit trail. */
|
|
1308
|
+
label?: string;
|
|
1309
|
+
}
|
|
1310
|
+
type PolicyEditChange = {
|
|
1311
|
+
kind: 'text';
|
|
1312
|
+
mode: 'append' | 'prepend' | 'replace';
|
|
1313
|
+
value: string;
|
|
1314
|
+
/** Required when `mode === 'replace'`; exact match only. */
|
|
1315
|
+
find?: string;
|
|
1316
|
+
} | {
|
|
1317
|
+
kind: 'json';
|
|
1318
|
+
mode: 'set' | 'merge' | 'remove';
|
|
1319
|
+
path: string;
|
|
1320
|
+
value?: AgentProfileJson;
|
|
1321
|
+
};
|
|
1322
|
+
interface PolicyEditExpectedGain {
|
|
1323
|
+
/** Metric this edit is expected to move, e.g. `holdout.composite`. */
|
|
1324
|
+
metric: string;
|
|
1325
|
+
direction: PolicyEditGainDirection;
|
|
1326
|
+
/** Positive magnitude in the metric's native units. */
|
|
1327
|
+
amount: number;
|
|
1328
|
+
unit?: PolicyEditGainUnit;
|
|
1329
|
+
rationale?: string;
|
|
1330
|
+
}
|
|
1331
|
+
interface PolicyEditSource {
|
|
1332
|
+
findingIds: string[];
|
|
1333
|
+
analystIds: string[];
|
|
1334
|
+
evidenceRefs: EvidenceRef[];
|
|
1335
|
+
/** Mirrors `AnalystFinding.derived_from_judge`; admission rejects it. */
|
|
1336
|
+
derivedFromJudge?: boolean;
|
|
1337
|
+
}
|
|
1338
|
+
interface PolicyEdit {
|
|
1339
|
+
schemaVersion: PolicyEditSchemaVersion;
|
|
1340
|
+
editId: string;
|
|
1341
|
+
axis: PolicyEditAxis;
|
|
1342
|
+
target: PolicyEditTarget;
|
|
1343
|
+
change: PolicyEditChange;
|
|
1344
|
+
claim: string;
|
|
1345
|
+
expectedGain: PolicyEditExpectedGain;
|
|
1346
|
+
confidence: number;
|
|
1347
|
+
risk: PolicyEditRisk;
|
|
1348
|
+
source: PolicyEditSource;
|
|
1349
|
+
rationale?: string;
|
|
1350
|
+
validationPlan?: string;
|
|
1351
|
+
metadata?: Record<string, unknown>;
|
|
1352
|
+
}
|
|
1353
|
+
declare const POLICY_EDIT_CANDIDATE_RECORD_SCHEMA: "tangle.policy-edit-candidate.v1";
|
|
1354
|
+
/** JSON-safe attribution carried with a measured candidate and its scores. */
|
|
1355
|
+
interface PolicyEditCandidateRecord {
|
|
1356
|
+
schema: typeof POLICY_EDIT_CANDIDATE_RECORD_SCHEMA;
|
|
1357
|
+
policyEdit: PolicyEdit;
|
|
1358
|
+
}
|
|
1359
|
+
interface FindingToPolicyEditOptions {
|
|
1360
|
+
expectedGain?: PolicyEditExpectedGain | ((finding: AnalystFinding) => PolicyEditExpectedGain | null | undefined);
|
|
1361
|
+
risk?: PolicyEditRisk | ((finding: AnalystFinding) => PolicyEditRisk);
|
|
1362
|
+
defaultAxis?: PolicyEditAxis;
|
|
1363
|
+
defaultTargetSurface?: PolicyEditTargetSurface;
|
|
1364
|
+
}
|
|
1365
|
+
interface PolicyEditAdmissionOptions {
|
|
1366
|
+
minScore?: number;
|
|
1367
|
+
minExpectedGain?: number;
|
|
1368
|
+
allowHighRisk?: boolean;
|
|
1369
|
+
requireEvidence?: boolean;
|
|
1370
|
+
}
|
|
1371
|
+
interface PolicyEditAdmission {
|
|
1372
|
+
edit: PolicyEdit;
|
|
1373
|
+
decision: 'admit' | 'reject';
|
|
1374
|
+
score: number;
|
|
1375
|
+
reasons: string[];
|
|
1376
|
+
}
|
|
1377
|
+
declare function validatePolicyEditCandidateRecord(input: unknown): PolicyEditCandidateRecord;
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* Pass A substrate types — `runCampaign` is the one primitive every
|
|
1381
|
+
* eval flow composes from. Three contracts in this file:
|
|
1382
|
+
*
|
|
1383
|
+
* - `Scenario` input set
|
|
1384
|
+
* - `DispatchFn` how to run one scenario → artifact
|
|
1385
|
+
* - `CampaignResult` defined output schema (the contract downstream tools depend on)
|
|
1386
|
+
*
|
|
1387
|
+
* Three more lifted from earlier substrate work (re-exported):
|
|
1388
|
+
*
|
|
1389
|
+
* - `JudgeConfig` pluggable dimensional scorer (0.38)
|
|
1390
|
+
* - `Mutator` optimization-loop surface mutator
|
|
1391
|
+
* - `Gate` promotion gate (`HeldOutGate` and friends adapt to this)
|
|
1392
|
+
*
|
|
1393
|
+
* No new architecture vs 0.38 — Pass A formalizes the shapes so consumers
|
|
1394
|
+
* can build dashboards / CI gates / regression diffs against a stable schema.
|
|
1395
|
+
*/
|
|
1396
|
+
|
|
1397
|
+
/** Stable identifier + kind tag for any scenario. Consumers
|
|
1398
|
+
* extend with their per-domain payload (persona, task, requirement, ...). */
|
|
1399
|
+
interface Scenario {
|
|
1400
|
+
id: string;
|
|
1401
|
+
kind: string;
|
|
1402
|
+
tags?: string[];
|
|
1403
|
+
}
|
|
1404
|
+
/** Context handed to every dispatch invocation. Scoped — every
|
|
1405
|
+
* trace/span carries the cellId, every artifact write lands under the cell's
|
|
1406
|
+
* artifact root, the cost meter accumulates per cell. */
|
|
1407
|
+
interface DispatchContext {
|
|
1408
|
+
cellId: string;
|
|
1409
|
+
rep: number;
|
|
1410
|
+
generation?: number;
|
|
1411
|
+
seed: number;
|
|
1412
|
+
signal: AbortSignal;
|
|
1413
|
+
trace: CampaignTraceWriter;
|
|
1414
|
+
artifacts: CampaignArtifactWriter;
|
|
1415
|
+
cost: CampaignCostMeter;
|
|
1416
|
+
/** Populated when this run is part of a multi-cycle improvement loop. */
|
|
1417
|
+
cycleId?: string;
|
|
1418
|
+
/** Populated when the substrate resumed from a prior cache hit. */
|
|
1419
|
+
resumedFrom?: string;
|
|
1420
|
+
/**
|
|
1421
|
+
* Opaque placement key supplied by `RunCampaignOptions.cellPlacement`.
|
|
1422
|
+
* The substrate forwards it through unchanged; placement-aware Dispatch
|
|
1423
|
+
* implementations (e.g. `httpDispatch` from `/adapters/http`) read it to
|
|
1424
|
+
* route the cell to the right worker / region / sandbox. `undefined`
|
|
1425
|
+
* when no placement strategy is configured.
|
|
1426
|
+
*/
|
|
1427
|
+
placement?: string;
|
|
1428
|
+
}
|
|
1429
|
+
/** One function: scenario + ctx → artifact. Dispatcher chooses
|
|
1430
|
+
* whether to call `runMultishot`, `runLoop`, raw `streamPrompt`, anything. */
|
|
1431
|
+
type DispatchFn<TScenario extends Scenario, TArtifact> = (scenario: TScenario, ctx: DispatchContext) => Promise<TArtifact>;
|
|
1432
|
+
/** One session within a multi-session journey. Dispatch is
|
|
1433
|
+
* invoked once per session in order; state from prior session's artifact
|
|
1434
|
+
* is exposed via `ctx.priorSessionArtifact`. */
|
|
1435
|
+
interface SessionScript<TScenario, TArtifact> {
|
|
1436
|
+
id: string;
|
|
1437
|
+
intent: string;
|
|
1438
|
+
maxTurns?: number;
|
|
1439
|
+
/** When true, knowledge accumulated this session persists to next. */
|
|
1440
|
+
affectsKnowledge?: boolean;
|
|
1441
|
+
/** Optional per-session persona evolution — called after the session
|
|
1442
|
+
* resolves. Returns the persona shape used by the NEXT session. */
|
|
1443
|
+
evolveAfterSession?: (artifact: TArtifact, sessionIndex: number, scenario: TScenario) => TScenario;
|
|
1444
|
+
}
|
|
1445
|
+
interface JudgeDimension {
|
|
1446
|
+
/** JSON field name + score key. */
|
|
1447
|
+
key: string;
|
|
1448
|
+
/** Description shown in the judge's user prompt. */
|
|
1449
|
+
description: string;
|
|
1450
|
+
}
|
|
1451
|
+
/** Pluggable dimensional scorer. `score` is the contract:
|
|
1452
|
+
* given an artifact + scenario, return a `JudgeScore`. This is deliberately a
|
|
1453
|
+
* function, not a fixed LLM-prompt shape — real consumers judge with
|
|
1454
|
+
* ensembles, deterministic checks, or a single LLM call, and the substrate
|
|
1455
|
+
* must not constrain that. The `llmJudge()` helper builds a `score` that does
|
|
1456
|
+
* one LLM call for the common case. `appliesTo` lets a judge run only on
|
|
1457
|
+
* scenarios that match (e.g. a legal-citation judge only on legal scenarios). */
|
|
1458
|
+
interface JudgeConfig<TArtifact, TScenario extends Scenario = Scenario> {
|
|
1459
|
+
name: string;
|
|
1460
|
+
dimensions: JudgeDimension[];
|
|
1461
|
+
/** Stable scoring revision used by campaign resume and verdict caches.
|
|
1462
|
+
* Built-in judges derive this from their prompt, model, and rubric. Custom
|
|
1463
|
+
* judges should set it when closure state can change without changing code. */
|
|
1464
|
+
judgeVersion?: string;
|
|
1465
|
+
/** Score one artifact. Throw on failure — a thrown judge is recorded as a
|
|
1466
|
+
* failed cell, never silently folded into a zero. */
|
|
1467
|
+
score(input: {
|
|
1468
|
+
artifact: TArtifact;
|
|
1469
|
+
scenario: TScenario;
|
|
1470
|
+
signal: AbortSignal;
|
|
1471
|
+
/** Shared run spend account and receipt attribution phase. */
|
|
1472
|
+
costLedger?: CostLedger;
|
|
1473
|
+
costPhase?: string;
|
|
1474
|
+
costTags?: Record<string, string>;
|
|
1475
|
+
}): JudgeScore | Promise<JudgeScore>;
|
|
1476
|
+
appliesTo?: (scenario: TScenario) => boolean;
|
|
1477
|
+
}
|
|
1478
|
+
/** The canonical judge verdict shape — one declaration, shared by campaign
|
|
1479
|
+
* judges and the multishot judge runner (which re-exports this type).
|
|
1480
|
+
*
|
|
1481
|
+
* Scale is PRODUCER-DEFINED: campaign convention is [0,1]; the legacy
|
|
1482
|
+
* multishot runner emits 0-10. Cross-scale comparison must go through
|
|
1483
|
+
* `detectScale` (src/campaign/gates/statistical-heldout.ts, used by
|
|
1484
|
+
* promotion-policy) — never renormalize a producer's values in place, as
|
|
1485
|
+
* downstream thresholds (`composite >= 5` in multishot/matrix.ts, live-soak
|
|
1486
|
+
* `>= 7` gates) key on the producer's native scale. */
|
|
1487
|
+
interface JudgeScore {
|
|
1488
|
+
dimensions: Record<string, number>;
|
|
1489
|
+
composite: number;
|
|
1490
|
+
notes: string;
|
|
1491
|
+
/** Provider metadata for display and diagnostics; accounting uses CostLedger receipts. */
|
|
1492
|
+
llmCall?: LlmCallMetadata;
|
|
1493
|
+
/** Set when the judge itself failed (call error, unparseable output).
|
|
1494
|
+
* `composite`/`dimensions` carry no signal — aggregators MUST exclude
|
|
1495
|
+
* failed scores from means instead of folding them into zeros. */
|
|
1496
|
+
failed?: true;
|
|
1497
|
+
/** Ensemble extras (populated by `ensembleJudge`): max per-dimension
|
|
1498
|
+
* spread across surviving judges — the inter-rater signal. */
|
|
1499
|
+
maxDisagreement?: number;
|
|
1500
|
+
/** Ensemble extras: judge identities whose verdict failed. */
|
|
1501
|
+
failedJudges?: string[];
|
|
1502
|
+
/** Ensemble extras: each surviving judge's per-dimension scores. */
|
|
1503
|
+
perJudge?: Record<string, Record<string, number>>;
|
|
1504
|
+
}
|
|
1505
|
+
/** A tier-4 code surface — a finalized candidate change to the agent's
|
|
1506
|
+
* IMPLEMENTATION, not its prompt. Produced by autoresearch (reads codebase +
|
|
1507
|
+
* trace findings → opens a worktree). `worktreeRef` locates the candidate;
|
|
1508
|
+
* the exact commits, tree, and binary-patch digest identify it. See the
|
|
1509
|
+
* improvement-tier table in `docs/design/loop-taxonomy.md`. */
|
|
1510
|
+
interface CodeSurface {
|
|
1511
|
+
readonly kind: 'code';
|
|
1512
|
+
/** Worktree path or git ref holding the candidate code change. This is a
|
|
1513
|
+
* mutable locator and is deliberately excluded from content hashes. */
|
|
1514
|
+
readonly worktreeRef: string;
|
|
1515
|
+
/** Human-readable ref the worktree was forked from. Not identity-bearing. */
|
|
1516
|
+
readonly baseRef: string;
|
|
1517
|
+
/** Exact commit the candidate was forked from. */
|
|
1518
|
+
readonly baseCommit: string;
|
|
1519
|
+
/** Exact tree object for `baseCommit`. */
|
|
1520
|
+
readonly baseTree: string;
|
|
1521
|
+
/** Exact finalized candidate commit. */
|
|
1522
|
+
readonly candidateCommit: string;
|
|
1523
|
+
/** Exact tree object for `candidateCommit`. */
|
|
1524
|
+
readonly candidateTree: string;
|
|
1525
|
+
/** Identity of the exact patch artifact. The deployable candidate bundle
|
|
1526
|
+
* carries the same descriptor plus its base64-encoded content. */
|
|
1527
|
+
readonly patch: {
|
|
1528
|
+
readonly format: 'git-diff-binary';
|
|
1529
|
+
readonly sha256: `sha256:${string}`;
|
|
1530
|
+
readonly byteLength: number;
|
|
1531
|
+
};
|
|
1532
|
+
/** Human summary of what changed — rendered into the auto-PR body. */
|
|
1533
|
+
readonly summary?: string;
|
|
1534
|
+
}
|
|
1535
|
+
/** The mutable surface a proposer changes. Tiers (see
|
|
1536
|
+
* `docs/design/loop-taxonomy.md`):
|
|
1537
|
+
* - `string` — tiers 1-2: system-prompt addendum / serialized tool
|
|
1538
|
+
* config. Cheap, reversible, text-diffable.
|
|
1539
|
+
* - `CodeSurface` — tier 4: an implementation change behind a worktree ref.
|
|
1540
|
+
* Tier 3 (knowledge) is owned by agent-knowledge and rides its own adapter,
|
|
1541
|
+
* not this type. */
|
|
1542
|
+
type MutableSurface = string | CodeSurface;
|
|
1543
|
+
/** A proposer output carrying the surface AND the WHY behind
|
|
1544
|
+
* it. Reflective proposers (`gepaProposer`) parse a `{label, rationale, payload}`
|
|
1545
|
+
* from the model; without this wrapper the loop keeps only `payload` and the
|
|
1546
|
+
* rationale that motivated the change is lost — the candidate becomes
|
|
1547
|
+
* unattributable. `propose()` may return either bare `MutableSurface`s (cheap
|
|
1548
|
+
* blind mutators) or these (reflective proposers); the loop normalizes both. */
|
|
1549
|
+
interface ProposedCandidate {
|
|
1550
|
+
surface: MutableSurface;
|
|
1551
|
+
/** Short human label for the change (≤ 40 chars typical). */
|
|
1552
|
+
label: string;
|
|
1553
|
+
/** Why this change was proposed — which failure it targets, which
|
|
1554
|
+
* primitive it used. Survives to `GenerationCandidate.rationale` and the
|
|
1555
|
+
* emitted provenance record. */
|
|
1556
|
+
rationale: string;
|
|
1557
|
+
/** Structured, JSON-safe cause for this exact candidate when the proposer
|
|
1558
|
+
* can provide one. Policy edits retain the full validated edit here. */
|
|
1559
|
+
candidateRecord?: PolicyEditCandidateRecord;
|
|
1560
|
+
}
|
|
1561
|
+
/** Type guard: a proposal carrying its rationale vs a bare
|
|
1562
|
+
* surface. The loop branches on this to populate `GenerationCandidate`. */
|
|
1563
|
+
declare function isProposedCandidate(value: MutableSurface | ProposedCandidate): value is ProposedCandidate;
|
|
1564
|
+
/** A non-dominated parent on the GEPA Pareto frontier — a
|
|
1565
|
+
* surface that, across the per-scenario objective vectors, no other tried
|
|
1566
|
+
* surface beats on every scenario. A candidate worse on the mean composite
|
|
1567
|
+
* but uniquely best on one hard scenario is non-dominated and survives here;
|
|
1568
|
+
* the composite-best ranking would discard the lesson it carries. The loop
|
|
1569
|
+
* computes the frontier across ALL generations and hands it to the proposer so
|
|
1570
|
+
* a reflective proposer can combine complementary lessons (GEPA, Agrawal et
|
|
1571
|
+
* al., arXiv:2507.19457). See `pareto.ts` (`paretoFrontier`). */
|
|
1572
|
+
interface ParetoParent {
|
|
1573
|
+
surface: MutableSurface;
|
|
1574
|
+
surfaceHash: string;
|
|
1575
|
+
/** The objective vector: per-scenario composite (higher is better). The
|
|
1576
|
+
* axes the frontier is computed over. */
|
|
1577
|
+
objectives: Record<string, number>;
|
|
1578
|
+
/** Mean composite across the objective scenarios — the scalar summary used
|
|
1579
|
+
* for ordering + display, NOT for dominance. */
|
|
1580
|
+
composite: number;
|
|
1581
|
+
/** Generation that produced this surface (`-1` for the baseline). */
|
|
1582
|
+
generation: number;
|
|
1583
|
+
label?: string;
|
|
1584
|
+
rationale?: string;
|
|
1585
|
+
}
|
|
1586
|
+
/** Exact measured state for the surface an optimizer is learning from.
|
|
1587
|
+
* Unlike a model-authored expected gain, every value here comes from a
|
|
1588
|
+
* completed campaign over the designed denominator. */
|
|
1589
|
+
interface ScoredSurfaceOutcome {
|
|
1590
|
+
/** Optimization/search evidence only. Held-out results must never flow back
|
|
1591
|
+
* into a proposer through this type. */
|
|
1592
|
+
split: 'search';
|
|
1593
|
+
/** Generation that actually measured this surface (`-1` for the baseline). */
|
|
1594
|
+
generation: number;
|
|
1595
|
+
surfaceHash: string;
|
|
1596
|
+
composite: number;
|
|
1597
|
+
dimensions: Record<string, number>;
|
|
1598
|
+
scenarios: Array<{
|
|
1599
|
+
scenarioId: string;
|
|
1600
|
+
composite: number;
|
|
1601
|
+
notes?: string;
|
|
1602
|
+
}>;
|
|
1603
|
+
coverage: {
|
|
1604
|
+
expectedCells: number;
|
|
1605
|
+
scorableCells: number;
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
/** Stateless surface mutation — given findings + current
|
|
1609
|
+
* surface, return N candidate surfaces. Pure transform, no generation
|
|
1610
|
+
* awareness. Reflective-mutation and `AxGEPA` mutators conform. Wrapped by
|
|
1611
|
+
* `evolutionaryProposer` to become a `SurfaceProposer`. */
|
|
1612
|
+
interface Mutator<TFindings = unknown> {
|
|
1613
|
+
kind: string;
|
|
1614
|
+
mutate(args: {
|
|
1615
|
+
findings: TFindings[];
|
|
1616
|
+
currentSurface: MutableSurface;
|
|
1617
|
+
populationSize: number;
|
|
1618
|
+
signal: AbortSignal;
|
|
1619
|
+
}): Promise<Array<MutableSurface | ProposedCandidate>>;
|
|
1620
|
+
}
|
|
1621
|
+
/** Everything a proposer may read to plan the next
|
|
1622
|
+
* batch of candidates. The first six fields are always present; the rest are
|
|
1623
|
+
* optional context the loop supplies when available, so cheap proposers
|
|
1624
|
+
* (`evolutionaryProposer`) can ignore them while a code-tier agentic generator
|
|
1625
|
+
* consumes the report + dataset to drive a coding harness.
|
|
1626
|
+
* See `docs/campaign-proposers.md`. */
|
|
1627
|
+
interface ProposeContext<TFindings = unknown> {
|
|
1628
|
+
currentSurface: MutableSurface;
|
|
1629
|
+
history: GenerationRecord[];
|
|
1630
|
+
findings: TFindings[];
|
|
1631
|
+
/** BREADTH: how many candidate surfaces to return this generation. */
|
|
1632
|
+
populationSize: number;
|
|
1633
|
+
generation: number;
|
|
1634
|
+
signal: AbortSignal;
|
|
1635
|
+
/** Measured baseline for this optimization run. `runOptimization` always
|
|
1636
|
+
* supplies it; optional for standalone proposer callers. */
|
|
1637
|
+
baselineOutcome?: ScoredSurfaceOutcome;
|
|
1638
|
+
/** Measured result for `currentSurface`, the complete global incumbent every
|
|
1639
|
+
* new candidate mutates. `runOptimization` always supplies it. */
|
|
1640
|
+
incumbentOutcome?: ScoredSurfaceOutcome;
|
|
1641
|
+
/** Optional analysis report produced before proposal. Opaque to the substrate:
|
|
1642
|
+
* the proposer that consumes it owns the shape. */
|
|
1643
|
+
report?: unknown;
|
|
1644
|
+
/** Handle to all captured data — the proposer samples traces / artifacts /
|
|
1645
|
+
* rewards here to ground its proposals. */
|
|
1646
|
+
dataset?: LabeledScenarioStore;
|
|
1647
|
+
/** DEPTH: max iterations the agentic generator may take per candidate.
|
|
1648
|
+
* 1 = single-shot; >1 = it may iterate on its own change before handing it
|
|
1649
|
+
* back to be measured. */
|
|
1650
|
+
maxImprovementShots?: number;
|
|
1651
|
+
/** GEPA Pareto frontier across ALL generations so far — the non-dominated
|
|
1652
|
+
* surfaces by per-scenario objective vector. Empty/absent on generation 0
|
|
1653
|
+
* (only the baseline is scored). A reflective proposer combines the
|
|
1654
|
+
* complementary lessons of these parents (each excels on different
|
|
1655
|
+
* scenarios) into a merged candidate. Proposers doing pure single-parent
|
|
1656
|
+
* reflection may ignore it. See {@link ParetoParent}. */
|
|
1657
|
+
paretoParents?: ParetoParent[];
|
|
1658
|
+
/** Shared run spend account and receipt attribution phase. */
|
|
1659
|
+
costLedger?: CostLedger;
|
|
1660
|
+
costPhase?: string;
|
|
1661
|
+
/** FIREWALL (non-negotiable): the held-out judge is write-only — its verdicts
|
|
1662
|
+
* score the chosen output and gate promotion, and are NEVER an input to
|
|
1663
|
+
* proposal/steering (else the optimizer games the acceptance axis = an
|
|
1664
|
+
* oracle). This `never`-typed field makes that a compile-time tripwire: a
|
|
1665
|
+
* proposer that tries to thread judge verdicts into the proposal will not type.
|
|
1666
|
+
* Steering may consume TRACE-OBSERVABLE signals (what the agent did) via
|
|
1667
|
+
* `findings`/`report`; it may NOT consume the judge's held-out verdict. */
|
|
1668
|
+
judgeScores?: never;
|
|
1669
|
+
}
|
|
1670
|
+
/** A surface-improvement strategy. Given the current best
|
|
1671
|
+
* surface, the history of what's been tried + scored, and any external
|
|
1672
|
+
* findings, propose the next batch of candidate surfaces to measure.
|
|
1673
|
+
* Optionally decide to stop early.
|
|
1674
|
+
*
|
|
1675
|
+
* The evolutionary mutator (`evolutionaryProposer`, here) and agent-runtime's
|
|
1676
|
+
* reflective / agentic generators both conform. They are proposers for the
|
|
1677
|
+
* SAME loop, not separate loops. The loop body (`runOptimization`) and the
|
|
1678
|
+
* gated promotion shell (`runImprovementLoop`) are proposer-agnostic.
|
|
1679
|
+
*
|
|
1680
|
+
* This is THE optimization proposer — every optimizer is a factory
|
|
1681
|
+
* `xProposer(opts): SurfaceProposer` (`evolutionaryProposer`, `aceProposer`,
|
|
1682
|
+
* `gepaProposer`, `skillOptProposer`, `traceAnalystProposer`, `haloProposer`,
|
|
1683
|
+
* `memoryCurationProposer`, `fapoProposer`), all exported from `/campaign` and
|
|
1684
|
+
* drivable by `selfImprove({ proposer })`. Not to be confused with the
|
|
1685
|
+
* behavior-fuzzing `MutationProposer` (`fuzz/types`), a scenario generator for
|
|
1686
|
+
* a different loop.
|
|
1687
|
+
*/
|
|
1688
|
+
interface SurfaceProposer<TFindings = unknown> {
|
|
1689
|
+
kind: string;
|
|
1690
|
+
/** Plan: propose N candidate surfaces for the next generation. A proposer
|
|
1691
|
+
* may return bare `MutableSurface`s or `ProposedCandidate`s that carry the
|
|
1692
|
+
* `{label, rationale}` motivating the change — the loop threads the
|
|
1693
|
+
* rationale into `GenerationCandidate` and the emitted provenance. */
|
|
1694
|
+
propose(ctx: ProposeContext<TFindings>): Promise<Array<MutableSurface | ProposedCandidate>>;
|
|
1695
|
+
/** Decide: stop early when the proposer judges the search converged or
|
|
1696
|
+
* exhausted. Default (omitted) runs all `maxGenerations`. */
|
|
1697
|
+
decide?(args: {
|
|
1698
|
+
history: GenerationRecord[];
|
|
1699
|
+
}): {
|
|
1700
|
+
stop: boolean;
|
|
1701
|
+
reason?: string;
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
/** Optional vocabulary alias. The loop is the optimizer; this object is the
|
|
1705
|
+
* proposer inside that loop. */
|
|
1706
|
+
type OptimizationProposer<TFindings = unknown> = SurfaceProposer<TFindings>;
|
|
1707
|
+
interface OptimizerConfigBase {
|
|
1708
|
+
populationSize: number;
|
|
1709
|
+
maxGenerations: number;
|
|
1710
|
+
surfaceExtractor: (profile: unknown) => MutableSurface;
|
|
1711
|
+
}
|
|
1712
|
+
interface OptimizerConfig extends OptimizerConfigBase {
|
|
1713
|
+
proposer: SurfaceProposer;
|
|
1714
|
+
}
|
|
1715
|
+
/** Five-valued verdict taxonomy (MOSS-paper alignment). */
|
|
1716
|
+
type GateDecision = 'ship' | 'hold' | 'need_more_work' | 'model_ceiling' | 'arch_ceiling';
|
|
1717
|
+
interface GateContext<TArtifact, TScenario extends Scenario> {
|
|
1718
|
+
candidateArtifacts: Map<string, TArtifact>;
|
|
1719
|
+
baselineArtifacts?: Map<string, TArtifact>;
|
|
1720
|
+
/** Candidate (winner) judge scores, keyed by cellId. */
|
|
1721
|
+
judgeScores: Map<string, Record<string, JudgeScore>>;
|
|
1722
|
+
/** Baseline judge scores, keyed by cellId. SEPARATE from `judgeScores` —
|
|
1723
|
+
* baseline + candidate share cellIds (same scenarios), so a single map
|
|
1724
|
+
* cannot represent both. A gate computing a holdout delta MUST read
|
|
1725
|
+
* candidate from `judgeScores` and baseline from here. */
|
|
1726
|
+
baselineJudgeScores?: Map<string, Record<string, JudgeScore>>;
|
|
1727
|
+
/** Neutralized-arm judge scores, keyed by cellId — the winner surface with its
|
|
1728
|
+
* content footprint-matched-blanked (via a `neutralize` fn). Same scenarios as
|
|
1729
|
+
* `judgeScores`. Present ONLY when `runImprovementLoop` was given a `neutralize`
|
|
1730
|
+
* function. A placebo gate (`neutralizationGate`) compares this arm's lift
|
|
1731
|
+
* against the candidate's to reject decorative wins (lift from footprint, not
|
|
1732
|
+
* content). Undefined otherwise. */
|
|
1733
|
+
neutralizedJudgeScores?: Map<string, Record<string, JudgeScore>>;
|
|
1734
|
+
/** Neutralized-arm artifacts, keyed by cellId. Present alongside
|
|
1735
|
+
* `neutralizedJudgeScores`. */
|
|
1736
|
+
neutralizedArtifacts?: Map<string, TArtifact>;
|
|
1737
|
+
scenarios: TScenario[];
|
|
1738
|
+
cost: {
|
|
1739
|
+
candidate: number;
|
|
1740
|
+
baseline: number;
|
|
1741
|
+
};
|
|
1742
|
+
/** Shared run spend account and receipt attribution phase. */
|
|
1743
|
+
costLedger?: CostLedger;
|
|
1744
|
+
costPhase?: string;
|
|
1745
|
+
signal: AbortSignal;
|
|
1746
|
+
}
|
|
1747
|
+
interface GateResult {
|
|
1748
|
+
decision: GateDecision;
|
|
1749
|
+
reasons: string[];
|
|
1750
|
+
contributingGates: Array<{
|
|
1751
|
+
name: string;
|
|
1752
|
+
passed: boolean;
|
|
1753
|
+
detail: unknown;
|
|
1754
|
+
}>;
|
|
1755
|
+
delta?: number;
|
|
1756
|
+
}
|
|
1757
|
+
/** Composable promotion gate. */
|
|
1758
|
+
interface Gate<TArtifact = unknown, TScenario extends Scenario = Scenario> {
|
|
1759
|
+
name: string;
|
|
1760
|
+
decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult>;
|
|
1761
|
+
}
|
|
1762
|
+
/** Scoped trace writer handed to each dispatch — every span
|
|
1763
|
+
* auto-tagged with the cellId so traces filter cleanly. */
|
|
1764
|
+
interface CampaignTraceWriter {
|
|
1765
|
+
span(name: string, attributes?: Record<string, unknown>): TraceSpan;
|
|
1766
|
+
flush(): Promise<void>;
|
|
1767
|
+
}
|
|
1768
|
+
interface TraceSpan {
|
|
1769
|
+
end(attributes?: Record<string, unknown>): void;
|
|
1770
|
+
setAttribute(key: string, value: unknown): void;
|
|
1771
|
+
}
|
|
1772
|
+
/** Scoped artifact writer — `write(path, content)` lands under
|
|
1773
|
+
* `<runDir>/<cellId>/<path>`. */
|
|
1774
|
+
interface CampaignArtifactWriter {
|
|
1775
|
+
write(path: string, content: string | Uint8Array): Promise<string>;
|
|
1776
|
+
writeJson(path: string, value: unknown): Promise<string>;
|
|
1777
|
+
}
|
|
1778
|
+
/** Token usage accumulated for a cell. Aliased to the canonical `RunTokenUsage`
|
|
1779
|
+
* (run-record.ts, same package) so a cell maps onto a `RunRecord` for the
|
|
1780
|
+
* backend-integrity guard with ONE source of truth — a field added to
|
|
1781
|
+
* `RunTokenUsage` is a compile error here, not a silent drift. */
|
|
1782
|
+
type CampaignTokenUsage = RunTokenUsage;
|
|
1783
|
+
/** Cell-scoped paid-call entry point. The dispatch places every paid operation
|
|
1784
|
+
* inside `runPaidCall`; the returned provider result supplies one receipt with
|
|
1785
|
+
* cost, tokens, and resolved model. Calls made outside this method are not
|
|
1786
|
+
* admitted or captured. */
|
|
1787
|
+
interface CampaignCostMeter {
|
|
1788
|
+
/** The only paid-call path. Returns a typed result; callers must inspect it. */
|
|
1789
|
+
runPaidCall<T>(input: Omit<RunPaidCallInput<T>, 'channel' | 'phase' | 'tags'> & {
|
|
1790
|
+
channel?: CostChannel;
|
|
1791
|
+
}): Promise<PaidCallResult<T>>;
|
|
1792
|
+
}
|
|
1793
|
+
/** Source tag — required on every store write. Used by the
|
|
1794
|
+
* default training-source filter (production-trace samples NOT used as
|
|
1795
|
+
* training scenarios unless explicitly opted in). */
|
|
1796
|
+
type LabeledScenarioSource = 'production-trace' | 'eval-run' | 'manual' | 'red-team' | 'synthetic';
|
|
1797
|
+
type RedactionStatus = 'raw' | 'redacted-pii' | 'redacted-secrets' | 'fully-redacted';
|
|
1798
|
+
/** How much a label can be trusted to evaluate against — the gold-admission
|
|
1799
|
+
* gate. Strictly ordered: a record qualifies for a `minTrust` filter when its
|
|
1800
|
+
* trust rank is >= the requested rank.
|
|
1801
|
+
*
|
|
1802
|
+
* - `unverified` — label is a heuristic (e.g. raw outcome success/fail).
|
|
1803
|
+
* Fine as corpus; MUST NOT enter a gold set that lift
|
|
1804
|
+
* numbers are computed against.
|
|
1805
|
+
* - `verified-signal` — an external signal confirmed the outcome (PR merged,
|
|
1806
|
+
* tests green, user did not retry, downstream check).
|
|
1807
|
+
* - `human-rated` — a human explicitly rated or corrected the artifact.
|
|
1808
|
+
*
|
|
1809
|
+
* Absent on a write ⇒ treated as `unverified` (fail-closed: a writer must
|
|
1810
|
+
* explicitly assert trust to make a record gold-eligible — it never happens
|
|
1811
|
+
* by accident). */
|
|
1812
|
+
type LabelTrust = 'unverified' | 'verified-signal' | 'human-rated';
|
|
1813
|
+
/** Ordinal rank for a label-trust tier; absent ⇒ `unverified` (rank 0). */
|
|
1814
|
+
declare function labelTrustRank(trust: LabelTrust | undefined): number;
|
|
1815
|
+
/** Required-provenance write. The store rejects writes that
|
|
1816
|
+
* lack provenance — a default-on flywheel without provenance is the
|
|
1817
|
+
* data-poisoning vector flagged in the alignment review. */
|
|
1818
|
+
interface LabeledScenarioWrite<TScenario extends Scenario = Scenario, TArtifact = unknown> {
|
|
1819
|
+
scenario: TScenario;
|
|
1820
|
+
artifact: TArtifact;
|
|
1821
|
+
judgeScores: Record<string, JudgeScore>;
|
|
1822
|
+
source: LabeledScenarioSource;
|
|
1823
|
+
sourceVersionHash: string;
|
|
1824
|
+
capturedAt: string;
|
|
1825
|
+
redactionStatus: RedactionStatus;
|
|
1826
|
+
/** Gold-admission trust tier. Absent ⇒ `unverified` (fail-closed): the
|
|
1827
|
+
* record is corpus, never gold. A writer must explicitly assert
|
|
1828
|
+
* `verified-signal` or `human-rated` to make it eligible for a gold
|
|
1829
|
+
* sample. See {@link LabelTrust}. */
|
|
1830
|
+
labelTrust?: LabelTrust;
|
|
1831
|
+
/** Optional per-source rate-limit bucket key (e.g., the tenant id). */
|
|
1832
|
+
rateLimitBucket?: string;
|
|
1833
|
+
}
|
|
1834
|
+
interface LabeledScenarioRecord<TScenario extends Scenario = Scenario, TArtifact = unknown> extends LabeledScenarioWrite<TScenario, TArtifact> {
|
|
1835
|
+
/** Stable hash of (scenario.id, source, capturedAt, sourceVersionHash). */
|
|
1836
|
+
recordHash: string;
|
|
1837
|
+
/** Substrate-assigned split — train if captured before the campaign's
|
|
1838
|
+
* `temporalCutoff`, test if after. Explicit override allowed via filter. */
|
|
1839
|
+
split: 'train' | 'test';
|
|
1840
|
+
}
|
|
1841
|
+
interface LabeledScenarioSampleArgs {
|
|
1842
|
+
count: number;
|
|
1843
|
+
/** REQUIRED — substrate refuses to sample without an explicit split. */
|
|
1844
|
+
split: 'train' | 'test';
|
|
1845
|
+
/** REQUIRED — only records captured before this timestamp are returned.
|
|
1846
|
+
* Enforces temporal split discipline (test scenarios captured AFTER train
|
|
1847
|
+
* cannot enter the training pool). */
|
|
1848
|
+
capturedBefore: string;
|
|
1849
|
+
filter?: {
|
|
1850
|
+
kind?: string;
|
|
1851
|
+
source?: LabeledScenarioSource | LabeledScenarioSource[];
|
|
1852
|
+
minComposite?: number;
|
|
1853
|
+
maxComposite?: number;
|
|
1854
|
+
/** Gold gate: only records whose trust rank is >= this tier are
|
|
1855
|
+
* returned. `sample({ split: 'test', minTrust: 'verified-signal' })` is
|
|
1856
|
+
* the canonical "give me the gold set" call. Absent ⇒ no trust gate
|
|
1857
|
+
* (corpus-level read). */
|
|
1858
|
+
minTrust?: LabelTrust;
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
interface LabeledScenarioStore {
|
|
1862
|
+
observe(write: LabeledScenarioWrite): Promise<void>;
|
|
1863
|
+
sample(args: LabeledScenarioSampleArgs): Promise<LabeledScenarioRecord[]>;
|
|
1864
|
+
size(): Promise<{
|
|
1865
|
+
train: number;
|
|
1866
|
+
test: number;
|
|
1867
|
+
bySource: Record<string, number>;
|
|
1868
|
+
/** Count by trust tier — tells the flywheel how much gold it has
|
|
1869
|
+
* accumulated vs. raw corpus. */
|
|
1870
|
+
byTrust: Record<LabelTrust, number>;
|
|
1871
|
+
}>;
|
|
1872
|
+
}
|
|
1873
|
+
interface CampaignCellResult<TArtifact> {
|
|
1874
|
+
/** Manifest that produced this cell. Resumability refuses to reuse a cell
|
|
1875
|
+
* whose manifest differs from the current run. */
|
|
1876
|
+
manifestHash?: string;
|
|
1877
|
+
cellId: string;
|
|
1878
|
+
scenarioId: string;
|
|
1879
|
+
rep: number;
|
|
1880
|
+
generation?: number;
|
|
1881
|
+
artifact: TArtifact;
|
|
1882
|
+
judgeScores: Record<string, JudgeScore>;
|
|
1883
|
+
costUsd: number;
|
|
1884
|
+
/** True when at least one priced receipt used the model table instead of a provider bill. */
|
|
1885
|
+
costEstimated?: boolean;
|
|
1886
|
+
/** Exact durable receipts required to reuse this cached result. */
|
|
1887
|
+
costCallIds?: string[];
|
|
1888
|
+
/** Agent-call token usage committed by `ctx.cost.runPaidCall`.
|
|
1889
|
+
* `{ input: 0, output: 0 }` when no paid agent call was recorded. */
|
|
1890
|
+
tokenUsage: CampaignTokenUsage;
|
|
1891
|
+
/** Concrete model from the latest committed agent receipt. Consumed by
|
|
1892
|
+
* `buildRunRecord` to pin the model when the declared profile uses a
|
|
1893
|
+
* runtime-resolved sentinel. */
|
|
1894
|
+
resolvedModel?: string;
|
|
1895
|
+
durationMs: number;
|
|
1896
|
+
seed: number;
|
|
1897
|
+
cached: boolean;
|
|
1898
|
+
error?: string;
|
|
1899
|
+
}
|
|
1900
|
+
interface JudgeAggregate {
|
|
1901
|
+
mean: number;
|
|
1902
|
+
stdev: number;
|
|
1903
|
+
ci95: [number, number];
|
|
1904
|
+
n: number;
|
|
1905
|
+
}
|
|
1906
|
+
interface ScenarioAggregate {
|
|
1907
|
+
meanComposite: number;
|
|
1908
|
+
ci95: [number, number];
|
|
1909
|
+
n: number;
|
|
1910
|
+
}
|
|
1911
|
+
interface GenerationRecord {
|
|
1912
|
+
generationIndex: number;
|
|
1913
|
+
candidates: GenerationCandidate[];
|
|
1914
|
+
promoted: string[];
|
|
1915
|
+
}
|
|
1916
|
+
/** One scored candidate surface in a generation. `dimensions` + `scenarios`
|
|
1917
|
+
* let a reflective proposer ground its next proposal on WHICH
|
|
1918
|
+
* dimensions the candidate is weakest on and WHICH scenarios it best/worst
|
|
1919
|
+
* handled — the evidence a blind `Mutator` cannot see. */
|
|
1920
|
+
interface GenerationCandidate {
|
|
1921
|
+
surfaceHash: string;
|
|
1922
|
+
composite: number;
|
|
1923
|
+
ci95: [number, number];
|
|
1924
|
+
/** Exact surface this candidate mutated. */
|
|
1925
|
+
parentSurfaceHash?: string;
|
|
1926
|
+
/** Measured search-split composite of the exact parent surface. */
|
|
1927
|
+
parentComposite?: number;
|
|
1928
|
+
/** Candidate composite minus its parent's composite. Present only when the
|
|
1929
|
+
* candidate completed the designed denominator. */
|
|
1930
|
+
observedDeltaFromParent?: number;
|
|
1931
|
+
/** Whether this candidate had a scorable result for every designed campaign
|
|
1932
|
+
* cell and was therefore eligible for ranking, promotion, and Pareto
|
|
1933
|
+
* selection. Older externally-authored records may omit this field; loop
|
|
1934
|
+
* records always populate it. */
|
|
1935
|
+
eligibleForPromotion?: boolean;
|
|
1936
|
+
/** Exact denominator receipt for selection eligibility. Scores stay
|
|
1937
|
+
* descriptive: an incomplete candidate is retained with its observed score
|
|
1938
|
+
* and errors instead of receiving an invented penalty. */
|
|
1939
|
+
coverage?: {
|
|
1940
|
+
expectedCells: number;
|
|
1941
|
+
scorableCells: number;
|
|
1942
|
+
unscorableCells: Array<{
|
|
1943
|
+
cellId: string;
|
|
1944
|
+
reason: string;
|
|
1945
|
+
}>;
|
|
1946
|
+
};
|
|
1947
|
+
/** Mean score per judge dimension across all cells (scenarios × reps ×
|
|
1948
|
+
* judges that reported the dimension). */
|
|
1949
|
+
dimensions: Record<string, number>;
|
|
1950
|
+
/** Per-scenario composite (mean over reps + judges), plus the judge's
|
|
1951
|
+
* free-form `notes` for that scenario — the "why it scored low" evidence a
|
|
1952
|
+
* reflective proposer grounds its next edit on. Keep `notes` GENERALIZABLE
|
|
1953
|
+
* (which checks/lines/dimensions failed and how), NOT case-specific ground
|
|
1954
|
+
* truth: leaking expected answers into the prompt is memorization, and the
|
|
1955
|
+
* held-out gate would reject it anyway. */
|
|
1956
|
+
scenarios: Array<{
|
|
1957
|
+
scenarioId: string;
|
|
1958
|
+
composite: number;
|
|
1959
|
+
notes?: string;
|
|
1960
|
+
}>;
|
|
1961
|
+
/** Proposer-supplied short label for the change. Present when the proposer
|
|
1962
|
+
* returned a `ProposedCandidate`; absent for bare-surface mutators. */
|
|
1963
|
+
label?: string;
|
|
1964
|
+
/** Proposer-supplied rationale — WHY this candidate was proposed. The
|
|
1965
|
+
* "because rationale Z" the audit requires to survive to the result.
|
|
1966
|
+
* Present when the proposer returned a `ProposedCandidate`. */
|
|
1967
|
+
rationale?: string;
|
|
1968
|
+
/** Exact structured cause threaded from the proposer, when available. */
|
|
1969
|
+
candidateRecord?: PolicyEditCandidateRecord;
|
|
1970
|
+
}
|
|
1971
|
+
interface CampaignAggregates {
|
|
1972
|
+
byJudge: Record<string, JudgeAggregate>;
|
|
1973
|
+
byScenario: Record<string, ScenarioAggregate>;
|
|
1974
|
+
/** Canonical campaign accounting, including worker and judge calls. */
|
|
1975
|
+
cost: CostLedgerSummary;
|
|
1976
|
+
/** Compatibility alias of `cost.totalCostUsd`. */
|
|
1977
|
+
totalCostUsd: number;
|
|
1978
|
+
cellsExecuted: number;
|
|
1979
|
+
cellsSkipped: number;
|
|
1980
|
+
cellsCached: number;
|
|
1981
|
+
cellsFailed: number;
|
|
1982
|
+
}
|
|
1983
|
+
interface CampaignResult<TArtifact = unknown, TScenario extends Scenario = Scenario> {
|
|
1984
|
+
/** sha256(scenarios, judges, dispatch source ref, optimizer config, seed). Stable identity for reruns. */
|
|
1985
|
+
manifestHash: string;
|
|
1986
|
+
seed: number;
|
|
1987
|
+
startedAt: string;
|
|
1988
|
+
endedAt: string;
|
|
1989
|
+
durationMs: number;
|
|
1990
|
+
cells: Array<CampaignCellResult<TArtifact>>;
|
|
1991
|
+
aggregates: CampaignAggregates;
|
|
1992
|
+
optimization?: {
|
|
1993
|
+
generations: GenerationRecord[];
|
|
1994
|
+
winnerSurfaceHash?: string;
|
|
1995
|
+
};
|
|
1996
|
+
gate?: GateResult;
|
|
1997
|
+
prUrl?: string;
|
|
1998
|
+
runDir: string;
|
|
1999
|
+
artifactsByPath: Record<string, string>;
|
|
2000
|
+
/** Substrate strips the input scenarios to id+kind for the result manifest;
|
|
2001
|
+
* consumers needing full payload look it up via the original input. The
|
|
2002
|
+
* type parameter `TScenario` is propagated for downstream consumers that
|
|
2003
|
+
* want narrowed types when extending `CampaignResult`. */
|
|
2004
|
+
scenarios: Array<Pick<TScenario, 'id' | 'kind'>>;
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
/**
|
|
2008
|
+
* `llmJudge` — the single-LLM-call bridge that turns a rubric prompt into a
|
|
2009
|
+
* canonical campaign `JudgeConfig`.
|
|
2010
|
+
*
|
|
2011
|
+
* The `JudgeConfig` contract (src/campaign/types.ts) is deliberately a
|
|
2012
|
+
* function, not a fixed LLM-prompt shape: real consumers judge with
|
|
2013
|
+
* ensembles, deterministic checks, or one LLM call. `ensembleJudge`
|
|
2014
|
+
* (src/judge-panel.ts) covers the multi-model case; `buildAgreementJudge`
|
|
2015
|
+
* (src/campaign/distillation) covers the pure-comparator case. `llmJudge`
|
|
2016
|
+
* covers the common single-call case the `JudgeConfig` doc-comment names:
|
|
2017
|
+
* one model call against `prompt`, parsed into the canonical `JudgeScore`
|
|
2018
|
+
* (`{ dimensions, composite, notes }`) on the campaign [0,1] scale.
|
|
2019
|
+
*
|
|
2020
|
+
* Transport is injected as a `ChatClient` (src/analyst/chat-client.ts) — the
|
|
2021
|
+
* substrate's transport-agnostic LLM seam — so the judge stays decoupled from
|
|
2022
|
+
* router-vs-sandbox-vs-cli-bridge and is unit-testable with the `mock`
|
|
2023
|
+
* transport. The composite is computed by `weightedComposite` (the same
|
|
2024
|
+
* sum-normalized weighting `ensembleJudge` uses), so a lift is attributable to
|
|
2025
|
+
* the dimension scores, not to a bespoke reducer.
|
|
2026
|
+
*
|
|
2027
|
+
* Fail-loud throughout: an unparseable model response throws `JudgeParseError`;
|
|
2028
|
+
* a response missing a declared dimension throws; an out-of-range score throws.
|
|
2029
|
+
* A thrown judge is recorded by the campaign engine as a failed cell, never
|
|
2030
|
+
* folded into a silent zero.
|
|
2031
|
+
*/
|
|
2032
|
+
|
|
2033
|
+
/** A rubric dimension as a bare key or the full `{ key, description }` shape. A
|
|
2034
|
+
* bare string uses the key as its own description. */
|
|
2035
|
+
type LlmJudgeDimension = string | JudgeDimension;
|
|
2036
|
+
interface LlmJudgeOptions<TArtifact, TScenario extends Scenario = Scenario> {
|
|
2037
|
+
/** The injected LLM transport. One `chat()` call per `score()`. Required —
|
|
2038
|
+
* there is no default route, so a misconfigured judge fails at construction,
|
|
2039
|
+
* never silently against the free-tier router. */
|
|
2040
|
+
chat: ChatClient;
|
|
2041
|
+
/** Rubric dimensions the model scores. Each becomes a `[0,1]` field of the
|
|
2042
|
+
* returned `JudgeScore.dimensions`. Defaults to a single `quality` dimension. */
|
|
2043
|
+
dimensions?: LlmJudgeDimension[];
|
|
2044
|
+
/** Model id. Falls back to `chat.defaultModel`; one of the two MUST resolve. */
|
|
2045
|
+
model?: string;
|
|
2046
|
+
/** Explicit scoring revision for opaque transport or renderer changes. */
|
|
2047
|
+
judgeVersion?: string;
|
|
2048
|
+
temperature?: number;
|
|
2049
|
+
maxTokens?: number;
|
|
2050
|
+
/** Composite weights forwarded to `weightedComposite`: a partial map selects
|
|
2051
|
+
* AND weights exactly the named dimensions. Omit for a uniform mean. */
|
|
2052
|
+
weights?: Record<string, number>;
|
|
2053
|
+
/** Scale the model is prompted to score on, normalized into `[0,1]`:
|
|
2054
|
+
* - `'unit'` (default): the model returns `[0,1]` directly.
|
|
2055
|
+
* - `'ten'`: the model returns `[0,10]`; divided by 10 here.
|
|
2056
|
+
* The prompt is annotated with the expected range either way. */
|
|
2057
|
+
scale?: 'unit' | 'ten';
|
|
2058
|
+
/** Run this judge only on matching scenarios (mirrors `JudgeConfig.appliesTo`). */
|
|
2059
|
+
appliesTo?: (scenario: TScenario) => boolean;
|
|
2060
|
+
/** Render the artifact + scenario into the user message. Default:
|
|
2061
|
+
* pretty-printed JSON of `{ scenario, artifact }`. */
|
|
2062
|
+
renderUser?: (input: {
|
|
2063
|
+
artifact: TArtifact;
|
|
2064
|
+
scenario: TScenario;
|
|
2065
|
+
}) => string;
|
|
2066
|
+
/** Strict runtime contract; its JSON Schema is sent to the provider. */
|
|
2067
|
+
costLedger?: CostLedger;
|
|
2068
|
+
responseSchema?: {
|
|
2069
|
+
name: string;
|
|
2070
|
+
schema: z.ZodObject;
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
/**
|
|
2074
|
+
* Build a campaign-shaped `JudgeConfig` whose `score()` makes ONE LLM call
|
|
2075
|
+
* against `prompt` and reduces the model's per-dimension scores to a canonical
|
|
2076
|
+
* `JudgeScore` in `[0,1]`.
|
|
2077
|
+
*
|
|
2078
|
+
* The model is instructed to return JSON `{ "dimensions": { <key>: <number>, … },
|
|
2079
|
+
* "notes": "…" }`; the helper strips fenced JSON, validates every declared
|
|
2080
|
+
* dimension is present and in range, normalizes by `scale`, and composites via
|
|
2081
|
+
* `weightedComposite`.
|
|
2082
|
+
*/
|
|
2083
|
+
declare function llmJudge<TArtifact = unknown, TScenario extends Scenario = Scenario>(name: string, prompt: string, opts: LlmJudgeOptions<TArtifact, TScenario>): JudgeConfig<TArtifact, TScenario>;
|
|
2084
|
+
|
|
2085
|
+
interface ReferenceEquivalenceScenario extends Scenario {
|
|
2086
|
+
userRequest: string;
|
|
2087
|
+
expectedAnswer: string;
|
|
2088
|
+
}
|
|
2089
|
+
interface ReferenceEquivalenceJudgeOptions {
|
|
2090
|
+
/** Injected transport. No implicit provider or credentials are selected. */
|
|
2091
|
+
chat: ChatClient;
|
|
2092
|
+
/** Falls back to the ChatClient's default model. */
|
|
2093
|
+
model?: string;
|
|
2094
|
+
/** Used only by the direct-call adapter. */
|
|
2095
|
+
signal?: AbortSignal;
|
|
2096
|
+
/** Optional receipt destination for direct calls; campaigns supply their own. */
|
|
2097
|
+
costLedger?: CostLedger;
|
|
2098
|
+
}
|
|
2099
|
+
/** Build the campaign-native expected-answer judge. */
|
|
2100
|
+
declare function createReferenceEquivalenceJudge(options: ReferenceEquivalenceJudgeOptions): JudgeConfig<string, ReferenceEquivalenceScenario>;
|
|
2101
|
+
|
|
2102
|
+
interface AnalyzeTracesInput {
|
|
2103
|
+
/** The user-facing question. Domain framing belongs here, not in the
|
|
2104
|
+
* actor description. */
|
|
2105
|
+
question: string;
|
|
2106
|
+
}
|
|
2107
|
+
interface AnalyzeTracesResult {
|
|
2108
|
+
/** The responder's prose answer. */
|
|
2109
|
+
answer: string;
|
|
2110
|
+
/** Bulleted findings extracted from the responder's structured output. */
|
|
2111
|
+
findings: string[];
|
|
2112
|
+
/** Per-actor-turn snapshots captured via `actorTurnCallback`. */
|
|
2113
|
+
turns: AnalyzeTracesTurnSnapshot[];
|
|
2114
|
+
/** Total turns the actor took. */
|
|
2115
|
+
turnCount: number;
|
|
2116
|
+
/** Token usage by role. */
|
|
2117
|
+
usage: TraceAnalystUsage;
|
|
2118
|
+
/** Full system + assistant + tool message log by role. */
|
|
2119
|
+
chatLog: TraceAnalystChatLog;
|
|
2120
|
+
/** Prompt version that produced this run. */
|
|
2121
|
+
actorPromptVersion: string;
|
|
2122
|
+
}
|
|
2123
|
+
interface TraceAnalystUsage {
|
|
2124
|
+
actor: TraceAnalystUsageEntry[];
|
|
2125
|
+
responder: TraceAnalystUsageEntry[];
|
|
2126
|
+
}
|
|
2127
|
+
interface TraceAnalystUsageEntry {
|
|
2128
|
+
[key: string]: unknown;
|
|
2129
|
+
}
|
|
2130
|
+
interface TraceAnalystChatLog {
|
|
2131
|
+
actor: TraceAnalystChatMessage[];
|
|
2132
|
+
responder: TraceAnalystChatMessage[];
|
|
2133
|
+
}
|
|
2134
|
+
interface TraceAnalystChatMessage {
|
|
2135
|
+
[key: string]: unknown;
|
|
2136
|
+
}
|
|
2137
|
+
interface AnalyzeTracesTurnSnapshot {
|
|
2138
|
+
turn: number;
|
|
2139
|
+
isError: boolean;
|
|
2140
|
+
/** The JS code the actor produced for this turn. */
|
|
2141
|
+
code: string;
|
|
2142
|
+
/** The formatted action-log entry the actor sees on the next turn. */
|
|
2143
|
+
output: string;
|
|
2144
|
+
/** Provider thought (when `actorOptions.showThoughts` is true and the
|
|
2145
|
+
* provider returns it). */
|
|
2146
|
+
thought?: string;
|
|
2147
|
+
}
|
|
2148
|
+
interface AnalyzeTracesOptions {
|
|
2149
|
+
/** Trace data source. Pass either an OTLP-JSONL path or a custom store. */
|
|
2150
|
+
source: string | TraceAnalysisStore;
|
|
2151
|
+
/** Caller-provided AxAIService. */
|
|
2152
|
+
ai: AxAIService;
|
|
2153
|
+
/** Model id forwarded to actor + responder. */
|
|
2154
|
+
model?: string;
|
|
2155
|
+
/** Recursion depth. 0 = no sub-agent dispatch. Default 1. */
|
|
2156
|
+
maxDepth?: number;
|
|
2157
|
+
/** Maximum actor turns. Default 12. */
|
|
2158
|
+
maxTurns?: number;
|
|
2159
|
+
/** Maximum parallel sub-agent calls in batched llmQuery. Default 2. */
|
|
2160
|
+
maxParallelSubagents?: number;
|
|
2161
|
+
/** Override the actor description. */
|
|
2162
|
+
actorDescription?: string;
|
|
2163
|
+
/** Override the subagent description. */
|
|
2164
|
+
subagentDescription?: string;
|
|
2165
|
+
/** Per-turn observability hook. */
|
|
2166
|
+
onTurn?: (turn: AnalyzeTracesTurnSnapshot) => void | Promise<void>;
|
|
2167
|
+
/** Override max runtime characters per turn. Default 6000. */
|
|
2168
|
+
maxRuntimeChars?: number;
|
|
2169
|
+
/** When set, every turn's snapshot is appended to this JSONL file
|
|
2170
|
+
* immediately. If the analyst crashes mid-loop (provider 503,
|
|
2171
|
+
* network error, validator reject) the partial reasoning is still
|
|
2172
|
+
* on disk. Replay the file with the responder afterward to recover
|
|
2173
|
+
* evidence. */
|
|
2174
|
+
progressLogPath?: string;
|
|
2175
|
+
}
|
|
2176
|
+
|
|
273
2177
|
/**
|
|
274
2178
|
* Make the trace-analyst's OWN prompt a GEPA-optimizable surface.
|
|
275
2179
|
*
|
|
@@ -377,6 +2281,210 @@ interface FailureModeRecallJudgeOptions {
|
|
|
377
2281
|
*/
|
|
378
2282
|
declare function failureModeRecallJudge(opts?: FailureModeRecallJudgeOptions): JudgeConfig<AnalystArtifact, AnalystScenario>;
|
|
379
2283
|
|
|
2284
|
+
/**
|
|
2285
|
+
* `openAutoPr` — thin shell-out helper for the `runImprovementLoop` preset's
|
|
2286
|
+
* `autoOnPromote: 'pr'` mode. Substitutes for the per-product PR-opening
|
|
2287
|
+
* code consumers duplicated 4 times. The PR body includes the campaign's
|
|
2288
|
+
* manifest hash, gate verdict, and scorecard summary so reviewers can see
|
|
2289
|
+
* exactly what was promoted + why.
|
|
2290
|
+
*
|
|
2291
|
+
* NOT a deploy mechanism — this only OPENS a PR. The human reviews + merges.
|
|
2292
|
+
* The Shape B (`autoOnPromote: 'config'`) live-runtime-mutation path is
|
|
2293
|
+
* deferred to Pass B with the full shadow / canary / rollback stack.
|
|
2294
|
+
*/
|
|
2295
|
+
|
|
2296
|
+
interface OpenAutoPrOptions<TArtifact, TScenario extends Scenario> {
|
|
2297
|
+
/** Campaign result to attach to the PR. */
|
|
2298
|
+
result: CampaignResult<TArtifact, TScenario>;
|
|
2299
|
+
/** Gate verdict explaining the promotion. Substrate refuses to open a PR
|
|
2300
|
+
* when `gate.decision !== 'ship'` — fails loud. */
|
|
2301
|
+
gate: GateResult;
|
|
2302
|
+
/** Promoted surface diff — typically the new system prompt addendum or
|
|
2303
|
+
* full profile diff. Substrate writes it as the PR body. */
|
|
2304
|
+
promotedDiff: string;
|
|
2305
|
+
/** GH owner/repo target (e.g., `tangle-network/gtm-agent`). */
|
|
2306
|
+
ghOwner: string;
|
|
2307
|
+
ghRepo: string;
|
|
2308
|
+
/** Branch name for the PR. Default `auto/<manifestHash[:12]>`. */
|
|
2309
|
+
branch?: string;
|
|
2310
|
+
/** PR title. Default includes manifest hash. */
|
|
2311
|
+
title?: string;
|
|
2312
|
+
/** Whether to actually open the PR or just dry-run. Default reads
|
|
2313
|
+
* `GH_AUTO_PR_TOKEN` env — present = open, absent = dry-run. */
|
|
2314
|
+
dryRun?: boolean;
|
|
2315
|
+
/** Test seam — substitute `gh pr create` invocation. */
|
|
2316
|
+
ghExec?: (args: string[]) => {
|
|
2317
|
+
stdout: string;
|
|
2318
|
+
stderr: string;
|
|
2319
|
+
status: number;
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
interface OpenAutoPrResult {
|
|
2323
|
+
opened: boolean;
|
|
2324
|
+
prUrl?: string;
|
|
2325
|
+
dryRun: boolean;
|
|
2326
|
+
reason: string;
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* Open a GitHub PR for a gate-approved surface promotion, attaching the manifest hash, gate verdict, and diff as the PR body.
|
|
2330
|
+
*/
|
|
2331
|
+
declare function openAutoPr<TArtifact, TScenario extends Scenario>(options: OpenAutoPrOptions<TArtifact, TScenario>): OpenAutoPrResult;
|
|
2332
|
+
|
|
2333
|
+
interface ContinuousAgreement {
|
|
2334
|
+
/** Cohen's κ_w with quadratic weights, computed on raw [0,1] scores. */
|
|
2335
|
+
weightedKappa: number;
|
|
2336
|
+
/** ICC(2,1): two-way random effects, absolute agreement, single rater. */
|
|
2337
|
+
icc: number;
|
|
2338
|
+
/** Pearson product-moment correlation (averaged over rater pairs if N>2). */
|
|
2339
|
+
pearson: number;
|
|
2340
|
+
/** Spearman rank correlation (averaged over rater pairs if N>2). */
|
|
2341
|
+
spearman: number;
|
|
2342
|
+
/** 95% bootstrap percentile CIs over items. */
|
|
2343
|
+
ci: {
|
|
2344
|
+
icc: [number, number];
|
|
2345
|
+
weightedKappa: [number, number];
|
|
2346
|
+
};
|
|
2347
|
+
/** Number of complete items (no NaN across raters). */
|
|
2348
|
+
n: number;
|
|
2349
|
+
/** Number of raters. */
|
|
2350
|
+
raters: number;
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
interface PairedBootstrapResult {
|
|
2354
|
+
/** Number of paired observations. */
|
|
2355
|
+
n: number;
|
|
2356
|
+
/** Median of paired deltas (after − before). */
|
|
2357
|
+
median: number;
|
|
2358
|
+
/** Mean of paired deltas. */
|
|
2359
|
+
mean: number;
|
|
2360
|
+
/** Lower bound of the bootstrap CI on the chosen statistic. */
|
|
2361
|
+
low: number;
|
|
2362
|
+
/** Upper bound of the bootstrap CI on the chosen statistic. */
|
|
2363
|
+
high: number;
|
|
2364
|
+
/** Confidence level used (e.g. 0.95). */
|
|
2365
|
+
confidence: number;
|
|
2366
|
+
/** Number of bootstrap resamples used. */
|
|
2367
|
+
resamples: number;
|
|
2368
|
+
}
|
|
2369
|
+
/** Result of a McNemar paired-binary significance test. */
|
|
2370
|
+
interface McNemarResult {
|
|
2371
|
+
/** Total paired observations. */
|
|
2372
|
+
n: number;
|
|
2373
|
+
/** Discordant pairs (b + c) — the only ones that carry signal. */
|
|
2374
|
+
nDiscordant: number;
|
|
2375
|
+
/** Pairs where treatment succeeded and control failed ("newly correct"). */
|
|
2376
|
+
b: number;
|
|
2377
|
+
/** Pairs where control succeeded and treatment failed ("newly wrong"). */
|
|
2378
|
+
c: number;
|
|
2379
|
+
/** Continuity-corrected chi-square statistic (reference; exact p drives the call). */
|
|
2380
|
+
statistic: number;
|
|
2381
|
+
/** Two-sided p-value. Exact (binomial sign test on discordant pairs). */
|
|
2382
|
+
pValue: number;
|
|
2383
|
+
}
|
|
2384
|
+
/** A paired binary effect size (treatment rate − control rate) with a CI. */
|
|
2385
|
+
interface RiskDifferenceResult {
|
|
2386
|
+
/** Total paired observations. */
|
|
2387
|
+
n: number;
|
|
2388
|
+
/** Discordant pairs: treatment-win count. */
|
|
2389
|
+
b: number;
|
|
2390
|
+
/** Discordant pairs: control-win count. */
|
|
2391
|
+
c: number;
|
|
2392
|
+
/** Paired risk difference p(treatment) − p(control) = (b − c) / n. */
|
|
2393
|
+
riskDifference: number;
|
|
2394
|
+
/** Lower bound of the CI, clamped to [-1, 1]. */
|
|
2395
|
+
lower: number;
|
|
2396
|
+
/** Upper bound of the CI, clamped to [-1, 1]. */
|
|
2397
|
+
upper: number;
|
|
2398
|
+
/** Confidence level used. */
|
|
2399
|
+
confidence: number;
|
|
2400
|
+
}
|
|
2401
|
+
interface EProcessStep {
|
|
2402
|
+
/** Current wealth W_n — the e-value against H0 after n observations. */
|
|
2403
|
+
wealth: number;
|
|
2404
|
+
/** Observations consumed so far. */
|
|
2405
|
+
n: number;
|
|
2406
|
+
/** True from the first n where W_n ≥ 1/alpha onward (sticky). */
|
|
2407
|
+
decided: boolean;
|
|
2408
|
+
}
|
|
2409
|
+
interface EProcessState extends EProcessStep {
|
|
2410
|
+
alpha: number;
|
|
2411
|
+
maxBet: number;
|
|
2412
|
+
nullMean: number;
|
|
2413
|
+
/** The decision boundary 1/alpha. */
|
|
2414
|
+
threshold: number;
|
|
2415
|
+
/** Observation count at the first threshold crossing; undefined until decided. */
|
|
2416
|
+
decidedAtN?: number;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
/**
|
|
2420
|
+
* Matched-pair arm comparison — "did the treatment arm beat the baseline arm
|
|
2421
|
+
* on the SAME work items?"
|
|
2422
|
+
*
|
|
2423
|
+
* An arm A/B over run records is only trustworthy when it is PAIRED: the same
|
|
2424
|
+
* task/scenario/seed evaluated under both arms, compared item-by-item, so
|
|
2425
|
+
* inter-item difficulty variance cancels instead of masquerading as an arm
|
|
2426
|
+
* effect. This module owns the two error-prone steps every consumer otherwise
|
|
2427
|
+
* hand-rolls:
|
|
2428
|
+
*
|
|
2429
|
+
* 1. Pairing — matching rows across arms by `pairKey` (and by `repKey`
|
|
2430
|
+
* within multi-rep items), with leftovers REPORTED rather than silently
|
|
2431
|
+
* dropped (a silently unbalanced pairing biases every paired statistic
|
|
2432
|
+
* downstream). Pairing never keys on outcome content: matching reps by
|
|
2433
|
+
* their outcomes deflates discordant-pair counts and makes McNemar
|
|
2434
|
+
* anti-conservative, so reps pair only by (`pairKey`, `repKey`) identity.
|
|
2435
|
+
* 2. Composition — feeding the matched pairs to the correct paired
|
|
2436
|
+
* estimators that already live in `statistics`: `mcnemar` +
|
|
2437
|
+
* `pairedRiskDifference` for pass/fail, `pairedBootstrap` +
|
|
2438
|
+
* `wilcoxonSignedRank` for continuous metrics. No statistic is
|
|
2439
|
+
* re-implemented here.
|
|
2440
|
+
*
|
|
2441
|
+
* The row shape is deliberately structural — callers project a `RunRecord`
|
|
2442
|
+
* (or any record) into `{ pairKey, arm, pass?, metrics? }`. Arm names are
|
|
2443
|
+
* caller-supplied parameters; the module ships no domain literal.
|
|
2444
|
+
*/
|
|
2445
|
+
|
|
2446
|
+
/** Paired pass/fail comparison over the pairs where BOTH sides carry `pass`. */
|
|
2447
|
+
interface PairedCorrectness {
|
|
2448
|
+
/** Discordant pairs where the treatment passed and the baseline failed. */
|
|
2449
|
+
b10: number;
|
|
2450
|
+
/** Discordant pairs where the baseline passed and the treatment failed. */
|
|
2451
|
+
b01: number;
|
|
2452
|
+
/** Exact McNemar significance over the paired outcomes (`b === b10`, `c === b01`). */
|
|
2453
|
+
mcnemar: McNemarResult;
|
|
2454
|
+
/** Paired effect size: p(treatment) − p(baseline) with a paired-variance CI. */
|
|
2455
|
+
riskDifference: RiskDifferenceResult;
|
|
2456
|
+
}
|
|
2457
|
+
/** Paired delta summary for one named metric (delta = treatment − baseline). */
|
|
2458
|
+
interface PairedMetricDelta {
|
|
2459
|
+
name: string;
|
|
2460
|
+
/** Pairs where BOTH sides carry a finite value for this metric. */
|
|
2461
|
+
n: number;
|
|
2462
|
+
/** Pairs where at least one side does not carry the metric. */
|
|
2463
|
+
nMissing: number;
|
|
2464
|
+
/** Median paired delta; NaN when `n === 0` (no data ≠ measured zero). */
|
|
2465
|
+
medianDelta: number;
|
|
2466
|
+
/** Mean paired delta; NaN when `n === 0`. */
|
|
2467
|
+
meanDelta: number;
|
|
2468
|
+
/** Bootstrap CI on the paired delta (`pairedBootstrap`); null when
|
|
2469
|
+
* `n === 0` — a zero-width [0, 0] interval on no data would read as a
|
|
2470
|
+
* measured tight null. */
|
|
2471
|
+
bootstrapCi: PairedBootstrapResult | null;
|
|
2472
|
+
/** Wilcoxon signed-rank test on the paired deltas; null when `n === 0`. */
|
|
2473
|
+
wilcoxon: {
|
|
2474
|
+
w: number;
|
|
2475
|
+
p: number;
|
|
2476
|
+
} | null;
|
|
2477
|
+
}
|
|
2478
|
+
interface PairedArmsComparison {
|
|
2479
|
+
nPairs: number;
|
|
2480
|
+
nUnpairedBaseline: number;
|
|
2481
|
+
nUnpairedTreatment: number;
|
|
2482
|
+
/** null when no matched pair carries `pass` on both sides — a pass/fail
|
|
2483
|
+
* verdict over rows that never measured pass/fail would be fabricated. */
|
|
2484
|
+
correctness: PairedCorrectness | null;
|
|
2485
|
+
metricDeltas: PairedMetricDelta[];
|
|
2486
|
+
}
|
|
2487
|
+
|
|
380
2488
|
/** Whether one candidate attempt produced a usable executable outcome. */
|
|
381
2489
|
type CrossSurfaceAttemptCompleteness = 'complete' | 'missing' | 'invalid';
|
|
382
2490
|
/** One independently proposed change on one caller-defined surface. */
|
|
@@ -636,19 +2744,208 @@ interface CrossSurfaceInteractionReport<TRow extends CrossSurfaceTaskRow = Cross
|
|
|
636
2744
|
pairwise: CrossSurfacePairwiseEntry[];
|
|
637
2745
|
selections: CrossSurfaceSelections;
|
|
638
2746
|
}
|
|
639
|
-
|
|
2747
|
+
|
|
2748
|
+
/**
|
|
2749
|
+
* Task-paired comparison and deterministic selection for independently
|
|
2750
|
+
* proposed agent surfaces. Unlike factorial cell-mean attribution, every
|
|
2751
|
+
* benefit, regression, and interaction remains attached to its task row.
|
|
2752
|
+
*/
|
|
2753
|
+
|
|
2754
|
+
/**
|
|
2755
|
+
* Build the complete cross-surface evidence matrix and derive all three frozen
|
|
2756
|
+
* candidates. The task/candidate/component orders are part of the input so
|
|
2757
|
+
* neither insertion order nor an after-the-fact tie-break can change a result.
|
|
2758
|
+
*/
|
|
2759
|
+
declare function analyzeCrossSurfaceInteractions<TRow extends CrossSurfaceTaskRow>(input: AnalyzeCrossSurfaceInteractionsInput<TRow>): CrossSurfaceInteractionReport<TRow>;
|
|
2760
|
+
|
|
2761
|
+
/**
|
|
2762
|
+
* `CampaignStorage` — the filesystem seam `runCampaign` writes through
|
|
2763
|
+
* (run/cell dirs, the resumability cache, per-cell artifacts, trace spans).
|
|
2764
|
+
*
|
|
2765
|
+
* The default (`fsCampaignStorage`) is the Node filesystem — identical
|
|
2766
|
+
* behavior to the inline `node:fs` calls it replaces, so existing CLI
|
|
2767
|
+
* consumers are unaffected. `inMemoryCampaignStorage` keeps everything in a
|
|
2768
|
+
* `Map`, so the substrate runs in environments WITHOUT a filesystem
|
|
2769
|
+
* (Cloudflare Workers, Deno Deploy, other edge runtimes) — the campaign
|
|
2770
|
+
* still produces its `CampaignResult` (cells + aggregates) in memory;
|
|
2771
|
+
* artifacts/traces simply aren't persisted to disk.
|
|
2772
|
+
*
|
|
2773
|
+
* Paths are opaque keys to the in-memory adapter — it does not parse them,
|
|
2774
|
+
* so the same `join(...)`-built paths work unchanged across both adapters.
|
|
2775
|
+
*/
|
|
2776
|
+
interface CampaignStorage {
|
|
2777
|
+
/** Ensure a directory exists (recursive). No-op for in-memory. */
|
|
2778
|
+
ensureDir(dir: string): void;
|
|
2779
|
+
/** Does this path exist (as a written file or an ensured dir)? */
|
|
2780
|
+
exists(path: string): boolean;
|
|
2781
|
+
/** Read a UTF-8 file; `undefined` when missing or unreadable. */
|
|
2782
|
+
read(path: string): string | undefined;
|
|
2783
|
+
/** Write a file (string or bytes). Parent dir is assumed ensured. */
|
|
2784
|
+
write(path: string, content: string | Uint8Array): void;
|
|
2785
|
+
/** Append only when the current UTF-8 byte length matches `expectedBytes`.
|
|
2786
|
+
* Returns the new length, or undefined when another writer won. */
|
|
2787
|
+
append?(path: string, content: string, expectedBytes: number): number | undefined;
|
|
2788
|
+
}
|
|
2789
|
+
/** Node-filesystem storage — the default. Lazily requires `node:fs` so the
|
|
2790
|
+
* module imports cleanly in non-Node runtimes (where the caller passes
|
|
2791
|
+
* `inMemoryCampaignStorage` instead and never constructs this).
|
|
2792
|
+
*
|
|
2793
|
+
* `createRequire(import.meta.url)` is the ESM-native lazy require — a bare
|
|
2794
|
+
* `require` is a ReferenceError under `"type": "module"`, which is exactly
|
|
2795
|
+
* the shape this package publishes. */
|
|
2796
|
+
declare function fsCampaignStorage(): CampaignStorage;
|
|
2797
|
+
/** In-memory storage for filesystem-less runtimes. Artifacts + trace spans
|
|
2798
|
+
* live in a `Map` for the duration of the run; the `CampaignResult` is
|
|
2799
|
+
* fully populated, but nothing is persisted to disk. */
|
|
2800
|
+
declare function inMemoryCampaignStorage(): CampaignStorage;
|
|
2801
|
+
/** Open the durable spend account stored beside a logical run. */
|
|
2802
|
+
declare function createRunCostLedger(input: {
|
|
2803
|
+
storage: CampaignStorage;
|
|
2804
|
+
runDir: string;
|
|
2805
|
+
costCeilingUsd?: number;
|
|
2806
|
+
}): CostLedger;
|
|
2807
|
+
|
|
2808
|
+
/**
|
|
2809
|
+
* `runCampaign` — Pass A substrate primitive. ONE function that orchestrates
|
|
2810
|
+
* scenarios → dispatch → artifacts → judges → aggregates, with full
|
|
2811
|
+
* reproducibility (seed + manifest hash), cell-level resumability, bootstrap
|
|
2812
|
+
* CIs, and the `LabeledScenarioStore` capture flywheel.
|
|
2813
|
+
*
|
|
2814
|
+
* Improvement loops (optimizer / gate / autoOnPromote) ride on top of this
|
|
2815
|
+
* primitive but live in `presets/run-improvement-loop.ts`. This file keeps
|
|
2816
|
+
* the core orchestrator minimal — Phase 1 of the Pass A track.
|
|
2817
|
+
*/
|
|
2818
|
+
|
|
2819
|
+
interface RunCampaignOptions<TScenario extends Scenario, TArtifact> {
|
|
2820
|
+
scenarios: TScenario[];
|
|
2821
|
+
dispatch: DispatchFn<TScenario, TArtifact>;
|
|
2822
|
+
/**
|
|
2823
|
+
* Stable identity for the dispatch behavior, included in the manifest/cache
|
|
2824
|
+
* key. Set this when the same function name can run different models,
|
|
2825
|
+
* prompts, tools, or external config.
|
|
2826
|
+
*/
|
|
2827
|
+
dispatchRef?: string;
|
|
2828
|
+
judges?: JudgeConfig<TArtifact, TScenario>[];
|
|
2829
|
+
/** Required for reproducibility. Default 42. */
|
|
2830
|
+
seed?: number;
|
|
2831
|
+
/** Per-scenario replicates for CI bands. Default 1; raise to 5+ for
|
|
2832
|
+
* bootstrap-tight intervals on critical eval. */
|
|
2833
|
+
reps?: number;
|
|
2834
|
+
/** When true (default), completed cells are cached by
|
|
2835
|
+
* (manifestHash, scenarioId, rep, generation). Re-runs skip cached cells. */
|
|
2836
|
+
resumable?: boolean;
|
|
2837
|
+
/** Optional store — when present, every artifact + judge score is captured
|
|
2838
|
+
* with the configured `captureSource`. Capture is default ON; pass `'off'`
|
|
2839
|
+
* to disable. */
|
|
2840
|
+
labeledStore?: LabeledScenarioStore | 'off';
|
|
2841
|
+
captureSource?: 'production-trace' | 'eval-run' | 'manual' | 'red-team' | 'synthetic';
|
|
2842
|
+
captureSourceVersionHash?: string;
|
|
2843
|
+
/** Hard spend cap. Each paid call reserves its enforced maximum before dispatch. */
|
|
2844
|
+
costCeiling?: number;
|
|
2845
|
+
/** Shared spend account. Improvement loops pass one ledger through every
|
|
2846
|
+
* campaign so the ceiling and returned total are run-wide. */
|
|
2847
|
+
costLedger?: CostLedger;
|
|
2848
|
+
/** Attribution label for receipts recorded by this campaign. */
|
|
2849
|
+
costPhase?: string;
|
|
2850
|
+
/** Max concurrent cells. Default 2. */
|
|
2851
|
+
maxConcurrency?: number;
|
|
2852
|
+
/**
|
|
2853
|
+
* Per-cell dispatch deadline in ms. A `dispatch` that neither resolves nor
|
|
2854
|
+
* rejects within this window is a hang (a stalled model request, an
|
|
2855
|
+
* exhausted runtime resource, a backend that never closes its stream). When
|
|
2856
|
+
* set, the cell's `ctx.signal` is aborted and the cell is recorded as a LOUD
|
|
2857
|
+
* error (`dispatch exceeded <N>ms`) so the campaign proceeds and the failure
|
|
2858
|
+
* is visible — instead of one wedged cell silently hanging the whole run (and
|
|
2859
|
+
* every loop/CI job above it) forever. `undefined`/`0` = unbounded (legacy).
|
|
2860
|
+
*/
|
|
2861
|
+
dispatchTimeoutMs?: number;
|
|
2862
|
+
/** Required: where artifacts + traces land. A bare name (not an absolute path)
|
|
2863
|
+
* resolves to the shared `~/.tangle/traces/<repo>/runs/<name>` root so run
|
|
2864
|
+
* bundles never pollute a repo working tree. Pass an absolute path to override. */
|
|
2865
|
+
runDir: string;
|
|
2866
|
+
/** Subject repo for the shared run-dir root (defaults to the CWD basename).
|
|
2867
|
+
* Only consulted when `runDir` is a bare name. */
|
|
2868
|
+
repo?: string;
|
|
2869
|
+
/** Tracing posture. Default is the substrate's `FileSystemTraceStore` rooted
|
|
2870
|
+
* at `<runDir>/traces/`. `'off'` disables capture entirely — substrate
|
|
2871
|
+
* refuses this when the caller wires `autoOnPromote !== 'none'`. */
|
|
2872
|
+
tracing?: 'on' | 'off';
|
|
2873
|
+
/**
|
|
2874
|
+
* Per-cell usage expectation — the early, fine-grained sibling of the
|
|
2875
|
+
* batch `assertRealBackend` guard. A cell that produced an artifact (no
|
|
2876
|
+
* error) but reported `costUsd === 0` AND zero tokens is a stub: the
|
|
2877
|
+
* dispatch never reported LLM activity via `ctx.cost`. Modes:
|
|
2878
|
+
* - `'warn'` (default) — log the offending cell loudly, keep going.
|
|
2879
|
+
* - `'assert'` — throw `BackendIntegrityError` on the first such cell
|
|
2880
|
+
* (fail-fast; recommended for CI campaigns expecting real LLM calls).
|
|
2881
|
+
* - `'off'` — no check (replay / deterministic-only / offline analysis).
|
|
2882
|
+
*/
|
|
2883
|
+
expectUsage?: 'assert' | 'warn' | 'off';
|
|
2884
|
+
/** Test seam — override the wall clock for deterministic tests. */
|
|
2885
|
+
now?: () => Date;
|
|
2886
|
+
/** Test seam — override per-cell trace writer factory. */
|
|
2887
|
+
buildTraceWriter?: (cellId: string, dir: string) => CampaignTraceWriter;
|
|
2888
|
+
/** Storage backend for run/cell dirs, the resumability cache, artifacts,
|
|
2889
|
+
* and trace spans. Default: the Node filesystem (`fsCampaignStorage`).
|
|
2890
|
+
* Pass `inMemoryCampaignStorage()` to run in a filesystem-less runtime
|
|
2891
|
+
* (Cloudflare Workers, Deno, edge) — the `CampaignResult` is still
|
|
2892
|
+
* produced; artifacts/traces just aren't persisted to disk. */
|
|
2893
|
+
storage?: CampaignStorage;
|
|
2894
|
+
/**
|
|
2895
|
+
* Optional per-cell placement strategy. Returns an opaque string the
|
|
2896
|
+
* substrate forwards as `ctx.placement` to the Dispatch — placement-aware
|
|
2897
|
+
* Dispatches (e.g. `httpDispatch` from `/adapters/http`) use it to route
|
|
2898
|
+
* each cell to the right worker, region, or sandbox. When unset, every
|
|
2899
|
+
* cell receives `ctx.placement = undefined` and behaves identically to
|
|
2900
|
+
* the in-process case.
|
|
2901
|
+
*
|
|
2902
|
+
* @example
|
|
2903
|
+
* cellPlacement: ({ scenario }) => scenario.tags?.includes('eu') ? 'eu-west' : 'us-east'
|
|
2904
|
+
*/
|
|
2905
|
+
cellPlacement?: (input: {
|
|
2906
|
+
scenario: TScenario;
|
|
2907
|
+
rep: number;
|
|
2908
|
+
generation?: number;
|
|
2909
|
+
}) => string | undefined;
|
|
2910
|
+
}
|
|
640
2911
|
/**
|
|
641
|
-
*
|
|
642
|
-
* proposed agent surfaces. Unlike factorial cell-mean attribution, every
|
|
643
|
-
* benefit, regression, and interaction remains attached to its task row.
|
|
2912
|
+
* Core campaign orchestrator: fan scenarios through dispatch, score with judges, aggregate bootstrap CIs, and persist reproducible `CampaignResult` records.
|
|
644
2913
|
*/
|
|
645
|
-
|
|
2914
|
+
declare function runCampaign<TScenario extends Scenario, TArtifact>(opts: RunCampaignOptions<TScenario, TArtifact>): Promise<CampaignResult<TArtifact, TScenario>>;
|
|
2915
|
+
interface CampaignRunPlanCell {
|
|
2916
|
+
cellId: string;
|
|
2917
|
+
scenarioId: string;
|
|
2918
|
+
rep: number;
|
|
2919
|
+
seed: number;
|
|
2920
|
+
cachePath: string;
|
|
2921
|
+
status: 'cached' | 'run';
|
|
2922
|
+
reason?: 'missing' | 'manifest-mismatch' | 'cell-mismatch' | 'corrupt' | 'resumable-off';
|
|
2923
|
+
}
|
|
2924
|
+
interface CampaignRunPlan {
|
|
2925
|
+
manifestHash: string;
|
|
2926
|
+
totalCells: number;
|
|
2927
|
+
cellsCached: number;
|
|
2928
|
+
cellsToRun: number;
|
|
2929
|
+
cells: CampaignRunPlanCell[];
|
|
2930
|
+
}
|
|
2931
|
+
interface PlanCampaignRunOptions<TScenario extends Scenario, TArtifact> {
|
|
2932
|
+
scenarios: TScenario[];
|
|
2933
|
+
dispatch?: DispatchFn<TScenario, TArtifact>;
|
|
2934
|
+
dispatchRef?: string;
|
|
2935
|
+
judges?: JudgeConfig<TArtifact, TScenario>[];
|
|
2936
|
+
seed?: number;
|
|
2937
|
+
reps?: number;
|
|
2938
|
+
resumable?: boolean;
|
|
2939
|
+
runDir: string;
|
|
2940
|
+
/** Subject repo for the shared run-dir root (see RunCampaignOptions.repo). */
|
|
2941
|
+
repo?: string;
|
|
2942
|
+
storage?: CampaignStorage;
|
|
2943
|
+
}
|
|
646
2944
|
/**
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
* neither insertion order nor an after-the-fact tie-break can change a result.
|
|
2945
|
+
* Plan a campaign WITHOUT dispatching: computes the manifest hash and the per-cell
|
|
2946
|
+
* run-vs-cached schedule so callers can preview cost and resumability before spending.
|
|
650
2947
|
*/
|
|
651
|
-
declare function
|
|
2948
|
+
declare function planCampaignRun<TScenario extends Scenario, TArtifact>(opts: PlanCampaignRunOptions<TScenario, TArtifact>): CampaignRunPlan;
|
|
652
2949
|
|
|
653
2950
|
type EvalFixtureValidationMode = 'vitest' | 'none';
|
|
654
2951
|
interface EvalFixtureFile {
|
|
@@ -710,6 +3007,185 @@ declare function loadEvalFixtureScenarios(evalsDir: string, options?: LoadEvalFi
|
|
|
710
3007
|
*/
|
|
711
3008
|
declare function planEvalFixtureRun<TArtifact = unknown>(options: PlanEvalFixtureRunOptions<TArtifact>): EvalFixtureRunPlan;
|
|
712
3009
|
|
|
3010
|
+
/**
|
|
3011
|
+
* Compose multiple `Gate` implementations — every gate must pass for the
|
|
3012
|
+
* composite to ship. Closes the alignment reviewer's "default-only
|
|
3013
|
+
* heldOutGate + costGate would happily promote a reward-hacked prompt"
|
|
3014
|
+
* concern by making safety gates first-class composable defaults.
|
|
3015
|
+
*/
|
|
3016
|
+
|
|
3017
|
+
/** Compose gates — all must `ship` for the composite to `ship`. First
|
|
3018
|
+
* non-ship verdict short-circuits the composite verdict, but ALL gates run
|
|
3019
|
+
* (so the result records every gate's reason — useful for diagnostics). */
|
|
3020
|
+
declare function composeGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(...gates: Array<Gate<TArtifact, TScenario>>): Gate<TArtifact, TScenario>;
|
|
3021
|
+
|
|
3022
|
+
/**
|
|
3023
|
+
* Dataset — versioned, sliceable, content-hashed scenario collection.
|
|
3024
|
+
*
|
|
3025
|
+
* Scenarios stop being ephemeral arrays and become first-class
|
|
3026
|
+
* artifacts. Every Dataset carries:
|
|
3027
|
+
* - content hash (sha256 over canonicalized scenario array)
|
|
3028
|
+
* - provenance (contributor, createdAt, sourceUrl)
|
|
3029
|
+
* - split labels (train | dev | test | holdout)
|
|
3030
|
+
* - difficulty tiers (easy | medium | hard | extreme)
|
|
3031
|
+
* - tags (free-form, per-scenario)
|
|
3032
|
+
*
|
|
3033
|
+
* `Dataset.slice({ difficulty, split, holdout, seed })` returns a
|
|
3034
|
+
* deterministic, reproducible subset. Holdout slices are locked: you
|
|
3035
|
+
* can read them but `mutate` throws, which prevents "oh I'll just
|
|
3036
|
+
* tweak that one scenario" contamination drift.
|
|
3037
|
+
*/
|
|
3038
|
+
type DatasetSplit = 'train' | 'dev' | 'test' | 'holdout';
|
|
3039
|
+
type DatasetDifficulty = 'easy' | 'medium' | 'hard' | 'extreme';
|
|
3040
|
+
interface DatasetScenario {
|
|
3041
|
+
id: string;
|
|
3042
|
+
/** Arbitrary payload; the framework doesn't interpret it. */
|
|
3043
|
+
payload: unknown;
|
|
3044
|
+
split?: DatasetSplit;
|
|
3045
|
+
difficulty?: DatasetDifficulty;
|
|
3046
|
+
/** Canary token that MUST NOT round-trip through a correct agent output. */
|
|
3047
|
+
canary?: string;
|
|
3048
|
+
/**
|
|
3049
|
+
* Behavioral-canary forbidden pattern. A string OR a serialized regex
|
|
3050
|
+
* (`/.../flags`) that the agent under test MUST NOT emit. Used by
|
|
3051
|
+
* {@link import('./canary').checkBehavioralCanary | checkBehavioralCanary},
|
|
3052
|
+
* which inverts the contamination-style semantic: presence in the
|
|
3053
|
+
* agent output is a LEAK / failure, not a positive signal.
|
|
3054
|
+
*
|
|
3055
|
+
* Falls back to {@link canary} when omitted.
|
|
3056
|
+
*/
|
|
3057
|
+
forbiddenPattern?: string;
|
|
3058
|
+
tags?: Record<string, string>;
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
/**
|
|
3062
|
+
* Red-team battery — adversarial scenario corpus with per-category
|
|
3063
|
+
* scorers.
|
|
3064
|
+
*
|
|
3065
|
+
* Every scenario is a `DatasetScenario` with a `redTeam: {...}` payload
|
|
3066
|
+
* that declares: category, expected refusal, attack surface. Scorers
|
|
3067
|
+
* inspect the agent's output + trajectory and return a PASS/FAIL per
|
|
3068
|
+
* category. Aggregated via `redTeamReport`.
|
|
3069
|
+
*
|
|
3070
|
+
* The built-in corpus is intentionally conservative — 40 cases
|
|
3071
|
+
* covering the most-seen attack patterns. Extend via `extendCorpus`.
|
|
3072
|
+
*/
|
|
3073
|
+
|
|
3074
|
+
type RedTeamCategory = 'prompt_injection_direct' | 'prompt_injection_indirect' | 'jailbreak_persona' | 'jailbreak_dan' | 'pii_leak' | 'permission_escalation' | 'data_exfil' | 'policy_override';
|
|
3075
|
+
interface RedTeamPayload {
|
|
3076
|
+
category: RedTeamCategory;
|
|
3077
|
+
/** The hostile input the agent sees. */
|
|
3078
|
+
input: string;
|
|
3079
|
+
/**
|
|
3080
|
+
* What a safe agent should do: "refuse" means explicit refusal,
|
|
3081
|
+
* "ignore" means process benignly without executing the attack.
|
|
3082
|
+
*/
|
|
3083
|
+
expected: 'refuse' | 'ignore';
|
|
3084
|
+
/** Substring(s) that MUST NOT appear in output. */
|
|
3085
|
+
forbiddenStrings?: string[];
|
|
3086
|
+
/** Tool names the agent MUST NOT call for this scenario. */
|
|
3087
|
+
forbiddenTools?: string[];
|
|
3088
|
+
}
|
|
3089
|
+
interface RedTeamCase extends DatasetScenario {
|
|
3090
|
+
payload: RedTeamPayload;
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
/**
|
|
3094
|
+
* `defaultProductionGate` — composes the substrate's existing safety
|
|
3095
|
+
* primitives (red-team / reward-hacking / canary / heldout) into a single
|
|
3096
|
+
* Gate.decide shape. Closes the alignment + Anthropic-SI reviewers' "safety
|
|
3097
|
+
* primitives are off the critical path" blocker.
|
|
3098
|
+
*
|
|
3099
|
+
* The composition is opinionated — when consumers wire `runImprovementLoop`,
|
|
3100
|
+
* THIS gate is the default. Consumers can still pass a custom gate to
|
|
3101
|
+
* override; the recommended pattern is to compose THIS gate with whatever
|
|
3102
|
+
* extra domain-specific gates they need (`composeGate(defaultProductionGate(...), customGate)`).
|
|
3103
|
+
*/
|
|
3104
|
+
|
|
3105
|
+
interface DefaultProductionGateOptions {
|
|
3106
|
+
/** Required: scenarios held out from training; substrate compares
|
|
3107
|
+
* candidate-on-holdout vs baseline-on-holdout. */
|
|
3108
|
+
holdoutScenarios: Scenario[];
|
|
3109
|
+
/** Minimum held-out lift the **paired-bootstrap CI lower bound** must clear
|
|
3110
|
+
* to ship — NOT a point estimate. Default 0 ⇒ "confidently positive at the
|
|
3111
|
+
* confidence level". Interpreted in the judge's native composite scale (set
|
|
3112
|
+
* e.g. 2 for a 0-100 rubric to require a ≥2-point significant gain). */
|
|
3113
|
+
deltaThreshold?: number;
|
|
3114
|
+
/** Confidence level for the held-out + dimension bootstraps. Default 0.95. */
|
|
3115
|
+
confidence?: number;
|
|
3116
|
+
/** Bootstrap resamples. Default 2000. */
|
|
3117
|
+
bootstrapResamples?: number;
|
|
3118
|
+
/** Fixed bootstrap seed for a deterministic verdict. Default 1337. */
|
|
3119
|
+
bootstrapSeed?: number;
|
|
3120
|
+
/** Minimum paired holdout observations (scenarios × reps) before a
|
|
3121
|
+
* significance claim is allowed; below it the gate HOLDS with `few_runs`
|
|
3122
|
+
* rather than reading a degenerate CI. Default 3. */
|
|
3123
|
+
minProductiveRuns?: number;
|
|
3124
|
+
/** Ship statistic for the held-out significance test. Default `'mean'`
|
|
3125
|
+
* (tie-robust — see `heldoutSignificance`). Pass `'median'` for
|
|
3126
|
+
* outlier-robustness at the cost of tie-blindness. */
|
|
3127
|
+
heldoutStatistic?: 'mean' | 'median';
|
|
3128
|
+
/** Critical judge dimensions that must NOT significantly regress even when
|
|
3129
|
+
* the net composite rises (anti-Goodhart). The gate HOLDS if any listed
|
|
3130
|
+
* dimension's paired-delta CI lower bound < −`regressionTolerance`. E.g.
|
|
3131
|
+
* `['hallucination_free']` for a legal agent. */
|
|
3132
|
+
criticalDimensions?: string[];
|
|
3133
|
+
/** Tolerance for the per-dimension regression guard, in the dimension's
|
|
3134
|
+
* native scale. When omitted it auto-scales off observed magnitudes:
|
|
3135
|
+
* 0.05 on [0,1], 5 on 0-100. */
|
|
3136
|
+
regressionTolerance?: number;
|
|
3137
|
+
/** Total $ budget for ALL cells in this campaign — including baseline + candidate.
|
|
3138
|
+
* Composite verdict refuses to ship when spend exceeded budget. */
|
|
3139
|
+
budgetUsd?: number;
|
|
3140
|
+
/** Red-team cases to probe candidate outputs against. When omitted the
|
|
3141
|
+
* substrate uses `DEFAULT_RED_TEAM_CORPUS`. Provide a domain-specific
|
|
3142
|
+
* battery for tighter coverage. */
|
|
3143
|
+
redTeamBattery?: RedTeamCase[];
|
|
3144
|
+
/** Run records (oldest-first) needed for the reward-hacking detector.
|
|
3145
|
+
* Substrate populates from prior production-loop generations. */
|
|
3146
|
+
recentRuns?: RunRecord[];
|
|
3147
|
+
/** When true, the gate refuses to ship if the reward-hacking detector
|
|
3148
|
+
* fires at the `gaming` severity. Default true. */
|
|
3149
|
+
blockOnRewardHackingGaming?: boolean;
|
|
3150
|
+
}
|
|
3151
|
+
/**
|
|
3152
|
+
* Opinionated production gate composing held-out significance, red-team, reward-hacking, and canary checks into a single `Gate.decide` decision.
|
|
3153
|
+
*/
|
|
3154
|
+
declare function defaultProductionGate<TArtifact, TScenario extends Scenario>(options: DefaultProductionGateOptions): Gate<TArtifact, TScenario>;
|
|
3155
|
+
|
|
3156
|
+
/**
|
|
3157
|
+
* @module
|
|
3158
|
+
* Composable held-out promotion gate backed by paired bootstrap confidence.
|
|
3159
|
+
*
|
|
3160
|
+
* Pair by full `scenario:rep` cellId, bootstrap the paired candidate-minus-
|
|
3161
|
+
* baseline delta, and ship only when CI.low strictly clears the threshold with
|
|
3162
|
+
* at least `minProductiveRuns` paired observations.
|
|
3163
|
+
*
|
|
3164
|
+
* Use when you want held-out significance as ONE of N composed gates instead
|
|
3165
|
+
* of the full `defaultProductionGate` stack (which adds critical-dimension
|
|
3166
|
+
* regression + reward-hacking guards on top).
|
|
3167
|
+
*/
|
|
3168
|
+
|
|
3169
|
+
interface HeldOutGateOptions<TScenario extends Scenario = Scenario> {
|
|
3170
|
+
scenarios: TScenario[];
|
|
3171
|
+
/** Effect-size threshold the CI lower bound must clear, in the judge's native
|
|
3172
|
+
* scale. Default 0.5. Equality holds; CI.low must be greater than this value. */
|
|
3173
|
+
deltaThreshold?: number;
|
|
3174
|
+
/** Bootstrap CI confidence. Default 0.95. */
|
|
3175
|
+
confidence?: number;
|
|
3176
|
+
/** Minimum paired holdout observations to claim significance. Default 3. */
|
|
3177
|
+
minProductiveRuns?: number;
|
|
3178
|
+
/** Bootstrap resamples. Default 2000. */
|
|
3179
|
+
resamples?: number;
|
|
3180
|
+
/** Fixed bootstrap seed for deterministic verdicts. Default 1337. */
|
|
3181
|
+
bootstrapSeed?: number;
|
|
3182
|
+
}
|
|
3183
|
+
/**
|
|
3184
|
+
* Composable held-out gate: ships only when the PAIRED bootstrap CI lower bound
|
|
3185
|
+
* of the candidate-minus-baseline composite delta clears `deltaThreshold`.
|
|
3186
|
+
*/
|
|
3187
|
+
declare function heldOutGate<TArtifact, TScenario extends Scenario>(options: HeldOutGateOptions<TScenario>): Gate<TArtifact, TScenario>;
|
|
3188
|
+
|
|
713
3189
|
/**
|
|
714
3190
|
* @module
|
|
715
3191
|
* Composable placebo / neutralization promotion gate.
|
|
@@ -747,6 +3223,274 @@ interface NeutralizationGateOptions<TScenario extends Scenario = Scenario> {
|
|
|
747
3223
|
*/
|
|
748
3224
|
declare function neutralizationGate<TArtifact, TScenario extends Scenario>(options: NeutralizationGateOptions<TScenario>): Gate<TArtifact, TScenario>;
|
|
749
3225
|
|
|
3226
|
+
/**
|
|
3227
|
+
* Power preflight — "can this budget detect the effect you are hunting?"
|
|
3228
|
+
*
|
|
3229
|
+
* The failure it prevents (measured, twice): a live prompt-improvement campaign ran
|
|
3230
|
+
* 333 sandbox cells over 5.6 hours and produced a +0.08 holdout lift the ship gate
|
|
3231
|
+
* (paired bootstrap, CI.low > 0.05) could not distinguish from zero — because at
|
|
3232
|
+
* that holdout size and worker variance the MINIMUM DETECTABLE lift was larger than
|
|
3233
|
+
* any effect a prompt change plausibly produces. The budget was spent learning what
|
|
3234
|
+
* a 30-second calculation on the baseline cells already knew. No eval framework we
|
|
3235
|
+
* know of surfaces this; every underpowered improvement run everywhere ends in an
|
|
3236
|
+
* uninformative "hold".
|
|
3237
|
+
*
|
|
3238
|
+
* Model: the ship rule is `CI.low(paired Δ) > deltaThreshold`. Approximating the
|
|
3239
|
+
* bootstrap CI as normal, `CI.low ≈ effect − z·sd_Δ/√n`, so the smallest shippable
|
|
3240
|
+
* true effect is `MDE = deltaThreshold + z·sd_Δ/√n`. The paired-delta SD is unknown
|
|
3241
|
+
* before the candidate exists; we bound it by the zero-correlation case
|
|
3242
|
+
* `sd_Δ ≤ √2·sd_baseline` — a CONSERVATIVE (upper) MDE, which is the correct
|
|
3243
|
+
* direction for a warning. Pairing is per cell (`scenario:rep`), so reps multiply n.
|
|
3244
|
+
*
|
|
3245
|
+
* Standalone by design: feed it any baseline composites (a `gate:'none'` run, a
|
|
3246
|
+
* live-proof table) BEFORE budgeting the real search; `selfImprove` also attaches
|
|
3247
|
+
* it to every result and warns when the run was structurally unable to ship.
|
|
3248
|
+
*/
|
|
3249
|
+
interface PowerPreflightOptions {
|
|
3250
|
+
/** Per-cell baseline composites on the HOLDOUT scenarios (one per scenario:rep cell). */
|
|
3251
|
+
baselineComposites: number[];
|
|
3252
|
+
/** Paired observations the budgeted comparison will produce
|
|
3253
|
+
* (holdout scenarios × reps). Defaults to `baselineComposites.length`. */
|
|
3254
|
+
pairedN?: number;
|
|
3255
|
+
/** The ship gate's effect-size threshold. Default 0.05 (defaultProductionGate). */
|
|
3256
|
+
deltaThreshold?: number;
|
|
3257
|
+
/** CI confidence the gate uses. Default 0.95. */
|
|
3258
|
+
confidence?: number;
|
|
3259
|
+
/** True when the holdout is scored by the SAME judge/scorer family as the gate
|
|
3260
|
+
* (selfImprove's default composition — one judge scores everything). Under a
|
|
3261
|
+
* shared channel, raising paired n reduces only the IDIOSYNCRATIC noise share;
|
|
3262
|
+
* systematic judge bias is untouched, so the MDE here is a lower bound and the
|
|
3263
|
+
* only full debiaser is an independent second scoring channel
|
|
3264
|
+
* (recursive-self-improvement S1c, closed form in EXP-023 P0). Default false. */
|
|
3265
|
+
sharedScorerChannel?: boolean;
|
|
3266
|
+
}
|
|
3267
|
+
interface PowerPreflight {
|
|
3268
|
+
/** Paired observations the comparison will have. */
|
|
3269
|
+
n: number;
|
|
3270
|
+
/** Baseline per-cell composite standard deviation (the variance the effect must beat). */
|
|
3271
|
+
sd: number;
|
|
3272
|
+
/** Minimum detectable lift: the smallest TRUE effect the gate could ship at this budget. */
|
|
3273
|
+
mde: number;
|
|
3274
|
+
/** Baseline holdout composite mean. */
|
|
3275
|
+
baselineMean: number;
|
|
3276
|
+
/** Headroom to a perfect 1.0 composite (the largest achievable lift on a [0,1] judge). */
|
|
3277
|
+
headroom: number;
|
|
3278
|
+
/** True when even the largest achievable effect (headroom) is below the MDE —
|
|
3279
|
+
* the run is structurally unable to ship regardless of proposal quality.
|
|
3280
|
+
* Only asserted for [0,1]-scaled judges (see `scaleAssumed`). */
|
|
3281
|
+
underpowered: boolean;
|
|
3282
|
+
/** True when composites look [0,1]-scaled; headroom/underpowered are only
|
|
3283
|
+
* meaningful under that convention (0-100 judges get mde/sd/n but no verdict). */
|
|
3284
|
+
scaleAssumed: boolean;
|
|
3285
|
+
deltaThreshold: number;
|
|
3286
|
+
confidence: number;
|
|
3287
|
+
/** Set when the holdout shares the gate's scoring channel: more cells cannot
|
|
3288
|
+
* buy back systematic judge bias — treat the MDE as a lower bound. */
|
|
3289
|
+
sharedChannelCaveat?: string;
|
|
3290
|
+
/** One actionable sentence for humans and logs. */
|
|
3291
|
+
recommendation: string;
|
|
3292
|
+
}
|
|
3293
|
+
/** Estimate the minimum detectable lift a paired-holdout improvement run can
|
|
3294
|
+
* ship at a given budget, from the baseline holdout composites — call it BEFORE
|
|
3295
|
+
* spending a search to learn whether the effect you are hunting is even
|
|
3296
|
+
* observable at this holdout size and worker variance. */
|
|
3297
|
+
declare function powerPreflight(opts: PowerPreflightOptions): PowerPreflight;
|
|
3298
|
+
|
|
3299
|
+
/**
|
|
3300
|
+
* Pareto frontier — multi-objective optimization over candidate runs.
|
|
3301
|
+
*
|
|
3302
|
+
* Lifted from ADC pareto.ts and blueprint-agent frontier.ts. When you're
|
|
3303
|
+
* trading off (cost, latency, quality) or (passRate, tokenBudget,
|
|
3304
|
+
* ttfb), you rarely have a single "winner" — you have a set of
|
|
3305
|
+
* non-dominated candidates. This module exposes:
|
|
3306
|
+
*
|
|
3307
|
+
* - `paretoFrontier`: filter a set of candidates to the non-dominated ones
|
|
3308
|
+
* - `dominates`: does A dominate B across all objectives?
|
|
3309
|
+
*
|
|
3310
|
+
* Each objective is declared with a direction: 'maximize' (higher=better)
|
|
3311
|
+
* or 'minimize' (lower=better). Candidates are any object; pass an
|
|
3312
|
+
* `objective(candidate)` accessor.
|
|
3313
|
+
*/
|
|
3314
|
+
type Direction = 'maximize' | 'minimize';
|
|
3315
|
+
|
|
3316
|
+
/**
|
|
3317
|
+
* Promotion policy over the evidence VECTOR — the substrate's answer to "never
|
|
3318
|
+
* collapse the multi-objective promotion decision into one scalar." A
|
|
3319
|
+
* `defaultProductionGate` is one opinionated composition; this module factors
|
|
3320
|
+
* the decision into two reusable pieces so MANY policies can compete over the
|
|
3321
|
+
* SAME evidence (the quant-desk pattern: one evidence bus, plural strategies):
|
|
3322
|
+
*
|
|
3323
|
+
* buildEvidenceVector(ctx, objectives, opts) -> EvidenceVector // the bus
|
|
3324
|
+
* PromotionPolicy = (ev: EvidenceVector) => GateResult // a strategy
|
|
3325
|
+
* paretoPolicy(ev) // the default strategy
|
|
3326
|
+
* paretoSignificanceGate(options): Gate // bus + policy as a Gate
|
|
3327
|
+
*
|
|
3328
|
+
* The Pareto policy is SYMMETRIC multi-objective: every objective is BOTH a
|
|
3329
|
+
* potential gain source AND a safety floor (unlike `defaultProductionGate`,
|
|
3330
|
+
* where only `composite` can win and `criticalDimensions` are pure floors). A
|
|
3331
|
+
* candidate ships iff it weakly DOMINATES the baseline at the confidence level —
|
|
3332
|
+
* no objective credibly worse (CI floor breach) AND at least one objective
|
|
3333
|
+
* credibly better (CI gain). Insufficient evidence on ANY axis -> need_more_work
|
|
3334
|
+
* (NOT folded into hold: "gather more reps" and "reject" are different actions).
|
|
3335
|
+
*
|
|
3336
|
+
* Cost/latency are NOT CI axes here — `GateContext` carries only an aggregate
|
|
3337
|
+
* per-side cost, no per-cell observation vector to bootstrap. Treat them as hard
|
|
3338
|
+
* constraints (compose with a budget gate via `composeGate`), not faked CIs.
|
|
3339
|
+
*/
|
|
3340
|
+
|
|
3341
|
+
/** Where an objective's per-cell scalar comes from. `composite` reads the
|
|
3342
|
+
* judge's composite; `dimension` reads a named per-dimension score. */
|
|
3343
|
+
type ObjectiveSource = {
|
|
3344
|
+
kind: 'composite';
|
|
3345
|
+
} | {
|
|
3346
|
+
kind: 'dimension';
|
|
3347
|
+
dimension: string;
|
|
3348
|
+
};
|
|
3349
|
+
interface PromotionObjective {
|
|
3350
|
+
/** Stable label used in reports + `contributingGates`. */
|
|
3351
|
+
name: string;
|
|
3352
|
+
source: ObjectiveSource;
|
|
3353
|
+
/** 'maximize' (quality dims) or 'minimize' (error/risk/length dims). Orients
|
|
3354
|
+
* the paired delta so a positive bootstrap always means "candidate better". */
|
|
3355
|
+
direction: Direction;
|
|
3356
|
+
/** The good-direction paired-delta CI lower bound must EXCEED this to count
|
|
3357
|
+
* as a significant gain on this axis. Interpreted in the judge's native
|
|
3358
|
+
* scale. Default 0 (⇒ "confidently better"). */
|
|
3359
|
+
gainThreshold?: number;
|
|
3360
|
+
/** A floor breach (regression) is declared when the good-direction CI lower
|
|
3361
|
+
* bound is below −floorTolerance. When omitted it auto-scales off observed
|
|
3362
|
+
* magnitudes (0.05 on [0,1], 5 on 0-100), matching `dimensionRegressions`. */
|
|
3363
|
+
floorTolerance?: number;
|
|
3364
|
+
}
|
|
3365
|
+
/** Per-axis verdict from the good-direction paired bootstrap. */
|
|
3366
|
+
type AxisVerdict = 'improved' | 'regressed' | 'flat' | 'few_runs';
|
|
3367
|
+
interface AxisEvidence {
|
|
3368
|
+
name: string;
|
|
3369
|
+
source: ObjectiveSource;
|
|
3370
|
+
direction: Direction;
|
|
3371
|
+
/** Paired bootstrap on the GOOD-DIRECTION delta (oriented by `direction`):
|
|
3372
|
+
* a positive value means the candidate is better on this axis. */
|
|
3373
|
+
bootstrap: PairedBootstrapResult;
|
|
3374
|
+
/** Paired observations contributing to this axis. */
|
|
3375
|
+
n: number;
|
|
3376
|
+
gainThreshold: number;
|
|
3377
|
+
floorTolerance: number;
|
|
3378
|
+
verdict: AxisVerdict;
|
|
3379
|
+
}
|
|
3380
|
+
interface EvidenceVector {
|
|
3381
|
+
/** One entry per objective — NOTHING averaged across axes. */
|
|
3382
|
+
axes: AxisEvidence[];
|
|
3383
|
+
/** Smallest paired n across axes that produced observations — the binding
|
|
3384
|
+
* evidence-sufficiency constraint. 0 when no axis produced observations. */
|
|
3385
|
+
minN: number;
|
|
3386
|
+
/** Aggregate per-side cost from the gate context (a constraint input, not a
|
|
3387
|
+
* CI axis — see the module header). */
|
|
3388
|
+
cost: {
|
|
3389
|
+
candidate: number;
|
|
3390
|
+
baseline: number;
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3393
|
+
/** A promotion strategy: a pure function from the evidence vector to a verdict.
|
|
3394
|
+
* Many policies can run over the same `EvidenceVector` and disagree — that's
|
|
3395
|
+
* the point (competing strategies, shared evidence). */
|
|
3396
|
+
type PromotionPolicy = (ev: EvidenceVector) => GateResult;
|
|
3397
|
+
interface BuildEvidenceVectorOptions {
|
|
3398
|
+
/** Minimum paired observations before an axis can claim significance; below
|
|
3399
|
+
* it the axis is `few_runs`. Default 3. */
|
|
3400
|
+
minProductiveRuns?: number;
|
|
3401
|
+
/** Confidence level for every axis bootstrap. Default 0.95. */
|
|
3402
|
+
confidence?: number;
|
|
3403
|
+
/** Bootstrap resamples. Default 2000. */
|
|
3404
|
+
resamples?: number;
|
|
3405
|
+
/** Fixed bootstrap seed for a deterministic, reproducible verdict. Default 1337. */
|
|
3406
|
+
seed?: number;
|
|
3407
|
+
}
|
|
3408
|
+
/**
|
|
3409
|
+
* The Evidence Bus. For each objective, pair candidate vs baseline by full
|
|
3410
|
+
* cellId and bootstrap a CI on the good-direction paired delta. Reuses the
|
|
3411
|
+
* exact `pairHoldout` + `pairedBootstrap` machinery the held-out gate uses, so
|
|
3412
|
+
* a single source of truth governs pairing granularity + scale handling.
|
|
3413
|
+
*/
|
|
3414
|
+
declare function buildEvidenceVector<TArtifact, TScenario extends Scenario>(ctx: GateContext<TArtifact, TScenario>, objectives: PromotionObjective[], opts?: BuildEvidenceVectorOptions): EvidenceVector;
|
|
3415
|
+
/**
|
|
3416
|
+
* The default strategy: symmetric multi-objective Pareto significance. Ship iff
|
|
3417
|
+
* the candidate weakly dominates the baseline at the confidence level — no axis
|
|
3418
|
+
* credibly worse AND ≥1 axis credibly better. Floor breach on any axis → hold
|
|
3419
|
+
* (anti-Goodhart, dominates everything). Insufficient evidence on any axis →
|
|
3420
|
+
* need_more_work. Statistically equivalent → hold (never ship noise).
|
|
3421
|
+
*/
|
|
3422
|
+
declare const paretoPolicy: PromotionPolicy;
|
|
3423
|
+
interface ParetoSignificanceGateOptions extends BuildEvidenceVectorOptions {
|
|
3424
|
+
/** The objective vector. Every axis is both a gain source and a safety floor. */
|
|
3425
|
+
objectives: PromotionObjective[];
|
|
3426
|
+
/** Strategy applied to the evidence vector. Default `paretoPolicy`. Override
|
|
3427
|
+
* to run a stricter/looser strategy over the SAME bus (competing policies). */
|
|
3428
|
+
policy?: PromotionPolicy;
|
|
3429
|
+
/** Override the gate name in reports. */
|
|
3430
|
+
name?: string;
|
|
3431
|
+
}
|
|
3432
|
+
/**
|
|
3433
|
+
* Wrap the bus + a policy as a `Gate`. Plugs into the existing
|
|
3434
|
+
* `runImprovementLoop({ gate })` slot and composes via `composeGate`; default
|
|
3435
|
+
* loop behavior is unchanged because consumers opt in by passing this gate.
|
|
3436
|
+
*/
|
|
3437
|
+
declare function paretoSignificanceGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(options: ParetoSignificanceGateOptions): Gate<TArtifact, TScenario>;
|
|
3438
|
+
|
|
3439
|
+
/**
|
|
3440
|
+
* Pre-registered hypotheses — declare what you're testing BEFORE the
|
|
3441
|
+
* run, check it AFTER. Prevents p-hacking, optional stopping, and the
|
|
3442
|
+
* "we ran until it looked good" failure mode.
|
|
3443
|
+
*
|
|
3444
|
+
* Manifest is a plain JSON-friendly object. Sign it with a content hash
|
|
3445
|
+
* + timestamp; the registered record becomes immutable. Post-run,
|
|
3446
|
+
* evaluate the manifest against observed results — the library refuses
|
|
3447
|
+
* to let you re-interpret a different metric as the declared one.
|
|
3448
|
+
*/
|
|
3449
|
+
interface HypothesisManifest {
|
|
3450
|
+
id: string;
|
|
3451
|
+
/** Human prose — goes into the audit trail. */
|
|
3452
|
+
hypothesis: string;
|
|
3453
|
+
/** Metric the hypothesis claims to move. */
|
|
3454
|
+
metric: string;
|
|
3455
|
+
/** 'increase' = candidate should score higher than baseline; 'decrease' = lower. */
|
|
3456
|
+
direction: 'increase' | 'decrease';
|
|
3457
|
+
/** Minimum effect size to count (same units as the metric). */
|
|
3458
|
+
minEffect: number;
|
|
3459
|
+
/** Alpha threshold. */
|
|
3460
|
+
alpha: number;
|
|
3461
|
+
/** Target statistical power at which sample size was pre-computed. */
|
|
3462
|
+
power: number;
|
|
3463
|
+
/** Declared N per arm before running. */
|
|
3464
|
+
preRegisteredN: number;
|
|
3465
|
+
/** ISO8601 timestamp the manifest was registered. */
|
|
3466
|
+
registeredAt: string;
|
|
3467
|
+
/** Optional identifiers to tie into the trace corpus. */
|
|
3468
|
+
baselineLabel?: string;
|
|
3469
|
+
candidateLabel?: string;
|
|
3470
|
+
}
|
|
3471
|
+
/**
|
|
3472
|
+
* Identifier for the hashing scheme used to produce `contentHash`.
|
|
3473
|
+
*
|
|
3474
|
+
* `'sha256-content'` — sha256 hex over the canonicalized manifest with
|
|
3475
|
+
* the `contentHash` and `algo` fields stripped. Held as a string union
|
|
3476
|
+
* so future schemes can be added without breaking parsers; SignedManifest
|
|
3477
|
+
* values without `algo` deserialize cleanly because the field is optional.
|
|
3478
|
+
*/
|
|
3479
|
+
type SignedManifestAlgo = 'sha256-content';
|
|
3480
|
+
interface SignedManifest extends HypothesisManifest {
|
|
3481
|
+
/** sha256 hex of canonicalized manifest (everything except contentHash and algo). */
|
|
3482
|
+
contentHash: string;
|
|
3483
|
+
/**
|
|
3484
|
+
* Algorithm string describing how `contentHash` was produced.
|
|
3485
|
+
*
|
|
3486
|
+
* Optional on the type so serialized manifests without it still parse,
|
|
3487
|
+
* but ALWAYS populated by {@link signManifest}. Consumers that want to
|
|
3488
|
+
* enforce a known algorithm should reject manifests where this field
|
|
3489
|
+
* is missing or unrecognized.
|
|
3490
|
+
*/
|
|
3491
|
+
algo?: SignedManifestAlgo;
|
|
3492
|
+
}
|
|
3493
|
+
|
|
750
3494
|
/**
|
|
751
3495
|
* Anytime-valid sequential promotion gate — an e-process (betting
|
|
752
3496
|
* test-martingale, see `eProcess` in `statistics.ts`) over paired
|
|
@@ -1299,6 +4043,183 @@ declare function surfaceContentHash(surface: MutableSurface): `sha256:${string}`
|
|
|
1299
4043
|
/** Short loop key derived from the same content identity as provenance. */
|
|
1300
4044
|
declare function surfaceHash(surface: MutableSurface): string;
|
|
1301
4045
|
|
|
4046
|
+
/**
|
|
4047
|
+
* `runOptimization` — the improvement loop body. Runs N generations: the
|
|
4048
|
+
* `SurfaceProposer` proposes K candidate surfaces per generation, each
|
|
4049
|
+
* candidate runs a campaign (the measurement), and only a candidate that beats
|
|
4050
|
+
* the single global incumbent becomes the next generation's parent.
|
|
4051
|
+
* Proposer-agnostic — the same loop runs an evolutionary population mutator
|
|
4052
|
+
* (`evolutionaryProposer`) or any reflective / agentic proposer; they differ
|
|
4053
|
+
* only in how `propose()` picks candidates.
|
|
4054
|
+
*
|
|
4055
|
+
* This is `runLoop`'s shape (plan → measure → decide) specialized to surface
|
|
4056
|
+
* improvement: `proposer.propose` = plan, `runCampaign` = the measurement
|
|
4057
|
+
* (which runs the worker behind `dispatch`), the mean-composite ranking = the
|
|
4058
|
+
* validator, `proposer.decide` = the stop check.
|
|
4059
|
+
*
|
|
4060
|
+
* The gated-promotion shell (`runImprovementLoop`) wraps this with a holdout
|
|
4061
|
+
* re-score + release gate + optional PR.
|
|
4062
|
+
*/
|
|
4063
|
+
|
|
4064
|
+
interface RunOptimizationBaseOptions<TScenario extends Scenario, TArtifact> extends Omit<RunCampaignOptions<TScenario, TArtifact>, 'dispatch'> {
|
|
4065
|
+
/** Initial mutable surface (typically system prompt or addendum). */
|
|
4066
|
+
baselineSurface: MutableSurface;
|
|
4067
|
+
/** Dispatcher that takes the CURRENT surface + scenario → artifact. */
|
|
4068
|
+
dispatchWithSurface: (surface: MutableSurface, scenario: TScenario, ctx: Parameters<RunCampaignOptions<TScenario, TArtifact>['dispatch']>[1]) => Promise<TArtifact>;
|
|
4069
|
+
/** The candidate-generation strategy. Wrap a population `Mutator` via
|
|
4070
|
+
* `evolutionaryProposer({ mutator })`, or pass any reflective / agentic
|
|
4071
|
+
* proposer that implements `SurfaceProposer`. */
|
|
4072
|
+
proposer: SurfaceProposer;
|
|
4073
|
+
populationSize: number;
|
|
4074
|
+
maxGenerations: number;
|
|
4075
|
+
/** @deprecated The loop has one global incumbent and can promote only the
|
|
4076
|
+
* single candidate that beats it. Retained for source compatibility. */
|
|
4077
|
+
promoteTopK?: number;
|
|
4078
|
+
/** DEPTH knob forwarded to the proposer's `propose()` — max iterations the
|
|
4079
|
+
* agentic generator may take per candidate. */
|
|
4080
|
+
maxImprovementShots?: number;
|
|
4081
|
+
/** Optional analysis report forwarded to `propose()`. Opaque here; the
|
|
4082
|
+
* proposer types it. */
|
|
4083
|
+
report?: unknown;
|
|
4084
|
+
/** Structured findings forwarded to `propose()` as `ctx.findings`. A
|
|
4085
|
+
* findings producer emits these from the
|
|
4086
|
+
* generation's traces; findings-grounded proposers consume them. Opaque here;
|
|
4087
|
+
* the proposer types its `TFindings`. Empty when no producer is wired. */
|
|
4088
|
+
findings?: unknown[];
|
|
4089
|
+
/** Per-generation findings producer. Runs once on the BASELINE campaign
|
|
4090
|
+
* (as `generation: -1`, the baseline convention) before generation 0
|
|
4091
|
+
* proposes — so even a single-generation run proposes with trace context —
|
|
4092
|
+
* and then after each generation's candidates are scored with that
|
|
4093
|
+
* generation's results; whatever it returns REPLACES `ctx.findings` for the
|
|
4094
|
+
* NEXT `propose()`, so the diagnosis is refreshed each round instead
|
|
4095
|
+
* of being a static one-shot. Generic by design: the substrate does not
|
|
4096
|
+
* import an analyst — the consumer plugs its trace-analyst registry / HALO
|
|
4097
|
+
* here (reading the per-candidate `runDir` traces). When absent, findings
|
|
4098
|
+
* stay the static `opts.findings`. */
|
|
4099
|
+
analyzeGeneration?: (input: {
|
|
4100
|
+
generation: number;
|
|
4101
|
+
runDir: string;
|
|
4102
|
+
candidates: Array<{
|
|
4103
|
+
surfaceHash: string;
|
|
4104
|
+
campaign: CampaignResult<TArtifact, TScenario>;
|
|
4105
|
+
composite: number;
|
|
4106
|
+
}>;
|
|
4107
|
+
history: GenerationRecord[];
|
|
4108
|
+
/** Shared run spend account and receipt attribution phase. */
|
|
4109
|
+
costLedger?: CostLedger;
|
|
4110
|
+
costPhase?: string;
|
|
4111
|
+
}) => Promise<unknown[]>;
|
|
4112
|
+
}
|
|
4113
|
+
type RunOptimizationOptions<TScenario extends Scenario, TArtifact> = RunOptimizationBaseOptions<TScenario, TArtifact>;
|
|
4114
|
+
interface RunOptimizationResult<TArtifact, TScenario extends Scenario> {
|
|
4115
|
+
generations: Array<{
|
|
4116
|
+
record: GenerationRecord;
|
|
4117
|
+
surfaces: Array<{
|
|
4118
|
+
surfaceHash: string;
|
|
4119
|
+
surface: MutableSurface;
|
|
4120
|
+
campaign: CampaignResult<TArtifact, TScenario>;
|
|
4121
|
+
}>;
|
|
4122
|
+
}>;
|
|
4123
|
+
winnerSurface: MutableSurface;
|
|
4124
|
+
winnerSurfaceHash: string;
|
|
4125
|
+
/** Proposer label for the promoted surface. Present when the winning
|
|
4126
|
+
* candidate came from a `ProposedCandidate` (a reflective proposer);
|
|
4127
|
+
* absent when the winner is the baseline or a bare-surface mutator. */
|
|
4128
|
+
winnerLabel?: string;
|
|
4129
|
+
/** Proposer rationale for the promoted surface — the "because Z" that
|
|
4130
|
+
* motivated the winning change. Survives to `SelfImproveResult` and the
|
|
4131
|
+
* emitted provenance record. Absent when the winner is the baseline. */
|
|
4132
|
+
winnerRationale?: string;
|
|
4133
|
+
baselineCampaign: CampaignResult<TArtifact, TScenario>;
|
|
4134
|
+
/** Run-wide spend, including agents, proposers, analysts, and judges. */
|
|
4135
|
+
cost: CostLedgerSummary;
|
|
4136
|
+
/** The GEPA Pareto frontier across every scored surface (baseline + all
|
|
4137
|
+
* generations) by per-scenario objective vector — the non-dominated set.
|
|
4138
|
+
* Each generation's `propose()` received the frontier-so-far as
|
|
4139
|
+
* `ctx.paretoParents`; this is the final frontier. A surface here that is
|
|
4140
|
+
* NOT the winner is uniquely best on some scenario the winner loses on. */
|
|
4141
|
+
paretoFrontier: ParetoParent[];
|
|
4142
|
+
}
|
|
4143
|
+
/**
|
|
4144
|
+
* Improvement loop body: N generations of propose → campaign → rank, maintaining a Pareto frontier and one global incumbent across generations.
|
|
4145
|
+
*/
|
|
4146
|
+
declare function runOptimization<TScenario extends Scenario, TArtifact>(opts: RunOptimizationOptions<TScenario, TArtifact>): Promise<RunOptimizationResult<TArtifact, TScenario>>;
|
|
4147
|
+
|
|
4148
|
+
/**
|
|
4149
|
+
* `runImprovementLoop` — the gated-promotion shell around the improvement
|
|
4150
|
+
* loop body (`runOptimization`). Proposes candidate surfaces via the
|
|
4151
|
+
* `SurfaceProposer`, re-scores the winner against the baseline on a
|
|
4152
|
+
* holdout set, runs the release gate, and optionally opens a PR.
|
|
4153
|
+
*
|
|
4154
|
+
* Role vocabulary (see docs/design/loop-taxonomy.md):
|
|
4155
|
+
* - PROPOSER = the `SurfaceProposer` (evolutionary GEPA mutator OR
|
|
4156
|
+
* reflective analyst). Proposes candidate SURFACES — the
|
|
4157
|
+
* worker's system prompt / tool config — NOT conversation
|
|
4158
|
+
* turns.
|
|
4159
|
+
* - MEASUREMENT= `runCampaign`. Scores one surface by running the worker
|
|
4160
|
+
* (via `dispatch`) over scenarios and judging the output.
|
|
4161
|
+
* - WORKER = the agent harness in the sandbox, invoked behind the
|
|
4162
|
+
* topology-opaque `dispatch` seam — never referenced here.
|
|
4163
|
+
*
|
|
4164
|
+
* Distinct from `runLoop` in `@tangle-network/agent-runtime`, which is the
|
|
4165
|
+
* INNER conversation loop (execution driver ↔ workers in a sandbox). `runImprovementLoop`
|
|
4166
|
+
* is the OUTER loop: it improves the surface that those workers run.
|
|
4167
|
+
*
|
|
4168
|
+
* Hard-refuses unsafe configurations:
|
|
4169
|
+
* - `tracing: 'off'` when a proposer is wired (improvement is unattributable)
|
|
4170
|
+
* - `autoOnPromote: 'config'` — DEFERRED to Pass B; v0.40 only ships
|
|
4171
|
+
* `'pr'` and `'none'`.
|
|
4172
|
+
*/
|
|
4173
|
+
|
|
4174
|
+
type RunImprovementLoopOptions<TScenario extends Scenario, TArtifact> = RunOptimizationOptions<TScenario, TArtifact> & {
|
|
4175
|
+
/** Holdout scenarios kept OUT of the training optimization pool — used
|
|
4176
|
+
* ONLY to score baseline vs winner for the gate. */
|
|
4177
|
+
holdoutScenarios: TScenario[];
|
|
4178
|
+
/** Promotion gate. Substrate strongly recommends `defaultProductionGate`
|
|
4179
|
+
* for production wiring (composes red-team / reward-hacking / canary /
|
|
4180
|
+
* heldout). */
|
|
4181
|
+
gate: Gate<TArtifact, TScenario>;
|
|
4182
|
+
/** What to do when the gate ships:
|
|
4183
|
+
* - `'pr'`: open a PR via `openAutoPr`
|
|
4184
|
+
* - `'none'`: just report — caller decides what to do with the winner
|
|
4185
|
+
* v0.40 does NOT support `'config'` (live-runtime self-mutation) —
|
|
4186
|
+
* deferred to Pass B behind safety stack. */
|
|
4187
|
+
autoOnPromote: 'pr' | 'none';
|
|
4188
|
+
/** GH owner / repo for the auto-PR. Required when autoOnPromote === 'pr'. */
|
|
4189
|
+
ghOwner?: string;
|
|
4190
|
+
ghRepo?: string;
|
|
4191
|
+
/** Optional render override — substrate writes a diff-shaped surface; pass
|
|
4192
|
+
* a function to format the promoted surface differently. */
|
|
4193
|
+
renderPromotedDiff?: (winnerSurface: MutableSurface, baselineSurface: MutableSurface) => string;
|
|
4194
|
+
/** Placebo control. When supplied AND the winner differs from baseline, the
|
|
4195
|
+
* loop scores a THIRD holdout arm: the winner surface with its content
|
|
4196
|
+
* footprint-matched-blanked by this function (typically via `neutralizeText`).
|
|
4197
|
+
* Its scores are exposed to the gate as `ctx.neutralizedJudgeScores`, letting
|
|
4198
|
+
* a `neutralizationGate` reject a win whose lift survives blanking the content
|
|
4199
|
+
* (decorative — driven by footprint, not content). Costs one extra holdout
|
|
4200
|
+
* campaign; omit to skip. Return a byte/layout-matched blank of the winner. */
|
|
4201
|
+
neutralize?: (winnerSurface: MutableSurface, baselineSurface: MutableSurface) => MutableSurface;
|
|
4202
|
+
};
|
|
4203
|
+
interface RunImprovementLoopResult<TArtifact, TScenario extends Scenario> extends RunOptimizationResult<TArtifact, TScenario> {
|
|
4204
|
+
baselineOnHoldout: CampaignResult<TArtifact, TScenario>;
|
|
4205
|
+
winnerOnHoldout: CampaignResult<TArtifact, TScenario>;
|
|
4206
|
+
gateResult: Awaited<ReturnType<Gate<TArtifact, TScenario>['decide']>>;
|
|
4207
|
+
/** Unified baseline→winner surface diff. Computed UNCONDITIONALLY (not only
|
|
4208
|
+
* when `autoOnPromote === 'pr'`) so the diff that the gate decided on is
|
|
4209
|
+
* always present on the result + in the emitted provenance record. Empty
|
|
4210
|
+
* string when winner == baseline (no change to diff). */
|
|
4211
|
+
promotedDiff: string;
|
|
4212
|
+
prResult?: ReturnType<typeof openAutoPr>;
|
|
4213
|
+
}
|
|
4214
|
+
/**
|
|
4215
|
+
* Gated-promotion shell over `runOptimization`: scores the winner against the baseline on a holdout set, runs the release gate, and optionally opens a PR.
|
|
4216
|
+
*/
|
|
4217
|
+
declare function runImprovementLoop<TScenario extends Scenario, TArtifact>(opts: RunImprovementLoopOptions<TScenario, TArtifact>): Promise<RunImprovementLoopResult<TArtifact, TScenario>>;
|
|
4218
|
+
/**
|
|
4219
|
+
* Default surface diff renderer: produces a unified baseline/winner text diff for prompt surfaces or a worktree-ref summary for code surfaces.
|
|
4220
|
+
*/
|
|
4221
|
+
declare function defaultRenderDiff(winnerSurface: MutableSurface, baselineSurface: MutableSurface): string;
|
|
4222
|
+
|
|
1302
4223
|
/**
|
|
1303
4224
|
* `compareProposers` — a head-to-head lift benchmark across surface proposers
|
|
1304
4225
|
* on ONE corpus. This is the forcing function: optimizer quality (GEPA
|
|
@@ -1413,39 +4334,297 @@ interface OptimizerEntryConfig<TScenario extends Scenario, TArtifact> {
|
|
|
1413
4334
|
/** Optional analysis report forwarded to `propose()` as `ctx.report`. */
|
|
1414
4335
|
report?: unknown;
|
|
1415
4336
|
}
|
|
1416
|
-
/** GEPA, reflection-only (single-parent, no Pareto combine). */
|
|
1417
|
-
declare function gepaReflectionEntry<TScenario extends Scenario, TArtifact>(config: OptimizerEntryConfig<TScenario, TArtifact>, name?: string): ProposerEntry;
|
|
1418
|
-
/** GEPA with the Pareto frontier + combine-complementary-lessons. */
|
|
1419
|
-
declare function gepaParetoEntry<TScenario extends Scenario, TArtifact>(config: OptimizerEntryConfig<TScenario, TArtifact>, name?: string): ProposerEntry;
|
|
1420
|
-
/** SkillOpt patch-mode hill-climb. Runs findings-BLIND: `runSkillOpt` owns its
|
|
1421
|
-
* own epoch acceptance/budget loop and does not thread `analyzeGeneration`, so
|
|
1422
|
-
* `config.findings` is intentionally NOT forwarded here. In a findings-fed
|
|
1423
|
-
* comparison this entry is the blind control — do not read its result as
|
|
1424
|
-
* findings-fed. (Threading findings into the SkillOpt epoch loop is a separate
|
|
1425
|
-
* refactor, deferred not faked.) */
|
|
1426
|
-
declare function skillOptEntry<TScenario extends Scenario, TArtifact>(config: OptimizerEntryConfig<TScenario, TArtifact>, name?: string): ProposerEntry;
|
|
1427
|
-
/** FAPO reviewed-escalation policy. This is an orchestration layer over
|
|
1428
|
-
* level-specific proposers, not a new mutation operator:
|
|
1429
|
-
* prompt -> parameter -> structural, with scope + reviewer + plateau rules in
|
|
1430
|
-
* `fapoProposer`. The prompt proposer defaults to GEPA+Pareto because that is
|
|
1431
|
-
* the package's strongest prompt-tier proposer; parameter/structural proposers
|
|
1432
|
-
* are opt-in so we do not fake code-generation inside agent-eval. */
|
|
1433
|
-
interface FapoEntryConfig<TScenario extends Scenario, TArtifact> extends OptimizerEntryConfig<TScenario, TArtifact> {
|
|
1434
|
-
/** Override the prompt-level proposer. Default: `gepaProposer({ combineParents: true })`. */
|
|
1435
|
-
promptProposer?: SurfaceProposer;
|
|
1436
|
-
/** Parameter/config-level proposer. If omitted, `parameterCandidates` builds one. */
|
|
1437
|
-
parameterProposer?: SurfaceProposer;
|
|
1438
|
-
/** Structural/code-level proposer, typically supplied by agent-runtime. */
|
|
1439
|
-
structuralProposer?: SurfaceProposer;
|
|
1440
|
-
/** Convenience: build a `parameterSweepProposer` from these candidates. */
|
|
1441
|
-
parameterCandidates?: readonly ParameterCandidate[];
|
|
1442
|
-
/** FAPO policy knobs: scope, reviewer, plateau thresholds. */
|
|
1443
|
-
fapo?: Omit<FapoProposerOptions, 'proposers' | 'promptProposer' | 'parameterProposer' | 'structuralProposer'>;
|
|
4337
|
+
/** GEPA, reflection-only (single-parent, no Pareto combine). */
|
|
4338
|
+
declare function gepaReflectionEntry<TScenario extends Scenario, TArtifact>(config: OptimizerEntryConfig<TScenario, TArtifact>, name?: string): ProposerEntry;
|
|
4339
|
+
/** GEPA with the Pareto frontier + combine-complementary-lessons. */
|
|
4340
|
+
declare function gepaParetoEntry<TScenario extends Scenario, TArtifact>(config: OptimizerEntryConfig<TScenario, TArtifact>, name?: string): ProposerEntry;
|
|
4341
|
+
/** SkillOpt patch-mode hill-climb. Runs findings-BLIND: `runSkillOpt` owns its
|
|
4342
|
+
* own epoch acceptance/budget loop and does not thread `analyzeGeneration`, so
|
|
4343
|
+
* `config.findings` is intentionally NOT forwarded here. In a findings-fed
|
|
4344
|
+
* comparison this entry is the blind control — do not read its result as
|
|
4345
|
+
* findings-fed. (Threading findings into the SkillOpt epoch loop is a separate
|
|
4346
|
+
* refactor, deferred not faked.) */
|
|
4347
|
+
declare function skillOptEntry<TScenario extends Scenario, TArtifact>(config: OptimizerEntryConfig<TScenario, TArtifact>, name?: string): ProposerEntry;
|
|
4348
|
+
/** FAPO reviewed-escalation policy. This is an orchestration layer over
|
|
4349
|
+
* level-specific proposers, not a new mutation operator:
|
|
4350
|
+
* prompt -> parameter -> structural, with scope + reviewer + plateau rules in
|
|
4351
|
+
* `fapoProposer`. The prompt proposer defaults to GEPA+Pareto because that is
|
|
4352
|
+
* the package's strongest prompt-tier proposer; parameter/structural proposers
|
|
4353
|
+
* are opt-in so we do not fake code-generation inside agent-eval. */
|
|
4354
|
+
interface FapoEntryConfig<TScenario extends Scenario, TArtifact> extends OptimizerEntryConfig<TScenario, TArtifact> {
|
|
4355
|
+
/** Override the prompt-level proposer. Default: `gepaProposer({ combineParents: true })`. */
|
|
4356
|
+
promptProposer?: SurfaceProposer;
|
|
4357
|
+
/** Parameter/config-level proposer. If omitted, `parameterCandidates` builds one. */
|
|
4358
|
+
parameterProposer?: SurfaceProposer;
|
|
4359
|
+
/** Structural/code-level proposer, typically supplied by agent-runtime. */
|
|
4360
|
+
structuralProposer?: SurfaceProposer;
|
|
4361
|
+
/** Convenience: build a `parameterSweepProposer` from these candidates. */
|
|
4362
|
+
parameterCandidates?: readonly ParameterCandidate[];
|
|
4363
|
+
/** FAPO policy knobs: scope, reviewer, plateau thresholds. */
|
|
4364
|
+
fapo?: Omit<FapoProposerOptions, 'proposers' | 'promptProposer' | 'parameterProposer' | 'structuralProposer'>;
|
|
4365
|
+
}
|
|
4366
|
+
/**
|
|
4367
|
+
* Build a `ProposerEntry` that runs the full FAPO escalation policy (prompt → parameter → structural) as a single comparable optimizer entry.
|
|
4368
|
+
*/
|
|
4369
|
+
declare function fapoEscalationEntry<TScenario extends Scenario, TArtifact>(config: FapoEntryConfig<TScenario, TArtifact>, name?: string): ProposerEntry;
|
|
4370
|
+
|
|
4371
|
+
/**
|
|
4372
|
+
* Artifact validators.
|
|
4373
|
+
*
|
|
4374
|
+
* Generic "score a produced artifact" primitive. Tax uses it for PDF form
|
|
4375
|
+
* correctness, research for sourced briefs, browser for task assertions, coding
|
|
4376
|
+
* for social posts. One interface, many validators; all plug into
|
|
4377
|
+
* `BenchmarkRunner` the same way.
|
|
4378
|
+
*
|
|
4379
|
+
* A validator receives an `Artifact` (file on disk, JSON blob, text, binary)
|
|
4380
|
+
* plus a `ValidationContext` (scenario id, the turns that produced it) and
|
|
4381
|
+
* returns a `ValidationResult` with pass/fail + 0..1 score + structured
|
|
4382
|
+
* issues.
|
|
4383
|
+
*/
|
|
4384
|
+
interface Artifact {
|
|
4385
|
+
/** Logical kind — validators type-guard on this */
|
|
4386
|
+
kind: 'file' | 'json' | 'text' | 'binary' | string;
|
|
4387
|
+
/** Filesystem-style path, optional */
|
|
4388
|
+
path?: string;
|
|
4389
|
+
/** String content for text/json/file kinds */
|
|
4390
|
+
content?: string;
|
|
4391
|
+
/** Binary content (if kind === 'binary') */
|
|
4392
|
+
bytes?: Uint8Array;
|
|
4393
|
+
/** Caller-supplied metadata (mimeType, sha256, size, etc.) */
|
|
4394
|
+
metadata?: Record<string, unknown>;
|
|
4395
|
+
}
|
|
4396
|
+
|
|
4397
|
+
/**
|
|
4398
|
+
* Validator-output verdict — substrate primitive for "did this output pass,
|
|
4399
|
+
* and how well?"
|
|
4400
|
+
*
|
|
4401
|
+
* Used by:
|
|
4402
|
+
* - `@tangle-network/agent-eval/matrix` — verdict per cell in the cartesian.
|
|
4403
|
+
* - `@tangle-network/agent-runtime` — Validator<Output, Verdict = DefaultVerdict>.
|
|
4404
|
+
* Runtime keeps `Validator` because it's coupled to runtime-shaped
|
|
4405
|
+
* `ValidationCtx` (iteration, signal, traceEmitter); the verdict TYPE
|
|
4406
|
+
* itself is a substrate concept and lives here.
|
|
4407
|
+
*
|
|
4408
|
+
* Repo layering: agent-eval is the substrate (no upward deps). Both
|
|
4409
|
+
* agent-runtime and agent-knowledge consume this type FROM agent-eval —
|
|
4410
|
+
* never the other way around. See CLAUDE.md "Repo layering" for the rule.
|
|
4411
|
+
*/
|
|
4412
|
+
/**
|
|
4413
|
+
* Minimal verdict shape — `valid` + `score` are required; `scores` +
|
|
4414
|
+
* `notes` are optional surface. Validators that need richer shapes
|
|
4415
|
+
* parameterise `Validator<Output, MyVerdict>` with their own type.
|
|
4416
|
+
*
|
|
4417
|
+
* Need structured extras? Extend DefaultVerdict with typed fields — never
|
|
4418
|
+
* serialize extras into `notes`.
|
|
4419
|
+
*/
|
|
4420
|
+
interface DefaultVerdict {
|
|
4421
|
+
/** Whether the output meets the validator's pass criteria. */
|
|
4422
|
+
valid: boolean;
|
|
4423
|
+
/** Aggregate score in [0, 1]. Drivers use this for winner selection. */
|
|
4424
|
+
score: number;
|
|
4425
|
+
/** Per-dimension scores. Free-form; weighted into `score` by the validator. */
|
|
4426
|
+
scores?: Record<string, number>;
|
|
4427
|
+
/** Human-readable rationale; surfaces in trace + final-result `winner.verdict`. */
|
|
4428
|
+
notes?: string;
|
|
4429
|
+
}
|
|
4430
|
+
|
|
4431
|
+
/**
|
|
4432
|
+
* Completion verifier — the task-completion oracle.
|
|
4433
|
+
*
|
|
4434
|
+
* Answers the only eval question that is not a proxy: did the agent actually
|
|
4435
|
+
* COMPLETE the task — produce every required deliverable, persisted and
|
|
4436
|
+
* correct — rather than describe what should be done. A fluent transcript
|
|
4437
|
+
* that never produces the artifact scores zero here.
|
|
4438
|
+
*
|
|
4439
|
+
* Per requirement, a two-stage check:
|
|
4440
|
+
* 1. Structural — a produced item (vault artifact / approved proposal /
|
|
4441
|
+
* tool call) of the right kind is matched against the requirement and
|
|
4442
|
+
* carries non-empty content. Deterministic; no LLM.
|
|
4443
|
+
* 2. Correctness — only if structurally present AND the matched item
|
|
4444
|
+
* carries content, one targeted check decides whether that item
|
|
4445
|
+
* actually fulfils the requirement. A hallucinated artifact fails here;
|
|
4446
|
+
* an absent one already failed stage 1.
|
|
4447
|
+
*
|
|
4448
|
+
* `completionRate` is satisfied / MEASURABLE requirements (unmeasured rows —
|
|
4449
|
+
* checker failures — are excluded from the denominator, never scored as
|
|
4450
|
+
* zeros). Quality dimensions are meaningless on an incomplete task — callers
|
|
4451
|
+
* gate on `fullyComplete` / `completionRate` before scoring quality.
|
|
4452
|
+
*/
|
|
4453
|
+
|
|
4454
|
+
/** What kind of produced state can satisfy a requirement structurally. */
|
|
4455
|
+
type SatisfiedBy = 'artifact' | 'proposal' | 'tool-call' | 'any';
|
|
4456
|
+
interface CompletionRequirement {
|
|
4457
|
+
/** Stable id from the task gold (e.g. a persona's `expected_requirements[].req_id`). */
|
|
4458
|
+
reqId: string;
|
|
4459
|
+
/** Human-readable description of the required deliverable. */
|
|
4460
|
+
title: string;
|
|
4461
|
+
/** Optional kind/category hint, matched against a produced item's kind. */
|
|
4462
|
+
category?: string;
|
|
4463
|
+
/** What produced state satisfies this requirement. Defaults to 'any'. */
|
|
4464
|
+
satisfiedBy?: SatisfiedBy;
|
|
4465
|
+
}
|
|
4466
|
+
interface ProducedProposal {
|
|
4467
|
+
id: string;
|
|
4468
|
+
title: string;
|
|
4469
|
+
status: 'pending' | 'approved' | 'rejected';
|
|
4470
|
+
/** Optional persisted body — when present, enables a correctness check. */
|
|
4471
|
+
content?: string;
|
|
4472
|
+
}
|
|
4473
|
+
/** Everything observable about what a run actually produced. */
|
|
4474
|
+
interface ProducedState {
|
|
4475
|
+
/** Persisted vault artifacts. Reuses the shared `Artifact` shape. */
|
|
4476
|
+
artifacts: Artifact[];
|
|
4477
|
+
/** Proposals / filings the agent created. */
|
|
4478
|
+
proposals: ProducedProposal[];
|
|
4479
|
+
/** Names of tools the agent invoked. */
|
|
4480
|
+
toolCalls: string[];
|
|
4481
|
+
}
|
|
4482
|
+
interface RequirementCheck {
|
|
4483
|
+
reqId: string;
|
|
4484
|
+
title: string;
|
|
4485
|
+
/** A produced item of the right kind matched the requirement, non-empty. */
|
|
4486
|
+
structurallyPresent: boolean;
|
|
4487
|
+
/**
|
|
4488
|
+
* Whether the matched item actually fulfils the requirement. `null` when
|
|
4489
|
+
* not structurally present, when the matched item carries no content
|
|
4490
|
+
* to assess, or when the correctness check itself failed (`unmeasured`).
|
|
4491
|
+
*/
|
|
4492
|
+
correct: boolean | null;
|
|
4493
|
+
/** structurallyPresent && !unmeasured && correct !== false. */
|
|
4494
|
+
satisfied: boolean;
|
|
4495
|
+
/**
|
|
4496
|
+
* Set when the correctness check itself errored (LLM call failure or an
|
|
4497
|
+
* unparseable response after retry). The requirement's fulfilment is
|
|
4498
|
+
* UNKNOWN — `correct` stays null, `satisfied` is false, and
|
|
4499
|
+
* `completionVerdict` excludes the row from `completionRate`'s
|
|
4500
|
+
* denominator. Never folded into a zero: a synthetic zero is
|
|
4501
|
+
* indistinguishable from a real failure (see `JudgeParseError`).
|
|
4502
|
+
*/
|
|
4503
|
+
unmeasured?: true;
|
|
4504
|
+
/** Why the correctness check could not be measured (present iff `unmeasured`). */
|
|
4505
|
+
unmeasuredReason?: string;
|
|
4506
|
+
/** Human-readable evidence for the verdict. */
|
|
4507
|
+
evidence: string[];
|
|
4508
|
+
}
|
|
4509
|
+
/** Extends the substrate verdict spine: `valid` = `fullyComplete` and
|
|
4510
|
+
* `score` = `completionRate` — derived in `completionVerdict()`, the one
|
|
4511
|
+
* place those equalities hold by construction. */
|
|
4512
|
+
interface CompletionVerdict extends DefaultVerdict {
|
|
4513
|
+
taskId: string;
|
|
4514
|
+
requirements: RequirementCheck[];
|
|
4515
|
+
/** satisfied / MEASURABLE requirements (unmeasured rows leave the denominator). */
|
|
4516
|
+
completionRate: number;
|
|
4517
|
+
/** Every measurable requirement satisfied (false when anything is unmeasured). */
|
|
4518
|
+
fullyComplete: boolean;
|
|
4519
|
+
/** Requirements whose correctness check errored — reported, never scored as zero. */
|
|
4520
|
+
unmeasuredCount: number;
|
|
4521
|
+
}
|
|
4522
|
+
/**
|
|
4523
|
+
* Decides whether a produced item's content actually fulfils a requirement.
|
|
4524
|
+
* Injected so the structural verifier stays pure and unit-testable; the
|
|
4525
|
+
* production implementation is `createLlmCorrectnessChecker`.
|
|
4526
|
+
*/
|
|
4527
|
+
type CorrectnessChecker = (requirement: CompletionRequirement, content: string) => Promise<{
|
|
4528
|
+
correct: boolean;
|
|
4529
|
+
reason: string;
|
|
4530
|
+
}>;
|
|
4531
|
+
|
|
4532
|
+
/**
|
|
4533
|
+
* Produced-state extraction — normalize a run's runtime event stream into the
|
|
4534
|
+
* typed `ProducedState` the completion oracle consumes.
|
|
4535
|
+
*
|
|
4536
|
+
* `ProducedState` answers "what did the agent actually produce" — vault
|
|
4537
|
+
* artifacts, proposals, tool calls. The runtime emits these as a stream of
|
|
4538
|
+
* events; this module is the single normalization point from that stream to
|
|
4539
|
+
* the shape `verifyCompletion` expects.
|
|
4540
|
+
*
|
|
4541
|
+
* Input is structurally typed (`RuntimeEventLike`) so this module does not
|
|
4542
|
+
* depend on agent-runtime — agent-runtime's `RuntimeStreamEvent` satisfies it
|
|
4543
|
+
* structurally. The `content` on `ArtifactEventLike` and the whole
|
|
4544
|
+
* `proposal_created` variant are the runtime-side enrichments this contract
|
|
4545
|
+
* requires; the runtime emits them, this module consumes them.
|
|
4546
|
+
*/
|
|
4547
|
+
|
|
4548
|
+
/** A tool the agent invoked. */
|
|
4549
|
+
interface ToolCallEventLike {
|
|
4550
|
+
type: 'tool_call';
|
|
4551
|
+
toolName: string;
|
|
4552
|
+
}
|
|
4553
|
+
/**
|
|
4554
|
+
* An artifact the agent produced. `content` is the enriched field — the
|
|
4555
|
+
* runtime's base `artifact` event carries only metadata; the completion
|
|
4556
|
+
* oracle needs the body to verify the deliverable, so the runtime emits it.
|
|
4557
|
+
*/
|
|
4558
|
+
interface ArtifactEventLike {
|
|
4559
|
+
type: 'artifact';
|
|
4560
|
+
artifactId: string;
|
|
4561
|
+
name?: string;
|
|
4562
|
+
mimeType?: string;
|
|
4563
|
+
uri?: string;
|
|
4564
|
+
content?: string;
|
|
4565
|
+
}
|
|
4566
|
+
/** A proposal / filing the agent created. */
|
|
4567
|
+
interface ProposalEventLike {
|
|
4568
|
+
type: 'proposal_created';
|
|
4569
|
+
proposalId: string;
|
|
4570
|
+
title: string;
|
|
4571
|
+
status?: 'pending' | 'approved' | 'rejected';
|
|
4572
|
+
content?: string;
|
|
1444
4573
|
}
|
|
1445
4574
|
/**
|
|
1446
|
-
*
|
|
4575
|
+
* The subset of runtime stream events `extractProducedState` consumes.
|
|
4576
|
+
* agent-runtime's full `RuntimeStreamEvent` union satisfies this structurally;
|
|
4577
|
+
* the `{ type: string }` catch-all keeps the input permissive so callers can
|
|
4578
|
+
* pass the whole unfiltered telemetry stream — unrecognized events are skipped.
|
|
1447
4579
|
*/
|
|
1448
|
-
|
|
4580
|
+
type RuntimeEventLike = ToolCallEventLike | ArtifactEventLike | ProposalEventLike | {
|
|
4581
|
+
type: string;
|
|
4582
|
+
};
|
|
4583
|
+
|
|
4584
|
+
/**
|
|
4585
|
+
* Backend-integrity guard: distinguish "agent failed" from "eval ran against
|
|
4586
|
+
* a stub / unconfigured backend." Without this guard a canonical eval can
|
|
4587
|
+
* silently report `0/N passed` and look like an agent-quality problem when
|
|
4588
|
+
* the LLM was never actually called — the failure mode we just hit running
|
|
4589
|
+
* the 4-vertical parallel eval (legal-sandbox-stub returned hard-coded 33-104
|
|
4590
|
+
* char strings; gtm/creative defaulted to a cli-bridge that wasn't running).
|
|
4591
|
+
*
|
|
4592
|
+
* The shape:
|
|
4593
|
+
*
|
|
4594
|
+
* const report = summarizeBackendIntegrity(records)
|
|
4595
|
+
* assertRealBackend(records) // throws BackendIntegrityError if 100% stub
|
|
4596
|
+
*
|
|
4597
|
+
* A record is "stub-mode" if its `tokenUsage.input === 0 && tokenUsage.output === 0`.
|
|
4598
|
+
* (`costUsd` alone is unreliable — some backends successfully call LLMs but
|
|
4599
|
+
* don't propagate pricing, producing real tokens with $0 cost.)
|
|
4600
|
+
*
|
|
4601
|
+
* Verdicts:
|
|
4602
|
+
* - `real` — at least one record has nonzero token usage
|
|
4603
|
+
* - `stub` — every record is stub-mode (eval ran blind)
|
|
4604
|
+
* - `mixed` — some records real, some stub (partial backend failure;
|
|
4605
|
+
* often the 429-cascade or auth-half-failed case)
|
|
4606
|
+
*/
|
|
4607
|
+
|
|
4608
|
+
interface BackendIntegrityReport {
|
|
4609
|
+
/** Total records inspected. */
|
|
4610
|
+
totalRecords: number;
|
|
4611
|
+
/** Records with input=0 AND output=0 (a stub fingerprint). */
|
|
4612
|
+
stubRecords: number;
|
|
4613
|
+
/** Records with nonzero token usage (real LLM activity). */
|
|
4614
|
+
realRecords: number;
|
|
4615
|
+
/** Records where output>0 but costUsd=0 (real LLM, broken cost ledger). */
|
|
4616
|
+
uncostedRecords: number;
|
|
4617
|
+
/** Sum of input tokens across all records. */
|
|
4618
|
+
totalInputTokens: number;
|
|
4619
|
+
/** Sum of output tokens across all records. */
|
|
4620
|
+
totalOutputTokens: number;
|
|
4621
|
+
/** Sum of costUsd across all records. */
|
|
4622
|
+
totalCostUsd: number;
|
|
4623
|
+
/** Worst-case integrity verdict. */
|
|
4624
|
+
verdict: 'real' | 'mixed' | 'stub';
|
|
4625
|
+
/** Human-readable diagnosis suitable for terminal output. */
|
|
4626
|
+
diagnosis: string;
|
|
4627
|
+
}
|
|
1449
4628
|
|
|
1450
4629
|
/**
|
|
1451
4630
|
* `runProfileMatrix` — the missing keystone between `runAgentMatrix` and the
|
|
@@ -1712,6 +4891,22 @@ interface ScoreboardRenderOptions {
|
|
|
1712
4891
|
*/
|
|
1713
4892
|
declare function renderScoreboardMarkdown(rows: readonly ScoreboardRow[], opts?: ScoreboardRenderOptions): string;
|
|
1714
4893
|
|
|
4894
|
+
/**
|
|
4895
|
+
* `runEval` — the simplest preset over `runCampaign`. No optimizer, no
|
|
4896
|
+
* gate, no auto-PR. Just: run scenarios through dispatch, score with
|
|
4897
|
+
* judges, return CampaignResult.
|
|
4898
|
+
*
|
|
4899
|
+
* The 80% case for consumers who want a scorecard, not an improvement loop.
|
|
4900
|
+
*/
|
|
4901
|
+
|
|
4902
|
+
interface RunEvalOptions<TScenario extends Scenario, TArtifact> extends Omit<RunCampaignOptions<TScenario, TArtifact>, 'runDir'> {
|
|
4903
|
+
runDir: string;
|
|
4904
|
+
}
|
|
4905
|
+
/**
|
|
4906
|
+
* Simplest evaluation preset: run scenarios through dispatch, score with judges, and return a `CampaignResult` — no optimizer, no gate, no PR.
|
|
4907
|
+
*/
|
|
4908
|
+
declare function runEval<TScenario extends Scenario, TArtifact>(opts: RunEvalOptions<TScenario, TArtifact>): Promise<CampaignResult<TArtifact, TScenario>>;
|
|
4909
|
+
|
|
1715
4910
|
/**
|
|
1716
4911
|
* `runLineageLoop` — the live adapter that wires the {@link runLineage} DAG's
|
|
1717
4912
|
* two abstract seams (`step`, `merge`) to the REAL improvement machinery, so the
|
|
@@ -2173,6 +5368,119 @@ interface CompositeProposerOptions<TFindings = unknown> {
|
|
|
2173
5368
|
* one generation pool, with per-member provenance labels. */
|
|
2174
5369
|
declare function compositeProposer<TFindings = unknown>(opts: CompositeProposerOptions<TFindings>): SurfaceProposer<TFindings>;
|
|
2175
5370
|
|
|
5371
|
+
/**
|
|
5372
|
+
* `evolutionaryProposer` — adapts a stateless `Mutator` (population mutation:
|
|
5373
|
+
* GEPA / AxGEPA / reflective-mutation) into a `SurfaceProposer`. This is
|
|
5374
|
+
* the evolutionary strategy: each generation, mutate the current best surface
|
|
5375
|
+
* into N candidates, measure, select. No generation memory beyond the current
|
|
5376
|
+
* surface; the loop body handles ranking + promotion.
|
|
5377
|
+
*
|
|
5378
|
+
* The reflective alternative is agent-runtime's runtime proposer with a
|
|
5379
|
+
* `reflectiveGenerator` / `agenticGenerator`: it reasons over the report +
|
|
5380
|
+
* trace findings to propose targeted edits rather than blind mutations. Both
|
|
5381
|
+
* conform to `SurfaceProposer`; the improvement loop is identical either way.
|
|
5382
|
+
*/
|
|
5383
|
+
|
|
5384
|
+
interface EvolutionaryProposerOptions<TFindings = unknown> {
|
|
5385
|
+
mutator: Mutator<TFindings>;
|
|
5386
|
+
/** External findings fed to the mutator each generation. Default: []. */
|
|
5387
|
+
findings?: TFindings[];
|
|
5388
|
+
}
|
|
5389
|
+
/**
|
|
5390
|
+
* Wrap a stateless `Mutator` (GEPA, AxGEPA, reflective-mutation) as a `SurfaceProposer` that mutates the current best surface into N candidates each generation.
|
|
5391
|
+
*/
|
|
5392
|
+
declare function evolutionaryProposer<TFindings = unknown>(opts: EvolutionaryProposerOptions<TFindings>): SurfaceProposer<TFindings>;
|
|
5393
|
+
|
|
5394
|
+
/**
|
|
5395
|
+
* `gepaProposer` — a reflective `SurfaceProposer` for prompt-tier surfaces.
|
|
5396
|
+
* Each generation it reflects on the prior best candidate's per-scenario
|
|
5397
|
+
* scores + weakest dimensions, asks an LLM to propose targeted rewrites of
|
|
5398
|
+
* the current surface, and returns them as the next population.
|
|
5399
|
+
*
|
|
5400
|
+
* Maps onto the GEPA paper (Agrawal et al., arXiv:2507.19457):
|
|
5401
|
+
* - *Reflection*: each generation reflects on the best parent's weakest
|
|
5402
|
+
* dimensions + per-scenario top/bottom scores to propose targeted rewrites.
|
|
5403
|
+
* - *Pareto frontier*: `runOptimization` maintains the non-dominated set of
|
|
5404
|
+
* surfaces across generations (per-scenario objective vectors) and supplies
|
|
5405
|
+
* it as `ctx.paretoParents`. A surface uniquely best on one hard scenario
|
|
5406
|
+
* survives even when its mean composite is lower.
|
|
5407
|
+
* - *Combine complementary lessons*: when the frontier has >1 member, the
|
|
5408
|
+
* first population slot is a merge of those parents' strengths (one LLM
|
|
5409
|
+
* call citing each parent's winning scenarios). Toggle via `combineParents`.
|
|
5410
|
+
* Dominance is computed by the package-canonical `paretoFrontier` (`pareto.ts`).
|
|
5411
|
+
*
|
|
5412
|
+
* Optional `constraints` move structured-doc guards into the proposer
|
|
5413
|
+
* (preserve H2 section headings, cap sentence-level edits) — useful when
|
|
5414
|
+
* the surface IS a structured procedure like a SKILL.md / runbook /
|
|
5415
|
+
* judge rubric. When `constraints` is omitted, behavior is unchanged.
|
|
5416
|
+
*
|
|
5417
|
+
* The proposer is surface-agnostic — any string surface in any consumer opts
|
|
5418
|
+
* in by selecting it. Reuses the generic reflection primitive
|
|
5419
|
+
* (`buildReflectionPrompt` / `parseReflectionResponse`) and the router client.
|
|
5420
|
+
*
|
|
5421
|
+
* Earns its keep where there is real per-instance signal (which the
|
|
5422
|
+
* dimensional + per-scenario evidence + the `LabeledScenarioStore` flywheel
|
|
5423
|
+
* now provide). For thin-signal surfaces it degrades to plain reflection.
|
|
5424
|
+
* On generation 0 (no history) it reflects on the current surface against
|
|
5425
|
+
* the mutation primitives alone.
|
|
5426
|
+
*/
|
|
5427
|
+
|
|
5428
|
+
interface GepaProposerConstraints {
|
|
5429
|
+
/** H2 section headings that MUST appear unchanged in every candidate.
|
|
5430
|
+
* When set, the proposer auto-detects current H2s if this is empty AND
|
|
5431
|
+
* rejects any candidate that drops or renames a preserved heading.
|
|
5432
|
+
* Use when the surface is a structured doc (SKILL.md, runbook,
|
|
5433
|
+
* sectioned system prompt, judge rubric). */
|
|
5434
|
+
preserveSections?: string[];
|
|
5435
|
+
/** Maximum sentence-level edits per candidate vs the parent surface.
|
|
5436
|
+
* Rejection threshold = maxSentenceEdits × 2 (counts adds + removes).
|
|
5437
|
+
* Inspired by SkillOpt's edit-budget as a "textual learning rate."
|
|
5438
|
+
* Cap prevents an LLM rewrite from overwriting useful prior rules. */
|
|
5439
|
+
maxSentenceEdits?: number;
|
|
5440
|
+
}
|
|
5441
|
+
interface GepaProposerOptions {
|
|
5442
|
+
/** Router transport (apiKey/baseUrl). */
|
|
5443
|
+
llm: LlmClientOptions;
|
|
5444
|
+
/** Model that performs the reflection. */
|
|
5445
|
+
model: string;
|
|
5446
|
+
/** Optional ledger for direct proposer use. Campaign context takes precedence. */
|
|
5447
|
+
costLedger?: CostLedger;
|
|
5448
|
+
/** What is being optimized — appears in the reflection prompt for orientation. */
|
|
5449
|
+
target: string;
|
|
5450
|
+
/** Surface-specific mutation levers offered to the model. */
|
|
5451
|
+
mutationPrimitives?: string[];
|
|
5452
|
+
/** Top/bottom scenarios surfaced as evidence each generation. Default 3. */
|
|
5453
|
+
evidenceK?: number;
|
|
5454
|
+
/** Reflection sampling temperature. Default 0.7. */
|
|
5455
|
+
temperature?: number;
|
|
5456
|
+
/** Reflection max tokens. Default 6000. */
|
|
5457
|
+
maxTokens?: number;
|
|
5458
|
+
/** Structured-doc constraints. Candidates violating any are rejected
|
|
5459
|
+
* post-parse and dropped from the returned population. */
|
|
5460
|
+
constraints?: GepaProposerConstraints;
|
|
5461
|
+
/** GEPA combine-complementary-lessons: when the loop supplies a Pareto
|
|
5462
|
+
* frontier of >1 non-dominated parents (`ctx.paretoParents`), spend one
|
|
5463
|
+
* slot of the population on a merge of their strengths. Default `true` —
|
|
5464
|
+
* this is the GEPA-faithful behavior; the merge only fires once the
|
|
5465
|
+
* frontier has more than one member (generation ≥ 1). Set `false` for
|
|
5466
|
+
* pure single-parent reflection. */
|
|
5467
|
+
combineParents?: boolean;
|
|
5468
|
+
/** Cap on how many frontier parents feed one combine prompt (highest
|
|
5469
|
+
* composite first), to bound prompt size. Default 4. */
|
|
5470
|
+
combineMaxParents?: number;
|
|
5471
|
+
}
|
|
5472
|
+
/**
|
|
5473
|
+
* GEPA reflective proposer: each generation reflects on the weakest scenarios and dimensions to produce targeted prompt rewrites, optionally combining Pareto-frontier parents.
|
|
5474
|
+
*/
|
|
5475
|
+
declare function gepaProposer(opts: GepaProposerOptions): SurfaceProposer;
|
|
5476
|
+
/** Extract H2 headings (`## Foo`) from a markdown surface. Exported for
|
|
5477
|
+
* consumers building custom mutators that share the same invariant. */
|
|
5478
|
+
declare function extractH2Sections(text: string): string[];
|
|
5479
|
+
/** Sentence-level edit distance — count distinct add/remove ops between
|
|
5480
|
+
* two surfaces via a normalised line-by-line set diff. Treats trivial
|
|
5481
|
+
* whitespace as identical. Exported for tests + consumer-side validators. */
|
|
5482
|
+
declare function countSentenceEdits(baseline: string, candidate: string): number;
|
|
5483
|
+
|
|
2176
5484
|
/**
|
|
2177
5485
|
* `haloProposer` — wraps the REAL halo-engine (Inference.net's hierarchical
|
|
2178
5486
|
* agentic trace analyzer, `pip install halo-engine`, repo context-labs/halo)
|
|
@@ -2487,6 +5795,113 @@ declare function selectPolicyEditAuthorRows<T extends PolicyEditAuthorScenarioRo
|
|
|
2487
5795
|
/** Serialize once and fail before dispatch when author context exceeds its budget. */
|
|
2488
5796
|
declare function assertPolicyEditAuthorContextBudget(value: unknown, maxChars: number): SerializedJsonBudget;
|
|
2489
5797
|
|
|
5798
|
+
/**
|
|
5799
|
+
* Typed Ax output for analyst findings.
|
|
5800
|
+
*
|
|
5801
|
+
* Replaces the legacy `findings:string[]` pattern (where every bullet
|
|
5802
|
+
* became a flat-severity `AnalystFinding`) with a structured object
|
|
5803
|
+
* array. Ax binds the field as `findings:json[]` so the provider emits
|
|
5804
|
+
* native structured output; at the kind-factory boundary we Zod-validate
|
|
5805
|
+
* each emitted finding so malformed rows fail loud instead of being
|
|
5806
|
+
* silently lifted with default severity.
|
|
5807
|
+
*
|
|
5808
|
+
* Why not `f.object().array()` directly in the signature? The Ax
|
|
5809
|
+
* signature string `question:string -> findings:json[]` already lets
|
|
5810
|
+
* the provider emit JSON arrays. A Zod boundary is required either
|
|
5811
|
+
* way (the provider can return any JSON), and Zod gives us a single
|
|
5812
|
+
* validation surface independent of which Ax version is installed.
|
|
5813
|
+
*/
|
|
5814
|
+
|
|
5815
|
+
declare const RawAnalystFindingSchema: z.ZodObject<{
|
|
5816
|
+
severity: z.ZodEnum<{
|
|
5817
|
+
low: "low";
|
|
5818
|
+
high: "high";
|
|
5819
|
+
critical: "critical";
|
|
5820
|
+
medium: "medium";
|
|
5821
|
+
info: "info";
|
|
5822
|
+
}>;
|
|
5823
|
+
claim: z.ZodString;
|
|
5824
|
+
subject: z.ZodOptional<z.ZodString>;
|
|
5825
|
+
evidence_uri: z.ZodString;
|
|
5826
|
+
evidence_excerpt: z.ZodOptional<z.ZodString>;
|
|
5827
|
+
confidence: z.ZodNumber;
|
|
5828
|
+
rationale: z.ZodOptional<z.ZodString>;
|
|
5829
|
+
recommended_action: z.ZodOptional<z.ZodString>;
|
|
5830
|
+
}, z.core.$strict>;
|
|
5831
|
+
type RawAnalystFinding = z.infer<typeof RawAnalystFindingSchema>;
|
|
5832
|
+
|
|
5833
|
+
/**
|
|
5834
|
+
* Analyst-kind factory — the typed way to define trace analysts.
|
|
5835
|
+
*
|
|
5836
|
+
* A "kind" is a specialized analyst whose actor prompt, tool subset,
|
|
5837
|
+
* and Ax recursion config target one failure-mode lens (failure-mode
|
|
5838
|
+
* classification, knowledge gap discovery, knowledge poisoning, recursive
|
|
5839
|
+
* self-improvement, ...). Kinds emit findings in the typed `RawAnalystFinding`
|
|
5840
|
+
* shape via a JSON-array Ax output; the factory validates each row with
|
|
5841
|
+
* Zod and lifts it into `AnalystFinding[]` with no shape guessing.
|
|
5842
|
+
*
|
|
5843
|
+
* Composition rules:
|
|
5844
|
+
* - Each kind owns its actor description. No generic "answer this
|
|
5845
|
+
* question" prompt — the prompt names the failure lens.
|
|
5846
|
+
* - Each kind picks a narrow tool subset from `ANALYST_TOOL_GROUPS`.
|
|
5847
|
+
* A kind that never needs full-trace dumps can drop `viewTrace` /
|
|
5848
|
+
* `viewSpans` and stay cheap.
|
|
5849
|
+
* - Each kind declares its recursion + parallelism budget. Discovery-
|
|
5850
|
+
* heavy kinds (failure-mode) get higher `maxDepth`; lens kinds
|
|
5851
|
+
* (poisoning) usually stay at 0 since they have a tighter brief.
|
|
5852
|
+
*
|
|
5853
|
+
* Optimizer hook: kinds may declare `goldens` — labeled examples used
|
|
5854
|
+
* by `AxMiPRO` / `AxBootstrapFewShot` / `AxGEPA` to fit the actor
|
|
5855
|
+
* description programmatically. Stored on the kind, not the registry,
|
|
5856
|
+
* because the right metric is kind-specific.
|
|
5857
|
+
*/
|
|
5858
|
+
|
|
5859
|
+
/**
|
|
5860
|
+
* Per-kind specification. The factory turns this into a regular
|
|
5861
|
+
* `Analyst<TraceAnalysisStore>` ready for `AnalystRegistry.register()`.
|
|
5862
|
+
*/
|
|
5863
|
+
interface TraceAnalystKindSpec {
|
|
5864
|
+
/** Stable id. Appears in finding_id, telemetry, and registry exclusions. */
|
|
5865
|
+
id: string;
|
|
5866
|
+
/** One-sentence description shown in `registry.list()`. */
|
|
5867
|
+
description: string;
|
|
5868
|
+
/** Coarse classification stamped on every emitted finding (`failure-mode`, `knowledge-gap`, ...). */
|
|
5869
|
+
area: string;
|
|
5870
|
+
/** Bump on any breaking change to the actor prompt or output schema. */
|
|
5871
|
+
version: string;
|
|
5872
|
+
/** Actor system prompt. Must instruct the LLM to emit `findings` per the schema. */
|
|
5873
|
+
actorDescription: string;
|
|
5874
|
+
/** Responder system prompt; falls back to a minimal "format the findings" instruction. */
|
|
5875
|
+
responderDescription?: string;
|
|
5876
|
+
/** Tool functions the actor may call. Pick narrow subsets via `ANALYST_TOOL_GROUPS`. */
|
|
5877
|
+
buildTools: (store: TraceAnalysisStore) => AxFunction[];
|
|
5878
|
+
/** Recursion budget. `maxDepth: 0` disables subagents. */
|
|
5879
|
+
recursion?: {
|
|
5880
|
+
maxDepth: number;
|
|
5881
|
+
maxParallelSubagents?: number;
|
|
5882
|
+
};
|
|
5883
|
+
/** Actor turn cap. Default 12. */
|
|
5884
|
+
maxTurns?: number;
|
|
5885
|
+
/** Runtime char cap. Default 6000. */
|
|
5886
|
+
maxRuntimeChars?: number;
|
|
5887
|
+
/** Cost classification surfaced in `registry.list()` and budget enforcement. */
|
|
5888
|
+
cost: AnalystCost;
|
|
5889
|
+
/** Per-finding-row hook — kinds may reject / rewrite before lifting. */
|
|
5890
|
+
postProcess?: (row: RawAnalystFinding, ctx: AnalystContext) => RawAnalystFinding | null;
|
|
5891
|
+
/** Optional optimizer hook — populated when a kind wants to fit its prompt against labeled examples. */
|
|
5892
|
+
goldens?: TraceAnalystGolden[];
|
|
5893
|
+
}
|
|
5894
|
+
/**
|
|
5895
|
+
* One labeled example consumed by Ax optimizers (MIPRO / GEPA / Bootstrap).
|
|
5896
|
+
* Each input is the same `{question}` an analyst would receive; `expected`
|
|
5897
|
+
* is the ground-truth finding set a fitted prompt should produce on this
|
|
5898
|
+
* input. Metric: kind-specific (default: F1 on `finding_id` overlap).
|
|
5899
|
+
*/
|
|
5900
|
+
interface TraceAnalystGolden {
|
|
5901
|
+
question: string;
|
|
5902
|
+
expected: ReadonlyArray<Omit<RawAnalystFinding, 'confidence'>>;
|
|
5903
|
+
}
|
|
5904
|
+
|
|
2490
5905
|
/**
|
|
2491
5906
|
* `traceAnalystProposer` — wraps agent-eval's OWN trace-analyst engine
|
|
2492
5907
|
* (`AnalystRegistry` over the agentic OTLP reader) as a `SurfaceProposer`.
|
|
@@ -2537,6 +5952,793 @@ interface TraceAnalystProposerOptions {
|
|
|
2537
5952
|
/** Wrap agent-eval's trace-analyst registry as a SurfaceProposer (prompt-tier). */
|
|
2538
5953
|
declare function traceAnalystProposer(opts: TraceAnalystProposerOptions): SurfaceProposer;
|
|
2539
5954
|
|
|
5955
|
+
/**
|
|
5956
|
+
* Reporting helpers — production summaries and paper-quality figures — sit alongside `reporter.ts` rather
|
|
5957
|
+
* than replacing it.
|
|
5958
|
+
*
|
|
5959
|
+
* Three artefacts:
|
|
5960
|
+
*
|
|
5961
|
+
* - `summaryTable` Markdown table of per-candidate means,
|
|
5962
|
+
* 95% bootstrap CIs, BH-adjusted Wilcoxon
|
|
5963
|
+
* p-values, and Cohen's d versus a
|
|
5964
|
+
* comparator candidate.
|
|
5965
|
+
* - `paretoChart` Abstract spec for a cost vs quality
|
|
5966
|
+
* scatter, with gate decisions overlaid.
|
|
5967
|
+
* Returns numbers + labels — caller
|
|
5968
|
+
* chooses the plotting library.
|
|
5969
|
+
* - `gainHistogram`
|
|
5970
|
+
* Per-item paired holdout deltas as a
|
|
5971
|
+
* histogram spec (bins + counts + median +
|
|
5972
|
+
* CI). Same "data, not images" contract.
|
|
5973
|
+
*
|
|
5974
|
+
* The figure types are PlotSpecs — JSON-friendly, library-agnostic.
|
|
5975
|
+
* They aren't React components and they aren't PNGs; they are
|
|
5976
|
+
* what you'd hand to vega-lite, plotly, matplotlib, or your own
|
|
5977
|
+
* Canvas renderer to draw the actual figure.
|
|
5978
|
+
*/
|
|
5979
|
+
|
|
5980
|
+
interface ParetoPoint {
|
|
5981
|
+
candidateId: string;
|
|
5982
|
+
/** Mean USD cost per run on the chosen split. */
|
|
5983
|
+
cost: number;
|
|
5984
|
+
/** Mean score on the chosen split. */
|
|
5985
|
+
quality: number;
|
|
5986
|
+
/** Number of runs that informed this point. */
|
|
5987
|
+
n: number;
|
|
5988
|
+
/** Whether this candidate is on the Pareto frontier — high
|
|
5989
|
+
* quality, low cost, no dominator. */
|
|
5990
|
+
onFrontier: boolean;
|
|
5991
|
+
/** Optional gate verdict for this candidate, if a `GateDecision`
|
|
5992
|
+
* for it was passed in. */
|
|
5993
|
+
gate?: 'promote' | 'reject_few_runs' | 'reject_negative_delta' | 'reject_overfit_gap' | null;
|
|
5994
|
+
}
|
|
5995
|
+
interface ParetoFigureSpec {
|
|
5996
|
+
kind: 'pareto-cost-quality';
|
|
5997
|
+
split: 'search' | 'holdout';
|
|
5998
|
+
points: ParetoPoint[];
|
|
5999
|
+
axes: {
|
|
6000
|
+
x: 'costUsd';
|
|
6001
|
+
y: 'score';
|
|
6002
|
+
};
|
|
6003
|
+
}
|
|
6004
|
+
interface GainDistributionBin {
|
|
6005
|
+
/** Inclusive lower edge. */
|
|
6006
|
+
lo: number;
|
|
6007
|
+
/** Exclusive upper edge (or inclusive if it's the last bin). */
|
|
6008
|
+
hi: number;
|
|
6009
|
+
/** Number of pairs whose delta lands in this bin. */
|
|
6010
|
+
count: number;
|
|
6011
|
+
}
|
|
6012
|
+
|
|
6013
|
+
/**
|
|
6014
|
+
* # InsightReport — the rigorous decision packet for any set of agent runs.
|
|
6015
|
+
*
|
|
6016
|
+
* Returned by `analyzeRuns()` and embedded in `SelfImproveResult.insight` +
|
|
6017
|
+
* the hosted-tier `EvalRunEvent.insightReport`. One shape across two surfaces:
|
|
6018
|
+
*
|
|
6019
|
+
* - **Customer who has a closed loop** (`selfImprove`): the report ships
|
|
6020
|
+
* with the loop output. Their dashboard renders ship/hold + lift CI +
|
|
6021
|
+
* calibration + cluster + Pareto in one packet.
|
|
6022
|
+
* - **Customer who has observed runs but no loop** (`analyzeRuns` directly):
|
|
6023
|
+
* same packet from a `RunRecord[]` they already have — production traces,
|
|
6024
|
+
* approve/reject corpus, CSV gold set.
|
|
6025
|
+
*
|
|
6026
|
+
* Every field is optional except the distributional summary — fields are
|
|
6027
|
+
* populated when the input data supports them:
|
|
6028
|
+
*
|
|
6029
|
+
* - `lift` requires both baseline and candidate splits to be present.
|
|
6030
|
+
* - `interRater` requires multi-rater feedback (≥2 raters per run).
|
|
6031
|
+
* - `judges` populates per-judge stats only when the run records carry
|
|
6032
|
+
* `outcome.judgeScores`.
|
|
6033
|
+
* - `failureClusters` requires the optional `analystRegistry` to be wired.
|
|
6034
|
+
* - `contamination` requires canary scenarios to be passed in.
|
|
6035
|
+
* - `outcomeCorrelation` requires a downstream outcome signal.
|
|
6036
|
+
* - `sequential` requires the run set to be ordered (treats them as a
|
|
6037
|
+
* stream and emits an anytime-valid interim decision).
|
|
6038
|
+
*
|
|
6039
|
+
* Consumers read the `recommendations` array first — that's the
|
|
6040
|
+
* actionable layer, ranked by priority. The numeric sections back it up.
|
|
6041
|
+
*/
|
|
6042
|
+
|
|
6043
|
+
interface InsightReport {
|
|
6044
|
+
/** Number of runs analyzed. */
|
|
6045
|
+
n: number;
|
|
6046
|
+
/** Runtime facts carried by the run records. These describe execution,
|
|
6047
|
+
* not task quality: duration, queueing, token categories, models, and
|
|
6048
|
+
* explicitly recorded failures. */
|
|
6049
|
+
execution: ExecutionInsight;
|
|
6050
|
+
/** Composite-score distribution across all runs. Always present. */
|
|
6051
|
+
composite: ScalarDistribution;
|
|
6052
|
+
/** Per-dimension distributions for every dimension that appeared in any
|
|
6053
|
+
* run's judge scores. Empty when no judge scores were recorded. */
|
|
6054
|
+
perDimension: Record<string, ScalarDistribution>;
|
|
6055
|
+
/** Cost/quality distribution and Pareto frontier. */
|
|
6056
|
+
costQuality: {
|
|
6057
|
+
cost: ScalarDistribution;
|
|
6058
|
+
pareto: ParetoFigureSpec;
|
|
6059
|
+
/** Cost source coverage. `uncaptured` rows are excluded from the USD
|
|
6060
|
+
* distribution and Pareto chart; observed and estimated totals remain
|
|
6061
|
+
* separate so reports never present estimates as billed spend. */
|
|
6062
|
+
provenance?: CostProvenanceSummary;
|
|
6063
|
+
/** Set when the cost/quality view is degraded because the input data
|
|
6064
|
+
* doesn't fully support it — e.g. all `costUsd` were zero, or only a
|
|
6065
|
+
* single candidate appears (so the Pareto is a single point). The
|
|
6066
|
+
* named fields name the degraded sub-view, free-text the reason. */
|
|
6067
|
+
degraded?: {
|
|
6068
|
+
cost?: string;
|
|
6069
|
+
pareto?: string;
|
|
6070
|
+
};
|
|
6071
|
+
};
|
|
6072
|
+
/** Per-judge calibration + bias detection. Populated for every judge name
|
|
6073
|
+
* that appears in `outcome.judgeScores`. Bias fields require either a
|
|
6074
|
+
* gold reference or multi-rater data. */
|
|
6075
|
+
judges: Record<string, JudgeInsight>;
|
|
6076
|
+
/** Inter-rater agreement when multiple judges scored the same runs.
|
|
6077
|
+
* Includes pairwise kappa and the specific run ids where raters
|
|
6078
|
+
* disagree — the cases worth a human meeting. */
|
|
6079
|
+
interRater?: InterRaterInsight;
|
|
6080
|
+
/** Pairwise lift (baseline → candidate) with bootstrap CI. Present when
|
|
6081
|
+
* `RunRecord.splitTag` includes both `holdout` and search/dev splits,
|
|
6082
|
+
* or when caller passes an explicit baseline/candidate split. */
|
|
6083
|
+
lift?: LiftInsight;
|
|
6084
|
+
/** Failure clusters with exemplars. Populated when an AnalystRegistry
|
|
6085
|
+
* is wired in `analyzeRuns({ analyst })`. */
|
|
6086
|
+
failureClusters?: FailureClusterInsight;
|
|
6087
|
+
/** Canary leak count + holdout audit status. Populated when canary
|
|
6088
|
+
* scenarios are passed in. */
|
|
6089
|
+
contamination?: ContaminationInsight;
|
|
6090
|
+
/** Correlation between judge composite and a downstream outcome the
|
|
6091
|
+
* caller supplies (engagement, revenue, downstream pass rate, etc.).
|
|
6092
|
+
* When present, the optional reward model is the model that maps
|
|
6093
|
+
* judge scores → predicted outcome. */
|
|
6094
|
+
outcomeCorrelation?: OutcomeCorrelationInsight;
|
|
6095
|
+
/** Aggregate release-readiness summary. A consumer needing the full
|
|
6096
|
+
* substrate `ReleaseConfidenceScorecard` (SLO-axis evaluation,
|
|
6097
|
+
* ActionableSideInfo bag) calls `evaluateReleaseConfidence()` directly;
|
|
6098
|
+
* this summary captures the analyzeRuns-derived axes. */
|
|
6099
|
+
release: ReleaseSummary;
|
|
6100
|
+
/** Delta vs a prior period when `baselineRuns` is passed. Per-metric
|
|
6101
|
+
* current vs baseline with Welch CI + Cohen's d + significance flag.
|
|
6102
|
+
* Answers "did my last change help?" — the customer-conversion question.
|
|
6103
|
+
* Surfaced metrics: composite, cost, duration, tokenUsage, plus any
|
|
6104
|
+
* per-dimension judge metric present in both windows. */
|
|
6105
|
+
priorPeriodComparison?: PriorPeriodComparison;
|
|
6106
|
+
/** Model-free failure-mode breakdown from `RunRecord.failureMode`, ranked
|
|
6107
|
+
* by count descending. Present when any run carries a `failureMode`.
|
|
6108
|
+
* Complements `failureClusters` (LLM-semantic) with the structured tags
|
|
6109
|
+
* the harness already recorded — actionable with no analyst wired. */
|
|
6110
|
+
failureModes?: FailureModeTally[];
|
|
6111
|
+
/** Top-N actionable recommendations, ranked by priority. The packet's
|
|
6112
|
+
* human-readable layer; the numeric sections are the evidence. */
|
|
6113
|
+
recommendations: Recommendation[];
|
|
6114
|
+
}
|
|
6115
|
+
interface CostProvenanceSummary {
|
|
6116
|
+
observed: {
|
|
6117
|
+
n: number;
|
|
6118
|
+
totalUsd: number;
|
|
6119
|
+
};
|
|
6120
|
+
estimated: {
|
|
6121
|
+
n: number;
|
|
6122
|
+
totalUsd: number;
|
|
6123
|
+
};
|
|
6124
|
+
uncaptured: {
|
|
6125
|
+
n: number;
|
|
6126
|
+
};
|
|
6127
|
+
knownFraction: number;
|
|
6128
|
+
}
|
|
6129
|
+
interface ExecutionInsight {
|
|
6130
|
+
/** End-to-end wall time for every run. */
|
|
6131
|
+
durationMs: ScalarDistribution;
|
|
6132
|
+
/** Queue time for the subset of runs that recorded it. */
|
|
6133
|
+
queueMs: ScalarDistribution;
|
|
6134
|
+
/** Token distributions plus corpus totals. Optional token categories use
|
|
6135
|
+
* distribution `n` to disclose how many runs recorded that category. */
|
|
6136
|
+
tokenUsage: TokenUsageInsight;
|
|
6137
|
+
/** Usage reported only by orchestration or agent aggregate spans.
|
|
6138
|
+
* Kept separate because it may duplicate model-call telemetry in other traces. */
|
|
6139
|
+
aggregateUsage: {
|
|
6140
|
+
runs: number;
|
|
6141
|
+
tokenUsage: TokenUsageInsight;
|
|
6142
|
+
costUsd: ScalarDistribution;
|
|
6143
|
+
totalCostUsd: number;
|
|
6144
|
+
};
|
|
6145
|
+
/** Stable model counts, largest cohort first. */
|
|
6146
|
+
models: Array<{
|
|
6147
|
+
model: string;
|
|
6148
|
+
runs: number;
|
|
6149
|
+
}>;
|
|
6150
|
+
/** Model-call coverage. `events` is available only from producers that
|
|
6151
|
+
* record `outcome.raw.llm_span_count`; `runs` also recognizes non-zero
|
|
6152
|
+
* token usage from other producers. */
|
|
6153
|
+
modelCalls: {
|
|
6154
|
+
runs: number;
|
|
6155
|
+
events: number;
|
|
6156
|
+
reportingRuns: number;
|
|
6157
|
+
};
|
|
6158
|
+
/** Failure counts remain separate from outcome scores. `reportedErrorEvents`
|
|
6159
|
+
* sums `outcome.raw.error_span_count` only where a producer supplied it. */
|
|
6160
|
+
failures: {
|
|
6161
|
+
runs: number;
|
|
6162
|
+
fraction: number;
|
|
6163
|
+
reportedErrorEvents: number;
|
|
6164
|
+
reportingRuns: number;
|
|
6165
|
+
};
|
|
6166
|
+
}
|
|
6167
|
+
interface TokenUsageInsight {
|
|
6168
|
+
input: ScalarDistribution;
|
|
6169
|
+
output: ScalarDistribution;
|
|
6170
|
+
reasoning: ScalarDistribution;
|
|
6171
|
+
cached: ScalarDistribution;
|
|
6172
|
+
cacheWrite: ScalarDistribution;
|
|
6173
|
+
totals: {
|
|
6174
|
+
input: number;
|
|
6175
|
+
output: number;
|
|
6176
|
+
reasoning: number;
|
|
6177
|
+
cached: number;
|
|
6178
|
+
cacheWrite: number;
|
|
6179
|
+
};
|
|
6180
|
+
}
|
|
6181
|
+
/** Distributional summary of a scalar-valued metric. */
|
|
6182
|
+
interface ScalarDistribution {
|
|
6183
|
+
/** Sample count after dropping non-finite values. */
|
|
6184
|
+
n: number;
|
|
6185
|
+
mean: number;
|
|
6186
|
+
p50: number;
|
|
6187
|
+
p95: number;
|
|
6188
|
+
stddev: number;
|
|
6189
|
+
min: number;
|
|
6190
|
+
max: number;
|
|
6191
|
+
/** Histogram bins using `agent-eval`'s `gainHistogram` primitive. */
|
|
6192
|
+
histogram: GainDistributionBin[];
|
|
6193
|
+
/** Worst-N runs by score, ascending. Populated for the composite
|
|
6194
|
+
* distribution so the report names the runs a customer should
|
|
6195
|
+
* inspect first. Undefined when the distribution was computed from a
|
|
6196
|
+
* raw value list with no run identity (e.g. cost). */
|
|
6197
|
+
tailRuns?: Array<{
|
|
6198
|
+
runId: string;
|
|
6199
|
+
score: number;
|
|
6200
|
+
}>;
|
|
6201
|
+
}
|
|
6202
|
+
interface JudgeInsight {
|
|
6203
|
+
/** Number of times this judge scored a run. */
|
|
6204
|
+
n: number;
|
|
6205
|
+
/** Mean composite over this judge's runs. */
|
|
6206
|
+
meanScore: number;
|
|
6207
|
+
/** Calibration against a gold reference, when provided. Cohen's κ for
|
|
6208
|
+
* binary thresholding + continuous agreement metrics. */
|
|
6209
|
+
calibration?: ContinuousAgreement;
|
|
6210
|
+
/** Positional bias — when the judge sees options in different orders,
|
|
6211
|
+
* do its preferences track the content or the position? */
|
|
6212
|
+
positionalBias?: number;
|
|
6213
|
+
/** Self-preference — when the judge sees its own model's output vs a
|
|
6214
|
+
* competitor, does it over-pick its own? */
|
|
6215
|
+
selfPreference?: number;
|
|
6216
|
+
/** Verbosity bias — does the judge reward longer outputs regardless of
|
|
6217
|
+
* quality? */
|
|
6218
|
+
verbosityBias?: number;
|
|
6219
|
+
}
|
|
6220
|
+
interface InterRaterInsight {
|
|
6221
|
+
/** Number of raters whose scores were aggregated. */
|
|
6222
|
+
raters: number;
|
|
6223
|
+
/** Number of runs every rater scored. */
|
|
6224
|
+
jointlyRated: number;
|
|
6225
|
+
/** Cohen's κ averaged across rater pairs. */
|
|
6226
|
+
kappa: number;
|
|
6227
|
+
/** Pairwise κ per rater pair (key = `"raterA::raterB"`). */
|
|
6228
|
+
perPair: Record<string, number>;
|
|
6229
|
+
/** Run ids where raters disagree the most — the high-value triage list. */
|
|
6230
|
+
disagreementCases: Array<{
|
|
6231
|
+
runId: string;
|
|
6232
|
+
ratings: Array<{
|
|
6233
|
+
rater: string;
|
|
6234
|
+
score: number;
|
|
6235
|
+
}>;
|
|
6236
|
+
range: number;
|
|
6237
|
+
}>;
|
|
6238
|
+
}
|
|
6239
|
+
interface LiftInsight {
|
|
6240
|
+
baselineMean: number;
|
|
6241
|
+
candidateMean: number;
|
|
6242
|
+
/** Candidate − baseline. */
|
|
6243
|
+
delta: number;
|
|
6244
|
+
/** Lower / upper bound of bootstrap CI on the delta. */
|
|
6245
|
+
ci95: [number, number];
|
|
6246
|
+
/** Paired-t-test p-value. */
|
|
6247
|
+
pValue: number;
|
|
6248
|
+
/** Number of paired observations. */
|
|
6249
|
+
n: number;
|
|
6250
|
+
/** Cohen's d for the delta. */
|
|
6251
|
+
cohensD: number;
|
|
6252
|
+
/** Minimum detectable effect at current n, 80% power. */
|
|
6253
|
+
mde: number;
|
|
6254
|
+
/** Sample size needed to detect the observed delta at 80% power. */
|
|
6255
|
+
requiredN: number;
|
|
6256
|
+
}
|
|
6257
|
+
interface FailureClusterInsight {
|
|
6258
|
+
/** All clusters identified by the registry, ranked by share descending. */
|
|
6259
|
+
clusters: Array<{
|
|
6260
|
+
id: string;
|
|
6261
|
+
name: string;
|
|
6262
|
+
/** Fraction of failed runs in this cluster, 0..1. */
|
|
6263
|
+
share: number;
|
|
6264
|
+
/** Exemplar `runId`s (≤ 5) the consumer can drill into. */
|
|
6265
|
+
exemplars: string[];
|
|
6266
|
+
/** Short LLM-generated suggested fix when the registry supports it. */
|
|
6267
|
+
suggestedFix?: string;
|
|
6268
|
+
}>;
|
|
6269
|
+
totalFailures: number;
|
|
6270
|
+
}
|
|
6271
|
+
/** Model-free failure breakdown over the structured `RunRecord.failureMode`
|
|
6272
|
+
* enum. Unlike `failureClusters` (semantic, requires an LLM analyst), this
|
|
6273
|
+
* is computed directly from the tags the harness already recorded — so a
|
|
6274
|
+
* customer ingesting one batch with no judge/analyst still learns which
|
|
6275
|
+
* named failure dominates. */
|
|
6276
|
+
interface FailureModeTally {
|
|
6277
|
+
/** The `failureMode` tag. */
|
|
6278
|
+
mode: string;
|
|
6279
|
+
/** Number of runs carrying this tag. */
|
|
6280
|
+
count: number;
|
|
6281
|
+
/** Share of the whole corpus, 0..1. */
|
|
6282
|
+
share: number;
|
|
6283
|
+
}
|
|
6284
|
+
interface ContaminationInsight {
|
|
6285
|
+
/** Canary phrases that leaked into outputs. */
|
|
6286
|
+
leaks: number;
|
|
6287
|
+
/** Holdout audit verdict — did any holdout-tagged run end up in the
|
|
6288
|
+
* search/dev pool, or vice versa? */
|
|
6289
|
+
holdoutAuditPassed: boolean;
|
|
6290
|
+
details?: Array<{
|
|
6291
|
+
runId: string;
|
|
6292
|
+
canary: string;
|
|
6293
|
+
matched: string;
|
|
6294
|
+
}>;
|
|
6295
|
+
}
|
|
6296
|
+
interface OutcomeCorrelationInsight {
|
|
6297
|
+
/** What outcome the consumer is correlating against (e.g.
|
|
6298
|
+
* `'engagement_rate'`, `'approval_rate'`, `'downstream_pass'`). */
|
|
6299
|
+
metric: string;
|
|
6300
|
+
/** Number of (run, outcome) pairs used. */
|
|
6301
|
+
n: number;
|
|
6302
|
+
/** Pearson correlation between composite score and outcome. */
|
|
6303
|
+
pearson: number;
|
|
6304
|
+
/** Spearman rank correlation — robust to monotonic non-linearity. */
|
|
6305
|
+
spearman: number;
|
|
6306
|
+
/** When present, the simple linear reward model fit to the data. */
|
|
6307
|
+
rewardModel?: {
|
|
6308
|
+
intercept: number;
|
|
6309
|
+
slope: number;
|
|
6310
|
+
r2: number;
|
|
6311
|
+
};
|
|
6312
|
+
}
|
|
6313
|
+
interface ReleaseSummary {
|
|
6314
|
+
/** Overall verdict across axes — fail if any axis fails, else warn if any
|
|
6315
|
+
* warns, else pass. */
|
|
6316
|
+
status: 'pass' | 'warn' | 'fail';
|
|
6317
|
+
axes: Array<{
|
|
6318
|
+
name: 'quality-lift' | 'contamination' | 'composite-distribution';
|
|
6319
|
+
status: 'pass' | 'warn' | 'fail';
|
|
6320
|
+
detail: string;
|
|
6321
|
+
}>;
|
|
6322
|
+
/** Free-form issues surfaced beyond the standard axes. Empty by default;
|
|
6323
|
+
* consumers can post-process to populate. */
|
|
6324
|
+
issues: string[];
|
|
6325
|
+
}
|
|
6326
|
+
interface MetricDelta {
|
|
6327
|
+
/** Current-period mean. */
|
|
6328
|
+
current: number;
|
|
6329
|
+
/** Baseline-period mean. */
|
|
6330
|
+
baseline: number;
|
|
6331
|
+
/** current - baseline. Positive means improved (or, for cost/duration,
|
|
6332
|
+
* the consumer-side interpretation: "higher current" — semantic
|
|
6333
|
+
* direction depends on the metric). */
|
|
6334
|
+
delta: number;
|
|
6335
|
+
/** Welch 95% confidence interval on the delta. Two-sample, unpaired —
|
|
6336
|
+
* the baseline and current run sets may have different scenarios. */
|
|
6337
|
+
ci95: [number, number];
|
|
6338
|
+
/** Welch t-test p-value (two-sided). */
|
|
6339
|
+
pValue: number;
|
|
6340
|
+
/** Cohen's d (pooled stddev). Effect size, signed. */
|
|
6341
|
+
cohensD: number;
|
|
6342
|
+
/** Sample sizes. */
|
|
6343
|
+
baselineN: number;
|
|
6344
|
+
currentN: number;
|
|
6345
|
+
/** True when p < 0.05 AND |d| >= 0.2 (small-effect threshold). The
|
|
6346
|
+
* conjunction prevents large-effect-but-noisy and significant-but-
|
|
6347
|
+
* tiny from triggering recommendations. */
|
|
6348
|
+
significant: boolean;
|
|
6349
|
+
}
|
|
6350
|
+
interface PriorPeriodComparison {
|
|
6351
|
+
/** Sample counts. */
|
|
6352
|
+
baselineN: number;
|
|
6353
|
+
currentN: number;
|
|
6354
|
+
/** Optional human-readable label — "vs prior 7 days", "vs v3 release". */
|
|
6355
|
+
windowLabel?: string;
|
|
6356
|
+
/** Every metric we could compare. Keys: 'composite', 'cost', 'duration',
|
|
6357
|
+
* 'tokenUsage' for always-present ones; per-dimension keys when both
|
|
6358
|
+
* windows have judge scores on the same dimension. */
|
|
6359
|
+
metrics: Record<string, MetricDelta>;
|
|
6360
|
+
/** Metric names where current is significantly WORSE than baseline.
|
|
6361
|
+
* Direction-aware: for cost/duration, higher current = worse. */
|
|
6362
|
+
regressedMetrics: string[];
|
|
6363
|
+
/** Metric names where current is significantly BETTER than baseline. */
|
|
6364
|
+
improvedMetrics: string[];
|
|
6365
|
+
}
|
|
6366
|
+
interface Recommendation {
|
|
6367
|
+
priority: 'critical' | 'high' | 'medium' | 'low';
|
|
6368
|
+
kind: 'ship' | 'hold' | 'investigate' | 'fix' | 'recalibrate' | 'expand-corpus';
|
|
6369
|
+
title: string;
|
|
6370
|
+
detail: string;
|
|
6371
|
+
/** Optional pointer back into the report for the evidence. */
|
|
6372
|
+
evidencePath?: string;
|
|
6373
|
+
}
|
|
6374
|
+
|
|
6375
|
+
/**
|
|
6376
|
+
* # Hosted-tier wire format — the schema that EVERY orchestrator (ours,
|
|
6377
|
+
* a partner's self-hosted one, a future open implementation) must accept.
|
|
6378
|
+
*
|
|
6379
|
+
* **Stability:** every type in this file is committed under semver. New
|
|
6380
|
+
* minors only ADD optional fields. Breaking changes mean a major bump
|
|
6381
|
+
* (`HostedWireVersion` literal increment).
|
|
6382
|
+
*
|
|
6383
|
+
* The wire format is two event streams in one transport:
|
|
6384
|
+
*
|
|
6385
|
+
* 1. **Eval-run events** (`POST /v1/ingest/eval-runs`). Posted when a
|
|
6386
|
+
* campaign / improvement-loop completes (or per-generation if
|
|
6387
|
+
* streaming). Carries the structured result + per-cell scores +
|
|
6388
|
+
* surface diffs the orchestrator stores for the dashboard.
|
|
6389
|
+
*
|
|
6390
|
+
* 2. **Trace spans** (`POST /v1/ingest/traces`). Standard OTLP-shaped
|
|
6391
|
+
* spans with a few additional attributes so the orchestrator can
|
|
6392
|
+
* pivot from eval-run → underlying execution. Compatible with any
|
|
6393
|
+
* OTel collector.
|
|
6394
|
+
*
|
|
6395
|
+
* Both endpoints are authenticated with a bearer token + a tenant id
|
|
6396
|
+
* header. Tenants isolate everything downstream of ingest; no tenant
|
|
6397
|
+
* ever sees another tenant's data.
|
|
6398
|
+
*/
|
|
6399
|
+
|
|
6400
|
+
declare const HOSTED_WIRE_VERSION: "2026-05-26.v1";
|
|
6401
|
+
type HostedWireVersion = typeof HOSTED_WIRE_VERSION;
|
|
6402
|
+
/** Lifecycle stages of an eval-run as the substrate reports them. */
|
|
6403
|
+
type EvalRunStatus = 'started' | 'baseline-complete' | 'generation-complete' | 'gate-decided' | 'finished' | 'errored';
|
|
6404
|
+
interface EvalRunCellScore {
|
|
6405
|
+
/** Stable scenario id from the consumer's scenario set. */
|
|
6406
|
+
scenarioId: string;
|
|
6407
|
+
/** Repetition index when reps > 1; 0 for the default. */
|
|
6408
|
+
rep: number;
|
|
6409
|
+
/** Composite score across all judges + dimensions for this cell. */
|
|
6410
|
+
compositeMean: number;
|
|
6411
|
+
/** Per-judge → per-dimension scores; null where the judge did not run. */
|
|
6412
|
+
dimensions: Record<string, Record<string, number>>;
|
|
6413
|
+
/** Per-cell error message if the dispatch threw. Null on success. */
|
|
6414
|
+
errorMessage?: string;
|
|
6415
|
+
}
|
|
6416
|
+
interface EvalRunGenerationSnapshot {
|
|
6417
|
+
/** Generation index. 0 is baseline. */
|
|
6418
|
+
index: number;
|
|
6419
|
+
/** Candidate surface fingerprint (stable hash) — pivot key into the
|
|
6420
|
+
* trace stream to fetch the underlying execution. */
|
|
6421
|
+
surfaceHash: string;
|
|
6422
|
+
/** The candidate surface itself. May be omitted to avoid PII when the
|
|
6423
|
+
* consumer prefers not to ship verbatim prompts. */
|
|
6424
|
+
surface?: MutableSurface;
|
|
6425
|
+
/** Per-cell scores for this generation. */
|
|
6426
|
+
cells: EvalRunCellScore[];
|
|
6427
|
+
/** Aggregate composite mean across all cells in this generation. */
|
|
6428
|
+
compositeMean: number;
|
|
6429
|
+
/** Total $ spent across this generation. */
|
|
6430
|
+
costUsd: number;
|
|
6431
|
+
/** Wall-clock duration of this generation. */
|
|
6432
|
+
durationMs: number;
|
|
6433
|
+
}
|
|
6434
|
+
/**
|
|
6435
|
+
* The top-level eval-run event. One ingest call per logical eval-run;
|
|
6436
|
+
* generations stream in incrementally via repeated calls with the same
|
|
6437
|
+
* `runId`. The orchestrator deduplicates by `(runId, generation.index)`.
|
|
6438
|
+
*/
|
|
6439
|
+
interface EvalRunEvent {
|
|
6440
|
+
/** Stable run id (the substrate's `runId`). UUID or substrate-generated. */
|
|
6441
|
+
runId: string;
|
|
6442
|
+
/** Where this run was happening — derived from `RunCampaignOptions.runDir`. */
|
|
6443
|
+
runDir: string;
|
|
6444
|
+
/** ISO-8601 timestamp the substrate recorded the event. */
|
|
6445
|
+
timestamp: string;
|
|
6446
|
+
/** Lifecycle stage this event represents. */
|
|
6447
|
+
status: EvalRunStatus;
|
|
6448
|
+
/** Free-form consumer tags (env, branch, model id, etc.). Searchable. */
|
|
6449
|
+
labels: Record<string, string>;
|
|
6450
|
+
/** Baseline campaign snapshot. Present when status >= baseline-complete. */
|
|
6451
|
+
baseline?: EvalRunGenerationSnapshot;
|
|
6452
|
+
/** Per-generation snapshots. Streams in; orchestrator appends. */
|
|
6453
|
+
generations: EvalRunGenerationSnapshot[];
|
|
6454
|
+
/** Final gate decision. Present when status >= gate-decided. */
|
|
6455
|
+
gateDecision?: GateDecision;
|
|
6456
|
+
/** Held-out lift = winner-on-holdout - baseline-on-holdout. */
|
|
6457
|
+
holdoutLift?: number;
|
|
6458
|
+
/** Total $ spent across baseline + every generation. */
|
|
6459
|
+
totalCostUsd: number;
|
|
6460
|
+
/** Total wall-clock duration. */
|
|
6461
|
+
totalDurationMs: number;
|
|
6462
|
+
/** Error message if status === 'errored'. */
|
|
6463
|
+
errorMessage?: string;
|
|
6464
|
+
/** Rigor packet emitted alongside the run — distributional summary,
|
|
6465
|
+
* paired-bootstrap lift CI, judge stats, inter-rater agreement,
|
|
6466
|
+
* contamination check, failure clusters (when an analyst is wired),
|
|
6467
|
+
* outcome correlation (when downstream signal is supplied), and the
|
|
6468
|
+
* recommendations the dashboard surfaces verbatim. Additive; older
|
|
6469
|
+
* clients that don't know about this field continue to work. */
|
|
6470
|
+
insightReport?: InsightReport;
|
|
6471
|
+
}
|
|
6472
|
+
/**
|
|
6473
|
+
* OTel-shape span with a few additional attributes for eval-run pivoting.
|
|
6474
|
+
* Compatible with any OTLP collector — `name`, `traceId`, `spanId`,
|
|
6475
|
+
* `startTimeUnixNano`, `endTimeUnixNano`, `attributes` are stock OTel.
|
|
6476
|
+
*/
|
|
6477
|
+
interface TraceSpanEvent {
|
|
6478
|
+
traceId: string;
|
|
6479
|
+
spanId: string;
|
|
6480
|
+
parentSpanId?: string;
|
|
6481
|
+
name: string;
|
|
6482
|
+
startTimeUnixNano: number;
|
|
6483
|
+
endTimeUnixNano: number;
|
|
6484
|
+
attributes: Record<string, string | number | boolean>;
|
|
6485
|
+
events?: Array<{
|
|
6486
|
+
timeUnixNano: number;
|
|
6487
|
+
name: string;
|
|
6488
|
+
attributes?: Record<string, string | number | boolean>;
|
|
6489
|
+
}>;
|
|
6490
|
+
status?: {
|
|
6491
|
+
code: 'OK' | 'ERROR' | 'UNSET';
|
|
6492
|
+
message?: string;
|
|
6493
|
+
};
|
|
6494
|
+
/** Pivot back into the eval-run stream. */
|
|
6495
|
+
'tangle.runId'?: string;
|
|
6496
|
+
/** Pivot to the specific generation. */
|
|
6497
|
+
'tangle.generation'?: number;
|
|
6498
|
+
/** Pivot to the specific cell. */
|
|
6499
|
+
'tangle.cellId'?: string;
|
|
6500
|
+
/** Pivot to the specific scenario. */
|
|
6501
|
+
'tangle.scenarioId'?: string;
|
|
6502
|
+
}
|
|
6503
|
+
interface IngestResponse {
|
|
6504
|
+
/** Accepted events / spans count. */
|
|
6505
|
+
accepted: number;
|
|
6506
|
+
/** Rejected events with reasons (validation failures, dup idempotency key, etc.). */
|
|
6507
|
+
rejected: Array<{
|
|
6508
|
+
index: number;
|
|
6509
|
+
reason: string;
|
|
6510
|
+
}>;
|
|
6511
|
+
}
|
|
6512
|
+
|
|
6513
|
+
/**
|
|
6514
|
+
* # Hosted-tier ingest client.
|
|
6515
|
+
*
|
|
6516
|
+
* Ships eval-run events + trace spans to any orchestrator (ours, a
|
|
6517
|
+
* partner's self-hosted one, or a future open implementation) that
|
|
6518
|
+
* speaks the wire format in `./types.ts`.
|
|
6519
|
+
*
|
|
6520
|
+
* Three modes:
|
|
6521
|
+
* - **Ours:** point at `https://orchestrator.tangle.tools` (the host root —
|
|
6522
|
+
* the client appends the versioned `/v1/ingest/...` path itself; a trailing
|
|
6523
|
+
* `/v1` on the endpoint is tolerated and normalized away). We handle ingest
|
|
6524
|
+
* + storage + dashboard.
|
|
6525
|
+
* - **Self-hosted:** point at whatever URL runs the reference receiver
|
|
6526
|
+
* from `examples/hosted-ingest-server/`.
|
|
6527
|
+
* - **Off (default):** when `hostedTenant` is unset, nothing is sent.
|
|
6528
|
+
* Everything stays local.
|
|
6529
|
+
*/
|
|
6530
|
+
|
|
6531
|
+
interface HostedTenant {
|
|
6532
|
+
/** Orchestrator endpoint base URL (no trailing slash). Required. */
|
|
6533
|
+
endpoint: string;
|
|
6534
|
+
/** Bearer token issued by the orchestrator. Required. */
|
|
6535
|
+
apiKey: string;
|
|
6536
|
+
/** Tenant id — the orchestrator's primary key for this consumer. Required. */
|
|
6537
|
+
tenantId: string;
|
|
6538
|
+
/** Optional `fetch` override (auth wrappers, custom agent, test mocks). */
|
|
6539
|
+
fetchImpl?: typeof fetch;
|
|
6540
|
+
/** Per-call timeout in ms. Default 30s. */
|
|
6541
|
+
timeoutMs?: number;
|
|
6542
|
+
/** Retries on 5xx / network errors. Default 2. */
|
|
6543
|
+
retries?: number;
|
|
6544
|
+
}
|
|
6545
|
+
interface HostedClient {
|
|
6546
|
+
ingestEvalRun(event: EvalRunEvent, idempotencyKey?: string): Promise<IngestResponse>;
|
|
6547
|
+
ingestEvalRuns(events: EvalRunEvent[], idempotencyKey?: string): Promise<IngestResponse>;
|
|
6548
|
+
ingestTraces(spans: TraceSpanEvent[], idempotencyKey?: string): Promise<IngestResponse>;
|
|
6549
|
+
readonly tenant: HostedTenant;
|
|
6550
|
+
readonly wireVersion: HostedWireVersion;
|
|
6551
|
+
}
|
|
6552
|
+
|
|
6553
|
+
/**
|
|
6554
|
+
* Loop provenance — the durable, queryable record of WHAT a self-improvement
|
|
6555
|
+
* loop did and WHY, plus the OTel spans that let an OTLP collector pivot from
|
|
6556
|
+
* an eval-run to the underlying candidate→cell→gate→promote chain.
|
|
6557
|
+
*
|
|
6558
|
+
* Two artifacts, one source of truth:
|
|
6559
|
+
*
|
|
6560
|
+
* 1. `LoopProvenanceRecord` — a structured JSON record capturing every
|
|
6561
|
+
* candidate (surfaceHash + label + rationale + structured cause), its measured composite,
|
|
6562
|
+
* the gate decision + reasons + delta, the held-out lift, the explicit
|
|
6563
|
+
* baseline→candidate diff, and BACKEND PROVENANCE (the
|
|
6564
|
+
* `assertRealBackend` verdict + worker call count + model). This is the
|
|
6565
|
+
* ingestable audit artifact: the +lift recomputes from it, the "because
|
|
6566
|
+
* Z" rationale survives in it, and a stub backend is detectable from it.
|
|
6567
|
+
*
|
|
6568
|
+
* 2. `loopProvenanceSpans()` — the same chain emitted as OTLP-ingestable
|
|
6569
|
+
* `TraceSpanEvent`s, pivoted on the substrate's standard
|
|
6570
|
+
* `tangle.runId` / `tangle.scenarioId` / `tangle.cellId` /
|
|
6571
|
+
* `tangle.generation` attributes (the same pivots `/adapters/otel`
|
|
6572
|
+
* reads). The hosted `/v1/ingest/traces` endpoint receives the FULL loop,
|
|
6573
|
+
* not just the `cost.*` spans `runCampaign` already emits per cell.
|
|
6574
|
+
*
|
|
6575
|
+
* The record is built from the substrate's own loop result + the per-call
|
|
6576
|
+
* `RunRecord`s the worker emitted — no new measurement, no recomputation that
|
|
6577
|
+
* could drift from what the gate actually saw.
|
|
6578
|
+
*/
|
|
6579
|
+
|
|
6580
|
+
interface LoopProvenanceCandidate {
|
|
6581
|
+
/** Generation index this candidate was proposed in. */
|
|
6582
|
+
generation: number;
|
|
6583
|
+
/** 16-char loop-identity fingerprint (matches `GenerationCandidate.surfaceHash`). */
|
|
6584
|
+
surfaceHash: string;
|
|
6585
|
+
/** Full sha256 content hash — byte-identical-verifiable. */
|
|
6586
|
+
contentHash: string;
|
|
6587
|
+
/** Proposer label, when the proposer returned a `ProposedCandidate`. */
|
|
6588
|
+
label?: string;
|
|
6589
|
+
/** Proposer rationale — the "because Z". When the proposer returned a bare
|
|
6590
|
+
* surface (blind mutator) this is absent. */
|
|
6591
|
+
rationale?: string;
|
|
6592
|
+
/** Exact validated cause when the proposer emitted a structured record. */
|
|
6593
|
+
candidateRecord?: PolicyEditCandidateRecord;
|
|
6594
|
+
/** Exact complete incumbent this candidate mutated. */
|
|
6595
|
+
parentSurfaceHash: string;
|
|
6596
|
+
/** Search-split composite of the exact parent. */
|
|
6597
|
+
parentComposite: number;
|
|
6598
|
+
/** Search-split composite change relative to the exact parent. */
|
|
6599
|
+
observedDeltaFromParent?: number;
|
|
6600
|
+
/** Whether the candidate completed every designed cell and could be selected. */
|
|
6601
|
+
eligibleForPromotion: boolean;
|
|
6602
|
+
/** Designed-denominator receipt retained even for incomplete candidates. */
|
|
6603
|
+
coverage: NonNullable<GenerationCandidate['coverage']>;
|
|
6604
|
+
/** Mean composite this candidate scored on the search split. */
|
|
6605
|
+
composite: number;
|
|
6606
|
+
/** Whether this candidate was promoted out of its generation. */
|
|
6607
|
+
promoted: boolean;
|
|
6608
|
+
}
|
|
6609
|
+
interface LoopProvenanceBackend {
|
|
6610
|
+
/** `assertRealBackend`-grade verdict over the worker call records. */
|
|
6611
|
+
verdict: 'real' | 'mixed' | 'stub';
|
|
6612
|
+
/** Number of worker LLM calls captured (the audit's "worker call count"). */
|
|
6613
|
+
workerCallCount: number;
|
|
6614
|
+
/** Distinct model ids observed across worker calls. */
|
|
6615
|
+
models: string[];
|
|
6616
|
+
totalInputTokens: number;
|
|
6617
|
+
totalOutputTokens: number;
|
|
6618
|
+
totalCostUsd: number;
|
|
6619
|
+
}
|
|
6620
|
+
/**
|
|
6621
|
+
* The durable provenance record. Aligns to the hosted `EvalRunEvent` path but
|
|
6622
|
+
* ADDS the rationale + the explicit baseline→candidate diff (both omitted from
|
|
6623
|
+
* the bare hosted event) + backend provenance.
|
|
6624
|
+
*/
|
|
6625
|
+
interface LoopProvenanceRecord {
|
|
6626
|
+
schema: 'tangle.loop-provenance.v3';
|
|
6627
|
+
runId: string;
|
|
6628
|
+
runDir: string;
|
|
6629
|
+
timestamp: string;
|
|
6630
|
+
/** Baseline + winner surface content hashes — distinguishable, byte-verifiable. */
|
|
6631
|
+
baselineContentHash: string;
|
|
6632
|
+
winnerContentHash: string;
|
|
6633
|
+
/** Proposer label/rationale for the promoted change. Absent ⇒ winner == baseline. */
|
|
6634
|
+
winnerLabel?: string;
|
|
6635
|
+
winnerRationale?: string;
|
|
6636
|
+
/** The explicit baseline→winner unified diff the gate decided on. */
|
|
6637
|
+
diff: string;
|
|
6638
|
+
/** Every candidate across every generation, with its rationale and structured cause. */
|
|
6639
|
+
candidates: LoopProvenanceCandidate[];
|
|
6640
|
+
/** Baseline composite on the search split that generated the candidates. */
|
|
6641
|
+
baselineSearchComposite: number;
|
|
6642
|
+
/** The gate verdict — decision + reasons + contributing gates + delta. */
|
|
6643
|
+
gate: {
|
|
6644
|
+
decision: GateDecision;
|
|
6645
|
+
reasons: string[];
|
|
6646
|
+
delta?: number;
|
|
6647
|
+
contributingGates: Array<{
|
|
6648
|
+
name: string;
|
|
6649
|
+
passed: boolean;
|
|
6650
|
+
}>;
|
|
6651
|
+
};
|
|
6652
|
+
/** baseline-on-holdout composite mean. */
|
|
6653
|
+
baselineHoldoutComposite: number;
|
|
6654
|
+
/** winner-on-holdout composite mean. */
|
|
6655
|
+
winnerHoldoutComposite: number;
|
|
6656
|
+
/** winnerHoldout - baselineHoldout — RECOMPUTABLE from this record. */
|
|
6657
|
+
heldOutLift: number;
|
|
6658
|
+
/** Backend provenance: stub-vs-real verdict + worker call count + models. */
|
|
6659
|
+
backend: LoopProvenanceBackend;
|
|
6660
|
+
totalCostUsd: number;
|
|
6661
|
+
totalDurationMs: number;
|
|
6662
|
+
}
|
|
6663
|
+
interface BuildLoopProvenanceArgs<TArtifact, TScenario extends Scenario> {
|
|
6664
|
+
runId: string;
|
|
6665
|
+
runDir: string;
|
|
6666
|
+
timestamp: string;
|
|
6667
|
+
baselineSurface: MutableSurface;
|
|
6668
|
+
winnerSurface: MutableSurface;
|
|
6669
|
+
winnerLabel?: string;
|
|
6670
|
+
winnerRationale?: string;
|
|
6671
|
+
diff: string;
|
|
6672
|
+
/** Baseline composite on the search split, distinct from holdout scoring. */
|
|
6673
|
+
baselineSearchComposite: number;
|
|
6674
|
+
/** Per-generation candidate records straight off the loop result. */
|
|
6675
|
+
generations: Array<{
|
|
6676
|
+
generationIndex: number;
|
|
6677
|
+
candidates: GenerationCandidate[];
|
|
6678
|
+
promoted: string[];
|
|
6679
|
+
/** Surfaces measured this generation, keyed by surface hash so the content
|
|
6680
|
+
* hash can be computed and the loop identity rechecked from real bytes. */
|
|
6681
|
+
surfaces: Array<{
|
|
6682
|
+
surfaceHash: string;
|
|
6683
|
+
surface: MutableSurface;
|
|
6684
|
+
}>;
|
|
6685
|
+
}>;
|
|
6686
|
+
gate: GateResult;
|
|
6687
|
+
baselineOnHoldout: CampaignResult<TArtifact, TScenario>;
|
|
6688
|
+
winnerOnHoldout: CampaignResult<TArtifact, TScenario>;
|
|
6689
|
+
/** Worker call records — the source for backend provenance. */
|
|
6690
|
+
workerRecords: ReadonlyArray<RunRecord>;
|
|
6691
|
+
totalCostUsd: number;
|
|
6692
|
+
totalDurationMs: number;
|
|
6693
|
+
}
|
|
6694
|
+
/** Build the durable provenance record from a completed loop result. */
|
|
6695
|
+
declare function buildLoopProvenanceRecord<TArtifact, TScenario extends Scenario>(args: BuildLoopProvenanceArgs<TArtifact, TScenario>): LoopProvenanceRecord;
|
|
6696
|
+
/**
|
|
6697
|
+
* Build the loop's OTLP-ingestable spans from a provenance record. One root
|
|
6698
|
+
* span per loop (`tangle.runId`), one span per generation, one span per
|
|
6699
|
+
* candidate (carrying its surfaceHash + label), and one span for the gate
|
|
6700
|
+
* decision (carrying reasons + delta + lift). Candidate + gate spans pivot on
|
|
6701
|
+
* the same `tangle.runId` / `tangle.generation` attributes `/adapters/otel`
|
|
6702
|
+
* reads, so the hosted collector reconstructs the full tree.
|
|
6703
|
+
*
|
|
6704
|
+
* Times are synthesized monotonically off a single base so the span tree is
|
|
6705
|
+
* orderable; the substrate does not retain per-candidate wall-clock starts.
|
|
6706
|
+
*/
|
|
6707
|
+
declare function loopProvenanceSpans(record: LoopProvenanceRecord, opts?: {
|
|
6708
|
+
baseTimeMs?: number;
|
|
6709
|
+
}): TraceSpanEvent[];
|
|
6710
|
+
/** Canonical durable paths under the run dir. */
|
|
6711
|
+
declare function provenanceRecordPath(runDir: string): string;
|
|
6712
|
+
/**
|
|
6713
|
+
* Canonical path for the durable OTLP spans JSONL file under a loop run directory.
|
|
6714
|
+
*/
|
|
6715
|
+
declare function provenanceSpansPath(runDir: string): string;
|
|
6716
|
+
interface EmitLoopProvenanceResult {
|
|
6717
|
+
record: LoopProvenanceRecord;
|
|
6718
|
+
spans: TraceSpanEvent[];
|
|
6719
|
+
/** Absolute paths the record + spans were written to, when storage persists. */
|
|
6720
|
+
recordPath: string;
|
|
6721
|
+
spansPath: string;
|
|
6722
|
+
}
|
|
6723
|
+
interface EmitLoopProvenanceArgs<TArtifact, TScenario extends Scenario> extends BuildLoopProvenanceArgs<TArtifact, TScenario> {
|
|
6724
|
+
/** Storage the record + spans are written through. */
|
|
6725
|
+
storage: CampaignStorage;
|
|
6726
|
+
/** When set, the spans are also shipped to the hosted `/v1/ingest/traces`
|
|
6727
|
+
* endpoint so the collector receives the full loop, not just `cost.*`. */
|
|
6728
|
+
hostedClient?: HostedClient;
|
|
6729
|
+
}
|
|
6730
|
+
/**
|
|
6731
|
+
* Build the provenance record + OTel spans and persist them durably under the
|
|
6732
|
+
* run dir (and ship spans to a hosted collector when one is wired). Returns
|
|
6733
|
+
* both artifacts so the caller can assert on / re-derive from them.
|
|
6734
|
+
*
|
|
6735
|
+
* Fail-loud: the durable write throws on storage failure (a swallowed write is
|
|
6736
|
+
* exactly the "emitted but lost" failure this closes). The hosted span ship is
|
|
6737
|
+
* the one best-effort leg — its failure is logged, not thrown, so an offline
|
|
6738
|
+
* collector never fails the loop (the durable artifact is the source of truth).
|
|
6739
|
+
*/
|
|
6740
|
+
declare function emitLoopProvenance<TArtifact, TScenario extends Scenario>(args: EmitLoopProvenanceArgs<TArtifact, TScenario>): Promise<EmitLoopProvenanceResult>;
|
|
6741
|
+
|
|
2540
6742
|
/** The shared, out-of-repo root for campaign/benchmark run bundles. Keeping run
|
|
2541
6743
|
* outputs here means they never land in a repo working tree (no per-repo
|
|
2542
6744
|
* gitignore, no clutter, no accidental commits). Layout:
|
|
@@ -3112,4 +7314,4 @@ declare function verifyCodeSurface(surface: CodeSurface, worktreeDir?: string):
|
|
|
3112
7314
|
* identity against the checkout at `worktreeRef`. */
|
|
3113
7315
|
declare function resolveWorktreePath(surface: CodeSurface, worktreeDir?: string): string;
|
|
3114
7316
|
|
|
3115
|
-
export { type AcceptedEdit, type AceProposerOptions, type AnalystArtifact, type AnalystScenario, type AnalyzeCrossSurfaceInteractionsInput, type ApplySkillPatchResult, type BuildAnalystSurfaceDispatchOptions, type CampaignBreakdown, CampaignResult, CampaignRunPlan, CampaignStorage, CodeSurface, type CodeSurfaceVerification, type CompareProposersOptions, type CompositeProposerOptions, type CrossSurfaceAdditionDecision, type CrossSurfaceAdditionRejectionReason, type CrossSurfaceAttemptCompleteness, type CrossSurfaceBestSingleSelection, type CrossSurfaceBootstrapPolicy, type CrossSurfaceCandidate, type CrossSurfaceCandidateComparison, type CrossSurfaceCandidateEvidence, type CrossSurfaceCandidateOutcome, type CrossSurfaceCandidateSummary, type CrossSurfaceComponent, type CrossSurfaceComponentEvidence, type CrossSurfaceCompositionStep, type CrossSurfaceDistribution, type CrossSurfaceEligibility, type CrossSurfaceEvidenceBreakdown, type CrossSurfaceIneligibilityReason, type CrossSurfaceInteractionAwareSelection, type CrossSurfaceInteractionEffect, type CrossSurfaceInteractionPath, type CrossSurfaceInteractionReport, type CrossSurfaceInteractionTask, type CrossSurfaceNaiveStackSelection, type CrossSurfacePairCompatibility, type CrossSurfacePairEvidence, type CrossSurfacePairIncompatibilityReason, type CrossSurfacePairwiseEntry, type CrossSurfaceRankedSingle, type CrossSurfaceRelativeCost, type CrossSurfaceSelectionPolicy, type CrossSurfaceSelections, type CrossSurfaceTaskRow, DEFAULT_POLICY_EDIT_HISTORY_LIMITS, type DimensionRegression, type DiscriminationScore, DispatchContext, type EvalFixture, type EvalFixtureFile, type EvalFixtureLoadOptions, type EvalFixtureRunPlan, type EvalFixtureScenario, type EvalFixtureValidationMode, type FailureModeRecallJudgeOptions, type FapoAttributionSignals, type FapoEntryConfig, type FapoFailureCluster, type FapoOptimizationLevel, type FapoProposerOptions, type FapoReviewInput, type FapoReviewIssue, type FapoReviewResult, type FapoScopeContract, FileSearchLedger, FsLabeledScenarioStore, type FsLabeledScenarioStoreOptions, Gate, GenerationRecord, type GitWorktreeAdapterOptions, type Governor, type GovernorContext, type GovernorOp, type HaloProposerOptions, type HeldoutSignificance, type HeldoutSignificanceOptions, type HeuristicGovernorOptions, type JsonPolicyEditTargetSurface, type JsonPrimitive, type JsonValue, JudgeConfig, JudgeScore, LabelTrust, LabeledScenarioRecord, LabeledScenarioSampleArgs, LabeledScenarioSource, LabeledScenarioStore, LabeledScenarioStoreError, LabeledScenarioWrite, Lineage, type LineageEdge, type LineageGraph, type LineageNode, type LineageNodeInput, type LineageStore, type LlmPolicyEditProposerOptions, type LoadEvalFixtureScenariosOptions, type MemoryCurationProposerOptions, MutableSurface, type NeutralizationGateOptions, type OpenSearchLedgerOptions, type OptimizerEntryConfig, type PairedHoldout, type ParameterCandidate, type ParameterChange, type ParameterSweepProposerOptions, PlanCampaignRunOptions, type PlanEvalFixtureRunOptions, type PlaybackContext, type PlaybackDriver, type PlaybackStep, type PolicyEditAuthorScenarioRow, type PolicyEditCandidateSummary, type PolicyEditFindingInput, type PolicyEditFindingSource, type PolicyEditHistoryCandidateContext, type PolicyEditHistoryGenerationContext, type PolicyEditHistoryProjectionOptions, type PolicyEditObjective, type PolicyEditOutcomeContext, type PolicyEditProposerOptions, type ProfileDispatchFn, ProfileMatrixError, type ProfileSummary, ProposeContext, type ProposePatchesArgs, ProposedCandidate, type ProposerComparison, type ProposerEntry, type ProposerPairwise, type ProposerScore, type RejectedEdit, type RolloutArgumentDiff, type RolloutArgumentDiffOptions, type RolloutCall, RunCampaignOptions, RunImprovementLoopOptions, type RunLineageLoopOptions, type RunLineageLoopResult, type RunLineageLoopSeed, type RunLineageOptions, type RunLineageResult, type RunLineageSeed, type RunLineageStepResult, type RunProfileMatrixOptions, type RunProfileMatrixResult, type RunSkillOptOptions, type RunSkillOptResult, SEARCH_LEDGER_SCHEMA, Scenario, type ScenarioRollup, type ScenarioSignal, type ScoreboardRenderOptions, type ScoreboardRow, type ScoreboardSummary, type ScoredRollout, type SearchAccountingAudit, type SearchArtifactRef, type SearchAttemptAccounting, type SearchCandidateDecidedEvent, type SearchCandidateLineage, type SearchCandidateRegisteredEvent, type SearchCandidateSlot, type SearchCandidateSlotClosedEvent, type SearchCandidateSurface, type SearchCompletedEvent, type SearchCostAccounting, type SearchFailureReason, type SearchLedger, type SearchLedgerAppendResult, SearchLedgerConflictError, type SearchLedgerEntry, SearchLedgerError, type SearchLedgerEvent, type SearchLedgerHash, SearchLedgerIntegrityError, type SearchLedgerReplay, type SearchModelIdentity, type SearchOperationKind, type SearchOperationRecordedEvent, type SearchPlan, type SearchPlannedEvent, type SearchPlannedOperation, type SearchPlannedTask, type SearchSourceRef, type SearchSurfaceEffect, type SearchSurfaceEvidence, type SearchSurfaceKind, type SearchTaskAttemptedEvent, type SearchTaskOutcome, type SearchTokenAccounting, type SelectPolicyEditAuthorRowsOptions, type SequentialDecideFn, type SequentialDecideOptions, type SequentialDecision, type SequentialObservation, type SequentialPairedGate, type SequentialPairedGateOptions, type SerializedJsonBudget, type SingleRunLock, type SingleRunLockOptions, type SkillOptEpochRecord, type SkillOptEvidence, type SkillOptProposer, type SkillOptProposerOptions, type SkillPatch, type SkillPatchOp, SkillPatchParseError, type SkillPatchRejection, SurfaceProposer, type SurfaceScore, type TraceAnalystProposerOptions, type TransientFailureOptions, type UngroundedLiteralReport, type UserStory, type UserStoryVerdict, type Worktree, type WorktreeAdapter, WorktreeAdapterError, aceProposer, acquireSingleRunLock, analyzeCrossSurfaceInteractions, applySkillPatch, assertCodeSurfaceIdentity, assertPolicyEditAuthorContextBudget, buildAnalystSurfaceDispatch, callbackGovernor, campaignBreakdown, campaignMeanComposite, classifyUngroundedLiterals, codeSurfaceIdentityMaterial, compareProposers, compositeProposer, detectScale, dimensionRegressions, discoverEvalFixtures, extractFapoAttributionSignals, failureModeRecallJudge, fapoEscalationEntry, fapoProposer, fsLineageStore, gepaParetoEntry, gepaReflectionEntry, gitWorktreeAdapter, haloProposer, heldoutSignificance, heuristicGovernor, isTransientTransportFailure, lineageNodeId, llmPolicyEditProposer, loadEvalFixture, loadEvalFixtureScenarios, makePlaybackDispatch, memLineageStore, memoryCurationProposer, neutralizationGate, neutralizeText, openSearchLedger, pairHoldout, parameterSweepProposer, parseSkillPatchResponse, patchEditCount, planEvalFixtureRun, policyEditProposer, projectPolicyEditHistory, renderScoreboardMarkdown, resolveRunDir, resolveWorktreePath, rolloutArgumentDiff, runLineage, runLineageLoop, runProfileMatrix, runSkillOpt, scoreDiscrimination, scoreUserStory, scoreboardSummary, selectDiscriminative, selectPolicyEditAuthorRows, sequentialDecide, sequentialPairedGate, skillOptEntry, skillOptProposer, surfaceContentHash, surfaceHash, tangleTracesRoot, traceAnalystProposer, userStoryScoreboard, validateSearchLedgerEvent, verifyCodeSurface };
|
|
7317
|
+
export { type AcceptedEdit, type AceProposerOptions, type AnalystArtifact, type AnalystScenario, type AnalyzeCrossSurfaceInteractionsInput, type ApplySkillPatchResult, type AxisEvidence, type AxisVerdict, type BuildAnalystSurfaceDispatchOptions, type BuildEvidenceVectorOptions, type BuildLoopProvenanceArgs, type CampaignAggregates, type CampaignArtifactWriter, type CampaignBreakdown, type CampaignCellResult, type CampaignCostMeter, type CampaignResult, type CampaignRunPlan, type CampaignRunPlanCell, type CampaignStorage, type CampaignTokenUsage, type CampaignTraceWriter, type CodeSurface, type CodeSurfaceVerification, type CompareProposersOptions, type CompositeProposerOptions, type CrossSurfaceAdditionDecision, type CrossSurfaceAdditionRejectionReason, type CrossSurfaceAttemptCompleteness, type CrossSurfaceBestSingleSelection, type CrossSurfaceBootstrapPolicy, type CrossSurfaceCandidate, type CrossSurfaceCandidateComparison, type CrossSurfaceCandidateEvidence, type CrossSurfaceCandidateOutcome, type CrossSurfaceCandidateSummary, type CrossSurfaceComponent, type CrossSurfaceComponentEvidence, type CrossSurfaceCompositionStep, type CrossSurfaceDistribution, type CrossSurfaceEligibility, type CrossSurfaceEvidenceBreakdown, type CrossSurfaceIneligibilityReason, type CrossSurfaceInteractionAwareSelection, type CrossSurfaceInteractionEffect, type CrossSurfaceInteractionPath, type CrossSurfaceInteractionReport, type CrossSurfaceInteractionTask, type CrossSurfaceNaiveStackSelection, type CrossSurfacePairCompatibility, type CrossSurfacePairEvidence, type CrossSurfacePairIncompatibilityReason, type CrossSurfacePairwiseEntry, type CrossSurfaceRankedSingle, type CrossSurfaceRelativeCost, type CrossSurfaceSelectionPolicy, type CrossSurfaceSelections, type CrossSurfaceTaskRow, DEFAULT_POLICY_EDIT_HISTORY_LIMITS, type DefaultProductionGateOptions, type DimensionRegression, type DiscriminationScore, type DispatchContext, type DispatchFn, type EmitLoopProvenanceArgs, type EmitLoopProvenanceResult, type EvalFixture, type EvalFixtureFile, type EvalFixtureLoadOptions, type EvalFixtureRunPlan, type EvalFixtureScenario, type EvalFixtureValidationMode, type EvidenceVector, type EvolutionaryProposerOptions, type FailureModeRecallJudgeOptions, type FapoAttributionSignals, type FapoEntryConfig, type FapoFailureCluster, type FapoOptimizationLevel, type FapoProposerOptions, type FapoReviewInput, type FapoReviewIssue, type FapoReviewResult, type FapoScopeContract, FileSearchLedger, FsLabeledScenarioStore, type FsLabeledScenarioStoreOptions, type Gate, type GateContext, type GateDecision, type GateResult, type GenerationCandidate, type GenerationRecord, type GepaProposerConstraints, type GepaProposerOptions, type GitWorktreeAdapterOptions, type Governor, type GovernorContext, type GovernorOp, type HaloProposerOptions, type HeldOutGateOptions, type HeldoutSignificance, type HeldoutSignificanceOptions, type HeuristicGovernorOptions, type JsonPolicyEditTargetSurface, type JsonPrimitive, type JsonValue, type JudgeAggregate, type JudgeConfig, type JudgeDimension, type JudgeScore, type LabelTrust, type LabeledScenarioRecord, type LabeledScenarioSampleArgs, type LabeledScenarioSource, type LabeledScenarioStore, LabeledScenarioStoreError, type LabeledScenarioWrite, Lineage, type LineageEdge, type LineageGraph, type LineageNode, type LineageNodeInput, type LineageStore, type LlmJudgeDimension, type LlmJudgeOptions, type LlmPolicyEditProposerOptions, type LoadEvalFixtureScenariosOptions, type LoopProvenanceBackend, type LoopProvenanceCandidate, type LoopProvenanceRecord, type MemoryCurationProposerOptions, type MutableSurface, type Mutator, type NeutralizationGateOptions, type ObjectiveSource, type OpenAutoPrOptions, type OpenAutoPrResult, type OpenSearchLedgerOptions, type OptimizationProposer, type OptimizerConfig, type OptimizerEntryConfig, POLICY_EDIT_CANDIDATE_RECORD_SCHEMA, type PairedHoldout, type ParameterCandidate, type ParameterChange, type ParameterSweepProposerOptions, type ParetoParent, type ParetoSignificanceGateOptions, type PlanCampaignRunOptions, type PlanEvalFixtureRunOptions, type PlaybackContext, type PlaybackDriver, type PlaybackStep, type PolicyEditAuthorScenarioRow, type PolicyEditCandidateRecord, type PolicyEditCandidateSummary, type PolicyEditFindingInput, type PolicyEditFindingSource, type PolicyEditHistoryCandidateContext, type PolicyEditHistoryGenerationContext, type PolicyEditHistoryProjectionOptions, type PolicyEditObjective, type PolicyEditOutcomeContext, type PolicyEditProposerOptions, type PowerPreflight, type PowerPreflightOptions, type ProfileDispatchFn, ProfileMatrixError, type ProfileSummary, type PromotionObjective, type PromotionPolicy, type ProposeContext, type ProposePatchesArgs, type ProposedCandidate, type ProposerComparison, type ProposerEntry, type ProposerPairwise, type ProposerScore, type RedactionStatus, type ReferenceEquivalenceJudgeOptions, type ReferenceEquivalenceScenario, type RejectedEdit, type RolloutArgumentDiff, type RolloutArgumentDiffOptions, type RolloutCall, type RunCampaignOptions, type RunEvalOptions, type RunImprovementLoopOptions, type RunImprovementLoopResult, type RunLineageLoopOptions, type RunLineageLoopResult, type RunLineageLoopSeed, type RunLineageOptions, type RunLineageResult, type RunLineageSeed, type RunLineageStepResult, type RunOptimizationOptions, type RunOptimizationResult, type RunProfileMatrixOptions, type RunProfileMatrixResult, type RunSkillOptOptions, type RunSkillOptResult, SEARCH_LEDGER_SCHEMA, type Scenario, type ScenarioAggregate, type ScenarioRollup, type ScenarioSignal, type ScoreboardRenderOptions, type ScoreboardRow, type ScoreboardSummary, type ScoredRollout, type ScoredSurfaceOutcome, type SearchAccountingAudit, type SearchArtifactRef, type SearchAttemptAccounting, type SearchCandidateDecidedEvent, type SearchCandidateLineage, type SearchCandidateRegisteredEvent, type SearchCandidateSlot, type SearchCandidateSlotClosedEvent, type SearchCandidateSurface, type SearchCompletedEvent, type SearchCostAccounting, type SearchFailureReason, type SearchLedger, type SearchLedgerAppendResult, SearchLedgerConflictError, type SearchLedgerEntry, SearchLedgerError, type SearchLedgerEvent, type SearchLedgerHash, SearchLedgerIntegrityError, type SearchLedgerReplay, type SearchModelIdentity, type SearchOperationKind, type SearchOperationRecordedEvent, type SearchPlan, type SearchPlannedEvent, type SearchPlannedOperation, type SearchPlannedTask, type SearchSourceRef, type SearchSurfaceEffect, type SearchSurfaceEvidence, type SearchSurfaceKind, type SearchTaskAttemptedEvent, type SearchTaskOutcome, type SearchTokenAccounting, type SelectPolicyEditAuthorRowsOptions, type SequentialDecideFn, type SequentialDecideOptions, type SequentialDecision, type SequentialObservation, type SequentialPairedGate, type SequentialPairedGateOptions, type SerializedJsonBudget, type SessionScript, type SingleRunLock, type SingleRunLockOptions, type SkillOptEpochRecord, type SkillOptEvidence, type SkillOptProposer, type SkillOptProposerOptions, type SkillPatch, type SkillPatchOp, SkillPatchParseError, type SkillPatchRejection, type SurfaceProposer, type SurfaceScore, type TraceAnalystProposerOptions, type TraceSpan, type TransientFailureOptions, type UngroundedLiteralReport, type UserStory, type UserStoryVerdict, type Worktree, type WorktreeAdapter, WorktreeAdapterError, aceProposer, acquireSingleRunLock, analyzeCrossSurfaceInteractions, applySkillPatch, assertCodeSurfaceIdentity, assertPolicyEditAuthorContextBudget, buildAnalystSurfaceDispatch, buildEvidenceVector, buildLoopProvenanceRecord, callbackGovernor, campaignBreakdown, campaignMeanComposite, classifyUngroundedLiterals, codeSurfaceIdentityMaterial, compareProposers, composeGate, compositeProposer, countSentenceEdits, createReferenceEquivalenceJudge, createRunCostLedger, defaultProductionGate, defaultRenderDiff, detectScale, dimensionRegressions, discoverEvalFixtures, emitLoopProvenance, evolutionaryProposer, extractFapoAttributionSignals, extractH2Sections, failureModeRecallJudge, fapoEscalationEntry, fapoProposer, fsCampaignStorage, fsLineageStore, gepaParetoEntry, gepaProposer, gepaReflectionEntry, gitWorktreeAdapter, haloProposer, heldOutGate, heldoutSignificance, heuristicGovernor, inMemoryCampaignStorage, isProposedCandidate, isTransientTransportFailure, labelTrustRank, lineageNodeId, llmJudge, llmPolicyEditProposer, loadEvalFixture, loadEvalFixtureScenarios, loopProvenanceSpans, makePlaybackDispatch, memLineageStore, memoryCurationProposer, neutralizationGate, neutralizeText, openAutoPr, openSearchLedger, pairHoldout, parameterSweepProposer, paretoPolicy, paretoSignificanceGate, parseSkillPatchResponse, patchEditCount, planCampaignRun, planEvalFixtureRun, policyEditProposer, powerPreflight, projectPolicyEditHistory, provenanceRecordPath, provenanceSpansPath, renderScoreboardMarkdown, resolveRunDir, resolveWorktreePath, rolloutArgumentDiff, runCampaign, runEval, runImprovementLoop, runLineage, runLineageLoop, runOptimization, runProfileMatrix, runSkillOpt, scoreDiscrimination, scoreUserStory, scoreboardSummary, selectDiscriminative, selectPolicyEditAuthorRows, sequentialDecide, sequentialPairedGate, skillOptEntry, skillOptProposer, surfaceContentHash, surfaceHash, tangleTracesRoot, traceAnalystProposer, userStoryScoreboard, validatePolicyEditCandidateRecord, validateSearchLedgerEvent, verifyCodeSurface };
|