@coinrithm/mcp-trading 0.7.15 → 0.7.17

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 (76) hide show
  1. package/CHANGELOG.md +885 -692
  2. package/README.md +486 -400
  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/capitalSizing.d.ts +10 -1
  20. package/dist/agent/capitalSizing.js +28 -4
  21. package/dist/agent/cli.d.ts +25 -0
  22. package/dist/agent/cli.js +300 -2
  23. package/dist/agent/client.d.ts +14 -0
  24. package/dist/agent/client.js +23 -6
  25. package/dist/agent/configurationWarnings.js +11 -0
  26. package/dist/agent/decision.d.ts +24 -16
  27. package/dist/agent/decision.js +92 -3
  28. package/dist/agent/decisionValidator.d.ts +31 -0
  29. package/dist/agent/decisionValidator.js +148 -3
  30. package/dist/agent/engine.d.ts +5 -3
  31. package/dist/agent/engine.js +5 -3
  32. package/dist/agent/futuresCloseOutcome.d.ts +19 -0
  33. package/dist/agent/futuresCloseOutcome.js +54 -0
  34. package/dist/agent/futuresEligibility.d.ts +13 -0
  35. package/dist/agent/futuresEligibility.js +43 -5
  36. package/dist/agent/observe.d.ts +28 -2
  37. package/dist/agent/observe.js +530 -90
  38. package/dist/agent/pmPaperCandidates.d.ts +17 -0
  39. package/dist/agent/pmPaperCandidates.js +125 -0
  40. package/dist/agent/pmPaperCycle.d.ts +16 -0
  41. package/dist/agent/pmPaperCycle.js +372 -0
  42. package/dist/agent/pmPaperDecision.d.ts +187 -0
  43. package/dist/agent/pmPaperDecision.js +101 -0
  44. package/dist/agent/pmPaperPolicy.d.ts +71 -0
  45. package/dist/agent/pmPaperPolicy.js +46 -0
  46. package/dist/agent/prompt.d.ts +15 -1
  47. package/dist/agent/prompt.js +187 -9
  48. package/dist/agent/promptTables.d.ts +1 -0
  49. package/dist/agent/promptTables.js +33 -0
  50. package/dist/agent/providerCapabilities.d.ts +7 -0
  51. package/dist/agent/providerCapabilities.js +27 -1
  52. package/dist/agent/providers.d.ts +26 -7
  53. package/dist/agent/providers.js +193 -48
  54. package/dist/agent/resolve.js +3 -0
  55. package/dist/agent/runner.d.ts +17 -1
  56. package/dist/agent/runner.js +210 -38
  57. package/dist/agent/setups.d.ts +2 -1
  58. package/dist/agent/setups.js +17 -16
  59. package/dist/agent/signals.d.ts +33 -0
  60. package/dist/agent/signals.js +89 -0
  61. package/dist/agent/skill.js +46 -0
  62. package/dist/agent/skillValidator.js +56 -0
  63. package/dist/agent/strictLint.js +30 -0
  64. package/dist/agent/templates.js +30 -30
  65. package/dist/agent/types.d.ts +180 -12
  66. package/dist/agent/universe.d.ts +78 -0
  67. package/dist/agent/universe.js +215 -0
  68. package/dist/agent/version.d.ts +1 -1
  69. package/dist/agent/version.js +1 -1
  70. package/dist/client.d.ts +14 -0
  71. package/dist/client.js +31 -1
  72. package/dist/http.js +1 -1
  73. package/dist/pmPaperV2.d.ts +81 -0
  74. package/dist/pmPaperV2.js +53 -0
  75. package/dist/tools.js +84 -6
  76. package/package.json +100 -97
package/dist/agent/cli.js CHANGED
@@ -18,12 +18,17 @@ import { buildManifest, writeManifest } from "./manifest.js";
18
18
  import { parseFrontmatter } from "./frontmatter.js";
19
19
  import { renderFolderOfOne, ejectFiles, PRESET_NAMES, } from "./templates.js";
20
20
  import { COINRITHM_API } from "./version.js";
21
- import { stableStringify, envFlag } from "./util.js";
22
- import { CoinRithmClient } from "./client.js";
21
+ import { stableStringify, envFlag, parseCadenceMs, sleep } from "./util.js";
22
+ import { CoinRithmClient, DEFAULT_BASE_URL } from "./client.js";
23
23
  import { selectProvider } from "./providers.js";
