@coinrithm/mcp-trading 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +171 -113
- package/README.md +277 -238
- package/dist/agent/act.d.ts +2 -2
- package/dist/agent/act.js +24 -3
- package/dist/agent/cli.js +68 -23
- package/dist/agent/client.d.ts +33 -0
- package/dist/agent/client.js +34 -7
- package/dist/agent/decision.d.ts +3 -0
- package/dist/agent/decision.js +26 -3
- package/dist/agent/decisionValidator.js +2 -1
- package/dist/agent/deploymentOverlay.js +25 -5
- package/dist/agent/engine.d.ts +2 -1
- package/dist/agent/engine.js +4 -1
- package/dist/agent/extract.js +3 -1
- package/dist/agent/gate.js +25 -5
- package/dist/agent/index.js +0 -1
- package/dist/agent/indicators.js +4 -2
- package/dist/agent/manifest.js +1 -1
- package/dist/agent/mechanical.d.ts +36 -0
- package/dist/agent/mechanical.js +286 -0
- package/dist/agent/observe.d.ts +4 -0
- package/dist/agent/observe.js +140 -53
- package/dist/agent/prompt.d.ts +3 -1
- package/dist/agent/prompt.js +17 -6
- package/dist/agent/providers.js +39 -4
- package/dist/agent/resolve.js +23 -6
- package/dist/agent/resolvePm.js +14 -3
- package/dist/agent/runEvidence.js +6 -2
- package/dist/agent/runner.d.ts +8 -2
- package/dist/agent/runner.js +363 -59
- package/dist/agent/scorecard.js +12 -4
- package/dist/agent/setups.js +57 -9
- package/dist/agent/skill.js +1 -1
- package/dist/agent/state.js +9 -4
- package/dist/agent/types.d.ts +17 -2
- package/dist/agent/types.js +2 -1
- package/dist/agent/util.js +11 -4
- package/dist/agent/version.d.ts +1 -1
- package/dist/agent/version.js +1 -1
- package/dist/client.d.ts +51 -0
- package/dist/client.js +30 -3
- package/dist/executionPolicy.d.ts +2 -0
- package/dist/executionPolicy.js +21 -0
- package/dist/http.js +13 -3
- package/dist/tools.d.ts +23 -0
- package/dist/tools.js +796 -39
- package/package.json +86 -78
package/dist/agent/providers.js
CHANGED
|
@@ -32,7 +32,9 @@ const DEFAULT_TIMEOUT_MS = 300_000;
|
|
|
32
32
|
// instruct mode (measured ~3-4s, clean JSON). Apply it automatically for any
|
|
33
33
|
// nemotron model so a per-cadence decision never blows the cadence.
|
|
34
34
|
function applyReasoningToggle(model, system) {
|
|
35
|
-
return /nemotron/i.test(model)
|
|
35
|
+
return /nemotron/i.test(model)
|
|
36
|
+
? `detailed thinking off\n\n${system}`
|
|
37
|
+
: system;
|
|
36
38
|
}
|
|
37
39
|
// fetch with a hard timeout via AbortController. A custom fetchFn (tests) that
|
|
38
40
|
// ignores `signal` still works — the timer just never fires for it.
|
|
@@ -66,6 +68,8 @@ function envKey(provider, env) {
|
|
|
66
68
|
return env.GEMINI_API_KEY ?? env.MODEL_API_KEY;
|
|
67
69
|
case "openai-compatible":
|
|
68
70
|
return env.MODEL_API_KEY ?? env.OPENAI_API_KEY;
|
|
71
|
+
case "mechanical":
|
|
72
|
+
return undefined; // no LLM, no key — selectProvider short-circuits before this matters
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
function baseUrlFor(provider, configured) {
|
|
@@ -82,6 +86,25 @@ function baseUrlFor(provider, configured) {
|
|
|
82
86
|
return (configured ?? "").replace(/\/+$/, "");
|
|
83
87
|
case "anthropic":
|
|
84
88
|
return "https://api.anthropic.com/v1";
|
|
89
|
+
case "mechanical":
|
|
90
|
+
return ""; // never used — mechanical agents make no HTTP call
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Non-LLM stub for the mechanical BENCHMARK agents. It satisfies the Provider
|
|
94
|
+
// contract so the deps stay non-null, but decide() is NEVER invoked: runCycle
|
|
95
|
+
// detects a mechanical agent and computes the decision deterministically before
|
|
96
|
+
// the provider is asked. If it is ever called, it fails closed (loudly) rather
|
|
97
|
+
// than silently pretending to reason.
|
|
98
|
+
class MechanicalProvider {
|
|
99
|
+
label;
|
|
100
|
+
constructor(strategy) {
|
|
101
|
+
this.label = `mechanical/${strategy}`;
|
|
102
|
+
}
|
|
103
|
+
async decide() {
|
|
104
|
+
return {
|
|
105
|
+
ok: false,
|
|
106
|
+
error: "mechanical benchmark provider has no model — runCycle must short-circuit before decide() (this call is a bug)",
|
|
107
|
+
};
|
|
85
108
|
}
|
|
86
109
|
}
|
|
87
110
|
class AnthropicProvider {
|
|
@@ -127,7 +150,9 @@ class AnthropicProvider {
|
|
|
127
150
|
completionTokens: json.usage.output_tokens ?? 0,
|
|
128
151
|
}
|
|
129
152
|
: undefined;
|
|
130
|
-
return text
|
|
153
|
+
return text
|
|
154
|
+
? { ok: true, text, usage }
|
|
155
|
+
: { ok: false, error: "anthropic returned empty content" };
|
|
131
156
|
}
|
|
132
157
|
catch (err) {
|
|
133
158
|
return { ok: false, error: callError(err, timeoutMs) };
|
|
@@ -162,7 +187,10 @@ class OpenAiCompatProvider {
|
|
|
162
187
|
max_tokens: input.maxTokens ?? 1024,
|
|
163
188
|
response_format: { type: "json_object" },
|
|
164
189
|
messages: [
|
|
165
|
-
{
|
|
190
|
+
{
|
|
191
|
+
role: "system",
|
|
192
|
+
content: applyReasoningToggle(this.model, input.system),
|
|
193
|
+
},
|
|
166
194
|
{ role: "user", content: input.user },
|
|
167
195
|
],
|
|
168
196
|
}),
|
|
@@ -182,7 +210,9 @@ class OpenAiCompatProvider {
|
|
|
182
210
|
completionTokens: json.usage.completion_tokens ?? 0,
|
|
183
211
|
}
|
|
184
212
|
: undefined;
|
|
185
|
-
return text
|
|
213
|
+
return text
|
|
214
|
+
? { ok: true, text, usage }
|
|
215
|
+
: { ok: false, error: "provider returned empty content" };
|
|
186
216
|
}
|
|
187
217
|
catch (err) {
|
|
188
218
|
return { ok: false, error: callError(err, timeoutMs) };
|
|
@@ -197,6 +227,11 @@ export function selectProvider(spec, env, fetchFn = fetch) {
|
|
|
197
227
|
throw new Error("no model configured: set model.provider + model.name in the agent (self-host needs an explicit model)");
|
|
198
228
|
}
|
|
199
229
|
const { provider, name, baseUrl } = spec.model;
|
|
230
|
+
// Mechanical benchmark agents need NO model key: they never call a model. Return
|
|
231
|
+
// the stub before the env-key requirement so a benchmark can be constructed with
|
|
232
|
+
// no ANTHROPIC/NVIDIA/etc. key present.
|
|
233
|
+
if (provider === "mechanical")
|
|
234
|
+
return new MechanicalProvider(name);
|
|
200
235
|
const key = envKey(provider, env);
|
|
201
236
|
if (!key) {
|
|
202
237
|
const varName = provider === "anthropic"
|
package/dist/agent/resolve.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// YAML/frontmatter feeds rawFrontmatter; only markdown bodies feed
|
|
16
16
|
// mergedProse.
|
|
17
17
|
import { readFileSync, existsSync, statSync, lstatSync } from "node:fs";
|
|
18
|
-
import { resolve as resolvePath, relative as relativePath, join, isAbsolute } from "node:path";
|
|
18
|
+
import { resolve as resolvePath, relative as relativePath, join, isAbsolute, } from "node:path";
|
|
19
19
|
import { parse as parseYaml } from "yaml";
|
|
20
20
|
import { parseFrontmatter } from "./frontmatter.js";
|
|
21
21
|
import { sha256, toPosix, isPathInside, boundTail, scanForSecrets, } from "./util.js";
|
|
@@ -43,7 +43,6 @@ const CONFIG_BLOCKS = [
|
|
|
43
43
|
"objective",
|
|
44
44
|
"capabilities",
|
|
45
45
|
];
|
|
46
|
-
const IDENTITY_KEYS = ["name", "description", "spec", "mode"];
|
|
47
46
|
const JOURNAL_MAX_LINES = 200;
|
|
48
47
|
const JOURNAL_MAX_BYTES = 8_000;
|
|
49
48
|
// Optional prose files (markdown the LLM reads), in assembly order.
|
|
@@ -81,7 +80,11 @@ function rel(dir, abs) {
|
|
|
81
80
|
function safePath(ctx, ref, label) {
|
|
82
81
|
const syn = refSyntaxIssue(ref);
|
|
83
82
|
if (syn) {
|
|
84
|
-
ctx.issues.push({
|
|
83
|
+
ctx.issues.push({
|
|
84
|
+
code: "unsafe_ref",
|
|
85
|
+
path: ref,
|
|
86
|
+
message: `${label} "${ref}": ${syn}`,
|
|
87
|
+
});
|
|
85
88
|
return null;
|
|
86
89
|
}
|
|
87
90
|
const target = resolvePath(ctx.dir, ref);
|
|
@@ -221,7 +224,13 @@ function resolveSingleFile(abs) {
|
|
|
221
224
|
},
|
|
222
225
|
]);
|
|
223
226
|
}
|
|
224
|
-
const ctx = {
|
|
227
|
+
const ctx = {
|
|
228
|
+
dir,
|
|
229
|
+
issues,
|
|
230
|
+
hashes: {},
|
|
231
|
+
mergeOrder: [],
|
|
232
|
+
seenLower: new Map(),
|
|
233
|
+
};
|
|
225
234
|
const r = toPosix(abs);
|
|
226
235
|
ctx.hashes[r] = sha256(content);
|
|
227
236
|
ctx.mergeOrder.push(r);
|
|
@@ -269,7 +278,13 @@ function loadSkillList(ctx, frontmatter) {
|
|
|
269
278
|
return [];
|
|
270
279
|
}
|
|
271
280
|
function resolveDirectory(dir) {
|
|
272
|
-
const ctx = {
|
|
281
|
+
const ctx = {
|
|
282
|
+
dir,
|
|
283
|
+
issues: [],
|
|
284
|
+
hashes: {},
|
|
285
|
+
mergeOrder: [],
|
|
286
|
+
seenLower: new Map(),
|
|
287
|
+
};
|
|
273
288
|
const keystoneAbs = findKeystone(dir);
|
|
274
289
|
if (!keystoneAbs) {
|
|
275
290
|
throw new ResolveError([
|
|
@@ -507,7 +522,9 @@ export function isSkillProseSource(source) {
|
|
|
507
522
|
// blank line. The resolver uses this for mergedProse; the run path reuses it to
|
|
508
523
|
// re-assemble a skills-ablated prompt deterministically.
|
|
509
524
|
export function mergeProseParts(parts) {
|
|
510
|
-
return parts
|
|
525
|
+
return parts
|
|
526
|
+
.map((p) => `<!-- ${p.source} -->\n${p.text.trim()}`)
|
|
527
|
+
.join("\n\n");
|
|
511
528
|
}
|
|
512
529
|
export function resolveAgent(inputPath) {
|
|
513
530
|
const abs = resolvePath(inputPath);
|
package/dist/agent/resolvePm.js
CHANGED
|
@@ -35,13 +35,24 @@ export function resolvePmRef(action, pmMarkets) {
|
|
|
35
35
|
if (ref) {
|
|
36
36
|
const mkt = pmMarkets.find((m) => trimLower(m.ref) === ref);
|
|
37
37
|
if (!mkt) {
|
|
38
|
-
const
|
|
39
|
-
|
|
38
|
+
const listed = pmMarkets.length;
|
|
39
|
+
const known = listed
|
|
40
|
+
? `pm1..pm${listed}`
|
|
40
41
|
: "(none discovered this cycle)";
|
|
42
|
+
// Trace-reading diagnostic (no behaviour change — still pm_ref_unknown). The
|
|
43
|
+
// refs are stamped contiguous pm1..pmN every cycle, so an unmatched ref is
|
|
44
|
+
// either INVENTED — its index runs past what was listed (or nothing was
|
|
45
|
+
// listed at all), i.e. the model made up a market that was never on the board
|
|
46
|
+
// (the pm_ref hallucination this guard exists for) — or STALE: an in-range ref
|
|
47
|
+
// that still doesn't match (e.g. carried over from a prior cycle's board).
|
|
48
|
+
const n = Number.parseInt(ref.replace(/^pm/i, ""), 10);
|
|
49
|
+
const diag = listed === 0 || !Number.isFinite(n) || n < 1 || n > listed
|
|
50
|
+
? "invented (ref index is beyond the markets listed this cycle)"
|
|
51
|
+
: "stale (in-range ref does not match this cycle's listed markets)";
|
|
41
52
|
return {
|
|
42
53
|
ok: false,
|
|
43
54
|
code: "pm_ref_unknown",
|
|
44
|
-
reason: `ref ${ref} is not one of this cycle's listed PM markets ${known}`,
|
|
55
|
+
reason: `ref ${ref} is not one of this cycle's listed PM markets ${known} — ${diag}`,
|
|
45
56
|
};
|
|
46
57
|
}
|
|
47
58
|
// Canonicalise: take the triple from the matched market, drop the ref.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// agentTrace stamped on every traced call, and the export at the end.
|
|
3
3
|
import { shortId } from "./util.js";
|
|
4
4
|
export function makeRunId(spec) {
|
|
5
|
-
const slug = (spec.name || "agent")
|
|
5
|
+
const slug = (spec.name || "agent")
|
|
6
|
+
.replace(/[^a-z0-9-]+/gi, "-")
|
|
7
|
+
.toLowerCase();
|
|
6
8
|
return `${slug}-${shortId()}`;
|
|
7
9
|
}
|
|
8
10
|
export function makeDecisionId(cycle) {
|
|
@@ -19,5 +21,7 @@ export function makeTrace(runId, decisionId, spec, confidence, rationaleSummary)
|
|
|
19
21
|
}
|
|
20
22
|
export async function exportRunEvidence(client, runId) {
|
|
21
23
|
const r = await client.exportRunEvidence(runId);
|
|
22
|
-
return r.ok
|
|
24
|
+
return r.ok
|
|
25
|
+
? r.data
|
|
26
|
+
: { error: `run-evidence export failed (HTTP ${r.status})` };
|
|
23
27
|
}
|
package/dist/agent/runner.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CoinRithmClient } from "./client.js";
|
|
1
|
+
import { CoinRithmClient, ProvenanceReport } from "./client.js";
|
|
2
2
|
import { Provider } from "./providers.js";
|
|
3
|
-
import { AgentSpec, RunState, CycleResult, ProposedAction, QuoteEvidence } from "./types.js";
|
|
3
|
+
import { AgentSpec, RunState, CycleResult, Decision, ProposedAction, PmMarket, PostedOpportunity, QuoteEvidence } from "./types.js";
|
|
4
4
|
export interface RunnerDeps {
|
|
5
5
|
client: CoinRithmClient;
|
|
6
6
|
provider: Provider;
|
|
@@ -11,11 +11,17 @@ export interface RunnerDeps {
|
|
|
11
11
|
stateFile?: string;
|
|
12
12
|
log?: (line: string) => void;
|
|
13
13
|
}
|
|
14
|
+
export declare function houseAgentForecastEnabled(): boolean;
|
|
15
|
+
export declare function agentOpportunityCaptureEnabled(): boolean;
|
|
16
|
+
export declare function runnerRuntimeKind(): ProvenanceReport["runtimeKind"];
|
|
17
|
+
export declare function buildRunnerProvenance(spec: AgentSpec): ProvenanceReport;
|
|
18
|
+
export declare function sanitizeForecastProbability(raw: unknown): number | undefined;
|
|
14
19
|
export declare function repairFuturesTakeProfit(action: ProposedAction, quote?: QuoteEvidence): {
|
|
15
20
|
action: ProposedAction;
|
|
16
21
|
repaired: boolean;
|
|
17
22
|
};
|
|
18
23
|
export declare function rationaleForAction(a: ProposedAction, decisionRationale: string | undefined, perActionSummary: string | undefined, totalActions: number): string | undefined;
|
|
24
|
+
export declare function buildSkipOpportunity(decision: Decision, pmMarkets: PmMarket[], forecastEnabled: boolean): PostedOpportunity | null;
|
|
19
25
|
export declare function runCycle(deps: RunnerDeps): Promise<CycleResult>;
|
|
20
26
|
export interface LoopOptions {
|
|
21
27
|
once?: boolean;
|