@tangle-network/agent-eval 0.106.3 → 0.108.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +2 -2
  3. package/dist/adapters/http.d.ts +1 -1
  4. package/dist/adapters/langchain.d.ts +1 -1
  5. package/dist/adapters/otel.d.ts +1 -1
  6. package/dist/benchmarks/index.d.ts +3 -1
  7. package/dist/benchmarks/index.js +49 -3
  8. package/dist/campaign/index.d.ts +75 -8
  9. package/dist/campaign/index.js +74 -3321
  10. package/dist/campaign/index.js.map +1 -1
  11. package/dist/chunk-4VLZEPJ3.js +4306 -0
  12. package/dist/chunk-4VLZEPJ3.js.map +1 -0
  13. package/dist/{chunk-3E5KUXYZ.js → chunk-OVPVM4JC.js} +1064 -643
  14. package/dist/chunk-OVPVM4JC.js.map +1 -0
  15. package/dist/chunk-T6W5ADLG.js +766 -0
  16. package/dist/chunk-T6W5ADLG.js.map +1 -0
  17. package/dist/contract/index.d.ts +16 -6
  18. package/dist/contract/index.js +10 -11
  19. package/dist/contract/index.js.map +1 -1
  20. package/dist/{gepa-DQ3ruj18.d.ts → gepa-B3x5Ulcv.d.ts} +11 -40
  21. package/dist/hosted/index.d.ts +1 -1
  22. package/dist/index-pPtfoIJO.d.ts +423 -0
  23. package/dist/index.d.ts +6 -5
  24. package/dist/index.js +8 -7
  25. package/dist/index.js.map +1 -1
  26. package/dist/multishot/index.d.ts +1 -1
  27. package/dist/openapi.json +1 -1
  28. package/dist/{pre-registration-BkkTmtQG.d.ts → pre-registration-BUhVPzE7.d.ts} +1 -1
  29. package/dist/{provenance-Bsyjc67Z.d.ts → provenance-DdDhf6cg.d.ts} +3 -2
  30. package/dist/rl.d.ts +1 -1
  31. package/dist/rl.js +6 -6
  32. package/dist/storage-Dw_f7WMt.d.ts +39 -0
  33. package/dist/{types-Ctf7XIAL.d.ts → types-BdIv5dvA.d.ts} +11 -1
  34. package/docs/improvement-glossary.md +5 -1
  35. package/package.json +1 -1
  36. package/dist/chunk-2HLM4SJJ.js +0 -861
  37. package/dist/chunk-2HLM4SJJ.js.map +0 -1
  38. package/dist/chunk-3E5KUXYZ.js.map +0 -1
  39. package/dist/chunk-6QDKWHLS.js +0 -223
  40. package/dist/chunk-6QDKWHLS.js.map +0 -1
  41. package/dist/chunk-J22CKVXN.js +0 -421
  42. package/dist/chunk-J22CKVXN.js.map +0 -1
  43. package/dist/index-C2CC_dry.d.ts +0 -159