24
24
  import { runLoop } from "./runner.js";
25
25
  import { loadState, saveState } from "./state.js";
26
26
  import { makeRunId } from "./runEvidence.js";
27
+ import { readCorpus, writeCassette } from "./bench/cassette.js";
28
+ import { recordCassette } from "./bench/recordingClient.js";
29
+ import { runBench } from "./bench/bench.js";
30
+ import { buildPmLabels, buildPriceLabels, DEFAULT_LABEL_HORIZON_HOURS, } from "./bench/labelBuilder.js";
31
+ import { parseLabelFile } from "./bench/labels.js";
27
32
  const fail = (lines) => ({ ok: false, code: 1, lines });
28
33
  function issuesResult(issues, header) {
29
34
  return {
@@ -438,8 +443,254 @@ export async function cmdRun(path, opts = {}) {
438
443
  release();
439
444
  }
440
445
  }
446
+ const MAX_RECORD_CYCLES = 10_000;
447
+ // Record bench cassettes (see bench/cassette.ts). Read-only by construction:
448
+ // every cycle runs DRY-RUN through a recording transport that refuses all
449
+ // non-GET requests, with a brain that never calls a model, so only
450
+ // COINRITHM_API_KEY is needed and no model key is read. The agent's own state
451
+ // file is not touched; each recorded cycle starts from a fresh run state.
452
+ export async function cmdRecord(path, opts = {}) {
453
+ if (!opts.out)
454
+ return fail(["record needs --out <dir> for the cassettes"]);
455
+ const cycles = opts.cycles ?? 1;
456
+ if (!Number.isInteger(cycles) || cycles < 1 || cycles > MAX_RECORD_CYCLES)
457
+ return fail([`--cycles must be an integer from 1 to ${MAX_RECORD_CYCLES}`]);
458
+ let loaded;
459
+ try {
460
+ loaded = loadAgent(path, "self-host");
461
+ }
462
+ catch (e) {
463
+ if (e instanceof ResolveError)
464
+ return issuesResult(e.issues, "resolve failed");
465
+ throw e;
466
+ }
467
+ const everyMs = opts.every !== undefined
468
+ ? parseCadenceMs(opts.every)
469
+ : (parseCadenceMs(loaded.spec.trigger.cadence) ?? 3_600_000);
470
+ if (everyMs === null)
471
+ return fail([`--every "${opts.every}" is not a cadence like 5m or 1h`]);
472
+ const apiKey = process.env.COINRITHM_API_KEY;
473
+ if (!apiKey)
474
+ return fail([
475
+ "COINRITHM_API_KEY is not set (recording reads your paper account; it never writes)",
476
+ ]);
477
+ const mergedProse = runtimeProse(loaded.resolved);
478
+ const lines = [
479
+ `record DRY-RUN (reads only, no model call): ${loaded.spec.name}, ${cycles} cycle(s)`,
480
+ ];
481
+ const files = [];
482
+ for (let i = 0; i < cycles; i++) {
483
+ let cassette;
484
+ try {
485
+ cassette = await recordCassette({
486
+ spec: loaded.spec,
487
+ mergedProse,
488
+ apiKey,
489
+ baseUrl: process.env.COINRITHM_API_URL || undefined,
490
+ fetchFn: opts.fetchFn,
491
+ });
492
+ }
493
+ catch (e) {
494
+ lines.push(`✗ cycle ${i + 1} failed: ${e.message}`);
495
+ return { ok: false, code: 1, lines, data: files };
496
+ }
497
+ files.push(writeCassette(resolvePath(opts.out), cassette));
498
+ const cycle = cassette.recordCycle;
499
+ lines.push(`recorded ${cassette.id}: ${cassette.responses.length} read(s), asOf ${cassette.asOf}, cycle ${cycle.decisionType ?? cycle.decision}${cycle.skipReason ? ` (${cycle.skipReason})` : ""}` +
500
+ (cassette.refusedRequests.length
501
+ ? `, ${cassette.refusedRequests.length} write(s) refused`
502
+ : ""));
503
+ if (i < cycles - 1)
504
+ await (opts.sleepFn ?? sleep)(everyMs);
505
+ }
506
+ return { ok: true, code: 0, lines, data: files };
507
+ }
508
+ // Write outcome labels for a recorded corpus (bench/labelBuilder.ts), reads
509
+ // only: price bars from candles published after each cassette's asOf (with
510
+ // COINRITHM_API_KEY), and PM settlement from the public event verdict
511
+ // (settlementEligible + "settle" + a provider won/lost outcome result; any
512
+ // other state stays unlabelled).
513
+ export async function cmdLabel(opts = {}) {
514
+ if (!opts.corpus)
515
+ return fail(["label needs --corpus <dir>"]);
516
+ const horizonHours = opts.horizonHours ?? DEFAULT_LABEL_HORIZON_HOURS;
517
+ if (!Number.isFinite(horizonHours) || horizonHours <= 0 || horizonHours > 720)
518
+ return fail(["--horizon-hours must be a number in (0, 720]"]);
519
+ const apiKey = process.env.COINRITHM_API_KEY;
520
+ if (!apiKey)
521
+ return fail([
522
+ "COINRITHM_API_KEY is not set (labels read public market candles through your key; nothing is written)",
523
+ ]);
524
+ const dir = resolvePath(opts.corpus);
525
+ let corpus;
526
+ try {
527
+ corpus = readCorpus(dir);
528
+ }
529
+ catch (e) {
530
+ return fail([e.message]);
531
+ }
532
+ const client = new CoinRithmClient({
533
+ apiKey,
534
+ baseUrl: process.env.COINRITHM_API_URL || undefined,
535
+ fetchFn: opts.fetchFn,
536
+ });
537
+ const results = await buildPriceLabels(corpus.cassettes, {
538
+ fetchCandles: (coinId, range) => client.candles(coinId, range),
539
+ horizonHours,
540
+ nowMs: opts.nowMs ?? Date.now(),
541
+ existing: corpus.labels,
542
+ overwrite: opts.overwrite,
543
+ });
544
+ const base = (process.env.COINRITHM_API_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
545
+ const fetchFn = opts.fetchFn ?? fetch;
546
+ const pmResults = await buildPmLabels(corpus.cassettes, {
547
+ // The public event detail: the same verdict the event page shows.
548
+ fetchEvent: async (source, slug) => {
549
+ const res = await fetchFn(`${base}/api/prediction-markets/events/${encodeURIComponent(source)}/${encodeURIComponent(slug)}`, { method: "GET", signal: AbortSignal.timeout(15_000) });
550
+ return {
551
+ ok: res.ok,
552
+ status: res.status,
553
+ data: res.ok ? await res.json() : null,
554
+ };
555
+ },
556
+ existing: corpus.labels,
557
+ overwrite: opts.overwrite,
558
+ });
559
+ const lines = [
560
+ `label (reads only): ${corpus.cassettes.length} cassette(s), horizon ${horizonHours} h`,
561
+ ];
562
+ let written = 0;
563
+ for (const r of results) {
564
+ const pm = pmResults.find((p) => p.id === r.id);
565
+ const notes = [];
566
+ let file;
567
+ if (r.status === "built") {
568
+ file = { ...r.file };
569
+ notes.push(`${r.range} bars for ${r.symbols.length} symbol(s)${r.missing.length ? `, missing ${r.missing.join(",")}` : ""}`);
570
+ }
571
+ else {
572
+ notes.push(`prices ${r.status}${r.status === "not_yet" ? ` (labelable after ${r.labelableAfter})` : ""}${r.status === "horizon_mismatch" ? ` (existing file labels ${r.existingHorizonHours ?? "no"} h, not ${horizonHours} h; rerun with --overwrite)` : ""}`);
573
+ }
574
+ if (pm) {
575
+ notes.push(`pm ${pm.labelled} new settled outcome(s) from ${pm.events} event(s)${pm.failed ? `, ${pm.failed} read(s) failed` : ""}`);
576
+ const hasPm = Object.keys(pm.pm).length > 0;
577
+ if (opts.overwrite) {
578
+ // --overwrite REPLACES the PM part, even with nothing: a label that
579
+ // was settled before but is void/unresolved now must not survive.
580
+ const base = file ?? corpus.labels[r.id];
581
+ if (base || hasPm) {
582
+ const { pm: _replaced, ...rest } = base ?? {};
583
+ file = hasPm ? { ...rest, pm: pm.pm } : rest;
584
+ }
585
+ }
586
+ else if (pm.labelled > 0 || (file && hasPm))
587
+ file = { ...(file ?? corpus.labels[r.id] ?? {}), pm: pm.pm };
588
+ }
589
+ lines.push(`${r.id}: ${notes.join("; ")}`);
590
+ if (!file)
591
+ continue;
592
+ const path = join(dir, "labels", `${r.id}.json`);
593
+ // The bench reads it back through the same validator; fail closed here.
594
+ parseLabelFile(file, `labels/${r.id}.json`);
595
+ mkdirSync(dirname(path), { recursive: true });
596
+ writeFileSync(path, `${JSON.stringify(file, null, 2)}\n`, "utf8");
597
+ written += 1;
598
+ }
599
+ lines.push(`wrote ${written} label file(s); PM outcomes without a settled platform verdict stay unlabelled`);
600
+ return { ok: true, code: 0, lines, data: { prices: results, pm: pmResults } };
601
+ }
602
+ // Compare agent variants on a recorded corpus (see bench/bench.ts). Each
603
+ // variant runs with its own model, built exactly as `run` builds it from the
604
+ // variant's model config and the environment's keys. Never writes.
605
+ export async function cmdBench(opts = {}) {
606
+ if (!opts.corpus)
607
+ return fail(["bench needs --corpus <dir>"]);
608
+ if (!opts.variants || opts.variants.length === 0)
609
+ return fail(["bench needs at least one --variant name=<agentPath>"]);
610
+ if (opts.seed !== undefined &&
611
+ (!Number.isSafeInteger(opts.seed) || opts.seed < 0))
612
+ return fail(["--seed must be a non-negative integer"]);
613
+ let corpus;
614
+ try {
615
+ corpus = readCorpus(resolvePath(opts.corpus));
616
+ }
617
+ catch (e) {
618
+ return fail([e.message]);
619
+ }
620
+ const variants = [];
621
+ for (const arg of opts.variants) {
622
+ const eq = arg.indexOf("=");
623
+ if (eq <= 0 || eq === arg.length - 1)
624
+ return fail([`--variant "${arg}" must look like name=<agentPath>`]);
625
+ const name = arg.slice(0, eq);
626
+ const agentPath = arg.slice(eq + 1);
627
+ let loaded;
628
+ try {
629
+ loaded = loadAgent(agentPath, "self-host");
630
+ }
631
+ catch (e) {
632
+ if (e instanceof ResolveError)
633
+ return issuesResult(e.issues, `variant ${name}: resolve failed`);
634
+ throw e;
635
+ }
636
+ let provider;
637
+ try {
638
+ provider = opts.providerFor
639
+ ? opts.providerFor(loaded.spec)
640
+ : selectProvider(loaded.spec, process.env, fetch);
641
+ }
642
+ catch (e) {
643
+ return fail([`variant ${name}: ${e.message}`]);
644
+ }
645
+ variants.push({
646
+ name,
647
+ spec: loaded.spec,
648
+ mergedProse: runtimeProse(loaded.resolved),
649
+ provider,
650
+ });
651
+ }
652
+ const lines = [
653
+ `bench DRY-RUN: ${corpus.cassettes.length} cassette(s) x ${variants.length} variant(s) x ${opts.repeats ?? 3} repeat(s)`,
654
+ ];
655
+ if (corpus.ignored.length)
656
+ lines.push(`ignored non-cassette file(s): ${corpus.ignored.join(", ")}`);
657
+ let report;
658
+ try {
659
+ report = await runBench({
660
+ cassettes: corpus.cassettes,
661
+ labels: corpus.labels,
662
+ variants,
663
+ repeats: opts.repeats,
664
+ seed: opts.seed,
665
+ baselines: opts.baselines,
666
+ });
667
+ }
668
+ catch (e) {
669
+ return fail([...lines, `✗ ${e.message}`]);
670
+ }
671
+ const summary = report.variants;
672
+ for (const [name, v] of Object.entries(summary)) {
673
+ const codes = Object.entries(v.all.actions.rejectCodes)
674
+ .map(([c, n]) => `${c} ${n}`)
675
+ .join(", ");
676
+ lines.push(`${name}: ${v.all.cycles} cycle(s), accepted ${v.all.actions.accepted}, rejected ${v.all.actions.rejected}${codes ? ` (${codes})` : ""}, model failures ${v.all.modelFailures}, cycles missing inputs ${v.all.cyclesWithMissingInputs}, repeat consistency ${v.all.repeatConsistency ?? "n/a"}`);
677
+ }
678
+ for (const c of report.comparisons) {
679
+ const accepted = c.all.metrics.acceptedActions;
680
+ lines.push(`${c.a} vs ${c.b}: overlap ${c.all.actionOverlapJaccard ?? "n/a"}, accepted diff ${accepted.meanDiff ?? "n/a"} (95% CI ${accepted.ci95 ? `${accepted.ci95[0]}..${accepted.ci95[1]}` : "n/a"}, n=${accepted.n})`);
681
+ }
682
+ if (opts.out) {
683
+ writeFileSync(resolvePath(opts.out), `${JSON.stringify(report, null, 2)}\n`, "utf8");
684
+ lines.push(`wrote ${resolvePath(opts.out)}`);
685
+ }
686
+ lines.push(`contentHash ${report.contentHash}`);
687
+ return { ok: true, code: 0, lines, data: report };
688
+ }
441
689
  function parseFlags(args) {
442
690
  const out = { _: [] };
691
+ // A missing or non-numeric value becomes NaN so the command rejects it,
692
+ // never silently falls back to its default.
693
+ const num = (v) => (v === undefined ? NaN : Number(v));
443
694
  for (let i = 0; i < args.length; i++) {
444
695
  const a = args[i];
445
696
  if (a === "--hosted")
@@ -462,6 +713,26 @@ function parseFlags(args) {
462
713
  out.state = args[++i];
463
714
  else if (a === "--expect-definition")
464
715
  out.expectDefinition = args[++i] ?? "";
716
+ else if (a === "--out")
717
+ out.out = args[++i];
718
+ else if (a === "--cycles")
719
+ out.cycles = num(args[++i]);
720
+ else if (a === "--every")
721
+ out.every = args[++i] ?? "";
722
+ else if (a === "--corpus")
723
+ out.corpus = args[++i];
724
+ else if (a === "--variant")
725
+ out.variant = [...(out.variant ?? []), args[++i] ?? ""];
726
+ else if (a === "--repeats")
727
+ out.repeats = num(args[++i]);
728
+ else if (a === "--seed")
729
+ out.seed = num(args[++i]);
730
+ else if (a === "--no-baselines")
731
+ out.noBaselines = true;
732
+ else if (a === "--horizon-hours")
733
+ out.horizonHours = num(args[++i]);
734
+ else if (a === "--overwrite")
735
+ out.overwrite = true;
465
736
  else
466
737
  out._.push(a);
467
738
  }
@@ -476,6 +747,9 @@ function usageLines() {
476
747
  " eject <agent.md | dir>",
477
748
  " lock <path>",
478
749
  " run <path> [--once] [--live] [--dry-run] [--state <file>] [--expect-definition sha256:...] (dry-run by default)",
750
+ " record <path> --out <dir> [--cycles N] [--every 5m] (bench inputs; reads only, no model call)",
751
+ " bench --corpus <dir> --variant a=<path> --variant b=<path> [--repeats 3] [--seed N] [--no-baselines] [--out report.json]",
752
+ " label --corpus <dir> [--horizon-hours 24] [--overwrite] (price outcome labels after asOf; reads only)",
479
753
  ];
480
754
  }
481
755
  export async function main(argv) {
@@ -513,6 +787,30 @@ export async function main(argv) {
513
787
  });
514
788
  break;
515
789
  }
790
+ case "record":
791
+ r = await cmdRecord(pos[0] ?? ".", {
792
+ out: flags.out,
793
+ cycles: flags.cycles,
794
+ every: flags.every,
795
+ });
796
+ break;
797
+ case "label":
798
+ r = await cmdLabel({
799
+ corpus: flags.corpus,
800
+ horizonHours: flags.horizonHours,
801
+ overwrite: flags.overwrite,
802
+ });
803
+ break;
804
+ case "bench":
805
+ r = await cmdBench({
806
+ corpus: flags.corpus,
807
+ variants: flags.variant,
808
+ repeats: flags.repeats,
809
+ seed: flags.seed,
810
+ out: flags.out,
811
+ baselines: !flags.noBaselines,
812
+ });
813
+ break;
516
814
  case undefined:
517
815
  case "help":
518
816
  case "--help":
@@ -1,3 +1,4 @@
1
+ import { type PmPaperOpen, type PmPaperClose } from "../pmPaperV2.js";
1
2
  import { AgentTrace, ApiResult } from "./types.js";
2
3
  export declare const DEFAULT_BASE_URL = "https://api.coinrithm.com";
3
4
  export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
@@ -53,6 +54,7 @@ export declare class CoinRithmClient {
53
54
  }, trace?: AgentTrace): Promise<ApiResult>;
54
55
  resolve(q: string, trace?: AgentTrace): Promise<ApiResult>;
55
56
  cryptoMovers(direction: "gainers" | "losers", limit: number, trace?: AgentTrace): Promise<ApiResult>;
57
+ agentUniverse(query: Record<string, string | number>, trace?: AgentTrace): Promise<ApiResult>;
56
58
  getPublicPmWhales(options?: {
57
59
  timeoutMs?: number;
58
60
  limit?: number;
@@ -85,6 +87,10 @@ export declare class CoinRithmClient {
85
87
  side: string;
86
88
  quantity: number;
87
89
  }, trace?: AgentTrace): Promise<ApiResult>;
90
+ discoverPmPaperV2(query?: {
91
+ q?: string;
92
+ limit?: number;
93
+ }, trace?: AgentTrace): Promise<ApiResult>;
88
94
  discoverPmMarkets(query?: {
89
95
  q?: string;
90
96
  source?: string;
@@ -142,6 +148,14 @@ export declare class CoinRithmClient {
142
148
  agentTrace?: AgentTrace;
143
149
  }): Promise<ApiResult>;
144
150
  cancelSpotOrder(orderId: number, idempotencyKey?: string, trace?: AgentTrace): Promise<ApiResult>;
151
+ openPmPaperV2(body: PmPaperOpen): Promise<ApiResult>;
152
+ replayPmPaperV2Open(body: PmPaperOpen): Promise<ApiResult>;
153
+ closePmPaperV2(body: PmPaperClose): Promise<ApiResult>;
154
+ pmPaperV2Positions(query?: {
155
+ status?: "open";
156
+ beforeId?: number;
157
+ limit?: number;
158
+ }): Promise<ApiResult>;
145
159
  openPmPosition(body: {
146
160
  source: string;
147
161
  slug: string;
@@ -1,9 +1,4 @@
1
- // Thin CoinRithm agent-API client for the runner (futures-focused v1).
2
- //
3
- // Auth: the user's own crk_live_ key from COINRITHM_API_KEY (env only — never
4
- // from an agent file). 429 backs off on Retry-After; 401/403/409/422 are
5
- // FAIL-CLOSED cycle outcomes (returned, not retried). fetch + sleep are
6
- // injectable so tests run with no network and no real waits.
1
+ import { PM_PAPER_V2_BASE, } from "../pmPaperV2.js";
7
2
  import { createHash } from "node:crypto";
8
3
  import { setTimeout as sleep } from "node:timers/promises";
9
4
  import { retryAfterSeconds } from "../retryAfter.js";
@@ -283,6 +278,12 @@ export class CoinRithmClient {
283
278
  ? "/api/coins/top-losers"
284
279
  : "/api/coins/top-gainers", { query: { limit }, trace });
285
280
  }
281
+ // Market-boundaries screener (GET /api/agent/universe, read scope): rows
282
+ // inside the agent's declared rank band / volume floor / sectors, sorted by
283
+ // the agent's notion of an opportunity. Shared 60 s server cache.
284
+ agentUniverse(query, trace) {
285
+ return this.request("GET", "/api/agent/universe", { query, trace });
286
+ }
286
287
  getPublicPmWhales(options) {
287
288
  return this.publicRequest("/api/prediction-markets/whales", { limit: options?.limit }, options?.timeoutMs);
288
289
  }
@@ -322,6 +323,9 @@ export class CoinRithmClient {
322
323
  });
323
324
  }
324
325
  // ── prediction markets ───────────────────────────────────────────────────
326
+ discoverPmPaperV2(query, trace) {
327
+ return this.request("GET", "/api/agent/pm/v2/discover", { query, trace });
328
+ }
325
329
  discoverPmMarkets(query, trace) {
326
330
  return this.request("GET", "/api/agent/pm/discover", { query, trace });
327
331
  }
@@ -361,6 +365,19 @@ export class CoinRithmClient {
361
365
  trace,
362
366
  });
363
367
  }
