@senpi/runtime 3.0.56 → 3.0.58

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 (99) hide show
  1. package/dist/cli/guide-commands.d.ts.map +1 -1
  2. package/dist/cli/guide-commands.js +28 -2
  3. package/dist/cli/guide-commands.js.map +1 -1
  4. package/dist/cli/senpi-commands.d.ts.map +1 -1
  5. package/dist/cli/senpi-commands.js +2 -0
  6. package/dist/cli/senpi-commands.js.map +1 -1
  7. package/dist/cli/validate-command.d.ts +29 -0
  8. package/dist/cli/validate-command.d.ts.map +1 -0
  9. package/dist/cli/validate-command.js +89 -0
  10. package/dist/cli/validate-command.js.map +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +62 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/runtime/load-runtime-config.d.ts +38 -0
  15. package/dist/runtime/load-runtime-config.d.ts.map +1 -1
  16. package/dist/runtime/load-runtime-config.js +33 -25
  17. package/dist/runtime/load-runtime-config.js.map +1 -1
  18. package/dist/runtime/runtime-schema.d.ts +100 -100
  19. package/dist/scanners/external/scaffold/__main__.py +8 -0
  20. package/dist/scanners/external/scaffold/ctx.py +15 -1
  21. package/dist/scanners/external/scaffold/http_post.py +30 -0
  22. package/dist/scanners/external/scaffold/import_check.py +310 -0
  23. package/dist/scanners/external/scaffold/preflight.py +545 -0
  24. package/dist/scanners/external/scaffold/scaffold.py +19 -6
  25. package/dist/scanners/implementations/market-regime.d.ts +1 -1
  26. package/dist/telemetry/metrics.d.ts +6 -0
  27. package/dist/telemetry/metrics.d.ts.map +1 -1
  28. package/dist/telemetry/metrics.js +45 -0
  29. package/dist/telemetry/metrics.js.map +1 -1
  30. package/dist/utils/event-catalog.d.ts +40 -0
  31. package/dist/utils/event-catalog.d.ts.map +1 -1
  32. package/dist/utils/event-catalog.js +12 -0
  33. package/dist/utils/event-catalog.js.map +1 -1
  34. package/dist/validate/codes.d.ts +165 -0
  35. package/dist/validate/codes.d.ts.map +1 -0
  36. package/dist/validate/codes.js +381 -0
  37. package/dist/validate/codes.js.map +1 -0
  38. package/dist/validate/coverage.d.ts +34 -0
  39. package/dist/validate/coverage.d.ts.map +1 -0
  40. package/dist/validate/coverage.js +34 -0
  41. package/dist/validate/coverage.js.map +1 -0
  42. package/dist/validate/import-stage.d.ts +56 -0
  43. package/dist/validate/import-stage.d.ts.map +1 -0
  44. package/dist/validate/import-stage.js +244 -0
  45. package/dist/validate/import-stage.js.map +1 -0
  46. package/dist/validate/index.d.ts +28 -0
  47. package/dist/validate/index.d.ts.map +1 -0
  48. package/dist/validate/index.js +27 -0
  49. package/dist/validate/index.js.map +1 -0
  50. package/dist/validate/install-gate.d.ts +44 -0
  51. package/dist/validate/install-gate.d.ts.map +1 -0
  52. package/dist/validate/install-gate.js +66 -0
  53. package/dist/validate/install-gate.js.map +1 -0
  54. package/dist/validate/live-stage.d.ts +134 -0
  55. package/dist/validate/live-stage.d.ts.map +1 -0
  56. package/dist/validate/live-stage.js +472 -0
  57. package/dist/validate/live-stage.js.map +1 -0
  58. package/dist/validate/proof.d.ts +104 -0
  59. package/dist/validate/proof.d.ts.map +1 -0
  60. package/dist/validate/proof.js +269 -0
  61. package/dist/validate/proof.js.map +1 -0
  62. package/dist/validate/recipe-checks.d.ts +79 -0
  63. package/dist/validate/recipe-checks.d.ts.map +1 -0
  64. package/dist/validate/recipe-checks.js +205 -0
  65. package/dist/validate/recipe-checks.js.map +1 -0
  66. package/dist/validate/recipe.d.ts +41 -0
  67. package/dist/validate/recipe.d.ts.map +1 -0
  68. package/dist/validate/recipe.js +215 -0
  69. package/dist/validate/recipe.js.map +1 -0
  70. package/dist/validate/report.d.ts +35 -0
  71. package/dist/validate/report.d.ts.map +1 -0
  72. package/dist/validate/report.js +80 -0
  73. package/dist/validate/report.js.map +1 -0
  74. package/dist/validate/run.d.ts +101 -0
  75. package/dist/validate/run.d.ts.map +1 -0
  76. package/dist/validate/run.js +471 -0
  77. package/dist/validate/run.js.map +1 -0
  78. package/dist/validate/signal-stage.d.ts +40 -0
  79. package/dist/validate/signal-stage.d.ts.map +1 -0
  80. package/dist/validate/signal-stage.js +116 -0
  81. package/dist/validate/signal-stage.js.map +1 -0
  82. package/dist/validate/target.d.ts +70 -0
  83. package/dist/validate/target.d.ts.map +1 -0
  84. package/dist/validate/target.js +231 -0
  85. package/dist/validate/target.js.map +1 -0
  86. package/dist/validate/telemetry.d.ts +24 -0
  87. package/dist/validate/telemetry.d.ts.map +1 -0
  88. package/dist/validate/telemetry.js +60 -0
  89. package/dist/validate/telemetry.js.map +1 -0
  90. package/dist/validate/types.d.ts +115 -0
  91. package/dist/validate/types.d.ts.map +1 -0
  92. package/dist/validate/types.js +31 -0
  93. package/dist/validate/types.js.map +1 -0
  94. package/dist/validate/wallet.d.ts +63 -0
  95. package/dist/validate/wallet.d.ts.map +1 -0
  96. package/dist/validate/wallet.js +66 -0
  97. package/dist/validate/wallet.js.map +1 -0
  98. package/openclaw.plugin.json +1 -1
  99. package/package.json +2 -1
