@senpi/runtime 3.0.146 → 3.0.149
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/dist/cli/guide-commands.d.ts.map +1 -1
- package/dist/cli/guide-commands.js +55 -0
- package/dist/cli/guide-commands.js.map +1 -1
- package/dist/cli/quant-command.d.ts +128 -0
- package/dist/cli/quant-command.d.ts.map +1 -0
- package/dist/cli/quant-command.js +345 -0
- package/dist/cli/quant-command.js.map +1 -0
- package/dist/cli/senpi-commands.d.ts +11 -0
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +49 -4
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -1
- package/dist/quant/engine/quantdesk/__init__.py +19 -0
- package/dist/quant/engine/quantdesk/__main__.py +281 -0
- package/dist/quant/engine/quantdesk/_version.py +24 -0
- package/dist/quant/engine/quantdesk/benchmark.py +90 -0
- package/dist/quant/engine/quantdesk/book.py +197 -0
- package/dist/quant/engine/quantdesk/deep.py +197 -0
- package/dist/quant/engine/quantdesk/desk.py +878 -0
- package/dist/quant/engine/quantdesk/dsl.py +224 -0
- package/dist/quant/engine/quantdesk/followups.py +117 -0
- package/dist/quant/engine/quantdesk/hl_api.py +473 -0
- package/dist/quant/engine/quantdesk/market.py +290 -0
- package/dist/quant/engine/quantdesk/mcp_client.py +158 -0
- package/dist/quant/engine/quantdesk/metrics.py +409 -0
- package/dist/quant/engine/quantdesk/opportunities.py +88 -0
- package/dist/quant/engine/quantdesk/render.py +787 -0
- package/dist/quant/engine/quantdesk/roundtrips.py +124 -0
- package/dist/quant/engine/quantdesk/score.py +817 -0
- package/dist/quant/engine/quantdesk/senpi_history.py +160 -0
- package/dist/quant/engine/quantdesk/smart_money.py +354 -0
- package/dist/quant/engine/quantdesk/strategy_read.py +292 -0
- package/dist/quant/engine/quantdesk/taxonomy.py +55 -0
- package/dist/quant/engine/quantdesk/timing.py +164 -0
- package/dist/quant/engine/quantdesk/voice.py +42 -0
- package/dist/quant/engine-runner.d.ts +133 -0
- package/dist/quant/engine-runner.d.ts.map +1 -0
- package/dist/quant/engine-runner.js +306 -0
- package/dist/quant/engine-runner.js.map +1 -0
- package/dist/quant/job.d.ts +125 -0
- package/dist/quant/job.d.ts.map +1 -0
- package/dist/quant/job.js +184 -0
- package/dist/quant/job.js.map +1 -0
- package/dist/quant/register.d.ts +53 -0
- package/dist/quant/register.d.ts.map +1 -0
- package/dist/quant/register.js +253 -0
- package/dist/quant/register.js.map +1 -0
- package/dist/quant/registry.d.ts +105 -0
- package/dist/quant/registry.d.ts.map +1 -0
- package/dist/quant/registry.js +248 -0
- package/dist/quant/registry.js.map +1 -0
- package/dist/quant/types.d.ts +216 -0
- package/dist/quant/types.d.ts.map +1 -0
- package/dist/quant/types.js +121 -0
- package/dist/quant/types.js.map +1 -0
- package/dist/skills-manager/manifest.js +1 -1
- package/dist/skills-manager/skills-manifest.json +73 -16
- package/dist/widgets/index.d.ts +8 -1
- package/dist/widgets/index.d.ts.map +1 -1
- package/dist/widgets/index.js +1 -0
- package/dist/widgets/index.js.map +1 -1
- package/dist/widgets/registry.d.ts +8 -0
- package/dist/widgets/registry.d.ts.map +1 -1
- package/dist/widgets/registry.js +26 -1
- package/dist/widgets/registry.js.map +1 -1
- package/dist/widgets/tool.d.ts +14 -4
- package/dist/widgets/tool.d.ts.map +1 -1
- package/dist/widgets/tool.js +36 -23
- package/dist/widgets/tool.js.map +1 -1
- package/dist/widgets/turn-cap.d.ts +33 -0
- package/dist/widgets/turn-cap.d.ts.map +1 -0
- package/dist/widgets/turn-cap.js +119 -0
- package/dist/widgets/turn-cap.js.map +1 -0
- package/dist/widgets/types.d.ts +33 -5
- package/dist/widgets/types.d.ts.map +1 -1
- package/dist/widgets/types.js +4 -3
- package/dist/widgets/types.js.map +1 -1
- package/dist/widgets/widgets/fund-user-wallet.d.ts.map +1 -1
- package/dist/widgets/widgets/fund-user-wallet.js +1 -0
- package/dist/widgets/widgets/fund-user-wallet.js.map +1 -1
- package/dist/widgets/widgets/quant-desk-recommendations.d.ts +52 -0
- package/dist/widgets/widgets/quant-desk-recommendations.d.ts.map +1 -0
- package/dist/widgets/widgets/quant-desk-recommendations.js +278 -0
- package/dist/widgets/widgets/quant-desk-recommendations.js.map +1 -0
- package/openclaw.plugin.json +12 -2
- package/package.json +4 -3
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quant run registry: every desk this runtime computed, under `<senpi-state>/quant/`.
|
|
3
|
+
*
|
|
4
|
+
* runs/<runId>.json one {@link QuantRunRecord} per run (document + meta)
|
|
5
|
+
* index.json newest-first summaries; the source for list, freshness and pruning
|
|
6
|
+
*
|
|
7
|
+
* Producer = owner (spec D3): the engine's document lives where the runtime can read it in-process
|
|
8
|
+
* for the widget. Writes are temp-then-rename; index read-modify-writes are serialized by a
|
|
9
|
+
* KeyedMutex. The single-flight slot is NOT here — it is in-memory in job.ts, so a crash can never
|
|
10
|
+
* wedge a wallet; a restart instead stamps leftover `running` rows failed (`markInterrupted`).
|
|
11
|
+
*/
|
|
12
|
+
import { randomBytes } from "node:crypto";
|
|
13
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { KeyedMutex } from "../utils/keyed-mutex.js";
|
|
16
|
+
import { QUANT_DOC_VERSION, QUANT_RUN_ID_RE, QuantRunLookupError, quantError, quantRequestKey, quantRunArgs, } from "./types.js";
|
|
17
|
+
/** Runs retained; older ones are deleted with their files. Running runs are never pruned. */
|
|
18
|
+
export const QUANT_KEEP_RUNS = 20;
|
|
19
|
+
/** A done run for the same request younger than this is served instead of recomputed. */
|
|
20
|
+
export const QUANT_FRESH_MS = 10 * 60_000;
|
|
21
|
+
const INDEX_FILE = "index.json";
|
|
22
|
+
const RUNS_DIR = "runs";
|
|
23
|
+
const INDEX_LOCK = "index";
|
|
24
|
+
/** Per-run lock key prefix for {@link QuantRunRegistry.finish}; distinct from {@link INDEX_LOCK}. */
|
|
25
|
+
const RUN_LOCK_PREFIX = "run:";
|
|
26
|
+
/** Temp file then rename: a reader sees the old file or the new one, never half of either. */
|
|
27
|
+
async function writeJsonAtomic(path, data) {
|
|
28
|
+
const tmp = `${path}.tmp.${process.pid}.${randomBytes(3).toString("hex")}`;
|
|
29
|
+
await writeFile(tmp, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
30
|
+
await rename(tmp, path);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Project a record onto its index row. The row copies `whose` from meta so list surfaces can print
|
|
34
|
+
* the voice without opening every record, and carries the freshness `key` so `findFresh` never does.
|
|
35
|
+
*/
|
|
36
|
+
function summaryOf(record) {
|
|
37
|
+
const m = record.meta;
|
|
38
|
+
return {
|
|
39
|
+
runId: record.runId,
|
|
40
|
+
key: quantRequestKey(m),
|
|
41
|
+
addresses: m.addresses,
|
|
42
|
+
days: m.days,
|
|
43
|
+
whose: m.whose,
|
|
44
|
+
book: m.book,
|
|
45
|
+
status: m.status,
|
|
46
|
+
startedAt: m.startedAt,
|
|
47
|
+
...(m.finishedAt ? { finishedAt: m.finishedAt } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** File-backed registry of quant runs. See the module doc for the layout and invariants. */
|
|
51
|
+
export class QuantRunRegistry {
|
|
52
|
+
opts;
|
|
53
|
+
mutex = new KeyedMutex();
|
|
54
|
+
keep;
|
|
55
|
+
freshMs;
|
|
56
|
+
constructor(opts) {
|
|
57
|
+
this.opts = opts;
|
|
58
|
+
this.keep = opts.keep ?? QUANT_KEEP_RUNS;
|
|
59
|
+
this.freshMs = opts.freshMs ?? QUANT_FRESH_MS;
|
|
60
|
+
}
|
|
61
|
+
/** A fresh run id (`qd-` + 8 hex). */
|
|
62
|
+
newRunId() {
|
|
63
|
+
return `qd-${randomBytes(4).toString("hex")}`;
|
|
64
|
+
}
|
|
65
|
+
/** The record path for a VALID run id. Throws on anything else, so no caller can build a traversal. */
|
|
66
|
+
runPath(runId) {
|
|
67
|
+
if (!QUANT_RUN_ID_RE.test(runId))
|
|
68
|
+
throw new Error(`not a quant run id: ${JSON.stringify(runId)}`);
|
|
69
|
+
return join(this.opts.dir, RUNS_DIR, `${runId}.json`);
|
|
70
|
+
}
|
|
71
|
+
/** Record a new `running` run and put it at the head of the index. */
|
|
72
|
+
async create(runId, request, engineVersion) {
|
|
73
|
+
const record = {
|
|
74
|
+
runId,
|
|
75
|
+
docVersion: QUANT_DOC_VERSION,
|
|
76
|
+
meta: {
|
|
77
|
+
...request,
|
|
78
|
+
addresses: request.addresses.map((a) => a.toLowerCase()),
|
|
79
|
+
startedAt: new Date(this.opts.now()).toISOString(),
|
|
80
|
+
engineVersion,
|
|
81
|
+
status: "running",
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
await mkdir(join(this.opts.dir, RUNS_DIR), { recursive: true });
|
|
85
|
+
await writeJsonAtomic(this.runPath(runId), record);
|
|
86
|
+
await this.updateIndex((runs) => [summaryOf(record), ...runs.filter((r) => r.runId !== runId)]);
|
|
87
|
+
return record;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Close a run with its outcome. The FIRST outcome wins: a record that is already terminal
|
|
91
|
+
* (done/failed/refused) is never overwritten, and the call answers the stored record instead.
|
|
92
|
+
*
|
|
93
|
+
* Why first-wins: two finishers can race on one run — the engine's exit landing just as its
|
|
94
|
+
* watchdog fires, or the boot janitor meeting a run whose file was written done a moment before a
|
|
95
|
+
* crash cut off the index update. Overwriting would turn a delivered desk into a failure. The
|
|
96
|
+
* check-then-write is serialized under a per-run lock (`run:<id>`, never the index lock; nothing
|
|
97
|
+
* nests on it), so the second finisher always sees the first one's file.
|
|
98
|
+
*
|
|
99
|
+
* @returns The stored record after the call — the new one, or the earlier terminal one untouched.
|
|
100
|
+
* @throws When the run has no record (it was never created, or its file is gone or unreadable).
|
|
101
|
+
*/
|
|
102
|
+
async finish(runId, outcome) {
|
|
103
|
+
const settled = await this.settle(runId, outcome);
|
|
104
|
+
if (!settled)
|
|
105
|
+
throw new Error(`quant run ${runId} has no record to finish`);
|
|
106
|
+
return settled.record;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The body of {@link finish}, reporting whether it wrote. `undefined` = no readable record.
|
|
110
|
+
*
|
|
111
|
+
* When the record is already terminal it re-syncs that run's index row from the file rather than
|
|
112
|
+
* writing: the file is the truth, and an index row still saying `running` (the crash window) would
|
|
113
|
+
* otherwise make `status` report a dead run as live. For a double finish that already updated the
|
|
114
|
+
* index, the re-sync rewrites the same row — harmless.
|
|
115
|
+
*/
|
|
116
|
+
async settle(runId, outcome) {
|
|
117
|
+
return this.mutex.withKeyLock(`${RUN_LOCK_PREFIX}${runId}`, async () => {
|
|
118
|
+
const current = await this.readRecord(runId);
|
|
119
|
+
if (!current?.meta)
|
|
120
|
+
return undefined;
|
|
121
|
+
if (current.meta.status !== "running") {
|
|
122
|
+
await this.updateIndex((runs) => runs.map((r) => (r.runId === runId ? summaryOf(current) : r)));
|
|
123
|
+
return { record: current, wrote: false };
|
|
124
|
+
}
|
|
125
|
+
const next = {
|
|
126
|
+
runId: current.runId,
|
|
127
|
+
docVersion: current.docVersion,
|
|
128
|
+
meta: { ...current.meta, status: outcome.status, finishedAt: new Date(this.opts.now()).toISOString() },
|
|
129
|
+
...(outcome.status === "done" ? { document: outcome.document } : { error: outcome.error }),
|
|
130
|
+
};
|
|
131
|
+
await writeJsonAtomic(this.runPath(runId), next);
|
|
132
|
+
await this.updateIndex((runs) => runs.map((r) => (r.runId === runId ? summaryOf(next) : r)));
|
|
133
|
+
return { record: next, wrote: true };
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Read a run. `undefined` for an invalid id, a missing file or an unreadable one — all three are
|
|
138
|
+
* "no such run" to a caller. Throws {@link QuantRunLookupError} for a record of another docVersion.
|
|
139
|
+
*/
|
|
140
|
+
async get(runId) {
|
|
141
|
+
if (!QUANT_RUN_ID_RE.test(runId))
|
|
142
|
+
return undefined;
|
|
143
|
+
const record = await this.readRecord(runId);
|
|
144
|
+
if (!record)
|
|
145
|
+
return undefined;
|
|
146
|
+
if (record.docVersion !== QUANT_DOC_VERSION) {
|
|
147
|
+
const again = record.meta?.addresses ? quantRunArgs(record.meta) : "<0x address>";
|
|
148
|
+
throw new QuantRunLookupError(quantError("E_QUANT_DOC_VERSION", `run ${runId} was stored as docVersion ${String(record.docVersion)}; this runtime reads docVersion ${QUANT_DOC_VERSION} only.`, `Run the desk again: openclaw senpi quant run ${again} --fresh`));
|
|
149
|
+
}
|
|
150
|
+
return record;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Newest-first summaries. The index is already bounded by {@link updateIndex}'s pruning: at most
|
|
154
|
+
* `keep` rows plus any `running` rows past the cap. It is deliberately NOT sliced to `keep` here —
|
|
155
|
+
* pruning spares a running run precisely so it stays visible, and a slice would hide a live run
|
|
156
|
+
* from `senpi quant list` while its record still exists.
|
|
157
|
+
*/
|
|
158
|
+
async list() {
|
|
159
|
+
return (await this.readIndex()).runs;
|
|
160
|
+
}
|
|
161
|
+
/** The newest done run for this exact request finished within the freshness window, if any. */
|
|
162
|
+
async findFresh(request) {
|
|
163
|
+
const key = quantRequestKey(request);
|
|
164
|
+
const hit = (await this.readIndex()).runs.find((r) => r.key === key && r.status === "done");
|
|
165
|
+
if (!hit?.finishedAt || this.opts.now() - Date.parse(hit.finishedAt) >= this.freshMs)
|
|
166
|
+
return undefined;
|
|
167
|
+
try {
|
|
168
|
+
const record = await this.get(hit.runId);
|
|
169
|
+
return record?.meta.status === "done" && record.document ? record : undefined;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Boot janitor: every run still `running` in the index belongs to a process that no longer exists
|
|
177
|
+
* (the slot is in-memory only). Each such row is resolved from its file:
|
|
178
|
+
* - file still `running` → stamped `failed` with the exact re-run command, so `senpi quant status`
|
|
179
|
+
* never reports a dead run as live;
|
|
180
|
+
* - file already terminal (the process died between writing the record and the index) → the
|
|
181
|
+
* index row is re-synced from the file; the delivered outcome is kept, never stamped failed;
|
|
182
|
+
* - no readable file → the row is dropped (there is nothing to report or re-read).
|
|
183
|
+
*
|
|
184
|
+
* Must run before this process starts any run of its own: a run it created would be `running`
|
|
185
|
+
* too and would be stamped. The job controller gates `start` on it for that reason.
|
|
186
|
+
*
|
|
187
|
+
* @returns Only the ids it STAMPED failed — not re-synced or dropped rows — so the boot log names
|
|
188
|
+
* exactly the runs a restart cut short.
|
|
189
|
+
*/
|
|
190
|
+
async markInterrupted() {
|
|
191
|
+
const stamped = [];
|
|
192
|
+
for (const s of (await this.readIndex()).runs) {
|
|
193
|
+
if (s.status !== "running")
|
|
194
|
+
continue;
|
|
195
|
+
const error = quantError("E_QUANT_ENGINE_ERROR", `run ${s.runId} was interrupted by a gateway restart before it finished.`, `Run the desk again: openclaw senpi quant run ${quantRunArgs(s)}`);
|
|
196
|
+
const settled = await this.settle(s.runId, { status: "failed", error });
|
|
197
|
+
if (!settled)
|
|
198
|
+
await this.updateIndex((runs) => runs.filter((r) => r.runId !== s.runId));
|
|
199
|
+
else if (settled.wrote)
|
|
200
|
+
stamped.push(s.runId);
|
|
201
|
+
}
|
|
202
|
+
return stamped;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Parse `runs/<runId>.json` without any version check. Missing, unparseable and invalid-id (runPath
|
|
206
|
+
* throws) all collapse to `undefined` — the caller decides what "no record" means.
|
|
207
|
+
*/
|
|
208
|
+
async readRecord(runId) {
|
|
209
|
+
try {
|
|
210
|
+
return JSON.parse(await readFile(this.runPath(runId), "utf8"));
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Read `index.json`; a missing or corrupt index reads as empty rather than failing, so a damaged
|
|
218
|
+
* index costs history, never the ability to start a run (the next write rewrites it).
|
|
219
|
+
*/
|
|
220
|
+
async readIndex() {
|
|
221
|
+
try {
|
|
222
|
+
const parsed = JSON.parse(await readFile(join(this.opts.dir, INDEX_FILE), "utf8"));
|
|
223
|
+
return Array.isArray(parsed.runs) ? { version: 1, runs: parsed.runs } : { version: 1, runs: [] };
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return { version: 1, runs: [] };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Serialized read-modify-write of the index; prunes past `keep` (never a running run) and deletes
|
|
231
|
+
* dropped files. A dropped row whose id is not a run id (a hand-edited index) loses its row but
|
|
232
|
+
* no file: `runPath` would throw on it, and one bad row must not break every later write — nor
|
|
233
|
+
* may it name a path outside `runs/`.
|
|
234
|
+
*/
|
|
235
|
+
async updateIndex(fn) {
|
|
236
|
+
await this.mutex.withKeyLock(INDEX_LOCK, async () => {
|
|
237
|
+
const kept = [];
|
|
238
|
+
const dropped = [];
|
|
239
|
+
for (const r of fn((await this.readIndex()).runs)) {
|
|
240
|
+
(kept.length < this.keep || r.status === "running" ? kept : dropped).push(r);
|
|
241
|
+
}
|
|
242
|
+
await mkdir(this.opts.dir, { recursive: true });
|
|
243
|
+
await writeJsonAtomic(join(this.opts.dir, INDEX_FILE), { version: 1, runs: kept });
|
|
244
|
+
await Promise.all(dropped.filter((r) => QUANT_RUN_ID_RE.test(r.runId)).map((r) => rm(this.runPath(r.runId), { force: true })));
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/quant/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quant desk's vocabulary inside the runtime: the engine's document (docVersion 1), the card
|
|
3
|
+
* schema the widget reads, the stored run record, and the refusal codes every surface prints.
|
|
4
|
+
*
|
|
5
|
+
* A leaf module with no imports, so the widgets module can depend on it without pulling in the job
|
|
6
|
+
* controller or the spawn helper. `QUANT_SECTIONS`, `QUANT_DOC_VERSION` and `QuantCardKind` are
|
|
7
|
+
* pinned against the Python engine by python-tests/quant/test_runtime_contract.py.
|
|
8
|
+
*/
|
|
9
|
+
/** The only document version this runtime reads. Any other version is refused, never guessed at. */
|
|
10
|
+
export declare const QUANT_DOC_VERSION: 1;
|
|
11
|
+
/** The engine's render sections, in render order (`render.SECTIONS`). */
|
|
12
|
+
export declare const QUANT_SECTIONS: readonly ["overview", "strategy", "context", "protection", "performance", "leaks", "smart", "market", "edge", "scout", "next", "followups"];
|
|
13
|
+
/** One renderable desk section. */
|
|
14
|
+
export type QuantSection = (typeof QUANT_SECTIONS)[number];
|
|
15
|
+
/** `qd-` + 8 hex. Every id is validated against this before it is joined into a path. */
|
|
16
|
+
export declare const QUANT_RUN_ID_RE: RegExp;
|
|
17
|
+
/** A Hyperliquid address as the engine accepts it. */
|
|
18
|
+
export declare const QUANT_ADDRESS_RE: RegExp;
|
|
19
|
+
/** The desk's window when none is asked for. */
|
|
20
|
+
export declare const QUANT_DEFAULT_DAYS = 90;
|
|
21
|
+
/** The widest window the runtime asks the engine for. */
|
|
22
|
+
export declare const QUANT_MAX_DAYS = 365;
|
|
23
|
+
/**
|
|
24
|
+
* A book can union at most this many wallets in one request. 25, not "every strategy wallet": the
|
|
25
|
+
* skill reads a senpi user's whole book, and users with more wallets than this pass the 25 with the
|
|
26
|
+
* most recent activity (the refusal in `parseStartParams` teaches exactly that). The engine's own
|
|
27
|
+
* breadth gate (coins touched, tape cap) still refuses a book too wide to read, whatever its count.
|
|
28
|
+
*/
|
|
29
|
+
export declare const QUANT_MAX_ADDRESSES = 25;
|
|
30
|
+
/** Lifecycle of a run: `refused` = the engine answered "no" deterministically; `failed` = it could not answer. */
|
|
31
|
+
export type QuantRunStatus = "running" | "done" | "failed" | "refused";
|
|
32
|
+
/** Whose book: the reader's own (second person) or someone else's (analyst voice). */
|
|
33
|
+
export type QuantWhose = "mine" | "other";
|
|
34
|
+
/** The card kinds the engine emits. */
|
|
35
|
+
export type QuantCardKind = "protect" | "leak" | "hire" | "build";
|
|
36
|
+
/** The executable actions a card can carry. One today: place a stop loss on a position the reader holds. */
|
|
37
|
+
export type QuantCardActionType = "set_stop_loss";
|
|
38
|
+
/**
|
|
39
|
+
* One stop loss the protect card's Set stop loss button can place, as the engine priced it during the run
|
|
40
|
+
* (`deep.card_stop_losses`: the stop ladder's hard stop). Snake_case because it is the engine's field
|
|
41
|
+
* spelling and the client's wire contract; the client re-reads the live position before signing,
|
|
42
|
+
* so `mark` is a run-time snapshot, never a price to sign.
|
|
43
|
+
*
|
|
44
|
+
* Invariants the widget enforces before a client sees one (`parseQuantStopLoss`): every number finite
|
|
45
|
+
* and > 0, `trigger_px` on the losing side of `mark` (below it for a long, above for a short) and
|
|
46
|
+
* short of `liq_px` when there is one, `dex` equal to the coin's prefix ("" on the main dex,
|
|
47
|
+
* "xyz" for `xyz:TSLA` — the coin keeps its prefix), `wallet` a lowercased 0x address: the
|
|
48
|
+
* position's owner, which a single-wallet run fills with the document's address.
|
|
49
|
+
*/
|
|
50
|
+
export interface QuantStopLoss {
|
|
51
|
+
readonly coin: string;
|
|
52
|
+
readonly dex: string;
|
|
53
|
+
readonly side: "long" | "short";
|
|
54
|
+
/** Absolute position size. Informational: the order closes the whole position. */
|
|
55
|
+
readonly size: number;
|
|
56
|
+
readonly entry: number;
|
|
57
|
+
readonly mark: number;
|
|
58
|
+
readonly liq_px: number | null;
|
|
59
|
+
/** The ladder's hard stop. */
|
|
60
|
+
readonly trigger_px: number;
|
|
61
|
+
/** Distance from `mark`, in percent. */
|
|
62
|
+
readonly trigger_pct: number;
|
|
63
|
+
readonly wallet: `0x${string}`;
|
|
64
|
+
}
|
|
65
|
+
/** What a card lets the reader do. `stop_losses` is never empty — a card with none carries no action. */
|
|
66
|
+
export interface QuantCardAction {
|
|
67
|
+
readonly type: QuantCardActionType;
|
|
68
|
+
readonly stop_losses: readonly QuantStopLoss[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The allowlist of executable cards: the ONE action type each kind may carry, or null for none.
|
|
72
|
+
*
|
|
73
|
+
* The runtime is the single enforcement point (spec D3). The widget handler is the only code that
|
|
74
|
+
* turns a stored document into a client payload, and a stored document crossed a file boundary —
|
|
75
|
+
* an older or newer engine, a cached run, a hand-edited file — so the engine's own restraint is not
|
|
76
|
+
* trusted: `cardAction` strips any action this map does not allow before the payload is built.
|
|
77
|
+
* The engine is pinned to it by python-tests/quant/test_runtime_contract.py.
|
|
78
|
+
*/
|
|
79
|
+
export declare const ACTIONABLE_KINDS: Readonly<Record<QuantCardKind, QuantCardActionType | null>>;
|
|
80
|
+
/**
|
|
81
|
+
* One recommendation card, exactly as the engine rendered it. Flat except for `action`. `usd`/`window`
|
|
82
|
+
* are the leak record's own values (null on non-leak cards); `cta` is text with no button semantics.
|
|
83
|
+
* `id` is stable per kind and position (today the engine emits `protect-1`, `leak-1`, `hire`,
|
|
84
|
+
* `build`; `leak-2` is legal), so it is typed as a string rather than an enum. Analyst (`other`)
|
|
85
|
+
* runs carry no cards. `action` is present only where {@link ACTIONABLE_KINDS} allows it (widget v2).
|
|
86
|
+
*/
|
|
87
|
+
export interface QuantCard {
|
|
88
|
+
readonly id: string;
|
|
89
|
+
readonly kind: QuantCardKind;
|
|
90
|
+
readonly title: string;
|
|
91
|
+
readonly detail: string;
|
|
92
|
+
readonly usd: number | null;
|
|
93
|
+
readonly window: string | null;
|
|
94
|
+
readonly cta: string;
|
|
95
|
+
readonly action?: QuantCardAction;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The engine's --json document plus `cards` and `doc_version`. Everything else is opaque here.
|
|
99
|
+
*
|
|
100
|
+
* The stored document crossed a strict-JSON boundary: the engine writes non-finite floats (e.g. a
|
|
101
|
+
* profit factor on an all-winning bucket) as the STRINGS `"Infinity"`, `"-Infinity"`, `"NaN"`, and
|
|
102
|
+
* `render --doc` restores them. So no opaque field may be assumed numeric — read them as `unknown`
|
|
103
|
+
* and narrow. `cards` are the exception: the engine only emits finite `usd` values there.
|
|
104
|
+
*/
|
|
105
|
+
export interface QuantDocument {
|
|
106
|
+
readonly doc_version: number;
|
|
107
|
+
readonly cards: readonly QuantCard[];
|
|
108
|
+
readonly address: string;
|
|
109
|
+
readonly days: number;
|
|
110
|
+
readonly whose?: string;
|
|
111
|
+
readonly [key: string]: unknown;
|
|
112
|
+
}
|
|
113
|
+
/** What a caller asks the desk for, after validation. Addresses are lowercased and de-duplicated. */
|
|
114
|
+
export interface QuantRequest {
|
|
115
|
+
readonly addresses: readonly string[];
|
|
116
|
+
readonly days: number;
|
|
117
|
+
readonly whose: QuantWhose;
|
|
118
|
+
readonly book: boolean;
|
|
119
|
+
readonly force: boolean;
|
|
120
|
+
}
|
|
121
|
+
/** Stable refusal/failure codes. Refusals lead their message with `[CODE]`. */
|
|
122
|
+
export type QuantErrorCode = "E_QUANT_INVALID_REQUEST" | "E_QUANT_UNSUPPORTED" | "E_QUANT_IN_PROGRESS" | "E_QUANT_NOT_FOUND" | "E_QUANT_DOC_VERSION" | "E_QUANT_NO_ACTIVITY" | "E_QUANT_NOT_A_TRADER" | "E_QUANT_UPSTREAM" | "E_QUANT_TIMEOUT" | "E_QUANT_ENGINE_ERROR";
|
|
123
|
+
/** A refusal or failure: the bracketed message, the non-destructive next step, optional engine detail. */
|
|
124
|
+
export interface QuantError {
|
|
125
|
+
readonly code: QuantErrorCode;
|
|
126
|
+
readonly message: string;
|
|
127
|
+
readonly what_to_do: string;
|
|
128
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build a {@link QuantError}. The message always leads with `[CODE]` — the CLI's exit-code
|
|
132
|
+
* classifier (`deploy/taxonomy.ts` `isRefusalMessage`) reads exactly that prefix.
|
|
133
|
+
*/
|
|
134
|
+
export declare function quantError(code: QuantErrorCode, text: string, whatToDo: string, details?: Record<string, unknown>): QuantError;
|
|
135
|
+
/** The stored run's meta: the request plus timing, engine version and status. */
|
|
136
|
+
export interface QuantRunMeta extends QuantRequest {
|
|
137
|
+
readonly startedAt: string;
|
|
138
|
+
readonly finishedAt?: string;
|
|
139
|
+
readonly engineVersion: string;
|
|
140
|
+
readonly status: QuantRunStatus;
|
|
141
|
+
}
|
|
142
|
+
/** `runs/<runId>.json`. `document` only when done; `error` only when failed/refused. */
|
|
143
|
+
export interface QuantRunRecord {
|
|
144
|
+
readonly runId: string;
|
|
145
|
+
readonly docVersion: number;
|
|
146
|
+
readonly meta: QuantRunMeta;
|
|
147
|
+
readonly document?: QuantDocument;
|
|
148
|
+
readonly error?: QuantError;
|
|
149
|
+
}
|
|
150
|
+
/** One row of `index.json` (newest first). `key` is {@link quantRequestKey}. */
|
|
151
|
+
export interface QuantRunSummary {
|
|
152
|
+
readonly runId: string;
|
|
153
|
+
readonly key: string;
|
|
154
|
+
readonly addresses: readonly string[];
|
|
155
|
+
readonly days: number;
|
|
156
|
+
readonly whose: QuantWhose;
|
|
157
|
+
readonly book: boolean;
|
|
158
|
+
readonly status: QuantRunStatus;
|
|
159
|
+
readonly startedAt: string;
|
|
160
|
+
readonly finishedAt?: string;
|
|
161
|
+
}
|
|
162
|
+
/** The read side the widget needs — injected at registration so widgets never construct a registry. */
|
|
163
|
+
export interface QuantRunReader {
|
|
164
|
+
get(runId: string): Promise<QuantRunRecord | undefined>;
|
|
165
|
+
list(): Promise<readonly QuantRunSummary[]>;
|
|
166
|
+
}
|
|
167
|
+
/** A stored run exists but cannot be read by this runtime (e.g. another docVersion). */
|
|
168
|
+
export declare class QuantRunLookupError extends Error {
|
|
169
|
+
readonly error: QuantError;
|
|
170
|
+
constructor(error: QuantError);
|
|
171
|
+
}
|
|
172
|
+
/** The single-flight key: the wallet set, case- and order-insensitive. */
|
|
173
|
+
export declare function addressSetKey(addresses: readonly string[]): string;
|
|
174
|
+
/**
|
|
175
|
+
* The freshness key: wallet set + days + voice + book. Voice is part of it on purpose — serving an
|
|
176
|
+
* analyst run to a reader asking about their own book would hand them a stranger's voice.
|
|
177
|
+
*/
|
|
178
|
+
export declare function quantRequestKey(req: Pick<QuantRequest, "addresses" | "days" | "whose" | "book">): string;
|
|
179
|
+
/**
|
|
180
|
+
* The arguments of the CLI command that re-runs EXACTLY this request, for teaching text.
|
|
181
|
+
*
|
|
182
|
+
* Every field that changes the desk must be spelled, or a "run it again" hint quietly asks for a
|
|
183
|
+
* different desk: without `--other` an exit-4 `… --force` would tell the agent to force-read a vault
|
|
184
|
+
* as the reader's OWN book, and without `--days` a 30-day timeout would retry as 90 days. Defaults
|
|
185
|
+
* (own voice, {@link QUANT_DEFAULT_DAYS}) are omitted so the common command stays short. Stable
|
|
186
|
+
* order: addresses, `--book`, `--other`, `--days N`. `force` is never emitted — only the exit-4
|
|
187
|
+
* refusal may add it, deliberately.
|
|
188
|
+
*
|
|
189
|
+
* @param req The request (or a stored run's meta/summary, which carry the same fields).
|
|
190
|
+
* @returns e.g. `0xabc… --book --other --days 30`.
|
|
191
|
+
*/
|
|
192
|
+
export declare function quantRunArgs(req: Pick<QuantRequest, "addresses" | "book" | "whose" | "days">): string;
|
|
193
|
+
/**
|
|
194
|
+
* Senpi's trader index, for the not-indexed answer (moved from the skill's references/coverage.json).
|
|
195
|
+
* Distinct wallets with closed positions in Senpi's index — never Hyperliquid's leaderboard row
|
|
196
|
+
* count (~46,600 rows; quoting that once overstated our coverage by 73%). The engine's exit-3
|
|
197
|
+
* refusal carries only `indexed` (true/false/unknown), never a count, so the runtime owns this
|
|
198
|
+
* figure and the skill hardcodes nothing. `staleAfterDays` bounds how long the exact count may be
|
|
199
|
+
* quoted after `asOf`; past it, `quoteAs` is the fallback phrasing and must never round upward.
|
|
200
|
+
*/
|
|
201
|
+
export declare const SENPI_INDEX_COVERAGE: {
|
|
202
|
+
readonly indexedWallets: 26188;
|
|
203
|
+
readonly asOf: "2026-09-21";
|
|
204
|
+
readonly staleAfterDays: 7;
|
|
205
|
+
readonly quoteAs: "over 25,000";
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* The index count to print: exact while fresh, the never-rounded-up fallback once stale (the
|
|
209
|
+
* skill's old rule). An index keeps growing after `asOf`, so an old exact figure is still a floor —
|
|
210
|
+
* but a stale "26,188" reads as current precision we no longer have, hence the round phrasing.
|
|
211
|
+
*
|
|
212
|
+
* @param nowMs Epoch milliseconds to judge staleness against (injected so callers and tests control time).
|
|
213
|
+
* @returns `indexedWallets` formatted en-US (e.g. "26,188") within `staleAfterDays` of `asOf`, else `quoteAs`.
|
|
214
|
+
*/
|
|
215
|
+
export declare function indexCoverageFigure(nowMs: number): string;
|
|
216
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/quant/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quant desk's vocabulary inside the runtime: the engine's document (docVersion 1), the card
|
|
3
|
+
* schema the widget reads, the stored run record, and the refusal codes every surface prints.
|
|
4
|
+
*
|
|
5
|
+
* A leaf module with no imports, so the widgets module can depend on it without pulling in the job
|
|
6
|
+
* controller or the spawn helper. `QUANT_SECTIONS`, `QUANT_DOC_VERSION` and `QuantCardKind` are
|
|
7
|
+
* pinned against the Python engine by python-tests/quant/test_runtime_contract.py.
|
|
8
|
+
*/
|
|
9
|
+
/** The only document version this runtime reads. Any other version is refused, never guessed at. */
|
|
10
|
+
export const QUANT_DOC_VERSION = 1;
|
|
11
|
+
/** The engine's render sections, in render order (`render.SECTIONS`). */
|
|
12
|
+
export const QUANT_SECTIONS = [
|
|
13
|
+
"overview", "strategy", "context", "protection", "performance", "leaks",
|
|
14
|
+
"smart", "market", "edge", "scout", "next", "followups",
|
|
15
|
+
];
|
|
16
|
+
/** `qd-` + 8 hex. Every id is validated against this before it is joined into a path. */
|
|
17
|
+
export const QUANT_RUN_ID_RE = /^qd-[0-9a-f]{8}$/;
|
|
18
|
+
/** A Hyperliquid address as the engine accepts it. */
|
|
19
|
+
export const QUANT_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
20
|
+
/** The desk's window when none is asked for. */
|
|
21
|
+
export const QUANT_DEFAULT_DAYS = 90;
|
|
22
|
+
/** The widest window the runtime asks the engine for. */
|
|
23
|
+
export const QUANT_MAX_DAYS = 365;
|
|
24
|
+
/**
|
|
25
|
+
* A book can union at most this many wallets in one request. 25, not "every strategy wallet": the
|
|
26
|
+
* skill reads a senpi user's whole book, and users with more wallets than this pass the 25 with the
|
|
27
|
+
* most recent activity (the refusal in `parseStartParams` teaches exactly that). The engine's own
|
|
28
|
+
* breadth gate (coins touched, tape cap) still refuses a book too wide to read, whatever its count.
|
|
29
|
+
*/
|
|
30
|
+
export const QUANT_MAX_ADDRESSES = 25;
|
|
31
|
+
/**
|
|
32
|
+
* The allowlist of executable cards: the ONE action type each kind may carry, or null for none.
|
|
33
|
+
*
|
|
34
|
+
* The runtime is the single enforcement point (spec D3). The widget handler is the only code that
|
|
35
|
+
* turns a stored document into a client payload, and a stored document crossed a file boundary —
|
|
36
|
+
* an older or newer engine, a cached run, a hand-edited file — so the engine's own restraint is not
|
|
37
|
+
* trusted: `cardAction` strips any action this map does not allow before the payload is built.
|
|
38
|
+
* The engine is pinned to it by python-tests/quant/test_runtime_contract.py.
|
|
39
|
+
*/
|
|
40
|
+
export const ACTIONABLE_KINDS = {
|
|
41
|
+
protect: "set_stop_loss", leak: null, hire: null, build: null,
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Build a {@link QuantError}. The message always leads with `[CODE]` — the CLI's exit-code
|
|
45
|
+
* classifier (`deploy/taxonomy.ts` `isRefusalMessage`) reads exactly that prefix.
|
|
46
|
+
*/
|
|
47
|
+
export function quantError(code, text, whatToDo, details) {
|
|
48
|
+
return {
|
|
49
|
+
code,
|
|
50
|
+
message: `[${code}] ${text}`,
|
|
51
|
+
what_to_do: whatToDo,
|
|
52
|
+
...(details && Object.keys(details).length > 0 ? { details } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** A stored run exists but cannot be read by this runtime (e.g. another docVersion). */
|
|
56
|
+
export class QuantRunLookupError extends Error {
|
|
57
|
+
error;
|
|
58
|
+
constructor(error) {
|
|
59
|
+
super(error.message);
|
|
60
|
+
this.error = error;
|
|
61
|
+
this.name = "QuantRunLookupError";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** The single-flight key: the wallet set, case- and order-insensitive. */
|
|
65
|
+
export function addressSetKey(addresses) {
|
|
66
|
+
return [...new Set(addresses.map((a) => a.trim().toLowerCase()))].sort().join(",");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The freshness key: wallet set + days + voice + book. Voice is part of it on purpose — serving an
|
|
70
|
+
* analyst run to a reader asking about their own book would hand them a stranger's voice.
|
|
71
|
+
*/
|
|
72
|
+
export function quantRequestKey(req) {
|
|
73
|
+
return `${req.book ? "book" : "one"}|${addressSetKey(req.addresses)}|${req.days}d|${req.whose}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The arguments of the CLI command that re-runs EXACTLY this request, for teaching text.
|
|
77
|
+
*
|
|
78
|
+
* Every field that changes the desk must be spelled, or a "run it again" hint quietly asks for a
|
|
79
|
+
* different desk: without `--other` an exit-4 `… --force` would tell the agent to force-read a vault
|
|
80
|
+
* as the reader's OWN book, and without `--days` a 30-day timeout would retry as 90 days. Defaults
|
|
81
|
+
* (own voice, {@link QUANT_DEFAULT_DAYS}) are omitted so the common command stays short. Stable
|
|
82
|
+
* order: addresses, `--book`, `--other`, `--days N`. `force` is never emitted — only the exit-4
|
|
83
|
+
* refusal may add it, deliberately.
|
|
84
|
+
*
|
|
85
|
+
* @param req The request (or a stored run's meta/summary, which carry the same fields).
|
|
86
|
+
* @returns e.g. `0xabc… --book --other --days 30`.
|
|
87
|
+
*/
|
|
88
|
+
export function quantRunArgs(req) {
|
|
89
|
+
const parts = [...req.addresses];
|
|
90
|
+
if (req.book)
|
|
91
|
+
parts.push("--book");
|
|
92
|
+
if (req.whose === "other")
|
|
93
|
+
parts.push("--other");
|
|
94
|
+
if (typeof req.days === "number" && req.days !== QUANT_DEFAULT_DAYS)
|
|
95
|
+
parts.push("--days", String(req.days));
|
|
96
|
+
return parts.join(" ");
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Senpi's trader index, for the not-indexed answer (moved from the skill's references/coverage.json).
|
|
100
|
+
* Distinct wallets with closed positions in Senpi's index — never Hyperliquid's leaderboard row
|
|
101
|
+
* count (~46,600 rows; quoting that once overstated our coverage by 73%). The engine's exit-3
|
|
102
|
+
* refusal carries only `indexed` (true/false/unknown), never a count, so the runtime owns this
|
|
103
|
+
* figure and the skill hardcodes nothing. `staleAfterDays` bounds how long the exact count may be
|
|
104
|
+
* quoted after `asOf`; past it, `quoteAs` is the fallback phrasing and must never round upward.
|
|
105
|
+
*/
|
|
106
|
+
export const SENPI_INDEX_COVERAGE = { indexedWallets: 26_188, asOf: "2026-09-21", staleAfterDays: 7, quoteAs: "over 25,000" };
|
|
107
|
+
/**
|
|
108
|
+
* The index count to print: exact while fresh, the never-rounded-up fallback once stale (the
|
|
109
|
+
* skill's old rule). An index keeps growing after `asOf`, so an old exact figure is still a floor —
|
|
110
|
+
* but a stale "26,188" reads as current precision we no longer have, hence the round phrasing.
|
|
111
|
+
*
|
|
112
|
+
* @param nowMs Epoch milliseconds to judge staleness against (injected so callers and tests control time).
|
|
113
|
+
* @returns `indexedWallets` formatted en-US (e.g. "26,188") within `staleAfterDays` of `asOf`, else `quoteAs`.
|
|
114
|
+
*/
|
|
115
|
+
export function indexCoverageFigure(nowMs) {
|
|
116
|
+
const ageDays = (nowMs - Date.parse(`${SENPI_INDEX_COVERAGE.asOf}T00:00:00Z`)) / 86_400_000;
|
|
117
|
+
return ageDays <= SENPI_INDEX_COVERAGE.staleAfterDays
|
|
118
|
+
? SENPI_INDEX_COVERAGE.indexedWallets.toLocaleString("en-US")
|
|
119
|
+
: SENPI_INDEX_COVERAGE.quoteAs;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/quant/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -67,7 +67,7 @@ export const FALLBACK_MANIFEST = {
|
|
|
67
67
|
{ name: "senpi-improve-trades", maxMajor: 1 },
|
|
68
68
|
{ name: "senpi-market-pulse", maxMajor: 1 },
|
|
69
69
|
{ name: "senpi-smart-money", maxMajor: 1 },
|
|
70
|
-
{ name: "quant-desk", maxMajor:
|
|
70
|
+
{ name: "quant-desk", maxMajor: 2 },
|
|
71
71
|
{ name: "senpi-signals", maxMajor: 2 },
|
|
72
72
|
{ name: "senpi-trader-research", maxMajor: 1 },
|
|
73
73
|
{ name: "senpi-trade", maxMajor: 1 },
|