368
+ // Explicit opt-in contract; API derives and authorizes the configured house tuple.
369
+ openPmPaperV2(body) {
370
+ return this.request("POST", `${PM_PAPER_V2_BASE}/open`, { body });
371
+ }
372
+ replayPmPaperV2Open(body) {
373
+ return this.request("POST", `${PM_PAPER_V2_BASE}/open/replay`, { body });
374
+ }
375
+ closePmPaperV2(body) {
376
+ return this.request("POST", `${PM_PAPER_V2_BASE}/close`, { body });
377
+ }
378
+ pmPaperV2Positions(query) {
379
+ return this.request("GET", `${PM_PAPER_V2_BASE}/positions`, { query });
380
+ }
364
381
  openPmPosition(body) {
365
382
  return this.request("POST", "/api/agent/pm/open", { body });
366
383
  }
@@ -1,6 +1,17 @@
1
+ // Per-day counters roll over on the UTC date (util.ts dayKey), whatever the
2
+ // bundle declares. trigger.timezone is parsed but read by nothing else, so a
3
+ // non-UTC value would silently promise local-day limits.
4
+ const UTC_NAMES = new Set(["utc", "etc/utc", "z", "gmt", "etc/gmt"]);
1
5
  // Advisory only: these fields remain loadable for backwards compatibility.
