@senpi/runtime 3.0.127 → 3.0.129

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 (267) hide show
  1. package/README.md +4 -12
  2. package/dist/actions/action-events.d.ts +7 -21
  3. package/dist/actions/action-events.d.ts.map +1 -1
  4. package/dist/actions/action-events.js +26 -54
  5. package/dist/actions/action-events.js.map +1 -1
  6. package/dist/actions/action-runtime-module.d.ts.map +1 -1
  7. package/dist/actions/action-runtime-module.js +7 -38
  8. package/dist/actions/action-runtime-module.js.map +1 -1
  9. package/dist/actions/close-position/close-position.action.d.ts.map +1 -1
  10. package/dist/actions/close-position/close-position.action.js +15 -122
  11. package/dist/actions/close-position/close-position.action.js.map +1 -1
  12. package/dist/actions/decision-engine.d.ts.map +1 -1
  13. package/dist/actions/decision-engine.js +2 -15
  14. package/dist/actions/decision-engine.js.map +1 -1
  15. package/dist/actions/event-derivation.d.ts +2 -71
  16. package/dist/actions/event-derivation.d.ts.map +1 -1
  17. package/dist/actions/event-derivation.js +8 -251
  18. package/dist/actions/event-derivation.js.map +1 -1
  19. package/dist/actions/open-position/open-position.action.d.ts +2 -17
  20. package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
  21. package/dist/actions/open-position/open-position.action.js +47 -162
  22. package/dist/actions/open-position/open-position.action.js.map +1 -1
  23. package/dist/actions/position-tracker/position-tracker.action.d.ts +1 -2
  24. package/dist/actions/position-tracker/position-tracker.action.d.ts.map +1 -1
  25. package/dist/actions/position-tracker/position-tracker.action.js +17 -62
  26. package/dist/actions/position-tracker/position-tracker.action.js.map +1 -1
  27. package/dist/actions/store/types.d.ts +2 -9
  28. package/dist/actions/store/types.d.ts.map +1 -1
  29. package/dist/actions/store/types.js.map +1 -1
  30. package/dist/actions/types.d.ts +0 -13
  31. package/dist/actions/types.d.ts.map +1 -1
  32. package/dist/actions/types.js +0 -13
  33. package/dist/actions/types.js.map +1 -1
  34. package/dist/cli/guide-commands.d.ts.map +1 -1
  35. package/dist/cli/guide-commands.js +6 -7
  36. package/dist/cli/guide-commands.js.map +1 -1
  37. package/dist/cli/senpi-commands.d.ts.map +1 -1
  38. package/dist/cli/senpi-commands.js +11 -4
  39. package/dist/cli/senpi-commands.js.map +1 -1
  40. package/dist/dsl/engine-client/event-mapping.d.ts.map +1 -1
  41. package/dist/dsl/engine-client/event-mapping.js +0 -14
  42. package/dist/dsl/engine-client/event-mapping.js.map +1 -1
  43. package/dist/dsl/events/build-closed-event.d.ts +1 -16
  44. package/dist/dsl/events/build-closed-event.d.ts.map +1 -1
  45. package/dist/dsl/events/build-closed-event.js +0 -6
  46. package/dist/dsl/events/build-closed-event.js.map +1 -1
  47. package/dist/dsl/events/handlers.d.ts +0 -7
  48. package/dist/dsl/events/handlers.d.ts.map +1 -1
  49. package/dist/dsl/events/handlers.js +7 -37
  50. package/dist/dsl/events/handlers.js.map +1 -1
  51. package/dist/dsl/monitor/create-monitor.d.ts +2 -6
  52. package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
  53. package/dist/dsl/monitor/create-monitor.js +9 -15
  54. package/dist/dsl/monitor/create-monitor.js.map +1 -1
  55. package/dist/dsl/monitor/dsl-failure-events.d.ts +0 -2
  56. package/dist/dsl/monitor/dsl-failure-events.d.ts.map +1 -1
  57. package/dist/dsl/monitor/dsl-failure-events.js +1 -2
  58. package/dist/dsl/monitor/dsl-failure-events.js.map +1 -1
  59. package/dist/dsl/monitor/process-one-position.d.ts +0 -1
  60. package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
  61. package/dist/dsl/monitor/process-one-position.js +25 -59
  62. package/dist/dsl/monitor/process-one-position.js.map +1 -1
  63. package/dist/dsl/plugin/index.d.ts +2 -8
  64. package/dist/dsl/plugin/index.d.ts.map +1 -1
  65. package/dist/dsl/plugin/index.js +6 -8
  66. package/dist/dsl/plugin/index.js.map +1 -1
  67. package/dist/health/types.d.ts +0 -13
  68. package/dist/health/types.d.ts.map +1 -1
  69. package/dist/health/types.js +0 -12
  70. package/dist/health/types.js.map +1 -1
  71. package/dist/index.d.ts +4 -27
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +42 -254
  74. package/dist/index.js.map +1 -1
  75. package/dist/runtime/action-registry.d.ts.map +1 -1
  76. package/dist/runtime/action-registry.js +3 -4
  77. package/dist/runtime/action-registry.js.map +1 -1
  78. package/dist/runtime/auto-update/coordinator.d.ts +6 -13
  79. package/dist/runtime/auto-update/coordinator.d.ts.map +1 -1
  80. package/dist/runtime/auto-update/coordinator.js +18 -86
  81. package/dist/runtime/auto-update/coordinator.js.map +1 -1
  82. package/dist/runtime/auto-update/run-update-command.d.ts.map +1 -1
  83. package/dist/runtime/auto-update/run-update-command.js +3 -4
  84. package/dist/runtime/auto-update/run-update-command.js.map +1 -1
  85. package/dist/runtime/auto-update/types.d.ts +5 -11
  86. package/dist/runtime/auto-update/types.d.ts.map +1 -1
  87. package/dist/runtime/auto-update/types.js +0 -11
  88. package/dist/runtime/auto-update/types.js.map +1 -1
  89. package/dist/runtime/domain-events.d.ts.map +1 -1
  90. package/dist/runtime/domain-events.js +0 -7
  91. package/dist/runtime/domain-events.js.map +1 -1
  92. package/dist/runtime/lifecycle-events.d.ts +1 -9
  93. package/dist/runtime/lifecycle-events.d.ts.map +1 -1
  94. package/dist/runtime/lifecycle-events.js +2 -11
  95. package/dist/runtime/lifecycle-events.js.map +1 -1
  96. package/dist/runtime/reconcile-run.d.ts +3 -13
  97. package/dist/runtime/reconcile-run.d.ts.map +1 -1
  98. package/dist/runtime/reconcile-run.js +3 -4
  99. package/dist/runtime/reconcile-run.js.map +1 -1
  100. package/dist/runtime/reconcile-state.d.ts +0 -8
  101. package/dist/runtime/reconcile-state.d.ts.map +1 -1
  102. package/dist/runtime/reconcile-state.js +1 -4
  103. package/dist/runtime/reconcile-state.js.map +1 -1
  104. package/dist/runtime/run.d.ts +0 -1
  105. package/dist/runtime/run.d.ts.map +1 -1
  106. package/dist/runtime/run.js.map +1 -1
  107. package/dist/runtime/runtime-schema.js +3 -3
  108. package/dist/runtime/runtime-schema.js.map +1 -1
  109. package/dist/runtime/runtime.d.ts +0 -1
  110. package/dist/runtime/runtime.d.ts.map +1 -1
  111. package/dist/runtime/runtime.js +3 -27
  112. package/dist/runtime/runtime.js.map +1 -1
  113. package/dist/runtime/supervisor/long-running-supervisor.d.ts +0 -13
  114. package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
  115. package/dist/runtime/supervisor/long-running-supervisor.js +0 -1
  116. package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
  117. package/dist/runtime/validate-dsl-position-tracking.d.ts.map +1 -1
  118. package/dist/runtime/validate-dsl-position-tracking.js +1 -2
  119. package/dist/runtime/validate-dsl-position-tracking.js.map +1 -1
  120. package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -1
  121. package/dist/runtime-api/handlers/ingest-signals.js +0 -1
  122. package/dist/runtime-api/handlers/ingest-signals.js.map +1 -1
  123. package/dist/runtime-api/handlers/types.d.ts +0 -5
  124. package/dist/runtime-api/handlers/types.d.ts.map +1 -1
  125. package/dist/runtime-api/intake/intake-events.d.ts +1 -1
  126. package/dist/runtime-api/intake/intake-events.d.ts.map +1 -1
  127. package/dist/runtime-api/intake/intake-events.js +3 -9
  128. package/dist/runtime-api/intake/intake-events.js.map +1 -1
  129. package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
  130. package/dist/runtime-api/intake/intake-mount.js +19 -4
  131. package/dist/runtime-api/intake/intake-mount.js.map +1 -1
  132. package/dist/runtime-api/intake/process-error-post.d.ts +1 -6
  133. package/dist/runtime-api/intake/process-error-post.d.ts.map +1 -1
  134. package/dist/runtime-api/intake/process-error-post.js +1 -5
  135. package/dist/runtime-api/intake/process-error-post.js.map +1 -1
  136. package/dist/runtime-api/intake/process-signal-post.d.ts +1 -7
  137. package/dist/runtime-api/intake/process-signal-post.d.ts.map +1 -1
  138. package/dist/runtime-api/intake/process-signal-post.js +0 -5
  139. package/dist/runtime-api/intake/process-signal-post.js.map +1 -1
  140. package/dist/runtime-api/intake/scaffold-error-router.js.map +1 -1
  141. package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
  142. package/dist/runtime-api/intake/scanner-launcher.js +0 -15
  143. package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
  144. package/dist/runtime-api/intake/types.d.ts +0 -55
  145. package/dist/runtime-api/intake/types.d.ts.map +1 -1
  146. package/dist/runtime-api/routes/signals.d.ts +0 -2
  147. package/dist/runtime-api/routes/signals.d.ts.map +1 -1
  148. package/dist/runtime-api/routes/signals.js +1 -5
  149. package/dist/runtime-api/routes/signals.js.map +1 -1
  150. package/dist/runtime-api/routes/signals.schema.d.ts +0 -43
  151. package/dist/runtime-api/routes/signals.schema.d.ts.map +1 -1
  152. package/dist/runtime-api/routes/signals.schema.js +0 -43
  153. package/dist/runtime-api/routes/signals.schema.js.map +1 -1
  154. package/dist/runtime-api/server.d.ts +1 -7
  155. package/dist/runtime-api/server.d.ts.map +1 -1
  156. package/dist/runtime-api/server.js +0 -1
  157. package/dist/runtime-api/server.js.map +1 -1
  158. package/dist/scanners/engine/engine.d.ts +1 -22
  159. package/dist/scanners/engine/engine.d.ts.map +1 -1
  160. package/dist/scanners/engine/engine.js +11 -78
  161. package/dist/scanners/engine/engine.js.map +1 -1
  162. package/dist/scanners/engine/lifecycle.d.ts +1 -7
  163. package/dist/scanners/engine/lifecycle.d.ts.map +1 -1
  164. package/dist/scanners/events.d.ts +0 -4
  165. package/dist/scanners/events.d.ts.map +1 -1
  166. package/dist/scanners/events.js +0 -2
  167. package/dist/scanners/events.js.map +1 -1
  168. package/dist/scanners/external/scaffold/delivery.py +25 -194
  169. package/dist/scanners/external/scaffold/scaffold.py +31 -172
  170. package/dist/scanners/external/scaffold/senpi_mcp.py +8 -150
  171. package/dist/scanners/external-scanner-receiver.d.ts +0 -17
  172. package/dist/scanners/external-scanner-receiver.d.ts.map +1 -1
  173. package/dist/scanners/external-scanner-receiver.js +0 -8
  174. package/dist/scanners/external-scanner-receiver.js.map +1 -1
  175. package/dist/scanners/runtime-module.d.ts +0 -1
  176. package/dist/scanners/runtime-module.d.ts.map +1 -1
  177. package/dist/scanners/runtime-module.js +9 -66
  178. package/dist/scanners/runtime-module.js.map +1 -1
  179. package/dist/telemetry/attributes.d.ts +0 -20
  180. package/dist/telemetry/attributes.d.ts.map +1 -1
  181. package/dist/telemetry/attributes.js +0 -24
  182. package/dist/telemetry/attributes.js.map +1 -1
  183. package/dist/telemetry/event-builders.d.ts +6 -81
  184. package/dist/telemetry/event-builders.d.ts.map +1 -1
  185. package/dist/telemetry/event-builders.js +17 -141
  186. package/dist/telemetry/event-builders.js.map +1 -1
  187. package/dist/telemetry/event-store.d.ts +0 -6
  188. package/dist/telemetry/event-store.d.ts.map +1 -1
  189. package/dist/telemetry/event-store.js +8 -12
  190. package/dist/telemetry/event-store.js.map +1 -1
  191. package/dist/telemetry/metrics.d.ts +2 -14
  192. package/dist/telemetry/metrics.d.ts.map +1 -1
  193. package/dist/telemetry/metrics.js +4 -19
  194. package/dist/telemetry/metrics.js.map +1 -1
  195. package/dist/telemetry/spans/redact-pii.d.ts.map +1 -1
  196. package/dist/telemetry/spans/redact-pii.js +2 -18
  197. package/dist/telemetry/spans/redact-pii.js.map +1 -1
  198. package/dist/telemetry/tracing.d.ts +0 -9
  199. package/dist/telemetry/tracing.d.ts.map +1 -1
  200. package/dist/telemetry/tracing.js +0 -36
  201. package/dist/telemetry/tracing.js.map +1 -1
  202. package/dist/types/dsl/index.d.ts +0 -11
  203. package/dist/types/dsl/index.d.ts.map +1 -1
  204. package/dist/types/scanner.d.ts +0 -15
  205. package/dist/types/scanner.d.ts.map +1 -1
  206. package/dist/utils/event-catalog.d.ts +0 -166
  207. package/dist/utils/event-catalog.d.ts.map +1 -1
  208. package/dist/utils/event-catalog.js +0 -18
  209. package/dist/utils/event-catalog.js.map +1 -1
  210. package/dist/utils/logger.d.ts +1 -4
  211. package/dist/utils/logger.d.ts.map +1 -1
  212. package/dist/utils/logger.js.map +1 -1
  213. package/dist/validate/recipe-checks.d.ts.map +1 -1
  214. package/dist/validate/recipe-checks.js +2 -2
  215. package/dist/validate/recipe-checks.js.map +1 -1
  216. package/openclaw.plugin.json +4 -8
  217. package/package.json +1 -1
  218. package/dist/actions/skipped-signal-results.d.ts +0 -25
  219. package/dist/actions/skipped-signal-results.d.ts.map +0 -1
  220. package/dist/actions/skipped-signal-results.js +0 -71
  221. package/dist/actions/skipped-signal-results.js.map +0 -1
  222. package/dist/runtime-api/intake/ingest-request.d.ts +0 -17
  223. package/dist/runtime-api/intake/ingest-request.d.ts.map +0 -1
  224. package/dist/runtime-api/intake/ingest-request.js +0 -34
  225. package/dist/runtime-api/intake/ingest-request.js.map +0 -1
  226. package/dist/runtime-api/intake/scaffold-tick-router.d.ts +0 -39
  227. package/dist/runtime-api/intake/scaffold-tick-router.d.ts.map +0 -1
  228. package/dist/runtime-api/intake/scaffold-tick-router.js +0 -122
  229. package/dist/runtime-api/intake/scaffold-tick-router.js.map +0 -1
  230. package/dist/scanners/external/scaffold/tick_facts.py +0 -130
  231. package/dist/scanners/external/scaffold/ulid.py +0 -58
  232. package/dist/telemetry/live-telemetry.d.ts +0 -238
  233. package/dist/telemetry/live-telemetry.d.ts.map +0 -1
  234. package/dist/telemetry/live-telemetry.js +0 -853
  235. package/dist/telemetry/live-telemetry.js.map +0 -1
  236. package/dist/telemetry/scanner-stats.d.ts +0 -143
  237. package/dist/telemetry/scanner-stats.d.ts.map +0 -1
  238. package/dist/telemetry/scanner-stats.js +0 -426
  239. package/dist/telemetry/scanner-stats.js.map +0 -1
  240. package/dist/telemetry/scanner-tick-feed.d.ts +0 -87
  241. package/dist/telemetry/scanner-tick-feed.d.ts.map +0 -1
  242. package/dist/telemetry/scanner-tick-feed.js +0 -48
  243. package/dist/telemetry/scanner-tick-feed.js.map +0 -1
  244. package/dist/telemetry/strategy-topology.d.ts +0 -99
  245. package/dist/telemetry/strategy-topology.d.ts.map +0 -1
  246. package/dist/telemetry/strategy-topology.js +0 -93
  247. package/dist/telemetry/strategy-topology.js.map +0 -1
  248. package/dist/telemetry/telemetry-publisher.d.ts +0 -63
  249. package/dist/telemetry/telemetry-publisher.d.ts.map +0 -1
  250. package/dist/telemetry/telemetry-publisher.js +0 -229
  251. package/dist/telemetry/telemetry-publisher.js.map +0 -1
  252. package/dist/telemetry/tick-frame.d.ts +0 -212
  253. package/dist/telemetry/tick-frame.d.ts.map +0 -1
  254. package/dist/telemetry/tick-frame.js +0 -421
  255. package/dist/telemetry/tick-frame.js.map +0 -1
  256. package/dist/telemetry/timeline-buffer.d.ts +0 -95
  257. package/dist/telemetry/timeline-buffer.d.ts.map +0 -1
  258. package/dist/telemetry/timeline-buffer.js +0 -237
  259. package/dist/telemetry/timeline-buffer.js.map +0 -1
  260. package/dist/utils/telemetry-required-log.d.ts +0 -60
  261. package/dist/utils/telemetry-required-log.d.ts.map +0 -1
  262. package/dist/utils/telemetry-required-log.js +0 -105
  263. package/dist/utils/telemetry-required-log.js.map +0 -1
  264. package/dist/utils/ulid.d.ts +0 -9
  265. package/dist/utils/ulid.d.ts.map +0 -1
  266. package/dist/utils/ulid.js +0 -43
  267. package/dist/utils/ulid.js.map +0 -1