@@ -1,223 +0,0 @@
1
- import {
2
- __export
3
- } from "./chunk-PZ5AY32C.js";
4
-
5
- // src/benchmarks/index.ts
6
- var benchmarks_exports = {};
7
- __export(benchmarks_exports, {
8
- BENCHMARK_SPLIT_SEED: () => BENCHMARK_SPLIT_SEED,
9
- deterministicSplit: () => deterministicSplit,
10
- routing: () => routing_exports
11
- });
12
-
13
- // src/benchmarks/routing/index.ts
14
- var routing_exports = {};
15
- __export(routing_exports, {
16
- ROUTING_DATASET: () => ROUTING_DATASET,
17
- RoutingAdapter: () => RoutingAdapter,
18
- assignSplit: () => assignSplit,
19
- evaluate: () => evaluate,
20
- extractRouteTokens: () => extractRouteTokens,
21
- loadDataset: () => loadDataset
22
- });
23
-
24
- // src/benchmarks/types.ts
25
- function fnv1a32(input) {
26
- let h = 2166136261;
27
- for (let i = 0; i < input.length; i++) {
28
- h ^= input.charCodeAt(i) & 255;
29
- h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
30
- }
31
- return h >>> 0;
32
- }
33
- var BENCHMARK_SPLIT_SEED = "agent-eval-v1";
34
- function deterministicSplit(itemId, seed = BENCHMARK_SPLIT_SEED) {
35
- const h = fnv1a32(`${seed}::${itemId}`);
36
- const pos = h / 4294967296;
37
- if (pos < 0.6) return "search";
38
- if (pos < 0.8) return "dev";
39
- return "holdout";
40
- }
41
-
42
- // src/benchmarks/routing/dataset.ts
43
- var ROUTING_DATASET = [
44
- {
45
- id: "file_001",
46
- category: "file",
47
- prompt: "Save the meeting notes to /tmp/notes-2025-04.md as markdown.",
48
- route: "fs.write",
49
- synonyms: ["filesystem.write", "write_file"],
50
- hardNegatives: ["fs.read", "chat.reply"]
51
- },
52
- {
53
- id: "file_002",
54
- category: "file",
55
- prompt: "Read the contents of /etc/hosts and summarize the entries.",
56
- route: "fs.read",
57
- synonyms: ["filesystem.read", "read_file"],
58
- hardNegatives: ["fs.write", "search.web"]
59
- },
60
- {
61
- id: "file_003",
62
- category: "file",
63
- prompt: "List every Python file under src/ recursively.",
64
- route: "fs.list",
65
- synonyms: ["filesystem.list", "list_files"],
66
- hardNegatives: ["fs.read", "search.code"]
67
- },
68
- {
69
- id: "file_004",
70
- category: "file",
71
- prompt: "Delete the cached build at .turbo/cache.",
72
- route: "fs.delete",
73
- synonyms: ["filesystem.delete", "remove_file"],
74
- hardNegatives: ["fs.write", "fs.list"]
75
- },
76
- {
77
- id: "math_001",
78
- category: "math",
79
- prompt: "What is the integral of 3x^2 + 2x from 0 to 5?",
80
- route: "math.integral",
81
- synonyms: ["calculator.integral", "math.solve"],
82
- hardNegatives: ["math.derivative", "chat.reply"]
83
- },
84
- {
85
- id: "math_002",
86
- category: "math",
87
- prompt: "Compute the derivative of sin(x) * cos(x).",
88
- route: "math.derivative",
89
- synonyms: ["calculator.derivative", "math.solve"],
90
- hardNegatives: ["math.integral", "math.algebra"]
91
- },
92
- {
93
- id: "math_003",
94
- category: "math",
95
- prompt: "Solve 2x + 7 = 19 for x.",
96
- route: "math.algebra",
97
- synonyms: ["calculator.algebra", "math.solve"],
98
- hardNegatives: ["math.derivative", "math.integral"]
99
- },
100
- {
101
- id: "math_004",
102
- category: "math",
103
- prompt: "What is the prime factorization of 360?",
104
- route: "math.numbertheory",
105
- synonyms: ["calculator.factor", "math.solve"],
106
- hardNegatives: ["math.algebra", "search.web"]
107
- },
108
- {
109
- id: "search_001",
110
- category: "search",
111
- prompt: "Find recent papers on agent prompt optimization with held-out promotion gates.",
112
- route: "search.web",
113
- synonyms: ["web.search", "search.papers"],
114
- hardNegatives: ["search.code", "chat.reply"]
115
- },
116
- {
117
- id: "search_002",
118
- category: "search",
119
- prompt: "Search the codebase for every call site of `runProposeReview`.",
120
- route: "search.code",
121
- synonyms: ["code.search", "grep"],
122
- hardNegatives: ["search.web", "fs.read"]
123
- },
124
- {
125
- id: "search_003",
126
- category: "search",
127
- prompt: "What is the latest release of the Tangle network on GitHub?",
128
- route: "search.web",
129
- synonyms: ["web.search", "github.releases"],
130
- hardNegatives: ["search.code", "chat.reply"]
131
- },
132
- {
133
- id: "search_004",
134
- category: "search",
135
- prompt: "Find all TODO comments in the agent-eval src tree.",
136
- route: "search.code",
137
- synonyms: ["code.search", "grep"],
138
- hardNegatives: ["search.web", "fs.list"]
139
- },
140
- {
141
- id: "chat_001",
142
- category: "chat",
143
- prompt: "Hi there, how are you doing today?",
144
- route: "chat.reply",
145
- synonyms: ["conversation.reply"],
146
- hardNegatives: ["search.web", "fs.read"]
147
- },
148
- {
149
- id: "chat_002",
150
- category: "chat",
151
- prompt: "Please explain the difference between an LLM and a foundation model.",
152
- route: "chat.reply",
153
- synonyms: ["conversation.reply", "qa.answer"],
154
- hardNegatives: ["search.web", "math.algebra"]
155
- },
156
- {
157
- id: "chat_003",
158
- category: "chat",
159
- prompt: "Tell me a short joke about distributed systems.",
160
- route: "chat.reply",
161
- synonyms: ["conversation.reply"],
162
- hardNegatives: ["search.web", "fs.read"]
163
- },
164
- {
165
- id: "chat_004",
166
- category: "chat",
167
- prompt: "Acknowledge my last message with a thumbs up.",
168
- route: "chat.reply",
169
- synonyms: ["conversation.reply", "react"],
170
- hardNegatives: ["fs.write", "search.web"]
171
- }
172
- ];
173
-
174
- // src/benchmarks/routing/index.ts
175
- var RoutingAdapter = class {
176
- async loadDataset(split) {
177
- return ROUTING_DATASET.map((item) => ({ id: item.id, payload: item })).filter(
178
- (it) => assignSplitImpl(it.id) === split
179
- );
180
- }
181
- async evaluate(item, response) {
182
- const tokens = extractRouteTokens(response);
183
- const correct = new Set(
184
- [item.payload.route, ...item.payload.synonyms].map((s) => s.toLowerCase())
185
- );
186
- const hardNeg = new Set(item.payload.hardNegatives.map((s) => s.toLowerCase()));
187
- const firstMatch = tokens.find((t) => correct.has(t.toLowerCase())) ?? null;
188
- const firstHardNeg = tokens.find((t) => hardNeg.has(t.toLowerCase())) ?? null;
189
- const score = firstMatch ? 1 : 0;
190
- return {
191
- score,
192
- raw: {
193
- firstToken: tokens[0] ?? null,
194
- matchedRoute: firstMatch,
195
- hitHardNegative: Boolean(firstHardNeg),
196
- hardNegativeRoute: firstHardNeg,
197
- category: item.payload.category
198
- }
199
- };
200
- }
201
- assignSplit(itemId) {
202
- return assignSplitImpl(itemId);
203
- }
204
- };
205
- function assignSplitImpl(itemId) {
206
- return deterministicSplit(`routing::${itemId}`);
207
- }
208
- function extractRouteTokens(response) {
209
- const matches = response.match(/[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*/gi);
210
- return matches ?? [];
211
- }
212
- var adapter = new RoutingAdapter();
213
- var loadDataset = adapter.loadDataset.bind(adapter);
214
- var evaluate = adapter.evaluate.bind(adapter);
215
- var assignSplit = adapter.assignSplit.bind(adapter);
216
-
217
- export {
218
- BENCHMARK_SPLIT_SEED,
219
- deterministicSplit,
220
- routing_exports,
221
- benchmarks_exports
222
- };
223
- //# sourceMappingURL=chunk-6QDKWHLS.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/benchmarks/index.ts","../src/benchmarks/routing/index.ts","../src/benchmarks/types.ts","../src/benchmarks/routing/dataset.ts"],"sourcesContent":["/**\n * Reference benchmark wrappers — entry point.\n *\n * Core surface (exported here):\n * - The `BenchmarkAdapter` contract.\n * - `deterministicSplit` + `BENCHMARK_SPLIT_SEED` for split assignment.\n * - `routing` — synthetic 16-task router benchmark. The only novel\n * benchmark we built; ships in the package.\n *\n * Example wrappers (under `examples/benchmarks/`, NOT in the bundle):\n * - `gsm8k` — exact-match math reasoning (HF mirror, dataset\n * not bundled).\n * - `swebench-lite` — 30-instance SWE-Bench subset via an external\n * grader command.\n *\n * The example wrappers are reference implementations of `BenchmarkAdapter`.\n * Read them, copy them, adapt them. They're intentionally not in the main\n * entry — every team will configure them differently.\n */\n\nexport * as routing from './routing/index'\nexport type {\n BenchmarkAdapter,\n BenchmarkDatasetItem,\n BenchmarkEvaluation,\n} from './types'\nexport { BENCHMARK_SPLIT_SEED, deterministicSplit } from './types'\n","/**\n * Routing benchmark — synthetic, dependency-free, ships in the\n * package. 16 cross-category items in `dataset.ts`. See\n * `routing/README.md` for the format.\n *\n * `evaluate` does case-insensitive exact match against the canonical\n * route plus declared synonyms. The first valid route token in the\n * response wins; everything else is ignored. Wrong answers also\n * report whether they hit a hard negative — useful when triaging\n * \"always picks the popular route\" failure modes.\n */\n\nimport type { RunSplitTag } from '../../run-record'\nimport type { BenchmarkAdapter, BenchmarkDatasetItem, BenchmarkEvaluation } from '../types'\nimport { deterministicSplit } from '../types'\nimport { ROUTING_DATASET, type RoutingItem } from './dataset'\n\nexport type { RoutingItem }\nexport type RoutingPayload = RoutingItem\nexport type RoutingDatasetItem = BenchmarkDatasetItem<RoutingPayload>\n\nclass RoutingAdapter implements BenchmarkAdapter<RoutingDatasetItem, RoutingPayload> {\n async loadDataset(split: RunSplitTag): Promise<RoutingDatasetItem[]> {\n return ROUTING_DATASET.map((item) => ({ id: item.id, payload: item })).filter(\n (it) => assignSplitImpl(it.id) === split,\n )\n }\n\n async evaluate(item: RoutingDatasetItem, response: string): Promise<BenchmarkEvaluation> {\n const tokens = extractRouteTokens(response)\n const correct = new Set<string>(\n [item.payload.route, ...item.payload.synonyms].map((s) => s.toLowerCase()),\n )\n const hardNeg = new Set<string>(item.payload.hardNegatives.map((s) => s.toLowerCase()))\n const firstMatch = tokens.find((t) => correct.has(t.toLowerCase())) ?? null\n const firstHardNeg = tokens.find((t) => hardNeg.has(t.toLowerCase())) ?? null\n const score = firstMatch ? 1 : 0\n return {\n score,\n raw: {\n firstToken: tokens[0] ?? null,\n matchedRoute: firstMatch,\n hitHardNegative: Boolean(firstHardNeg),\n hardNegativeRoute: firstHardNeg,\n category: item.payload.category,\n },\n }\n }\n\n assignSplit(itemId: string): RunSplitTag {\n return assignSplitImpl(itemId)\n }\n}\n\nfunction assignSplitImpl(itemId: string): RunSplitTag {\n return deterministicSplit(`routing::${itemId}`)\n}\n\n/**\n * Pull route-shaped tokens out of a model response. Routes look like\n * `category.action` (`fs.write`, `chat.reply`). Bare alphanumerics\n * are not routes, but `category.action` patterns are robust to most\n * model wrappers (JSON output, prose explanations, code fences).\n */\nexport function extractRouteTokens(response: string): string[] {\n const matches = response.match(/[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*/gi)\n return matches ?? []\n}\n\nconst adapter = new RoutingAdapter()\n\nexport const loadDataset = adapter.loadDataset.bind(adapter)\nexport const evaluate = adapter.evaluate.bind(adapter)\nexport const assignSplit = adapter.assignSplit.bind(adapter)\nexport { ROUTING_DATASET, RoutingAdapter }\n","/**\n * Shared types for the reference benchmark wrappers under\n * `src/benchmarks/`. Each wrapper exports the three functions in\n * `BenchmarkAdapter` plus its own typed `DatasetItem` shape.\n */\n\nimport type { RunSplitTag } from '../run-record'\n\nexport interface BenchmarkDatasetItem<TPayload = unknown> {\n /** Stable dataset-local item id (used for split assignment + paper\n * references). Unique within a benchmark. */\n id: string\n /** Free-form payload. Each benchmark defines its own shape. */\n payload: TPayload\n}\n\nexport interface BenchmarkEvaluation {\n /** [0, 1] score for the response on this item. Exact-match\n * benchmarks use 0/1; partial-credit benchmarks may return\n * fractional values. */\n score: number\n /** Optional bag of raw scoring signals — e.g. parsed numeric\n * answer, regex match, judge sub-scores. */\n raw: Record<string, unknown>\n}\n\n/** Common signature implemented by every adapter under `src/benchmarks/*`. */\n// `TPayload` is the per-item payload type; `_TItem` is preserved for\n// downstream type-narrowing extensions (a richer `BenchmarkDatasetItem`\n// subclass that adds e.g. provenance metadata) but is intentionally\n// unused here. `noUnusedLocals` requires the leading underscore.\nexport interface BenchmarkAdapter<_TItem = unknown, TPayload = unknown> {\n /** Load the dataset for the given split. May hit the network on\n * first call but should be cache-friendly. Adapters that don't\n * ship the dataset itself MUST throw a clearly-marked error\n * pointing the caller at the loader script. */\n loadDataset(split: RunSplitTag): Promise<BenchmarkDatasetItem<TPayload>[]>\n /** Score a single response. Pure with respect to the inputs. */\n evaluate(item: BenchmarkDatasetItem<TPayload>, response: string): Promise<BenchmarkEvaluation>\n /** Deterministic split assignment via item id hashing. The\n * fraction of items in each split is implementation-defined but\n * MUST be stable across processes and platforms. */\n assignSplit(itemId: string): RunSplitTag\n}\n\n// ── Deterministic split assignment ───────────────────────────────────\n\n/**\n * 32-bit FNV-1a hash. Stable, allocation-free, deterministic across\n * runtimes. We use it to assign items to splits rather than depending\n * on a polyfilled crypto.subtle path.\n */\nfunction fnv1a32(input: string): number {\n let h = 0x811c9dc5\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i) & 0xff\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0\n }\n return h >>> 0\n}\n\n/** Split-assignment seed shared across all benchmarks. Bumping this\n * value reshuffles every split — do NOT do that lightly. */\nexport const BENCHMARK_SPLIT_SEED = 'agent-eval-v1'\n\n/**\n * Assign an item id to one of `'search' | 'dev' | 'holdout'` using a\n * stable 32-bit hash of `${seed}::${id}`. Default proportions:\n *\n * search: 60% (optimization-readable)\n * dev: 20% (held-out for tuning, leak-on-purpose during dev)\n * holdout:20% (paper-grade held-out, gated reads)\n */\nexport function deterministicSplit(\n itemId: string,\n seed: string = BENCHMARK_SPLIT_SEED,\n): RunSplitTag {\n const h = fnv1a32(`${seed}::${itemId}`)\n const pos = h / 0x100000000\n if (pos < 0.6) return 'search'\n if (pos < 0.8) return 'dev'\n return 'holdout'\n}\n","/**\n * Synthetic routing dataset. 16 tasks across 4 categories. Used as a\n * deterministic, dependency-free benchmark for any router that maps a\n * natural-language request to one of a fixed set of route labels.\n *\n * Format (see `routing/README.md` for prose):\n *\n * {\n * id: stable per-task ID (matches across processes).\n * category: one of the four route labels.\n * prompt: the user-facing request the router must classify.\n * route: the ground-truth route the router should pick.\n * synonyms: other strings that count as a correct answer.\n * hardNegatives:close-but-wrong route labels — used to detect the\n * \"always picks the popular route\" failure mode.\n * }\n *\n * The four categories are intentionally cross-domain (file ops,\n * math, search, conversation) so a router that collapses to one\n * category is easy to spot.\n */\n\nexport interface RoutingItem {\n id: string\n category: 'file' | 'math' | 'search' | 'chat'\n prompt: string\n /** Canonical correct route label. */\n route: string\n /** Alternate route labels that also count as correct. */\n synonyms: string[]\n /** Wrong-but-tempting route labels (for analysis, not grading). */\n hardNegatives: string[]\n}\n\nexport const ROUTING_DATASET: RoutingItem[] = [\n {\n id: 'file_001',\n category: 'file',\n prompt: 'Save the meeting notes to /tmp/notes-2025-04.md as markdown.',\n route: 'fs.write',\n synonyms: ['filesystem.write', 'write_file'],\n hardNegatives: ['fs.read', 'chat.reply'],\n },\n {\n id: 'file_002',\n category: 'file',\n prompt: 'Read the contents of /etc/hosts and summarize the entries.',\n route: 'fs.read',\n synonyms: ['filesystem.read', 'read_file'],\n hardNegatives: ['fs.write', 'search.web'],\n },\n {\n id: 'file_003',\n category: 'file',\n prompt: 'List every Python file under src/ recursively.',\n route: 'fs.list',\n synonyms: ['filesystem.list', 'list_files'],\n hardNegatives: ['fs.read', 'search.code'],\n },\n {\n id: 'file_004',\n category: 'file',\n prompt: 'Delete the cached build at .turbo/cache.',\n route: 'fs.delete',\n synonyms: ['filesystem.delete', 'remove_file'],\n hardNegatives: ['fs.write', 'fs.list'],\n },\n {\n id: 'math_001',\n category: 'math',\n prompt: 'What is the integral of 3x^2 + 2x from 0 to 5?',\n route: 'math.integral',\n synonyms: ['calculator.integral', 'math.solve'],\n hardNegatives: ['math.derivative', 'chat.reply'],\n },\n {\n id: 'math_002',\n category: 'math',\n prompt: 'Compute the derivative of sin(x) * cos(x).',\n route: 'math.derivative',\n synonyms: ['calculator.derivative', 'math.solve'],\n hardNegatives: ['math.integral', 'math.algebra'],\n },\n {\n id: 'math_003',\n category: 'math',\n prompt: 'Solve 2x + 7 = 19 for x.',\n route: 'math.algebra',\n synonyms: ['calculator.algebra', 'math.solve'],\n hardNegatives: ['math.derivative', 'math.integral'],\n },\n {\n id: 'math_004',\n category: 'math',\n prompt: 'What is the prime factorization of 360?',\n route: 'math.numbertheory',\n synonyms: ['calculator.factor', 'math.solve'],\n hardNegatives: ['math.algebra', 'search.web'],\n },\n {\n id: 'search_001',\n category: 'search',\n prompt: 'Find recent papers on agent prompt optimization with held-out promotion gates.',\n route: 'search.web',\n synonyms: ['web.search', 'search.papers'],\n hardNegatives: ['search.code', 'chat.reply'],\n },\n {\n id: 'search_002',\n category: 'search',\n prompt: 'Search the codebase for every call site of `runProposeReview`.',\n route: 'search.code',\n synonyms: ['code.search', 'grep'],\n hardNegatives: ['search.web', 'fs.read'],\n },\n {\n id: 'search_003',\n category: 'search',\n prompt: 'What is the latest release of the Tangle network on GitHub?',\n route: 'search.web',\n synonyms: ['web.search', 'github.releases'],\n hardNegatives: ['search.code', 'chat.reply'],\n },\n {\n id: 'search_004',\n category: 'search',\n prompt: 'Find all TODO comments in the agent-eval src tree.',\n route: 'search.code',\n synonyms: ['code.search', 'grep'],\n hardNegatives: ['search.web', 'fs.list'],\n },\n {\n id: 'chat_001',\n category: 'chat',\n prompt: 'Hi there, how are you doing today?',\n route: 'chat.reply',\n synonyms: ['conversation.reply'],\n hardNegatives: ['search.web', 'fs.read'],\n },\n {\n id: 'chat_002',\n category: 'chat',\n prompt: 'Please explain the difference between an LLM and a foundation model.',\n route: 'chat.reply',\n synonyms: ['conversation.reply', 'qa.answer'],\n hardNegatives: ['search.web', 'math.algebra'],\n },\n {\n id: 'chat_003',\n category: 'chat',\n prompt: 'Tell me a short joke about distributed systems.',\n route: 'chat.reply',\n synonyms: ['conversation.reply'],\n hardNegatives: ['search.web', 'fs.read'],\n },\n {\n id: 'chat_004',\n category: 'chat',\n prompt: 'Acknowledge my last message with a thumbs up.',\n route: 'chat.reply',\n synonyms: ['conversation.reply', 'react'],\n hardNegatives: ['fs.write', 'search.web'],\n },\n]\n"],"mappings":"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","names":[]}
@@ -1,421 +0,0 @@
1
- import {
2
- TIE_WARN_FRACTION,
3
- detectScale,
4
- dimensionRegressions,
5
- heldoutSignificance,
6
- pairHoldout,
7
- runCanaries,
8
- scoreRedTeamOutput
9
- } from "./chunk-3E5KUXYZ.js";
10
- import {
11
- runCampaign
12
- } from "./chunk-3PFZBGMR.js";
13
- import {
14
- detectRewardHacking
15
- } from "./chunk-N22ZO7FV.js";
16
- import {
17
- pairedBootstrap
18
- } from "./chunk-XMSYF4A7.js";
19
-
20
- // src/campaign/gates/compose.ts
21
- function composeGate(...gates) {
22
- if (gates.length === 0) {
23
- throw new Error("composeGate requires at least one gate");
24
- }
25
- return {
26
- name: `composed(${gates.map((g) => g.name).join(",")})`,
27
- async decide(ctx) {
28
- const results = [];
29
- for (const gate of gates) {
30
- const res = await gate.decide(ctx);
31
- results.push({ gate, res });
32
- }
33
- const decisions = results.map((r) => r.res.decision);
34
- const overall = decisions.every((d) => d === "ship") ? "ship" : decisions.includes("arch_ceiling") ? "arch_ceiling" : decisions.includes("model_ceiling") ? "model_ceiling" : decisions.includes("hold") ? "hold" : "need_more_work";
35
- const contributing = results.flatMap(
36
- (r) => r.res.contributingGates.length > 0 ? r.res.contributingGates : [{ name: r.gate.name, passed: r.res.decision === "ship", detail: r.res }]
37
- );
38
- const reasons = results.flatMap(
39
- (r) => r.res.reasons.map((reason) => `[${r.gate.name}] ${reason}`)
40
- );
41
- return {
42
- decision: overall,
43
- reasons,
44
- contributingGates: contributing,
45
- delta: results[0]?.res.delta
46
- };
47
- }
48
- };
49
- }
50
-
51
- // src/campaign/gates/default-production-gate.ts
52
- function defaultProductionGate(options) {
53
- const deltaThreshold = options.deltaThreshold ?? 0;
54
- const confidence = options.confidence ?? 0.95;
55
- const resamples = options.bootstrapResamples ?? 2e3;
56
- const seed = options.bootstrapSeed ?? 1337;
57
- const minProductiveRuns = options.minProductiveRuns ?? 3;
58
- const heldoutStatistic = options.heldoutStatistic ?? "mean";
59
- const blockOnGaming = options.blockOnRewardHackingGaming ?? true;
60
- return {
61
- name: "defaultProductionGate",
62
- async decide(ctx) {
63
- const reasons = [];
64
- const contributing = [];
65
- const scenarioIds = new Set(options.holdoutScenarios.map((s) => s.id));
66
- const sig = heldoutSignificance(
67
- pairHoldout(
68
- ctx.judgeScores,
69
- ctx.baselineJudgeScores ?? ctx.judgeScores,
70
- scenarioIds,
71
- (s) => s.composite
72
- ),
73
- {
74
- deltaThreshold,
75
- minProductiveRuns,
76
- confidence,
77
- resamples,
78
- seed,
79
- statistic: heldoutStatistic
80
- }
81
- );
82
- const delta = heldoutStatistic === "median" ? sig.bootstrap.median : sig.bootstrap.mean;
83
- const heldoutPass = sig.significant;
84
- contributing.push({
85
- name: "heldout-significance",
86
- passed: heldoutPass,
87
- detail: {
88
- n: sig.n,
89
- delta,
90
- deltaMean: sig.bootstrap.mean,
91
- deltaMedianDiagnostic: sig.medianBootstrap.median,
92
- // Back-compat: prior consumers read `deltaMedian`. It now always carries
93
- // the median diagnostic (the ship decision keys on `delta`/mean).
94
- deltaMedian: sig.medianBootstrap.median,
95
- tieFraction: sig.tieFraction,
96
- ciLow: sig.bootstrap.low,
97
- ciHigh: sig.bootstrap.high,
98
- confidence: sig.bootstrap.confidence,
99
- deltaThreshold,
100
- fewRuns: sig.fewRuns
101
- }
102
- });
103
- if (!heldoutPass) {
104
- const tieNote = sig.tieFraction >= TIE_WARN_FRACTION ? `; ${(sig.tieFraction * 100).toFixed(0)}% tied scenarios` : "";
105
- reasons.push(
106
- sig.fewRuns ? `held-out: only ${sig.n} paired runs (< ${minProductiveRuns}) \u2014 too few to claim significance` : `held-out CI.low ${sig.bootstrap.low.toFixed(3)} \u2264 threshold ${deltaThreshold} (${heldoutStatistic} \u0394 ${delta.toFixed(3)}, ${(sig.bootstrap.confidence * 100).toFixed(0)}% CI [${sig.bootstrap.low.toFixed(3)}, ${sig.bootstrap.high.toFixed(3)}]${tieNote})`
107
- );
108
- }
109
- const dimRegs = options.criticalDimensions?.length ? dimensionRegressions(
110
- ctx.judgeScores,
111
- ctx.baselineJudgeScores ?? ctx.judgeScores,
112
- scenarioIds,
113
- options.criticalDimensions,
114
- { tolerance: options.regressionTolerance, confidence, resamples, seed }
115
- ) : [];
116
- const regressed = dimRegs.filter((d) => d.regressed);
117
- const dimPass = regressed.length === 0;
118
- contributing.push({
119
- name: "dimension-regression",
120
- passed: dimPass,
121
- detail: {
122
- guarded: options.criticalDimensions ?? [],
123
- regressions: dimRegs.map((d) => ({
124
- dimension: d.dimension,
125
- ciLow: d.bootstrap.low,
126
- median: d.bootstrap.median,
127
- tolerance: d.tolerance,
128
- n: d.n,
129
- regressed: d.regressed
130
- }))
131
- }
132
- });
133
- if (!dimPass) {
134
- reasons.push(
135
- `critical dimension(s) regressed: ${regressed.map((d) => `${d.dimension} CI.low ${d.bootstrap.low.toFixed(3)} < -${d.tolerance}`).join("; ")}`
136
- );
137
- }
138
- const budgetPass = options.budgetUsd === void 0 || ctx.cost.candidate + ctx.cost.baseline <= options.budgetUsd;
139
- contributing.push({
140
- name: "budget",
141
- passed: budgetPass,
142
- detail: {
143
- candidateUsd: ctx.cost.candidate,
144
- baselineUsd: ctx.cost.baseline,
145
- budgetUsd: options.budgetUsd
146
- }
147
- });
148
- if (!budgetPass) {
149
- reasons.push(
150
- `spend ${(ctx.cost.candidate + ctx.cost.baseline).toFixed(2)} > budget ${options.budgetUsd}`
151
- );
152
- }
153
- const redTeamFindings = options.redTeamBattery ? probeRedTeam(ctx.candidateArtifacts, options.redTeamBattery) : { passed: true, findings: [] };
154
- contributing.push({
155
- name: "red-team",
156
- passed: redTeamFindings.passed,
157
- detail: {
158
- failures: redTeamFindings.findings.length,
159
- sample: redTeamFindings.findings.slice(0, 3)
160
- }
161
- });
162
- if (!redTeamFindings.passed) {
163
- reasons.push(`red-team probe failed (${redTeamFindings.findings.length} findings)`);
164
- }
165
- let rewardHackingReport = null;
166
- if (options.recentRuns && options.recentRuns.length >= 10) {
167
- rewardHackingReport = detectRewardHacking({ runs: options.recentRuns });
168
- }
169
- const gamingThreshold = 0.6;
170
- const gamingFindings = (rewardHackingReport?.findings ?? []).filter(
171
- (f) => f.severity >= gamingThreshold
172
- );
173
- const rewardHackingPass = !rewardHackingReport || !blockOnGaming || gamingFindings.length === 0 && rewardHackingReport.verdict !== "gaming";
174
- contributing.push({
175
- name: "reward-hacking",
176
- passed: rewardHackingPass,
177
- detail: { report: rewardHackingReport, gamingFindingCount: gamingFindings.length }
178
- });
179
- if (!rewardHackingPass) {
180
- reasons.push(
181
- `reward-hacking detector flagged ${gamingFindings.length} gaming-severity findings (verdict=${rewardHackingReport.verdict})`
182
- );
183
- }
184
- let canaryReport = null;
185
- if (options.recentRuns && options.recentRuns.length >= 10) {
186
- canaryReport = runCanaries(options.recentRuns, {});
187
- }
188
- const errorAlerts = (canaryReport?.alerts ?? []).filter((a) => a.severity === "error");
189
- const canaryPass = errorAlerts.length === 0;
190
- contributing.push({
191
- name: "canary",
192
- passed: canaryPass,
193
- detail: { totalAlerts: canaryReport?.alerts.length ?? 0, errorAlerts: errorAlerts.length }
194
- });
195
- if (!canaryPass) {
196
- reasons.push(`canary error alerts: ${errorAlerts.length}`);
197
- }
198
- const allPassed = contributing.every((c) => c.passed);
199
- const decision = allPassed ? "ship" : "hold";
200
- return {
201
- decision,
202
- reasons: reasons.length > 0 ? reasons : ["all gates passed"],
203
- contributingGates: contributing,
204
- delta
205
- };
206
- }
207
- };
208
- }
209
- function probeRedTeam(artifacts, battery) {
210
- const findings = [];
211
- for (const [_cellId, artifact] of artifacts) {
212
- const text = extractText(artifact);
213
- if (text === void 0) continue;
214
- for (const rtCase of battery) {
215
- const finding = scoreRedTeamOutput(text, [], rtCase);
216
- if (!finding.passed) {
217
- findings.push({ scenarioId: rtCase.id, reason: finding.reason ?? "red-team probe failed" });
218
- }
219
- }
220
- }
221
- return { passed: findings.length === 0, findings };
222
- }
223
- function extractText(artifact) {
224
- if (typeof artifact === "string") return artifact;
225
- if (artifact && typeof artifact === "object") {
226
- const rec = artifact;
227
- if (typeof rec.text === "string") return rec.text;
228
- if (typeof rec.output === "string") return rec.output;
229
- if (typeof rec.content === "string") return rec.content;
230
- }
231
- return void 0;
232
- }
233
-
234
- // src/campaign/gates/power-preflight.ts
235
- function zFor(confidence) {
236
- if (confidence >= 0.99) return 2.576;
237
- if (confidence >= 0.95) return 1.96;
238
- if (confidence >= 0.9) return 1.645;
239
- return 1.282;
240
- }
241
- function powerPreflight(opts) {
242
- const composites = opts.baselineComposites.filter((v) => Number.isFinite(v));
243
- if (composites.length < 3) {
244
- throw new Error(
245
- `powerPreflight: need >= 3 finite baseline composites to estimate variance, got ${composites.length}`
246
- );
247
- }
248
- const deltaThreshold = opts.deltaThreshold ?? 0.05;
249
- const confidence = opts.confidence ?? 0.95;
250
- const n = opts.pairedN ?? composites.length;
251
- if (n < 2) throw new Error(`powerPreflight: pairedN must be >= 2, got ${n}`);
252
- const mean = composites.reduce((a, b) => a + b, 0) / composites.length;
253
- const variance = composites.reduce((a, b) => a + (b - mean) * (b - mean), 0) / (composites.length - 1);
254
- const sd = Math.sqrt(variance);
255
- const z = zFor(confidence);
256
- const mde = deltaThreshold + z * Math.SQRT2 * sd / Math.sqrt(n);
257
- const scaleAssumed = composites.every((v) => v >= -1e-3 && v <= 1.5);
258
- const headroom = Math.max(0, 1 - mean);
259
- const underpowered = scaleAssumed && mde > headroom;
260
- const sharedChannelCaveat = opts.sharedScorerChannel ? "Holdout and gate share one scoring channel: raising n/reps reduces only idiosyncratic noise \u2014 systematic judge bias remains and this MDE is a lower bound. Full debiasing needs an independent second scoring channel (different judge/benchmark family)." : void 0;
261
- const recommendation = underpowered ? `UNDERPOWERED: minimum detectable lift ${mde.toFixed(3)} exceeds the ${headroom.toFixed(3)} headroom above the baseline (${mean.toFixed(3)}) \u2014 no achievable effect can ship at this budget. Raise paired n (scenarios x reps) to ~${Math.ceil((z * Math.SQRT2 * sd / Math.max(headroom - deltaThreshold, 0.01)) ** 2)} or reduce worker variance before searching.` : `Minimum detectable lift at n=${n}: ${mde.toFixed(3)} (baseline sd ${sd.toFixed(3)}). Effects smaller than this cannot clear the gate; budget the search for effects you believe exceed it.`;
262
- return {
263
- n,
264
- sd,
265
- mde,
266
- baselineMean: mean,
267
- headroom,
268
- underpowered,
269
- scaleAssumed,
270
- deltaThreshold,
271
- confidence,
272
- ...sharedChannelCaveat ? { sharedChannelCaveat } : {},
273
- recommendation: sharedChannelCaveat ? `${recommendation} ${sharedChannelCaveat}` : recommendation
274
- };
275
- }
276
-
277
- // src/campaign/gates/promotion-policy.ts
278
- function buildEvidenceVector(ctx, objectives, opts = {}) {
279
- if (objectives.length === 0) {
280
- throw new Error("buildEvidenceVector: at least 1 objective required");
281
- }
282
- const minProductiveRuns = opts.minProductiveRuns ?? 3;
283
- const confidence = opts.confidence ?? 0.95;
284
- const resamples = opts.resamples ?? 2e3;
285
- const seed = opts.seed ?? 1337;
286
- const baseline = ctx.baselineJudgeScores ?? ctx.judgeScores;
287
- const scenarioIds = new Set(ctx.scenarios.map((s) => s.id));
288
- const axes = [];
289
- for (const obj of objectives) {
290
- let select;
291
- if (obj.source.kind === "composite") {
292
- select = (s) => s.composite;
293
- } else {
294
- const dim = obj.source.dimension;
295
- select = (s) => s.dimensions[dim];
296
- }
297
- const paired = pairHoldout(ctx.judgeScores, baseline, scenarioIds, select);
298
- const before = obj.direction === "maximize" ? paired.before : paired.after;
299
- const after = obj.direction === "maximize" ? paired.after : paired.before;
300
- const bootstrap = pairedBootstrap(before, after, {
301
- confidence,
302
- resamples,
303
- statistic: "median",
304
- seed
305
- });
306
- const n = paired.before.length;
307
- const floorTolerance = obj.floorTolerance ?? 0.05 * detectScale([...paired.before, ...paired.after]);
308
- const gainThreshold = obj.gainThreshold ?? 0;
309
- const verdict = n < minProductiveRuns ? "few_runs" : bootstrap.low < -floorTolerance ? "regressed" : bootstrap.low > gainThreshold ? "improved" : "flat";
310
- axes.push({
311
- name: obj.name,
312
- source: obj.source,
313
- direction: obj.direction,
314
- bootstrap,
315
- n,
316
- gainThreshold,
317
- floorTolerance,
318
- verdict
319
- });
320
- }
321
- const ns = axes.map((a) => a.n).filter((n) => n > 0);
322
- const minN = ns.length > 0 ? Math.min(...ns) : 0;
323
- return { axes, minN, cost: { candidate: ctx.cost.candidate, baseline: ctx.cost.baseline } };
324
- }
325
- var paretoPolicy = (ev) => {
326
- const contributingGates = ev.axes.map((ax) => ({
327
- name: `objective:${ax.name}`,
328
- passed: ax.verdict === "improved",
329
- detail: {
330
- direction: ax.direction,
331
- source: ax.source,
332
- verdict: ax.verdict,
333
- n: ax.n,
334
- deltaMedian: ax.bootstrap.median,
335
- ciLow: ax.bootstrap.low,
336
- ciHigh: ax.bootstrap.high,
337
- confidence: ax.bootstrap.confidence,
338
- gainThreshold: ax.gainThreshold,
339
- floorTolerance: ax.floorTolerance
340
- }
341
- }));
342
- const regressed = ev.axes.filter((a) => a.verdict === "regressed");
343
- const fewRuns = ev.axes.filter((a) => a.verdict === "few_runs");
344
- const improved = ev.axes.filter((a) => a.verdict === "improved");
345
- let decision;
346
- const reasons = [];
347
- if (regressed.length > 0) {
348
- decision = "hold";
349
- for (const a of regressed) {
350
- reasons.push(
351
- `objective '${a.name}' regressed: good-direction CI.low ${a.bootstrap.low.toFixed(3)} < -${a.floorTolerance} (n=${a.n})`
352
- );
353
- }
354
- } else if (fewRuns.length > 0) {
355
- decision = "need_more_work";
356
- for (const a of fewRuns) {
357
- reasons.push(
358
- `objective '${a.name}' has only n=${a.n} paired runs \u2014 insufficient evidence to claim significance`
359
- );
360
- }
361
- } else if (improved.length > 0) {
362
- decision = "ship";
363
- reasons.push(
364
- `Pareto improvement at the confidence level: ${improved.map(
365
- (a) => `'${a.name}' +${a.bootstrap.median.toFixed(3)} (CI.low ${a.bootstrap.low.toFixed(3)})`
366
- ).join(", ")}; no objective regressed`
367
- );
368
- } else {
369
- decision = "hold";
370
- reasons.push(
371
- "no Pareto improvement: candidate statistically equivalent to baseline on every objective"
372
- );
373
- }
374
- const composite = ev.axes.find((a) => a.source.kind === "composite") ?? ev.axes[0];
375
- return { decision, reasons, contributingGates, delta: composite?.bootstrap.median };
376
- };
377
- function paretoSignificanceGate(options) {
378
- if (options.objectives.length === 0) {
379
- throw new Error("paretoSignificanceGate: at least 1 objective required");
380
- }
381
- const policy = options.policy ?? paretoPolicy;
382
- return {
383
- name: options.name ?? "paretoSignificanceGate",
384
- async decide(ctx) {
385
- const ev = buildEvidenceVector(ctx, options.objectives, options);
386
- return policy(ev);
387
- }
388
- };
389
- }
390
-
391
- // src/campaign/presets/run-eval.ts
392
- async function runEval(opts) {
393
- return runCampaign(opts);
394
- }
395
-
396
- // src/campaign/proposers/evolutionary.ts
397
- function evolutionaryProposer(opts) {
398
- return {
399
- kind: `evolutionary:${opts.mutator.kind}`,
400
- async propose({ currentSurface, findings, populationSize, signal }) {
401
- return opts.mutator.mutate({
402
- findings: findings.length > 0 ? findings : opts.findings ?? [],
403
- currentSurface,
404
- populationSize,
405
- signal
406
- });
407
- }
408
- };
409
- }
410
-
411
- export {
412
- composeGate,
413
- defaultProductionGate,
414
- powerPreflight,
415
- buildEvidenceVector,
416
- paretoPolicy,
417
- paretoSignificanceGate,
418
- runEval,
419
- evolutionaryProposer
420
- };
421
- //# sourceMappingURL=chunk-J22CKVXN.js.map