@senpi/runtime 3.0.66 → 3.0.68

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 (72) hide show
  1. package/dist/actions/event-derivation.d.ts.map +1 -1
  2. package/dist/actions/event-derivation.js +2 -0
  3. package/dist/actions/event-derivation.js.map +1 -1
  4. package/dist/deploy/job.d.ts.map +1 -1
  5. package/dist/deploy/job.js +11 -2
  6. package/dist/deploy/job.js.map +1 -1
  7. package/dist/deploy/orchestrator.d.ts.map +1 -1
  8. package/dist/deploy/orchestrator.js +9 -1
  9. package/dist/deploy/orchestrator.js.map +1 -1
  10. package/dist/deploy/register.d.ts.map +1 -1
  11. package/dist/deploy/register.js +10 -2
  12. package/dist/deploy/register.js.map +1 -1
  13. package/dist/health/types.d.ts +6 -0
  14. package/dist/health/types.d.ts.map +1 -1
  15. package/dist/health/types.js.map +1 -1
  16. package/dist/index.d.ts +16 -4
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +202 -18
  19. package/dist/index.js.map +1 -1
  20. package/dist/runtime/runtime.d.ts.map +1 -1
  21. package/dist/runtime/runtime.js +7 -1
  22. package/dist/runtime/runtime.js.map +1 -1
  23. package/dist/scanners/engine/engine.d.ts +5 -0
  24. package/dist/scanners/engine/engine.d.ts.map +1 -1
  25. package/dist/scanners/engine/engine.js +1 -0
  26. package/dist/scanners/engine/engine.js.map +1 -1
  27. package/dist/scanners/runtime-module.d.ts.map +1 -1
  28. package/dist/scanners/runtime-module.js +5 -0
  29. package/dist/scanners/runtime-module.js.map +1 -1
  30. package/dist/skills-manager/manifest.js +2 -2
  31. package/dist/skills-manager/skills-manifest.json +2 -2
  32. package/dist/telemetry/event-builders.d.ts +12 -0
  33. package/dist/telemetry/event-builders.d.ts.map +1 -1
  34. package/dist/telemetry/event-builders.js +2 -0
  35. package/dist/telemetry/event-builders.js.map +1 -1
  36. package/dist/telemetry/event-store.d.ts +6 -0
  37. package/dist/telemetry/event-store.d.ts.map +1 -1
  38. package/dist/telemetry/event-store.js +6 -6
  39. package/dist/telemetry/event-store.js.map +1 -1
  40. package/dist/telemetry/live-telemetry.d.ts +153 -0
  41. package/dist/telemetry/live-telemetry.d.ts.map +1 -0
  42. package/dist/telemetry/live-telemetry.js +471 -0
  43. package/dist/telemetry/live-telemetry.js.map +1 -0
  44. package/dist/telemetry/scanner-stats.d.ts +136 -0
  45. package/dist/telemetry/scanner-stats.d.ts.map +1 -0
  46. package/dist/telemetry/scanner-stats.js +304 -0
  47. package/dist/telemetry/scanner-stats.js.map +1 -0
  48. package/dist/telemetry/strategy-topology.d.ts +99 -0
  49. package/dist/telemetry/strategy-topology.d.ts.map +1 -0
  50. package/dist/telemetry/strategy-topology.js +93 -0
  51. package/dist/telemetry/strategy-topology.js.map +1 -0
  52. package/dist/telemetry/telemetry-publisher.d.ts +63 -0
  53. package/dist/telemetry/telemetry-publisher.d.ts.map +1 -0
  54. package/dist/telemetry/telemetry-publisher.js +229 -0
  55. package/dist/telemetry/telemetry-publisher.js.map +1 -0
  56. package/dist/telemetry/timeline-buffer.d.ts +95 -0
  57. package/dist/telemetry/timeline-buffer.d.ts.map +1 -0
  58. package/dist/telemetry/timeline-buffer.js +237 -0
  59. package/dist/telemetry/timeline-buffer.js.map +1 -0
  60. package/dist/telemetry/tracing.d.ts +9 -0
  61. package/dist/telemetry/tracing.d.ts.map +1 -1
  62. package/dist/telemetry/tracing.js +36 -0
  63. package/dist/telemetry/tracing.js.map +1 -1
  64. package/dist/utils/event-catalog.d.ts +8 -0
  65. package/dist/utils/event-catalog.d.ts.map +1 -1
  66. package/dist/utils/event-catalog.js.map +1 -1
  67. package/dist/utils/ulid.d.ts +9 -0
  68. package/dist/utils/ulid.d.ts.map +1 -0
  69. package/dist/utils/ulid.js +43 -0
  70. package/dist/utils/ulid.js.map +1 -0
  71. package/openclaw.plugin.json +1 -1
  72. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -108,6 +108,21 @@ interface PluginLogger {
108
108
  * managed agent can approve. See `registerGatewayMethod` call sites below.
109
109
  */
110
110
  type OperatorScope = "operator.admin" | "operator.read" | "operator.write" | "operator.approvals" | "operator.pairing" | "operator.talk.secrets";
111
+ /**
112
+ * What a gateway RPC handler receives. `context` is openclaw's `GatewayRequestContext`, narrowed to
113
+ * the one member the plugin uses: it is the ONLY route to the websocket push layer — the plugin api
114
+ * object carries no broadcast, emit, push or publish. Optional because a host that predates it, and
115
+ * every test double, calls handlers without one.
116
+ */
117
+ interface GatewayRequestCtx {
118
+ respond: (ok: boolean, value: unknown) => void;
119
+ params?: Record<string, unknown>;
120
+ context?: {
121
+ broadcast?: (event: string, payload: unknown, opts?: {
122
+ dropIfSlow?: boolean;
123
+ }) => void;
124
+ };
125
+ }
111
126
  interface PluginApi {
112
127
  id: string;
113
128
  name: string;
@@ -139,10 +154,7 @@ interface PluginApi {
139
154
  }) => void, opts?: {
140
155
  commands: string[];
141
156
  }): void;
