@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,297 @@
1
+ /**
2
+ * The package's CALCULATED minimum budget — a 1:1 port of `min_budget.py`'s `strategy_min_budget`
3
+ * (Senpi-ai/senpi-skills `senpi-strategy-ops/scripts/min_budget.py`, vendored byte-identically into
4
+ * that repo's `senpi-trading-runtime` skill, where `gen_catalog.py` bakes the number into the
5
+ * discovery card).
6
+ *
7
+ * WHAT IT IS. The smallest TOTAL budget at which the authored design functions: every wallet funds
8
+ * (>= the {@link WALLET_FLOOR} platform floor) AND every wallet's SMALLEST slot can open at least
9
+ * the engine's bumped-minimum notional at that slot's smallest marginPct and its LOWEST leverage —
10
+ * worst case on BOTH axes, so even the least-leveraged, smallest-margin position clears the floor.
11
+ *
12
+ * perWalletMin = max(WALLET_FLOOR, BUMPED_NOTIONAL / (marginPct/100 * minLeverage) + FEE_BUFFER)
13
+ * strategyMin = max over wallets( perWalletMin / fundingShare )
14
+ * floored at WALLET_FLOOR * walletCount
15
+ * rounded up to a clean rung (STEPS); past the last rung, ceil to the next $25
16
+ *
17
+ * Funding shares matter, not just wallet count: deploy splits the budget by share, so a small-share
18
+ * sleeve must still receive its own per-wallet minimum — it can DRIVE the total up.
19
+ *
20
+ * WHY IT IS A WARNING, NOT A GATE. It is a local estimate over a package's own YAML, not a platform
21
+ * rule: the hard money invariant is the $10 × wallets floor, which the backend enforces. A budget
22
+ * at or above that floor but below this number DEPLOYS and runs degraded — fewer slots than
23
+ * designed. See the soft tier in `orchestrator.ts`'s preflight, which warns and proceeds.
24
+ *
25
+ * PARITY IS TESTED, NOT ASSERTED. `__tests__/min-budget.test.ts` diffs this port against goldens
26
+ * generated by executing the real Python; `scripts/gen-min-budget-goldens.py` regenerates them.
27
+ * Parity is defined at the **parsed-document level**: both sides are handed an already-parsed
28
+ * mapping, so YAML-parser differences (PyYAML is YAML 1.1 — `2_5` → 25, `yes` → true, `1:30` → 90;
29
+ * the JS `yaml` package is YAML 1.2 and leaves all three as strings) are upstream of this file and
30
+ * out of the port's scope. That gap is only observable on a package that quotes such a scalar, and
31
+ * only a dev-box comparison against `catalog.json`'s baked `min_budget` can prove it clean.
32
+ *
33
+ * DOCUMENTED DEVIATIONS from the reference — deliberate, each pinned by a test:
34
+ *
35
+ * 1. **An absent `funding_share` resolves to `1/n`, not `1.0`** — in {@link packageMinBudget} only;
36
+ * {@link strategyMinBudget} keeps the Python's `1.0`. `1/n` is the split a FRESH deploy of the
37
+ * package would apply (funding.ts `sharesOf`), which is what this total means; the Python's
38
+ * `1.0` understates it by the wallet count, so a 2-sleeve package could advertise a minimum
39
+ * neither wallet could actually trade at. The Python is safe with `1.0` because it only runs at
40
+ * catalog-generation time, on packages `_pkg.validate` has already forced to declare shares
41
+ * summing to 1; the verb is a DIRECT path and `loadDeployPackage` enforces neither.
42
+ *
43
+ * Note what this figure is NOT: the yardstick for a partially-adopted re-run, where the budget
44
+ * is split among fewer wallets. The shortfall claim belongs to the caller, per wallet, against
45
+ * `breakdown[].perWalletMin` — see `applyCalculatedMinimum` in orchestrator.ts.
46
+ * 2. **`toFloat` is Python-`float()`-shaped, not Python-`float()`-exact** — see `python-float.ts`,
47
+ * which owns that function and its two documented gaps for this file AND for `package.ts`.
48
+ * 3. **`round2` uses `toFixed(2)`** — see its own comment.
49
+ * 4. **A non-mapping `strategy:` block (or runtime doc) degrades to `{}`; the Python raises.**
50
+ * `strategy: "oops"` makes `rt.get("strategy") or {}` return the string and `.get` blow up with
51
+ * an AttributeError — which is right for `gen_catalog.py`, where a malformed package should fail
52
+ * the build loudly. Here the same input yields an unresolved sleeve and a warn. This is an
53
+ * advisory computed AFTER the money gates have passed: it must never be the thing that fails a
54
+ * deploy (the same doctrine that makes `packageMinBudget`'s callers swallow a throw).
55
+ * 5. **Instance names are coerced with `String(...)`** where the Python uses the raw key, so a
56
+ * nameless instance yields `""` rather than `None` and a numeric `name: 5` finds `runtimes["5"]`
57
+ * where the Python misses `runtimes[5]`. Unreachable through {@link packageMinBudget} —
58
+ * `package.ts` stringifies names and refuses empty ones — so this can only surface if
59
+ * {@link strategyMinBudget} is ever called with a hand-built manifest.
60
+ */
61
+ import { parse as parseYaml } from "yaml";
62
+ import { readRuntimeYaml } from "./package.js";
63
+ import { pythonFloat as toFloat } from "./python-float.js";
64
+ /**
65
+ * The platform per-wallet floor — mirrors the backend's `MINIMUM_STRATEGY_BUDGET` default
66
+ * (`getMinimumStrategyBudget` in senpi-hyperliquid-mcp). `funding.ts` re-exports it as `MIN_WALLET`
67
+ * so the runtime, like the skills side, has ONE copy of the physics.
68
+ */
69
+ export const WALLET_FLOOR = 10.0;
70
+ /** The notional the engine bumps a small order up to. */
71
+ export const BUMPED_NOTIONAL = 12.0;
72
+ /** USDC reserved per wallet for the creation fee (observed ~$1). */
73
+ export const FEE_BUFFER = 1.5;
74
+ /**
75
+ * The clean rungs a minimum rounds UP to. Past the last rung we ceil to the next $25 — never clamp,
76
+ * or a design above $250 would silently advertise a minimum below what it needs.
77
+ */
78
+ const STEPS = [10, 15, 20, 25, 30, 40, 50, 60, 75, 100, 125, 200, 250];
79
+ /**
80
+ * marginPct is read from an EXPLICIT allowlist of sizing keys — NOT a `*MarginPct*` match. A pattern
81
+ * would wrongly pick up non-sizing thresholds (e.g. sailfish's `leaderMarginPct`, an RS-gap in
82
+ * percentage points), collapsing its minimum from $25 to $250.
83
+ */
84
+ const MARGIN_KEYS = ["marginPct", "marginPctBase", "baseAllocationPct", "minMarginPct"];
85
+ /**
86
+ * Python `round(x, 2)`. `toFixed` rounds the exact decimal value of the double, which is what
87
+ * `round()` does too — unlike `Math.round(x * 100) / 100`, which rounds a second time after the
88
+ * multiply and disagrees on ordinary values like 1.005.
89
+ *
90
+ * They still part company on an exact binary tie, where Python rounds half-to-even and `toFixed`
91
+ * rounds half-up. Such a value EXISTS (`marginPct` 139.1304347826087 → 10.12 vs 10.13) but no
92
+ * realistic authored sizing reaches one: a 7,920-pair sweep of `marginPct ∈ [1.25,100] step 0.25`
93
+ * × `leverage ∈ {0.25…50}` found zero divergences. It would cost only the two rounded DISPLAY
94
+ * fields (`raw`, `breakdown[].perWalletMin`) and the binding tie-break anyway — `minBudget` itself
95
+ * is exact rung/ceil arithmetic and never rides this.
96
+ */
97
+ function round2(value) {
98
+ return Number(value.toFixed(2));
99
+ }
100
+ /** Round UP to the next clean rung; past the last one, ceil to the next $25 — never clamp. */
101
+ function roundStep(value) {
102
+ for (const step of STEPS) {
103
+ if (value <= step + 1e-9)
104
+ return step;
105
+ }
106
+ return Math.ceil(value / 25) * 25;
107
+ }
108
+ function isRecord(value) {
109
+ return typeof value === "object" && value !== null && !Array.isArray(value);
110
+ }
111
+ /**
112
+ * Every `external_scanner`'s `inputs` map. A multi-scanner runtime is sized off the WORST
113
+ * (smallest-slot) of all of them, not just the first.
114
+ */
115
+ function externalScannerInputs(runtime) {
116
+ const scanners = runtime.scanners;
117
+ if (!Array.isArray(scanners))
118
+ return [];
119
+ return scanners
120
+ .filter((sc) => isRecord(sc) && sc.type === "external_scanner")
121
+ .map((sc) => sc.inputs)
122
+ .filter(isRecord);
123
+ }
124
+ /**
125
+ * A marginPct is a PERCENT in (0,100]; a value <= 1 is either a pasted fraction or, for the
126
+ * vol-parity sleeves, an internal risk weight that is NOT a slot size. Reject it, so those sleeves
127
+ * come back UNRESOLVED and warn rather than being read as a 0.03%-margin slot.
128
+ */
129
+ function asPercent(value) {
130
+ const parsed = toFloat(value);
131
+ return parsed !== null && parsed > 1.0 ? parsed : null;
132
+ }
133
+ /**
134
+ * The SMALLEST configured slot marginPct — the min over the declared baseline, every allowlisted
135
+ * scanner sizing input, and every `marginPctTiers` value. Worst case, so a wallet funded at the
136
+ * minimum can open even its lowest-conviction slot. null when nothing resolves.
137
+ */
138
+ function marginPct(strategy, inputsList) {
139
+ const candidates = [];
140
+ const push = (value) => {
141
+ if (value)
142
+ candidates.push(value);
143
+ };
144
+ push(asPercent(strategy.margin_pct));
145
+ for (const inputs of inputsList) {
146
+ for (const key of MARGIN_KEYS)
147
+ push(asPercent(inputs[key]));
148
+ const tiers = inputs.marginPctTiers;
149
+ if (isRecord(tiers))
150
+ for (const tier of Object.values(tiers))
151
+ push(asPercent(tier));
152
+ }
153
+ return candidates.length > 0 ? Math.min(...candidates) : null;
154
+ }
155
+ /** Leverage values from a tiers config: a map (`{apex,good,base}`) or a list of `[score, lev]` pairs. */
156
+ function leverageValues(tiers) {
157
+ const collect = (raw) => raw.map(toFloat).filter((value) => value !== null && value > 0);
158
+ if (isRecord(tiers))
159
+ return collect(Object.values(tiers));
160
+ if (Array.isArray(tiers)) {
161
+ return collect(tiers.map((row) => (Array.isArray(row) && row.length > 0 ? row[row.length - 1] : row)));
162
+ }
163
+ return [];
164
+ }
165
+ /**
166
+ * The LOWEST leverage the wallet sizes a position at (worst case for notional): the lowest of every
167
+ * explicit tier, tiered-margin std, and conservative multiplier across all scanners; else the
168
+ * declared default; else 1. A `minLeverage` clamp-floor is deliberately NOT used — it is a safety
169
+ * clamp, not a configured tier.
170
+ */
171
+ function minLeverage(strategy, inputsList) {
172
+ const candidates = [];
173
+ for (const inputs of inputsList) {
174
+ candidates.push(...leverageValues(inputs.leverageTiers));
175
+ const std = toFloat(inputs.stdLeverage);
176
+ if (std !== null && std > 0)
177
+ candidates.push(std);
178
+ }
179
+ const multipliers = strategy.leverage_multipliers;
180
+ if (isRecord(multipliers)) {
181
+ const conservative = toFloat(multipliers.conservative);
182
+ if (conservative)
183
+ candidates.push(conservative);
184
+ }
185
+ if (candidates.length > 0)
186
+ return Math.min(...candidates);
187
+ const declared = toFloat(strategy.default_leverage);
188
+ return declared !== null && declared > 0 ? declared : 1.0;
189
+ }
190
+ function perWalletMin(margin, leverage) {
191
+ return Math.max(WALLET_FLOOR, BUMPED_NOTIONAL / ((margin / 100.0) * leverage) + FEE_BUFFER);
192
+ }
193
+ /**
194
+ * PURE. `manifest` is the parsed `strategy.yaml`; `runtimes` maps instance name → parsed
195
+ * `runtime.yaml`. Mirrors the Python's full return shape (camelCased).
196
+ */
197
+ export function strategyMinBudget(manifest, runtimes) {
198
+ const instances = manifest.instances ?? [];
199
+ const catalog = manifest.catalog ?? {};
200
+ const perWalletOverShare = [];
201
+ const breakdown = [];
202
+ const unresolvedWallets = [];
203
+ for (const instance of instances) {
204
+ const name = typeof instance.name === "string" ? instance.name : String(instance.name ?? "");
205
+ const runtime = runtimes[name] ?? {};
206
+ const strategy = isRecord(runtime.strategy) ? runtime.strategy : {};
207
+ const inputsList = externalScannerInputs(runtime);
208
+ const margin = marginPct(strategy, inputsList);
209
+ const leverage = minLeverage(strategy, inputsList);
210
+ const declaredShare = toFloat(instance.funding_share);
211
+ // A 0 or absent share is treated as the whole book — the fail-closed direction, because
212
+ // dividing by a share smaller than the truth would OVERSTATE the minimum and over-warn.
213
+ const fundingShare = declaredShare !== null && declaredShare > 0 ? declaredShare : 1.0;
214
+ let walletMin;
215
+ if (margin) {
216
+ walletMin = perWalletMin(margin, leverage);
217
+ }
218
+ else {
219
+ walletMin = WALLET_FLOOR;
220
+ unresolvedWallets.push(name);
221
+ }
222
+ perWalletOverShare.push(walletMin / fundingShare);
223
+ breakdown.push({
224
+ wallet: name,
225
+ marginPct: margin,
226
+ minLeverage: leverage,
227
+ fundingShare,
228
+ perWalletMin: round2(walletMin),
229
+ });
230
+ }
231
+ const walletCount = instances.length;
232
+ const raw = perWalletOverShare.length > 0 ? Math.max(...perWalletOverShare) : WALLET_FLOOR;
233
+ const floored = Math.max(raw, WALLET_FLOOR * Math.max(walletCount, 1));
234
+ let minBudget = roundStep(floored);
235
+ // A deliberate authored number RAISES only, and is never shrunk by the rounding.
236
+ const floorOverride = toFloat(catalog.min_budget_floor);
237
+ if (floorOverride)
238
+ minBudget = Math.max(minBudget, floorOverride);
239
+ // The binding sleeve is the one whose ROUNDED per-wallet minimum over its share is largest.
240
+ // Python's `max(…, key=…)` keeps the FIRST row on a tie, which `>` (never `>=`) preserves.
241
+ const weightOf = (row) => row.perWalletMin / (row.fundingShare || 1.0);
242
+ let binding = null;
243
+ for (const row of breakdown) {
244
+ if (binding === null || weightOf(row) > weightOf(binding))
245
+ binding = row;
246
+ }
247
+ return {
248
+ minBudget,
249
+ walletCount,
250
+ bindingWallet: binding?.wallet ?? null,
251
+ unresolvedWallets,
252
+ raw: round2(floored),
253
+ breakdown,
254
+ };
255
+ }
256
+ /**
257
+ * The same calculation over a loaded package — the port of `deploy.py`'s `strategy_min(pkg)`. The
258
+ * package's own YAML is the only input, because a custom-authored package never passes through
259
+ * catalog generation: "what the card promised" and "what deploy warns about" are one function.
260
+ *
261
+ * A runtime.yaml that cannot be parsed contributes an EMPTY document rather than throwing: this is
262
+ * an advisory warning computed after the money gates have already passed, and it must never be the
263
+ * thing that fails a deploy. Such a sleeve lands in `unresolvedWallets`, which is exactly the
264
+ * "may be understated" warning.
265
+ *
266
+ * **An absent `funding_share` resolves to `1/n`, not to the Python's `1.0`** — see the header's
267
+ * documented-deviation list. The adapter is the right place for it: `strategyMinBudget` keeps
268
+ * byte-parity with the reference, and this layer is where the manifest meets the verb that will
269
+ * actually spend the money.
270
+ */
271
+ export function packageMinBudget(pkg) {
272
+ const runtimes = {};
273
+ for (const inst of pkg.instances) {
274
+ try {
275
+ const parsed = parseYaml(readRuntimeYaml(inst));
276
+ runtimes[inst.name] = isRecord(parsed) ? parsed : {};
277
+ }
278
+ catch {
279
+ runtimes[inst.name] = {};
280
+ }
281
+ }
282
+ // The even split `sharesOf` (funding.ts) would apply to a FRESH deploy of this package — which is
283
+ // exactly what this figure means: "what deploying the whole thing costs". It is deliberately NOT
284
+ // the split of a partially-adopted re-run, where `planFunding` divides among the instances still
285
+ // NEEDING a wallet and the denominator is smaller. Do not "fix" the two to match: they answer
286
+ // different questions, and the caller compares per-wallet allocations against `breakdown[]`
287
+ // rather than comparing a budget to this total (orchestrator.ts `applyCalculatedMinimum`).
288
+ const evenSplit = 1 / pkg.instances.length;
289
+ return strategyMinBudget({
290
+ instances: pkg.instances.map((inst) => ({
291
+ name: inst.name,
292
+ funding_share: inst.fundingShare ?? evenSplit,
293
+ })),
294
+ catalog: pkg.catalog,
295
+ }, runtimes);
296
+ }
297
+ //# sourceMappingURL=min-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"min-budget.js","sourceRoot":"","sources":["../../src/deploy/min-budget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,305 @@
1
+ import type { StrategySummary } from "../senpi/types.js";
2
+ import { type DeployPackage } from "./package.js";
3
+ export type JobPhase = "reconcile" | "preflight" | "create" | "install" | "observe";
4
+ export type StepName = "reconcile" | "preflight" | "create" | "install" | "observe";
5
+ export type StepStatus = "ok" | "skipped" | "pending" | "refused" | "failed" | "unobserved";
6
+ /** Emitted by runDeploy at every transition; seq/ts are stamped by the journaling layer (job.ts). */
7
+ export interface DeployEventBody {
8
+ type: "job" | "step";
9
+ /** job transitions */
10
+ phase?: JobPhase;
11
+ /** step outcomes */
12
+ instance?: string;
13
+ step?: StepName;
14
+ status?: StepStatus;
15
+ /** the SAME string the report carries */
16
+ detail: string;
17
+ /** verbatim surface quotes (totalFunded, scanner row…) */
18
+ evidence?: Record<string, unknown>;
19
+ }
20
+ export interface DeployEvent extends DeployEventBody {
21
+ seq: number;
22
+ ts: string;
23
+ }
24
+ /** The scanner-row fields the observe step reads; narrowed from `ScannerRegistrationSystemState`. */
25
+ export interface ScannerRowLite {
26
+ scannerId: string;
27
+ health: string;
28
+ enabled: boolean;
29
+ /** "interval" (built-in) vs "external" (supervised push) — decides which liveness fact applies */
30
+ scheduleMode: string;
31
+ lastRunStatus: string | null;
32
+ /** epoch ms; NAME-keyed telemetry survives a supervisor re-mint, so it MUST be freshness-checked */
33
+ lastRunFinishedAt: number | null;
34
+ /** epoch ms; ID-keyed external liveness — resets to null on re-mint, so freshness-safe by construction */
35
+ lastAliveAt: number | null;
36
+ nextRunAt: number | null;
37
+ runCount: number;
38
+ errorCount: number;
39
+ signalsProduced: number;
40
+ lastError: string | null;
41
+ }
42
+ export interface RegistryEntryLite {
43
+ id: string;
44
+ wallet?: string;
45
+ }
46
+ export interface DeployDeps {
47
+ listStrategies(filters?: {
48
+ strategyIds?: string[];
49
+ }): Promise<StrategySummary[]>;
50
+ createCustomStrategy(p: {
51
+ initialBudget: number;
52
+ strategyName?: string;
53
+ skillName: string;
54
+ skillVersion: string;
55
+ }): Promise<{
56
+ success: boolean;
57
+ strategyId?: string;
58
+ message?: string;
59
+ }>;
60
+ /** already forceFetch:true inside */
61
+ getPortfolio(): Promise<Record<string, unknown> | null>;
62
+ /**
63
+ * Non-delisted live instrument names (MCP `market_list_instruments` via
64
+ * `client.listInstrumentNames`). Same shape as `UniverseCheckDeps` — the deps object feeds
65
+ * `checkPackageUniverse` directly. [] = the list could not be read; the check treats empty as
66
+ * unavailable (fail-closed), NEVER as "everything is dead".
67
+ */
68
+ listLiveInstruments(): Promise<string[]>;
69
+ installRuntime(p: {
70
+ runtimeYamlContent: string;
71
+ runtimeYamlDir: string;
72
+ id: string;
73
+ }): Promise<{
74
+ ok: boolean;
75
+ /**
76
+ * `unwired` is the phase at which the runtime lost its entry scanners (`install_wire` /
77
+ * `install_launch` / `no_intake`). The install itself succeeded; the runtime is BLIND. Deploy
78
+ * refuses to call that a successful install — see {@link buildUnwiredInstallDetail}.
79
+ */
80
+ payload?: {
81
+ id: string;
82
+ name: string;
83
+ wallet: string;
84
+ unwired?: string;
85
+ };
86
+ error?: {
87
+ code: string;
88
+ message: string;
89
+ };
90
+ }>;
91
+ deleteRuntime(id: string): Promise<void>;
92
+ registryFindByWallet(wallet: string): Promise<RegistryEntryLite | undefined>;
93
+ registryFindById(id: string): Promise<RegistryEntryLite | undefined>;
94
+ getScannerRows(runtimeId: string): Promise<ScannerRowLite[] | null>;
95
+ /** H4: the registry row exists AND its `run()` handle is alive — a row alone proves nothing. */
96
+ runtimeIsLive(id: string): Promise<boolean>;
97
+ /**
98
+ * Wrap one phase in a span. Injected so `runDeploy` stays OTel-free (and pure): tests pass
99
+ * `(_n, _a, fn) => fn()`, register.ts passes `withSpan`. The value the phase fn resolves with is
100
+ * its worst step status — the wiring turns it into the phase metric's `status` dimension.
101
+ */
102
+ span<T>(name: string, attrs: Record<string, unknown>, fn: () => Promise<T>): Promise<T>;
103
+ /** true when the wallet holds zero open positions (client.listOpenPositions(wallet).length === 0) */
104
+ walletIsFlat(wallet: string): Promise<boolean>;
105
+ /** strategy_close via client.closeStrategy(wallet) — returns funds to the owner wallet on its own (D-7) */
106
+ closeStrategy(wallet: string): Promise<unknown>;
107
+ /** narration only — journaled by job.ts; MUST NOT affect flow */
108
+ emit(event: DeployEventBody): void;
109
+ /**
110
+ * The deploy deadline, checked at step boundaries and between poll iterations ONLY. Set by the
111
+ * job layer's wall-clock watchdog — never by a user, because there is no cancel verb (D-4).
112
+ */
113
+ shouldAbandon(): boolean;
114
+ now(): number;
115
+ sleep(ms: number): Promise<void>;
116
+ log(line: string): void;
117
+ }
118
+ export interface DeployParams {
119
+ pkg: DeployPackage;
120
+ budget?: number;
121
+ decisionModel?: string;
122
+ /** wallet-ACTIVE poll budget, default 150 (deploy.py DEFAULT_MAX_WAIT) */
123
+ maxWaitSec?: number;
124
+ /** verified-tick observe budget, default 120; 0 = skip */
125
+ tickWaitSec?: number;
126
+ /**
127
+ * C2 lookup keys: instance name → strategyIds this box journaled at create time (newest first),
128
+ * from `DeployJobController.priorCreatedStrategyIds`. Used ONLY when the name match finds
129
+ * nothing, and only after `listStrategies({ strategyIds })` confirms the id is still live — a
130
+ * journal supplies the key, the backend supplies the decision.
131
+ */
132
+ priorStrategyIds?: Map<string, string[]>;
133
+ /**
134
+ * D-9 lookup input, second form: strategyId → the amount a PRIOR run of this package journaled as
135
+ * its ask for that strategy, from `DeployJobController.priorRequestedFunding`.
136
+ *
137
+ * Why it exists: the ask lives only in the memory of the run that funded (`run.fundAmount`), so a
138
+ * deploy that funds $60 of a $500 ask and dies before assembling its report resumes as an
139
+ * ADOPTION — and the funded-vs-requested comparison never runs at all. The report reads `live`
140
+ * over a 12%-sized book, which is exactly the silent case this whole check exists to close.
141
+ *
142
+ * It stays inside the journal doctrine. The journal supplies ONE narration input — the ask — and
143
+ * nothing else: `funded` is always re-read live from `strategy_list` on this run, no orchestration
144
+ * branch consults it (no create, no close, no funding decision changes), and its only consumer is
145
+ * a warn. A stale or missing entry costs a warn, never an action.
146
+ */
147
+ priorRequested?: Map<string, number>;
148
+ }
149
+ export interface StepOutcome {
150
+ status: StepStatus;
151
+ detail: string;
152
+ evidence?: Record<string, unknown>;
153
+ }
154
+ export interface InstanceReport {
155
+ instance: string;
156
+ wallet?: string;
157
+ strategyId?: string;
158
+ funded?: number;
159
+ runtimeId?: string;
160
+ /** D-6: this job created the wallet, its install failed, and the close+refund completed. */
161
+ rolledBack?: boolean;
162
+ steps: {
163
+ reconcile: StepOutcome;
164
+ preflight: StepOutcome;
165
+ create: StepOutcome;
166
+ install: StepOutcome;
167
+ observe: StepOutcome;
168
+ };
169
+ }
170
+ export interface DeployReport {
171
+ packageId: string;
172
+ version: string;
173
+ overall: "live" | "installed-unobserved" | "pending" | "refused" | "failed";
174
+ instances: InstanceReport[];
175
+ next?: string;
176
+ /**
177
+ * What deploying the WHOLE package fresh would need (see {@link packageMinBudget}), present only
178
+ * when this deploy actually planned to fund a new wallet.
179
+ *
180
+ * CONTEXT, not the claim: on a partially-adopted deploy the budget is split only among the
181
+ * instances still needing a wallet, so this figure is not the yardstick for what any one wallet
182
+ * receives — {@link DeployReport.belowMin} is. Read them as "the package wants $30 across 2
183
+ * wallets" and "…and something this deploy funded came up short", separately.
184
+ *
185
+ * The whole `min*` group below is ADVISORY: it never changes `overall`, never produces a
186
+ * `refused` step, and never stops anything. The hard money gate is the {@link MIN_WALLET} ×
187
+ * wallets floor, which refuses through the `preflight` step like every other refusal.
188
+ */
189
+ minBudget?: number;
190
+ /** Wallets {@link DeployReport.minBudget} is spread across — every instance, adopted ones included. */
191
+ minWalletCount?: number;
192
+ /**
193
+ * true = the funding PLAN allocated at least one wallet less than its own sizing needs.
194
+ *
195
+ * A claim about the plan, deliberately, and it stays true on a deploy that then failed before
196
+ * creating anything: the budget really was sized too small, and that is the fact worth counting
197
+ * (an agent sizing budgets badly is the signal; whether an unrelated backend error followed is
198
+ * not). The NOTE marks the difference in tense — "would have funded" when nothing landed — so the
199
+ * prose never claims money moved, while the metric keeps the whole population.
200
+ *
201
+ * Deliberately NOT "the budget is below `minBudget`": those differ whenever some instances were
202
+ * adopted, because the plan splits the budget among fewer wallets than the package minimum
203
+ * assumes.
204
+ *
205
+ * Set whenever it holds, INCLUDING alongside `minBudgetUnresolved` — the two are not exclusive.
206
+ * Suppressing it on the unresolved path made the telemetry assert the opposite of the truth.
207
+ * Which note leads is a separate decision (see `softLead`).
208
+ */
209
+ belowMin?: boolean;
210
+ /**
211
+ * The `[W_BUDGET_BELOW_STRATEGY_MIN]` / `[W_BUDGET_UNRESOLVED]` warn, verbatim. Exactly one code
212
+ * leads; when both conditions hold, the unresolved note leads and carries the shortfall sentence.
213
+ */
214
+ minBudgetNote?: string;
215
+ /** Sleeves whose sizing did not resolve — the minimum above may be understated. */
216
+ minBudgetUnresolved?: string[];
217
+ /**
218
+ * The `[W_BUDGET_PARTIAL_FUND]` warn, verbatim: the backend landed materially less on a wallet
219
+ * than this deploy asked it to.
220
+ *
221
+ * Independent of the whole `min*` group above and can ride the same report. Those judge the
222
+ * funding PLAN against the package's own sizing BEFORE anything is created; this one judges what
223
+ * the backend actually did, by comparing the post-create strategy read's `totalFunded` against
224
+ * the amount `planFunding` allocated. Nothing else on the report closes that loop — the create
225
+ * step quotes `totalFunded` as a bare fact, so a $500 ask that lands $60 used to report `live`.
226
+ *
227
+ * ADVISORY, like the soft tier: the strategy is live and trading, so `overall` and the exit code
228
+ * are untouched. It is emphatically NOT a `not-live` verdict — the wallet holds real money, and a
229
+ * report calling it not-live invites the teardown of a funded, running strategy.
230
+ */
231
+ partialFundNote?: string;
232
+ /**
233
+ * The `[W_BUDGET_FUNDED_UNREADABLE]` advisory, verbatim: the same OUTCOME comparison as
234
+ * {@link partialFundNote}, on a wallet whose funded amount the backend did not report.
235
+ *
236
+ * Its own code rather than a variant of the warn above, because the two steer opposite actions:
237
+ * the partial-fund warn names a shortfall and a top-up, this one forbids both and routes to the
238
+ * USER to establish the real figure. Mutually exclusive per wallet by construction.
239
+ */
240
+ fundedUnreadableNote?: string;
241
+ }
242
+ /**
243
+ * Record that an instance is bound to `strategyId` — the WRITE half of the reconcile claim ledger
244
+ * (see `claimed` in {@link runDeploy}).
245
+ *
246
+ * An unreadable id is not claimable. `listStrategies` coerces a missing or unparseable id to `""`
247
+ * (senpi/client.ts), and a `""` in the ledger answers "already accounted for" for every OTHER
248
+ * unreadable-id wallet in the same read — which is exactly how a second live funded wallet became
249
+ * invisible to the gate. Not recording it is the fail-closed direction: the gate keeps presenting
250
+ * it and the run refuses rather than funding beside it.
251
+ *
252
+ * **Either this guard or {@link isAccountedFor}'s alone closes that hole; both are kept
253
+ * deliberately.** Delete this one and the suite still passes on the deploy paths — the read guard
254
+ * covers it. That is defence in depth, not dead code, and `orchestrator.test.ts` pins each half
255
+ * separately so neither can be removed as redundant.
256
+ */
257
+ export declare function claimStrategyId(claimed: Set<string>, strategyId: string | undefined): void;
258
+ /**
259
+ * Is this live strategy PROVEN to be accounted for by an instance of this run? — the READ half of
260
+ * the same ledger.
261
+ *
262
+ * Fail-closed on an unreadable id: it can never have been claimed and can never be matched, so it
263
+ * can never be proven accounted for. Resting that on `claimed.has("")` happening to answer false is
264
+ * not a property to give a money gate — it is only true while the write half holds. Same rule this
265
+ * file already applies to an unreadable `totalFunded` and an unreadable `skillName`: unknown is
266
+ * never zero, and unknown is never "not ours".
267
+ *
268
+ * **Either this guard or {@link claimStrategyId}'s alone closes the hole; both are kept
269
+ * deliberately** — see there.
270
+ */
271
+ export declare function isAccountedFor(claimed: ReadonlySet<string>, strategyId: string | undefined): boolean;
272
+ /**
273
+ * Run one phase inside an injected span, surviving a tracer that throws.
274
+ *
275
+ * Same rule as narration's `emit` and `log` — telemetry may not decide what a deploy does — but
276
+ * with the largest blast radius of the three: the span wraps EVERY phase, including create and
277
+ * install, so a throwing tracer escaped `runDeploy` after a wallet had been funded and left the job
278
+ * layer synthesizing an empty `instances: []` report over a live funded wallet.
279
+ *
280
+ * It is deliberately NOT "on failure, run the body": **the body is only safe to run once.** A
281
+ * tracer that fails while ENDING a span has already run it, and re-running `deploy.create` is a
282
+ * second funded wallet — telemetry causing the exact loss this file is built to prevent. So the
283
+ * body is invoked through a one-shot handle and the two cases are told apart by whether it fired:
284
+ *
285
+ * - the tracer failed BEFORE reaching the body — run it here, un-spanned, and return its value;
286
+ * - the tracer failed AFTER the body ran — the body's own outcome is the truth, including its
287
+ * rejection if it had one. A tracer that masks a failing body with an error of its own therefore
288
+ * cannot hide the real cause: what surfaces is the body's error, not the tracer's.
289
+ *
290
+ * Module-scope and exported for ONE reason: the synchronous-throw edge below is unreachable through
291
+ * `runDeploy` (every call site passes an async arrow), so it can only be pinned here.
292
+ *
293
+ * @param spanFn the injected tracer — assumed to call `handle` at most once; calling it more often
294
+ * is harmless, since the handle collapses to the first invocation either way
295
+ * @param log narration for the degraded path; must not throw (callers pass the guarded wrapper)
296
+ * @returns whatever the body resolves with — never the tracer's own value on a failure
297
+ */
298
+ export declare function spanOnce<T>(spanFn: DeployDeps["span"], log: (line: string) => void, name: string, attrs: Record<string, unknown>, body: () => Promise<T>): Promise<T>;
299
+ /**
300
+ * Sequential orchestration of one deploy. Resolves with a report in every case —
301
+ * refusals and failures are reported, never thrown, so the job layer always has
302
+ * a truthful terminal state to journal.
303
+ */
304
+ export declare function runDeploy(params: DeployParams, deps: DeployDeps): Promise<DeployReport>;
305
+ //# sourceMappingURL=orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/deploy/orchestrator.ts"],"names":[],"mappings":"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"}