package/dist/index.js CHANGED
@@ -64,11 +64,7 @@ import { createIntakeContext } from "./runtime-api/intake/intake-context.js";
64
64
  import { wireRuntime, intakeUrlFor, selectScanners } from "./runtime-api/intake/intake-mount.js";
65
65
  import { ingestDispatchMutex, ingestDispatchKey } from "./runtime-api/intake/ingest-mutex.js";
66
66
  import { buildScaffoldErrorRouter } from "./runtime-api/intake/scaffold-error-router.js";
67
- import { buildScaffoldTickRouter } from "./runtime-api/intake/scaffold-tick-router.js";
68
- import { toIngestRequest } from "./runtime-api/intake/ingest-request.js";
69
67
  import { loadRuntimeConfig } from "./runtime/load-runtime-config.js";
70
- import { createLiveTelemetry } from "./telemetry/live-telemetry.js";
71
- import { resolveTickTelemetry } from "./telemetry/tick-frame.js";
72
68
  /** Initialize all core features (DSL, risk, health) on the given event bus. */
73
69
  export function initCore(bus) {
74
70
  initDsl(bus);
@@ -227,16 +223,6 @@ const senpiRuntimePlugin = {
227
223
  ? "env SENPI_LOG_LEVEL"
228
224
  : "default";
229
225
  pluginLogger.info(`[senpi-runtime] log level: ${startupLogLevel} (source: ${logLevelSourceLabel}). Set plugin config "logLevel" or env SENPI_LOG_LEVEL to debug|info|warn|error to override.`);
230
- // Same shape as the log-level line above, and for the same reason: a box publishing less than
231
- // an operator expects must say so at boot rather than be diagnosed from an empty screen. No env
232
- // var is named because there is none — see `tickTelemetry` on SenpiRuntimePluginConfig.
233
- const tickTelemetry = resolveTickTelemetry(config.tickTelemetry);
234
- if (tickTelemetry.invalid !== undefined) {
235
- pluginLogger.warn(`[senpi-runtime] Ignoring invalid plugin config ${tickTelemetry.invalid} (expected off|summary|verbose); keeping mcp=${tickTelemetry.settings.mcp} state=${tickTelemetry.settings.state}.`);
236
- }
237
- pluginLogger.info(tickTelemetry.settings.enabled
238
- ? `[senpi-runtime] tick telemetry: mcp=${tickTelemetry.settings.mcp} state=${tickTelemetry.settings.state} (source: ${config.tickTelemetry === undefined ? "default" : "plugin config tickTelemetry"}). Set plugin config "tickTelemetry" to off|summary|verbose, or {mcp,state}, to override.`
239
- : `[senpi-runtime] tick telemetry: off (source: plugin config tickTelemetry). No per-tick frame is published.`);
240
226
  const senpiCliStore = readSenpiCliStore();
241
227
  const baseStateDir = resolveSenpiBaseStateDir({
242
228
  store: senpiCliStore,
@@ -369,7 +355,6 @@ const senpiRuntimePlugin = {
369
355
  }
370
356
  let routeAcceptedSignalToRuntime;
371
357
  let routeScaffoldErrorToRuntime;
372
- let routeScaffoldTickToTelemetry;
373
358
  const launchers = new Map();
374
359
  // Runtimes that are UP but whose external scanners never wired (mount/launch/install
375
360
  // failure), keyed runtimeId → failed phase. Populated at the four unwired sites,
@@ -397,149 +382,10 @@ const senpiRuntimePlugin = {
397
382
  * engines, one margin account: the exact state `[E_WALLET_HAS_RUNTIME]` exists to prevent.
398
383
  */
399
384
  const installsInFlight = new Map();
400
- // ── Live telemetry ────────────────────────────────────────────────
401
- // Every runtime this box knows about, running or not, keyed runtimeId. The topology payload has
402
- // to name a stopped strategy too, so this is populated from the registry rather than from
403
- // `runtimeHandles` (which holds only the live ones) and is the source of the two facts the
404
- // registry entry does not carry: the strategy's display name and its declared scanners. Both come
405
- // from the parsed YAML — `name` is the same `config.name` that `RuntimeHealthStatus.runtimeName`
406
- // is built from, so a runtime whose config never parsed falls back to its id rather than to a
407
- // guess, and its scanner list stays empty rather than inventing rows.
408
- const telemetryStrategies = new Map();
409
- const noteTelemetryRuntime = (runtimeId, address) => {
410
- const existing = telemetryStrategies.get(runtimeId);
411
- telemetryStrategies.set(runtimeId, {
412
- address,
413
- name: existing?.name ?? runtimeId,
414
- scanners: existing?.scanners ?? [],
415
- });
416
- };
417
- const noteTelemetryRuntimeConfig = (runtimeId, config) => {
418
- telemetryStrategies.set(runtimeId, {
419
- address: config.strategy?.wallet ?? telemetryStrategies.get(runtimeId)?.address ?? "",
420
- name: config.name,
421
- scanners: config.scanners ?? [],
422
- });
423
- };
424
385
  // Dedicated logger for the runtime-api subtree — required because the
425
386
  // PluginLogger surface (info/warn/error) lacks `debug`, while the
426
387
  // Logger contract used by `runtime-api/` includes it.
427
388
  const apiLogger = createLogger("[senpi-core][api]");
428
- /**
429
- * One gather per publish tick. Health and system state are read together because the two payloads
430
- * need both: the topology reads health and `runtimeName`, the stats read the per-scanner rows,
431
- * and `restartCount` / `degradedReason` live only on the health rows while every other scanner
432
- * field lives only on the system-state rows — so they are joined here on `(runtimeId, scannerId)`.
433
- */
434
- const collectTelemetryFacts = async () => {
435
- const [statuses, states] = await Promise.all([
436
- collectRuntimeHealthStatuses(runtimeHandles, api.logger),
437
- collectRuntimeSystemStates(runtimeHandles, api.logger, baseStateDir),
438
- ]);
439
- const healthByRuntime = new Map();
440
- for (const status of statuses) {
441
- if (status.runtimeId !== undefined)
442
- healthByRuntime.set(status.runtimeId, status);
443
- }
444
- const scannerState = [];
445
- // Registration facts per (runtimeId, scannerId), so the topology can report a scanner's live
446
- // schedule rather than only what its YAML declared.
447
- const registrations = new Map();
448
- for (const state of states) {
449
- const runtimeId = state.runtimeId;
450
- const healthRows = runtimeId === undefined
451
- ? []
452
- : healthByRuntime.get(runtimeId)?.components.scanners.scanners ?? [];
453
- for (const row of state.components.scanners.state.scanners) {
454
- const supervision = healthRows.find((r) => r.scannerId === row.scannerId);
455
- const snapshot = {
456
- address: row.address,
457
- scannerId: row.scannerId,
458
- health: row.health,
459
- degradedReason: supervision?.degradedReason ?? null,
460
- enabled: row.enabled,
461
- inFlightSince: row.inFlightSince,
462
- lastRunStartedAt: row.lastRunStartedAt,
463
- lastRunFinishedAt: row.lastRunFinishedAt,
464
- lastRunStatus: row.lastRunStatus,
465
- lastRunReason: row.lastRunReason,
466
- nextRunAt: row.nextRunAt,
467
- lastAliveAt: row.lastAliveAt,
468
- // No supervision row means the restart count is unknown, not zero. A zero here would
469
- // read as "this scanner has never restarted" on a scanner nobody is supervising.
470
- restartCount: supervision?.restartCount ?? null,
471
- };
472
- scannerState.push(snapshot);
473
- if (runtimeId !== undefined) {
474
- registrations.set(`${runtimeId}::${row.scannerId}`, {
475
- ...snapshot,
476
- scheduleMode: row.scheduleMode,
477
- configVersion: row.configVersion,
478
- intervalSeconds: row.intervalSeconds,
479
- });
480
- }
481
- }
482
- }
483
- const strategies = [];
484
- for (const [runtimeId, entry] of telemetryStrategies) {
485
- const health = healthByRuntime.get(runtimeId);
486
- const scanners = entry.scanners.map((declared) => {
487
- const live = registrations.get(`${runtimeId}::${declared.name}`);
488
- return live === undefined
489
- ? { entry: declared }
490
- : {
491
- entry: declared,
492
- registration: {
493
- enabled: live.enabled,
494
- scheduleMode: live.scheduleMode,
495
- // The engine's own cadence, which is what the scanner actually polls on — a
496
- // recipe may omit it and take the scanner definition's default. An external
497
- // scanner registers zero because it is POSTed to rather than polled, and only
498
- // the recipe knows the cadence its scaffold was told to tick at.
499
- intervalSeconds: live.intervalSeconds > 0 ? live.intervalSeconds : declared.interval_seconds ?? null,
500
- configVersion: live.configVersion,
501
- },
502
- };
503
- });
504
- strategies.push({
505
- runtimeId,
506
- address: entry.address,
507
- // `runtimeName` is built from the same `config.name`; the indexed value is used because it
508
- // is available for a stopped runtime too, where there is no health status to read.
509
- name: health?.runtimeName ?? entry.name,
510
- scanners,
511
- });
512
- }
513
- return {
514
- strategies,
515
- runningRuntimeIds: runtimeHandles,
516
- unwiredRuntimeIds: unwiredRuntimes,
517
- readHealth: (runtimeId) => healthByRuntime.get(runtimeId),
518
- scannerState,
519
- };
520
- };
521
- const liveTelemetry = createLiveTelemetry({
522
- collectFacts: collectTelemetryFacts,
523
- tickTelemetry: config.tickTelemetry,
524
- });
525
- /**
526
- * Wrapped gateway methods capture the broadcast handle on the FIRST request to ANY of them —
527
- * not only on subscribe. `ctx.context` is the only route to the websocket push layer and it
528
- * exists only inside a handler, so capturing it in the subscribe method alone would mean a box
529
- * whose client never subscribes can never publish anything, silently.
530
- *
531
- * The wrapper covers only what is registered through it. `senpi.deploy.start`,
532
- * `senpi.deploy.status` and `senpi.update` are registered straight onto the plugin api by their
533
- * own registrars and never arm the handle. That is not a gap in practice: one wrapped call arms
534
- * it for the life of the process, and the websocket client admits `senpi.telemetry.subscribe`,
535
- * which is wrapped.
536
- */
537
- const registerGatewayMethod = (name, handler, opts) => {
538
- api.registerGatewayMethod(name, (ctx) => {
539
- liveTelemetry.captureBroadcast(ctx, name);
540
- return handler(ctx);
541
- }, opts);
542
- };
543
389
  // A gateway seam failed to wire/launch a runtime's external scanner stack: the runtime is up but
544
390
  // runs blind on internal scanners only. Two records, by design: a first-class `runtime.scanners_unwired`
545
391
  // event (the queryable/alertable fact, by phase) and the migrated exception-form `logger.error`
@@ -713,14 +559,26 @@ const senpiRuntimePlugin = {
713
559
  // /signals will not be mounted and no signals will be routed.
714
560
  if (sharedIntakeContext) {
715
561
  const ctx = sharedIntakeContext;
716
- routeAcceptedSignalToRuntime = (signal, correlationId, scannerId) => {
562
+ routeAcceptedSignalToRuntime = (signal, _correlationId, scannerId) => {
717
563
  const resolved = ctx.registry.resolve(scannerId);
718
564
  if (!resolved)
719
565
  return;
720
566
  const h = runtimeHandles.get(resolved.runtimeId);
721
567
  if (!h)
722
568
  return;
723
- const request = toIngestRequest(signal, correlationId, resolved.scannerName);
569
+ const request = {
570
+ scanner: resolved.scannerName,
571
+ signal_id: signal.signal_id, // greppable handle across intake→ingest (obs only)
572
+ asset: signal.asset,
573
+ direction: signal.direction,
574
+ ...(signal.signal_type !== undefined && { signal_type: signal.signal_type }),
575
+ data: signal.data,
576
+ // Per-signal sizing rides at the top level; the open-position action
577
+ // reads request.marginPct / request.leverage directly. Without this a
578
+ // supervised signal's sizing is lost ("no margin source resolved").
579
+ ...(signal.marginPct !== undefined && { marginPct: signal.marginPct }),
580
+ ...(signal.leverage !== undefined && { leverage: signal.leverage }),
581
+ };
724
582
  // Trace the sizing-relevant fields the narrowed request carries, so a
725
583
  // field dropped at this seam is visible instead of vanishing silently.
726
584
  apiLogger.debug(`[intake] ingest req signal_id=${signal.signal_id} scanner=${resolved.scannerName} asset=${signal.asset} direction=${signal.direction} marginPct=${signal.marginPct} leverage=${signal.leverage}`);
@@ -740,18 +598,15 @@ const senpiRuntimePlugin = {
740
598
  // The single server-level onError closure — the `/errors` counterpart of
741
599
  // routeAcceptedSignalToRuntime. It records a scaffold-reported tick failure into
742
600
  // the owning runtime's scanner telemetry so a scanner that fails every tick stops
743
- // rendering "healthy". Filter: only tick-level failure types count — a
744
- // `candidate_rejected` body is a per-candidate reject on an otherwise-OK tick, not
745
- // a failed run.
601
+ // rendering "healthy".
746
602
  //
747
- // This is the SHARED implementation (scaffold-error-router.ts), so this route and
748
- // buildIntakeMount's onScaffoldError apply one filter, and scaffold-error-router.test.ts
749
- // drives THIS closure.
603
+ // This is the SHARED implementation (scaffold-error-router.ts), not a copy of
604
+ // buildIntakeMount's onScaffoldError. It used to be a copy, and the copy had its
605
+ // own hardcoded three-type filter: when `mcp_error` was added to the tick-failure
606
+ // list it reached the tested seam and never reached this route, so the
607
+ // wedged-scanner signal was green in CI and inert in production. There is now one
608
+ // filter, and scaffold-error-router.test.ts drives THIS closure.
750
609
  routeScaffoldErrorToRuntime = buildScaffoldErrorRouter(ctx, runtimeHandles);
751
- // The per-tick level for a supervised external scanner. Both intake routes call it, so a
752
- // quiet tick and a failed tick are equally visible; it needs no runtime handle because it
753
- // publishes a level rather than recording a run.
754
- routeScaffoldTickToTelemetry = buildScaffoldTickRouter(ctx);
755
610
  }
756
611
  // One bounded read: which registered wallets the platform says are dead, so a restart does
757
612
  // not re-scaffold runtimes on closed wallets (fail-open — see runtime/boot-skip.ts).
@@ -803,9 +658,6 @@ const senpiRuntimePlugin = {
803
658
  continue;
804
659
  const pathOrContent = entry.runtimeYamlPath ?? entry.runtimeYamlContent;
805
660
  const entryStateDir = buildRuntimeStateDir(baseStateDir, entry.id, entry.wallet ?? "");
806
- // Indexed before the boot attempt: a runtime whose start throws is installed but stopped,
807
- // and the telemetry topology has to be able to say so.
808
- noteTelemetryRuntime(entry.id, entry.wallet ?? "");
809
661
  const handle = await run(pathOrContent, {
810
662
  fromPath: hasPath,
811
663
  stateDir: entryStateDir,
@@ -829,7 +681,6 @@ const senpiRuntimePlugin = {
829
681
  if (sharedIntakeContext) {
830
682
  try {
831
683
  const runtimeConfig = await loadRuntimeConfig(pathOrContent, { fromPath: hasPath });
832
- noteTelemetryRuntimeConfig(entry.id, runtimeConfig);
833
684
  // Content-installed runtimes carry their recipe dir in the registry
834
685
  // entry (runtimeYamlDir) so relative scanner paths still resolve
835
686
  // after a restart; path-installed ones derive it from the path.
@@ -881,7 +732,6 @@ const senpiRuntimePlugin = {
881
732
  // event) so listRuntimes/health don't render a bare "running".
882
733
  try {
883
734
  const runtimeConfig = await loadRuntimeConfig(pathOrContent, { fromPath: hasPath });
884
- noteTelemetryRuntimeConfig(entry.id, runtimeConfig);
885
735
  if (selectScanners(runtimeConfig).length > 0) {
886
736
  unwiredRuntimes.set(entry.id, "no_intake");
887
737
  handle.attachSupervisionSource(() => ({
@@ -940,7 +790,6 @@ const senpiRuntimePlugin = {
940
790
  intakeContext: sharedIntakeContext,
941
791
  onAccepted: routeAcceptedSignalToRuntime,
942
792
  onError: routeScaffoldErrorToRuntime,
943
- onTick: routeScaffoldTickToTelemetry,
944
793
  });
945
794
  apiHandle.ready();
946
795
  }
@@ -984,12 +833,6 @@ const senpiRuntimePlugin = {
984
833
  }
985
834
  }
986
835
  }
987
- // Publish ticks start only once the runtimes are up, so the first frames describe a real
988
- // box. Gated by the same switch the skills coordinator uses: unit tests boot `register()`
989
- // and must not acquire background timers. The subscribe method still answers without them.
990
- if (process.env.SENPI_RUNTIME_API_DISABLED !== "true") {
991
- liveTelemetry.start();
992
- }
993
836
  // Last, once everything above is up: the reaper's post-boot pass and hourly sweep. Its
994
837
  // not-active escalation is also off until here, so no delete can land while the boot loop is
995
838
  // still wiring runtimes and launchers. Nothing here may fail start() — a throw from inside the boot loop takes every runtime down, which
@@ -1007,14 +850,9 @@ const senpiRuntimePlugin = {
1007
850
  }
1008
851
  },
1009
852
  async stop() {
1010
- // Live telemetry goes first: it owns a timer and an in-memory buffer, nothing downstream
1011
- // depends on it, and stopping it here guarantees no publish tick reads a half-torn-down
1012
- // runtime handle. Records emitted during the teardown below land in a buffer nobody drains,
1013
- // which is the right trade for a process on its way out.
1014
- liveTelemetry.stop();
1015
- // Then the reaper, before the runtimes are torn down below, so no pass starts deleting
1016
- // while they go away. The wait is bounded, so a pass already under way cannot stop a handle
1017
- // the teardown below is stopping too.
853
+ // Stop the reaper first so no pass starts deleting while the runtimes are torn down below,
854
+ // and wait (bounded) for a pass already under way, so its delete cannot stop a handle the
855
+ // teardown below is stopping too.
1018
856
  await reaper.stop();
1019
857
  // Drain the HTTP server FIRST so in-flight POST /signals requests
1020
858
  // finish before the underlying runtime they target is torn down.
@@ -1351,10 +1189,6 @@ const senpiRuntimePlugin = {
1351
1189
  onStrategyNotActive: (address) => reaper.checkWallet(address),
1352
1190
  });
1353
1191
  runtimeHandles.set(resolvedId, handle);
1354
- if (parsedConfig)
1355
- noteTelemetryRuntimeConfig(resolvedId, parsedConfig);
1356
- else
1357
- noteTelemetryRuntime(resolvedId, wallet);
1358
1192
  // Registry create fact — distinct from the runtime.started boot transition (which also fires
1359
1193
  // on a transient restart). Emitted inside emitEvent's guard so telemetry never fails the install.
1360
1194
  emitEvent(apiLogger, () => buildRuntimeCreatedEvent({ runtimeId: resolvedId, address: wallet, recipeName: runtimeName }));
@@ -1467,7 +1301,7 @@ const senpiRuntimePlugin = {
1467
1301
  }
1468
1302
  }
1469
1303
  /** Gateway respond accepts (ok, payload?, error?) with error as { code, message }; plugin SDK types may omit the third arg. */
1470
- registerGatewayMethod("senpi.installRuntime", async (ctx) => {
1304
+ api.registerGatewayMethod("senpi.installRuntime", async (ctx) => {
1471
1305
  const respond = ctx.respond;
1472
1306
  const result = await installRuntimeCore(ctx.params);
1473
1307
  if (result.ok)
@@ -1475,7 +1309,7 @@ const senpiRuntimePlugin = {
1475
1309
  else
1476
1310
  respond(false, undefined, result.error);
1477
1311
  }, { scope: "operator.write" });
1478
- registerGatewayMethod("senpi.listRuntimes", async (ctx) => {
1312
+ api.registerGatewayMethod("senpi.listRuntimes", async (ctx) => {
1479
1313
  const respond = ctx.respond;
1480
1314
  const { readRegistry } = await import("./runtime/strategy-registry.js");
1481
1315
  const registry = await readRegistry(baseStateDir);
@@ -1618,9 +1452,6 @@ const senpiRuntimePlugin = {
1618
1452
  // "running — NO ENTRY SCANNERS" forever. The map is otherwise only cleared on a
1619
1453
  // successful wire/install, which a scanner-less reinstall never reaches.
1620
1454
  unwiredRuntimes.delete(targetId);
1621
- // Same reuse hazard: a deleted id left in the index would keep publishing a strategy that no
1622
- // longer exists, and would hand a reinstall the old name and scanner list.
1623
- telemetryStrategies.delete(targetId);
1624
1455
  bootSkippedRuntimes.delete(targetId);
1625
1456
  // Archive state directory before removing from registry
1626
1457
  if (deletedEntry) {
@@ -1651,7 +1482,7 @@ const senpiRuntimePlugin = {
1651
1482
  }));
1652
1483
  return { ok: true, payload: { id: targetId } };
1653
1484
  }
1654
- registerGatewayMethod("senpi.deleteRuntime", async (ctx) => {
1485
+ api.registerGatewayMethod("senpi.deleteRuntime", async (ctx) => {
1655
1486
  const respond = ctx.respond;
1656
1487
  const result = await deleteRuntimeCore(ctx.params);
1657
1488
  if (result.ok)
@@ -1758,17 +1589,6 @@ const senpiRuntimePlugin = {
1758
1589
  return checkProofCoversRecipe(packageDir, verifyProof(packageDir), appliedRecipe, (f) => readFileSync(f, "utf8"));
1759
1590
  },
1760
1591
  applyToRuntime: makeApplyToRuntime(runtimeHandles, async (runtimeId, next, recipe) => {
1761
- // Re-index the topology facts FIRST, ahead of every early return and every failure path
1762
- // below. `makeApplyToRuntime` only reaches this callback once the in-process swap has
1763
- // succeeded, so `next` is already the recipe this box runs: its display name and its
1764
- // declared scanner list are the ones the topology payload must name. Leaving the index on
1765
- // the previous config would publish strategy rows the new scanner stats cannot join to.
1766
- //
1767
- // Ahead of the early return because an apply on a box with no intake still swapped the
1768
- // recipe, and ahead of the launcher because a launcher that fails is a runtime running the
1769
- // new recipe with no external scanners — the degraded state a dashboard most needs to show,
1770
- // and one it can only render if the declared scanners are the new ones.
1771
- noteTelemetryRuntimeConfig(runtimeId, next);
1772
1592
  // Restart this runtime's external scanner children against the new package.
1773
1593
  //
1774
1594
  // The rebuild swaps in-process components; these are separate OS processes, spawned once
@@ -1916,7 +1736,7 @@ const senpiRuntimePlugin = {
1916
1736
  * producers — slated for removal in Task 5 once the migration counter
1917
1737
  * decays to zero (see `runtime-phase-2-api-plan.md` Section 8).
1918
1738
  */
1919
- registerGatewayMethod("senpi.ingestExternalScannerData", async (ctx) => {
1739
+ api.registerGatewayMethod("senpi.ingestExternalScannerData", async (ctx) => {
1920
1740
  const respond = ctx.respond;
1921
1741
  // TODO(s11): wire actual telemetry counter for
1922
1742
  // runtime.deprecated_gateway_ingest_call (event name, properties:
@@ -1935,7 +1755,6 @@ const senpiRuntimePlugin = {
1935
1755
  ...(request.direction !== undefined && { direction: request.direction }),
1936
1756
  ...(request.score !== undefined && { score: request.score }),
1937
1757
  ...(request.signal_type !== undefined && { signal_type: request.signal_type }),
1938
- ...(request.tick_id !== undefined && { tick_id: request.tick_id }),
1939
1758
  ...(request.data !== undefined && { data: request.data }),
1940
1759
  ...(request.signals !== undefined && { signals: request.signals }),
1941
1760
  };
@@ -1993,7 +1812,7 @@ const senpiRuntimePlugin = {
1993
1812
  });
1994
1813
  }
1995
1814
  }, { scope: "operator.write" });
1996
- registerGatewayMethod("senpi.listDslPositions", async (ctx) => {
1815
+ api.registerGatewayMethod("senpi.listDslPositions", async (ctx) => {
1997
1816
  const respond = ctx.respond;
1998
1817
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1999
1818
  const address = typeof ctx.params?.address === "string" ? ctx.params.address : undefined;
@@ -2018,7 +1837,7 @@ const senpiRuntimePlugin = {
2018
1837
  }
2019
1838
  respond(true, { positions });
2020
1839
  }, { scope: "operator.read" });
2021
- registerGatewayMethod("senpi.getDslPositionState", async (ctx) => {
1840
+ api.registerGatewayMethod("senpi.getDslPositionState", async (ctx) => {
2022
1841
  const respond = ctx.respond;
2023
1842
  const asset = typeof ctx.params?.asset === "string" ? ctx.params.asset : undefined;
2024
1843
  if (!asset) {
@@ -2059,7 +1878,7 @@ const senpiRuntimePlugin = {
2059
1878
  }
2060
1879
  respond(true, { positions: matches });
2061
1880
  }, { scope: "operator.read" });
2062
- registerGatewayMethod("senpi.listDslArchives", async (ctx) => {
1881
+ api.registerGatewayMethod("senpi.listDslArchives", async (ctx) => {
2063
1882
  const respond = ctx.respond;
2064
1883
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
2065
1884
  const address = typeof ctx.params?.address === "string" ? ctx.params.address : undefined;
@@ -2085,7 +1904,7 @@ const senpiRuntimePlugin = {
2085
1904
  closes = closes.slice(0, limit);
2086
1905
  respond(true, { closes });
2087
1906
  }, { scope: "operator.read" });
2088
- registerGatewayMethod("senpi.getHealthStatus", async (ctx) => {
1907
+ api.registerGatewayMethod("senpi.getHealthStatus", async (ctx) => {
2089
1908
  const respond = ctx.respond;
2090
1909
  const updater = autoUpdateCoordinator.getSnapshot();
2091
1910
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
@@ -2113,7 +1932,7 @@ const senpiRuntimePlugin = {
2113
1932
  const statuses = await collectRuntimeHealthStatuses(runtimeHandles, api.logger);
2114
1933
  respond(true, { statuses, updater });
2115
1934
  }, { scope: "operator.read" });
2116
- registerGatewayMethod("senpi.getSystemState", async (ctx) => {
1935
+ api.registerGatewayMethod("senpi.getSystemState", async (ctx) => {
2117
1936
  const respond = ctx.respond;
2118
1937
  const updater = autoUpdateCoordinator.getSnapshot();
2119
1938
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
@@ -2141,36 +1960,6 @@ const senpiRuntimePlugin = {
2141
1960
  const states = await collectRuntimeSystemStates(runtimeHandles, api.logger, baseStateDir);
2142
1961
  respond(true, { states, updater });
2143
1962
  }, { scope: "operator.read" });
2144
- /**
2145
- * Live telemetry — the one method of the stream, called once per connect.
2146
- *
2147
- * It returns the current value of all three broadcast payloads plus the recent record window, so
2148
- * a client joining a box that has been running for hours renders a full screen immediately
2149
- * instead of waiting for the next thing to happen. `(afterEpoch, afterSeq)` resumes a client that
2150
- * already holds part of this epoch's run — a cursor naming any other epoch is dropped, because a
2151
- * seq from a previous run cannot address these records. `truncated` says when the records between
2152
- * a cursor of this epoch and the oldest record still held are gone, which nothing in `recent`
2153
- * alone can express.
2154
- *
2155
- * Read-only, like every other inspection method here. The scope that decides who RECEIVES the
2156
- * broadcasts is a property of the client's own connection, not of this method.
2157
- */
2158
- registerGatewayMethod("senpi.telemetry.subscribe", async (ctx) => {
2159
- const respond = ctx.respond;
2160
- try {
2161
- respond(true, await liveTelemetry.snapshot({
2162
- afterEpoch: ctx.params?.afterEpoch,
2163
- afterSeq: ctx.params?.afterSeq,
2164
- }));
2165
- }
2166
- catch (err) {
2167
- api.logger.warn("[senpi-runtime] telemetry subscribe error", { err });
2168
- respond(false, undefined, {
2169
- code: "INTERNAL_ERROR",
2170
- message: `Failed to build the telemetry snapshot: ${formatGatewayError(err)}`,
2171
- });
2172
- }
2173
- }, { scope: "operator.read" });
2174
1963
  /**
2175
1964
  * Force the auto-update poll to run now instead of waiting for its timer.
2176
1965
  *
@@ -2229,7 +2018,7 @@ const senpiRuntimePlugin = {
2229
2018
  * persisted action-store machinery (`src/actions/store/`) usable
2230
2019
  * from the operator-facing CLI again.
2231
2020
  */
2232
- registerGatewayMethod("senpi.listActions", async (ctx) => {
2021
+ api.registerGatewayMethod("senpi.listActions", async (ctx) => {
2233
2022
  const respond = ctx.respond;
2234
2023
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
2235
2024
  const address = typeof ctx.params?.address === "string" ? ctx.params.address : undefined;
@@ -2259,7 +2048,7 @@ const senpiRuntimePlugin = {
2259
2048
  }
2260
2049
  respond(true, { actions: merged });
2261
2050
  }, { scope: "operator.read" });
2262
- registerGatewayMethod("senpi.getActionState", async (ctx) => {
2051
+ api.registerGatewayMethod("senpi.getActionState", async (ctx) => {
2263
2052
  const respond = ctx.respond;
2264
2053
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
2265
2054
  const actionName = typeof ctx.params?.actionName === "string" ? ctx.params.actionName : undefined;
@@ -2296,7 +2085,7 @@ const senpiRuntimePlugin = {
2296
2085
  });
2297
2086
  }
2298
2087
  }, { scope: "operator.read" });
2299
- registerGatewayMethod("senpi.getActionHistory", async (ctx) => {
2088
+ api.registerGatewayMethod("senpi.getActionHistory", async (ctx) => {
2300
2089
  const respond = ctx.respond;
2301
2090
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
2302
2091
  const actionName = typeof ctx.params?.actionName === "string" ? ctx.params.actionName : undefined;
@@ -2364,7 +2153,7 @@ const senpiRuntimePlugin = {
2364
2153
  // reachable on HTTP `/audit`. Passes through the caller-controlled subset
2365
2154
  // of the audit_query contract; resource scoping (wallet → strategyId) is
2366
2155
  // owned by handle.auditQuery, never the caller.
2367
- registerGatewayMethod("senpi.auditQuery", async (ctx) => {
2156
+ api.registerGatewayMethod("senpi.auditQuery", async (ctx) => {
2368
2157
  const respond = ctx.respond;
2369
2158
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
2370
2159
  if (!runtimeId) {
@@ -2412,7 +2201,7 @@ const senpiRuntimePlugin = {
2412
2201
  // its state root private, so re-derive it from baseStateDir + the handle's
2413
2202
  // wallet (same helper the writer's stateDir was built with); the writer
2414
2203
  // nests entries under `<root>/<address>/risk-guard/audit.jsonl`.
2415
- registerGatewayMethod("senpi.getRiskAudit", async (ctx) => {
2204
+ api.registerGatewayMethod("senpi.getRiskAudit", async (ctx) => {
2416
2205
  const respond = ctx.respond;
2417
2206
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
2418
2207
  if (!runtimeId) {
@@ -2455,7 +2244,7 @@ const senpiRuntimePlugin = {
2455
2244
  // Per-strategy local domain-event ring (on-disk JSONL) — the trade narrative, queryable locally.
2456
2245
  // Same state-root re-derivation as getRiskAudit: the Runtime keeps its root private, so rebuild it
2457
2246
  // from baseStateDir + the handle's wallet; the writer nests rows under `<root>/strategies/<address>/events/`.
2458
- registerGatewayMethod("senpi.getEvents", async (ctx) => {
2247
+ api.registerGatewayMethod("senpi.getEvents", async (ctx) => {
2459
2248
  const respond = ctx.respond;
2460
2249
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
2461
2250
  if (!runtimeId) {
@@ -2512,12 +2301,12 @@ const senpiRuntimePlugin = {
2512
2301
  }
2513
2302
  }, { scope: "operator.read" });
2514
2303
  // ─── Skills manager — read + mutate ──────────────────────────────────────
2515
- registerGatewayMethod("senpi.skills.status", async (ctx) => {
2304
+ api.registerGatewayMethod("senpi.skills.status", async (ctx) => {
2516
2305
  const respond = ctx.respond;
2517
2306
  const snapshot = skillsCoordinator.getSnapshot();
2518
2307
  respond(true, { snapshot });
2519
2308
  }, { scope: "operator.read" });
2520
- registerGatewayMethod("senpi.skills.update", async (ctx) => {
2309
+ api.registerGatewayMethod("senpi.skills.update", async (ctx) => {
2521
2310
  const respond = ctx.respond;
2522
2311
  const name = typeof ctx.params?.name === "string" ? ctx.params.name.trim() : undefined;
2523
2312
  if (!name) {
@@ -2542,7 +2331,7 @@ const senpiRuntimePlugin = {
2542
2331
  });
2543
2332
  }
2544
2333
  }, { scope: "operator.write" });
2545
- registerGatewayMethod("senpi.skills.updateAll", async (ctx) => {
2334
+ api.registerGatewayMethod("senpi.skills.updateAll", async (ctx) => {
2546
2335
  const respond = ctx.respond;
2547
2336
  try {
2548
2337
  const results = await skillsCoordinator.updateAll();
@@ -2608,7 +2397,6 @@ function parseGatewayExternalScannerIngestParams(params) {
2608
2397
  direction: params?.direction,
2609
2398
  score: params?.score,
2610
2399
  signal_type: params?.signal_type,
2611
- tick_id: params?.tick_id,
2612
2400
  data: params?.data,
2613
2401
  signals: params?.signals,
2614
2402
  },