@@ -0,0 +1,244 @@
1
+ /**
2
+ * The import depth: run the loadability checks and turn what they find into findings.
3
+ *
4
+ * The checks themselves live in Python, next to the loader the scaffold uses before its own first
5
+ * tick — so a package that passes has satisfied the contract production applies. This side spawns
6
+ * that checker, one process per scanner, and translates its output.
7
+ *
8
+ * ## Why a process per scanner
9
+ *
10
+ * Importing puts the scanner's directory at the front of the module search path, and two scanners
11
+ * routinely contain files of the same name. Checking several in one interpreter lets one scanner's
12
+ * `scoring` satisfy another's import of a file that does not exist — a broken package reporting
13
+ * clean. Production spawns a process per scanner for the same reason, so this matches it.
14
+ */
15
+ // Via the repo's wrapper, never the stdlib module directly: OpenClaw's install gate
16
+ // blocks any plugin file matching BOTH a spawn-like call AND the literal module name,
17
+ // and `safe-spawn` exists to keep those bytes out of a file. Importing it directly here
18
+ // shipped in 3.0.57, whose published dist the gate refuses outright — a hard failure,
19
+ // not a warning, because senpi-agent's bootstrap deliberately stopped passing
20
+ // --dangerously-force-unsafe-install so that exactly this regression would be loud.
21
+ import { spawn } from "../utils/safe-spawn.js";
22
+ import { isAbsolute, resolve } from "node:path";
23
+ import { fileURLToPath } from "node:url";
24
+ import { VALIDATE_CODE } from "./codes.js";
25
+ /**
26
+ * Loading must terminate. Importing a module runs its body, so a `while True:` or a blocking
27
+ * connection at module level — exactly the class of defect this depth exists to find — would
28
+ * otherwise hang the command looking for it. Generous, because a large package legitimately takes
29
+ * a moment; the point is that it ends.
30
+ */
31
+ const IMPORT_BUDGET_MS = 60_000;
32
+ /** The directory the `scaffold` package is importable from, in dev and in the built package alike. */
33
+ export function scaffoldPackageDir() {
34
+ return fileURLToPath(new URL("../scanners/external", import.meta.url));
35
+ }
36
+ function importFinding(code, fixKind, parts) {
37
+ const severity = code.startsWith("W_") ? "warning" : "error";
38
+ return { code, severity, stage: "import", actor: "agent", fix_kind: fixKind, ...parts };
39
+ }
40
+ /**
41
+ * Turn one raw result into findings.
42
+ *
43
+ * The Python side reports *what it observed*; the reasons and remedies are composed here, so all
44
+ * message policy lives in one language rather than being split across the boundary.
45
+ */
46
+ export function toFindings(scanner, raw) {
47
+ const at = (file, line) => file === undefined ? undefined : { file, ...(line != null ? { line } : {}) };
48
+ switch (raw.code) {
49
+ case "SYNTAX":
50
+ return [
51
+ importFinding(VALIDATE_CODE.syntax, "edit", {
52
+ what: `${raw.file} does not compile: ${raw.message}`,
53
+ why: "Every file under the scanner directory is compiled, not only the ones the entrypoint " +
54
+ "imports — a broken helper behind a condition would otherwise stay invisible until that " +
55
+ "condition is true, in production.",
56
+ fix: `Correct the syntax at ${raw.file}${raw.line != null ? `:${raw.line}` : ""}.`,
57
+ where: at(raw.file, raw.line),
58
+ }),
59
+ ];
60
+ case "IMPORT_FAILED": {
61
+ const chain = raw.import_chain ?? [];
62
+ const trail = chain.length > 1 ? `${chain.join(" → ")}: ` : "";
63
+ return [
64
+ importFinding(VALIDATE_CODE.importFailed, "edit", {
65
+ what: `${trail}${raw.error_type}: ${raw.message}`,
66
+ why: "This runs while the module is being imported, which the runtime does before the first " +
67
+ "tick. The scanner would die on launch and never produce a signal.",
68
+ fix: raw.where !== null && raw.where !== undefined
69
+ ? `Fix ${raw.where.file}:${raw.where.line} — that is where the import chain actually broke, not ${scanner.entrypoint ?? "the entrypoint"}.`
70
+ : `Resolve the failed import in ${scanner.entrypoint ?? "the entrypoint"}.`,
71
+ where: raw.where ?? undefined,
72
+ evidence: { traceback: raw.traceback, import_chain: chain },
73
+ }),
74
+ ];
75
+ }
76
+ case "SCAN_MISSING":
77
+ return [
78
+ importFinding(VALIDATE_CODE.scanMissing, "edit", {
79
+ what: `${raw.file} imports, but defines no \`scan\` function`,
80
+ why: "The runtime looks up exactly this name. A module that imports cleanly with the entry " +
81
+ "function named anything else passes every check that does not run it, and then does " +
82
+ "nothing forever.",
83
+ fix: "Rename the entry function to `scan`, taking exactly two parameters: `def scan(inputs, ctx)`.",
84
+ where: at(raw.file),
85
+ }),
86
+ ];
87
+ case "SCAN_NOT_CALLABLE":
88
+ return [
89
+ importFinding(VALIDATE_CODE.scanNotCallable, "edit", {
90
+ what: `\`scan\` exists in ${raw.file} but is not callable: ${raw.message}`,
91
+ why: "The runtime calls it every interval; a value cannot be called.",
92
+ fix: "Define `scan` as a function: `def scan(inputs, ctx)`.",
93
+ where: at(raw.file),
94
+ }),
95
+ ];
96
+ case "SCAN_BAD_ARITY":
97
+ return [
98
+ importFinding(VALIDATE_CODE.scanBadArity, "edit", {
99
+ what: raw.message ?? "scan has the wrong number of parameters",
100
+ why: "The runtime calls `scan(inputs, ctx)` with exactly two arguments. Any other required shape raises on the first tick.",
101
+ fix: "Change the signature to `def scan(inputs, ctx)`. Extra parameters are allowed only with defaults.",
102
+ where: at(raw.file),
103
+ }),
104
+ ];
105
+ case "SCANNERS_IS_PACKAGE":
106
+ return [
107
+ importFinding(VALIDATE_CODE.scannersIsPackage, "edit", {
108
+ what: `${scanner.root} contains __init__.py`,
109
+ why: "The runtime imports these files as loose top-level modules, never as a package. The " +
110
+ "marker suggests relative imports are available; they resolve in an editor and fail at " +
111
+ "run time.",
112
+ fix: "Delete __init__.py and import siblings by plain name — `import scoring`, not `from .scoring import …`.",
113
+ where: at(raw.file),
114
+ }),
115
+ ];
116
+ case "SHADOWS_STDLIB":
117
+ return [
118
+ importFinding(VALIDATE_CODE.shadowsStdlib, "edit", {
119
+ what: `${raw.file} shadows the standard-library module \`${raw.module}\``,
120
+ why: "The scanner directory goes to the front of the module search path, so everything " +
121
+ "imported afterwards — including the runtime's own dependencies — resolves to this file " +
122
+ "instead of the real module.",
123
+ fix: `Rename ${raw.file} to something outside the standard library, and update its importers.`,
124
+ where: at(raw.file),
125
+ }),
126
+ ];
127
+ case "NON_MCP_SOURCE":
128
+ return [
129
+ importFinding(VALIDATE_CODE.nonMcpSource, "edit", {
130
+ what: `${raw.file} imports \`${raw.module}\``,
131
+ why: "Data fetched outside the sanctioned client is invisible to the runtime, and to the " +
132
+ "check that decides whether a tick read anything at all — a scanner sourcing this way " +
133
+ "cannot be proven to work.",
134
+ fix: "Fetch through the context's data client so reads are observable.",
135
+ where: at(raw.file, raw.line),
136
+ }),
137
+ ];
138
+ default:
139
+ // A code the Python side emits that this side does not know is a wiring mistake between the
140
+ // two, and silently dropping it would hide a check that is running but never reported.
141
+ return [
142
+ importFinding(VALIDATE_CODE.importFailed, "locate-only", {
143
+ what: `The loadability checker reported an unrecognised result: ${raw.code}`,
144
+ why: "This is a defect in the validator itself rather than in the strategy.",
145
+ fix: "Report this — the checker and its reader are out of step.",
146
+ evidence: { raw: raw },
147
+ }),
148
+ ];
149
+ }
150
+ }
151
+ /** Run the Python checker for one scanner. */
152
+ export async function checkScanner(scanner, options = {}) {
153
+ const python = options.pythonBin ?? process.env.PYTHON ?? "python3";
154
+ const budgetMs = options.budgetMs ?? IMPORT_BUDGET_MS;
155
+ const args = ["-m", "scaffold.import_check", scanner.root];
156
+ if (scanner.entrypoint !== undefined)
157
+ args.push(scanner.entrypoint);
158
+ const existing = process.env.PYTHONPATH;
159
+ const pythonPath = existing ? `${scaffoldPackageDir()}:${existing}` : scaffoldPackageDir();
160
+ const raw = await new Promise((done) => {
161
+ const child = spawn(python, args, {
162
+ cwd: options.cwd,
163
+ env: { ...process.env, PYTHONPATH: pythonPath },
164
+ stdio: ["ignore", "pipe", "pipe"],
165
+ });
166
+ let stdout = "";
167
+ let stderr = "";
168
+ let settled = false;
169
+ const finish = (r) => {
170
+ if (settled)
171
+ return;
172
+ settled = true;
173
+ clearTimeout(timer);
174
+ done(r);
175
+ };
176
+ // Importing a module executes its body, and a `while True:` or a blocking connection at module
177
+ // level is exactly the broken code this depth exists to find — so it must not be able to hang
178
+ // the command that is looking for it. Generous, because a large package legitimately takes a
179
+ // moment; the point is that it terminates.
180
+ const timer = setTimeout(() => {
181
+ child.kill("SIGKILL");
182
+ finish({ code: null, stdout, stderr, timedOut: true });
183
+ }, budgetMs);
184
+ child.stdout.on("data", (chunk) => (stdout += String(chunk)));
185
+ child.stderr.on("data", (chunk) => (stderr += String(chunk)));
186
+ child.on("error", (err) => finish({ code: null, stdout, stderr: String(err) }));
187
+ child.on("close", (code) => finish({ code, stdout, stderr }));
188
+ });
189
+ if (raw.timedOut) {
190
+ return [
191
+ {
192
+ code: VALIDATE_CODE.importTimeout,
193
+ severity: "error",
194
+ stage: "import",
195
+ actor: "agent",
196
+ fix_kind: "locate-only",
197
+ what: `Loading scanner '${scanner.name}' did not finish within ${budgetMs / 1000}s`,
198
+ why: "Importing a module runs its body. Work at module level — a loop, a blocking connection, a long computation — runs on every launch in production too, before the scanner has ticked once.",
199
+ fix: "Move work out of the module body and into scan(), where the tick budget governs it.",
200
+ evidence: { stdout: raw.stdout.slice(-2000), stderr: raw.stderr.slice(-2000) },
201
+ },
202
+ ];
203
+ }
204
+ let parsed;
205
+ try {
206
+ parsed = JSON.parse(raw.stdout);
207
+ }
208
+ catch {
209
+ // Reaching here means the interpreter could not be started, or died before writing. That is an
210
+ // environment fault: an agent responding by editing the strategy would be editing working code.
211
+ return [
212
+ {
213
+ code: VALIDATE_CODE.importHarnessFailed,
214
+ severity: "error",
215
+ stage: "import",
216
+ actor: "ops",
217
+ fix_kind: "command",
218
+ what: `The loadability checker could not be run for scanner '${scanner.name}'`,
219
+ why: "Checking whether the code loads needs a Python interpreter that can import the runtime's scaffold package.",
220
+ fix: `Ensure \`${python}\` is on PATH. This is an environment problem, not a problem with the strategy.`,
221
+ evidence: { exit_code: raw.code, stderr: raw.stderr.slice(0, 2000) },
222
+ },
223
+ ];
224
+ }
225
+ if (!parsed.ok) {
226
+ return [
227
+ importFinding(VALIDATE_CODE.importFailed, "locate-only", {
228
+ what: parsed.error ?? "the loadability checker could not run",
229
+ why: "Nothing about this scanner's code could be established.",
230
+ fix: `Check that ${scanner.root} exists and is readable.`,
231
+ }),
232
+ ];
233
+ }
234
+ return (parsed.findings ?? []).flatMap((finding) => toFindings(scanner, finding));
235
+ }
236
+ /** Absolute scanner root for a declared path, resolved against the recipe's directory. */
237
+ export function resolveRoot(declared, baseDir) {
238
+ if (isAbsolute(declared))
239
+ return declared;
240
+ if (baseDir === undefined)
241
+ return undefined;
242
+ return resolve(baseDir, declared);
243
+ }
244
+ //# sourceMappingURL=import-stage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-stage.js","sourceRoot":"","sources":["../../src/validate/import-stage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `senpi validate` — proving a strategy can actually run, before it is funded.
3
+ *
4
+ * This layer is the vocabulary: the finding shape every check speaks, the closed set of codes, and
5
+ * the accounting that keeps the two honest. Checks themselves live in sibling modules.
6
+ */
7
+ export type { Actor, Depth, Finding, FindingLocation, FixKind, Severity, ValidationStage, } from "./types.js";
8
+ export { DEPTH_ORDER, depthsUpTo } from "./types.js";
9
+ export { resolveTarget, type ResolvedTarget, type TargetInput, type TargetKind, type TargetResolution, } from "./target.js";
10
+ export { ALL_VALIDATE_CODES, VALIDATE_CODE, VALIDATE_CODE_META, codesForStage, severityOf, type ValidateCode, type ValidateCodeMeta, } from "./codes.js";
11
+ export { findUncoveredCodes, findUnknownCodes, type CoverageOptions } from "./coverage.js";
12
+ export { findLlmActionsWithoutModel, findMarginPctFraction, findMissingEntrypoints, findUngatedStrategy, findUnsizedStrategy, } from "./recipe-checks.js";
13
+ export { collectRecipeFindings, type RecipeSource, type RecipeValidation, } from "./recipe.js";
14
+ export { isImplemented, runValidation, type ExitCode, type ValidateOptions, type ValidateResult, type Verdict, type VerdictBasis, } from "./run.js";
15
+ export { renderFinding, renderHuman, renderJson } from "./report.js";
16
+ /**
17
+ * What the runtime refuses to install. Wired into `senpi.installRuntime`; the boot loop
18
+ * deliberately does NOT use it — see the module header for why.
19
+ */
20
+ export { INSTALL_BLOCKING_CODES, checkInstallGate, type InstallGateResult } from "./install-gate.js";
21
+ /**
22
+ * The deploy verb's half of the gate. `verifyProof(packageDir)` answers "is this package's
23
+ * content the content that passed?" — pure observation, never throws, and every refusal carries
24
+ * a finding whose fix is the same one command. Enforcement belongs to the caller.
25
+ */
26
+ export { PROOF_FILENAME, fingerprintPackage, verifyProof, writeProof, type ProofRecord, type ScannerObservation, type VerifyOptions, type VerifyReason, type VerifyResult, } from "./proof.js";
27
+ export { checkScanner, resolveRoot, scaffoldPackageDir, toFindings, type ScannerTarget, } from "./import-stage.js";
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,KAAK,EACL,KAAK,EACL,OAAO,EACP,eAAe,EACf,OAAO,EACP,QAAQ,EACR,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAE3F,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,aAAa,EACb,aAAa,EACb,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,OAAO,EACZ,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErE;;;GAGG;AACH,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAErG;;;;GAIG;AACH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,KAAK,aAAa,GACnB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `senpi validate` — proving a strategy can actually run, before it is funded.
3
+ *
4
+ * This layer is the vocabulary: the finding shape every check speaks, the closed set of codes, and
5
+ * the accounting that keeps the two honest. Checks themselves live in sibling modules.
6
+ */
7
+ export { DEPTH_ORDER, depthsUpTo } from "./types.js";
8
+ export { resolveTarget, } from "./target.js";
9
+ export { ALL_VALIDATE_CODES, VALIDATE_CODE, VALIDATE_CODE_META, codesForStage, severityOf, } from "./codes.js";
10
+ export { findUncoveredCodes, findUnknownCodes } from "./coverage.js";
11
+ export { findLlmActionsWithoutModel, findMarginPctFraction, findMissingEntrypoints, findUngatedStrategy, findUnsizedStrategy, } from "./recipe-checks.js";
12
+ export { collectRecipeFindings, } from "./recipe.js";
13
+ export { isImplemented, runValidation, } from "./run.js";
14
+ export { renderFinding, renderHuman, renderJson } from "./report.js";
15
+ /**
16
+ * What the runtime refuses to install. Wired into `senpi.installRuntime`; the boot loop
17
+ * deliberately does NOT use it — see the module header for why.
18
+ */
19
+ export { INSTALL_BLOCKING_CODES, checkInstallGate } from "./install-gate.js";
20
+ /**
21
+ * The deploy verb's half of the gate. `verifyProof(packageDir)` answers "is this package's
22
+ * content the content that passed?" — pure observation, never throws, and every refusal carries
23
+ * a finding whose fix is the same one command. Enforcement belongs to the caller.
24
+ */
25
+ export { PROOF_FILENAME, fingerprintPackage, verifyProof, writeProof, } from "./proof.js";
26
+ export { checkScanner, resolveRoot, scaffoldPackageDir, toFindings, } from "./import-stage.js";
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EACL,aAAa,GAKd,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,UAAU,GAGX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAwB,MAAM,eAAe,CAAC;AAE3F,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,qBAAqB,GAGtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,aAAa,EACb,aAAa,GAMd,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErE;;;GAGG;AACH,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAA0B,MAAM,mBAAmB,CAAC;AAErG;;;;GAIG;AACH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,UAAU,GAMX,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,UAAU,GAEX,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * What the runtime refuses to install.
3
+ *
4
+ * `senpi validate` reports these same problems, but reporting is advisory: nothing stops an
5
+ * operator installing a recipe validate would refuse. This closes that from the other side — at
6
+ * the one boundary where refusing is free of consequence.
7
+ *
8
+ * ## Why install and not boot
9
+ *
10
+ * Refusing an install affects only the thing being installed: the gateway answers an error and
11
+ * everything already running is untouched. The boot loop is not like that — it starts runtimes in
12
+ * a single `try` whose `catch` stops every runtime already started, so a throw there takes the box
13
+ * down. A boot-time refusal would convert "one bad recipe trades" into "one bad recipe stops every
14
+ * other strategy", which is strictly worse than the problem. Boot-side enforcement waits for the
15
+ * per-entry degrade the supervised-scanner mount already uses.
16
+ *
17
+ * ## What it refuses, and what it deliberately does not
18
+ *
19
+ * Only conditions that make the recipe **inert or unsafe on every tick**, never a matter of taste:
20
+ * an entry action with no reachable margin source declines every signal it ever sees; one with no
21
+ * guardrails trades with no daily-loss or drawdown ceiling; a declared scanner file that does not
22
+ * exist can never start. Warnings stay warnings — this gate is not `--strict`, because an install
23
+ * refusal an operator disagrees with is how a safety check gets routed around.
24
+ *
25
+ * Measured before it was enforced: none of the 124 deployed instances trips any of these.
26
+ */
27
+ import type { Finding } from "./types.js";
28
+ export interface InstallGateResult {
29
+ ok: boolean;
30
+ /** The findings that blocked, in report order. Empty when `ok`. */
31
+ blocking: Finding[];
32
+ /** Ready to hand to the gateway's error responder. Present exactly when blocked. */
33
+ message?: string;
34
+ }
35
+ /**
36
+ * Decide whether a parsed recipe may be installed.
37
+ *
38
+ * Takes findings rather than a config so the caller owns how they were produced, and so this stays
39
+ * a pure decision over data — the same findings the CLI would print.
40
+ */
41
+ export declare function checkInstallGate(findings: Finding[]): InstallGateResult;
42
+ /** The codes this gate blocks on. Exported so a test can assert the list, not infer it. */
43
+ export declare const INSTALL_BLOCKING_CODES: readonly string[];
44
+ //# sourceMappingURL=install-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-gate.d.ts","sourceRoot":"","sources":["../../src/validate/install-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAgB1C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,mEAAmE;IACnE,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAKvE;AAED,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAAkB,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * What the runtime refuses to install.
3
+ *
4
+ * `senpi validate` reports these same problems, but reporting is advisory: nothing stops an
5
+ * operator installing a recipe validate would refuse. This closes that from the other side — at
6
+ * the one boundary where refusing is free of consequence.
7
+ *
8
+ * ## Why install and not boot
9
+ *
10
+ * Refusing an install affects only the thing being installed: the gateway answers an error and
11
+ * everything already running is untouched. The boot loop is not like that — it starts runtimes in
12
+ * a single `try` whose `catch` stops every runtime already started, so a throw there takes the box
13
+ * down. A boot-time refusal would convert "one bad recipe trades" into "one bad recipe stops every
14
+ * other strategy", which is strictly worse than the problem. Boot-side enforcement waits for the
15
+ * per-entry degrade the supervised-scanner mount already uses.
16
+ *
17
+ * ## What it refuses, and what it deliberately does not
18
+ *
19
+ * Only conditions that make the recipe **inert or unsafe on every tick**, never a matter of taste:
20
+ * an entry action with no reachable margin source declines every signal it ever sees; one with no
21
+ * guardrails trades with no daily-loss or drawdown ceiling; a declared scanner file that does not
22
+ * exist can never start. Warnings stay warnings — this gate is not `--strict`, because an install
23
+ * refusal an operator disagrees with is how a safety check gets routed around.
24
+ *
25
+ * Measured before it was enforced: none of the 124 deployed instances trips any of these.
26
+ */
27
+ import { VALIDATE_CODE } from "./codes.js";
28
+ /**
29
+ * The conditions that block an install.
30
+ *
31
+ * A closed list rather than "any error-severity finding": the static depth also reports things
32
+ * that are wrong but not dangerous, and an install gate that grows silently as checks are added
33
+ * is one that will one day refuse something nobody intended it to.
34
+ */
35
+ const BLOCKING = new Set([
36
+ VALIDATE_CODE.unsized,
37
+ VALIDATE_CODE.ungated,
38
+ VALIDATE_CODE.entrypointMissing,
39
+ VALIDATE_CODE.unresolvableScannerPath,
40
+ ]);
41
+ /**
42
+ * Render a refusal the recipient can act on without seeing this code.
43
+ *
44
+ * The gateway's error string is often the only thing an operator or agent ever reads, so it
45
+ * carries the same what/why/fix the findings do rather than a count and a pointer to logs.
46
+ */
47
+ function render(blocking) {
48
+ const lines = blocking.map((f) => ` • ${f.what}\n why: ${f.why}\n fix: ${f.fix}`);
49
+ return (`This recipe cannot be installed — it would not trade.\n\n${lines.join("\n\n")}\n\n` +
50
+ `Run \`senpi validate\` on the package for the full report.`);
51
+ }
52
+ /**
53
+ * Decide whether a parsed recipe may be installed.
54
+ *
55
+ * Takes findings rather than a config so the caller owns how they were produced, and so this stays
56
+ * a pure decision over data — the same findings the CLI would print.
57
+ */
58
+ export function checkInstallGate(findings) {
59
+ const blocking = findings.filter((f) => BLOCKING.has(f.code) && f.severity === "error");
60
+ return blocking.length === 0
61
+ ? { ok: true, blocking: [] }
62
+ : { ok: false, blocking, message: render(blocking) };
63
+ }
64
+ /** The codes this gate blocks on. Exported so a test can assert the list, not infer it. */
65
+ export const INSTALL_BLOCKING_CODES = [...BLOCKING];
66
+ //# sourceMappingURL=install-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-gate.js","sourceRoot":"","sources":["../../src/validate/install-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3C;;;;;;GAMG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS;IAC/B,aAAa,CAAC,OAAO;IACrB,aAAa,CAAC,OAAO;IACrB,aAAa,CAAC,iBAAiB;IAC/B,aAAa,CAAC,uBAAuB;CACtC,CAAC,CAAC;AAUH;;;;;GAKG;AACH,SAAS,MAAM,CAAC,QAAmB;IACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACzF,OAAO,CACL,4DAA4D,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM;QACpF,4DAA4D,CAC7D,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAmB;IAClD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IACxF,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC;QAC1B,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC5B,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;AACzD,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,sBAAsB,GAAsB,CAAC,GAAG,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The live depth: run one tick against real data, and say what it established.
3
+ *
4
+ * The harness is in Python, beside the loop production uses. This side builds its launch
5
+ * configuration, runs it, and judges the outcome.
6
+ *
7
+ * ## The judgement that matters
8
+ *
9
+ * A tick returning nothing is ambiguous. It could be a healthy scanner with no setups, or one that
10
+ * stopped before touching anything, or one whose every read failed. Those look identical from the
11
+ * outside and mean completely different things, so the verdict keys on **successful reads** rather
12
+ * than on the tick completing.
13
+ *
14
+ * A read that failed is not a read. Observed in practice: an authentication failure arrives as an
15
+ * ordinary response carrying a false flag, raising nothing, so a scanner's own error handling never
16
+ * fires and it computes a confidently wrong answer from missing input. Counting that as a read
17
+ * would report the run clean.
18
+ */
19
+ import { type ScannerTarget } from "./import-stage.js";
20
+ import { type SignalRecord } from "./signal-stage.js";
21
+ import type { Finding, FindingLocation } from "./types.js";
22
+ import type { ResolvedWallet } from "./wallet.js";
23
+ /** One recorded call, as the harness reports it. */
24
+ export interface ReadRecord {
25
+ tool: string;
26
+ ms: number;
27
+ ok: boolean;
28
+ error?: string;
29
+ error_type?: string;
30
+ response_keys?: string[];
31
+ cast_dropped?: boolean;
32
+ /** The call would have changed state and was refused rather than made. */
33
+ blocked?: boolean;
34
+ /** The response came back and the scanner never looked inside it. */
35
+ unread?: boolean;
36
+ }
37
+ export interface TickOutcome {
38
+ ok: boolean;
39
+ reason?: string;
40
+ detail?: string;
41
+ status?: "ok" | "error" | "timeout";
42
+ failure?: {
43
+ type: string;
44
+ message: string;
45
+ where?: {
46
+ file: string;
47
+ line: number;
48
+ } | null;
49
+ } | null;
50
+ reads?: ReadRecord[];
51
+ reads_ok?: number;
52
+ reads_failed?: number;
53
+ signals?: SignalRecord[];
54
+ suppressed?: {
55
+ file: string;
56
+ line: number;
57
+ type: string;
58
+ message: string;
59
+ }[];
60
+ last_return?: {
61
+ file: string;
62
+ line: number;
63
+ function: string;
64
+ } | null;
65
+ stdout?: string;
66
+ stderr?: string;
67
+ }
68
+ export interface LiveOptions {
69
+ scanner: ScannerTarget;
70
+ wallet: ResolvedWallet;
71
+ /** Per-scanner budget. Defaults to what the recipe declares — never to the cadence. */
72
+ timeoutSeconds: number;
73
+ intervalSeconds: number;
74
+ defaultSignalValiditySeconds: number;
75
+ inputs?: Record<string, unknown>;
76
+ signalDataSchema?: Record<string, unknown>;
77
+ stateHistoryMaxCount?: number;
78
+ pythonBin?: string;
79
+ }
80
+ /**
81
+ * Group failed reads by the service they belong to.
82
+ *
83
+ * One failed call among many is noise; every call to one service failing means a whole input is
84
+ * missing and the scanner scored without it. The tool-name prefix is the service, which is enough
85
+ * to tell those apart without knowing anything about the catalogue.
86
+ */
87
+ export declare function groupFailuresByService(reads: ReadRecord[]): Map<string, ReadRecord[]>;
88
+ /** What an earlier depth found that changes how this one reads its own result. */
89
+ export interface TickContext {
90
+ /**
91
+ * Places the import depth saw a data route the runtime cannot observe.
92
+ *
93
+ * Carried here because a tick that read nothing through the client, in a scanner that imports
94
+ * `requests`, is a different situation from one that stopped early — and pointing the second
95
+ * diagnosis at the first sends someone hunting a gate that does not exist.
96
+ */
97
+ nonMcpSources?: FindingLocation[];
98
+ /**
99
+ * What this run allowed a tick, and what production would allow it.
100
+ *
101
+ * They are not the same number. Validate caps at its own default so a six-hour-cadence scanner
102
+ * does not make the command hang for six hours; production, when the recipe declares no
103
+ * `timeout_seconds`, falls back to the interval (`scaffold.py`: `timeout_seconds = interval`).
104
+ * Carried here because a timeout finding that says "this would time out in production too" is
105
+ * simply false in that case, and a validator that states something untrue about production has
106
+ * failed at the one job it has.
107
+ */
108
+ budget?: TickBudget;
109
+ }
110
+ export interface TickBudget {
111
+ /** The budget this run enforced. */
112
+ validateSeconds: number;
113
+ /** The budget production would enforce for the same scanner. */
114
+ productionSeconds: number;
115
+ /** Whether the recipe declared `timeout_seconds`. When it did, the two agree. */
116
+ declared: boolean;
117
+ }
118
+ /**
119
+ * Production's tick budget for a scanner, mirroring the scaffold's own fallback.
120
+ *
121
+ * Kept beside the judgement that reports it, with the source named, because the whole point is
122
+ * that this number is production's and not ours.
123
+ */
124
+ export declare function productionBudgetSeconds(declaredTimeout: number | undefined, intervalSeconds: number): number;
125
+ /**
126
+ * Turn one tick into findings.
127
+ *
128
+ * Exported separately from running it so the judgement can be tested against recorded outcomes
129
+ * without spawning anything or needing credentials.
130
+ */
131
+ export declare function judgeTick(scanner: ScannerTarget, outcome: TickOutcome, context?: TickContext): Finding[];
132
+ /** Run one tick for one scanner. */
133
+ export declare function runTick(options: LiveOptions): Promise<TickOutcome>;
134
+ //# sourceMappingURL=live-stage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-stage.d.ts","sourceRoot":"","sources":["../../src/validate/live-stage.ts"],"names":[],"mappings":"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"}