142
- registerGatewayMethod(name: string, handler: (ctx: {
143
- respond: (ok: boolean, value: unknown) => void;
144
- params?: Record<string, unknown>;
145
- }) => void | Promise<void>, opts?: {
157
+ registerGatewayMethod(name: string, handler: (ctx: GatewayRequestCtx) => void | Promise<void>, opts?: {
146
158
  scope?: OperatorScope;
147
159
  }): void;
148
160
  registerReload?: (registration: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -56,6 +56,7 @@ import { createIntakeContext } from "./runtime-api/intake/intake-context.js";
56
56
  import { wireRuntime, intakeUrlFor, selectScanners } from "./runtime-api/intake/intake-mount.js";
57
57
  import { ingestDispatchMutex, ingestDispatchKey } from "./runtime-api/intake/ingest-mutex.js";
58
58
  import { loadRuntimeConfig } from "./runtime/load-runtime-config.js";
59
+ import { createLiveTelemetry } from "./telemetry/live-telemetry.js";
59
60
  /** Initialize all core features (DSL, risk, health) on the given event bus. */
60
61
  export function initCore(bus) {
61
62
  initDsl(bus);
@@ -316,10 +317,140 @@ const senpiRuntimePlugin = {
316
317
  // cleared on a successful wire/install. senpi.listRuntimes reads it to render
317
318
  // "running — NO ENTRY SCANNERS" instead of a bare "running" for a blind runtime.
318
319
  const unwiredRuntimes = new Map();
320
+ // ── Live telemetry ────────────────────────────────────────────────
321
+ // Every runtime this box knows about, running or not, keyed runtimeId. The topology payload has
322
+ // to name a stopped strategy too, so this is populated from the registry rather than from
323
+ // `runtimeHandles` (which holds only the live ones) and is the source of the two facts the
324
+ // registry entry does not carry: the strategy's display name and its declared scanners. Both come
325
+ // from the parsed YAML — `name` is the same `config.name` that `RuntimeHealthStatus.runtimeName`
326
+ // is built from, so a runtime whose config never parsed falls back to its id rather than to a
327
+ // guess, and its scanner list stays empty rather than inventing rows.
328
+ const telemetryStrategies = new Map();
329
+ const noteTelemetryRuntime = (runtimeId, address) => {
330
+ const existing = telemetryStrategies.get(runtimeId);
331
+ telemetryStrategies.set(runtimeId, {
332
+ address,
333
+ name: existing?.name ?? runtimeId,
334
+ scanners: existing?.scanners ?? [],
335
+ });
336
+ };
337
+ const noteTelemetryRuntimeConfig = (runtimeId, config) => {
338
+ telemetryStrategies.set(runtimeId, {
339
+ address: config.strategy?.wallet ?? telemetryStrategies.get(runtimeId)?.address ?? "",
340
+ name: config.name,
341
+ scanners: config.scanners ?? [],
342
+ });
343
+ };
319
344
  // Dedicated logger for the runtime-api subtree — required because the
320
345
  // PluginLogger surface (info/warn/error) lacks `debug`, while the
321
346
  // Logger contract used by `runtime-api/` includes it.
322
347
  const apiLogger = createLogger("[senpi-core][api]");
348
+ /**
349
+ * One gather per publish tick. Health and system state are read together because the two payloads
350
+ * need both: the topology reads health and `runtimeName`, the stats read the per-scanner rows,
351
+ * and `restartCount` / `degradedReason` live only on the health rows while every other scanner
352
+ * field lives only on the system-state rows — so they are joined here on `(runtimeId, scannerId)`.
353
+ */
354
+ const collectTelemetryFacts = async () => {
355
+ const [statuses, states] = await Promise.all([
356
+ collectRuntimeHealthStatuses(runtimeHandles, api.logger),
357
+ collectRuntimeSystemStates(runtimeHandles, api.logger, baseStateDir),
358
+ ]);
359
+ const healthByRuntime = new Map();
360
+ for (const status of statuses) {
361
+ if (status.runtimeId !== undefined)
362
+ healthByRuntime.set(status.runtimeId, status);
363
+ }
364
+ const scannerState = [];
365
+ // Registration facts per (runtimeId, scannerId), so the topology can report a scanner's live
366
+ // schedule rather than only what its YAML declared.
367
+ const registrations = new Map();
368
+ for (const state of states) {
369
+ const runtimeId = state.runtimeId;
370
+ const healthRows = runtimeId === undefined
371
+ ? []
372
+ : healthByRuntime.get(runtimeId)?.components.scanners.scanners ?? [];
373
+ for (const row of state.components.scanners.state.scanners) {
374
+ const supervision = healthRows.find((r) => r.scannerId === row.scannerId);
375
+ const snapshot = {
376
+ address: row.address,
377
+ scannerId: row.scannerId,
378
+ health: row.health,
379
+ degradedReason: supervision?.degradedReason ?? null,
380
+ enabled: row.enabled,
381
+ inFlightSince: row.inFlightSince,
382
+ lastRunStartedAt: row.lastRunStartedAt,
383
+ lastRunFinishedAt: row.lastRunFinishedAt,
384
+ lastRunStatus: row.lastRunStatus,
385
+ lastRunReason: row.lastRunReason,
386
+ nextRunAt: row.nextRunAt,
387
+ lastAliveAt: row.lastAliveAt,
388
+ // No supervision row means the restart count is unknown, not zero. A zero here would
389
+ // read as "this scanner has never restarted" on a scanner nobody is supervising.
390
+ restartCount: supervision?.restartCount ?? null,
391
+ };
392
+ scannerState.push(snapshot);
393
+ if (runtimeId !== undefined) {
394
+ registrations.set(`${runtimeId}::${row.scannerId}`, {
395
+ ...snapshot,
396
+ scheduleMode: row.scheduleMode,
397
+ configVersion: row.configVersion,
398
+ intervalSeconds: row.intervalSeconds,
399
+ });
400
+ }
401
+ }
402
+ }
403
+ const strategies = [];
404
+ for (const [runtimeId, entry] of telemetryStrategies) {
405
+ const health = healthByRuntime.get(runtimeId);
406
+ const scanners = entry.scanners.map((declared) => {
407
+ const live = registrations.get(`${runtimeId}::${declared.name}`);
408
+ return live === undefined
409
+ ? { entry: declared }
410
+ : {
411
+ entry: declared,
412
+ registration: {
413
+ enabled: live.enabled,
414
+ scheduleMode: live.scheduleMode,
415
+ // The engine's own cadence, which is what the scanner actually polls on — a
416
+ // recipe may omit it and take the scanner definition's default. An external
417
+ // scanner registers zero because it is POSTed to rather than polled, and only
418
+ // the recipe knows the cadence its scaffold was told to tick at.
419
+ intervalSeconds: live.intervalSeconds > 0 ? live.intervalSeconds : declared.interval_seconds ?? null,
420
+ configVersion: live.configVersion,
421
+ },
422
+ };
423
+ });
424
+ strategies.push({
425
+ runtimeId,
426
+ address: entry.address,
427
+ // `runtimeName` is built from the same `config.name`; the indexed value is used because it
428
+ // is available for a stopped runtime too, where there is no health status to read.
429
+ name: health?.runtimeName ?? entry.name,
430
+ scanners,
431
+ });
432
+ }
433
+ return {
434
+ strategies,
435
+ runningRuntimeIds: runtimeHandles,
436
+ unwiredRuntimeIds: unwiredRuntimes,
437
+ readHealth: (runtimeId) => healthByRuntime.get(runtimeId),
438
+ scannerState,
439
+ };
440
+ };
441
+ const liveTelemetry = createLiveTelemetry({ collectFacts: collectTelemetryFacts });
442
+ /**
443
+ * Every gateway method goes through here so the broadcast handle is captured on the FIRST request
444
+ * of any kind. `ctx.context` is the only route to the websocket push layer and it exists only
445
+ * inside a handler — capturing it in the subscribe method alone would mean a box whose client
446
+ * never subscribes can never publish anything, silently.
447
+ */
448
+ const registerGatewayMethod = (name, handler, opts) => {
449
+ api.registerGatewayMethod(name, (ctx) => {
450
+ liveTelemetry.captureBroadcast(ctx, name);
451
+ return handler(ctx);
452
+ }, opts);
453
+ };
323
454
  // A gateway seam failed to wire/launch a runtime's external scanner stack: the runtime is up but
324
455
  // runs blind on internal scanners only. Two records, by design: a first-class `runtime.scanners_unwired`
325
456
  // event (the queryable/alertable fact, by phase) and the migrated exception-form `logger.error`
@@ -527,6 +658,9 @@ const senpiRuntimePlugin = {
527
658
  continue;
528
659
  const pathOrContent = entry.runtimeYamlPath ?? entry.runtimeYamlContent;
529
660
  const entryStateDir = buildRuntimeStateDir(baseStateDir, entry.id, entry.wallet ?? "");
661
+ // Indexed before the boot attempt: a runtime whose start throws is installed but stopped,
662
+ // and the telemetry topology has to be able to say so.
663
+ noteTelemetryRuntime(entry.id, entry.wallet ?? "");
530
664
  const handle = await run(pathOrContent, {
531
665
  fromPath: hasPath,
532
666
  stateDir: entryStateDir,
@@ -549,6 +683,7 @@ const senpiRuntimePlugin = {
549
683
  if (sharedIntakeContext) {
550
684
  try {
551
685
  const runtimeConfig = await loadRuntimeConfig(pathOrContent, { fromPath: hasPath });
686
+ noteTelemetryRuntimeConfig(entry.id, runtimeConfig);
552
687
  // Content-installed runtimes carry their recipe dir in the registry
553
688
  // entry (runtimeYamlDir) so relative scanner paths still resolve
554
689
  // after a restart; path-installed ones derive it from the path.
@@ -600,6 +735,7 @@ const senpiRuntimePlugin = {
600
735
  // event) so listRuntimes/health don't render a bare "running".
601
736
  try {
602
737
  const runtimeConfig = await loadRuntimeConfig(pathOrContent, { fromPath: hasPath });
738
+ noteTelemetryRuntimeConfig(entry.id, runtimeConfig);
603
739
  if (selectScanners(runtimeConfig).length > 0) {
604
740
  unwiredRuntimes.set(entry.id, "no_intake");
605
741
  handle.attachSupervisionSource(() => ({
@@ -701,8 +837,19 @@ const senpiRuntimePlugin = {
701
837
  }
702
838
  }
703
839
  }
840
+ // Publish ticks start only once the runtimes are up, so the first frames describe a real
841
+ // box. Gated by the same switch the skills coordinator uses: unit tests boot `register()`
842
+ // and must not acquire background timers. The subscribe method still answers without them.
843
+ if (process.env.SENPI_RUNTIME_API_DISABLED !== "true") {
844
+ liveTelemetry.start();
845
+ }
704
846
  },
705
847
  async stop() {
848
+ // Live telemetry goes first: it owns a timer and an in-memory buffer, nothing downstream
849
+ // depends on it, and stopping it here guarantees no publish tick reads a half-torn-down
850
+ // runtime handle. Records emitted during the teardown below land in a buffer nobody drains,
851
+ // which is the right trade for a process on its way out.
852
+ liveTelemetry.stop();
706
853
  // Drain the HTTP server FIRST so in-flight POST /signals requests
707
854
  // finish before the underlying runtime they target is torn down.
708
855
  // `apiHandle.close()` never throws.
@@ -1009,6 +1156,10 @@ const senpiRuntimePlugin = {
1009
1156
  runtimeId: resolvedId,
1010
1157
  });
1011
1158
  runtimeHandles.set(resolvedId, handle);
1159
+ if (parsedConfig)
1160
+ noteTelemetryRuntimeConfig(resolvedId, parsedConfig);
1161
+ else
1162
+ noteTelemetryRuntime(resolvedId, wallet);
1012
1163
  // Registry create fact — distinct from the runtime.started boot transition (which also fires
1013
1164
  // on a transient restart). Emitted inside emitEvent's guard so telemetry never fails the install.
1014
1165
  emitEvent(apiLogger, () => buildRuntimeCreatedEvent({ runtimeId: resolvedId, address: wallet, recipeName: runtimeName }));
@@ -1114,7 +1265,7 @@ const senpiRuntimePlugin = {
1114
1265
  }
1115
1266
  }
1116
1267
  /** Gateway respond accepts (ok, payload?, error?) with error as { code, message }; plugin SDK types may omit the third arg. */
1117
- api.registerGatewayMethod("senpi.installRuntime", async (ctx) => {
1268
+ registerGatewayMethod("senpi.installRuntime", async (ctx) => {
1118
1269
  const respond = ctx.respond;
1119
1270
  const result = await installRuntimeCore(ctx.params);
1120
1271
  if (result.ok)
@@ -1122,7 +1273,7 @@ const senpiRuntimePlugin = {
1122
1273
  else
1123
1274
  respond(false, undefined, result.error);
1124
1275
  }, { scope: "operator.write" });
1125
- api.registerGatewayMethod("senpi.listRuntimes", async (ctx) => {
1276
+ registerGatewayMethod("senpi.listRuntimes", async (ctx) => {
1126
1277
  const respond = ctx.respond;
1127
1278
  const { readRegistry } = await import("./runtime/strategy-registry.js");
1128
1279
  const registry = await readRegistry(baseStateDir);
@@ -1201,6 +1352,9 @@ const senpiRuntimePlugin = {
1201
1352
  // "running — NO ENTRY SCANNERS" forever. The map is otherwise only cleared on a
1202
1353
  // successful wire/install, which a scanner-less reinstall never reaches.
1203
1354
  unwiredRuntimes.delete(targetId);
1355
+ // Same reuse hazard: a deleted id left in the index would keep publishing a strategy that no
1356
+ // longer exists, and would hand a reinstall the old name and scanner list.
1357
+ telemetryStrategies.delete(targetId);
1204
1358
  // Archive state directory before removing from registry
1205
1359
  if (deletedEntry) {
1206
1360
  const runtimeStateDir = buildRuntimeStateDir(baseStateDir, targetId, deletedEntry.wallet ?? "");
@@ -1227,7 +1381,7 @@ const senpiRuntimePlugin = {
1227
1381
  }));
1228
1382
  return { ok: true, payload: { id: targetId } };
1229
1383
  }
1230
- api.registerGatewayMethod("senpi.deleteRuntime", async (ctx) => {
1384
+ registerGatewayMethod("senpi.deleteRuntime", async (ctx) => {
1231
1385
  const respond = ctx.respond;
1232
1386
  const result = await deleteRuntimeCore(ctx.params);
1233
1387
  if (result.ok)
@@ -1321,7 +1475,7 @@ const senpiRuntimePlugin = {
1321
1475
  * producers — slated for removal in Task 5 once the migration counter
1322
1476
  * decays to zero (see `runtime-phase-2-api-plan.md` Section 8).
1323
1477
  */
1324
- api.registerGatewayMethod("senpi.ingestExternalScannerData", async (ctx) => {
1478
+ registerGatewayMethod("senpi.ingestExternalScannerData", async (ctx) => {
1325
1479
  const respond = ctx.respond;
1326
1480
  // TODO(s11): wire actual telemetry counter for
1327
1481
  // runtime.deprecated_gateway_ingest_call (event name, properties:
@@ -1397,7 +1551,7 @@ const senpiRuntimePlugin = {
1397
1551
  });
1398
1552
  }
1399
1553
  }, { scope: "operator.write" });
1400
- api.registerGatewayMethod("senpi.listDslPositions", async (ctx) => {
1554
+ registerGatewayMethod("senpi.listDslPositions", async (ctx) => {
1401
1555
  const respond = ctx.respond;
1402
1556
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1403
1557
  const address = typeof ctx.params?.address === "string" ? ctx.params.address : undefined;
@@ -1422,7 +1576,7 @@ const senpiRuntimePlugin = {
1422
1576
  }
1423
1577
  respond(true, { positions });
1424
1578
  }, { scope: "operator.read" });
1425
- api.registerGatewayMethod("senpi.getDslPositionState", async (ctx) => {
1579
+ registerGatewayMethod("senpi.getDslPositionState", async (ctx) => {
1426
1580
  const respond = ctx.respond;
1427
1581
  const asset = typeof ctx.params?.asset === "string" ? ctx.params.asset : undefined;
1428
1582
  if (!asset) {
@@ -1463,7 +1617,7 @@ const senpiRuntimePlugin = {
1463
1617
  }
1464
1618
  respond(true, { positions: matches });
1465
1619
  }, { scope: "operator.read" });
1466
- api.registerGatewayMethod("senpi.listDslArchives", async (ctx) => {
1620
+ registerGatewayMethod("senpi.listDslArchives", async (ctx) => {
1467
1621
  const respond = ctx.respond;
1468
1622
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1469
1623
  const address = typeof ctx.params?.address === "string" ? ctx.params.address : undefined;
@@ -1489,7 +1643,7 @@ const senpiRuntimePlugin = {
1489
1643
  closes = closes.slice(0, limit);
1490
1644
  respond(true, { closes });
1491
1645
  }, { scope: "operator.read" });
1492
- api.registerGatewayMethod("senpi.getHealthStatus", async (ctx) => {
1646
+ registerGatewayMethod("senpi.getHealthStatus", async (ctx) => {
1493
1647
  const respond = ctx.respond;
1494
1648
  const updater = autoUpdateCoordinator.getSnapshot();
1495
1649
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
@@ -1517,7 +1671,7 @@ const senpiRuntimePlugin = {
1517
1671
  const statuses = await collectRuntimeHealthStatuses(runtimeHandles, api.logger);
1518
1672
  respond(true, { statuses, updater });
1519
1673
  }, { scope: "operator.read" });
1520
- api.registerGatewayMethod("senpi.getSystemState", async (ctx) => {
1674
+ registerGatewayMethod("senpi.getSystemState", async (ctx) => {
1521
1675
  const respond = ctx.respond;
1522
1676
  const updater = autoUpdateCoordinator.getSnapshot();
1523
1677
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
@@ -1545,6 +1699,36 @@ const senpiRuntimePlugin = {
1545
1699
  const states = await collectRuntimeSystemStates(runtimeHandles, api.logger, baseStateDir);
1546
1700
  respond(true, { states, updater });
1547
1701
  }, { scope: "operator.read" });
1702
+ /**
1703
+ * Live telemetry — the one method of the stream, called once per connect.
1704
+ *
1705
+ * It returns the current value of all three broadcast payloads plus the recent record window, so
1706
+ * a client joining a box that has been running for hours renders a full screen immediately
1707
+ * instead of waiting for the next thing to happen. `(afterEpoch, afterSeq)` resumes a client that
1708
+ * already holds part of this epoch's run — a cursor naming any other epoch is dropped, because a
1709
+ * seq from a previous run cannot address these records. `truncated` says when the records between
1710
+ * a cursor of this epoch and the oldest record still held are gone, which nothing in `recent`
1711
+ * alone can express.
1712
+ *
1713
+ * Read-only, like every other inspection method here. The scope that decides who RECEIVES the
1714
+ * broadcasts is a property of the client's own connection, not of this method.
1715
+ */
1716
+ registerGatewayMethod("senpi.telemetry.subscribe", async (ctx) => {
1717
+ const respond = ctx.respond;
1718
+ try {
1719
+ respond(true, await liveTelemetry.snapshot({
1720
+ afterEpoch: ctx.params?.afterEpoch,
1721
+ afterSeq: ctx.params?.afterSeq,
1722
+ }));
1723
+ }
1724
+ catch (err) {
1725
+ api.logger.warn("[senpi-runtime] telemetry subscribe error", { err });
1726
+ respond(false, undefined, {
1727
+ code: "INTERNAL_ERROR",
1728
+ message: `Failed to build the telemetry snapshot: ${formatGatewayError(err)}`,
1729
+ });
1730
+ }
1731
+ }, { scope: "operator.read" });
1548
1732
  /**
1549
1733
  * Action layer — read-only inspection.
1550
1734
  *
@@ -1564,7 +1748,7 @@ const senpiRuntimePlugin = {
1564
1748
  * persisted action-store machinery (`src/actions/store/`) usable
1565
1749
  * from the operator-facing CLI again.
1566
1750
  */
1567
- api.registerGatewayMethod("senpi.listActions", async (ctx) => {
1751
+ registerGatewayMethod("senpi.listActions", async (ctx) => {
1568
1752
  const respond = ctx.respond;
1569
1753
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1570
1754
  const address = typeof ctx.params?.address === "string" ? ctx.params.address : undefined;
@@ -1594,7 +1778,7 @@ const senpiRuntimePlugin = {
1594
1778
  }
1595
1779
  respond(true, { actions: merged });
1596
1780
  }, { scope: "operator.read" });
1597
- api.registerGatewayMethod("senpi.getActionState", async (ctx) => {
1781
+ registerGatewayMethod("senpi.getActionState", async (ctx) => {
1598
1782
  const respond = ctx.respond;
1599
1783
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1600
1784
  const actionName = typeof ctx.params?.actionName === "string" ? ctx.params.actionName : undefined;
@@ -1631,7 +1815,7 @@ const senpiRuntimePlugin = {
1631
1815
  });
1632
1816
  }
1633
1817
  }, { scope: "operator.read" });
1634
- api.registerGatewayMethod("senpi.getActionHistory", async (ctx) => {
1818
+ registerGatewayMethod("senpi.getActionHistory", async (ctx) => {
1635
1819
  const respond = ctx.respond;
1636
1820
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1637
1821
  const actionName = typeof ctx.params?.actionName === "string" ? ctx.params.actionName : undefined;
@@ -1699,7 +1883,7 @@ const senpiRuntimePlugin = {
1699
1883
  // reachable on HTTP `/audit`. Passes through the caller-controlled subset
1700
1884
  // of the audit_query contract; resource scoping (wallet → strategyId) is
1701
1885
  // owned by handle.auditQuery, never the caller.
1702
- api.registerGatewayMethod("senpi.auditQuery", async (ctx) => {
1886
+ registerGatewayMethod("senpi.auditQuery", async (ctx) => {
1703
1887
  const respond = ctx.respond;
1704
1888
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1705
1889
  if (!runtimeId) {
@@ -1747,7 +1931,7 @@ const senpiRuntimePlugin = {
1747
1931
  // its state root private, so re-derive it from baseStateDir + the handle's
1748
1932
  // wallet (same helper the writer's stateDir was built with); the writer
1749
1933
  // nests entries under `<root>/<address>/risk-guard/audit.jsonl`.
1750
- api.registerGatewayMethod("senpi.getRiskAudit", async (ctx) => {
1934
+ registerGatewayMethod("senpi.getRiskAudit", async (ctx) => {
1751
1935
  const respond = ctx.respond;
1752
1936
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1753
1937
  if (!runtimeId) {
@@ -1790,7 +1974,7 @@ const senpiRuntimePlugin = {
1790
1974
  // Per-strategy local domain-event ring (on-disk JSONL) — the trade narrative, queryable locally.
1791
1975
  // Same state-root re-derivation as getRiskAudit: the Runtime keeps its root private, so rebuild it
1792
1976
  // from baseStateDir + the handle's wallet; the writer nests rows under `<root>/strategies/<address>/events/`.
1793
- api.registerGatewayMethod("senpi.getEvents", async (ctx) => {
1977
+ registerGatewayMethod("senpi.getEvents", async (ctx) => {
1794
1978
  const respond = ctx.respond;
1795
1979
  const runtimeId = typeof ctx.params?.runtimeId === "string" ? ctx.params.runtimeId : undefined;
1796
1980
  if (!runtimeId) {
@@ -1847,12 +2031,12 @@ const senpiRuntimePlugin = {
1847
2031
  }
1848
2032
  }, { scope: "operator.read" });
1849
2033
  // ─── Skills manager — read + mutate ──────────────────────────────────────
1850
- api.registerGatewayMethod("senpi.skills.status", async (ctx) => {
2034
+ registerGatewayMethod("senpi.skills.status", async (ctx) => {
1851
2035
  const respond = ctx.respond;
1852
2036
  const snapshot = skillsCoordinator.getSnapshot();
1853
2037
  respond(true, { snapshot });
1854
2038
  }, { scope: "operator.read" });
1855
- api.registerGatewayMethod("senpi.skills.update", async (ctx) => {
2039
+ registerGatewayMethod("senpi.skills.update", async (ctx) => {
1856
2040
  const respond = ctx.respond;
1857
2041
  const name = typeof ctx.params?.name === "string" ? ctx.params.name.trim() : undefined;
1858
2042
  if (!name) {
@@ -1877,7 +2061,7 @@ const senpiRuntimePlugin = {
1877
2061
  });
1878
2062
  }
1879
2063
  }, { scope: "operator.write" });
1880
- api.registerGatewayMethod("senpi.skills.updateAll", async (ctx) => {
2064
+ registerGatewayMethod("senpi.skills.updateAll", async (ctx) => {
1881
2065
  const respond = ctx.respond;
1882
2066
  try {
1883
2067
  const results = await skillsCoordinator.updateAll();