@coinrithm/mcp-trading 0.7.15 → 0.7.16

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 (53) hide show
  1. package/CHANGELOG.md +127 -8
  2. package/README.md +18 -3
  3. package/dist/agent/bench/bench.d.ts +59 -0
  4. package/dist/agent/bench/bench.js +319 -0
  5. package/dist/agent/bench/cassette.d.ts +97 -0
  6. package/dist/agent/bench/cassette.js +181 -0
  7. package/dist/agent/bench/costs.d.ts +28 -0
  8. package/dist/agent/bench/costs.js +60 -0
  9. package/dist/agent/bench/labelBuilder.d.ts +123 -0
  10. package/dist/agent/bench/labelBuilder.js +282 -0
  11. package/dist/agent/bench/labels.d.ts +75 -0
  12. package/dist/agent/bench/labels.js +259 -0
  13. package/dist/agent/bench/recordingClient.d.ts +51 -0
  14. package/dist/agent/bench/recordingClient.js +193 -0
  15. package/dist/agent/bench/replayClient.d.ts +41 -0
  16. package/dist/agent/bench/replayClient.js +262 -0
  17. package/dist/agent/bench/report.d.ts +115 -0
  18. package/dist/agent/bench/report.js +435 -0
  19. package/dist/agent/cli.d.ts +25 -0
  20. package/dist/agent/cli.js +300 -2
  21. package/dist/agent/client.d.ts +1 -0
  22. package/dist/agent/client.js +6 -0
  23. package/dist/agent/configurationWarnings.js +11 -0
  24. package/dist/agent/decision.d.ts +3 -0
  25. package/dist/agent/decision.js +85 -3
  26. package/dist/agent/decisionValidator.d.ts +30 -0
  27. package/dist/agent/decisionValidator.js +147 -2
  28. package/dist/agent/engine.d.ts +3 -3
  29. package/dist/agent/engine.js +3 -3
  30. package/dist/agent/observe.d.ts +27 -2
  31. package/dist/agent/observe.js +529 -89
  32. package/dist/agent/prompt.d.ts +4 -1
  33. package/dist/agent/prompt.js +98 -7
  34. package/dist/agent/promptTables.d.ts +1 -0
  35. package/dist/agent/promptTables.js +33 -0
  36. package/dist/agent/providerCapabilities.d.ts +6 -0
  37. package/dist/agent/providerCapabilities.js +23 -0
  38. package/dist/agent/providers.d.ts +25 -7
  39. package/dist/agent/providers.js +188 -48
  40. package/dist/agent/resolve.js +3 -0
  41. package/dist/agent/runner.d.ts +8 -0
  42. package/dist/agent/runner.js +59 -15
  43. package/dist/agent/setups.d.ts +2 -1
  44. package/dist/agent/setups.js +17 -16
  45. package/dist/agent/signals.d.ts +33 -0
  46. package/dist/agent/signals.js +89 -0
  47. package/dist/agent/skill.js +37 -0
  48. package/dist/agent/skillValidator.js +33 -0
  49. package/dist/agent/strictLint.js +29 -0
  50. package/dist/agent/types.d.ts +149 -12
  51. package/dist/agent/universe.d.ts +78 -0
  52. package/dist/agent/universe.js +215 -0
  53. package/package.json +3 -2
