bridgebench 3.1.0-alpha.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 (86) hide show
  1. package/CITATION.cff +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +249 -0
  4. package/dist/chunk-4TWPCPRP.cjs +1097 -0
  5. package/dist/chunk-4TWPCPRP.cjs.map +1 -0
  6. package/dist/chunk-7YCJSOK7.cjs +398 -0
  7. package/dist/chunk-7YCJSOK7.cjs.map +1 -0
  8. package/dist/chunk-CIXITJW6.cjs +249 -0
  9. package/dist/chunk-CIXITJW6.cjs.map +1 -0
  10. package/dist/chunk-EQHRUV2I.js +1466 -0
  11. package/dist/chunk-EQHRUV2I.js.map +1 -0
  12. package/dist/chunk-JTVNKSMO.js +1096 -0
  13. package/dist/chunk-JTVNKSMO.js.map +1 -0
  14. package/dist/chunk-LFKEV2YL.js +398 -0
  15. package/dist/chunk-LFKEV2YL.js.map +1 -0
  16. package/dist/chunk-NJTYVNP4.cjs +1467 -0
  17. package/dist/chunk-NJTYVNP4.cjs.map +1 -0
  18. package/dist/chunk-UECBSKTD.js +244 -0
  19. package/dist/chunk-UECBSKTD.js.map +1 -0
  20. package/dist/cli.cjs +409 -0
  21. package/dist/cli.cjs.map +1 -0
  22. package/dist/cli.d.cts +1 -0
  23. package/dist/cli.d.ts +1 -0
  24. package/dist/cli.js +408 -0
  25. package/dist/cli.js.map +1 -0
  26. package/dist/client.cjs +42 -0
  27. package/dist/client.cjs.map +1 -0
  28. package/dist/client.d.cts +93 -0
  29. package/dist/client.d.ts +93 -0
  30. package/dist/client.js +42 -0
  31. package/dist/client.js.map +1 -0
  32. package/dist/contracts/index.cjs +47 -0
  33. package/dist/contracts/index.cjs.map +1 -0
  34. package/dist/contracts/index.d.cts +14 -0
  35. package/dist/contracts/index.d.ts +14 -0
  36. package/dist/contracts/index.js +47 -0
  37. package/dist/contracts/index.js.map +1 -0
  38. package/dist/index.cjs +171 -0
  39. package/dist/index.cjs.map +1 -0
  40. package/dist/index.d.cts +214 -0
  41. package/dist/index.d.ts +214 -0
  42. package/dist/index.js +171 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/logger-CCR9Mg1c.d.cts +319 -0
  45. package/dist/logger-QJU7SBDz.d.ts +319 -0
  46. package/dist/reports-4CejmOHf.d.cts +454 -0
  47. package/dist/reports-s2CTnGN8.d.ts +454 -0
  48. package/dist/tasks-CpaCJ6JE.d.cts +151 -0
  49. package/dist/tasks-CpaCJ6JE.d.ts +151 -0
  50. package/dist/tasks.cjs +22 -0
  51. package/dist/tasks.cjs.map +1 -0
  52. package/dist/tasks.d.cts +39 -0
  53. package/dist/tasks.d.ts +39 -0
  54. package/dist/tasks.js +22 -0
  55. package/dist/tasks.js.map +1 -0
  56. package/docs/README.md +25 -0
  57. package/docs/glossary.md +49 -0
  58. package/docs/methodology.md +58 -0
  59. package/docs/private-packs.md +74 -0
  60. package/docs/replay-elo.md +79 -0
  61. package/docs/task-authoring.md +80 -0
  62. package/package.json +137 -0
  63. package/tasks/hallucination/public/boundary-coverage-audit.yaml +274 -0
  64. package/tasks/hallucination/public/boundary-migration-audit.yaml +284 -0
  65. package/tasks/hallucination/public/conflict-dependency-versions.yaml +324 -0
  66. package/tasks/hallucination/public/conflict-runbook-versions.yaml +229 -0
  67. package/tasks/hallucination/public/fabrication-agent-tools.yaml +224 -0
  68. package/tasks/hallucination/public/fabrication-api-surface.yaml +239 -0
  69. package/tasks/hallucination/public/fidelity-commit-attribution.yaml +304 -0
  70. package/tasks/hallucination/public/fidelity-config-drift.yaml +307 -0
  71. package/tasks/hallucination/public/missing-deploy-window.yaml +204 -0
  72. package/tasks/hallucination/public/missing-latency-baseline.yaml +239 -0
  73. package/tasks/hallucination/public/premise-quota-breach.yaml +202 -0
  74. package/tasks/hallucination/public/premise-rollback-cause.yaml +235 -0
  75. package/tasks/reasoning/public/constraint-capacity-allocation.yaml +196 -0
  76. package/tasks/reasoning/public/constraint-deployment-policy.yaml +203 -0
  77. package/tasks/reasoning/public/counterexample-authorization-rule.yaml +278 -0
  78. package/tasks/reasoning/public/counterexample-scheduler-starvation.yaml +290 -0
  79. package/tasks/reasoning/public/root-cache-tenant-leak.yaml +225 -0
  80. package/tasks/reasoning/public/root-event-ordering.yaml +184 -0
  81. package/tasks/reasoning/public/stateful-lease-handoff.yaml +213 -0
  82. package/tasks/reasoning/public/stateful-retry-budget.yaml +222 -0
  83. package/tasks/reasoning/public/synthesis-api-contract.yaml +214 -0
  84. package/tasks/reasoning/public/synthesis-permission-migration.yaml +190 -0
  85. package/tasks/reasoning/public/uncertainty-conflicting-telemetry.yaml +242 -0
  86. package/tasks/reasoning/public/uncertainty-incomplete-incident.yaml +223 -0
