@senpi/runtime 3.0.59 → 3.0.63

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 (151) hide show
  1. package/README.md +73 -2
  2. package/dist/actions/decision-engine.d.ts +0 -2
  3. package/dist/actions/decision-engine.d.ts.map +1 -1
  4. package/dist/actions/decision-engine.js +1 -79
  5. package/dist/actions/decision-engine.js.map +1 -1
  6. package/dist/cli/deploy-render.d.ts +91 -0
  7. package/dist/cli/deploy-render.d.ts.map +1 -0
  8. package/dist/cli/deploy-render.js +216 -0
  9. package/dist/cli/deploy-render.js.map +1 -0
  10. package/dist/cli/guide-commands.d.ts.map +1 -1
  11. package/dist/cli/guide-commands.js +78 -6
  12. package/dist/cli/guide-commands.js.map +1 -1
  13. package/dist/cli/senpi-commands.d.ts +6 -1
  14. package/dist/cli/senpi-commands.d.ts.map +1 -1
  15. package/dist/cli/senpi-commands.js +244 -8
  16. package/dist/cli/senpi-commands.js.map +1 -1
  17. package/dist/cli/validate-command.d.ts.map +1 -1
  18. package/dist/cli/validate-command.js +32 -1
  19. package/dist/cli/validate-command.js.map +1 -1
  20. package/dist/deploy/deadlines.d.ts +47 -0
  21. package/dist/deploy/deadlines.d.ts.map +1 -0
  22. package/dist/deploy/deadlines.js +56 -0
  23. package/dist/deploy/deadlines.js.map +1 -0
  24. package/dist/deploy/error-text.d.ts +25 -0
  25. package/dist/deploy/error-text.d.ts.map +1 -0
  26. package/dist/deploy/error-text.js +42 -0
  27. package/dist/deploy/error-text.js.map +1 -0
  28. package/dist/deploy/funding.d.ts +87 -0
  29. package/dist/deploy/funding.d.ts.map +1 -0
  30. package/dist/deploy/funding.js +229 -0
  31. package/dist/deploy/funding.js.map +1 -0
  32. package/dist/deploy/install-gate-runner.d.ts +12 -0
  33. package/dist/deploy/install-gate-runner.d.ts.map +1 -0
  34. package/dist/deploy/install-gate-runner.js +17 -0
  35. package/dist/deploy/install-gate-runner.js.map +1 -0
  36. package/dist/deploy/job.d.ts +199 -0
  37. package/dist/deploy/job.d.ts.map +1 -0
  38. package/dist/deploy/job.js +463 -0
  39. package/dist/deploy/job.js.map +1 -0
  40. package/dist/deploy/min-budget.d.ts +68 -0
  41. package/dist/deploy/min-budget.d.ts.map +1 -0
  42. package/dist/deploy/min-budget.js +297 -0
  43. package/dist/deploy/min-budget.js.map +1 -0
  44. package/dist/deploy/orchestrator.d.ts +305 -0
  45. package/dist/deploy/orchestrator.d.ts.map +1 -0
  46. package/dist/deploy/orchestrator.js +2910 -0
  47. package/dist/deploy/orchestrator.js.map +1 -0
  48. package/dist/deploy/package.d.ts +130 -0
  49. package/dist/deploy/package.d.ts.map +1 -0
  50. package/dist/deploy/package.js +351 -0
  51. package/dist/deploy/package.js.map +1 -0
  52. package/dist/deploy/proof-gate.d.ts +82 -0
  53. package/dist/deploy/proof-gate.d.ts.map +1 -0
  54. package/dist/deploy/proof-gate.js +269 -0
  55. package/dist/deploy/proof-gate.js.map +1 -0
  56. package/dist/deploy/python-float.d.ts +34 -0
  57. package/dist/deploy/python-float.d.ts.map +1 -0
  58. package/dist/deploy/python-float.js +46 -0
  59. package/dist/deploy/python-float.js.map +1 -0
  60. package/dist/deploy/recheck-commands.d.ts +50 -0
  61. package/dist/deploy/recheck-commands.d.ts.map +1 -0
  62. package/dist/deploy/recheck-commands.js +59 -0
  63. package/dist/deploy/recheck-commands.js.map +1 -0
  64. package/dist/deploy/register.d.ts +99 -0
  65. package/dist/deploy/register.d.ts.map +1 -0
  66. package/dist/deploy/register.js +689 -0
  67. package/dist/deploy/register.js.map +1 -0
  68. package/dist/deploy/taxonomy.d.ts +24 -0
  69. package/dist/deploy/taxonomy.d.ts.map +1 -0
  70. package/dist/deploy/taxonomy.js +31 -0
  71. package/dist/deploy/taxonomy.js.map +1 -0
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +244 -132
  74. package/dist/index.js.map +1 -1
  75. package/dist/runtime/strategy-registry.d.ts +8 -0
  76. package/dist/runtime/strategy-registry.d.ts.map +1 -1
  77. package/dist/runtime/strategy-registry.js +14 -1
  78. package/dist/runtime/strategy-registry.js.map +1 -1
  79. package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
  80. package/dist/runtime-api/intake/intake-mount.js +7 -4
  81. package/dist/runtime-api/intake/intake-mount.js.map +1 -1
  82. package/dist/scanners/implementations/market-regime.d.ts +1 -1
  83. package/dist/senpi/client.d.ts +58 -4
  84. package/dist/senpi/client.d.ts.map +1 -1
  85. package/dist/senpi/client.js +192 -15
  86. package/dist/senpi/client.js.map +1 -1
  87. package/dist/senpi/strategy-status.d.ts +32 -0
  88. package/dist/senpi/strategy-status.d.ts.map +1 -0
  89. package/dist/senpi/strategy-status.js +70 -0
  90. package/dist/senpi/strategy-status.js.map +1 -0
  91. package/dist/senpi/types.d.ts +47 -3
  92. package/dist/senpi/types.d.ts.map +1 -1
  93. package/dist/skills-manager/manifest.js +1 -1
  94. package/dist/skills-manager/skills-manifest.json +1 -1
  95. package/dist/telemetry/metrics.d.ts +23 -0
  96. package/dist/telemetry/metrics.d.ts.map +1 -1
  97. package/dist/telemetry/metrics.js +65 -0
  98. package/dist/telemetry/metrics.js.map +1 -1
  99. package/dist/types/strategy.d.ts +9 -1
  100. package/dist/types/strategy.d.ts.map +1 -1
  101. package/dist/universe/package-universe.d.ts +97 -0
  102. package/dist/universe/package-universe.d.ts.map +1 -0
  103. package/dist/universe/package-universe.js +275 -0
  104. package/dist/universe/package-universe.js.map +1 -0
  105. package/dist/validate/codes.d.ts +20 -0
  106. package/dist/validate/codes.d.ts.map +1 -1
  107. package/dist/validate/codes.js +36 -0
  108. package/dist/validate/codes.js.map +1 -1
  109. package/dist/validate/import-stage.js +5 -5
  110. package/dist/validate/import-stage.js.map +1 -1
  111. package/dist/validate/index.d.ts +1 -1
  112. package/dist/validate/index.d.ts.map +1 -1
  113. package/dist/validate/index.js.map +1 -1
  114. package/dist/validate/install-gate.d.ts.map +1 -1
  115. package/dist/validate/install-gate.js +10 -2
  116. package/dist/validate/install-gate.js.map +1 -1
  117. package/dist/validate/live-instruments.d.ts +44 -0
  118. package/dist/validate/live-instruments.d.ts.map +1 -0
  119. package/dist/validate/live-instruments.js +71 -0
  120. package/dist/validate/live-instruments.js.map +1 -0
  121. package/dist/validate/live-stage.js +5 -5
  122. package/dist/validate/live-stage.js.map +1 -1
  123. package/dist/validate/proof.d.ts +11 -1
  124. package/dist/validate/proof.d.ts.map +1 -1
  125. package/dist/validate/proof.js +7 -6
  126. package/dist/validate/proof.js.map +1 -1
  127. package/dist/validate/recipe-checks.d.ts.map +1 -1
  128. package/dist/validate/recipe-checks.js +6 -4
  129. package/dist/validate/recipe-checks.js.map +1 -1
  130. package/dist/validate/run.d.ts +5 -0
  131. package/dist/validate/run.d.ts.map +1 -1
  132. package/dist/validate/run.js +10 -1
  133. package/dist/validate/run.js.map +1 -1
  134. package/dist/validate/target.d.ts +41 -0
  135. package/dist/validate/target.d.ts.map +1 -1
  136. package/dist/validate/target.js +192 -16
  137. package/dist/validate/target.js.map +1 -1
  138. package/dist/validate/universe-stage.d.ts +71 -0
  139. package/dist/validate/universe-stage.d.ts.map +1 -0
  140. package/dist/validate/universe-stage.js +183 -0
  141. package/dist/validate/universe-stage.js.map +1 -0
  142. package/openclaw.plugin.json +1 -1
  143. package/package.json +1 -1
  144. package/dist/runtime/managed-wallet-guard.d.ts +0 -47
  145. package/dist/runtime/managed-wallet-guard.d.ts.map +0 -1
  146. package/dist/runtime/managed-wallet-guard.js +0 -106
  147. package/dist/runtime/managed-wallet-guard.js.map +0 -1
  148. package/dist/runtime/protection-nudge.d.ts +0 -67
  149. package/dist/runtime/protection-nudge.d.ts.map +0 -1
  150. package/dist/runtime/protection-nudge.js +0 -244
  151. package/dist/runtime/protection-nudge.js.map +0 -1
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Deploy's half of the proof contract.
3
+ *
4
+ * `verifyProof` owns the fact; this module owns turning it into a refusal deploy can render.
5
+ * The instances argument is the whole reason this file exists: VerifyOptions.instances is
6
+ * documented as directory names relative to the package root and consumed as
7
+ * `join(packageRoot, instance)`, while a DeployInstance carries a MANIFEST name. Passing the
8
+ * name verifies a directory that may not exist and reports `no_proof` for a proven package.
9
+ *
10
+ * ## What a proof actually attests, and what it does not
11
+ *
12
+ * A proof records that a full, unscoped `senpi validate` at live depth PASSED over exactly these
13
+ * bytes under exactly this runtime. It does NOT assert that every check that depth offers actually
14
+ * produced a verdict: the live-instrument check warns (`W_VALIDATE_UNIVERSE_UNAVAILABLE`) when the
15
+ * exchange list cannot be read, the verdict stays `PASS`, and `PASS` at live depth is what makes a
16
+ * run proof-eligible (`validate/run.ts`). So a proof can attest a run in which the universe check
17
+ * never ran.
18
+ *
19
+ * That is not a money leak, and it is deliberately not repaired here: `runDeploy` re-checks the
20
+ * universe itself, pre-money, from a live read on every deploy (`universe/package-universe.ts`), so
21
+ * no dead instrument reaches funding either way. What it means for THIS module is a rule about
22
+ * language: the refusals below say what a proof is a claim about — "this content ran under this
23
+ * runtime" — and never that the package was "fully validated" or "everything was checked". Recording
24
+ * the skipped checks INTO the proof was considered and rejected: deploy would then be enforcing on a
25
+ * stale record of an ops fault (an unreachable MCP server), refusing packages whose instruments it
26
+ * had just read as live, and routing an environment problem to the author.
27
+ */
28
+ import { isAbsolute, join, relative } from "node:path";
29
+ import { parse as parseYaml } from "yaml";
30
+ import { verifyProof, VALIDATE_CODE } from "../validate/index.js";
31
+ import { VERSION } from "../version.js";
32
+ import { readRuntimeYaml } from "./package.js";
33
+ import { validateCommand } from "./recheck-commands.js";
34
+ export function checkPackageProof(pkg) {
35
+ const instances = pkg.instances.map((inst) => relative(pkg.dir, inst.runtimeYamlDir));
36
+ return verifyProof(pkg.dir, { instances });
37
+ }
38
+ /**
39
+ * The one package shape a proof cannot answer for, which therefore has to be refused rather than
40
+ * verified.
41
+ *
42
+ * A proof is one file per DIRECTORY — `writeProof` writes `dirname(recipePath)/.senpi-proof.json` —
43
+ * and it records the recipe it was made for, which `verifyProofInner` re-fingerprints. Nothing in
44
+ * `loadDeployPackage` requires instances to live in different directories: a manifest naming
45
+ * `aggressive.yaml` and `calm.yaml` at the package root is legal and gives both instances the same
46
+ * `runtimeYamlDir`. Verifying that package asks the same directory twice and gets the same answer
47
+ * twice, so a proof recorded for ONE recipe passes the other — a recipe nobody watched run reaches
48
+ * funding, which is the precise harm this gate exists to prevent.
49
+ *
50
+ * Fixing it inside `verifyProof` would mean teaching the proof format about instances; the deploy
51
+ * layer is the one that HAS the manifest and can see the collision, so it refuses here. Fail-closed
52
+ * on principle and free in practice: of the 103 packages in the shipped catalog, 21 are
53
+ * multi-instance and none shares a runtime directory.
54
+ */
55
+ export function collidingInstanceDirs(pkg) {
56
+ const byDir = new Map();
57
+ for (const inst of pkg.instances) {
58
+ byDir.set(inst.runtimeYamlDir, [...(byDir.get(inst.runtimeYamlDir) ?? []), inst]);
59
+ }
60
+ return [...byDir.entries()]
61
+ .filter(([, instances]) => instances.length > 1)
62
+ .map(([dir, instances]) => ({ dir, instances }));
63
+ }
64
+ /**
65
+ * The relative `scanners[].path` values in one instance's recipe — the things that silently
66
+ * re-point if the recipe moves without them.
67
+ *
68
+ * `runtimeYamlDir` is documented as "the base relative scanner paths resolve against"
69
+ * (`package.ts`), so a recipe moved into a new directory takes every relative path with it and
70
+ * lands them somewhere that does not exist. Never throws: this feeds a refusal, and a gate that
71
+ * can fail to render is a gate that sometimes does nothing. (`runDeploy` has already parsed these
72
+ * same files twice by the time it gets here, so a parse error is not reachable on that path.)
73
+ */
74
+ function relativeScannerPaths(inst) {
75
+ try {
76
+ const parsed = parseYaml(readRuntimeYaml(inst));
77
+ const scanners = Array.isArray(parsed?.scanners) ? parsed.scanners : [];
78
+ const paths = scanners
79
+ .map((scanner) => scanner !== null && typeof scanner === "object"
80
+ ? scanner.path
81
+ : undefined)
82
+ .filter((path) => typeof path === "string" && path.length > 0 && !isAbsolute(path));
83
+ return [...new Set(paths)];
84
+ }
85
+ catch {
86
+ return [];
87
+ }
88
+ }
89
+ /**
90
+ * The collision as an instruction — deliberately an END STATE, not a command.
91
+ *
92
+ * The obvious rendering ("mv the recipe into its own directory") produces a package that does not
93
+ * validate: a relative `path:` resolves against the recipe's own directory, so moving the recipe
94
+ * alone re-points `./scanners` at a directory that does not exist and the very next validation
95
+ * fails on an unresolvable scanner path. Nor can the right move be computed here — two recipes that
96
+ * share a directory may well be sharing what is in it, and whether the tree moves, is copied, or is
97
+ * referenced from both is a decision about this package that this gate does not have the standing
98
+ * to make. So it names what will break (computed from each recipe, not templated), describes the
99
+ * state to reach, and emits no command whose preconditions it cannot check — the same rule
100
+ * `buildBudgetEscape` follows when a report cannot support a teardown.
101
+ *
102
+ * It also emits no post-move `senpi validate` path. Those directories do not exist yet, and a
103
+ * non-colliding sleeve's directory is not its manifest name — the exact trap this module exists to
104
+ * prevent, which is easy to walk back into while writing helpful-looking text. Re-running the
105
+ * deploy is the loop: it names any instance still unproven and carries the exact command for it.
106
+ */
107
+ export function buildInstanceDirCollisionRefusal(collisions, pkg) {
108
+ const relatives = new Set();
109
+ const groups = collisions.map((collision) => {
110
+ const rows = collision.instances.map((inst) => {
111
+ const paths = relativeScannerPaths(inst);
112
+ paths.forEach((path) => relatives.add(path));
113
+ const carries = paths.length > 0 ? ` — relative paths inside it: ${paths.join(", ")}` : "";
114
+ return ` "${inst.name}" → ${inst.runtimeYamlPath}${carries}`;
115
+ });
116
+ return ` - ${collision.dir} is shared by:\n${rows.join("\n")}`;
117
+ });
118
+ const movingCaveat = relatives.size > 0
119
+ ? `Moving a recipe is not enough on its own: a relative path in it (${[...relatives].join(", ")} ` +
120
+ `above) resolves against the directory the RECIPE is in, so a recipe that moves alone points ` +
121
+ `every one of them at a directory that does not exist, and the next validation fails on an ` +
122
+ `unresolvable scanner path. Whether the tree it names moves with the recipe, is copied, or is ` +
123
+ `referenced from both is a decision about this package — these sleeves share a directory ` +
124
+ `today, so they may be sharing what is in it. `
125
+ : "";
126
+ return (`[INVALID_REQUEST] ${collisions.length} directory(ies) in this package hold more than one ` +
127
+ `instance's recipe, and no proof can speak for either of them:\n${groups.join("\n")}\n\n` +
128
+ `Why: a proof lives BESIDE the recipe it is a claim about — one \`.senpi-proof.json\` per ` +
129
+ `directory, naming the one recipe it was recorded for. Two recipes in one directory therefore ` +
130
+ `share a single proof file, and whichever validation ran last is the only one it describes. ` +
131
+ `Deploy will not read that as evidence for both: it would fund and install a recipe nobody has ` +
132
+ `ever watched run, which is the one thing this gate exists to prevent.\n\n` +
133
+ `Nothing was created by this run — this gate reads the package's own files and nothing else, so ` +
134
+ `it stopped before any live read. Whatever this package already had is untouched, and this gate ` +
135
+ `cannot see it.\n\n` +
136
+ `Fix — each instance above needs its OWN directory, holding its recipe, with its \`runtime:\` in ` +
137
+ `${join(pkg.dir, "strategy.yaml")} updated to point there. ${movingCaveat}` +
138
+ `Once each one has its own directory and every relative path in its recipe still resolves from ` +
139
+ `there, re-run the deploy: it names any instance that is still unproven and carries the exact ` +
140
+ `\`openclaw senpi validate\` command for it.`);
141
+ }
142
+ /** How many changed files a single sentence names before it starts counting instead. */
143
+ const MAX_NAMED_FILES = 5;
144
+ /**
145
+ * The command that produces a proof, one per directory — from the shared producer, so the quoting
146
+ * these strings need (they are copied into a shell verbatim, and a real workspace path has spaces)
147
+ * is decided in one place for every surface that emits it.
148
+ *
149
+ * No python route here, deliberately: this gate is ABOUT the proof, and the skills validator does
150
+ * not write one. Naming it would be naming a command that cannot answer the refusal.
151
+ */
152
+ function validateCommands(dirs) {
153
+ return dirs.map((dir) => ` ${validateCommand(dir)}`).join("\n");
154
+ }
155
+ /**
156
+ * Which directories the fix should send the reader to, and how to name the instance in prose.
157
+ *
158
+ * `verifyProof` reports the FIRST instance it could not verify and stops, carrying its name on the
159
+ * result. Using it keeps the fix to the sleeve that actually failed — listing every instance is not
160
+ * wrong (deploy requires them all proven) but costs a real live tick per sleeve to follow. When
161
+ * there is no instance identity — a flat package, whose one instance IS the package root — the
162
+ * package directory is the only honest target.
163
+ */
164
+ function failingTarget(result, pkg) {
165
+ const failed = result.instance === undefined || result.instance === ""
166
+ ? undefined
167
+ : pkg.instances.find((inst) => relative(pkg.dir, inst.runtimeYamlDir) === result.instance);
168
+ if (failed === undefined) {
169
+ // Either a flat package, or a name this package no longer declares — in both cases the whole
170
+ // package is the target, which is never a wrong instruction, only a broader one.
171
+ const dirs = [...new Set(pkg.instances.map((inst) => inst.runtimeYamlDir))];
172
+ return { dirs, named: undefined };
173
+ }
174
+ return { dirs: [failed.runtimeYamlDir], named: failed.name };
175
+ }
176
+ /**
177
+ * The sentence that stops a second refusal from reading as "my fix did not work".
178
+ *
179
+ * Verification stops at the first unproven instance, so a package with two unproven sleeves refuses
180
+ * twice. Only said when there is more than one instance — on a flat package it would be noise.
181
+ */
182
+ function everyInstanceNote(pkg) {
183
+ // Its OWN line. Appended to the last command line, an agent copying that line pastes the
184
+ // sentence into a shell along with it.
185
+ return pkg.instances.length > 1
186
+ ? `\nDeploy needs every instance of this package proven and stops at the first that is not, so ` +
187
+ `expect to be sent back here if another one is also unproven.`
188
+ : "";
189
+ }
190
+ /** `scan.py, scoring.py` — the first few, then a count. Never a bare "the package differs". */
191
+ function nameChangedFiles(changed) {
192
+ const named = changed.slice(0, MAX_NAMED_FILES).join(", ");
193
+ return changed.length > MAX_NAMED_FILES ? `${named} and ${changed.length - MAX_NAMED_FILES} more` : named;
194
+ }
195
+ /**
196
+ * The three reasons, each with its own fix.
197
+ *
198
+ * A single generic "run senpi validate" would be a template hint rather than a computed one: the
199
+ * three states call for different reading. `no_proof` says nothing has ever been observed to run
200
+ * here; `content_changed` says the bytes moved and names which; `runtime_version_changed` says the
201
+ * package is unchanged and only the engine under it moved — the one case where editing the package
202
+ * would be exactly the wrong response.
203
+ */
204
+ export function proofRefusalParts(result, pkg) {
205
+ const { dirs, named } = failingTarget(result, pkg);
206
+ const commands = validateCommands(dirs);
207
+ const scope = named === undefined ? "this package" : `instance "${named}" (${dirs[0]})`;
208
+ if (result.reason === "content_changed") {
209
+ const changed = result.changedFiles ?? [];
210
+ return {
211
+ code: VALIDATE_CODE.contentChanged,
212
+ what: changed.length > 0
213
+ ? `This package differs from the content that passed: ${nameChangedFiles(changed)}`
214
+ : "This package differs from the content that passed",
215
+ why: "A proof is a claim about exact bytes: these files are not the ones that ran, so nothing is " +
216
+ "known about how they behave. An edit is not required to be large to change what a scanner does.",
217
+ fix: `either restore the proven content, or prove what is there now — re-run the validation and ` +
218
+ `let it PASS, then re-run the deploy:\n${commands}${everyInstanceNote(pkg)}`,
219
+ };
220
+ }
221
+ if (result.reason === "runtime_version_changed") {
222
+ const proved = result.record?.runtime_version;
223
+ return {
224
+ code: VALIDATE_CODE.runtimeVersionChanged,
225
+ what: proved
226
+ ? `The proof was recorded under runtime ${proved}; this box runs ${VERSION}`
227
+ : `The proof was recorded under a runtime other than the ${VERSION} running here`,
228
+ why: "A pass is a claim about this package running under a specific runtime build. The proof " +
229
+ "predates the running runtime — the runtime self-updates in place — or, on a box that was " +
230
+ "rolled back, was made under a later one. Either way it no longer describes the code that " +
231
+ "would actually execute; the two versions above say which case this is.",
232
+ fix: `the package needs no editing: its content still matches its proof, and only the engine under ` +
233
+ `it moved. Re-prove it against the runtime running here, then re-run the deploy:` +
234
+ `\n${commands}${everyInstanceNote(pkg)}`,
235
+ };
236
+ }
237
+ return {
238
+ code: VALIDATE_CODE.noProof,
239
+ what: `No proof of a passing validation exists for ${scope}`,
240
+ why: "Deploy funds a wallet and installs code onto it. A proof is the record that this exact " +
241
+ "content ran: only a full, unscoped `openclaw senpi validate` at live depth that PASSES writes one — a " +
242
+ "scoped or shallow run deliberately does not. Without it, nothing here has been observed to " +
243
+ "execute at all.",
244
+ fix: `record one, and let it PASS, then re-run the deploy:\n${commands}${everyInstanceNote(pkg)}`,
245
+ };
246
+ }
247
+ /**
248
+ * The refusal as the step row's `detail`.
249
+ *
250
+ * The bracketed code leads, and is not decoration: `taxonomy.ts` extracts it and the CLI uses it to
251
+ * exit 2 (obey this) rather than 1 (state unknown).
252
+ *
253
+ * It emits NO re-run command of its own. The report's `next` already ends a refused report with
254
+ * `openclaw senpi deploy -p '<dir>' --budget <the real figure>`; a copy here would carry a `<usd>` placeholder
255
+ * instead, which is a second, worse command competing with the authoritative one.
256
+ */
257
+ export function buildProofRefusal(result, pkg) {
258
+ const parts = proofRefusalParts(result, pkg);
259
+ return (`[${parts.code}] ${parts.what}.\n\n` +
260
+ `Why: ${parts.why}\n\n` +
261
+ // Scoped to THIS RUN, like the universe gate's refusal: the check runs before deploy reads any
262
+ // live state, so on a resume the package may already own a funded wallet this gate cannot see.
263
+ // Claiming "no wallet exists" there would invite funding a second one beside the live one.
264
+ `Nothing was created by this run — this gate reads the package's own files and nothing else, so ` +
265
+ `it stopped before any live read. Whatever this package already had is untouched, and this gate ` +
266
+ `cannot see it.\n\n` +
267
+ `Fix — ${parts.fix}`);
268
+ }
269
+ //# sourceMappingURL=proof-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proof-gate.js","sourceRoot":"","sources":["../../src/deploy/proof-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Read a YAML scalar the way Python's `float()` would — the ONE implementation of that question in
3
+ * the deploy module.
4
+ *
5
+ * Two places need it, for the same reason: the numbers they read are also read on the skills side
6
+ * by Python, and a disagreement means the discovery card and the verb compute different money.
7
+ * `min-budget.ts` uses it as the port of `min_budget.py`'s `_f`; `package.ts` uses it for
8
+ * `funding_share`, which additionally drives `planFunding`'s real allocation. They were briefly two
9
+ * near-copies that had already drifted (one had a boolean branch, the other did not) — which is the
10
+ * drift class this whole workstream exists to close, so they are one function now.
11
+ *
12
+ * SHAPE, NOT LETTER. A number, a bool, or a numeric string parses; everything else is null. Two
13
+ * classes of input where that is not exactly `float()`, both verified, both deliberately left:
14
+ *
15
+ * - **A blank/whitespace string.** `float("")` raises, `Number("")` is `0`, so the explicit guard
16
+ * aligns them. Defence in depth: it changes no output today, because every call site rejects `0`
17
+ * downstream anyway — but that alignment then survives the next edit to any of those guards
18
+ * instead of holding by accident.
19
+ * - **Quoted exotic numeric strings**, the only real parity gap across ~450 adversarial inputs, and
20
+ * reachable ONLY through a quoted YAML scalar (an unquoted `0x14` parses to `20` on both sides
21
+ * before either implementation sees it). Python accepts more than `Number` in some places
22
+ * (`"2_5"` → 25.0 under PEP 515, `"Infinity"`/`"inf"` → inf) and less in others (`"0x14"` raises
23
+ * where `Number` gives 20). Deliberately NOT "fixed" with a numeric-literal regex: that closes
24
+ * the `0x14` direction while leaving the underscore direction open, trading a documented, tested
25
+ * divergence for a half-corrected one. Both directions are pinned as tests — in
26
+ * `min-budget.test.ts` for the sizing path and `package.test.ts` for `funding_share` — so a
27
+ * future "fix" cannot flip one silently.
28
+ *
29
+ * No shipped package is affected either way: across the 103 catalog packages, zero declare a quoted
30
+ * or non-numeric `funding_share` and zero declare a quoted sizing value.
31
+ */
32
+ /** Python `float(x)`, returning null where it would raise. See the module header for the two gaps. */
33
+ export declare function pythonFloat(value: unknown): number | null;
34
+ //# sourceMappingURL=python-float.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python-float.d.ts","sourceRoot":"","sources":["../../src/deploy/python-float.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,sGAAsG;AACtG,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CASzD"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Read a YAML scalar the way Python's `float()` would — the ONE implementation of that question in
3
+ * the deploy module.
4
+ *
5
+ * Two places need it, for the same reason: the numbers they read are also read on the skills side
6
+ * by Python, and a disagreement means the discovery card and the verb compute different money.
7
+ * `min-budget.ts` uses it as the port of `min_budget.py`'s `_f`; `package.ts` uses it for
8
+ * `funding_share`, which additionally drives `planFunding`'s real allocation. They were briefly two
9
+ * near-copies that had already drifted (one had a boolean branch, the other did not) — which is the
10
+ * drift class this whole workstream exists to close, so they are one function now.
11
+ *
12
+ * SHAPE, NOT LETTER. A number, a bool, or a numeric string parses; everything else is null. Two
13
+ * classes of input where that is not exactly `float()`, both verified, both deliberately left:
14
+ *
15
+ * - **A blank/whitespace string.** `float("")` raises, `Number("")` is `0`, so the explicit guard
16
+ * aligns them. Defence in depth: it changes no output today, because every call site rejects `0`
17
+ * downstream anyway — but that alignment then survives the next edit to any of those guards
18
+ * instead of holding by accident.
19
+ * - **Quoted exotic numeric strings**, the only real parity gap across ~450 adversarial inputs, and
20
+ * reachable ONLY through a quoted YAML scalar (an unquoted `0x14` parses to `20` on both sides
21
+ * before either implementation sees it). Python accepts more than `Number` in some places
22
+ * (`"2_5"` → 25.0 under PEP 515, `"Infinity"`/`"inf"` → inf) and less in others (`"0x14"` raises
23
+ * where `Number` gives 20). Deliberately NOT "fixed" with a numeric-literal regex: that closes
24
+ * the `0x14` direction while leaving the underscore direction open, trading a documented, tested
25
+ * divergence for a half-corrected one. Both directions are pinned as tests — in
26
+ * `min-budget.test.ts` for the sizing path and `package.test.ts` for `funding_share` — so a
27
+ * future "fix" cannot flip one silently.
28
+ *
29
+ * No shipped package is affected either way: across the 103 catalog packages, zero declare a quoted
30
+ * or non-numeric `funding_share` and zero declare a quoted sizing value.
31
+ */
32
+ /** Python `float(x)`, returning null where it would raise. See the module header for the two gaps. */
33
+ export function pythonFloat(value) {
34
+ if (typeof value === "number")
35
+ return Number.isFinite(value) ? value : null;
36
+ if (typeof value === "boolean")
37
+ return value ? 1 : 0; // float(True) === 1.0
38
+ if (typeof value === "string") {
39
+ if (value.trim() === "")
40
+ return null;
41
+ const parsed = Number(value);
42
+ return Number.isFinite(parsed) ? parsed : null;
43
+ }
44
+ return null;
45
+ }
46
+ //# sourceMappingURL=python-float.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python-float.js","sourceRoot":"","sources":["../../src/deploy/python-float.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,sGAAsG;AACtG,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB;IAC5E,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The commands a deploy refusal sends a reader to when something has to be re-checked.
3
+ *
4
+ * TWO ROUTES, and both are named wherever either is. `python3 senpi-strategy-ops/scripts/…` is what
5
+ * the skills layer teaches today; `senpi validate` is what actually enforces, because a full,
6
+ * unscoped run at live depth that PASSES is the only thing that writes the `.senpi-proof.json` this
7
+ * verb now refuses a package without. A refusal naming only the python route sends an agent around
8
+ * a loop that cannot close: run the python validator, deploy, get refused for a missing proof,
9
+ * repeat — the python route cannot end it, and nothing in the refusal says so.
10
+ *
11
+ * ONE producer, for the same reason `resumeCommand` is one: every string here is pasted into a
12
+ * shell verbatim, so the quoting rule (a real workspace path has spaces in it) is decided once
13
+ * rather than per call site. Its own module because the callers span `deploy/` and `universe/`:
14
+ * `orchestrator.ts` imports `../universe/package-universe.ts`, so a producer living in either one
15
+ * would be imported back by the other — the same cycle constraint that gave `error-text.ts` a
16
+ * module of its own.
17
+ */
18
+ /**
19
+ * `openclaw senpi validate '<dir>'` — the route that records a proof.
20
+ *
21
+ * The argument is always a DIRECTORY: an instance's `runtimeYamlDir`, a package root when no
22
+ * instance is known, or — where the package would not load at all — the directory the caller named,
23
+ * which is the only target left and the one validate diagnoses identically. Never a manifest
24
+ * instance NAME (validate resolves a path, and a name is not one — the exact confusion the proof
25
+ * gate exists to survive), and never a `<placeholder>`, which an agent pastes literally.
26
+ */
27
+ export declare function validateCommand(dir: string): string;
28
+ /** The skills package validator: the author-side checks over a package, and no proof. */
29
+ export declare function deployPyValidateCommand(packageDir: string): string;
30
+ /** The skills universe validator: the live-instrument half of the checks, and no proof. */
31
+ export declare function validateUniversePyCommand(packageDir: string): string;
32
+ /**
33
+ * Both routes as an indented block, one command per line — the form every refusal below the "fix"
34
+ * line uses.
35
+ *
36
+ * The validate directories are de-duplicated because two instances can legally share one, and the
37
+ * same line twice reads as two things to do. The runtime route leads: it is the one that ends the
38
+ * loop, and a reader who runs only the first command they see should be that reader.
39
+ */
40
+ export declare function recheckBlock(validateDirs: readonly string[], pythonCommand: string): string;
41
+ /**
42
+ * The sentence that stops "either" from reading as "either one finishes this".
43
+ *
44
+ * Named routes without it are a coin flip an agent loses half the time: the python route answers
45
+ * the question the refusal asked and still leaves the deploy refusing, with nothing in the text to
46
+ * explain why. It says which route ends the loop, and does not disparage the other — the skills
47
+ * validator is what the author-side skill teaches, and it catches these same faults.
48
+ */
49
+ export declare const PROOF_ROUTE_NOTE = "Only `openclaw senpi validate` records the proof this deploy requires; the python route writes none.";
50
+ //# sourceMappingURL=recheck-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recheck-commands.d.ts","sourceRoot":"","sources":["../../src/deploy/recheck-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,yFAAyF;AACzF,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,2FAA2F;AAC3F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,yGAC2E,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The commands a deploy refusal sends a reader to when something has to be re-checked.
3
+ *
4
+ * TWO ROUTES, and both are named wherever either is. `python3 senpi-strategy-ops/scripts/…` is what
5
+ * the skills layer teaches today; `senpi validate` is what actually enforces, because a full,
6
+ * unscoped run at live depth that PASSES is the only thing that writes the `.senpi-proof.json` this
7
+ * verb now refuses a package without. A refusal naming only the python route sends an agent around
8
+ * a loop that cannot close: run the python validator, deploy, get refused for a missing proof,
9
+ * repeat — the python route cannot end it, and nothing in the refusal says so.
10
+ *
11
+ * ONE producer, for the same reason `resumeCommand` is one: every string here is pasted into a
12
+ * shell verbatim, so the quoting rule (a real workspace path has spaces in it) is decided once
13
+ * rather than per call site. Its own module because the callers span `deploy/` and `universe/`:
14
+ * `orchestrator.ts` imports `../universe/package-universe.ts`, so a producer living in either one
15
+ * would be imported back by the other — the same cycle constraint that gave `error-text.ts` a
16
+ * module of its own.
17
+ */
18
+ /**
19
+ * `openclaw senpi validate '<dir>'` — the route that records a proof.
20
+ *
21
+ * The argument is always a DIRECTORY: an instance's `runtimeYamlDir`, a package root when no
22
+ * instance is known, or — where the package would not load at all — the directory the caller named,
23
+ * which is the only target left and the one validate diagnoses identically. Never a manifest
24
+ * instance NAME (validate resolves a path, and a name is not one — the exact confusion the proof
25
+ * gate exists to survive), and never a `<placeholder>`, which an agent pastes literally.
26
+ */
27
+ export function validateCommand(dir) {
28
+ return `openclaw senpi validate '${dir}'`;
29
+ }
30
+ /** The skills package validator: the author-side checks over a package, and no proof. */
31
+ export function deployPyValidateCommand(packageDir) {
32
+ return `python3 senpi-strategy-ops/scripts/deploy.py validate '${packageDir}'`;
33
+ }
34
+ /** The skills universe validator: the live-instrument half of the checks, and no proof. */
35
+ export function validateUniversePyCommand(packageDir) {
36
+ return `python3 senpi-strategy-ops/scripts/validate_universe.py '${packageDir}'`;
37
+ }
38
+ /**
39
+ * Both routes as an indented block, one command per line — the form every refusal below the "fix"
40
+ * line uses.
41
+ *
42
+ * The validate directories are de-duplicated because two instances can legally share one, and the
43
+ * same line twice reads as two things to do. The runtime route leads: it is the one that ends the
44
+ * loop, and a reader who runs only the first command they see should be that reader.
45
+ */
46
+ export function recheckBlock(validateDirs, pythonCommand) {
47
+ const lines = [...new Set(validateDirs)].map(validateCommand);
48
+ return [...lines, pythonCommand].map((command) => ` ${command}`).join("\n");
49
+ }
50
+ /**
51
+ * The sentence that stops "either" from reading as "either one finishes this".
52
+ *
53
+ * Named routes without it are a coin flip an agent loses half the time: the python route answers
54
+ * the question the refusal asked and still leaves the deploy refusing, with nothing in the text to
55
+ * explain why. It says which route ends the loop, and does not disparage the other — the skills
56
+ * validator is what the author-side skill teaches, and it catches these same faults.
57
+ */
58
+ export const PROOF_ROUTE_NOTE = "Only `openclaw senpi validate` records the proof this deploy requires; the python route writes none.";
59
+ //# sourceMappingURL=recheck-commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recheck-commands.js","sourceRoot":"","sources":["../../src/deploy/recheck-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,4BAA4B,GAAG,GAAG,CAAC;AAC5C,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,uBAAuB,CAAC,UAAkB;IACxD,OAAO,0DAA0D,UAAU,GAAG,CAAC;AACjF,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,yBAAyB,CAAC,UAAkB;IAC1D,OAAO,4DAA4D,UAAU,GAAG,CAAC;AACnF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,YAA+B,EAAE,aAAqB;IACjF,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,KAAK,EAAE,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,sGAAsG,CAAC"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Gateway wiring for the deploy job: `senpi.deploy.start` / `.status`.
3
+ *
4
+ * These two methods are **internal transport** between the `senpi deploy` CLI
5
+ * verbs and the plugin. They are deliberately absent from the cheatsheet, the
6
+ * README's agent-facing sections and the skills: one teaching surface (the CLI)
7
+ * wins routing, and naming the RPCs too would invite raw-RPC calls that bypass
8
+ * the CLI-side validation.
9
+ *
10
+ * The module owns no logic of its own beyond adapting ctx → collaborators:
11
+ * everything it needs arrives in an explicit {@link DeployGatewayContext}, so it
12
+ * never reaches into the plugin's boot closure.
13
+ */
14
+ import type { SenpiClient } from "../senpi/client.js";
15
+ import { type RegistryEntryLite, type ScannerRowLite } from "./orchestrator.js";
16
+ import { type DeployJobController } from "./job.js";
17
+ /**
18
+ * Mirrors `installRuntimeCore`'s return shape (index.ts) without importing the boot closure.
19
+ *
20
+ * `payload.unwired` is the unwired phase — the install succeeded, but the runtime has NO live entry
21
+ * scanners. Deploy reads it as an install failure (it funded a wallet for a runtime that can never
22
+ * tick); direct gateway/CLI installs keep treating `ok` as ok.
23
+ */
24
+ export type InstallGatewayResult = {
25
+ ok: true;
26
+ payload: {
27
+ id: string;
28
+ name: string;
29
+ wallet: string;
30
+ unwired?: string;
31
+ };
32
+ } | {
33
+ ok: false;
34
+ error: {
35
+ code: string;
36
+ message: string;
37
+ };
38
+ };
39
+ /** The slice of the plugin API this module uses. */
40
+ export interface DeployPluginApi {
41
+ registerGatewayMethod(name: string, handler: (ctx: {
42
+ respond: (ok: boolean, value: unknown) => void;
43
+ params?: Record<string, unknown>;
44
+ }) => void | Promise<void>, opts?: {
45
+ scope?: "operator.write" | "operator.read";
46
+ }): void;
47
+ }
48
+ /**
49
+ * Everything the deploy gateway needs from the plugin boot closure, injected
50
+ * explicitly — register.ts never reaches into index.ts internals.
51
+ */
52
+ export interface DeployGatewayContext {
53
+ controller: DeployJobController;
54
+ /**
55
+ * Runs fn with a fresh connected SenpiClient; always disconnects. One helper, reused per job.
56
+ *
57
+ * NOTE: a deploy holds this client for the whole job (up to `maxWait` + `tickWait` + install),
58
+ * unlike every other call site, which connects per operation. That is deliberate — the create
59
+ * poll must keep the same session — but it is the seam where an MCP idle/TTL bound would first
60
+ * surface, as a throw from the poll. That throw is now contained: the orchestrator records the
61
+ * step `failed` with the message quoted rather than losing the whole report.
62
+ */
63
+ withSenpiClient<T>(fn: (client: SenpiClient) => Promise<T>): Promise<T>;
64
+ /** = installRuntimeCore */
65
+ installRuntime(params: Record<string, unknown>): Promise<InstallGatewayResult>;
66
+ /** = deleteRuntimeCore — archival/launcher teardown is NOT reimplemented here. */
67
+ deleteRuntime(id: string): Promise<void>;
68
+ registryFindByWallet(wallet: string): Promise<RegistryEntryLite | undefined>;
69
+ registryFindById(id: string): Promise<RegistryEntryLite | undefined>;
70
+ getScannerRows(runtimeId: string): Promise<ScannerRowLite[] | null>;
71
+ /** H4: does this runtime id have a LIVE handle (not merely a registry row)? */
72
+ runtimeIsLive(id: string): Promise<boolean>;
73
+ logger: {
74
+ info(line: string): void;
75
+ };
76
+ }
77
+ export interface DeployLiveLayer {
78
+ strategies: Array<{
79
+ strategyName: string;
80
+ status: string;
81
+ strategyWalletAddress: string;
82
+ totalFunded?: number;
83
+ }>;
84
+ runtimes: Array<{
85
+ id: string;
86
+ wallet?: string;
87
+ }>;
88
+ /**
89
+ * Set when this layer could not be read at all — the package is gone (moved/deleted) or the
90
+ * strategy read failed closed (`listStrategies` throws rather than reporting zero). The layer is
91
+ * then EMPTY, not wrong: the renderer prints the reason instead of "none matching this package",
92
+ * which over a job that may have funded wallets would be the one claim this layer exists to
93
+ * prevent. Never set for a genuinely empty result.
94
+ */
95
+ unavailable?: string;
96
+ }
97
+ /** Register the internal deploy transport pair. */
98
+ export declare function registerDeployGatewayMethods(api: DeployPluginApi, ctx: DeployGatewayContext): void;
99
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/deploy/register.ts"],"names":[],"mappings":"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"}