2
6
  export function configurationWarnings(raw) {
3
7
  const warnings = [];
8
+ const trigger = raw.trigger;
9
+ if (trigger && typeof trigger === "object" && !Array.isArray(trigger)) {
10
+ const tz = trigger.timezone;
11
+ if (typeof tz === "string" && !UTC_NAMES.has(tz.trim().toLowerCase())) {
12
+ warnings.push(`trigger.timezone "${tz}" is informational: maxTradesPerDay and maxDailyLossMusd reset at 00:00 UTC, not in this timezone`);
13
+ }
14
+ }
4
15
  if (Array.isArray(raw.capabilities) &&
5
16
  raw.capabilities.includes("websearch")) {
6
17
  warnings.push("capabilities.websearch is reserved: the runner does not perform web searches");
@@ -13,24 +13,24 @@ export declare const actionSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<
13
13
  thesis: z.ZodOptional<z.ZodEffects<z.ZodAny, import("./types.js").Thesis | undefined, any>>;
14
14
  }, "strict", z.ZodTypeAny, {
15
15
  symbol: string;
16
- side: "long" | "short";
17
16
  type: "futures_open";
17
+ side: "long" | "short";
18
+ thesis?: import("./types.js").Thesis | undefined;
18
19
  leverage?: any;
19
20
  marginMusd?: any;
20
21
  stopLossPrice?: any;
21
22
  takeProfitPrice?: any;
22
- thesis?: import("./types.js").Thesis | undefined;
23
23
  confidence?: any;
24
24
  rationaleSummary?: string | undefined;
25
25
  }, {
26
26
  symbol: string;
27
- side: "long" | "short";
28
27
  type: "futures_open";
28
+ side: "long" | "short";
29
+ thesis?: any;
29
30
  leverage?: unknown;
30
31
  marginMusd?: unknown;
31
32
  stopLossPrice?: unknown;
32
33
  takeProfitPrice?: unknown;
33
- thesis?: any;
34
34
  confidence?: unknown;
35
35
  rationaleSummary?: string | undefined;
36
36
  }>, z.ZodObject<{
@@ -42,16 +42,16 @@ export declare const actionSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<
42
42
  thesis: z.ZodOptional<z.ZodEffects<z.ZodAny, import("./types.js").Thesis | undefined, any>>;
43
43
  }, "strict", z.ZodTypeAny, {
44
44
  type: "futures_close";
45
+ thesis?: import("./types.js").Thesis | undefined;
45
46
  positionId?: any;
46
47
  fraction?: any;
47
- thesis?: import("./types.js").Thesis | undefined;
48
48
  confidence?: any;
49
49
  rationaleSummary?: string | undefined;
50
50
  }, {
51
51
  type: "futures_close";
52
+ thesis?: any;
52
53
  positionId?: unknown;
53
54
  fraction?: unknown;
54
- thesis?: any;
55
55
  confidence?: unknown;
56
56
  rationaleSummary?: string | undefined;
57
57
  }>, z.ZodObject<{
@@ -64,18 +64,18 @@ export declare const actionSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<
64
64
  thesis: z.ZodOptional<z.ZodEffects<z.ZodAny, import("./types.js").Thesis | undefined, any>>;
65
65
  }, "strict", z.ZodTypeAny, {
66
66
  type: "futures_set_sltp";
67
+ thesis?: import("./types.js").Thesis | undefined;
68
+ positionId?: any;
67
69
  stopLossPrice?: any;
68
70
  takeProfitPrice?: any;
69
- positionId?: any;
70
- thesis?: import("./types.js").Thesis | undefined;
71
71
  confidence?: any;
72
72
  rationaleSummary?: string | undefined;
73
73
  }, {
74
74
  type: "futures_set_sltp";
75
+ thesis?: any;
76
+ positionId?: unknown;
75
77
  stopLossPrice?: unknown;
76
78
  takeProfitPrice?: unknown;
77
- positionId?: unknown;
78
- thesis?: any;
79
79
  confidence?: unknown;
80
80
  rationaleSummary?: string | undefined;
81
81
  }>, z.ZodObject<{
@@ -91,24 +91,24 @@ export declare const actionSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<
91
91
  thesis: z.ZodOptional<z.ZodEffects<z.ZodAny, import("./types.js").Thesis | undefined, any>>;
92
92
  }, "strict", z.ZodTypeAny, {
93
93
  symbol: string;
94
+ type: "spot_order";
94
95
  side: "buy" | "sell";
95
96
  orderType: "limit" | "market" | "stop";
96
- type: "spot_order";
97
+ thesis?: import("./types.js").Thesis | undefined;
97
98
  quantity?: any;
98
99
  limitPrice?: any;
99
100
  stopPrice?: any;
100
- thesis?: import("./types.js").Thesis | undefined;
101
101
  confidence?: any;
102
102
  rationaleSummary?: string | undefined;
103
103
  }, {
104
104
  symbol: string;
105
+ type: "spot_order";
105
106
  side: "buy" | "sell";
106
107
  orderType: "limit" | "market" | "stop";
107
- type: "spot_order";
108
+ thesis?: any;
108
109
  quantity?: unknown;
109
110
  limitPrice?: unknown;
110
111
  stopPrice?: unknown;
111
- thesis?: any;
112
112
  confidence?: unknown;
113
113
  rationaleSummary?: string | undefined;
114
114
  }>, z.ZodObject<{
@@ -145,9 +145,9 @@ export declare const actionSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<
145
145
  source?: string | undefined;
146
146
  slug?: string | undefined;
147
147
  outcomeExternalMarketId?: string | undefined;
148
- stakeMusd?: any;
149
148
  forecastProbability?: number | undefined;
150
149
  thesis?: import("./types.js").Thesis | undefined;
150
+ stakeMusd?: any;
151
151
  confidence?: any;
152
152
  rationaleSummary?: string | undefined;
153
153
  ref?: string | undefined;
@@ -156,9 +156,9 @@ export declare const actionSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<
156
156
  source?: string | undefined;
157
157
  slug?: string | undefined;
158
158
  outcomeExternalMarketId?: string | undefined;
159
- stakeMusd?: unknown;
160
159
  forecastProbability?: any;
161
160
  thesis?: any;
161
+ stakeMusd?: unknown;
162
162
  confidence?: unknown;
163
163
  rationaleSummary?: string | undefined;
164
164
  ref?: string | undefined;
@@ -537,11 +537,19 @@ export declare const DECISION_JSON_SCHEMA: {
537
537
  };
538
538
  }];
539
539
  };
540
+ export declare const ACTIONS_STRING_DIAGNOSTICS: readonly ["json_array_empty_valid_decision", "json_array_nonempty_valid_decision", "json_array_invalid_decision", "json_nonarray", "invalid_json", "empty_string_valid_skip", "empty_string_other_decision", "non_json_array_text", "json5_array_valid_decision", "json5_array_invalid_decision", "over_size_limit"];
541
+ export type ActionsStringDiagnostic = (typeof ACTIONS_STRING_DIAGNOSTICS)[number];
540
542
  export type ParseDecisionResult = {
541
543
  ok: true;
542
544
  decision: Decision;
543
545
  } | {
544
546
  ok: false;
545
547
  error: string;
548
+ actionsStringDiagnostic?: ActionsStringDiagnostic;
546
549
  };
547
550
  export declare function parseDecision(text: string): ParseDecisionResult;
551
+ /** Only the trusted host selects a versioned contract; default stays legacy. */
552
+ export declare function parseDecisionForContract(text: string, contract?: "pm_paper_v2"): ParseDecisionResult | {
553
+ ok: true;
554
+ decision: z.infer<typeof import("./pmPaperDecision.js").pmPaperDecisionSchema>;
555
+ };