sigrank 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -82
- package/adapters.mjs +493 -238
- package/badges.mjs +157 -56
- package/cascade.mjs +132 -70
- package/cli.mjs +1311 -893
- package/connect.mjs +2 -2
- package/index.mjs +277 -60
- package/keystore.mjs +64 -44
- package/narrate.mjs +34 -22
- package/package.json +2 -1
- package/preflight.mjs +101 -29
- package/sign.mjs +46 -27
- package/submit.mjs +137 -71
- package/tokenpull.mjs +325 -188
- package/tools.mjs +1717 -955
- package/tui.mjs +1860 -1081
package/narrate.mjs
CHANGED
|
@@ -11,55 +11,67 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
// Safe formatters: never emit NaN/Infinity/undefined into the card.
|
|
14
|
-
const safeNum = (n) => (Number.isFinite(Number(n)) ? Number(n) : null)
|
|
14
|
+
const safeNum = (n) => (Number.isFinite(Number(n)) ? Number(n) : null);
|
|
15
15
|
const comma = (n, dec) => {
|
|
16
|
-
const v = safeNum(n)
|
|
17
|
-
return v !== null
|
|
18
|
-
|
|
16
|
+
const v = safeNum(n);
|
|
17
|
+
return v !== null
|
|
18
|
+
? v.toLocaleString("en-US", {
|
|
19
|
+
minimumFractionDigits: dec,
|
|
20
|
+
maximumFractionDigits: dec,
|
|
21
|
+
})
|
|
22
|
+
: "—";
|
|
23
|
+
};
|
|
19
24
|
const plain = (n, dec) => {
|
|
20
|
-
const v = safeNum(n)
|
|
21
|
-
return v !== null ? v.toFixed(dec) :
|
|
22
|
-
}
|
|
25
|
+
const v = safeNum(n);
|
|
26
|
+
return v !== null ? v.toFixed(dec) : "—";
|
|
27
|
+
};
|
|
23
28
|
|
|
24
29
|
/**
|
|
25
30
|
* Given a cascade result ({ velocity, leverage, dev10x, pillars, class }) and an
|
|
26
31
|
* optional subject name, return "**CLASS.** <one or two sentences>". Deterministic.
|
|
27
32
|
*/
|
|
28
|
-
export function narrate(cascade, name =
|
|
29
|
-
const klass = cascade.class || cascade.klass ||
|
|
30
|
-
const v = safeNum(cascade.velocity)
|
|
31
|
-
const l = safeNum(cascade.leverage)
|
|
33
|
+
export function narrate(cascade, name = "This operator") {
|
|
34
|
+
const klass = cascade.class || cascade.klass || "UNCLASSED";
|
|
35
|
+
const v = safeNum(cascade.velocity);
|
|
36
|
+
const l = safeNum(cascade.leverage);
|
|
32
37
|
|
|
33
38
|
// "non-compounding" = a stateless pipe: no cache commits, so the cascade can't
|
|
34
39
|
// form. cascade.mjs leaves dev10x null when cacheCreate is 0 (the cw/o term
|
|
35
40
|
// collapses), which is exactly metrics.py's non_compounding flag.
|
|
36
41
|
// Also catches zero-input sessions where velocity/leverage are null.
|
|
37
|
-
const cw = cascade.pillars ? Number(cascade.pillars.cacheCreate) : NaN
|
|
38
|
-
const nonCompounding =
|
|
42
|
+
const cw = cascade.pillars ? Number(cascade.pillars.cacheCreate) : NaN;
|
|
43
|
+
const nonCompounding =
|
|
44
|
+
cascade.dev10x == null || !(cw > 0) || v === null || l === null;
|
|
39
45
|
|
|
40
|
-
let body
|
|
46
|
+
let body;
|
|
41
47
|
if (nonCompounding) {
|
|
42
|
-
const leverageStr =
|
|
43
|
-
|
|
48
|
+
const leverageStr =
|
|
49
|
+
l !== null
|
|
50
|
+
? `Leverage ${comma(l, 1)}x comes from reuse alone.`
|
|
51
|
+
: "Leverage is undefined (no fresh input recorded).";
|
|
52
|
+
const dev10xNote =
|
|
53
|
+
cascade.dev10x == null
|
|
54
|
+
? " 10xDEV is undefined — the compounding loop has not formed yet."
|
|
55
|
+
: "";
|
|
44
56
|
body =
|
|
45
57
|
`${name} runs a stateless pipe — no cache commits, so the cascade can't form. ` +
|
|
46
|
-
`High read volume, but nothing is being built forward. ${leverageStr}${dev10xNote}
|
|
58
|
+
`High read volume, but nothing is being built forward. ${leverageStr}${dev10xNote}`;
|
|
47
59
|
} else if (v >= 1 && l >= 100) {
|
|
48
60
|
body =
|
|
49
61
|
`${name} holds both axes at once: ${plain(v, 1)}x generation AND ${comma(l, 0)}x memory leverage. ` +
|
|
50
|
-
`A closed kinetic loop — the rare operator the leverage/generation tradeoff says shouldn't exist
|
|
62
|
+
`A closed kinetic loop — the rare operator the leverage/generation tradeoff says shouldn't exist.`;
|
|
51
63
|
} else if (l >= 10 && v < 1) {
|
|
52
64
|
body =
|
|
53
65
|
`${name} is an archival sponge — ${comma(l, 0)}x reuse but only ${plain(v, 2)}x generation. ` +
|
|
54
|
-
`Holds context beautifully, executes little with it. The reuse number is inflated by a weak commitment stage
|
|
66
|
+
`Holds context beautifully, executes little with it. The reuse number is inflated by a weak commitment stage.`;
|
|
55
67
|
} else if (v >= 0.8 && l < 2) {
|
|
56
68
|
body =
|
|
57
69
|
`${name} is a volatile ingestor — ${plain(v, 2)}x generation but ${plain(l, 1)}x leverage. ` +
|
|
58
|
-
`Fast on single shots, resets between turns. Memory doesn't persist into a compounding loop
|
|
70
|
+
`Fast on single shots, resets between turns. Memory doesn't persist into a compounding loop.`;
|
|
59
71
|
} else {
|
|
60
72
|
body =
|
|
61
73
|
`${name} sits low on both axes: ${plain(v, 2)}x generation, ${plain(l, 1)}x leverage. ` +
|
|
62
|
-
`A transient profile — neither building state nor converting input to output efficiently
|
|
74
|
+
`A transient profile — neither building state nor converting input to output efficiently.`;
|
|
63
75
|
}
|
|
64
|
-
return `**${klass}.** ${body}
|
|
76
|
+
return `**${klass}.** ${body}`;
|
|
65
77
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigrank",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
|
+
"author": "SunrisesIllNeverSee (https://github.com/SunrisesIllNeverSee)",
|
|
4
5
|
"description": "SigRank MCP server — the yield cascade + live leaderboard as MCP tools any agent can call",
|
|
5
6
|
"mcpName": "io.github.SunrisesIllNeverSee/sigrank-mcp",
|
|
6
7
|
"type": "module",
|
package/preflight.mjs
CHANGED
|
@@ -28,63 +28,129 @@ const GATE_LIMITS = {
|
|
|
28
28
|
MIN_CACHE_WRITE_RATIO: 0.5,
|
|
29
29
|
MIN_INPUT_SHARE_FRAC: 0.0003,
|
|
30
30
|
MAX_CADENCE_PER_MIN: 15,
|
|
31
|
-
}
|
|
31
|
+
};
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Run plausibility checks against a payload's raw_telemetry.
|
|
35
35
|
* Returns array of issues: { severity, code, detail }
|
|
36
36
|
*/
|
|
37
37
|
export function plausibilityCheck(rt, window) {
|
|
38
|
-
const out = []
|
|
39
|
-
const pillars =
|
|
38
|
+
const out = [];
|
|
39
|
+
const pillars =
|
|
40
|
+
rt.tokens_input_fresh +
|
|
41
|
+
rt.tokens_output +
|
|
42
|
+
rt.tokens_cache_read +
|
|
43
|
+
rt.tokens_cache_creation;
|
|
40
44
|
|
|
41
45
|
if (rt.tokens_total > 0) {
|
|
42
|
-
const tol = Math.max(
|
|
46
|
+
const tol = Math.max(
|
|
47
|
+
1,
|
|
48
|
+
rt.tokens_total * GATE_LIMITS.TOTALS_TOLERANCE_FRAC,
|
|
49
|
+
);
|
|
43
50
|
if (Math.abs(pillars - rt.tokens_total) > tol) {
|
|
44
|
-
out.push({
|
|
51
|
+
out.push({
|
|
52
|
+
severity: "reject",
|
|
53
|
+
code: "totals_inconsistent",
|
|
54
|
+
detail: `Σ4 pillars (${pillars}) ≠ tokens_total (${rt.tokens_total})`,
|
|
55
|
+
});
|
|
45
56
|
}
|
|
46
57
|
if (rt.sessions_count === 0) {
|
|
47
|
-
out.push({
|
|
58
|
+
out.push({
|
|
59
|
+
severity: "reject",
|
|
60
|
+
code: "tokens_without_sessions",
|
|
61
|
+
detail: `tokens_total ${rt.tokens_total} with sessions_count 0`,
|
|
62
|
+
});
|
|
48
63
|
}
|
|
49
64
|
}
|
|
50
65
|
if (rt.turns_total < rt.sessions_count) {
|
|
51
|
-
out.push({
|
|
66
|
+
out.push({
|
|
67
|
+
severity: "reject",
|
|
68
|
+
code: "turns_lt_sessions",
|
|
69
|
+
detail: `turns_total ${rt.turns_total} < sessions_count ${rt.sessions_count}`,
|
|
70
|
+
});
|
|
52
71
|
}
|
|
53
72
|
if (rt.tokens_output > 0 && rt.turns_total === 0) {
|
|
54
|
-
out.push({
|
|
73
|
+
out.push({
|
|
74
|
+
severity: "reject",
|
|
75
|
+
code: "output_without_turns",
|
|
76
|
+
detail: `tokens_output ${rt.tokens_output} with turns_total 0`,
|
|
77
|
+
});
|
|
55
78
|
}
|
|
56
79
|
|
|
57
80
|
if (window) {
|
|
58
|
-
const spanMin =
|
|
81
|
+
const spanMin =
|
|
82
|
+
(Date.parse(window.end) - Date.parse(window.start)) / 60_000;
|
|
59
83
|
if (Number.isFinite(spanMin) && rt.active_minutes_est > spanMin + 1) {
|
|
60
|
-
out.push({
|
|
84
|
+
out.push({
|
|
85
|
+
severity: "flag",
|
|
86
|
+
code: "active_exceeds_window",
|
|
87
|
+
detail: `active_minutes_est ${rt.active_minutes_est} > window span ${Math.round(spanMin)}m`,
|
|
88
|
+
});
|
|
61
89
|
}
|
|
62
90
|
}
|
|
63
91
|
|
|
64
|
-
const outPerMin = rt.tokens_output / Math.max(rt.active_minutes_est, 1)
|
|
92
|
+
const outPerMin = rt.tokens_output / Math.max(rt.active_minutes_est, 1);
|
|
65
93
|
if (outPerMin > GATE_LIMITS.MAX_OUTPUT_TOKENS_PER_MIN) {
|
|
66
|
-
out.push({
|
|
94
|
+
out.push({
|
|
95
|
+
severity: "flag",
|
|
96
|
+
code: "implausible_output_rate",
|
|
97
|
+
detail: `${Math.round(outPerMin)} output tok/min > ${GATE_LIMITS.MAX_OUTPUT_TOKENS_PER_MIN}`,
|
|
98
|
+
});
|
|
67
99
|
}
|
|
68
100
|
|
|
69
101
|
// Cross-field ratio checks (defense-in-depth — also in the plausibility gate)
|
|
70
102
|
// Bounds tightened (deviewreview3): original 100:1 + 50/min were too loose.
|
|
71
103
|
if (rt.tokens_cache_read > 1_000 && rt.tokens_cache_creation === 0) {
|
|
72
|
-
out.push({
|
|
104
|
+
out.push({
|
|
105
|
+
severity: "flag",
|
|
106
|
+
code: "cache_without_creation",
|
|
107
|
+
detail: `${rt.tokens_cache_read} cache_read with 0 cache_creation (impossible cascade)`,
|
|
108
|
+
});
|
|
73
109
|
}
|
|
74
|
-
if (
|
|
75
|
-
|
|
110
|
+
if (
|
|
111
|
+
rt.tokens_cache_creation > 0 &&
|
|
112
|
+
rt.tokens_cache_read / rt.tokens_cache_creation >
|
|
113
|
+
GATE_LIMITS.MAX_CACHE_REUSE_RATIO
|
|
114
|
+
) {
|
|
115
|
+
out.push({
|
|
116
|
+
severity: "flag",
|
|
117
|
+
code: "extreme_cache_ratio",
|
|
118
|
+
detail: `cache_read/cache_creation = ${(rt.tokens_cache_read / rt.tokens_cache_creation).toFixed(1)}:1 (real max ~30:1)`,
|
|
119
|
+
});
|
|
76
120
|
}
|
|
77
|
-
if (
|
|
78
|
-
|
|
121
|
+
if (
|
|
122
|
+
rt.tokens_output > 1_000 &&
|
|
123
|
+
rt.tokens_cache_creation / rt.tokens_output <
|
|
124
|
+
GATE_LIMITS.MIN_CACHE_WRITE_RATIO
|
|
125
|
+
) {
|
|
126
|
+
out.push({
|
|
127
|
+
severity: "flag",
|
|
128
|
+
code: "low_cache_write_ratio",
|
|
129
|
+
detail: `cache_creation/output = ${(rt.tokens_cache_creation / rt.tokens_output).toFixed(2)}:1 (real min ~1.5:1)`,
|
|
130
|
+
});
|
|
79
131
|
}
|
|
80
|
-
if (
|
|
81
|
-
|
|
132
|
+
if (
|
|
133
|
+
pillars > 10_000 &&
|
|
134
|
+
rt.tokens_input_fresh / pillars < GATE_LIMITS.MIN_INPUT_SHARE_FRAC
|
|
135
|
+
) {
|
|
136
|
+
out.push({
|
|
137
|
+
severity: "flag",
|
|
138
|
+
code: "implausible_input_share",
|
|
139
|
+
detail: `input is ${((rt.tokens_input_fresh / pillars) * 100).toFixed(3)}% of total (real min ~0.3%)`,
|
|
140
|
+
});
|
|
82
141
|
}
|
|
83
|
-
if (
|
|
84
|
-
|
|
142
|
+
if (
|
|
143
|
+
rt.active_minutes_est > 0 &&
|
|
144
|
+
rt.turns_total / rt.active_minutes_est > GATE_LIMITS.MAX_CADENCE_PER_MIN
|
|
145
|
+
) {
|
|
146
|
+
out.push({
|
|
147
|
+
severity: "flag",
|
|
148
|
+
code: "implausible_cadence",
|
|
149
|
+
detail: `${(rt.turns_total / rt.active_minutes_est).toFixed(1)} turns/min (real: 0.5-10)`,
|
|
150
|
+
});
|
|
85
151
|
}
|
|
86
152
|
|
|
87
|
-
return out
|
|
153
|
+
return out;
|
|
88
154
|
}
|
|
89
155
|
|
|
90
156
|
// ── full preflight (plausibility only — NO battery) ─────────────────────────
|
|
@@ -100,10 +166,10 @@ export function plausibilityCheck(rt, window) {
|
|
|
100
166
|
* "guaranteed to pass all server gates."
|
|
101
167
|
*/
|
|
102
168
|
export function preflight(payload) {
|
|
103
|
-
const issues = plausibilityCheck(payload.raw_telemetry, payload.window)
|
|
169
|
+
const issues = plausibilityCheck(payload.raw_telemetry, payload.window);
|
|
104
170
|
|
|
105
|
-
const hasReject = issues.some((i) => i.severity ===
|
|
106
|
-
const hasFlag = issues.some((i) => i.severity ===
|
|
171
|
+
const hasReject = issues.some((i) => i.severity === "reject");
|
|
172
|
+
const hasFlag = issues.some((i) => i.severity === "flag");
|
|
107
173
|
|
|
108
174
|
return {
|
|
109
175
|
pass: issues.length === 0,
|
|
@@ -111,9 +177,15 @@ export function preflight(payload) {
|
|
|
111
177
|
wouldDowngrade: hasFlag,
|
|
112
178
|
issues,
|
|
113
179
|
summary: hasReject
|
|
114
|
-
? `REJECT: ${issues
|
|
180
|
+
? `REJECT: ${issues
|
|
181
|
+
.filter((i) => i.severity === "reject")
|
|
182
|
+
.map((i) => i.code)
|
|
183
|
+
.join(", ")}`
|
|
115
184
|
: hasFlag
|
|
116
|
-
? `FLAG (verified → flagged, not ranked): ${issues
|
|
117
|
-
|
|
118
|
-
|
|
185
|
+
? `FLAG (verified → flagged, not ranked): ${issues
|
|
186
|
+
.filter((i) => i.severity === "flag")
|
|
187
|
+
.map((i) => i.code)
|
|
188
|
+
.join(", ")}`
|
|
189
|
+
: "clean — passes plausibility gate (server runs additional proprietary checks)",
|
|
190
|
+
};
|
|
119
191
|
}
|
package/sign.mjs
CHANGED
|
@@ -12,54 +12,64 @@
|
|
|
12
12
|
// The canon-parity fixture (tests/fixtures/canon_parity.json) + sign.test.mjs + the
|
|
13
13
|
// sigrank-app canon_parity gate together lock this byte-for-byte.
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
createHash,
|
|
17
|
+
createPrivateKey,
|
|
18
|
+
createPublicKey,
|
|
19
|
+
sign as edSign,
|
|
20
|
+
verify as edVerify,
|
|
21
|
+
} from "node:crypto";
|
|
16
22
|
|
|
17
23
|
/** Agent fields excluded from the canonical (signed/hashed) body — they derive from it. */
|
|
18
|
-
const DERIVED_AGENT_FIELDS = [
|
|
24
|
+
const DERIVED_AGENT_FIELDS = ["signature", "snapshot_hash"];
|
|
19
25
|
|
|
20
26
|
/** Recursively sort object keys (matches the server's Object.keys(...).sort()); arrays keep order. */
|
|
21
27
|
function sortDeep(v) {
|
|
22
|
-
if (Array.isArray(v)) return v.map(sortDeep)
|
|
23
|
-
if (v && typeof v ===
|
|
24
|
-
const out = {}
|
|
25
|
-
for (const k of Object.keys(v).sort()) out[k] = sortDeep(v[k])
|
|
26
|
-
return out
|
|
28
|
+
if (Array.isArray(v)) return v.map(sortDeep);
|
|
29
|
+
if (v && typeof v === "object") {
|
|
30
|
+
const out = {};
|
|
31
|
+
for (const k of Object.keys(v).sort()) out[k] = sortDeep(v[k]);
|
|
32
|
+
return out;
|
|
27
33
|
}
|
|
28
|
-
return v
|
|
34
|
+
return v;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
/** Canonical JSON string the agent signs/hashes (derived agent fields stripped). */
|
|
32
38
|
export function canonicalJson(payload) {
|
|
33
|
-
const clone = JSON.parse(JSON.stringify(payload))
|
|
34
|
-
const agent = clone.agent
|
|
35
|
-
if (agent && typeof agent ===
|
|
36
|
-
for (const f of DERIVED_AGENT_FIELDS) delete agent[f]
|
|
39
|
+
const clone = JSON.parse(JSON.stringify(payload));
|
|
40
|
+
const agent = clone.agent;
|
|
41
|
+
if (agent && typeof agent === "object") {
|
|
42
|
+
for (const f of DERIVED_AGENT_FIELDS) delete agent[f];
|
|
37
43
|
}
|
|
38
|
-
return JSON.stringify(sortDeep(clone))
|
|
44
|
+
return JSON.stringify(sortDeep(clone));
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
/** UTF-8 bytes of the canonical JSON — the exact bytes that get signed. */
|
|
42
48
|
export function canonicalBytes(payload) {
|
|
43
|
-
return Buffer.from(canonicalJson(payload),
|
|
49
|
+
return Buffer.from(canonicalJson(payload), "utf-8");
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
/** "sha256:<hex>" over the canonical bytes (matches the server snapshotHash). */
|
|
47
53
|
export function snapshotHash(payload) {
|
|
48
|
-
return `sha256:${createHash(
|
|
54
|
+
return `sha256:${createHash("sha256").update(canonicalBytes(payload)).digest("hex")}`;
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
/** SPKI DER prefix for a raw 32-byte ed25519 public key. */
|
|
52
|
-
const ED25519_SPKI_PREFIX = Buffer.from(
|
|
58
|
+
const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
53
59
|
|
|
54
60
|
/** Build a node KeyObject from an "ed25519:<base64>" (or bare base64) public key, or null. */
|
|
55
61
|
function publicKeyFrom(pk) {
|
|
56
62
|
try {
|
|
57
|
-
const body = pk.startsWith(
|
|
58
|
-
const raw = Buffer.from(body,
|
|
59
|
-
if (raw.length !== 32) return null
|
|
60
|
-
return createPublicKey({
|
|
63
|
+
const body = pk.startsWith("ed25519:") ? pk.slice("ed25519:".length) : pk;
|
|
64
|
+
const raw = Buffer.from(body, "base64");
|
|
65
|
+
if (raw.length !== 32) return null;
|
|
66
|
+
return createPublicKey({
|
|
67
|
+
key: Buffer.concat([ED25519_SPKI_PREFIX, raw]),
|
|
68
|
+
format: "der",
|
|
69
|
+
type: "spki",
|
|
70
|
+
});
|
|
61
71
|
} catch {
|
|
62
|
-
return null
|
|
72
|
+
return null;
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
|
|
@@ -68,8 +78,12 @@ function publicKeyFrom(pk) {
|
|
|
68
78
|
* The returned string is the X-Agent-Signature header value.
|
|
69
79
|
*/
|
|
70
80
|
export function signPayload(payload, privateKeyPkcs8B64) {
|
|
71
|
-
const key = createPrivateKey({
|
|
72
|
-
|
|
81
|
+
const key = createPrivateKey({
|
|
82
|
+
key: Buffer.from(privateKeyPkcs8B64, "base64"),
|
|
83
|
+
format: "der",
|
|
84
|
+
type: "pkcs8",
|
|
85
|
+
});
|
|
86
|
+
return edSign(null, canonicalBytes(payload), key).toString("base64");
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
/**
|
|
@@ -77,11 +91,16 @@ export function signPayload(payload, privateKeyPkcs8B64) {
|
|
|
77
91
|
* and as a pre-send sanity check. Returns false on any malformation, never throws.
|
|
78
92
|
*/
|
|
79
93
|
export function verifyPayload(payload, signatureB64, publicKey) {
|
|
80
|
-
const key = publicKeyFrom(publicKey)
|
|
81
|
-
if (!key) return false
|
|
94
|
+
const key = publicKeyFrom(publicKey);
|
|
95
|
+
if (!key) return false;
|
|
82
96
|
try {
|
|
83
|
-
return edVerify(
|
|
97
|
+
return edVerify(
|
|
98
|
+
null,
|
|
99
|
+
canonicalBytes(payload),
|
|
100
|
+
key,
|
|
101
|
+
Buffer.from(signatureB64, "base64"),
|
|
102
|
+
);
|
|
84
103
|
} catch {
|
|
85
|
-
return false
|
|
104
|
+
return false;
|
|
86
105
|
}
|
|
87
106
|
}
|