package/dist/cli.cjs ADDED
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env node
2
+ "use strict"; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
3
+
4
+
5
+
6
+
7
+
8
+ var _chunk7YCJSOK7cjs = require('./chunk-7YCJSOK7.cjs');
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+ var _chunkNJTYVNP4cjs = require('./chunk-NJTYVNP4.cjs');
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+ var _chunk4TWPCPRPcjs = require('./chunk-4TWPCPRP.cjs');
31
+
32
+
33
+
34
+
35
+ var _chunkCIXITJW6cjs = require('./chunk-CIXITJW6.cjs');
36
+
37
+ // src/commands.ts
38
+ var _path = require('path'); var _path2 = _interopRequireDefault(_path);
39
+ var _commander = require('commander');
40
+
41
+ // src/env.ts
42
+ var _fs = require('fs');
43
+
44
+ var ENV_PATH = _path2.default.join(_chunk4TWPCPRPcjs.findProjectRoot.call(void 0, _chunkCIXITJW6cjs.importMetaUrl), ".env");
45
+ function loadProjectEnv() {
46
+ if (!_fs.existsSync.call(void 0, ENV_PATH)) return { status: "missing" };
47
+ try {
48
+ process.loadEnvFile(ENV_PATH);
49
+ return { status: "loaded" };
50
+ } catch (error) {
51
+ return {
52
+ status: "error",
53
+ reason: error instanceof Error ? error.message : String(error)
54
+ };
55
+ }
56
+ }
57
+
58
+ // src/commands.ts
59
+ var ROOT = process.cwd();
60
+ var defaultDependencies = {
61
+ stdout: (message) => console.log(message),
62
+ stderr: (message) => console.error(message),
63
+ setExitCode: (code) => {
64
+ process.exitCode = code;
65
+ },
66
+ createStore: (category) => new (0, _chunkNJTYVNP4cjs.ArenaStore)(_chunkNJTYVNP4cjs.categoryStoreConfig.call(void 0, category)),
67
+ createTaskLoader: (category) => new (0, _chunk4TWPCPRPcjs.TaskLoader)(category),
68
+ createOpenRouter: (logger) => new (0, _chunk4TWPCPRPcjs.OpenRouterClient)(_nullishCoalesce(process.env.OPENROUTER_API_KEY, () => ( "")), logger),
69
+ publishTasks: _chunk7YCJSOK7cjs.publishTasks,
70
+ publishJournal: _chunk7YCJSOK7cjs.publishJournal,
71
+ resolveApiConfig: _chunk7YCJSOK7cjs.resolveApiConfig
72
+ };
73
+ function parseCategory(value) {
74
+ const parsed = _chunkCIXITJW6cjs.BenchmarkCategorySchema.safeParse(value);
75
+ if (!parsed.success) {
76
+ throw new (0, _commander.InvalidArgumentError)(`expected one of ${_chunkCIXITJW6cjs.CATEGORIES.join(", ")}, received ${value}`);
77
+ }
78
+ return parsed.data;
79
+ }
80
+ function positiveInteger(value) {
81
+ const parsed = Number(value);
82
+ if (!Number.isInteger(parsed) || parsed < 1) {
83
+ throw new (0, _commander.InvalidArgumentError)(`expected a positive integer, received ${value}`);
84
+ }
85
+ return parsed;
86
+ }
87
+ function positiveNumber(value) {
88
+ const parsed = Number(value);
89
+ if (!Number.isFinite(parsed) || parsed <= 0) {
90
+ throw new (0, _commander.InvalidArgumentError)(`expected a positive number, received ${value}`);
91
+ }
92
+ return parsed;
93
+ }
94
+ function collectOption(value, previous = []) {
95
+ return [...previous, value];
96
+ }
97
+ function arenaRunConfigFromOptions(options) {
98
+ const competitorIds = _optionalChain([options, 'access', _ => _.competitor, 'optionalAccess', _2 => _2.length]) ? _chunk4TWPCPRPcjs.resolveCompetitorRoster.call(void 0, options.competitor).map((model) => model.id) : void 0;
99
+ return {
100
+ category: options.category,
101
+ seed: options.seed,
102
+ matches: options.matches,
103
+ maxCostUsd: options.maxCostUsd,
104
+ competitorIds,
105
+ resume: options.resume,
106
+ healthStop: options.healthStop
107
+ };
108
+ }
109
+ function selectedCategories(options, command) {
110
+ if (options.category && options.all) {
111
+ throw new Error(`${command} accepts either --category or --all, not both`);
112
+ }
113
+ if (options.category) return [options.category];
114
+ if (options.all) return [..._chunkCIXITJW6cjs.CATEGORIES];
115
+ throw new Error(`${command} requires --category <category> or --all`);
116
+ }
117
+ function logRoot(category) {
118
+ return _path2.default.join(ROOT, "results", category, "logs");
119
+ }
120
+ function displayPath(target) {
121
+ const relative = _path2.default.relative(ROOT, target);
122
+ return relative.startsWith("..") ? _path2.default.basename(target) : relative;
123
+ }
124
+ function buildProgram(overrides = {}) {
125
+ const dependencies = { ...defaultDependencies, ...overrides };
126
+ const program = new (0, _commander.Command)().name(_chunkNJTYVNP4cjs.PACKAGE_NAME).description("Autonomous BridgeBench arenas for reasoning and hallucination").version(_chunkNJTYVNP4cjs.ENGINE_VERSION).showHelpAfterError().configureHelp({ sortOptions: true, sortSubcommands: true }).configureOutput({
127
+ writeOut: (value) => dependencies.stdout(value.replace(/\n$/, "")),
128
+ writeErr: (value) => dependencies.stderr(value.replace(/\n$/, ""))
129
+ });
130
+ const models = program.command("models").description("Inspect or validate the pinned model registry");
131
+ models.command("list").description("List enabled competitors and judges").action(() => {
132
+ for (const model of _chunk4TWPCPRPcjs.listModels.call(void 0, )) {
133
+ dependencies.stdout(`${model.role.padEnd(10)} ${model.id.padEnd(40)} ${model.displayName}`);
134
+ }
135
+ });
136
+ models.command("validate").description("Validate pinned model IDs and judge capabilities against OpenRouter").action(async () => {
137
+ const client = dependencies.createOpenRouter();
138
+ for (const model of _chunk4TWPCPRPcjs.listModels.call(void 0, )) {
139
+ await client.validateModel(model);
140
+ dependencies.stdout(`\u2713 ${model.id} -> ${model.canonicalSlug}`);
141
+ }
142
+ });
143
+ const tasks = program.command("tasks").description("Validate or publish task packs");
144
+ tasks.command("validate").description("Validate public task schemas and pack balance").option(
145
+ "-c, --category <category>",
146
+ `limit to one category (${_chunkCIXITJW6cjs.CATEGORIES.join(", ")})`,
147
+ parseCategory
148
+ ).option("--file <path>", "validate one public task without pack-balance checks").action(async (options) => {
149
+ if (options.file) {
150
+ if (options.category) {
151
+ throw new Error("tasks validate accepts --file or --category, not both");
152
+ }
153
+ const task = await _chunk4TWPCPRPcjs.validatePublicTaskFile.call(void 0, options.file);
154
+ dependencies.stdout(
155
+ `\u2713 ${task.public.id}: public task validated (${task.public.category}/${task.public.cluster})`
156
+ );
157
+ return;
158
+ }
159
+ const categories = options.category ? [options.category] : _chunkCIXITJW6cjs.CATEGORIES;
160
+ for (const category of categories) {
161
+ const loader = dependencies.createTaskLoader(category);
162
+ const loaded = await loader.loadAll();
163
+ const mode = loader.hasPrivate ? "public + private halves" : "public halves only";
164
+ dependencies.stdout(`\u2713 ${category}: ${loaded.length} tasks validated (${mode})`);
165
+ for (const task of loaded) {
166
+ dependencies.stdout(` ${task.public.cluster.padEnd(28)} ${task.public.id}`);
167
+ }
168
+ }
169
+ });
170
+ tasks.command("publish").description("Publish public and private task halves to the configured API").option(
171
+ "-c, --category <category>",
172
+ `category to publish (${_chunkCIXITJW6cjs.CATEGORIES.join(", ")})`,
173
+ parseCategory
174
+ ).option("--all", "publish every category explicitly", false).action(async (options) => {
175
+ const categories = selectedCategories(options, "tasks publish");
176
+ const config = dependencies.resolveApiConfig();
177
+ dependencies.stdout(`Publishing ${categories.join(", ")} tasks to ${_chunk7YCJSOK7cjs.publishTarget.call(void 0, config)}`);
178
+ for (const category of categories) {
179
+ const result = await dependencies.publishTasks(category, config);
180
+ dependencies.stdout(`\u2713 ${category}: published ${result.imported} tasks`);
181
+ }
182
+ });
183
+ const arena = program.command("arena").description("Run and inspect arena matches");
184
+ arena.command("run").description("Run a paid deterministic match schedule").requiredOption(
185
+ "-c, --category <category>",
186
+ `arena to run (${_chunkCIXITJW6cjs.CATEGORIES.join(", ")})`,
187
+ parseCategory
188
+ ).option("-n, --matches <count>", "number of task-level matches", positiveInteger, 12).option("-s, --seed <seed>", "reproducible scheduling seed", "bridgebench-v3-mvp").option(
189
+ "--max-cost-usd <amount>",
190
+ "hard stop before the next match after this spend",
191
+ positiveNumber,
192
+ 25
193
+ ).option(
194
+ "--competitor <modelId>",
195
+ "limit the run roster; repeat for each competitor (minimum two)",
196
+ collectOption
197
+ ).option("--resume", "skip match IDs already present in the journal", false).option("--debug", "mirror structured log entries to the console", false).option("--no-health-stop", "do not halt when most matches have failed responses").action(async (options) => {
198
+ const config = arenaRunConfigFromOptions(options);
199
+ const logger = new (0, _chunk4TWPCPRPcjs.FileArenaLogger)({
200
+ dir: logRoot(config.category),
201
+ verbose: options.debug
202
+ });
203
+ const cancellation = new AbortController();
204
+ const requestCancellation = () => {
205
+ if (cancellation.signal.aborted) return;
206
+ dependencies.stderr("Cancellation requested; aborting active model calls.");
207
+ cancellation.abort();
208
+ };
209
+ process.once("SIGINT", requestCancellation);
210
+ try {
211
+ dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);
212
+ const store = dependencies.createStore(config.category);
213
+ const loaded = await dependencies.createTaskLoader(config.category).loadAll({ requirePrivate: true });
214
+ const progressOutput = (event) => {
215
+ if (event.type !== "match.completed") return;
216
+ dependencies.stdout(
217
+ `[${event.data.completed}/${event.data.total}] ${event.data.taskId}: ${_nullishCoalesce(event.data.winnerModelId, () => ( "no contest"))} (${event.data.outcome}, $${event.data.costUsd.toFixed(4)})`
218
+ );
219
+ };
220
+ const runner = new (0, _chunkNJTYVNP4cjs.ArenaRunner)(
221
+ dependencies.createOpenRouter(logger),
222
+ store,
223
+ progressOutput,
224
+ logger
225
+ );
226
+ const result = await runner.run(config, loaded, { signal: cancellation.signal });
227
+ if (result.cancelled) {
228
+ dependencies.stdout(
229
+ `Cancelled after ${result.completed} new ${config.category} matches; journaled spend $${result.costUsd.toFixed(4)}. Completed matches remain journaled.`
230
+ );
231
+ dependencies.setExitCode(130);
232
+ } else {
233
+ dependencies.stdout(
234
+ `Completed ${result.completed} new ${config.category} matches; run spend $${result.costUsd.toFixed(4)}.`
235
+ );
236
+ if (result.stoppedForBudget) {
237
+ dependencies.stdout(
238
+ "Stopped at the cost boundary; rerun the same command with --resume."
239
+ );
240
+ }
241
+ }
242
+ const runMatches = store.readAll().filter((match) => match.runId === result.runId);
243
+ if (runMatches.length > 0) {
244
+ const triage = _chunkNJTYVNP4cjs.triageJournal.call(void 0, runMatches);
245
+ dependencies.stdout(`=== Run health ===
246
+ ${_chunkNJTYVNP4cjs.formatTriage.call(void 0, triage)}`);
247
+ }
248
+ } finally {
249
+ process.removeListener("SIGINT", requestCancellation);
250
+ dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);
251
+ }
252
+ });
253
+ arena.command("remote-run").description("Run a match schedule against the configured API and stream live arena events").requiredOption(
254
+ "-c, --category <category>",
255
+ `arena to run (${_chunkCIXITJW6cjs.CATEGORIES.join(", ")})`,
256
+ parseCategory
257
+ ).option("-n, --matches <count>", "number of task-level matches", positiveInteger, 12).option("-s, --seed <seed>", "reproducible scheduling seed", "bridgebench-v3-mvp").option(
258
+ "--max-cost-usd <amount>",
259
+ "hard stop before the next match after this spend",
260
+ positiveNumber,
261
+ 25
262
+ ).option(
263
+ "--competitor <modelId>",
264
+ "limit the run roster; repeat for each competitor (minimum two)",
265
+ collectOption
266
+ ).option("--resume", "skip match IDs already present in the journal", false).option("--debug", "mirror structured log entries to the console", false).option("--no-health-stop", "do not halt when most matches have failed responses").option("--mock", "use deterministic mock model completions instead of OpenRouter", false).option("--no-publish-matches", "skip publishing match results to the API as they complete").action(async (options) => {
267
+ const config = arenaRunConfigFromOptions(options);
268
+ const apiConfig = dependencies.resolveApiConfig();
269
+ const logger = new (0, _chunk4TWPCPRPcjs.FileArenaLogger)({
270
+ dir: logRoot(config.category),
271
+ verbose: options.debug
272
+ });
273
+ dependencies.stdout(
274
+ `Remote ${config.category} run against ${_chunk7YCJSOK7cjs.publishTarget.call(void 0, apiConfig)} (${options.mock ? "mock" : "openrouter"})`
275
+ );
276
+ dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);
277
+ try {
278
+ const result = await _chunk7YCJSOK7cjs.runRemoteArena.call(void 0, apiConfig, {
279
+ config,
280
+ mock: options.mock,
281
+ publishMatches: options.publishMatches,
282
+ logger
283
+ });
284
+ dependencies.stdout(
285
+ `Remote run ${result.runKey}: ${result.completed} matches, $${result.costUsd.toFixed(4)}`
286
+ );
287
+ if (result.cancelled) dependencies.setExitCode(130);
288
+ } finally {
289
+ dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);
290
+ }
291
+ });
292
+ arena.command("verify").description("Validate and replay outcomes, points, manifests, and Elo").option(
293
+ "-c, --category <category>",
294
+ `journal category (${_chunkCIXITJW6cjs.CATEGORIES.join(", ")})`,
295
+ parseCategory,
296
+ "reasoning"
297
+ ).option("--journal <path>", "journal file to verify (defaults to the local category journal)").option("--manifests-dir <path>", "run-manifest directory (defaults to a runs/ sibling)").option("--json", "emit the machine-readable verification result", false).action(
298
+ (options) => {
299
+ const defaults = _chunkNJTYVNP4cjs.categoryStoreConfig.call(void 0, options.category);
300
+ const journalPath = options.journal ? _path2.default.resolve(options.journal) : defaults.journalPath;
301
+ const runsDir = options.manifestsDir ? _path2.default.resolve(options.manifestsDir) : options.journal ? _path2.default.join(_path2.default.dirname(journalPath), "runs") : defaults.runsDir;
302
+ const store = new (0, _chunkNJTYVNP4cjs.ArenaStore)({
303
+ ...defaults,
304
+ journalPath,
305
+ runsDir,
306
+ readOnly: true
307
+ });
308
+ const verified = _chunkNJTYVNP4cjs.verifyJournal.call(void 0, store.readAll(), options.category, {
309
+ manifestForRun: (runId) => store.readRunManifest(runId),
310
+ requireManifests: true
311
+ });
312
+ if (options.json) {
313
+ dependencies.stdout(JSON.stringify(verified, null, 2));
314
+ return;
315
+ }
316
+ dependencies.stdout(
317
+ `\u2713 ${options.category}: verified ${verified.matches.length} matches across ${verified.runs.length} runs`
318
+ );
319
+ for (const warning of verified.warnings) {
320
+ dependencies.stderr(`Warning: ${warning}`);
321
+ }
322
+ }
323
+ );
324
+ arena.command("publish").description("Publish a verified match journal to the configured API").option(
325
+ "-c, --category <category>",
326
+ `journal to publish (${_chunkCIXITJW6cjs.CATEGORIES.join(", ")})`,
327
+ parseCategory
328
+ ).option("--all", "publish every category explicitly", false).option("--allow-empty", "treat an empty journal as a successful no-op", false).action(async (options) => {
329
+ const categories = selectedCategories(options, "arena publish");
330
+ const config = dependencies.resolveApiConfig();
331
+ dependencies.stdout(
332
+ `Publishing ${categories.join(", ")} journals to ${_chunk7YCJSOK7cjs.publishTarget.call(void 0, config)}`
333
+ );
334
+ for (const category of categories) {
335
+ const store = dependencies.createStore(category);
336
+ _chunkNJTYVNP4cjs.verifyJournal.call(void 0, store.readAll(), category, {
337
+ manifestForRun: (runId) => store.readRunManifest(runId),
338
+ requireManifests: true
339
+ });
340
+ const result = await dependencies.publishJournal(category, config);
341
+ if (result.matches === 0 && !options.allowEmpty) {
342
+ throw new Error(
343
+ `The ${category} journal is empty; pass --allow-empty for an intentional no-op`
344
+ );
345
+ }
346
+ dependencies.stdout(
347
+ `\u2713 ${category}: ${result.imported} new, ${result.skipped} already present (of ${result.matches} lines)`
348
+ );
349
+ }
350
+ });
351
+ arena.command("triage").description("Analyze failures, suspicious responses, and judging anomalies").option(
352
+ "-c, --category <category>",
353
+ `journal to analyze (${_chunkCIXITJW6cjs.CATEGORIES.join(", ")})`,
354
+ parseCategory,
355
+ "reasoning"
356
+ ).option("--run <runId>", "limit the report to one run ID").option("--json", "emit the machine-readable report", false).option("--strict", "exit nonzero when any anomaly is found", false).option("--allow-empty", "treat no matching journal lines as success", false).action(
357
+ (options) => {
358
+ const results = dependencies.createStore(options.category).readAll().filter((match) => !options.run || match.runId === options.run);
359
+ if (results.length === 0) {
360
+ const message = options.run ? `No ${options.category} matches found for run ${options.run}.` : `The ${options.category} journal is empty.`;
361
+ if (!options.allowEmpty) throw new Error(message);
362
+ dependencies.stdout(message);
363
+ return;
364
+ }
365
+ const reports = _chunkNJTYVNP4cjs.triageJournal.call(void 0, results);
366
+ dependencies.stdout(
367
+ options.json ? JSON.stringify(reports, null, 2) : _chunkNJTYVNP4cjs.formatTriage.call(void 0, reports)
368
+ );
369
+ const anomalies = reports.reduce((sum, report) => sum + report.anomalies.length, 0);
370
+ if (options.strict && anomalies > 0) {
371
+ dependencies.stderr(`Strict mode: ${anomalies} anomalies found.`);
372
+ dependencies.setExitCode(1);
373
+ }
374
+ }
375
+ );
376
+ arena.command("generation <generationId>").description("Fetch OpenRouter's record for a journaled generation").action(async (generationId) => {
377
+ const data = await dependencies.createOpenRouter().fetchGeneration(generationId);
378
+ dependencies.stdout(JSON.stringify(data, null, 2));
379
+ });
380
+ program.command("report").description("Verify journals and rebuild JSON and Markdown reports").option(
381
+ "-c, --category <category>",
382
+ `limit to one category (${_chunkCIXITJW6cjs.CATEGORIES.join(", ")})`,
383
+ parseCategory
384
+ ).action((options) => {
385
+ const categories = options.category ? [options.category] : _chunkCIXITJW6cjs.CATEGORIES;
386
+ for (const category of categories) {
387
+ const snapshot = _chunkNJTYVNP4cjs.writeReports.call(void 0, dependencies.createStore(category));
388
+ dependencies.stdout(`\u2713 ${category}: wrote reports for ${snapshot.matches.length} matches`);
389
+ }
390
+ });
391
+ return program;
392
+ }
393
+ async function runCli(argv = process.argv, overrides = {}) {
394
+ const dependencies = { ...defaultDependencies, ...overrides };
395
+ const envResult = loadProjectEnv();
396
+ if (envResult.status === "error") {
397
+ dependencies.stderr(
398
+ `Warning: project environment file could not be loaded: ${_chunk4TWPCPRPcjs.redactSecrets.call(void 0, envResult.reason)}`
399
+ );
400
+ }
401
+ await buildProgram(dependencies).parseAsync(argv);
402
+ }
403
+
404
+ // src/cli.ts
405
+ runCli().catch((error) => {
406
+ console.error(`BridgeBench: ${_chunk4TWPCPRPcjs.sanitizeError.call(void 0, error)}`);
407
+ process.exitCode = 1;
408
+ });
409
+ //# sourceMappingURL=cli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/matthewmiller/Desktop/bridgemind/bridgebench/dist/cli.cjs","../src/commands.ts","../src/env.ts","../src/cli.ts"],"names":[],"mappings":"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","file":"/Users/matthewmiller/Desktop/bridgemind/bridgebench/dist/cli.cjs","sourcesContent":[null,"import path from 'node:path';\n\nimport { Command, InvalidArgumentError } from 'commander';\n\nimport { ArenaRunner } from './arena.js';\nimport { loadProjectEnv } from './env.js';\nimport { FileArenaLogger, redactSecrets, type ArenaLogger } from './logger.js';\nimport { listModels, resolveCompetitorRoster } from './models.js';\nimport { OpenRouterClient } from './openrouter.js';\nimport { publishJournal, publishTarget, publishTasks, resolveApiConfig } from './publish.js';\nimport { runRemoteArena } from './remote-arena.js';\nimport { writeReports } from './report.js';\nimport { ArenaStore, categoryStoreConfig } from './store.js';\nimport { TaskLoader, validatePublicTaskFile } from './tasks.js';\nimport { formatTriage, triageJournal } from './triage.js';\nimport {\n BenchmarkCategorySchema,\n CATEGORIES,\n type ArenaEventSink,\n type ArenaRunConfig,\n type BenchmarkCategory,\n} from './types.js';\nimport { verifyJournal } from './verification.js';\nimport { ENGINE_VERSION, PACKAGE_NAME } from './version.js';\n\n// CLI logs and path display anchor to the operator's working directory,\n// matching where categoryStoreConfig writes results.\nconst ROOT = process.cwd();\n\ninterface CategorySelection {\n category?: BenchmarkCategory;\n all?: boolean;\n}\n\nexport interface ArenaRunCliOptions {\n category: BenchmarkCategory;\n matches: number;\n seed: string;\n maxCostUsd: number;\n competitor?: string[];\n resume: boolean;\n debug: boolean;\n healthStop: boolean;\n}\n\nexport interface CliDependencies {\n stdout: (message: string) => void;\n stderr: (message: string) => void;\n setExitCode: (code: number) => void;\n createStore: (category: BenchmarkCategory) => ArenaStore;\n createTaskLoader: (category: BenchmarkCategory) => TaskLoader;\n createOpenRouter: (logger?: ArenaLogger) => OpenRouterClient;\n publishTasks: typeof publishTasks;\n publishJournal: typeof publishJournal;\n resolveApiConfig: typeof resolveApiConfig;\n}\n\nconst defaultDependencies: CliDependencies = {\n stdout: (message) => console.log(message),\n stderr: (message) => console.error(message),\n setExitCode: (code) => {\n process.exitCode = code;\n },\n createStore: (category) => new ArenaStore(categoryStoreConfig(category)),\n createTaskLoader: (category) => new TaskLoader(category),\n createOpenRouter: (logger) => new OpenRouterClient(process.env.OPENROUTER_API_KEY ?? '', logger),\n publishTasks,\n publishJournal,\n resolveApiConfig,\n};\n\nfunction parseCategory(value: string): BenchmarkCategory {\n const parsed = BenchmarkCategorySchema.safeParse(value);\n if (!parsed.success) {\n throw new InvalidArgumentError(`expected one of ${CATEGORIES.join(', ')}, received ${value}`);\n }\n return parsed.data;\n}\n\nfunction positiveInteger(value: string): number {\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed < 1) {\n throw new InvalidArgumentError(`expected a positive integer, received ${value}`);\n }\n return parsed;\n}\n\nfunction positiveNumber(value: string): number {\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new InvalidArgumentError(`expected a positive number, received ${value}`);\n }\n return parsed;\n}\n\nfunction collectOption(value: string, previous: string[] = []): string[] {\n return [...previous, value];\n}\n\nexport function arenaRunConfigFromOptions(options: ArenaRunCliOptions): ArenaRunConfig {\n const competitorIds = options.competitor?.length\n ? resolveCompetitorRoster(options.competitor).map((model) => model.id)\n : undefined;\n return {\n category: options.category,\n seed: options.seed,\n matches: options.matches,\n maxCostUsd: options.maxCostUsd,\n competitorIds,\n resume: options.resume,\n healthStop: options.healthStop,\n };\n}\n\nfunction selectedCategories(options: CategorySelection, command: string): BenchmarkCategory[] {\n if (options.category && options.all) {\n throw new Error(`${command} accepts either --category or --all, not both`);\n }\n if (options.category) return [options.category];\n if (options.all) return [...CATEGORIES];\n throw new Error(`${command} requires --category <category> or --all`);\n}\n\nfunction logRoot(category: BenchmarkCategory): string {\n return path.join(ROOT, 'results', category, 'logs');\n}\n\nfunction displayPath(target: string): string {\n const relative = path.relative(ROOT, target);\n return relative.startsWith('..') ? path.basename(target) : relative;\n}\n\nexport function buildProgram(overrides: Partial<CliDependencies> = {}): Command {\n const dependencies: CliDependencies = { ...defaultDependencies, ...overrides };\n const program = new Command()\n .name(PACKAGE_NAME)\n .description('Autonomous BridgeBench arenas for reasoning and hallucination')\n .version(ENGINE_VERSION)\n .showHelpAfterError()\n .configureHelp({ sortOptions: true, sortSubcommands: true })\n .configureOutput({\n writeOut: (value) => dependencies.stdout(value.replace(/\\n$/, '')),\n writeErr: (value) => dependencies.stderr(value.replace(/\\n$/, '')),\n });\n\n const models = program\n .command('models')\n .description('Inspect or validate the pinned model registry');\n models\n .command('list')\n .description('List enabled competitors and judges')\n .action(() => {\n for (const model of listModels()) {\n dependencies.stdout(`${model.role.padEnd(10)} ${model.id.padEnd(40)} ${model.displayName}`);\n }\n });\n models\n .command('validate')\n .description('Validate pinned model IDs and judge capabilities against OpenRouter')\n .action(async () => {\n const client = dependencies.createOpenRouter();\n for (const model of listModels()) {\n await client.validateModel(model);\n dependencies.stdout(`✓ ${model.id} -> ${model.canonicalSlug}`);\n }\n });\n\n const tasks = program.command('tasks').description('Validate or publish task packs');\n tasks\n .command('validate')\n .description('Validate public task schemas and pack balance')\n .option(\n '-c, --category <category>',\n `limit to one category (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('--file <path>', 'validate one public task without pack-balance checks')\n .action(async (options: { category?: BenchmarkCategory; file?: string }) => {\n if (options.file) {\n if (options.category) {\n throw new Error('tasks validate accepts --file or --category, not both');\n }\n const task = await validatePublicTaskFile(options.file);\n dependencies.stdout(\n `✓ ${task.public.id}: public task validated (${task.public.category}/${task.public.cluster})`,\n );\n return;\n }\n const categories = options.category ? [options.category] : CATEGORIES;\n for (const category of categories) {\n const loader = dependencies.createTaskLoader(category);\n const loaded = await loader.loadAll();\n const mode = loader.hasPrivate ? 'public + private halves' : 'public halves only';\n dependencies.stdout(`✓ ${category}: ${loaded.length} tasks validated (${mode})`);\n for (const task of loaded) {\n dependencies.stdout(` ${task.public.cluster.padEnd(28)} ${task.public.id}`);\n }\n }\n });\n\n tasks\n .command('publish')\n .description('Publish public and private task halves to the configured API')\n .option(\n '-c, --category <category>',\n `category to publish (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('--all', 'publish every category explicitly', false)\n .action(async (options: CategorySelection) => {\n const categories = selectedCategories(options, 'tasks publish');\n const config = dependencies.resolveApiConfig();\n dependencies.stdout(`Publishing ${categories.join(', ')} tasks to ${publishTarget(config)}`);\n for (const category of categories) {\n const result = await dependencies.publishTasks(category, config);\n dependencies.stdout(`✓ ${category}: published ${result.imported} tasks`);\n }\n });\n\n const arena = program.command('arena').description('Run and inspect arena matches');\n arena\n .command('run')\n .description('Run a paid deterministic match schedule')\n .requiredOption(\n '-c, --category <category>',\n `arena to run (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('-n, --matches <count>', 'number of task-level matches', positiveInteger, 12)\n .option('-s, --seed <seed>', 'reproducible scheduling seed', 'bridgebench-v3-mvp')\n .option(\n '--max-cost-usd <amount>',\n 'hard stop before the next match after this spend',\n positiveNumber,\n 25,\n )\n .option(\n '--competitor <modelId>',\n 'limit the run roster; repeat for each competitor (minimum two)',\n collectOption,\n )\n .option('--resume', 'skip match IDs already present in the journal', false)\n .option('--debug', 'mirror structured log entries to the console', false)\n .option('--no-health-stop', 'do not halt when most matches have failed responses')\n .action(async (options: ArenaRunCliOptions) => {\n const config = arenaRunConfigFromOptions(options);\n const logger = new FileArenaLogger({\n dir: logRoot(config.category),\n verbose: options.debug,\n });\n const cancellation = new AbortController();\n const requestCancellation = (): void => {\n if (cancellation.signal.aborted) return;\n dependencies.stderr('Cancellation requested; aborting active model calls.');\n cancellation.abort();\n };\n process.once('SIGINT', requestCancellation);\n try {\n dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);\n const store = dependencies.createStore(config.category);\n const loaded = await dependencies\n .createTaskLoader(config.category)\n .loadAll({ requirePrivate: true });\n const progressOutput: ArenaEventSink = (event) => {\n if (event.type !== 'match.completed') return;\n dependencies.stdout(\n `[${event.data.completed}/${event.data.total}] ${event.data.taskId}: ${event.data.winnerModelId ?? 'no contest'} (${event.data.outcome}, $${event.data.costUsd.toFixed(4)})`,\n );\n };\n const runner = new ArenaRunner(\n dependencies.createOpenRouter(logger),\n store,\n progressOutput,\n logger,\n );\n const result = await runner.run(config, loaded, { signal: cancellation.signal });\n if (result.cancelled) {\n dependencies.stdout(\n `Cancelled after ${result.completed} new ${config.category} matches; journaled spend $${result.costUsd.toFixed(4)}. Completed matches remain journaled.`,\n );\n dependencies.setExitCode(130);\n } else {\n dependencies.stdout(\n `Completed ${result.completed} new ${config.category} matches; run spend $${result.costUsd.toFixed(4)}.`,\n );\n if (result.stoppedForBudget) {\n dependencies.stdout(\n 'Stopped at the cost boundary; rerun the same command with --resume.',\n );\n }\n }\n const runMatches = store.readAll().filter((match) => match.runId === result.runId);\n if (runMatches.length > 0) {\n const triage = triageJournal(runMatches);\n dependencies.stdout(`=== Run health ===\\n${formatTriage(triage)}`);\n }\n } finally {\n process.removeListener('SIGINT', requestCancellation);\n dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);\n }\n });\n\n arena\n .command('remote-run')\n .description('Run a match schedule against the configured API and stream live arena events')\n .requiredOption(\n '-c, --category <category>',\n `arena to run (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('-n, --matches <count>', 'number of task-level matches', positiveInteger, 12)\n .option('-s, --seed <seed>', 'reproducible scheduling seed', 'bridgebench-v3-mvp')\n .option(\n '--max-cost-usd <amount>',\n 'hard stop before the next match after this spend',\n positiveNumber,\n 25,\n )\n .option(\n '--competitor <modelId>',\n 'limit the run roster; repeat for each competitor (minimum two)',\n collectOption,\n )\n .option('--resume', 'skip match IDs already present in the journal', false)\n .option('--debug', 'mirror structured log entries to the console', false)\n .option('--no-health-stop', 'do not halt when most matches have failed responses')\n .option('--mock', 'use deterministic mock model completions instead of OpenRouter', false)\n .option('--no-publish-matches', 'skip publishing match results to the API as they complete')\n .action(async (options: ArenaRunCliOptions & { mock: boolean; publishMatches: boolean }) => {\n const config = arenaRunConfigFromOptions(options);\n const apiConfig = dependencies.resolveApiConfig();\n const logger = new FileArenaLogger({\n dir: logRoot(config.category),\n verbose: options.debug,\n });\n dependencies.stdout(\n `Remote ${config.category} run against ${publishTarget(apiConfig)} (${options.mock ? 'mock' : 'openrouter'})`,\n );\n dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);\n try {\n const result = await runRemoteArena(apiConfig, {\n config,\n mock: options.mock,\n publishMatches: options.publishMatches,\n logger,\n });\n dependencies.stdout(\n `Remote run ${result.runKey}: ${result.completed} matches, $${result.costUsd.toFixed(4)}`,\n );\n if (result.cancelled) dependencies.setExitCode(130);\n } finally {\n dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);\n }\n });\n\n arena\n .command('verify')\n .description('Validate and replay outcomes, points, manifests, and Elo')\n .option(\n '-c, --category <category>',\n `journal category (${CATEGORIES.join(', ')})`,\n parseCategory,\n 'reasoning',\n )\n .option('--journal <path>', 'journal file to verify (defaults to the local category journal)')\n .option('--manifests-dir <path>', 'run-manifest directory (defaults to a runs/ sibling)')\n .option('--json', 'emit the machine-readable verification result', false)\n .action(\n (options: {\n category: BenchmarkCategory;\n journal?: string;\n manifestsDir?: string;\n json: boolean;\n }) => {\n const defaults = categoryStoreConfig(options.category);\n const journalPath = options.journal ? path.resolve(options.journal) : defaults.journalPath;\n const runsDir = options.manifestsDir\n ? path.resolve(options.manifestsDir)\n : options.journal\n ? path.join(path.dirname(journalPath), 'runs')\n : defaults.runsDir;\n const store = new ArenaStore({\n ...defaults,\n journalPath,\n runsDir,\n readOnly: true,\n });\n const verified = verifyJournal(store.readAll(), options.category, {\n manifestForRun: (runId) => store.readRunManifest(runId),\n requireManifests: true,\n });\n if (options.json) {\n dependencies.stdout(JSON.stringify(verified, null, 2));\n return;\n }\n dependencies.stdout(\n `✓ ${options.category}: verified ${verified.matches.length} matches across ${verified.runs.length} runs`,\n );\n for (const warning of verified.warnings) {\n dependencies.stderr(`Warning: ${warning}`);\n }\n },\n );\n\n arena\n .command('publish')\n .description('Publish a verified match journal to the configured API')\n .option(\n '-c, --category <category>',\n `journal to publish (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('--all', 'publish every category explicitly', false)\n .option('--allow-empty', 'treat an empty journal as a successful no-op', false)\n .action(async (options: CategorySelection & { allowEmpty: boolean }) => {\n const categories = selectedCategories(options, 'arena publish');\n const config = dependencies.resolveApiConfig();\n dependencies.stdout(\n `Publishing ${categories.join(', ')} journals to ${publishTarget(config)}`,\n );\n for (const category of categories) {\n const store = dependencies.createStore(category);\n verifyJournal(store.readAll(), category, {\n manifestForRun: (runId) => store.readRunManifest(runId),\n requireManifests: true,\n });\n const result = await dependencies.publishJournal(category, config);\n if (result.matches === 0 && !options.allowEmpty) {\n throw new Error(\n `The ${category} journal is empty; pass --allow-empty for an intentional no-op`,\n );\n }\n dependencies.stdout(\n `✓ ${category}: ${result.imported} new, ${result.skipped} already present (of ${result.matches} lines)`,\n );\n }\n });\n\n arena\n .command('triage')\n .description('Analyze failures, suspicious responses, and judging anomalies')\n .option(\n '-c, --category <category>',\n `journal to analyze (${CATEGORIES.join(', ')})`,\n parseCategory,\n 'reasoning',\n )\n .option('--run <runId>', 'limit the report to one run ID')\n .option('--json', 'emit the machine-readable report', false)\n .option('--strict', 'exit nonzero when any anomaly is found', false)\n .option('--allow-empty', 'treat no matching journal lines as success', false)\n .action(\n (options: {\n category: BenchmarkCategory;\n run?: string;\n json: boolean;\n strict: boolean;\n allowEmpty: boolean;\n }) => {\n const results = dependencies\n .createStore(options.category)\n .readAll()\n .filter((match) => !options.run || match.runId === options.run);\n if (results.length === 0) {\n const message = options.run\n ? `No ${options.category} matches found for run ${options.run}.`\n : `The ${options.category} journal is empty.`;\n if (!options.allowEmpty) throw new Error(message);\n dependencies.stdout(message);\n return;\n }\n const reports = triageJournal(results);\n dependencies.stdout(\n options.json ? JSON.stringify(reports, null, 2) : formatTriage(reports),\n );\n const anomalies = reports.reduce((sum, report) => sum + report.anomalies.length, 0);\n if (options.strict && anomalies > 0) {\n dependencies.stderr(`Strict mode: ${anomalies} anomalies found.`);\n dependencies.setExitCode(1);\n }\n },\n );\n\n arena\n .command('generation <generationId>')\n .description(\"Fetch OpenRouter's record for a journaled generation\")\n .action(async (generationId: string) => {\n const data = await dependencies.createOpenRouter().fetchGeneration(generationId);\n dependencies.stdout(JSON.stringify(data, null, 2));\n });\n\n program\n .command('report')\n .description('Verify journals and rebuild JSON and Markdown reports')\n .option(\n '-c, --category <category>',\n `limit to one category (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .action((options: { category?: BenchmarkCategory }) => {\n const categories = options.category ? [options.category] : CATEGORIES;\n for (const category of categories) {\n const snapshot = writeReports(dependencies.createStore(category));\n dependencies.stdout(`✓ ${category}: wrote reports for ${snapshot.matches.length} matches`);\n }\n });\n\n return program;\n}\n\nexport async function runCli(\n argv: string[] = process.argv,\n overrides: Partial<CliDependencies> = {},\n): Promise<void> {\n const dependencies = { ...defaultDependencies, ...overrides };\n const envResult = loadProjectEnv();\n if (envResult.status === 'error') {\n dependencies.stderr(\n `Warning: project environment file could not be loaded: ${redactSecrets(envResult.reason)}`,\n );\n }\n await buildProgram(dependencies).parseAsync(argv);\n}\n","import { existsSync } from 'node:fs';\nimport path from 'node:path';\n\nimport { findProjectRoot } from './paths.js';\n\nexport const ENV_PATH = path.join(findProjectRoot(import.meta.url), '.env');\n\nexport type ProjectEnvLoadResult =\n { status: 'missing' } | { status: 'loaded' } | { status: 'error'; reason: string };\n\n/**\n * Loads the project-root .env into process.env regardless of the process cwd\n * or launch flags. Variables already present in the environment win, matching\n * node's --env-file precedence. Safe to call repeatedly: a key added to .env\n * after startup is picked up on the next call.\n */\nexport function loadProjectEnv(): ProjectEnvLoadResult {\n if (!existsSync(ENV_PATH)) return { status: 'missing' };\n try {\n process.loadEnvFile(ENV_PATH);\n return { status: 'loaded' };\n } catch (error) {\n return {\n status: 'error',\n reason: error instanceof Error ? error.message : String(error),\n };\n }\n}\n","#!/usr/bin/env node\n\nimport { runCli } from './commands.js';\nimport { sanitizeError } from './openrouter.js';\n\nrunCli().catch((error) => {\n console.error(`BridgeBench: ${sanitizeError(error)}`);\n process.exitCode = 1;\n});\n"]}
package/dist/cli.d.cts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node