@@ -0,0 +1,435 @@
1
+ // The bench report: deterministic JSON over replayed cycles.
2
+ //
3
+ // Determinism rules: no wall-clock field, cassettes in chronological order,
4
+ // every map key sorted, every number rounded to 6 decimals, and the bootstrap
5
+ // driven by a SEEDED PRNG (never Math.random). The same corpus and the same
6
+ // decisions therefore produce the same JSON and the same contentHash (sha256
7
+ // of the canonical JSON, the scorecard.ts approach). With a real model the
8
+ // decisions themselves vary run to run; that is what --repeats measures.
9
+ //
10
+ // Statistics, stated plainly:
11
+ // - Paired comparison: for every cassette both variants saw, the per-cassette
12
+ // difference of a metric (each side averaged over its repeats). The report
13
+ // gives the mean difference and a percentile bootstrap 95% CI over
14
+ // cassettes. A CI that excludes 0 is evidence of a difference on THIS
15
+ // corpus, not a guarantee; few cassettes give wide intervals.
16
+ // - Chronological split: cassettes sorted by asOf; the first 70% are "tune",
17
+ // the last 30% "holdout". Choose between variants on tune, report holdout.
18
+ // - Failures are results: model failures, runtime errors and validator
19
+ // rejections are counted per variant and never dropped from a denominator.
20
+ // - Calibrated null: a single A/A comparison can exclude 0 by chance (about
21
+ // 1 time in 20 at 95%). So the report does not demand that every A/A CI
22
+ // covers 0. Instead, per variant with 2+ repeats, it builds many seeded
23
+ // A/A comparisons from the variant's OWN repeats (two different repeats
24
+ // per cassette) and reports how often the 95% CI test fires. That
25
+ // false-positive rate should sit near 5%; far above means the CI is too
26
+ // narrow for this corpus and its differences must not be trusted.
27
+ import { canonicalJson, sha256Hex } from "./cassette.js";
28
+ export const REPORT_SCHEMA = "coinrithm.bench.report.v1";
29
+ export const TUNE_FRACTION = 0.7;
30
+ export const NULL_CALIBRATION_RESAMPLINGS = 200;
31
+ export const NULL_CALIBRATION_BOOTSTRAP = 500;
32
+ export const NULL_CALIBRATION_TARGET = 0.05;
33
+ /** Deterministic 32-bit PRNG (mulberry32). Same seed, same sequence. */
34
+ export function mulberry32(seed) {
35
+ let a = seed >>> 0;
36
+ return () => {
37
+ a = (a + 0x6d2b79f5) >>> 0;
38
+ let t = a;
39
+ t = Math.imul(t ^ (t >>> 15), t | 1);
40
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
41
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
42
+ };
43
+ }
44
+ function fnv1a32(s) {
45
+ let h = 0x811c9dc5;
46
+ for (let i = 0; i < s.length; i++) {
47
+ h ^= s.charCodeAt(i);
48
+ h = Math.imul(h, 0x01000193);
49
+ }
50
+ return h >>> 0;
51
+ }
52
+ const mean = (xs) => xs.length === 0 ? null : xs.reduce((s, x) => s + x, 0) / xs.length;
53
+ /**
54
+ * Percentile bootstrap CI of the mean of `diffs`. Deterministic for a given
55
+ * seed. n = 0 gives null; n = 1 gives a degenerate [d, d] interval.
56
+ */
57
+ export function bootstrapMeanCi(diffs, seed, resamples) {
58
+ const n = diffs.length;
59
+ if (n === 0)
60
+ return null;
61
+ const rand = mulberry32(seed);
62
+ const means = [];
63
+ for (let b = 0; b < resamples; b++) {
64
+ let s = 0;
65
+ for (let i = 0; i < n; i++)
66
+ s += diffs[Math.floor(rand() * n)];
67
+ means.push(s / n);
68
+ }
69
+ means.sort((x, y) => x - y);
70
+ const at = (q) => means[Math.min(means.length - 1, Math.max(0, Math.floor(q * means.length)))];
71
+ return [at(0.025), at(0.975)];
72
+ }
73
+ /**
74
+ * Calibrated-null check of the paired bootstrap test. `repeatValues` holds,
75
+ * per cassette, one metric value per repeat of the SAME variant. Each of the
76
+ * `resamplings` draws two different repeats per cassette (seeded), treats them
77
+ * as A and B, and runs the same 95% CI test the comparisons use. Under this
78
+ * construction there is no true difference, so the share of draws whose CI
79
+ * excludes 0 estimates the test's false-positive rate. A draw where every
80
+ * difference is exactly 0 has CI [0, 0] and counts as a non-rejection.
81
+ */
82
+ export function calibrateNull(repeatValues, seed, resamplings = NULL_CALIBRATION_RESAMPLINGS, bootstrapResamples = NULL_CALIBRATION_BOOTSTRAP) {
83
+ const usable = repeatValues.filter((v) => v.length >= 2);
84
+ if (usable.length < 2)
85
+ return {
86
+ resamplings: 0,
87
+ usableCassettes: usable.length,
88
+ falsePositives: 0,
89
+ falsePositiveRate: null,
90
+ };
91
+ const rand = mulberry32(seed);
92
+ let falsePositives = 0;
93
+ for (let s = 0; s < resamplings; s++) {
94
+ const diffs = usable.map((v) => {
95
+ const i = Math.floor(rand() * v.length);
96
+ let j = Math.floor(rand() * (v.length - 1));
97
+ if (j >= i)
98
+ j += 1;
99
+ return v[i] - v[j];
100
+ });
101
+ const innerSeed = Math.floor(rand() * 4294967296) >>> 0;
102
+ if (diffs.every((d) => d === 0))
103
+ continue;
104
+ const ci = bootstrapMeanCi(diffs, innerSeed, bootstrapResamples);
105
+ if (ci && (ci[0] > 0 || ci[1] < 0))
106
+ falsePositives += 1;
107
+ }
108
+ return {
109
+ resamplings,
110
+ usableCassettes: usable.length,
111
+ falsePositives,
112
+ falsePositiveRate: falsePositives / resamplings,
113
+ };
114
+ }
115
+ /** Jaccard similarity of two action-key sets; two empty sets count as 1. */
116
+ export function jaccard(a, b) {
117
+ const sa = new Set(a);
118
+ const sb = new Set(b);
119
+ if (sa.size === 0 && sb.size === 0)
120
+ return 1;
121
+ let inter = 0;
122
+ for (const k of sa)
123
+ if (sb.has(k))
124
+ inter += 1;
125
+ return inter / (sa.size + sb.size - inter);
126
+ }
127
+ /** Round every finite number in a JSON-like value (deterministic output). */
128
+ export function roundDeep(value, digits = 6) {
129
+ if (typeof value === "number") {
130
+ if (!Number.isFinite(value))
131
+ return null;
132
+ const f = 10 ** digits;
133
+ const r = Math.round(value * f) / f;
134
+ return Object.is(r, -0) ? 0 : r;
135
+ }
136
+ if (Array.isArray(value))
137
+ return value.map((v) => roundDeep(v, digits));
138
+ if (value && typeof value === "object") {
139
+ const out = {};
140
+ for (const [k, v] of Object.entries(value)) {
141
+ if (v !== undefined)
142
+ out[k] = roundDeep(v, digits);
143
+ }
144
+ return out;
145
+ }
146
+ return value;
147
+ }
148
+ // ── per-cassette summaries ──────────────────────────────────────────────────
149
+ const PAIRED_METRICS = [
150
+ "acceptedActions",
151
+ "rejectedActions",
152
+ "actRate",
153
+ "modelFailureRate",
154
+ "labelledPnlMusd",
155
+ "pmBrier",
156
+ "pmReturnOnStake",
157
+ "futuresReturnOnMargin",
158
+ ];
159
+ function acceptedKeys(row) {
160
+ return [
161
+ ...new Set(row.actions.filter((a) => a.accepted).map((a) => a.key)),
162
+ ].sort();
163
+ }
164
+ function rowValues(row) {
165
+ const accepted = row.actions.filter((a) => a.accepted);
166
+ const labelled = accepted
167
+ .map((a) => a.score)
168
+ .filter((s) => s?.status === "labelled");
169
+ const pm = labelled.filter((s) => s.venue === "pm");
170
+ const futures = labelled.filter((s) => s.venue === "futures");
171
+ const briers = pm
172
+ .map((s) => s.brier)
173
+ .filter((b) => b !== undefined);
174
+ const out = {
175
+ acceptedActions: accepted.length,
176
+ rejectedActions: row.actions.length - accepted.length,
177
+ actRate: row.decisionType === "act" ? 1 : 0,
178
+ modelFailureRate: row.modelFailed || row.runtimeError ? 1 : 0,
179
+ };
180
+ // A genuine no-action cycle earns zero. Missing outcomes/inputs are unknown,
181
+ // not cash: zero-filling them biases comparisons toward unlabelled trades.
182
+ if (!row.runtimeError &&
183
+ row.missingInputs.length === 0 &&
184
+ accepted.every((a) => a.score?.status === "labelled"))
185
+ out.labelledPnlMusd = labelled.reduce((s, x) => s + x.pnlMusd, 0);
186
+ const pmBrier = mean(briers);
187
+ if (pmBrier !== null)
188
+ out.pmBrier = pmBrier;
189
+ const pmRet = mean(pm.map((s) => s.returnOnStake));
190
+ if (pmRet !== null)
191
+ out.pmReturnOnStake = pmRet;
192
+ const futRet = mean(futures.map((s) => s.returnOnMargin));
193
+ if (futRet !== null)
194
+ out.futuresReturnOnMargin = futRet;
195
+ return out;
196
+ }
197
+ function summarize(rows) {
198
+ const perRepeat = rows.map(rowValues);
199
+ const values = {};
200
+ for (const m of PAIRED_METRICS) {
201
+ // Compare equal repeat sets, never just whichever repeats got labels.
202
+ if (m === "labelledPnlMusd" && perRepeat.some((r) => r[m] === undefined))
203
+ continue;
204
+ const v = mean(perRepeat.map((r) => r[m]).filter((x) => x !== undefined));
205
+ if (v !== null)
206
+ values[m] = v;
207
+ }
208
+ return { values, perRepeat, actionSets: rows.map(acceptedKeys) };
209
+ }
210
+ // ── variant aggregates ──────────────────────────────────────────────────────
211
+ function variantMetrics(rows, summaries) {
212
+ const decisionMix = {
213
+ act: 0,
214
+ skip: 0,
215
+ gate_skip: 0,
216
+ model_error: 0,
217
+ runtime_error: 0,
218
+ };
219
+ // Trigger code occurrences across rows (a row can carry several codes), not
220
+ // model calls: llmCallMade/decisionMix count those.
221
+ const triggerMix = {};
222
+ const rejectCodes = {};
223
+ let proposed = 0;
224
+ let accepted = 0;
225
+ const missingKeys = new Set();
226
+ const scores = [];
227
+ for (const row of rows) {
228
+ decisionMix[row.decisionType] = (decisionMix[row.decisionType] ?? 0) + 1;
229
+ for (const code of row.triggerCodes ?? [])
230
+ triggerMix[code] = (triggerMix[code] ?? 0) + 1;
231
+ for (const a of row.actions) {
232
+ proposed += 1;
233
+ if (a.accepted) {
234
+ accepted += 1;
235
+ if (a.score)
236
+ scores.push(a.score);
237
+ }
238
+ else {
239
+ const code = a.code ?? "unknown";
240
+ rejectCodes[code] = (rejectCodes[code] ?? 0) + 1;
241
+ }
242
+ }
243
+ for (const k of row.missingInputs)
244
+ missingKeys.add(k);
245
+ }
246
+ const pm = scores.filter((s) => s.status === "labelled" && s.venue === "pm");
247
+ const futures = scores.filter((s) => s.status === "labelled" && s.venue === "futures");
248
+ const consistent = summaries.filter((s) => s.actionSets.every((set) => set.join("|") === s.actionSets[0].join("|"))).length;
249
+ return {
250
+ cassettes: summaries.length,
251
+ cycles: rows.length,
252
+ modelFailures: rows.filter((r) => r.modelFailed).length,
253
+ runtimeErrors: rows.filter((r) => r.runtimeError).length,
254
+ decisionMix,
255
+ triggerMix,
256
+ actions: {
257
+ proposed,
258
+ accepted,
259
+ rejected: proposed - accepted,
260
+ rejectCodes,
261
+ },
262
+ repeatConsistency: summaries.length === 0 ? null : consistent / summaries.length,
263
+ synthesizedQuotes: rows.reduce((s, r) => s + r.synthesizedQuotes, 0),
264
+ cyclesWithMissingInputs: rows.filter((r) => r.missingInputs.length > 0)
265
+ .length,
266
+ missingInputKeys: [...missingKeys].sort(),
267
+ refusedWrites: rows.reduce((s, r) => s + r.refusedWrites.length, 0),
268
+ pnlComparableCycles: rows.filter((r) => rowValues(r).labelledPnlMusd !== undefined).length,
269
+ pnlExcludedCycles: rows.filter((r) => rowValues(r).labelledPnlMusd === undefined).length,
270
+ labelled: {
271
+ pmOpens: pm.length,
272
+ pmBrierMean: mean(pm.map((s) => s.brier).filter((b) => b !== undefined)),
273
+ pmMarketBrierMean: mean(pm
274
+ .map((s) => s.marketBrier)
275
+ .filter((b) => b !== undefined)),
276
+ pmReturnOnStakeMean: mean(pm.map((s) => s.returnOnStake)),
277
+ futuresOpens: futures.length,
278
+ futuresReturnOnMarginMean: mean(futures.map((s) => s.returnOnMargin)),
279
+ futuresSameBarStopFirst: futures.filter((s) => s.sameBarStopAndTarget)
280
+ .length,
281
+ futuresFundingIncluded: futures.filter((s) => s.fundingIncluded).length,
282
+ unlabelledOpens: scores.filter((s) => s.status === "unlabelled").length,
283
+ unlabelledGapOpens: scores.filter((s) => s.status === "unlabelled_gap")
284
+ .length,
285
+ labelledPnlMusd: [...pm, ...futures].reduce((s, x) => s + x.pnlMusd, 0),
286
+ },
287
+ };
288
+ }
289
+ /** Chronological order: asOf (parsed when possible), then id. */
290
+ export function chronological(cassettes) {
291
+ const t = (c) => {
292
+ const ms = Date.parse(c.asOf);
293
+ return Number.isFinite(ms) ? ms : Number.POSITIVE_INFINITY;
294
+ };
295
+ return [...cassettes].sort((a, b) => t(a) - t(b) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
296
+ }
297
+ export function buildReport(input) {
298
+ const ordered = chronological(input.cassettes);
299
+ const ids = ordered.map((c) => c.id);
300
+ const tuneCount = Math.floor(ids.length * TUNE_FRACTION);
301
+ const splits = {
302
+ all: ids,
303
+ tune: ids.slice(0, tuneCount),
304
+ holdout: ids.slice(tuneCount),
305
+ };
306
+ const byKey = new Map();
307
+ for (const row of input.rows) {
308
+ const k = `${row.variant}\u0000${row.cassetteId}`;
309
+ const list = byKey.get(k) ?? [];
310
+ list.push(row);
311
+ byKey.set(k, list);
312
+ }
313
+ const rowsOf = (variant, id) => (byKey.get(`${variant}\u0000${id}`) ?? []).sort((a, b) => a.repeat - b.repeat);
314
+ const summaryCache = new Map();
315
+ const summaryOf = (variant, id) => {
316
+ const k = `${variant}\u0000${id}`;
317
+ if (!summaryCache.has(k)) {
318
+ const rows = rowsOf(variant, id);
319
+ summaryCache.set(k, rows.length > 0 ? summarize(rows) : undefined);
320
+ }
321
+ return summaryCache.get(k);
322
+ };
323
+ const variants = {};
324
+ for (const v of input.variants) {
325
+ const bySplit = {};
326
+ for (const [split, splitIds] of Object.entries(splits)) {
327
+ const rows = splitIds.flatMap((id) => rowsOf(v.name, id));
328
+ const summaries = splitIds
329
+ .map((id) => summaryOf(v.name, id))
330
+ .filter((s) => s !== undefined);
331
+ bySplit[split] = variantMetrics(rows, summaries);
332
+ }
333
+ const allSummaries = ids
334
+ .map((id) => summaryOf(v.name, id))
335
+ .filter((s) => s !== undefined);
336
+ const nullMetrics = {};
337
+ if (v.kind === "variant" && input.repeats >= 2) {
338
+ for (const m of PAIRED_METRICS) {
339
+ nullMetrics[m] = calibrateNull(allSummaries.map((s) => s.perRepeat
340
+ .map((r) => r[m])
341
+ .filter((x) => x !== undefined)), (input.seed ^ fnv1a32(`null|${v.name}|${m}`)) >>> 0);
342
+ }
343
+ }
344
+ variants[v.name] = {
345
+ kind: v.kind,
346
+ ...(v.definitionHash ? { definitionHash: v.definitionHash } : {}),
347
+ ...bySplit,
348
+ nullCalibration: Object.keys(nullMetrics).length === 0
349
+ ? null
350
+ : {
351
+ method: "seeded A/A from this variant's own repeats (two different repeats per cassette), paired bootstrap 95% CI per resampling, all cassettes",
352
+ target: NULL_CALIBRATION_TARGET,
353
+ resamplings: NULL_CALIBRATION_RESAMPLINGS,
354
+ bootstrapResamples: NULL_CALIBRATION_BOOTSTRAP,
355
+ seed: input.seed,
356
+ metrics: nullMetrics,
357
+ },
358
+ };
359
+ }
360
+ // Every variant against every later variant and every baseline; baselines
361
+ // are not compared with each other.
362
+ const comparisons = [];
363
+ for (let i = 0; i < input.variants.length; i++) {
364
+ for (let j = i + 1; j < input.variants.length; j++) {
365
+ const a = input.variants[i];
366
+ const b = input.variants[j];
367
+ if (a.kind === "baseline" && b.kind === "baseline")
368
+ continue;
369
+ const bySplit = {};
370
+ for (const [split, splitIds] of Object.entries(splits)) {
371
+ const pairs = splitIds
372
+ .map((id) => [summaryOf(a.name, id), summaryOf(b.name, id)])
373
+ .filter((p) => p[0] !== undefined && p[1] !== undefined);
374
+ const overlaps = pairs.map(([sa, sb]) => {
375
+ const scores = [];
376
+ for (const x of sa.actionSets)
377
+ for (const y of sb.actionSets)
378
+ scores.push(jaccard(x, y));
379
+ return mean(scores) ?? 1;
380
+ });
381
+ const metrics = {};
382
+ for (const m of PAIRED_METRICS) {
383
+ const both = pairs.filter(([sa, sb]) => sa.values[m] !== undefined && sb.values[m] !== undefined);
384
+ const va = both.map(([sa]) => sa.values[m]);
385
+ const vb = both.map(([, sb]) => sb.values[m]);
386
+ const diffs = va.map((x, k) => x - vb[k]);
387
+ const ci = bootstrapMeanCi(diffs, (input.seed ^ fnv1a32(`${a.name}|${b.name}|${split}|${m}`)) >>> 0, input.bootstrapResamples);
388
+ metrics[m] = {
389
+ n: diffs.length,
390
+ meanA: mean(va),
391
+ meanB: mean(vb),
392
+ meanDiff: mean(diffs),
393
+ ci95: ci,
394
+ // One paired cassette is an anecdote, not evidence.
395
+ excludesZero: ci !== null && diffs.length >= 2 && (ci[0] > 0 || ci[1] < 0),
396
+ };
397
+ }
398
+ bySplit[split] = {
399
+ pairedCassettes: pairs.length,
400
+ actionOverlapJaccard: mean(overlaps),
401
+ metrics,
402
+ };
403
+ }
404
+ comparisons.push({ a: a.name, b: b.name, ...bySplit });
405
+ }
406
+ }
407
+ const cycles = [...input.rows].sort((x, y) => ids.indexOf(x.cassetteId) - ids.indexOf(y.cassetteId) ||
408
+ (x.variant < y.variant ? -1 : x.variant > y.variant ? 1 : 0) ||
409
+ x.repeat - y.repeat);
410
+ const body = roundDeep({
411
+ schema: REPORT_SCHEMA,
412
+ corpus: {
413
+ cassettes: ids.length,
414
+ ids,
415
+ split: {
416
+ tuneFraction: TUNE_FRACTION,
417
+ tune: splits.tune,
418
+ holdout: splits.holdout,
419
+ },
420
+ },
421
+ settings: {
422
+ repeats: input.repeats,
423
+ seed: input.seed,
424
+ bootstrapResamples: input.bootstrapResamples,
425
+ pairedMetrics: [...PAIRED_METRICS],
426
+ },
427
+ assumptions: input.assumptions,
428
+ fidelity: input.fidelity,
429
+ variants,
430
+ comparisons,
431
+ cycles,
432
+ });
433
+ const contentHash = `sha256:${sha256Hex(canonicalJson(body))}`;
434
+ return { ...body, schema: REPORT_SCHEMA, contentHash };
435
+ }
@@ -1,4 +1,6 @@
1
1
  import { SkillValidationMode } from "./skillValidator.js";
2
+ import { AgentSpec } from "./types.js";
3
+ import { Provider } from "./providers.js";
2
4
  export interface CmdResult {
3
5
  ok: boolean;
4
6
  code: number;
@@ -20,4 +22,27 @@ export declare function cmdRun(path: string, opts?: {
20
22
  stateFile?: string;
21
23
  expectDefinition?: string;
22
24
  }): Promise<CmdResult>;
25
+ export declare function cmdRecord(path: string, opts?: {
26
+ out?: string;
27
+ cycles?: number;
28
+ every?: string;
29
+ fetchFn?: typeof fetch;
30
+ sleepFn?: (ms: number) => Promise<void>;
31
+ }): Promise<CmdResult>;
32
+ export declare function cmdLabel(opts?: {
33
+ corpus?: string;
34
+ horizonHours?: number;
35
+ overwrite?: boolean;
36
+ fetchFn?: typeof fetch;
37
+ nowMs?: number;
38
+ }): Promise<CmdResult>;
39
+ export declare function cmdBench(opts?: {
40
+ corpus?: string;
41
+ variants?: string[];
42
+ repeats?: number;
43
+ seed?: number;
44
+ out?: string;
45
+ baselines?: boolean;
46
+ providerFor?: (spec: AgentSpec) => Provider;
47
+ }): Promise<CmdResult>;
23
48
  export declare function main(argv: string[]): Promise<number>;