@camstack/system 1.2.218 → 1.2.220

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 (97) hide show
  1. package/dist/addon-runner.js +2 -2
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/addon-utils.js +2 -2
  4. package/dist/addon-utils.mjs +2 -2
  5. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  9. package/dist/builtins/alerts/alerts.addon.js +1 -1
  10. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  13. package/dist/builtins/console-logging/index.js +1 -1
  14. package/dist/builtins/console-logging/index.mjs +1 -1
  15. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  16. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  17. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  18. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  20. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  21. package/dist/builtins/hub-forwarder/index.js +1 -1
  22. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  24. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  25. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  26. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  27. package/dist/builtins/local-network/local-network.addon.js +2 -2
  28. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  29. package/dist/builtins/loki-logging/index.js +1 -1
  30. package/dist/builtins/loki-logging/index.mjs +1 -1
  31. package/dist/builtins/native-metrics/gpu-probe.d.ts +18 -0
  32. package/dist/builtins/native-metrics/native-metrics-provider.d.ts +1 -1
  33. package/dist/builtins/native-metrics/native-metrics.addon.d.ts +26 -3
  34. package/dist/builtins/native-metrics/native-metrics.addon.js +1320 -1093
  35. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1320 -1096
  36. package/dist/builtins/native-metrics/proc-process-table.d.ts +25 -0
  37. package/dist/builtins/platform-probe/index.js +3 -3
  38. package/dist/builtins/platform-probe/index.mjs +3 -3
  39. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  40. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  41. package/dist/builtins/snapshot/index.js +1 -1
  42. package/dist/builtins/snapshot/index.mjs +1 -1
  43. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  44. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  45. package/dist/builtins/sqlite-storage/sqlite-pragmas.d.ts +23 -0
  46. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +1 -1
  47. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +1 -0
  48. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  49. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  50. package/dist/builtins/sqlite-storage/wal-checkpoint-policy.d.ts +115 -0
  51. package/dist/builtins/sqlite-storage/wal-checkpoint-worker.d.ts +1 -0
  52. package/dist/builtins/sqlite-storage/wal-checkpointer.d.ts +70 -0
  53. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  54. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  55. package/dist/builtins/system-config/system-config.addon.js +1 -1
  56. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  57. package/dist/builtins/winston-logging/index.js +1 -1
  58. package/dist/builtins/winston-logging/index.mjs +1 -1
  59. package/dist/{custom-action-registry-BDwM5qAK.js → custom-action-registry-B42MC_Pv.js} +1 -1
  60. package/dist/{custom-action-registry-CsONUlhC.mjs → custom-action-registry-CpR58DXT.mjs} +1 -1
  61. package/dist/{dist-DigmGsQS.mjs → dist-Ca_jaxXs.mjs} +232 -4
  62. package/dist/{dist-BOlMCOeT.js → dist-CllKpcTd.js} +232 -4
  63. package/dist/index.d.ts +6 -1
  64. package/dist/index.js +99 -65
  65. package/dist/index.mjs +84 -67
  66. package/dist/kernel/gc-census.d.ts +144 -0
  67. package/dist/kernel/heap-watch.d.ts +32 -1
  68. package/dist/kernel/moleculer/mesh-queue-report.d.ts +51 -0
  69. package/dist/kernel/outbound-queue-watch.d.ts +94 -0
  70. package/dist/kernel/socket-plane-report.d.ts +112 -4
  71. package/dist/kernel/transport/cap-action-census.d.ts +218 -0
  72. package/dist/kernel/transport/cap-child-index.d.ts +49 -0
  73. package/dist/kernel/transport/cap-routing-hints.d.ts +21 -0
  74. package/dist/kernel/transport/child-cap-protocol.d.ts +28 -2
  75. package/dist/kernel/transport/frame-codec.d.ts +49 -0
  76. package/dist/kernel/transport/index.d.ts +2 -0
  77. package/dist/kernel/transport/local-child-client.d.ts +7 -0
  78. package/dist/kernel/transport/local-child-registry.d.ts +99 -5
  79. package/dist/kernel/transport/local-transport.d.ts +54 -2
  80. package/dist/kernel/transport/socket-channel.d.ts +40 -1
  81. package/dist/{manifest-system-deps-Bo92lg5u.js → manifest-system-deps-DfpwaXkh.js} +1725 -614
  82. package/dist/{manifest-system-deps-D--wyMD9.mjs → manifest-system-deps-iadzxSBr.mjs} +1850 -841
  83. package/dist/process/proc-stat.d.ts +75 -0
  84. package/dist/process/resource-monitor.d.ts +9 -1
  85. package/dist/{resource-monitor-CdnzxBLP.js → resource-monitor-LVE1BoLs.js} +42 -23
  86. package/dist/{resource-monitor-BWmQ5i-o.mjs → resource-monitor-Vp0bPV3U.mjs} +42 -22
  87. package/dist/{retired-settings-keys-xEAlYnv7.js → retired-settings-keys-7iEsSzmD.js} +1 -1
  88. package/dist/{retired-settings-keys-vK_U-CG2.mjs → retired-settings-keys-DQnfr__p.mjs} +1 -1
  89. package/dist/wal-checkpoint-policy-CAcg63o-.mjs +92 -0
  90. package/dist/wal-checkpoint-policy-CCJZngds.js +127 -0
  91. package/dist/wal-checkpoint-worker.js +138 -0
  92. package/dist/wal-checkpoint-worker.mjs +137 -0
  93. package/package.json +1 -1
  94. package/dist/{event-loop-stall-monitor-DM9OAzy8.js → event-loop-stall-monitor-CAsHdMwP.js} +1 -1
  95. package/dist/{event-loop-stall-monitor-B3CFd9tM.mjs → event-loop-stall-monitor-GPrVvx14.mjs} +1 -1
  96. package/dist/{tls-DOTmtLCW.mjs → tls-2xbgg78V.mjs} +2 -2
  97. package/dist/{tls-BxQlomxd.js → tls-BFs4PzoW.js} +2 -2
@@ -6,12 +6,12 @@ let _camstack_types_node = require("@camstack/types/node");
6
6
  let node_fs_promises = require("node:fs/promises");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_chunk.__toESM(node_path);
9
- let node_child_process = require("node:child_process");
10
- let node_util = require("node:util");
11
9
  let node_fs = require("node:fs");
12
10
  node_fs = require_chunk.__toESM(node_fs);
11
+ let node_child_process = require("node:child_process");
13
12
  let node_os = require("node:os");
14
13
  node_os = require_chunk.__toESM(node_os);
14
+ let node_util = require("node:util");
15
15
  let node_http = require("node:http");
16
16
  let node_v8 = require("node:v8");
17
17
  node_v8 = require_chunk.__toESM(node_v8);
@@ -155,6 +155,166 @@ function formatEventPlane(window) {
155
155
  return ` fanoutMode=uds:${window.udsMode}/cross-node:${window.crossNodeMode} children=${window.childrenConnected} childrenUndeclared=${window.childrenUndeclared} fanoutSent=${window.fanoutSent} fanoutSuppressed=${window.fanoutSuppressed} crossNodeDelivered=${window.crossNodeDelivered} crossNodeSuppressed=${window.crossNodeSuppressed}` + (top.length === 0 ? "" : ` fanoutTop=${top}`);
156
156
  }
157
157
  //#endregion
158
+ //#region src/kernel/transport/cap-action-census.ts
159
+ /** A process that has never read the census. */
160
+ var EMPTY_CAP_ACTION_READING = /* @__PURE__ */ new Map();
161
+ /**
162
+ * Bytes that crossed on a frame carrying no cap envelope.
163
+ *
164
+ * Named rather than omitted so the census's coverage is readable off its own
165
+ * output: `register`, `addon-call`, log frames and the event fan-out all land
166
+ * here. A census that reported only what it could name would look like a
167
+ * complete attribution of whatever share it happened to cover.
168
+ */
169
+ var CAP_ACTION_UNLABELLED = "(unlabelled)";
170
+ /** Bytes of actions past {@link MAX_CAP_ACTIONS_PER_PEER} on one peer. */
171
+ var CAP_ACTION_OVERFLOW_LABEL = "(overflow)";
172
+ /**
173
+ * Distinct named actions kept per peer before the overflow bucket takes over.
174
+ *
175
+ * Sixty-four against a measured worst case of 14 (`pipeline-analytics`, the
176
+ * heaviest peer on this hub, 2026-09-11) — four times the observed shape, so a
177
+ * peer has to change character before it overflows, and a bound that is never
178
+ * reached in practice still forbids the unbounded growth a wire-supplied key
179
+ * makes possible in principle.
180
+ */
181
+ var MAX_CAP_ACTIONS_PER_PEER = 64;
182
+ /**
183
+ * `<cap>.<method>` for a cap envelope, `null` for anything else.
184
+ *
185
+ * Both directions are labelled by the same function: a child's `cap-call-out`
186
+ * and the parent's `cap-call` carry `capName` + `method` in the same two keys,
187
+ * so one predicate covers the whole plane. Anything else — a `register`, an
188
+ * `addon-call`, a log or an event — is deliberately unlabelled; see
189
+ * {@link CAP_ACTION_UNLABELLED} for where its bytes go.
190
+ *
191
+ * Cost: two property reads, two `typeof`s and one concatenation of two short
192
+ * strings, called once per `req` frame (not per frame). Against the
193
+ * `encodeFrame` it sits beside — 1 948–1 975 ns on a 0.33 kB body — it is
194
+ * noise, and unlike `recordSocketFrame` it is not on the `evt` path at all.
195
+ */
196
+ function capActionLabel(body) {
197
+ if (typeof body !== "object" || body === null) return null;
198
+ const env = body;
199
+ if (env.kind !== "cap-call" && env.kind !== "cap-call-out") return null;
200
+ if (typeof env.capName !== "string" || typeof env.method !== "string") return null;
201
+ return `${env.capName}.${env.method}`;
202
+ }
203
+ function emptyCounters() {
204
+ return {
205
+ calls: 0,
206
+ reqBytes: 0,
207
+ resBytes: 0,
208
+ raw: 0,
209
+ decoded: 0
210
+ };
211
+ }
212
+ function createCapActionCensus() {
213
+ const peers = /* @__PURE__ */ new Map();
214
+ /**
215
+ * The per-peer slot for `action`, creating it under the bound.
216
+ *
217
+ * Past {@link MAX_CAP_ACTIONS_PER_PEER} a NEW label is folded into the
218
+ * overflow bucket; labels already present keep their own slot, so a peer
219
+ * that overflows does not lose the attribution it had built.
220
+ */
221
+ const slot = (peerId, action) => {
222
+ let byAction = peers.get(peerId);
223
+ if (byAction === void 0) {
224
+ byAction = /* @__PURE__ */ new Map();
225
+ peers.set(peerId, byAction);
226
+ }
227
+ const existing = byAction.get(action);
228
+ if (existing !== void 0) return existing;
229
+ if (byAction.size >= 64) {
230
+ let overflow = byAction.get(CAP_ACTION_OVERFLOW_LABEL);
231
+ if (overflow === void 0) {
232
+ overflow = emptyCounters();
233
+ byAction.set(CAP_ACTION_OVERFLOW_LABEL, overflow);
234
+ }
235
+ return overflow;
236
+ }
237
+ const fresh = emptyCounters();
238
+ byAction.set(action, fresh);
239
+ return fresh;
240
+ };
241
+ return {
242
+ record(peerId, action, reqBytes, resBytes) {
243
+ const c = slot(peerId, action);
244
+ c.calls += 1;
245
+ c.reqBytes += reqBytes;
246
+ c.resBytes += resBytes;
247
+ },
248
+ recordForward(peerId, action, kind) {
249
+ const c = slot(peerId, action);
250
+ if (kind === "raw") c.raw += 1;
251
+ else c.decoded += 1;
252
+ },
253
+ recordUnlabelled(peerId, bytes) {
254
+ const c = slot(peerId, CAP_ACTION_UNLABELLED);
255
+ c.resBytes += bytes;
256
+ },
257
+ forget(peerId) {
258
+ peers.delete(peerId);
259
+ },
260
+ read() {
261
+ const out = /* @__PURE__ */ new Map();
262
+ for (const [peerId, byAction] of peers) {
263
+ const copy = /* @__PURE__ */ new Map();
264
+ for (const [action, c] of byAction) copy.set(action, {
265
+ calls: c.calls,
266
+ reqBytes: c.reqBytes,
267
+ resBytes: c.resBytes,
268
+ raw: c.raw,
269
+ decoded: c.decoded
270
+ });
271
+ out.set(peerId, copy);
272
+ }
273
+ return out;
274
+ }
275
+ };
276
+ }
277
+ function sampleOf(reading, peerId, action) {
278
+ return reading.get(peerId)?.get(action);
279
+ }
280
+ /**
281
+ * Subtract the previous reading from the current one.
282
+ *
283
+ * A peer whose runner RESPAWNED reconnects under the same id with counters
284
+ * that went backwards. Clamped at 0, exactly as `diffSocketPlane` clamps for
285
+ * the same reason: its traffic in that window is genuinely unknown, and a
286
+ * negative number on a rate reads as a broken meter.
287
+ */
288
+ function diffCapActionCensus(before, current, topN) {
289
+ const deltas = [];
290
+ let labelledBytes = 0;
291
+ let totalBytes = 0;
292
+ for (const [peerId, byAction] of current) for (const [action, now] of byAction) {
293
+ const then = sampleOf(before, peerId, action);
294
+ const reqBytes = Math.max(0, now.reqBytes - (then?.reqBytes ?? 0));
295
+ const resBytes = Math.max(0, now.resBytes - (then?.resBytes ?? 0));
296
+ const bytes = reqBytes + resBytes;
297
+ totalBytes += bytes;
298
+ if (action === "(unlabelled)" || action === "(overflow)") continue;
299
+ labelledBytes += bytes;
300
+ if (bytes === 0) continue;
301
+ deltas.push({
302
+ peerId,
303
+ action,
304
+ calls: Math.max(0, now.calls - (then?.calls ?? 0)),
305
+ reqBytes,
306
+ resBytes,
307
+ raw: Math.max(0, now.raw - (then?.raw ?? 0)),
308
+ decoded: Math.max(0, now.decoded - (then?.decoded ?? 0))
309
+ });
310
+ }
311
+ return {
312
+ top: deltas.toSorted((a, b) => b.reqBytes + b.resBytes - (a.reqBytes + a.resBytes) || a.peerId.localeCompare(b.peerId) || a.action.localeCompare(b.action)).slice(0, topN),
313
+ labelledBytes,
314
+ totalBytes
315
+ };
316
+ }
317
+ //#endregion
158
318
  //#region src/kernel/transport/socket-traffic.ts
159
319
  function createSocketDirectionCounters() {
160
320
  return {
@@ -311,12 +471,17 @@ function createSocketPlaneReader(sources) {
311
471
  peers.push({
312
472
  peerId: child.childId,
313
473
  tx: traffic.tx,
314
- rx: traffic.rx
474
+ rx: traffic.rx,
475
+ queuedBytes: registry.getChildQueuedBytes(child.childId)
315
476
  });
316
477
  }
478
+ const forward = registry.readForwardCensus?.();
479
+ const actions = registry.readActionCensus?.();
317
480
  return {
318
481
  peers,
319
- peersConnected: listed.length
482
+ peersConnected: listed.length,
483
+ ...forward === void 0 ? {} : { forward },
484
+ ...actions === void 0 ? {} : { actions }
320
485
  };
321
486
  } catch {
322
487
  return;
@@ -334,8 +499,41 @@ function createSocketPlaneReader(sources) {
334
499
  * stops being read.
335
500
  */
336
501
  var SOCKET_PLANE_TOP_N = 5;
502
+ /** How many queue-holding peers get named. Three: a queue is an exception,
503
+ * and the line already carries five names for traffic. */
504
+ var SOCKET_QUEUE_TOP_N = 3;
505
+ /** The queue reading over the current counters — a level, so no baseline. */
506
+ function readSocketQueues(current) {
507
+ let totalBytes = 0;
508
+ let unmeasured = 0;
509
+ const holding = [];
510
+ for (const peer of current.peers) {
511
+ if (peer.queuedBytes === null) {
512
+ unmeasured += 1;
513
+ continue;
514
+ }
515
+ totalBytes += peer.queuedBytes;
516
+ if (peer.queuedBytes > 0) holding.push({
517
+ peerId: peer.peerId,
518
+ bytes: peer.queuedBytes
519
+ });
520
+ }
521
+ return {
522
+ totalBytes,
523
+ unmeasured,
524
+ top: holding.toSorted((a, b) => b.bytes - a.bytes || a.peerId.localeCompare(b.peerId)).slice(0, 3)
525
+ };
526
+ }
337
527
  /** A process that has never read the plane. */
338
528
  var EMPTY_SOCKET_PLANE_BASELINE = { peers: /* @__PURE__ */ new Map() };
529
+ /** The census over the window; a counter that went backwards reads as quiet. */
530
+ function diffForward(current, before) {
531
+ if (current === void 0) return void 0;
532
+ return {
533
+ raw: Math.max(0, current.raw - (before?.raw ?? 0)),
534
+ decoded: Math.max(0, current.decoded - (before?.decoded ?? 0))
535
+ };
536
+ }
339
537
  function diffDirection(current, before) {
340
538
  const previous = before ?? EMPTY_SOCKET_DIRECTION;
341
539
  return {
@@ -361,6 +559,14 @@ function addDirection(into, add) {
361
559
  function peerMessages(delta) {
362
560
  return socketDirectionMessages(delta.tx) + socketDirectionMessages(delta.rx);
363
561
  }
562
+ /**
563
+ * Total bytes a peer moved in the window, both directions — the OTHER
564
+ * ranking, and the one that names a peer the message ranking structurally
565
+ * cannot see (D454).
566
+ */
567
+ function peerBytes(delta) {
568
+ return socketDirectionBytes(delta.tx) + socketDirectionBytes(delta.rx);
569
+ }
364
570
  /** Subtract the previous cumulative reading from the current one. */
365
571
  function diffSocketPlane(baseline, current, topN = 5) {
366
572
  const deltas = [];
@@ -377,12 +583,18 @@ function diffSocketPlane(baseline, current, topN = 5) {
377
583
  rx = addDirection(rx, delta.rx);
378
584
  if (peerMessages(delta) > 0) deltas.push(delta);
379
585
  }
586
+ const forward = diffForward(current.forward, baseline.forward);
587
+ const actions = current.actions === void 0 ? void 0 : diffCapActionCensus(baseline.actions ?? EMPTY_CAP_ACTION_READING, current.actions, 5);
380
588
  return {
381
589
  peersConnected: current.peersConnected,
382
590
  peersMeasured: current.peers.length,
383
591
  tx,
384
592
  rx,
385
- top: deltas.toSorted((a, b) => peerMessages(b) - peerMessages(a) || a.peerId.localeCompare(b.peerId)).slice(0, topN)
593
+ top: deltas.toSorted((a, b) => peerMessages(b) - peerMessages(a) || a.peerId.localeCompare(b.peerId)).slice(0, topN),
594
+ topBytes: deltas.toSorted((a, b) => peerBytes(b) - peerBytes(a) || a.peerId.localeCompare(b.peerId)).slice(0, topN),
595
+ queued: readSocketQueues(current),
596
+ ...forward === void 0 ? {} : { forward },
597
+ ...actions === void 0 ? {} : { actions }
386
598
  };
387
599
  }
388
600
  function createSocketPlaneMeter(reader, topN = 5) {
@@ -391,24 +603,62 @@ function createSocketPlaneMeter(reader, topN = 5) {
391
603
  const current = reader();
392
604
  if (current === void 0) return void 0;
393
605
  const window = diffSocketPlane(baseline, current, topN);
394
- baseline = { peers: new Map(current.peers.map((p) => [p.peerId, p])) };
606
+ baseline = {
607
+ peers: new Map(current.peers.map((p) => [p.peerId, p])),
608
+ ...current.forward === void 0 ? {} : { forward: current.forward },
609
+ ...current.actions === void 0 ? {} : { actions: current.actions }
610
+ };
395
611
  return window;
396
612
  } };
397
613
  }
398
614
  /** Bytes → whole kB. The plane moves 180–330 MB/min; raw bytes would put
399
615
  * nine-digit numbers on a line that has to stay readable. */
400
- function kb(bytes) {
616
+ function kb$1(bytes) {
401
617
  return Math.round(bytes / 1024);
402
618
  }
403
619
  /** `req:<msgs>/<kB>,res:<msgs>/<kB>,evt:<msgs>/<kB>` — every kind, always,
404
620
  * including at zero: a kind that went quiet is a reading, not a blank. */
405
621
  function formatDirection(sample) {
406
- return `req:${sample.reqMessages}/${kb(sample.reqBytes)}kB,res:${sample.resMessages}/${kb(sample.resBytes)}kB,evt:${sample.evtMessages}/${kb(sample.evtBytes)}kB`;
622
+ return `req:${sample.reqMessages}/${kb$1(sample.reqBytes)}kB,res:${sample.resMessages}/${kb$1(sample.resBytes)}kB,evt:${sample.evtMessages}/${kb$1(sample.evtBytes)}kB`;
623
+ }
624
+ /** `<req>/<res>/<evt>` in kB — the byte triple, in the same order as the
625
+ * message triple next to it, so the two read as one table. */
626
+ function formatPeerBytes(sample) {
627
+ return `${kb$1(sample.reqBytes)}/${kb$1(sample.resBytes)}/${kb$1(sample.evtBytes)}`;
407
628
  }
408
- /** `<peer>:tx=<req>/<res>/<evt>:rx=<req>/<res>/<evt>:kB=<tx>/<rx>` — the whole
409
- * peer×kind cross for one peer, which is the question in one token. */
629
+ /**
630
+ * `<peer>:tx=<req>/<res>/<evt>:rx=…:txkB=<req>/<res>/<evt>:rxkB=…` — the whole
631
+ * peer×kind cross for one peer, messages and bytes, which is the question in
632
+ * one token.
633
+ *
634
+ * The bytes were a per-direction TOTAL until D454, and the total could not
635
+ * answer the question `socket-traffic.ts` says this instrument exists to
636
+ * settle: whether a peer's bytes are RESPONSES (media, and D9/D18 re-opens) or
637
+ * fan-out (`evt`, which scales with runner count and nothing an operator
638
+ * asked for). On 2026-09-11 hub-main wrote 118 MB in one minute to
639
+ * `hub/pipeline-analytics` and the line could only say "118 MB" — separating
640
+ * `res` from `evt` took an hour of arithmetic against the plane-wide totals.
641
+ */
410
642
  function formatPeer(delta) {
411
- return `${delta.peerId}:tx=${delta.tx.reqMessages}/${delta.tx.resMessages}/${delta.tx.evtMessages}:rx=${delta.rx.reqMessages}/${delta.rx.resMessages}/${delta.rx.evtMessages}:kB=${kb(socketDirectionBytes(delta.tx))}/${kb(socketDirectionBytes(delta.rx))}`;
643
+ return `${delta.peerId}:tx=${delta.tx.reqMessages}/${delta.tx.resMessages}/${delta.tx.evtMessages}:rx=${delta.rx.reqMessages}/${delta.rx.resMessages}/${delta.rx.evtMessages}:txkB=${formatPeerBytes(delta.tx)}:rxkB=${formatPeerBytes(delta.rx)}`;
644
+ }
645
+ /**
646
+ * One named action: `<peer>!<cap>.<method>:<kB>kB/<calls>:r<raw>/d<decoded>`.
647
+ *
648
+ * `!` separates the peer from the action because `.` already separates cap
649
+ * from method and a single delimiter for both would make the token ambiguous
650
+ * to split.
651
+ *
652
+ * The raw/decoded pair is the half that did not exist anywhere before D456.
653
+ * `socketFwd=` answers it for the whole plane — 30 349/5 051 on this hub — and
654
+ * a plane-wide ratio cannot tell a method that is entirely forwarded from one
655
+ * that is entirely decoded, which is the difference between bytes hub-main
656
+ * copies and bytes hub-main reads. The census pays for the two counters it
657
+ * takes; the conclusion they buy is which of the line's megabytes are actually
658
+ * expensive.
659
+ */
660
+ function formatAction(entry) {
661
+ return `${entry.peerId}!${entry.action}:${kb$1(entry.reqBytes + entry.resBytes)}kB/${entry.calls}:r${entry.raw}/d${entry.decoded}`;
412
662
  }
413
663
  /**
414
664
  * The suffix appended to a `[mem]` line — leading space included, empty when
@@ -421,14 +671,234 @@ function formatPeer(delta) {
421
671
  * is the only way to see an unmeasured channel, and the per-kind splits because
422
672
  * "the plane went quiet for a minute" is a reading.
423
673
  *
424
- * `socketTop` is the one conditional field — a ranked list of nothing is noise —
425
- * and it is a single comma-separated value so the field COUNT does not change
426
- * from line to line.
674
+ * `socketTop` is a conditional field — a ranked list of nothing is noise — and
675
+ * it is a single comma-separated value so the field COUNT does not change from
676
+ * line to line.
677
+ *
678
+ * `socketTopBytes` is the other one, and its PRESENCE is the reading: it is
679
+ * printed only when ranking by bytes names a peer that ranking by messages
680
+ * does not. A line without it says the two rankings agree; a line with it says
681
+ * a peer is moving few, heavy frames and names it (D454).
427
682
  */
428
683
  function formatSocketPlane(window) {
429
684
  if (window === void 0) return "";
430
685
  const top = window.top.map(formatPeer).join(",");
431
- return ` socketPeers=${window.peersMeasured}/${window.peersConnected} socketTx=${formatDirection(window.tx)} socketRx=${formatDirection(window.rx)}` + (top.length === 0 ? "" : ` socketTop=${top}`);
686
+ const named = new Set(window.top.map((p) => p.peerId));
687
+ const topBytes = window.topBytes.filter((p) => !named.has(p.peerId)).map(formatPeer).join(",");
688
+ const queuedTop = window.queued.top.map((p) => `${p.peerId}:${kb$1(p.bytes)}`).join(",");
689
+ return ` socketPeers=${window.peersMeasured}/${window.peersConnected} socketTx=${formatDirection(window.tx)} socketRx=${formatDirection(window.rx)}` + (top.length === 0 ? "" : ` socketTop=${top}`) + (topBytes.length === 0 ? "" : ` socketTopBytes=${topBytes}`) + ` socketQueued=${kb$1(window.queued.totalBytes)}kB` + (window.queued.unmeasured === 0 ? "" : ` socketQueuedUnmeasured=${window.queued.unmeasured}`) + (queuedTop.length === 0 ? "" : ` socketQueuedTop=${queuedTop}`) + (window.forward === void 0 ? "" : ` socketFwd=${window.forward.raw}/${window.forward.decoded}`) + (window.actions === void 0 ? "" : ` socketActions=${kb$1(window.actions.labelledBytes)}/${kb$1(window.actions.totalBytes)}kB` + (window.actions.top.length === 0 ? "" : ` socketTopActions=${window.actions.top.map(formatAction).join(",")}`));
690
+ }
691
+ //#endregion
692
+ //#region src/kernel/moleculer/mesh-queue-report.ts
693
+ function isRecord$1(value) {
694
+ return typeof value === "object" && value !== null;
695
+ }
696
+ /** The shape `TcpWriter` keeps its sockets in. Anything else is not a mesh. */
697
+ function writerSocketsOf(broker) {
698
+ if (!isRecord$1(broker)) return void 0;
699
+ const transit = broker["transit"];
700
+ if (!isRecord$1(transit)) return void 0;
701
+ const transporter = transit["tx"];
702
+ if (!isRecord$1(transporter)) return void 0;
703
+ const writer = transporter["writer"];
704
+ if (!isRecord$1(writer)) return void 0;
705
+ const sockets = writer["sockets"];
706
+ return sockets instanceof Map ? sockets : void 0;
707
+ }
708
+ function readSocket(nodeId, socket) {
709
+ if (typeof nodeId !== "string" || !isRecord$1(socket)) return void 0;
710
+ const queuedBytes = socket["writableLength"];
711
+ const bytesWritten = socket["bytesWritten"];
712
+ if (typeof queuedBytes !== "number" || typeof bytesWritten !== "number") return void 0;
713
+ return {
714
+ nodeId,
715
+ queuedBytes,
716
+ bytesWritten
717
+ };
718
+ }
719
+ /**
720
+ * Build a reader over a lazily-resolved broker. Wrapped: a broker mid-teardown
721
+ * may throw, and a diagnostic that can crash the process it watches is worse
722
+ * than none.
723
+ */
724
+ function createMeshQueueReader(getBroker) {
725
+ return () => {
726
+ try {
727
+ const sockets = writerSocketsOf(getBroker());
728
+ if (sockets === void 0) return void 0;
729
+ const peers = [];
730
+ for (const [nodeId, socket] of sockets) {
731
+ const peer = readSocket(nodeId, socket);
732
+ if (peer !== void 0) peers.push(peer);
733
+ }
734
+ return peers;
735
+ } catch {
736
+ return;
737
+ }
738
+ };
739
+ }
740
+ /** At most this many nodes named. A cluster has a handful. */
741
+ var MESH_QUEUE_TOP_N = 3;
742
+ function kb(bytes) {
743
+ return Math.round(bytes / 1024);
744
+ }
745
+ /**
746
+ * The suffix appended to a `[mem]` line — leading space included, empty when
747
+ * this process has no mesh writer. `meshQueued` is printed always, at zero
748
+ * included: a quiet mesh is a reading. `meshQueuedTop` names only nodes with
749
+ * a queue, largest first — a ranked list of zeros is noise.
750
+ */
751
+ function formatMeshQueue(peers) {
752
+ if (peers === void 0) return "";
753
+ const total = peers.reduce((sum, p) => sum + p.queuedBytes, 0);
754
+ const top = peers.filter((p) => p.queuedBytes > 0).toSorted((a, b) => b.queuedBytes - a.queuedBytes || a.nodeId.localeCompare(b.nodeId)).slice(0, 3).map((p) => `${p.nodeId}:${kb(p.queuedBytes)}`).join(",");
755
+ return ` meshQueued=${kb(total)}kB` + (top.length === 0 ? "" : ` meshQueuedTop=${top}`);
756
+ }
757
+ //#endregion
758
+ //#region src/kernel/outbound-queue-watch.ts
759
+ /**
760
+ * outbound-queue-watch — an outbound socket queue that HOLDS is named, and its
761
+ * release is timestamped (D446).
762
+ *
763
+ * ## The blind spot this closes
764
+ *
765
+ * On 2026-09-10 hub-main's `arrayBuffers` climbed twice at a constant rate
766
+ * (20 MB/min to 1 264 MB; 63 MB/min for 77 min to 4 968 MB), LIVE across a
767
+ * forced compaction, with ~4 % of JS heap alongside, and each time released in
768
+ * one instant with no line from any owner. The investigation excluded "every
769
+ * socket write queue" by summing `writableLength` over
770
+ * `process._getActiveHandles()` — 84 MB, all of it the phone's WebSocket.
771
+ *
772
+ * `_getActiveHandles()` lists only handles that keep the loop alive. An
773
+ * UNREF'D socket is not one of them, and Moleculer's `TcpWriter.connect`
774
+ * unrefs every socket it opens to a peer node (`transporters/tcp/tcp-writer.js`,
775
+ * `socket.unref()`). So the one outbound plane that writes to another MACHINE
776
+ * — where a reader that stops draining is the ordinary failure — was never in
777
+ * the sum. The UDS children were in it, but only as a total, at one instant,
778
+ * by hand.
779
+ *
780
+ * ## What this is
781
+ *
782
+ * A pure gate over per-peer queue depths the heartbeat reads on every probe:
783
+ * the UDS children's `socket.writableLength` (`SocketChannel.queuedBytes`) and
784
+ * the mesh writer sockets' (`mesh-queue-report.ts`). It says three things and
785
+ * nothing else:
786
+ *
787
+ * - a peer whose queue has been at or above {@link OUTBOUND_QUEUE_THRESHOLD_BYTES}
788
+ * for {@link OUTBOUND_QUEUE_SUSTAIN_MS} is NAMED, with its current and peak
789
+ * depth and how long it has held — at most once per
790
+ * {@link OUTBOUND_QUEUE_COOLDOWN_MS};
791
+ * - a named peer whose queue then falls under half the threshold, or that
792
+ * vanishes from the readings (its socket died — which is how a queue is
793
+ * dropped in one instant), is reported as RELEASED with its peak and the
794
+ * hold, so the release the episodes never logged has a line and a peer;
795
+ * - a burst that drains inside the sustain window says nothing. Backpressure
796
+ * measures LIVENESS, not identity (D444): a peer momentarily behind is
797
+ * coming back.
798
+ *
799
+ * ## What it is NOT
800
+ *
801
+ * Not a bound. It terminates nothing: a UDS child whose channel is destroyed
802
+ * loses every in-flight call, and a mesh socket Moleculer reconnects on its
803
+ * own terms. The WS guard (`ws-slow-consumer-guard.ts`) bounds the one plane
804
+ * whose client is expendable. Here the line is the deliverable; what to do
805
+ * about a peer that holds is decided with the peer named, not before.
806
+ */
807
+ /** 64 MiB. Below this a queue is a burst on a busy peer (the orchestrator's
808
+ * channel moves ~1.5 MB/s of audio responses); above it, and held, it is
809
+ * retention with a name. */
810
+ var OUTBOUND_QUEUE_THRESHOLD_BYTES = 64 * 1024 * 1024;
811
+ /** Matches the ArrayBuffer census sustain: a minute above the bound. */
812
+ var OUTBOUND_QUEUE_SUSTAIN_MS = 6e4;
813
+ /** A held queue is re-named every ten minutes, not every probe. */
814
+ var OUTBOUND_QUEUE_COOLDOWN_MS = 10 * 6e4;
815
+ /** A release is a fall under this fraction of the threshold. */
816
+ var RELEASE_RATIO = .5;
817
+ /**
818
+ * The pure decision — threshold, sustain, cooldown, release, gone — testable
819
+ * without a socket.
820
+ */
821
+ function createOutboundQueueGate(options = {}) {
822
+ const thresholdBytes = options.thresholdBytes ?? 67108864;
823
+ const sustainMs = options.sustainMs ?? 6e4;
824
+ const cooldownMs = options.cooldownMs ?? 6e5;
825
+ const tracked = /* @__PURE__ */ new Map();
826
+ const judgeOne = (peerId, bytes, at) => {
827
+ const state = tracked.get(peerId);
828
+ if (bytes >= thresholdBytes) {
829
+ const next = state === void 0 ? {
830
+ aboveSince: at,
831
+ peakBytes: bytes,
832
+ lastNamedAt: null
833
+ } : {
834
+ ...state,
835
+ peakBytes: Math.max(state.peakBytes, bytes)
836
+ };
837
+ const heldForMs = at - next.aboveSince;
838
+ const due = heldForMs >= sustainMs && (next.lastNamedAt === null || at - next.lastNamedAt >= cooldownMs);
839
+ tracked.set(peerId, due ? {
840
+ ...next,
841
+ lastNamedAt: at
842
+ } : next);
843
+ return due ? {
844
+ kind: "sustained",
845
+ peerId,
846
+ bytes,
847
+ peakBytes: next.peakBytes,
848
+ heldForMs
849
+ } : void 0;
850
+ }
851
+ if (state === void 0) return void 0;
852
+ if (bytes >= thresholdBytes * RELEASE_RATIO) return void 0;
853
+ tracked.delete(peerId);
854
+ if (state.lastNamedAt === null) return void 0;
855
+ return {
856
+ kind: "released",
857
+ peerId,
858
+ peakBytes: state.peakBytes,
859
+ toBytes: bytes,
860
+ heldForMs: at - state.aboveSince,
861
+ gone: false
862
+ };
863
+ };
864
+ return { observe(readings, at) {
865
+ const verdicts = [];
866
+ const seen = /* @__PURE__ */ new Set();
867
+ for (const reading of readings) {
868
+ seen.add(reading.peerId);
869
+ const verdict = judgeOne(reading.peerId, reading.bytes, at);
870
+ if (verdict !== void 0) verdicts.push(verdict);
871
+ }
872
+ for (const [peerId, state] of tracked) {
873
+ if (seen.has(peerId)) continue;
874
+ tracked.delete(peerId);
875
+ if (state.lastNamedAt === null) continue;
876
+ verdicts.push({
877
+ kind: "released",
878
+ peerId,
879
+ peakBytes: state.peakBytes,
880
+ toBytes: 0,
881
+ heldForMs: at - state.aboveSince,
882
+ gone: true
883
+ });
884
+ }
885
+ return verdicts;
886
+ } };
887
+ }
888
+ var BYTES_PER_MB$1 = 1048576;
889
+ function mb$2(bytes) {
890
+ return Math.round(bytes / BYTES_PER_MB$1);
891
+ }
892
+ /** `60s` under two minutes, `77m` from there — the hold is read at a glance. */
893
+ function duration(ms) {
894
+ const seconds = Math.round(ms / 1e3);
895
+ return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)}m`;
896
+ }
897
+ /** The line for one verdict. Both carry the peer, the bytes and the hold;
898
+ * the sustained one also says why the older census could not see it. */
899
+ function formatOutboundQueueVerdict(label, verdict) {
900
+ if (verdict.kind === "sustained") return `[mem] ${label} outbound queue to ${verdict.peerId} has held ${mb$2(verdict.bytes)}MB (peak ${mb$2(verdict.peakBytes)}MB) for ${duration(verdict.heldForMs)} — the peer is not draining what this process writes to it. This is socket.writableLength on the channel itself; a sum over _getActiveHandles() does not list an unref'd socket and would miss it.`;
901
+ return `[mem] ${label} outbound queue to ${verdict.peerId} RELEASED — ${mb$2(verdict.peakBytes)}MB at peak, ${mb$2(verdict.toBytes)}MB now, held ${duration(verdict.heldForMs)}` + (verdict.gone ? " (peer gone: the socket closed and dropped its queue)" : " (drained)");
432
902
  }
433
903
  //#endregion
434
904
  //#region src/kernel/heap-spaces.ts
@@ -852,6 +1322,125 @@ function formatArrayBufferCensus(label, report, context) {
852
1322
  return `[mem] ${label} arrayBuffer census: live=${live}MB in ${report.count} ArrayBuffers (raw arrayBuffers=${context.rawMb}MB at the trigger → ${verdict}) top sizes: ${report.topSizes.map(formatExact).join(", ") || "(none)"}; buckets: ${report.buckets.map(formatBucket).join(", ") || "(none)"}; detached=${report.detached} took=${context.tookMs}ms — cost: ONE forced full GC (the same pause as a reclaim pass), walked in memory, nothing serialised, nothing written; trigger: ${context.reason}`;
853
1323
  }
854
1324
  //#endregion
1325
+ //#region src/kernel/gc-census.ts
1326
+ function countsOf(entries, kinds) {
1327
+ let majors = 0;
1328
+ let minors = 0;
1329
+ let incrementals = 0;
1330
+ let pauseMs = 0;
1331
+ for (const e of entries) {
1332
+ if (e.kind === kinds.major) majors++;
1333
+ else if (e.kind === kinds.minor) minors++;
1334
+ else if (e.kind === kinds.incremental) incrementals++;
1335
+ pauseMs += e.durationMs;
1336
+ }
1337
+ return {
1338
+ majors,
1339
+ minors,
1340
+ incrementals,
1341
+ pauseMs,
1342
+ windowMs: 0
1343
+ };
1344
+ }
1345
+ /**
1346
+ * Build a census over an injected observer.
1347
+ *
1348
+ * Returns `undefined` when the observer cannot be installed — the caller then
1349
+ * prints nothing, which is the honest reading. It must never degrade into a
1350
+ * census that reports zero collections: a process whose GC is not being
1351
+ * observed and a process that is not collecting look identical on the line, and
1352
+ * they are the two answers furthest apart.
1353
+ */
1354
+ function createGcCensus(install, kinds, startedAt) {
1355
+ let pending = [];
1356
+ const stopObserving = install((entry) => {
1357
+ pending.push(entry);
1358
+ });
1359
+ if (stopObserving === void 0) return void 0;
1360
+ let windowStart = startedAt;
1361
+ return {
1362
+ read: (at) => {
1363
+ const windowMs = at - windowStart;
1364
+ if (windowMs < 1e3) return null;
1365
+ const taken = pending;
1366
+ pending = [];
1367
+ windowStart = at;
1368
+ return {
1369
+ ...countsOf(taken, kinds),
1370
+ windowMs
1371
+ };
1372
+ },
1373
+ stop: () => {
1374
+ stopObserving();
1375
+ pending = [];
1376
+ }
1377
+ };
1378
+ }
1379
+ /**
1380
+ * The observer this process actually gets.
1381
+ *
1382
+ * Everything that can fail is inside the try: a runtime without
1383
+ * `perf_hooks`, without the `gc` entry type, or without the kind constants
1384
+ * yields `undefined` from `install`, and {@link createGcCensus} then declines
1385
+ * to exist rather than reporting zeros.
1386
+ */
1387
+ function nodeGcObserver() {
1388
+ return {
1389
+ kinds: readKinds({
1390
+ major: 4,
1391
+ minor: 1,
1392
+ incremental: 8
1393
+ }),
1394
+ install: (observe) => {
1395
+ try {
1396
+ const observer = new (process.getBuiltinModule("node:perf_hooks")).PerformanceObserver((list) => {
1397
+ for (const entry of list.getEntries()) {
1398
+ const detail = "detail" in entry ? entry.detail : void 0;
1399
+ observe({
1400
+ kind: typeof detail === "object" && detail !== null && "kind" in detail && typeof detail.kind === "number" ? detail.kind : -1,
1401
+ durationMs: entry.duration
1402
+ });
1403
+ }
1404
+ });
1405
+ observer.observe({ entryTypes: ["gc"] });
1406
+ return () => {
1407
+ observer.disconnect();
1408
+ };
1409
+ } catch {
1410
+ return;
1411
+ }
1412
+ }
1413
+ };
1414
+ }
1415
+ function readKinds(fallback) {
1416
+ try {
1417
+ const c = process.getBuiltinModule("node:perf_hooks").constants;
1418
+ return {
1419
+ major: c.NODE_PERFORMANCE_GC_MAJOR,
1420
+ minor: c.NODE_PERFORMANCE_GC_MINOR,
1421
+ incremental: c.NODE_PERFORMANCE_GC_INCREMENTAL
1422
+ };
1423
+ } catch {
1424
+ return fallback;
1425
+ }
1426
+ }
1427
+ /**
1428
+ * The two fields, APPENDED LAST on the `[mem]` line.
1429
+ *
1430
+ * D446's rule: no existing field moves, so every Loki prefix written since
1431
+ * 2026-08-29 still matches. `gc=` prints even when nothing was collected — a
1432
+ * window with no collections is a reading, and it is the reading that says a
1433
+ * process is idle rather than unobserved. `gcPauseMs=` is the MAIN-THREAD
1434
+ * pause only; the concurrent marking that cost hub-main 70 % of its CPU is on
1435
+ * the platform workers and no in-process counter sees it, which is why the
1436
+ * field is named for the thread it describes (D456, part 4).
1437
+ */
1438
+ function formatGcCensus(window) {
1439
+ if (window === null || window === void 0) return "";
1440
+ const perMin = (n) => window.windowMs <= 0 ? "?" : (n / (window.windowMs / 6e4)).toFixed(1);
1441
+ return ` gc=maj${window.majors}/min${window.minors}/inc${window.incrementals}@${Math.round(window.windowMs / 1e3)}s gcMajPerMin=${perMin(window.majors)} gcPauseMs=${Math.round(window.pauseMs)}`;
1442
+ }
1443
+ //#endregion
855
1444
  //#region src/kernel/heap-watch.ts
856
1445
  /**
857
1446
  * heap-watch — hub-main's own memory, on the record, and returned to the OS.
@@ -1416,6 +2005,27 @@ function format(label, s, loop, budgetMb, suffix) {
1416
2005
  return `${line} loopP50=${loop.p50Ms}ms loopP99=${loop.p99Ms}ms loopMax=${loop.maxMs}ms${suffix}`;
1417
2006
  }
1418
2007
  /**
2008
+ * Every outbound queue the heartbeat can reach, as the gate wants them: the
2009
+ * UDS children (only those whose channel keeps a reading — an unmeasured
2010
+ * peer is not a zero) and the mesh writer sockets, each prefixed with its
2011
+ * plane.
2012
+ */
2013
+ function outboundQueueReadings(uds, mesh) {
2014
+ const readings = [];
2015
+ for (const peer of uds?.peers ?? []) {
2016
+ if (peer.queuedBytes === null) continue;
2017
+ readings.push({
2018
+ peerId: `uds:${peer.peerId}`,
2019
+ bytes: peer.queuedBytes
2020
+ });
2021
+ }
2022
+ for (const node of mesh ?? []) readings.push({
2023
+ peerId: `mesh:${node.nodeId}`,
2024
+ bytes: node.queuedBytes
2025
+ });
2026
+ return readings;
2027
+ }
2028
+ /**
1419
2029
  * Start the heartbeat. Returns a stop function.
1420
2030
  *
1421
2031
  * `unref`ed so it can never hold the process open, and every read is wrapped:
@@ -1427,6 +2037,8 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
1427
2037
  const socketPlane = probes?.socketPlane === void 0 ? void 0 : createSocketPlaneMeter(probes.socketPlane);
1428
2038
  const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
1429
2039
  const now = reclaimOptions?.now ?? (() => Date.now());
2040
+ const gcObserver = probes?.gcObserver ?? nodeGcObserver();
2041
+ const gcCensus = createGcCensus(gcObserver.install, gcObserver.kinds, now());
1430
2042
  const triggerMb = reclaimOptions?.triggerMb ?? 1024;
1431
2043
  const minIntervalMs = reclaimOptions?.minIntervalMs ?? 12e4;
1432
2044
  const steadyStateIntervalMs = reclaimOptions?.steadyStateIntervalMs ?? 6e5;
@@ -1529,7 +2141,7 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
1529
2141
  const due = at - lastLoggedAt >= intervalMs;
1530
2142
  if (mode === "escalated" || due) {
1531
2143
  lastLoggedAt = at;
1532
- const line = format(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()));
2144
+ const line = format(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()) + formatMeshQueue(meshQueue?.()) + formatGcCensus(gcCensus?.read(at)));
1533
2145
  if (sample.nearLimit) sink.warn(`${line} — APPROACHING HEAP LIMIT`);
1534
2146
  else if (mode === "escalated") sink.warn(`${line} — heap elevated, sampling every ${probeIntervalMs}ms`);
1535
2147
  else sink.info(line);
@@ -1538,8 +2150,17 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
1538
2150
  checkRssBudget(sample, at);
1539
2151
  maybeReclaim(sample);
1540
2152
  judgeArrayBuffers(sample, at);
2153
+ judgeOutboundQueues(at);
1541
2154
  } catch {}
1542
2155
  };
2156
+ const meshQueue = probes?.meshQueue;
2157
+ const queueReader = probes?.socketPlane;
2158
+ const queueGate = queueReader === void 0 && meshQueue === void 0 ? void 0 : createOutboundQueueGate(probes?.outboundQueue);
2159
+ const judgeOutboundQueues = (at) => {
2160
+ if (queueGate === void 0) return;
2161
+ const readings = outboundQueueReadings(queueReader?.(), meshQueue?.());
2162
+ for (const verdict of queueGate.observe(readings, at)) sink.warn(formatOutboundQueueVerdict(label, verdict));
2163
+ };
1543
2164
  const censusProbe = probes?.arrayBufferCensus;
1544
2165
  const censusGate = censusProbe === void 0 ? void 0 : createArrayBufferCensusGate(censusProbe);
1545
2166
  let censusInFlight = false;
@@ -1577,6 +2198,7 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
1577
2198
  stopped = true;
1578
2199
  clearInterval(timer);
1579
2200
  loopDelay?.stop();
2201
+ gcCensus?.stop();
1580
2202
  };
1581
2203
  }
1582
2204
  /**
@@ -5336,6 +5958,74 @@ function encodeFrame(value) {
5336
5958
  Buffer.from(body.buffer, body.byteOffset, body.byteLength).copy(out, 4);
5337
5959
  return out;
5338
5960
  }
5961
+ /** msgpack `fixstr` of {@link PAYLOAD_KEY}: `0xa0 | length`, then the bytes. */
5962
+ var PAYLOAD_KEY_BYTES = Buffer.from([162, ...Buffer.from("pl")]);
5963
+ /** msgpack `bin 32` marker: one byte, then a 4-byte big-endian length. */
5964
+ var BIN32_MARKER = 198;
5965
+ /** msgpack `fixmap` range: `0x80 | count`, count in `0..15`. */
5966
+ var FIXMAP_BASE = 128;
5967
+ var FIXMAP_MAX_COUNT = 15;
5968
+ /**
5969
+ * Encode the HEAD of a frame that carries an opaque payload: the length
5970
+ * prefix, the msgpack envelope with one extra key ({@link PAYLOAD_KEY}), and
5971
+ * the `bin 32` header for the payload — everything but the payload bytes
5972
+ * themselves, which the caller writes straight after it.
5973
+ *
5974
+ * ## Why the payload is the frame's TAIL and not a field
5975
+ *
5976
+ * hub-main routes every sibling↔sibling call. Until this existed it decoded
5977
+ * the whole request (args included), built a new message, and re-encoded it
5978
+ * — walking a 13.5 KB PCM chunk or a 2 000-row settings answer twice on the
5979
+ * one thread that serves every tRPC request, and allocating an `Encoder`
5980
+ * buffer that doubles from 2 KiB up to the body's size on every write
5981
+ * (D441: ~27 % of hub-main's busy time in msgpack decode, ~6 % in encode,
5982
+ * and the churn that fed a 29.5 % GC share).
5983
+ *
5984
+ * With the payload appended as the LAST key of the map, the receiver's
5985
+ * ordinary msgpack decode yields it as a zero-copy VIEW of the frame
5986
+ * (`@msgpack/msgpack` decodes `bin` as `subarray`), the router reads only the
5987
+ * envelope, and forwarding is: encode a ~60-byte head, write it, write the
5988
+ * view. The wire stays plain msgpack — a peer that never heard of payloads
5989
+ * decodes `{ …frame, pl: <bytes> }` and can be told, at register, not to be
5990
+ * sent one.
5991
+ *
5992
+ * ## The one hand-written msgpack in this repository
5993
+ *
5994
+ * `@msgpack/msgpack` has no "encode a map, then append a key" API, so the
5995
+ * envelope is encoded normally and its `fixmap` count byte is bumped by one
5996
+ * before the key and the `bin 32` header are appended. That patch is only
5997
+ * valid for a `fixmap` (≤ 15 keys); a `Frame` has at most four, and anything
5998
+ * else is refused rather than silently corrupted.
5999
+ */
6000
+ function encodeFrameHead(frame, payload) {
6001
+ const envelope = encode(frame, ENCODE_OPTIONS);
6002
+ const first = envelope[0];
6003
+ if (envelope.byteLength === 0 || first === void 0 || first < FIXMAP_BASE || first > 142) throw new Error(`encodeFrameHead: envelope must be a msgpack fixmap with at most ${FIXMAP_MAX_COUNT - 1} keys (got leading byte 0x${(first ?? 0).toString(16)})`);
6004
+ const bodyLength = envelope.byteLength + PAYLOAD_KEY_BYTES.byteLength + 5 + payload.byteLength;
6005
+ const head = Buffer.allocUnsafe(HEADER_BYTES + envelope.byteLength + PAYLOAD_KEY_BYTES.byteLength + 5);
6006
+ head.writeUInt32BE(bodyLength, 0);
6007
+ Buffer.from(envelope.buffer, envelope.byteOffset, envelope.byteLength).copy(head, HEADER_BYTES);
6008
+ head[HEADER_BYTES] = first + 1;
6009
+ let at = HEADER_BYTES + envelope.byteLength;
6010
+ PAYLOAD_KEY_BYTES.copy(head, at);
6011
+ at += PAYLOAD_KEY_BYTES.byteLength;
6012
+ head[at] = BIN32_MARKER;
6013
+ head.writeUInt32BE(payload.byteLength, at + 1);
6014
+ return head;
6015
+ }
6016
+ /**
6017
+ * Serialise a value into the bytes a payload carries. The SAME options as the
6018
+ * envelope codec, so `undefined` properties are omitted rather than turned
6019
+ * into `null` — a `z.void()` cap output must read back as `undefined` on the
6020
+ * far side exactly as it did when it travelled inline.
6021
+ */
6022
+ function packPayload(value) {
6023
+ return encode(value, ENCODE_OPTIONS);
6024
+ }
6025
+ /** Inverse of {@link packPayload}. A `bin` inside decodes as a view of `bytes`. */
6026
+ function unpackPayload(bytes) {
6027
+ return decode(bytes);
6028
+ }
5339
6029
  /** Bytes in the big-endian length prefix that opens every frame. */
5340
6030
  var HEADER_BYTES = 4;
5341
6031
  /**
@@ -5494,6 +6184,25 @@ function localEndpointPath(nodeId) {
5494
6184
  if (process.platform === "win32") return `\\\\.\\pipe\\camstack-${safe}`;
5495
6185
  return (0, node_path.join)((0, node_os.tmpdir)(), `camstack-${safe}.sock`);
5496
6186
  }
6187
+ //#endregion
6188
+ //#region src/kernel/transport/local-transport.ts
6189
+ /**
6190
+ * An answer that carries an opaque payload beside its body.
6191
+ *
6192
+ * A CLASS rather than a shape so the channel can tell it from a provider's
6193
+ * ordinary `{ body, payload }` return value by identity, never by duck-typing:
6194
+ * a cap method is free to return an object with those two keys and it must
6195
+ * travel inline like any other. `request()` resolves with one of these only
6196
+ * when the peer's `res` frame carried a payload.
6197
+ */
6198
+ var PayloadReply = class {
6199
+ body;
6200
+ payload;
6201
+ constructor(body, payload) {
6202
+ this.body = body;
6203
+ this.payload = payload;
6204
+ }
6205
+ };
5497
6206
  var SocketChannel = class {
5498
6207
  socket;
5499
6208
  nextId = 1;
@@ -5511,6 +6220,8 @@ var SocketChannel = class {
5511
6220
  txCounters = createSocketDirectionCounters();
5512
6221
  /** Frames read from the peer, cumulative, split by kind. */
5513
6222
  rxCounters = createSocketDirectionCounters();
6223
+ /** See {@link CapActionChannelObserver}. Absent = no per-action census. */
6224
+ actionObserver = null;
5514
6225
  constructor(socket) {
5515
6226
  this.socket = socket;
5516
6227
  socket.on("data", (chunk) => this.onData(chunk));
@@ -5541,15 +6252,25 @@ var SocketChannel = class {
5541
6252
  }, timeoutMs);
5542
6253
  timer.unref?.();
5543
6254
  }
6255
+ const label = this.labelSafely(body);
5544
6256
  this.pending.set(id, {
5545
6257
  resolve,
5546
6258
  reject,
5547
- timer
6259
+ timer,
6260
+ action: null
5548
6261
  });
5549
- this.send({
6262
+ const reqBytes = this.send({
5550
6263
  k: "req",
5551
6264
  id,
5552
6265
  body
6266
+ }, opts?.payload);
6267
+ const entry = this.pending.get(id);
6268
+ if (entry !== void 0 && label !== void 0) this.pending.set(id, {
6269
+ ...entry,
6270
+ action: {
6271
+ label,
6272
+ reqBytes
6273
+ }
5553
6274
  });
5554
6275
  });
5555
6276
  }
@@ -5560,6 +6281,36 @@ var SocketChannel = class {
5560
6281
  body
5561
6282
  });
5562
6283
  }
6284
+ /**
6285
+ * Wire the per-action census. Only one observer is active at a time; an
6286
+ * unwired channel records nothing at all (never zeros).
6287
+ */
6288
+ observeActions(observer) {
6289
+ this.actionObserver = observer;
6290
+ }
6291
+ /**
6292
+ * The label for one envelope, or `undefined` when there is nothing to
6293
+ * record. Wrapped: a broken labeller must never fail the call it watches —
6294
+ * the same discipline `LocalChildRegistry.recordCapUsage` applies, and for
6295
+ * the same reason.
6296
+ */
6297
+ labelSafely(body) {
6298
+ const obs = this.actionObserver;
6299
+ if (obs === null) return void 0;
6300
+ try {
6301
+ return obs.label(body);
6302
+ } catch {
6303
+ return null;
6304
+ }
6305
+ }
6306
+ /** Settle one request/response pair on the census. Never throws. */
6307
+ recordAction(label, reqBytes, resBytes) {
6308
+ const obs = this.actionObserver;
6309
+ if (obs === null) return;
6310
+ try {
6311
+ obs.record(label, reqBytes, resBytes);
6312
+ } catch {}
6313
+ }
5563
6314
  /** Replace the request handler. Only one handler is active at a time. */
5564
6315
  onRequest(handler) {
5565
6316
  this.requestHandler = handler;
@@ -5590,11 +6341,44 @@ var SocketChannel = class {
5590
6341
  rx: sampleSocketDirection(this.rxCounters)
5591
6342
  };
5592
6343
  }
5593
- send(frame) {
5594
- if (this.closed) return;
5595
- const encoded = encodeFrame(frame);
5596
- recordSocketFrame(this.txCounters, frame.k, encoded.byteLength);
5597
- this.socket.write(encoded);
6344
+ /**
6345
+ * Bytes handed to the socket that the kernel has not taken yet — the
6346
+ * `Writable` buffer plus the write in flight. Read LIVE, never cached: this
6347
+ * is the number that says whether the peer is draining what we write, and
6348
+ * it is read on every heartbeat probe (D446). An unref'd socket keeps it
6349
+ * exactly like a ref'd one, which is why it is read here and not summed
6350
+ * over `_getActiveHandles()`.
6351
+ */
6352
+ queuedBytes() {
6353
+ return this.socket.writableLength;
6354
+ }
6355
+ /**
6356
+ * Write one frame. With a payload the head and the tail are two `write`
6357
+ * calls under `cork()` — the kernel sees one `writev`, and the payload bytes
6358
+ * are never copied into the head. A forwarded payload is a view into the
6359
+ * frame it arrived on, and that frame's buffer is never mutated, so handing
6360
+ * the view to the socket is safe.
6361
+ *
6362
+ * Returns the on-the-wire bytes it charged — the same number the per-kind
6363
+ * counter got, so the per-action census can never disagree with the per-peer
6364
+ * one. `0` when the channel is closed and nothing was written.
6365
+ */
6366
+ send(frame, payload) {
6367
+ if (this.closed) return 0;
6368
+ if (payload === void 0) {
6369
+ const encoded = encodeFrame(frame);
6370
+ recordSocketFrame(this.txCounters, frame.k, encoded.byteLength);
6371
+ this.socket.write(encoded);
6372
+ return encoded.byteLength;
6373
+ }
6374
+ const head = encodeFrameHead(frame, payload);
6375
+ const bytes = head.byteLength + payload.byteLength;
6376
+ recordSocketFrame(this.txCounters, frame.k, bytes);
6377
+ this.socket.cork();
6378
+ this.socket.write(head);
6379
+ this.socket.write(payload);
6380
+ this.socket.uncork();
6381
+ return bytes;
5598
6382
  }
5599
6383
  onData(chunk) {
5600
6384
  const frames = this.decoder.push(chunk);
@@ -5613,22 +6397,31 @@ var SocketChannel = class {
5613
6397
  async handleFrame(frame, wireBytes) {
5614
6398
  recordSocketFrame(this.rxCounters, frame.k, wireBytes);
5615
6399
  if (frame.k === "req") {
6400
+ const label = this.labelSafely(frame.body);
6401
+ let resBytes = 0;
5616
6402
  try {
5617
- const result = await this.requestHandler(frame.body);
5618
- this.send({
6403
+ const result = await this.requestHandler(frame.body, frame.pl);
6404
+ if (result instanceof PayloadReply) resBytes = this.send({
6405
+ k: "res",
6406
+ id: frame.id,
6407
+ ok: true,
6408
+ body: result.body
6409
+ }, result.payload);
6410
+ else resBytes = this.send({
5619
6411
  k: "res",
5620
6412
  id: frame.id,
5621
6413
  ok: true,
5622
6414
  body: result
5623
6415
  });
5624
6416
  } catch (err) {
5625
- this.send({
6417
+ resBytes = this.send({
5626
6418
  k: "res",
5627
6419
  id: frame.id,
5628
6420
  ok: false,
5629
6421
  err: err instanceof Error ? err.message : String(err)
5630
6422
  });
5631
6423
  }
6424
+ if (label !== void 0) this.recordAction(label, wireBytes, resBytes);
5632
6425
  return;
5633
6426
  }
5634
6427
  if (frame.k === "res") {
@@ -5636,8 +6429,10 @@ var SocketChannel = class {
5636
6429
  if (!p) return;
5637
6430
  this.pending.delete(frame.id);
5638
6431
  if (p.timer !== null) clearTimeout(p.timer);
5639
- if (frame.ok) p.resolve(frame.body);
5640
- else p.reject(new Error(frame.err));
6432
+ if (p.action !== null) this.recordAction(p.action.label, p.action.reqBytes, wireBytes);
6433
+ if (!frame.ok) p.reject(new Error(frame.err));
6434
+ else if (frame.pl !== void 0) p.resolve(new PayloadReply(frame.body, frame.pl));
6435
+ else p.resolve(frame.body);
5641
6436
  return;
5642
6437
  }
5643
6438
  this.eventHandler(frame.body);
@@ -6188,454 +6983,79 @@ function deliverShared(state, event) {
6188
6983
  }
6189
6984
  }
6190
6985
  //#endregion
6191
- //#region src/kernel/moleculer/resilient-cap-call.ts
6192
- /** Moleculer error `type` values meaning "the service is not (yet) routable". */
6193
- var DISCOVERY_ERROR_TYPES = new Set(["SERVICE_NOT_FOUND", "SERVICE_NOT_AVAILABLE"]);
6194
- /** Default ceiling for the discovery wait — fail-fast past this. */
6195
- var DEFAULT_DISCOVERY_TIMEOUT_MS$1 = 3e4;
6196
- function isDiscoveryError(err) {
6197
- if (typeof err !== "object" || err === null) return false;
6198
- const type = err.type;
6199
- return typeof type === "string" && DISCOVERY_ERROR_TYPES.has(type);
6200
- }
6986
+ //#region src/kernel/transport/cap-child-index.ts
6987
+ /** Separator that cannot occur in a kebab cap name, so the key is unambiguous. */
6988
+ var DEVICE_KEY_SEPARATOR = "\0";
6989
+ var NO_CANDIDATES = Object.freeze([]);
6990
+ var MapCapChildIndex = class {
6991
+ deviceOwners;
6992
+ singletons;
6993
+ constructor(deviceOwners, singletons) {
6994
+ this.deviceOwners = deviceOwners;
6995
+ this.singletons = singletons;
6996
+ }
6997
+ deviceOwner(capName, deviceId) {
6998
+ return this.deviceOwners.get(`${capName}${DEVICE_KEY_SEPARATOR}${deviceId}`) ?? null;
6999
+ }
7000
+ singletonCandidates(capName) {
7001
+ return this.singletons.get(capName) ?? NO_CANDIDATES;
7002
+ }
7003
+ };
6201
7004
  /**
6202
- * Call a Moleculer action; on a service-discovery error, wait for the
6203
- * named service to be discovered and retry the call exactly once.
6204
- */
6205
- async function callWithServiceDiscovery(broker, serviceName, action, params, opts, discoveryTimeoutMs = DEFAULT_DISCOVERY_TIMEOUT_MS$1) {
6206
- try {
6207
- return await broker.call(action, params, opts);
6208
- } catch (err) {
6209
- if (!isDiscoveryError(err)) throw err;
6210
- await broker.waitForServices([serviceName], discoveryTimeoutMs);
6211
- return await broker.call(action, params, opts);
7005
+ * Build the `(cap, device?) → child` index from the connected children.
7006
+ *
7007
+ * A PURE function of what it is handed — it captures nothing, subscribes to
7008
+ * nothing and cannot be updated in place. That is the whole safety argument
7009
+ * (D3, D457): the only way to change what it says is to build a new one from
7010
+ * the `children` map, so an index can never name a child that map has already
7011
+ * dropped. An incremental index — add on register, remove on close — would be
7012
+ * a second registry that can disagree with the first, and a stale one routes
7013
+ * to a dead runner, which is strictly worse than a slow scan.
7014
+ *
7015
+ * Cost: O(children × descriptors), paid once per mutation of the children map
7016
+ * (a register, a re-register, a disconnect) rather than once per cap call.
7017
+ *
7018
+ * The two tiers stay separate, exactly as the scan had them: a device lookup
7019
+ * is never answered from a deviceId-less descriptor. `resolveChildId` owns
7020
+ * the fallback from tier 1 to tier 2, and folding it in here would make it
7021
+ * unreachable.
7022
+ */
7023
+ function buildCapChildIndex(entries) {
7024
+ const deviceOwners = /* @__PURE__ */ new Map();
7025
+ const singletons = /* @__PURE__ */ new Map();
7026
+ for (const entry of entries) {
7027
+ const namedForThisChild = /* @__PURE__ */ new Set();
7028
+ for (const cap of entry.caps) {
7029
+ if (cap.deviceId === void 0) {
7030
+ if (namedForThisChild.has(cap.capName)) continue;
7031
+ namedForThisChild.add(cap.capName);
7032
+ const candidates = singletons.get(cap.capName);
7033
+ if (candidates === void 0) singletons.set(cap.capName, [entry.childId]);
7034
+ else candidates.push(entry.childId);
7035
+ continue;
7036
+ }
7037
+ const key = `${cap.capName}${DEVICE_KEY_SEPARATOR}${cap.deviceId}`;
7038
+ if (!deviceOwners.has(key)) deviceOwners.set(key, entry.childId);
7039
+ }
6212
7040
  }
7041
+ return new MapCapChildIndex(deviceOwners, singletons);
6213
7042
  }
6214
7043
  //#endregion
6215
- //#region src/kernel/transport/cap-route.ts
6216
- function buildMessage(capName, method, detail) {
6217
- const call = method !== void 0 ? `${capName}.${method}` : capName;
6218
- const target = detail.nodeId !== void 0 ? ` to node ${detail.nodeId}` : "";
6219
- const rejectedStr = detail.rejected.map((r) => `${r.kind}=${r.why}`).join("; ");
6220
- const rejectedClause = rejectedStr.length > 0 ? ` (rejected: ${rejectedStr})` : "";
6221
- return `${call} not routable${target}: ${detail.reason}${rejectedClause}`;
6222
- }
6223
- var CapRouteError = class extends Error {
6224
- reason;
6225
- nodeId;
6226
- rejected;
6227
- /**
6228
- * @param cause Optional original error that triggered this routing failure.
6229
- * Stored as `Error.cause` (TC39 standard option, Node 16.9+).
6230
- * Dispatchers wrapping transport errors MUST pass the original.
6231
- */
6232
- constructor(capName, method, detail, cause) {
6233
- super(buildMessage(capName, method, detail), cause !== void 0 ? { cause } : void 0);
6234
- this.name = "CapRouteError";
6235
- this.reason = detail.reason;
6236
- this.nodeId = detail.nodeId;
6237
- this.rejected = detail.rejected;
6238
- }
6239
- };
6240
- /**
6241
- * Classifies a (capName, opts) pair into a typed CapRoute dispatch descriptor.
6242
- *
6243
- * Precedence (explicit nodeId path):
6244
- * 1. hub-in-process — hub node + hubInProcessProvides
6245
- * 2. hub-local-uds — hub node + hubLocalChildProvides
6246
- * 3. node-offline → CapRouteError{reason:'node-offline'}
6247
- * 4. agent-child-forward — node is an agent AND nodeKnowsCap
6248
- * 5. remote-moleculer — any other online non-agent node
6249
- *
6250
- * Singleton path (no nodeId):
6251
- * 1. hub-in-process (hub provides in-process)
6252
- * 2. hub-local-uds (a hub-local UDS child provides it)
6253
- * 3. remote-moleculer (any online, non-hub node that knows it)
6254
- * 4. → CapRouteError{reason:'no-provider', rejected: all considered routes}
6255
- *
6256
- * PURE: no side effects, no async, no broker/registry imports.
6257
- */
6258
- function classifyCapRoute(capName, opts, snapshot) {
6259
- const rejected = [];
6260
- if (opts.nodeId !== void 0) return classifyExplicitNode(capName, opts.nodeId, opts.deviceId, snapshot, rejected);
6261
- return classifySingleton(capName, opts.deviceId, snapshot, rejected);
6262
- }
6263
- function classifyExplicitNode(capName, nodeId, deviceId, snap, rejected) {
6264
- if (nodeId === snap.hubNodeId) {
6265
- const residentProvides = snap.hubResidentProvides;
6266
- const providesOnHub = residentProvides ?? snap.hubInProcessProvides;
6267
- const getRef = residentProvides !== void 0 ? snap.getHubResidentProviderRef : snap.getInProcessProviderRef;
6268
- if (providesOnHub(capName)) {
6269
- const ref = getRef?.(capName) ?? null;
6270
- if (ref !== null) return {
6271
- kind: "hub-in-process",
6272
- capName,
6273
- ref
6274
- };
6275
- rejected.push({
6276
- kind: "hub-in-process",
6277
- why: "provider ref not available in snapshot"
6278
- });
6279
- throw new CapRouteError(capName, void 0, {
6280
- reason: "no-provider",
6281
- nodeId,
6282
- rejected
6283
- });
6284
- }
6285
- if (snap.hubLocalChildProvides(capName, deviceId)) {
6286
- const childId = snap.getHubLocalChildId?.(capName, deviceId) ?? null;
6287
- if (childId !== null) return {
6288
- kind: "hub-local-uds",
6289
- capName,
6290
- childId
6291
- };
6292
- rejected.push({
6293
- kind: "hub-local-uds",
6294
- why: "child id not resolvable from snapshot"
6295
- });
6296
- throw new CapRouteError(capName, void 0, {
6297
- reason: "no-provider",
6298
- nodeId,
6299
- rejected
6300
- });
6301
- }
6302
- rejected.push({
6303
- kind: "hub-in-process",
6304
- why: "hub does not provide this cap in-process"
6305
- });
6306
- rejected.push({
6307
- kind: "hub-local-uds",
6308
- why: "no hub-local child provides this cap"
6309
- });
6310
- throw new CapRouteError(capName, void 0, {
6311
- reason: "no-provider",
6312
- nodeId,
6313
- rejected
6314
- });
6315
- }
6316
- if (!snap.nodeOnline(nodeId)) {
6317
- rejected.push({
6318
- kind: "remote-moleculer",
6319
- why: `node ${nodeId} is offline`
6320
- });
6321
- throw new CapRouteError(capName, void 0, {
6322
- reason: "node-offline",
6323
- nodeId,
6324
- rejected
6325
- });
6326
- }
6327
- if (snap.nodeIsAgent(nodeId)) {
6328
- if (snap.nodeKnowsCap(nodeId, capName)) return {
6329
- kind: "agent-child-forward",
6330
- capName,
6331
- agentNodeId: nodeId,
6332
- childId: snap.getAgentChildId?.(nodeId, capName) ?? void 0
6333
- };
6334
- rejected.push({
6335
- kind: "agent-child-forward",
6336
- why: `agent ${nodeId} does not know cap ${capName}`
6337
- });
6338
- throw new CapRouteError(capName, void 0, {
6339
- reason: "no-provider",
6340
- nodeId,
6341
- rejected
6342
- });
6343
- }
6344
- return {
6345
- kind: "remote-moleculer",
6346
- capName,
6347
- nodeId
6348
- };
6349
- }
6350
- function classifySingleton(capName, deviceId, snap, rejected) {
6351
- if (snap.hubInProcessProvides(capName)) {
6352
- const ref = snap.getInProcessProviderRef?.(capName) ?? null;
6353
- if (ref !== null) return {
6354
- kind: "hub-in-process",
6355
- capName,
6356
- ref
6357
- };
6358
- rejected.push({
6359
- kind: "hub-in-process",
6360
- why: "provider ref not available in snapshot"
6361
- });
6362
- } else rejected.push({
6363
- kind: "hub-in-process",
6364
- why: "hub does not provide this cap in-process"
6365
- });
6366
- if (snap.hubLocalChildProvides(capName, deviceId)) {
6367
- const childId = snap.getHubLocalChildId?.(capName, deviceId) ?? null;
6368
- if (childId !== null) return {
6369
- kind: "hub-local-uds",
6370
- capName,
6371
- childId
6372
- };
6373
- rejected.push({
6374
- kind: "hub-local-uds",
6375
- why: "child id not resolvable from snapshot"
6376
- });
6377
- } else rejected.push({
6378
- kind: "hub-local-uds",
6379
- why: "no hub-local child provides this cap"
6380
- });
6381
- const knownNodes = snap.listKnownNodeIds?.() ?? [];
6382
- for (const nodeId of knownNodes) if (nodeId !== snap.hubNodeId && snap.nodeOnline(nodeId) && snap.nodeKnowsCap(nodeId, capName)) return {
6383
- kind: "remote-moleculer",
6384
- capName,
6385
- nodeId
6386
- };
6387
- rejected.push({
6388
- kind: "remote-moleculer",
6389
- why: "no online remote node knows this cap"
6390
- });
6391
- throw new CapRouteError(capName, void 0, {
6392
- reason: "no-provider",
6393
- rejected
6394
- });
6395
- }
6396
- //#endregion
6397
- //#region src/kernel/transport/cap-route-resolver.ts
6398
- /** The Moleculer service name that Task 6's agent registers. */
6399
- var AGENT_CAP_FWD_SERVICE = "$agent-cap-fwd";
6400
- /** The Moleculer action (service.action) for agent cap forwarding. */
6401
- var AGENT_CAP_FWD_ACTION = `${AGENT_CAP_FWD_SERVICE}.forward`;
6402
- /** Default timeout for remote Moleculer cap calls (ms). */
6403
- var REMOTE_CALL_TIMEOUT_MS = 6e4;
6404
- function extractDeviceId(args) {
6405
- if (args === null || typeof args !== "object") return void 0;
6406
- const raw = Reflect.get(args, "deviceId");
6407
- return typeof raw === "number" ? raw : void 0;
6408
- }
6409
- /**
6410
- * Extract an inline `nodeId` string from a cap call's args — the per-call node
6411
- * pin a forked addon expresses by carrying `nodeId` in the input (the SAME way
6412
- * device-scoped caps carry `deviceId`, and the way the generated cap-router on
6413
- * the hub already honours an inline pin). Mirrors {@link extractDeviceId}.
6414
- *
6415
- * This is the routing source for hub→remote-node EXECUTION pinning (e.g. the
6416
- * benchmark addon running a synthetic/decoder workload ON a chosen agent). The
6417
- * out-of-band `nodePin(op.context)` path does NOT survive the forked addon →
6418
- * hub UDS link chain, so `onUnownedCall` reads the inline pin instead. Provider
6419
- * methods that don't declare `nodeId` simply ignore the extra field (they
6420
- * destructure the fields they need); methods that DO declare it (e.g.
6421
- * `getEngineProvisioning({nodeId})`) still receive it unchanged.
6422
- */
6423
- function extractNodeId(args) {
6424
- if (args === null || typeof args !== "object") return void 0;
6425
- const raw = Reflect.get(args, "nodeId");
6426
- return typeof raw === "string" && raw.length > 0 ? raw : void 0;
6427
- }
6428
- /**
6429
- * Merge `callerAddonId` into `args` as an extra field — the SAME "provider
6430
- * destructures what it needs and ignores the rest" convention
6431
- * {@link extractNodeId} already relies on for the inline `nodeId` pin.
6432
- * `undefined` (the overwhelmingly common case: no caller hint, or the census
6433
- * consuming it is disarmed) returns `args` unchanged — no allocation. A
6434
- * non-object `args` (or `null`/array) is returned unchanged too: there is
6435
- * nowhere to attach an extra key without changing the payload's shape, and a
6436
- * provider taking a bare value never reads `callerAddonId` off it anyway.
6437
- */
6438
- function withCallerAddonId(args, callerAddonId) {
6439
- if (callerAddonId === void 0) return args;
6440
- if (args === null || typeof args !== "object" || Array.isArray(args)) return args;
6441
- return {
6442
- ...args,
6443
- callerAddonId
6444
- };
6445
- }
6446
- var CapRouteResolver = class {
6447
- hubNodeId;
6448
- broker;
6449
- hubLocalRegistry;
6450
- nodeAuthority;
6451
- inProcessProviders;
6452
- hubResidentProviders;
6453
- capTimeoutMs;
6454
- snapshot;
6455
- constructor(deps) {
6456
- this.hubNodeId = deps.hubNodeId;
6457
- this.broker = deps.broker;
6458
- this.hubLocalRegistry = deps.hubLocalRegistry;
6459
- this.nodeAuthority = deps.nodeAuthority;
6460
- this.inProcessProviders = deps.inProcessProviders;
6461
- this.hubResidentProviders = deps.hubResidentProviders;
6462
- this.capTimeoutMs = deps.capTimeoutMs;
6463
- this.snapshot = this.buildSnapshot();
6464
- }
6465
- /** Per-call RPC timeout: the cap method's declared override, else the default. */
6466
- callTimeout(capName, method) {
6467
- return this.capTimeoutMs?.(capName, method) ?? REMOTE_CALL_TIMEOUT_MS;
6468
- }
6469
- resolveCapRoute(capName, opts) {
6470
- return classifyCapRoute(capName, opts, this.snapshot);
6471
- }
6472
- /**
6473
- * Resolve the hub-local-uds route for a cap owned by a forked hub-local
6474
- * child, IGNORING any in-hub provider registered for the same cap name.
6475
- *
6476
- * `resolveCapRoute` gives Priority 1 to `hub-in-process`: when an in-hub
6477
- * provider (e.g. a wrapper) is registered for the cap, the route always
6478
- * classifies as `hub-in-process` and the hub-local-uds NATIVE child is never
6479
- * reached. That is correct for the generic dispatch path (the wrapper is the
6480
- * active provider), but WRONG for the native-cap fallback
6481
- * (`setNativeFallback`), whose contract is to reach the NATIVE provider in
6482
- * the forked vendor child so a wrapper can delegate to it. A wrapper cap
6483
- * with a forked native (today: `snapshot`) otherwise resolves to the wrapper
6484
- * itself, the native is never invoked, and the wrapper silently falls
6485
- * through to its secondary strategy.
6486
- *
6487
- * This method consults ONLY the hub-local-child authority
6488
- * (`hubLocalChildProvides` + `getHubLocalChildId`, both deviceId-aware), so
6489
- * it returns the native child route regardless of any in-process shadow.
6490
- * Returns null when no hub-local child owns `(capName, deviceId)` — the
6491
- * caller then falls through to its remote-resolution branch.
6492
- */
6493
- resolveHubLocalUdsRoute(capName, deviceId) {
6494
- if (!this.snapshot.hubLocalChildProvides(capName, deviceId)) return null;
6495
- const childId = this.snapshot.getHubLocalChildId?.(capName, deviceId) ?? null;
6496
- if (childId === null) return null;
6497
- return {
6498
- kind: "hub-local-uds",
6499
- capName,
6500
- childId
6501
- };
6502
- }
6503
- /**
6504
- * @param addonId Optional out-of-band PROVIDER-selection hint (Task 7.5a):
6505
- * the addonId of the provider the CALLER already resolved (e.g.
6506
- * `buildCapCallFn`'s `deps.addonId` for a registered grouped-runner
6507
- * provider). Only meaningful for `agent-child-forward` — a `remote-moleculer`
6508
- * route already encodes the provider in its action name
6509
- * (`${addonId}.${cap}.${method}`, via `NodeCapAuthority.getAddonId`), and a
6510
- * `hub-local-uds` caller that already knows the addonId (`buildCapCallFn`)
6511
- * talks to `LocalChildRegistry.callCapOnChild` directly rather than through
6512
- * `dispatch`.
6513
- * @param callerAddonId Optional out-of-band CALLER-identity hint — the
6514
- * addon that ORIGINATED the call, distinct from `addonId` above (which
6515
- * names the resolved PROVIDER, not the caller). Set by
6516
- * `onUnownedCall` from `CapCallInput.callerAddonId`
6517
- * (`LocalChildRegistry` stamped it off the registered `childId`). Only
6518
- * the `hub-in-process` branch consumes it — merged into `args` so a
6519
- * diagnostic tool deep in that provider (e.g. device-manager's
6520
- * fleet-read census) can read it without every provider method changing
6521
- * signature, the same "extra field the provider destructures or
6522
- * ignores" convention `nodeId` already uses on this path.
6523
- */
6524
- async dispatch(route, method, args, addonId, callerAddonId) {
6525
- try {
6526
- return await this.dispatchInner(route, method, args, addonId, callerAddonId);
6527
- } catch (err) {
6528
- if (err instanceof CapRouteError) throw err;
6529
- const nodeId = this.routeNodeId(route);
6530
- const cause = err instanceof Error ? err : new Error(String(err));
6531
- throw new CapRouteError(route.capName, method, {
6532
- reason: "transport-failed",
6533
- nodeId,
6534
- rejected: [{
6535
- kind: route.kind,
6536
- why: cause.message
6537
- }]
6538
- }, cause);
6539
- }
6540
- }
6541
- /**
6542
- * Inner dispatch: may throw CapRouteError (validation failures) or arbitrary
6543
- * transport errors. The outer `dispatch` wraps non-CapRouteErrors.
6544
- */
6545
- async dispatchInner(route, method, args, addonId, callerAddonId) {
6546
- switch (route.kind) {
6547
- case "hub-in-process": return route.ref.invoke(method, withCallerAddonId(args, callerAddonId));
6548
- case "hub-local-uds": {
6549
- const registry = this.hubLocalRegistry;
6550
- if (registry === null) throw new CapRouteError(route.capName, method, {
6551
- reason: "no-provider",
6552
- rejected: [{
6553
- kind: "hub-local-uds",
6554
- why: "UDS registry not available"
6555
- }]
6556
- });
6557
- const deviceId = extractDeviceId(args);
6558
- const input = {
6559
- capName: route.capName,
6560
- method,
6561
- args,
6562
- ...deviceId !== void 0 ? { deviceId } : {}
6563
- };
6564
- return registry.callCapOnChild(route.childId, input);
6565
- }
6566
- case "remote-moleculer": {
6567
- const addonId = this.nodeAuthority.getAddonId(route.nodeId, route.capName);
6568
- if (addonId === null) throw new CapRouteError(route.capName, method, {
6569
- reason: "no-provider",
6570
- nodeId: route.nodeId,
6571
- rejected: [{
6572
- kind: "remote-moleculer",
6573
- why: `no addonId known for ${route.nodeId}/${route.capName}`
6574
- }]
6575
- });
6576
- const deviceId = extractDeviceId(args);
6577
- const isNative = this.nodeAuthority.isNativeCap(route.nodeId, route.capName, deviceId);
6578
- const action = capActionName(addonId, route.capName, method, isNative);
6579
- return callWithServiceDiscovery(this.broker, addonId, action, args, {
6580
- nodeID: route.nodeId,
6581
- timeout: this.callTimeout(route.capName, method)
6582
- });
6583
- }
6584
- case "agent-child-forward": {
6585
- const deviceId = extractDeviceId(args);
6586
- const params = {
6587
- capName: route.capName,
6588
- method,
6589
- args,
6590
- ...route.childId !== void 0 ? { childId: route.childId } : {},
6591
- ...deviceId !== void 0 ? { deviceId } : {},
6592
- ...addonId !== void 0 ? { addonId } : {}
6593
- };
6594
- return callWithServiceDiscovery(this.broker, AGENT_CAP_FWD_SERVICE, AGENT_CAP_FWD_ACTION, params, {
6595
- nodeID: route.agentNodeId,
6596
- timeout: this.callTimeout(route.capName, method)
6597
- });
6598
- }
6599
- }
6600
- }
6601
- buildSnapshot() {
6602
- const hubLocalRegistry = this.hubLocalRegistry;
6603
- const nodeAuthority = this.nodeAuthority;
6604
- const inProcessProviders = this.inProcessProviders;
6605
- const hubResidentProviders = this.hubResidentProviders;
6606
- return {
6607
- hubNodeId: this.hubNodeId,
6608
- hubInProcessProvides: (cap) => inProcessProviders(cap) !== null,
6609
- ...hubResidentProviders !== void 0 ? {
6610
- hubResidentProvides: (cap) => hubResidentProviders(cap) !== null,
6611
- getHubResidentProviderRef: (cap) => hubResidentProviders(cap)
6612
- } : {},
6613
- hubLocalChildProvides: (cap, deviceId) => hubLocalRegistry !== null && hubLocalRegistry.resolveChildId(cap, deviceId) !== null,
6614
- nodeKnowsCap: (nodeId, cap) => nodeAuthority.nodeKnowsCap(nodeId, cap),
6615
- nodeIsAgent: (nodeId) => nodeAuthority.nodeIsAgent(nodeId),
6616
- nodeOnline: (nodeId) => nodeAuthority.nodeOnline(nodeId),
6617
- listKnownNodeIds: () => nodeAuthority.listNodeIds(),
6618
- getInProcessProviderRef: (cap) => inProcessProviders(cap),
6619
- getHubLocalChildId: (cap, deviceId) => hubLocalRegistry !== null ? hubLocalRegistry.resolveChildId(cap, deviceId) : null,
6620
- getAgentChildId: (agentNodeId, cap) => nodeAuthority.getAgentChildId(agentNodeId, cap)
6621
- };
6622
- }
6623
- /** Extract a nodeId string from a route for error reporting, or undefined. */
6624
- routeNodeId(route) {
6625
- switch (route.kind) {
6626
- case "hub-in-process": return this.hubNodeId;
6627
- case "hub-local-uds": return `${this.hubNodeId}/${route.childId}`;
6628
- case "remote-moleculer": return route.nodeId;
6629
- case "agent-child-forward": return route.agentNodeId;
6630
- }
6631
- }
6632
- };
6633
- //#endregion
6634
- //#region src/kernel/transport/local-child-registry.ts
6635
- function readFanoutMode() {
6636
- const raw = process.env.CAMSTACK_UDS_EVENT_FANOUT;
6637
- if (raw === "shadow" || raw === "broadcast") return raw;
6638
- return "filter";
7044
+ //#region src/kernel/transport/cap-routing-hints.ts
7045
+ /** Lift {@link CapRoutingHints} from a call's args. Never throws. */
7046
+ function liftRoutingHints(args) {
7047
+ if (args === null || typeof args !== "object" || Array.isArray(args)) return {};
7048
+ const nodeId = Reflect.get(args, "nodeId");
7049
+ const addonId = Reflect.get(args, "addonId");
7050
+ return {
7051
+ ...typeof nodeId === "string" && nodeId.length > 0 ? { nodeId } : {},
7052
+ ...typeof addonId === "string" && addonId.length > 0 ? { addonId } : {}
7053
+ };
7054
+ }
7055
+ function readFanoutMode() {
7056
+ const raw = process.env.CAMSTACK_UDS_EVENT_FANOUT;
7057
+ if (raw === "shadow" || raw === "broadcast") return raw;
7058
+ return "filter";
6639
7059
  }
6640
7060
  /**
6641
7061
  * Sentinel prefix used in the no-route error thrown when `cap-call-out` has no
@@ -6669,6 +7089,17 @@ var LocalChildRegistry = class {
6669
7089
  /** See {@link LocalChildRegistryOptions.isAggregatedCollectionMethod}. */
6670
7090
  isAggregatedCollectionMethod;
6671
7091
  isAddonPinnedCall;
7092
+ /** See {@link ForwardCensus}. Mutable counters on the hot path, by design. */
7093
+ forwardedRaw = 0;
7094
+ forwardedDecoded = 0;
7095
+ /**
7096
+ * The per-ACTION byte census (D456). `ForwardCensus` above is the same
7097
+ * question asked of the whole plane; this one asks it per `<cap>.<method>`,
7098
+ * beside the bytes that method actually moved. Both are kept: `socketFwd=`
7099
+ * says whether the negotiation is working at all, and a `raw=0` on ONE
7100
+ * method with `raw>0` on its neighbours is a different fault.
7101
+ */
7102
+ actionCensus = createCapActionCensus();
6672
7103
  /** See {@link LocalChildRegistryOptions.capTimeoutMs}. */
6673
7104
  capTimeoutMs;
6674
7105
  /** See {@link LocalChildRegistryOptions.capUsageObserver}. */
@@ -6682,6 +7113,12 @@ var LocalChildRegistry = class {
6682
7113
  /** Last pattern-set string logged per child, to dedup the INFO line. */
6683
7114
  loggedPatternSet = /* @__PURE__ */ new Map();
6684
7115
  /**
7116
+ * Derived `(cap, device?) → child` index over {@link children}, or `null`
7117
+ * when it must be rebuilt. See {@link capIndex} for why it is a cache and
7118
+ * never a registry (D3, D457).
7119
+ */
7120
+ capChildIndex = null;
7121
+ /**
6685
7122
  * Accepts either a plain positional `server` argument (backward-compatible)
6686
7123
  * or a full `LocalChildRegistryOptions` object.
6687
7124
  *
@@ -6738,10 +7175,43 @@ var LocalChildRegistry = class {
6738
7175
  * only per-peer attribution that exists: `/proc` reports one `rchar`/`wchar`
6739
7176
  * pair for the whole process and has no per-socket breakdown.
6740
7177
  */
6741
- getChildSocketTraffic(childId) {
7178
+ getChildSocketTraffic(childId) {
7179
+ const entry = this.children.get(childId);
7180
+ if (entry === void 0) return null;
7181
+ return entry.channel.readTraffic?.() ?? null;
7182
+ }
7183
+ /**
7184
+ * Bytes this process has written to a child that the child has not yet
7185
+ * taken, live, or `null` when the channel keeps no such reading. Read on
7186
+ * every heartbeat probe (D446) — a child that stops draining is the shape
7187
+ * of an outbound-queue episode, and the number lives on the channel.
7188
+ */
7189
+ getChildQueuedBytes(childId) {
6742
7190
  const entry = this.children.get(childId);
6743
7191
  if (entry === void 0) return null;
6744
- return entry.channel.readTraffic?.() ?? null;
7192
+ return entry.channel.queuedBytes?.() ?? null;
7193
+ }
7194
+ /** Cumulative {@link ForwardCensus} of this parent's `cap-call-out` routing. */
7195
+ readForwardCensus() {
7196
+ return {
7197
+ raw: this.forwardedRaw,
7198
+ decoded: this.forwardedDecoded
7199
+ };
7200
+ }
7201
+ /**
7202
+ * Cumulative per-action byte census: peer → `<cap>.<method>` → bytes, calls
7203
+ * and the raw/decoded split. A COPY (`cap-action-census.ts` explains why it
7204
+ * must be one); the heartbeat subtracts the previous reading.
7205
+ *
7206
+ * This is the instrument D454 closed by asking for. `socketTopBytes=` names
7207
+ * the peer that moves the bytes and `txkB=` says they are responses; neither
7208
+ * can say WHICH method originates them, and after raw-forward the bytes that
7209
+ * cross are no longer the proxy for what hub-main pays — 85.7 % of sibling
7210
+ * calls cross unread. What costs is what hub-main LOOKS at, and that is the
7211
+ * `decoded` half of an entry, next to the bytes it carried.
7212
+ */
7213
+ readActionCensus() {
7214
+ return this.actionCensus.read();
6745
7215
  }
6746
7216
  /**
6747
7217
  * The regime the counters above were produced under, read once from
@@ -6857,11 +7327,12 @@ var LocalChildRegistry = class {
6857
7327
  * compete for the same capName.
6858
7328
  */
6859
7329
  resolveChildId(capName, deviceId) {
7330
+ const index = this.capIndex();
6860
7331
  if (deviceId !== void 0) {
6861
- const deviceOwner = this.findChildId((cap) => cap.capName === capName && cap.deviceId === deviceId);
7332
+ const deviceOwner = index.deviceOwner(capName, deviceId);
6862
7333
  if (deviceOwner !== null) return deviceOwner;
6863
7334
  }
6864
- const candidates = this.findAllChildIds((cap) => cap.capName === capName && cap.deviceId === void 0);
7335
+ const candidates = index.singletonCandidates(capName);
6865
7336
  if (candidates.length === 0) return null;
6866
7337
  if (candidates.length === 1) return candidates[0];
6867
7338
  const preferredAddonId = this.getActiveSingletonAddonId?.(capName) ?? null;
@@ -6894,16 +7365,37 @@ var LocalChildRegistry = class {
6894
7365
  });
6895
7366
  } catch {}
6896
7367
  }
6897
- /** First child whose cap manifest contains a descriptor matching `predicate`. */
6898
- findChildId(predicate) {
6899
- for (const entry of this.children.values()) if (entry.caps.some(predicate)) return entry.childId;
6900
- return null;
7368
+ /**
7369
+ * The cap index over the CURRENT children, rebuilt on first use after any
7370
+ * mutation of {@link children}.
7371
+ *
7372
+ * This replaced a linear walk of every child's cap manifest per
7373
+ * `cap-call-out`, measured at **7.1 % of hub-main's busy main thread**
7374
+ * across 39 children (D456). Why it did not exist before is the whole
7375
+ * design: what it indexes moves — a runner is replaced on every update, a
7376
+ * crash respawns one, a child re-registers with a replacement manifest after
7377
+ * init — and an index that names a dead child routes work into a socket
7378
+ * nobody is reading, which is strictly worse than a slow scan.
7379
+ *
7380
+ * So it is not maintained; it is DISCARDED. `buildCapChildIndex` is a pure
7381
+ * function of `children.values()`, the only two writers of that map
7382
+ * ({@link invalidateCapIndex}'s call sites) drop the index in the same
7383
+ * statement, and nothing else can construct one. There is no add-on-register
7384
+ * / remove-on-close path that could disagree with the map — i.e. no shadow
7385
+ * registry (D3): the map remains the single authority and this holds no fact
7386
+ * it does not.
7387
+ *
7388
+ * The operator's active-singleton preference is deliberately NOT indexed —
7389
+ * it changes without any child connecting or leaving, so `resolveChildId`
7390
+ * reads it live and the index only supplies the candidate list.
7391
+ */
7392
+ capIndex() {
7393
+ if (this.capChildIndex === null) this.capChildIndex = buildCapChildIndex(this.children.values());
7394
+ return this.capChildIndex;
6901
7395
  }
6902
- /** Every child whose cap manifest contains a descriptor matching `predicate`. */
6903
- findAllChildIds(predicate) {
6904
- const out = [];
6905
- for (const entry of this.children.values()) if (entry.caps.some(predicate)) out.push(entry.childId);
6906
- return out;
7396
+ /** Drop the derived index. MUST follow every write to {@link children}. */
7397
+ invalidateCapIndex() {
7398
+ this.capChildIndex = null;
6907
7399
  }
6908
7400
  /**
6909
7401
  * Does the named child currently provide `(capName, deviceId?)`?
@@ -7099,6 +7591,14 @@ var LocalChildRegistry = class {
7099
7591
  onConnection(channel) {
7100
7592
  let childId = null;
7101
7593
  const incarnation = this.nextIncarnation++;
7594
+ channel.observeActions?.({
7595
+ label: capActionLabel,
7596
+ record: (label, reqBytes, resBytes) => {
7597
+ const peerId = childId ?? "(pre-register)";
7598
+ if (label === null) this.actionCensus.recordUnlabelled(peerId, reqBytes + resBytes);
7599
+ else this.actionCensus.record(peerId, label, reqBytes, resBytes);
7600
+ }
7601
+ });
7102
7602
  channel.onEvent((body) => {
7103
7603
  const msg = body;
7104
7604
  if (msg.kind === "event") {
@@ -7121,7 +7621,7 @@ var LocalChildRegistry = class {
7121
7621
  return;
7122
7622
  }
7123
7623
  });
7124
- channel.onRequest(async (body) => {
7624
+ channel.onRequest(async (body, payload) => {
7125
7625
  const msg = body;
7126
7626
  if (msg.kind === "register") {
7127
7627
  if (childId !== null && childId !== msg.childId) throw new Error(`child attempted to change identity from "${childId}" to "${msg.childId}"`);
@@ -7138,8 +7638,10 @@ var LocalChildRegistry = class {
7138
7638
  channel,
7139
7639
  caps: msg.caps,
7140
7640
  eventPatterns,
7141
- incarnation
7641
+ incarnation,
7642
+ rawForward: msg.rawForward === true
7142
7643
  });
7644
+ this.invalidateCapIndex();
7143
7645
  this.logPatternSet(msg.childId, eventPatterns);
7144
7646
  this.registeredHandler({
7145
7647
  childId: msg.childId,
@@ -7147,33 +7649,62 @@ var LocalChildRegistry = class {
7147
7649
  incarnation,
7148
7650
  ...msg.customActions !== void 0 ? { customActions: msg.customActions } : {}
7149
7651
  });
7150
- return { ok: true };
7652
+ return {
7653
+ ok: true,
7654
+ rawForward: true
7655
+ };
7151
7656
  }
7152
7657
  if (msg.kind === "cap-call-out") {
7153
7658
  const out = msg;
7154
- const input = {
7659
+ const hints = out.hints ?? liftRoutingHints(out.args);
7660
+ const materialise = () => payload !== void 0 ? unpackPayload(payload) : out.args;
7661
+ const buildInput = () => ({
7155
7662
  capName: out.capName,
7156
7663
  method: out.method,
7157
- args: out.args,
7664
+ args: materialise(),
7158
7665
  ...out.deviceId !== void 0 ? { deviceId: out.deviceId } : {},
7159
7666
  ...out.nodeId !== void 0 ? { nodeId: out.nodeId } : {},
7160
7667
  ...out.native === true ? { native: true } : {},
7161
7668
  ...childId !== null ? { callerAddonId: childId } : {}
7162
- };
7163
- const pinnedNodeId = out.nodeId ?? extractNodeId(out.args);
7669
+ });
7670
+ const pinnedNodeId = out.nodeId ?? hints.nodeId;
7164
7671
  const pinTargetsThisNode = pinnedNodeId !== void 0 && this.ownNodeId !== void 0 && pinnedNodeId === this.ownNodeId;
7165
7672
  const aggregated = pinnedNodeId === void 0 && this.isAggregatedCollectionMethod?.(out.capName, out.method) === true;
7166
- const addonPinned = pinnedNodeId === void 0 && this.isAddonPinnedCall?.(out.capName, out.method, out.args) === true;
7673
+ const addonPinned = pinnedNodeId === void 0 && this.isAddonPinnedCall?.(out.capName, out.method, hints) === true;
7167
7674
  const target = !(out.native === true) && !aggregated && !addonPinned && (pinnedNodeId === void 0 || pinTargetsThisNode) ? this.resolveChildId(out.capName, out.deviceId) : null;
7168
7675
  this.recordCapUsage(childId, target, out.capName, out.method);
7676
+ const actionPeer = childId ?? "(pre-register)";
7677
+ const actionLabel = `${out.capName}.${out.method}`;
7169
7678
  if (target !== null) {
7170
7679
  if (!this.egressRoutedCaps.has(out.capName)) {
7171
7680
  this.egressRoutedCaps.add(out.capName);
7172
7681
  this.logger?.info("routed child egress over UDS", { capName: out.capName });
7173
7682
  }
7174
- return this.callCap(input);
7683
+ const entry = this.children.get(target);
7684
+ if (payload !== void 0 && entry !== void 0 && entry.rawForward) {
7685
+ this.forwardedRaw += 1;
7686
+ this.actionCensus.recordForward(actionPeer, actionLabel, "raw");
7687
+ const forward = {
7688
+ kind: "cap-call",
7689
+ capName: out.capName,
7690
+ method: out.method,
7691
+ args: void 0,
7692
+ ...out.deviceId !== void 0 ? { deviceId: out.deviceId } : {}
7693
+ };
7694
+ const declared = this.capTimeoutMs?.(out.capName, out.method);
7695
+ return entry.channel.request(forward, {
7696
+ ...typeof declared === "number" ? { timeoutMs: declared } : {},
7697
+ payload
7698
+ });
7699
+ }
7700
+ this.forwardedDecoded += 1;
7701
+ this.actionCensus.recordForward(actionPeer, actionLabel, "decoded");
7702
+ const input = buildInput();
7703
+ return entry !== void 0 ? this.callCapOnChild(target, input) : this.callCap(input);
7175
7704
  }
7176
- if (this.onUnownedCall !== void 0) return this.onUnownedCall(input);
7705
+ this.forwardedDecoded += 1;
7706
+ this.actionCensus.recordForward(actionPeer, actionLabel, "decoded");
7707
+ if (this.onUnownedCall !== void 0) return this.onUnownedCall(buildInput());
7177
7708
  throw new Error(`${UDS_NO_ROUTE_PREFIX}: cap-call-out has no local provider for "${out.capName}" and no fallback`);
7178
7709
  }
7179
7710
  if (msg.kind === "readiness-request") return {
@@ -7195,7 +7726,9 @@ var LocalChildRegistry = class {
7195
7726
  return;
7196
7727
  }
7197
7728
  this.children.delete(childId);
7729
+ this.invalidateCapIndex();
7198
7730
  this.childEventStats.delete(childId);
7731
+ this.actionCensus.forget(childId);
7199
7732
  this.loggedPatternSet.delete(childId);
7200
7733
  this.goneHandler(childId, incarnation);
7201
7734
  });
@@ -7203,6 +7736,11 @@ var LocalChildRegistry = class {
7203
7736
  };
7204
7737
  //#endregion
7205
7738
  //#region src/kernel/transport/local-child-client.ts
7739
+ /** Did the parent's `register` answer say it accepts payload-borne args? */
7740
+ function ackAcceptsRawForward(ack) {
7741
+ if (ack === null || typeof ack !== "object") return false;
7742
+ return ack.rawForward === true;
7743
+ }
7206
7744
  /**
7207
7745
  * Child side of the local UDS transport. Connects to its parent (hub or
7208
7746
  * agent), registers its cap manifest, and serves parent→child cap calls by
@@ -7258,6 +7796,13 @@ var LocalChildClient = class {
7258
7796
  latestCustomActions = null;
7259
7797
  /** Events and logs queued while the channel is not yet open. */
7260
7798
  pendingEmits = [];
7799
+ /**
7800
+ * Whether the parent acknowledged raw-forward at `register`. Until it has,
7801
+ * `callOut` sends args inline exactly as every child always did — a legacy
7802
+ * parent would decode a payload-borne call to `args: undefined` and run the
7803
+ * provider on nothing, silently. Re-read on every re-register.
7804
+ */
7805
+ parentRawForward = false;
7261
7806
  /** Handler for parent→child events. Registered via `onEvent`. */
7262
7807
  eventHandler = null;
7263
7808
  /**
@@ -7365,147 +7910,611 @@ var LocalChildClient = class {
7365
7910
  if (this.client !== null) throw new Error("LocalChildClient: already started — call close() first");
7366
7911
  const client = createLocalTransport().createClient(this.options.nodeId);
7367
7912
  const channel = await client.connect();
7368
- channel.onRequest(async (body) => {
7913
+ channel.onRequest(async (body, payload) => {
7369
7914
  const msg = body;
7370
- if (msg.kind === "cap-call") return this.options.dispatch({
7371
- capName: msg.capName,
7372
- method: msg.method,
7373
- args: msg.args,
7374
- ...msg.deviceId !== void 0 ? { deviceId: msg.deviceId } : {},
7375
- ...msg.addonId !== void 0 ? { addonId: msg.addonId } : {}
7915
+ if (msg.kind === "cap-call") {
7916
+ const args = payload !== void 0 ? unpackPayload(payload) : msg.args;
7917
+ const result = await this.options.dispatch({
7918
+ capName: msg.capName,
7919
+ method: msg.method,
7920
+ args,
7921
+ ...msg.deviceId !== void 0 ? { deviceId: msg.deviceId } : {},
7922
+ ...msg.addonId !== void 0 ? { addonId: msg.addonId } : {}
7923
+ });
7924
+ if (payload !== void 0 && result !== void 0) return new PayloadReply(void 0, packPayload(result));
7925
+ return result;
7926
+ }
7927
+ if (msg.kind === "addon-call") {
7928
+ if (this.addonCallHandler === null) throw new Error(`LocalChildClient: addon-call for "${msg.addonId}" arrived but no onAddonCall handler is registered`);
7929
+ return this.addonCallHandler({
7930
+ addonId: msg.addonId,
7931
+ target: msg.target,
7932
+ ...msg.action !== void 0 ? { action: msg.action } : {},
7933
+ ...msg.method !== void 0 ? { method: msg.method } : {},
7934
+ ...msg.args !== void 0 ? { args: msg.args } : {},
7935
+ ...msg.caller !== void 0 ? { caller: msg.caller } : {}
7936
+ });
7937
+ }
7938
+ throw new Error(`unknown parent request kind: ${msg.kind}`);
7939
+ });
7940
+ channel.onEvent((body) => {
7941
+ const msg = body;
7942
+ if (msg.kind === "event") {
7943
+ this.eventHandler?.(msg.event);
7944
+ return;
7945
+ }
7946
+ if (msg.kind === "set-log-level") this.setLogLevelHandler?.(msg.level);
7947
+ });
7948
+ const register = this.registerFrame(this.latestCaps);
7949
+ try {
7950
+ this.parentRawForward = ackAcceptsRawForward(await channel.request(register));
7951
+ } catch (err) {
7952
+ await client.close();
7953
+ throw err;
7954
+ }
7955
+ this.client = client;
7956
+ this.channel = channel;
7957
+ this.flushPending(channel);
7958
+ for (const cb of this.connectedHandlers) cb();
7959
+ }
7960
+ /** Flush buffered pre-start emits over the now-open channel. */
7961
+ flushPending(channel) {
7962
+ for (const item of this.pendingEmits) channel.emit(item.msg);
7963
+ this.pendingEmits.length = 0;
7964
+ }
7965
+ /**
7966
+ * Re-send the cap manifest to the parent, atomically replacing the child's
7967
+ * registered descriptor set. Call this after device-restore so newly
7968
+ * registered native (device-scoped) caps become routable over UDS.
7969
+ *
7970
+ * Safe to call before `start()`: the new set is buffered and `start()` sends
7971
+ * it (device-restore can race ahead of the UDS connect). After `start()`,
7972
+ * the set is sent immediately.
7973
+ */
7974
+ async updateCaps(caps, customActions) {
7975
+ this.latestCaps = caps;
7976
+ if (customActions !== void 0) this.latestCustomActions = customActions;
7977
+ if (this.channel === null) return;
7978
+ this.parentRawForward = ackAcceptsRawForward(await this.channel.request(this.registerFrame(caps)));
7979
+ }
7980
+ /**
7981
+ * The register frame: caps, plus everything a re-register must atomically
7982
+ * re-carry so no separate re-sync can be missed — the subscription union
7983
+ * (omitted if nothing ever declared, which keeps the fail-open shape) and the
7984
+ * custom-action catalogs (omitted until the init loop produced them).
7985
+ */
7986
+ registerFrame(caps) {
7987
+ return {
7988
+ kind: "register",
7989
+ childId: this.options.childId,
7990
+ caps,
7991
+ ...this.hasDeclaredEventPatterns ? { eventPatterns: this.latestEventPatterns } : {},
7992
+ ...this.latestCustomActions !== null ? { customActions: this.latestCustomActions } : {},
7993
+ rawForward: true
7994
+ };
7995
+ }
7996
+ /**
7997
+ * Fire-and-forget: send a system event to the parent for forwarding to the
7998
+ * hub event bus. Safe to call before `start()` — events are buffered and
7999
+ * flushed on connect.
8000
+ */
8001
+ emitEvent(event) {
8002
+ const msg = {
8003
+ kind: "event",
8004
+ event
8005
+ };
8006
+ if (this.channel !== null) this.channel.emit(msg);
8007
+ else this.pendingEmits.push({
8008
+ kind: "event",
8009
+ msg
8010
+ });
8011
+ }
8012
+ /**
8013
+ * Fire-and-forget: send a structured log entry to the parent. Safe to call
8014
+ * before `start()` — log entries are buffered and flushed on connect.
8015
+ */
8016
+ sendLog(entry) {
8017
+ const msg = {
8018
+ kind: "log",
8019
+ ...entry
8020
+ };
8021
+ if (this.channel !== null) this.channel.emit(msg);
8022
+ else this.pendingEmits.push({
8023
+ kind: "log",
8024
+ msg
8025
+ });
8026
+ }
8027
+ /**
8028
+ * Request the current readiness snapshot from the parent. Returns the
8029
+ * authoritative set of `IReadinessRegistryRecord` entries the hub holds.
8030
+ * Requires `start()` to have been called; throws with a clear message if not.
8031
+ */
8032
+ async requestReadinessSnapshot() {
8033
+ if (this.channel === null) throw new Error("LocalChildClient: requestReadinessSnapshot called before start()");
8034
+ return (await this.channel.request({ kind: "readiness-request" })).records;
8035
+ }
8036
+ /**
8037
+ * Ask the parent to execute a cap call this child does NOT own. The parent
8038
+ * routes to the owning local sibling over UDS, or — if no sibling owns it —
8039
+ * to its `onUnownedCall` fallback (the cluster CapabilityRegistry in
8040
+ * production). Throws if called before `start()`.
8041
+ */
8042
+ async callOut(input) {
8043
+ if (this.channel === null) throw new Error("LocalChildClient: callOut before start");
8044
+ const routing = {
8045
+ ...input.deviceId !== void 0 ? { deviceId: input.deviceId } : {},
8046
+ ...input.nodeId !== void 0 ? { nodeId: input.nodeId } : {},
8047
+ ...input.native === true ? { native: true } : {}
8048
+ };
8049
+ if (this.parentRawForward && input.args !== void 0) {
8050
+ const hints = liftRoutingHints(input.args);
8051
+ const msg = {
8052
+ kind: "cap-call-out",
8053
+ capName: input.capName,
8054
+ method: input.method,
8055
+ ...Object.keys(hints).length > 0 ? { hints } : {},
8056
+ ...routing
8057
+ };
8058
+ const reply = await this.channel.request(msg, { payload: packPayload(input.args) });
8059
+ return reply instanceof PayloadReply ? unpackPayload(reply.payload) : reply;
8060
+ }
8061
+ const msg = {
8062
+ kind: "cap-call-out",
8063
+ capName: input.capName,
8064
+ method: input.method,
8065
+ args: input.args,
8066
+ ...routing
8067
+ };
8068
+ return this.channel.request(msg);
8069
+ }
8070
+ /** Disconnect from the parent. Safe to call before `start()` (no-op) and idempotent. */
8071
+ async close() {
8072
+ await this.client?.close();
8073
+ this.client = null;
8074
+ this.channel = null;
8075
+ }
8076
+ };
8077
+ //#endregion
8078
+ //#region src/kernel/transport/cap-route.ts
8079
+ function buildMessage(capName, method, detail) {
8080
+ const call = method !== void 0 ? `${capName}.${method}` : capName;
8081
+ const target = detail.nodeId !== void 0 ? ` to node ${detail.nodeId}` : "";
8082
+ const rejectedStr = detail.rejected.map((r) => `${r.kind}=${r.why}`).join("; ");
8083
+ const rejectedClause = rejectedStr.length > 0 ? ` (rejected: ${rejectedStr})` : "";
8084
+ return `${call} not routable${target}: ${detail.reason}${rejectedClause}`;
8085
+ }
8086
+ var CapRouteError = class extends Error {
8087
+ reason;
8088
+ nodeId;
8089
+ rejected;
8090
+ /**
8091
+ * @param cause Optional original error that triggered this routing failure.
8092
+ * Stored as `Error.cause` (TC39 standard option, Node 16.9+).
8093
+ * Dispatchers wrapping transport errors MUST pass the original.
8094
+ */
8095
+ constructor(capName, method, detail, cause) {
8096
+ super(buildMessage(capName, method, detail), cause !== void 0 ? { cause } : void 0);
8097
+ this.name = "CapRouteError";
8098
+ this.reason = detail.reason;
8099
+ this.nodeId = detail.nodeId;
8100
+ this.rejected = detail.rejected;
8101
+ }
8102
+ };
8103
+ /**
8104
+ * Classifies a (capName, opts) pair into a typed CapRoute dispatch descriptor.
8105
+ *
8106
+ * Precedence (explicit nodeId path):
8107
+ * 1. hub-in-process — hub node + hubInProcessProvides
8108
+ * 2. hub-local-uds — hub node + hubLocalChildProvides
8109
+ * 3. node-offline → CapRouteError{reason:'node-offline'}
8110
+ * 4. agent-child-forward — node is an agent AND nodeKnowsCap
8111
+ * 5. remote-moleculer — any other online non-agent node
8112
+ *
8113
+ * Singleton path (no nodeId):
8114
+ * 1. hub-in-process (hub provides in-process)
8115
+ * 2. hub-local-uds (a hub-local UDS child provides it)
8116
+ * 3. remote-moleculer (any online, non-hub node that knows it)
8117
+ * 4. → CapRouteError{reason:'no-provider', rejected: all considered routes}
8118
+ *
8119
+ * PURE: no side effects, no async, no broker/registry imports.
8120
+ */
8121
+ function classifyCapRoute(capName, opts, snapshot) {
8122
+ const rejected = [];
8123
+ if (opts.nodeId !== void 0) return classifyExplicitNode(capName, opts.nodeId, opts.deviceId, snapshot, rejected);
8124
+ return classifySingleton(capName, opts.deviceId, snapshot, rejected);
8125
+ }
8126
+ function classifyExplicitNode(capName, nodeId, deviceId, snap, rejected) {
8127
+ if (nodeId === snap.hubNodeId) {
8128
+ const residentProvides = snap.hubResidentProvides;
8129
+ const providesOnHub = residentProvides ?? snap.hubInProcessProvides;
8130
+ const getRef = residentProvides !== void 0 ? snap.getHubResidentProviderRef : snap.getInProcessProviderRef;
8131
+ if (providesOnHub(capName)) {
8132
+ const ref = getRef?.(capName) ?? null;
8133
+ if (ref !== null) return {
8134
+ kind: "hub-in-process",
8135
+ capName,
8136
+ ref
8137
+ };
8138
+ rejected.push({
8139
+ kind: "hub-in-process",
8140
+ why: "provider ref not available in snapshot"
8141
+ });
8142
+ throw new CapRouteError(capName, void 0, {
8143
+ reason: "no-provider",
8144
+ nodeId,
8145
+ rejected
8146
+ });
8147
+ }
8148
+ if (snap.hubLocalChildProvides(capName, deviceId)) {
8149
+ const childId = snap.getHubLocalChildId?.(capName, deviceId) ?? null;
8150
+ if (childId !== null) return {
8151
+ kind: "hub-local-uds",
8152
+ capName,
8153
+ childId
8154
+ };
8155
+ rejected.push({
8156
+ kind: "hub-local-uds",
8157
+ why: "child id not resolvable from snapshot"
8158
+ });
8159
+ throw new CapRouteError(capName, void 0, {
8160
+ reason: "no-provider",
8161
+ nodeId,
8162
+ rejected
7376
8163
  });
7377
- if (msg.kind === "addon-call") {
7378
- if (this.addonCallHandler === null) throw new Error(`LocalChildClient: addon-call for "${msg.addonId}" arrived but no onAddonCall handler is registered`);
7379
- return this.addonCallHandler({
7380
- addonId: msg.addonId,
7381
- target: msg.target,
7382
- ...msg.action !== void 0 ? { action: msg.action } : {},
7383
- ...msg.method !== void 0 ? { method: msg.method } : {},
7384
- ...msg.args !== void 0 ? { args: msg.args } : {},
7385
- ...msg.caller !== void 0 ? { caller: msg.caller } : {}
7386
- });
7387
- }
7388
- throw new Error(`unknown parent request kind: ${msg.kind}`);
8164
+ }
8165
+ rejected.push({
8166
+ kind: "hub-in-process",
8167
+ why: "hub does not provide this cap in-process"
7389
8168
  });
7390
- channel.onEvent((body) => {
7391
- const msg = body;
7392
- if (msg.kind === "event") {
7393
- this.eventHandler?.(msg.event);
7394
- return;
7395
- }
7396
- if (msg.kind === "set-log-level") this.setLogLevelHandler?.(msg.level);
8169
+ rejected.push({
8170
+ kind: "hub-local-uds",
8171
+ why: "no hub-local child provides this cap"
8172
+ });
8173
+ throw new CapRouteError(capName, void 0, {
8174
+ reason: "no-provider",
8175
+ nodeId,
8176
+ rejected
7397
8177
  });
7398
- const register = this.registerFrame(this.latestCaps);
7399
- try {
7400
- await channel.request(register);
7401
- } catch (err) {
7402
- await client.close();
7403
- throw err;
7404
- }
7405
- this.client = client;
7406
- this.channel = channel;
7407
- this.flushPending(channel);
7408
- for (const cb of this.connectedHandlers) cb();
7409
- }
7410
- /** Flush buffered pre-start emits over the now-open channel. */
7411
- flushPending(channel) {
7412
- for (const item of this.pendingEmits) channel.emit(item.msg);
7413
- this.pendingEmits.length = 0;
7414
8178
  }
7415
- /**
7416
- * Re-send the cap manifest to the parent, atomically replacing the child's
7417
- * registered descriptor set. Call this after device-restore so newly
7418
- * registered native (device-scoped) caps become routable over UDS.
7419
- *
7420
- * Safe to call before `start()`: the new set is buffered and `start()` sends
7421
- * it (device-restore can race ahead of the UDS connect). After `start()`,
7422
- * the set is sent immediately.
7423
- */
7424
- async updateCaps(caps, customActions) {
7425
- this.latestCaps = caps;
7426
- if (customActions !== void 0) this.latestCustomActions = customActions;
7427
- if (this.channel === null) return;
7428
- await this.channel.request(this.registerFrame(caps));
8179
+ if (!snap.nodeOnline(nodeId)) {
8180
+ rejected.push({
8181
+ kind: "remote-moleculer",
8182
+ why: `node ${nodeId} is offline`
8183
+ });
8184
+ throw new CapRouteError(capName, void 0, {
8185
+ reason: "node-offline",
8186
+ nodeId,
8187
+ rejected
8188
+ });
7429
8189
  }
7430
- /**
7431
- * The register frame: caps, plus everything a re-register must atomically
7432
- * re-carry so no separate re-sync can be missed — the subscription union
7433
- * (omitted if nothing ever declared, which keeps the fail-open shape) and the
7434
- * custom-action catalogs (omitted until the init loop produced them).
7435
- */
7436
- registerFrame(caps) {
7437
- return {
7438
- kind: "register",
7439
- childId: this.options.childId,
7440
- caps,
7441
- ...this.hasDeclaredEventPatterns ? { eventPatterns: this.latestEventPatterns } : {},
7442
- ...this.latestCustomActions !== null ? { customActions: this.latestCustomActions } : {}
8190
+ if (snap.nodeIsAgent(nodeId)) {
8191
+ if (snap.nodeKnowsCap(nodeId, capName)) return {
8192
+ kind: "agent-child-forward",
8193
+ capName,
8194
+ agentNodeId: nodeId,
8195
+ childId: snap.getAgentChildId?.(nodeId, capName) ?? void 0
7443
8196
  };
8197
+ rejected.push({
8198
+ kind: "agent-child-forward",
8199
+ why: `agent ${nodeId} does not know cap ${capName}`
8200
+ });
8201
+ throw new CapRouteError(capName, void 0, {
8202
+ reason: "no-provider",
8203
+ nodeId,
8204
+ rejected
8205
+ });
7444
8206
  }
7445
- /**
7446
- * Fire-and-forget: send a system event to the parent for forwarding to the
7447
- * hub event bus. Safe to call before `start()` — events are buffered and
7448
- * flushed on connect.
7449
- */
7450
- emitEvent(event) {
7451
- const msg = {
7452
- kind: "event",
7453
- event
8207
+ return {
8208
+ kind: "remote-moleculer",
8209
+ capName,
8210
+ nodeId
8211
+ };
8212
+ }
8213
+ function classifySingleton(capName, deviceId, snap, rejected) {
8214
+ if (snap.hubInProcessProvides(capName)) {
8215
+ const ref = snap.getInProcessProviderRef?.(capName) ?? null;
8216
+ if (ref !== null) return {
8217
+ kind: "hub-in-process",
8218
+ capName,
8219
+ ref
7454
8220
  };
7455
- if (this.channel !== null) this.channel.emit(msg);
7456
- else this.pendingEmits.push({
7457
- kind: "event",
7458
- msg
8221
+ rejected.push({
8222
+ kind: "hub-in-process",
8223
+ why: "provider ref not available in snapshot"
8224
+ });
8225
+ } else rejected.push({
8226
+ kind: "hub-in-process",
8227
+ why: "hub does not provide this cap in-process"
8228
+ });
8229
+ if (snap.hubLocalChildProvides(capName, deviceId)) {
8230
+ const childId = snap.getHubLocalChildId?.(capName, deviceId) ?? null;
8231
+ if (childId !== null) return {
8232
+ kind: "hub-local-uds",
8233
+ capName,
8234
+ childId
8235
+ };
8236
+ rejected.push({
8237
+ kind: "hub-local-uds",
8238
+ why: "child id not resolvable from snapshot"
7459
8239
  });
8240
+ } else rejected.push({
8241
+ kind: "hub-local-uds",
8242
+ why: "no hub-local child provides this cap"
8243
+ });
8244
+ const knownNodes = snap.listKnownNodeIds?.() ?? [];
8245
+ for (const nodeId of knownNodes) if (nodeId !== snap.hubNodeId && snap.nodeOnline(nodeId) && snap.nodeKnowsCap(nodeId, capName)) return {
8246
+ kind: "remote-moleculer",
8247
+ capName,
8248
+ nodeId
8249
+ };
8250
+ rejected.push({
8251
+ kind: "remote-moleculer",
8252
+ why: "no online remote node knows this cap"
8253
+ });
8254
+ throw new CapRouteError(capName, void 0, {
8255
+ reason: "no-provider",
8256
+ rejected
8257
+ });
8258
+ }
8259
+ //#endregion
8260
+ //#region src/kernel/moleculer/resilient-cap-call.ts
8261
+ /** Moleculer error `type` values meaning "the service is not (yet) routable". */
8262
+ var DISCOVERY_ERROR_TYPES = new Set(["SERVICE_NOT_FOUND", "SERVICE_NOT_AVAILABLE"]);
8263
+ /** Default ceiling for the discovery wait — fail-fast past this. */
8264
+ var DEFAULT_DISCOVERY_TIMEOUT_MS$1 = 3e4;
8265
+ function isDiscoveryError(err) {
8266
+ if (typeof err !== "object" || err === null) return false;
8267
+ const type = err.type;
8268
+ return typeof type === "string" && DISCOVERY_ERROR_TYPES.has(type);
8269
+ }
8270
+ /**
8271
+ * Call a Moleculer action; on a service-discovery error, wait for the
8272
+ * named service to be discovered and retry the call exactly once.
8273
+ */
8274
+ async function callWithServiceDiscovery(broker, serviceName, action, params, opts, discoveryTimeoutMs = DEFAULT_DISCOVERY_TIMEOUT_MS$1) {
8275
+ try {
8276
+ return await broker.call(action, params, opts);
8277
+ } catch (err) {
8278
+ if (!isDiscoveryError(err)) throw err;
8279
+ await broker.waitForServices([serviceName], discoveryTimeoutMs);
8280
+ return await broker.call(action, params, opts);
8281
+ }
8282
+ }
8283
+ //#endregion
8284
+ //#region src/kernel/transport/cap-route-resolver.ts
8285
+ /** The Moleculer service name that Task 6's agent registers. */
8286
+ var AGENT_CAP_FWD_SERVICE = "$agent-cap-fwd";
8287
+ /** The Moleculer action (service.action) for agent cap forwarding. */
8288
+ var AGENT_CAP_FWD_ACTION = `${AGENT_CAP_FWD_SERVICE}.forward`;
8289
+ /** Default timeout for remote Moleculer cap calls (ms). */
8290
+ var REMOTE_CALL_TIMEOUT_MS = 6e4;
8291
+ function extractDeviceId(args) {
8292
+ if (args === null || typeof args !== "object") return void 0;
8293
+ const raw = Reflect.get(args, "deviceId");
8294
+ return typeof raw === "number" ? raw : void 0;
8295
+ }
8296
+ /**
8297
+ * Extract an inline `nodeId` string from a cap call's args — the per-call node
8298
+ * pin a forked addon expresses by carrying `nodeId` in the input (the SAME way
8299
+ * device-scoped caps carry `deviceId`, and the way the generated cap-router on
8300
+ * the hub already honours an inline pin). Mirrors {@link extractDeviceId}.
8301
+ *
8302
+ * This is the routing source for hub→remote-node EXECUTION pinning (e.g. the
8303
+ * benchmark addon running a synthetic/decoder workload ON a chosen agent). The
8304
+ * out-of-band `nodePin(op.context)` path does NOT survive the forked addon →
8305
+ * hub UDS link chain, so `onUnownedCall` reads the inline pin instead. Provider
8306
+ * methods that don't declare `nodeId` simply ignore the extra field (they
8307
+ * destructure the fields they need); methods that DO declare it (e.g.
8308
+ * `getEngineProvisioning({nodeId})`) still receive it unchanged.
8309
+ */
8310
+ function extractNodeId(args) {
8311
+ if (args === null || typeof args !== "object") return void 0;
8312
+ const raw = Reflect.get(args, "nodeId");
8313
+ return typeof raw === "string" && raw.length > 0 ? raw : void 0;
8314
+ }
8315
+ /**
8316
+ * Merge `callerAddonId` into `args` as an extra field — the SAME "provider
8317
+ * destructures what it needs and ignores the rest" convention
8318
+ * {@link extractNodeId} already relies on for the inline `nodeId` pin.
8319
+ * `undefined` (the overwhelmingly common case: no caller hint, or the census
8320
+ * consuming it is disarmed) returns `args` unchanged — no allocation. A
8321
+ * non-object `args` (or `null`/array) is returned unchanged too: there is
8322
+ * nowhere to attach an extra key without changing the payload's shape, and a
8323
+ * provider taking a bare value never reads `callerAddonId` off it anyway.
8324
+ */
8325
+ function withCallerAddonId(args, callerAddonId) {
8326
+ if (callerAddonId === void 0) return args;
8327
+ if (args === null || typeof args !== "object" || Array.isArray(args)) return args;
8328
+ return {
8329
+ ...args,
8330
+ callerAddonId
8331
+ };
8332
+ }
8333
+ var CapRouteResolver = class {
8334
+ hubNodeId;
8335
+ broker;
8336
+ hubLocalRegistry;
8337
+ nodeAuthority;
8338
+ inProcessProviders;
8339
+ hubResidentProviders;
8340
+ capTimeoutMs;
8341
+ snapshot;
8342
+ constructor(deps) {
8343
+ this.hubNodeId = deps.hubNodeId;
8344
+ this.broker = deps.broker;
8345
+ this.hubLocalRegistry = deps.hubLocalRegistry;
8346
+ this.nodeAuthority = deps.nodeAuthority;
8347
+ this.inProcessProviders = deps.inProcessProviders;
8348
+ this.hubResidentProviders = deps.hubResidentProviders;
8349
+ this.capTimeoutMs = deps.capTimeoutMs;
8350
+ this.snapshot = this.buildSnapshot();
8351
+ }
8352
+ /** Per-call RPC timeout: the cap method's declared override, else the default. */
8353
+ callTimeout(capName, method) {
8354
+ return this.capTimeoutMs?.(capName, method) ?? REMOTE_CALL_TIMEOUT_MS;
8355
+ }
8356
+ resolveCapRoute(capName, opts) {
8357
+ return classifyCapRoute(capName, opts, this.snapshot);
7460
8358
  }
7461
8359
  /**
7462
- * Fire-and-forget: send a structured log entry to the parent. Safe to call
7463
- * before `start()` — log entries are buffered and flushed on connect.
8360
+ * Resolve the hub-local-uds route for a cap owned by a forked hub-local
8361
+ * child, IGNORING any in-hub provider registered for the same cap name.
8362
+ *
8363
+ * `resolveCapRoute` gives Priority 1 to `hub-in-process`: when an in-hub
8364
+ * provider (e.g. a wrapper) is registered for the cap, the route always
8365
+ * classifies as `hub-in-process` and the hub-local-uds NATIVE child is never
8366
+ * reached. That is correct for the generic dispatch path (the wrapper is the
8367
+ * active provider), but WRONG for the native-cap fallback
8368
+ * (`setNativeFallback`), whose contract is to reach the NATIVE provider in
8369
+ * the forked vendor child so a wrapper can delegate to it. A wrapper cap
8370
+ * with a forked native (today: `snapshot`) otherwise resolves to the wrapper
8371
+ * itself, the native is never invoked, and the wrapper silently falls
8372
+ * through to its secondary strategy.
8373
+ *
8374
+ * This method consults ONLY the hub-local-child authority
8375
+ * (`hubLocalChildProvides` + `getHubLocalChildId`, both deviceId-aware), so
8376
+ * it returns the native child route regardless of any in-process shadow.
8377
+ * Returns null when no hub-local child owns `(capName, deviceId)` — the
8378
+ * caller then falls through to its remote-resolution branch.
7464
8379
  */
7465
- sendLog(entry) {
7466
- const msg = {
7467
- kind: "log",
7468
- ...entry
8380
+ resolveHubLocalUdsRoute(capName, deviceId) {
8381
+ if (!this.snapshot.hubLocalChildProvides(capName, deviceId)) return null;
8382
+ const childId = this.snapshot.getHubLocalChildId?.(capName, deviceId) ?? null;
8383
+ if (childId === null) return null;
8384
+ return {
8385
+ kind: "hub-local-uds",
8386
+ capName,
8387
+ childId
7469
8388
  };
7470
- if (this.channel !== null) this.channel.emit(msg);
7471
- else this.pendingEmits.push({
7472
- kind: "log",
7473
- msg
7474
- });
7475
8389
  }
7476
8390
  /**
7477
- * Request the current readiness snapshot from the parent. Returns the
7478
- * authoritative set of `IReadinessRegistryRecord` entries the hub holds.
7479
- * Requires `start()` to have been called; throws with a clear message if not.
8391
+ * @param addonId Optional out-of-band PROVIDER-selection hint (Task 7.5a):
8392
+ * the addonId of the provider the CALLER already resolved (e.g.
8393
+ * `buildCapCallFn`'s `deps.addonId` for a registered grouped-runner
8394
+ * provider). Only meaningful for `agent-child-forward` — a `remote-moleculer`
8395
+ * route already encodes the provider in its action name
8396
+ * (`${addonId}.${cap}.${method}`, via `NodeCapAuthority.getAddonId`), and a
8397
+ * `hub-local-uds` caller that already knows the addonId (`buildCapCallFn`)
8398
+ * talks to `LocalChildRegistry.callCapOnChild` directly rather than through
8399
+ * `dispatch`.
8400
+ * @param callerAddonId Optional out-of-band CALLER-identity hint — the
8401
+ * addon that ORIGINATED the call, distinct from `addonId` above (which
8402
+ * names the resolved PROVIDER, not the caller). Set by
8403
+ * `onUnownedCall` from `CapCallInput.callerAddonId`
8404
+ * (`LocalChildRegistry` stamped it off the registered `childId`). Only
8405
+ * the `hub-in-process` branch consumes it — merged into `args` so a
8406
+ * diagnostic tool deep in that provider (e.g. device-manager's
8407
+ * fleet-read census) can read it without every provider method changing
8408
+ * signature, the same "extra field the provider destructures or
8409
+ * ignores" convention `nodeId` already uses on this path.
7480
8410
  */
7481
- async requestReadinessSnapshot() {
7482
- if (this.channel === null) throw new Error("LocalChildClient: requestReadinessSnapshot called before start()");
7483
- return (await this.channel.request({ kind: "readiness-request" })).records;
8411
+ async dispatch(route, method, args, addonId, callerAddonId) {
8412
+ try {
8413
+ return await this.dispatchInner(route, method, args, addonId, callerAddonId);
8414
+ } catch (err) {
8415
+ if (err instanceof CapRouteError) throw err;
8416
+ const nodeId = this.routeNodeId(route);
8417
+ const cause = err instanceof Error ? err : new Error(String(err));
8418
+ throw new CapRouteError(route.capName, method, {
8419
+ reason: "transport-failed",
8420
+ nodeId,
8421
+ rejected: [{
8422
+ kind: route.kind,
8423
+ why: cause.message
8424
+ }]
8425
+ }, cause);
8426
+ }
7484
8427
  }
7485
8428
  /**
7486
- * Ask the parent to execute a cap call this child does NOT own. The parent
7487
- * routes to the owning local sibling over UDS, or — if no sibling owns it —
7488
- * to its `onUnownedCall` fallback (the cluster CapabilityRegistry in
7489
- * production). Throws if called before `start()`.
8429
+ * Inner dispatch: may throw CapRouteError (validation failures) or arbitrary
8430
+ * transport errors. The outer `dispatch` wraps non-CapRouteErrors.
7490
8431
  */
7491
- async callOut(input) {
7492
- if (this.channel === null) throw new Error("LocalChildClient: callOut before start");
7493
- const msg = {
7494
- kind: "cap-call-out",
7495
- capName: input.capName,
7496
- method: input.method,
7497
- args: input.args,
7498
- ...input.deviceId !== void 0 ? { deviceId: input.deviceId } : {},
7499
- ...input.nodeId !== void 0 ? { nodeId: input.nodeId } : {},
7500
- ...input.native === true ? { native: true } : {}
8432
+ async dispatchInner(route, method, args, addonId, callerAddonId) {
8433
+ switch (route.kind) {
8434
+ case "hub-in-process": return route.ref.invoke(method, withCallerAddonId(args, callerAddonId));
8435
+ case "hub-local-uds": {
8436
+ const registry = this.hubLocalRegistry;
8437
+ if (registry === null) throw new CapRouteError(route.capName, method, {
8438
+ reason: "no-provider",
8439
+ rejected: [{
8440
+ kind: "hub-local-uds",
8441
+ why: "UDS registry not available"
8442
+ }]
8443
+ });
8444
+ const deviceId = extractDeviceId(args);
8445
+ const input = {
8446
+ capName: route.capName,
8447
+ method,
8448
+ args,
8449
+ ...deviceId !== void 0 ? { deviceId } : {}
8450
+ };
8451
+ return registry.callCapOnChild(route.childId, input);
8452
+ }
8453
+ case "remote-moleculer": {
8454
+ const addonId = this.nodeAuthority.getAddonId(route.nodeId, route.capName);
8455
+ if (addonId === null) throw new CapRouteError(route.capName, method, {
8456
+ reason: "no-provider",
8457
+ nodeId: route.nodeId,
8458
+ rejected: [{
8459
+ kind: "remote-moleculer",
8460
+ why: `no addonId known for ${route.nodeId}/${route.capName}`
8461
+ }]
8462
+ });
8463
+ const deviceId = extractDeviceId(args);
8464
+ const isNative = this.nodeAuthority.isNativeCap(route.nodeId, route.capName, deviceId);
8465
+ const action = capActionName(addonId, route.capName, method, isNative);
8466
+ return callWithServiceDiscovery(this.broker, addonId, action, args, {
8467
+ nodeID: route.nodeId,
8468
+ timeout: this.callTimeout(route.capName, method)
8469
+ });
8470
+ }
8471
+ case "agent-child-forward": {
8472
+ const deviceId = extractDeviceId(args);
8473
+ const params = {
8474
+ capName: route.capName,
8475
+ method,
8476
+ args,
8477
+ ...route.childId !== void 0 ? { childId: route.childId } : {},
8478
+ ...deviceId !== void 0 ? { deviceId } : {},
8479
+ ...addonId !== void 0 ? { addonId } : {}
8480
+ };
8481
+ return callWithServiceDiscovery(this.broker, AGENT_CAP_FWD_SERVICE, AGENT_CAP_FWD_ACTION, params, {
8482
+ nodeID: route.agentNodeId,
8483
+ timeout: this.callTimeout(route.capName, method)
8484
+ });
8485
+ }
8486
+ }
8487
+ }
8488
+ buildSnapshot() {
8489
+ const hubLocalRegistry = this.hubLocalRegistry;
8490
+ const nodeAuthority = this.nodeAuthority;
8491
+ const inProcessProviders = this.inProcessProviders;
8492
+ const hubResidentProviders = this.hubResidentProviders;
8493
+ return {
8494
+ hubNodeId: this.hubNodeId,
8495
+ hubInProcessProvides: (cap) => inProcessProviders(cap) !== null,
8496
+ ...hubResidentProviders !== void 0 ? {
8497
+ hubResidentProvides: (cap) => hubResidentProviders(cap) !== null,
8498
+ getHubResidentProviderRef: (cap) => hubResidentProviders(cap)
8499
+ } : {},
8500
+ hubLocalChildProvides: (cap, deviceId) => hubLocalRegistry !== null && hubLocalRegistry.resolveChildId(cap, deviceId) !== null,
8501
+ nodeKnowsCap: (nodeId, cap) => nodeAuthority.nodeKnowsCap(nodeId, cap),
8502
+ nodeIsAgent: (nodeId) => nodeAuthority.nodeIsAgent(nodeId),
8503
+ nodeOnline: (nodeId) => nodeAuthority.nodeOnline(nodeId),
8504
+ listKnownNodeIds: () => nodeAuthority.listNodeIds(),
8505
+ getInProcessProviderRef: (cap) => inProcessProviders(cap),
8506
+ getHubLocalChildId: (cap, deviceId) => hubLocalRegistry !== null ? hubLocalRegistry.resolveChildId(cap, deviceId) : null,
8507
+ getAgentChildId: (agentNodeId, cap) => nodeAuthority.getAgentChildId(agentNodeId, cap)
7501
8508
  };
7502
- return this.channel.request(msg);
7503
8509
  }
7504
- /** Disconnect from the parent. Safe to call before `start()` (no-op) and idempotent. */
7505
- async close() {
7506
- await this.client?.close();
7507
- this.client = null;
7508
- this.channel = null;
8510
+ /** Extract a nodeId string from a route for error reporting, or undefined. */
8511
+ routeNodeId(route) {
8512
+ switch (route.kind) {
8513
+ case "hub-in-process": return this.hubNodeId;
8514
+ case "hub-local-uds": return `${this.hubNodeId}/${route.childId}`;
8515
+ case "remote-moleculer": return route.nodeId;
8516
+ case "agent-child-forward": return route.agentNodeId;
8517
+ }
7509
8518
  }
7510
8519
  };
7511
8520
  //#endregion
@@ -10219,6 +11228,18 @@ Object.defineProperty(exports, "AddonDepsManager", {
10219
11228
  return AddonDepsManager;
10220
11229
  }
10221
11230
  });
11231
+ Object.defineProperty(exports, "CAP_ACTION_OVERFLOW_LABEL", {
11232
+ enumerable: true,
11233
+ get: function() {
11234
+ return CAP_ACTION_OVERFLOW_LABEL;
11235
+ }
11236
+ });
11237
+ Object.defineProperty(exports, "CAP_ACTION_UNLABELLED", {
11238
+ enumerable: true,
11239
+ get: function() {
11240
+ return CAP_ACTION_UNLABELLED;
11241
+ }
11242
+ });
10222
11243
  Object.defineProperty(exports, "CapRouteError", {
10223
11244
  enumerable: true,
10224
11245
  get: function() {
@@ -10261,6 +11282,12 @@ Object.defineProperty(exports, "DeviceRegistry", {
10261
11282
  return DeviceRegistry;
10262
11283
  }
10263
11284
  });
11285
+ Object.defineProperty(exports, "EMPTY_CAP_ACTION_READING", {
11286
+ enumerable: true,
11287
+ get: function() {
11288
+ return EMPTY_CAP_ACTION_READING;
11289
+ }
11290
+ });
10264
11291
  Object.defineProperty(exports, "EMPTY_SOCKET_DIRECTION", {
10265
11292
  enumerable: true,
10266
11293
  get: function() {
@@ -10357,12 +11384,42 @@ Object.defineProperty(exports, "LocalChildRegistry", {
10357
11384
  return LocalChildRegistry;
10358
11385
  }
10359
11386
  });
11387
+ Object.defineProperty(exports, "MAX_CAP_ACTIONS_PER_PEER", {
11388
+ enumerable: true,
11389
+ get: function() {
11390
+ return MAX_CAP_ACTIONS_PER_PEER;
11391
+ }
11392
+ });
11393
+ Object.defineProperty(exports, "MESH_QUEUE_TOP_N", {
11394
+ enumerable: true,
11395
+ get: function() {
11396
+ return MESH_QUEUE_TOP_N;
11397
+ }
11398
+ });
10360
11399
  Object.defineProperty(exports, "NATIVE_PROVIDER_SERVICE_INFIX", {
10361
11400
  enumerable: true,
10362
11401
  get: function() {
10363
11402
  return NATIVE_PROVIDER_SERVICE_INFIX;
10364
11403
  }
10365
11404
  });
11405
+ Object.defineProperty(exports, "OUTBOUND_QUEUE_COOLDOWN_MS", {
11406
+ enumerable: true,
11407
+ get: function() {
11408
+ return OUTBOUND_QUEUE_COOLDOWN_MS;
11409
+ }
11410
+ });
11411
+ Object.defineProperty(exports, "OUTBOUND_QUEUE_SUSTAIN_MS", {
11412
+ enumerable: true,
11413
+ get: function() {
11414
+ return OUTBOUND_QUEUE_SUSTAIN_MS;
11415
+ }
11416
+ });
11417
+ Object.defineProperty(exports, "OUTBOUND_QUEUE_THRESHOLD_BYTES", {
11418
+ enumerable: true,
11419
+ get: function() {
11420
+ return OUTBOUND_QUEUE_THRESHOLD_BYTES;
11421
+ }
11422
+ });
10366
11423
  Object.defineProperty(exports, "PARENT_ROUTED_PROVIDER_ADDON_ID", {
10367
11424
  enumerable: true,
10368
11425
  get: function() {
@@ -10399,6 +11456,12 @@ Object.defineProperty(exports, "SOCKET_PLANE_TOP_N", {
10399
11456
  return SOCKET_PLANE_TOP_N;
10400
11457
  }
10401
11458
  });
11459
+ Object.defineProperty(exports, "SOCKET_QUEUE_TOP_N", {
11460
+ enumerable: true,
11461
+ get: function() {
11462
+ return SOCKET_QUEUE_TOP_N;
11463
+ }
11464
+ });
10402
11465
  Object.defineProperty(exports, "SocketChannel", {
10403
11466
  enumerable: true,
10404
11467
  get: function() {
@@ -10483,6 +11546,12 @@ Object.defineProperty(exports, "callWithServiceDiscovery", {
10483
11546
  return callWithServiceDiscovery;
10484
11547
  }
10485
11548
  });
11549
+ Object.defineProperty(exports, "capActionLabel", {
11550
+ enumerable: true,
11551
+ get: function() {
11552
+ return capActionLabel;
11553
+ }
11554
+ });
10486
11555
  Object.defineProperty(exports, "capActionName", {
10487
11556
  enumerable: true,
10488
11557
  get: function() {
@@ -10555,6 +11624,12 @@ Object.defineProperty(exports, "createBrokerDeviceManagerApi", {
10555
11624
  return createBrokerDeviceManagerApi;
10556
11625
  }
10557
11626
  });
11627
+ Object.defineProperty(exports, "createCapActionCensus", {
11628
+ enumerable: true,
11629
+ get: function() {
11630
+ return createCapActionCensus;
11631
+ }
11632
+ });
10558
11633
  Object.defineProperty(exports, "createDeferredHeapWatchSink", {
10559
11634
  enumerable: true,
10560
11635
  get: function() {
@@ -10597,6 +11672,18 @@ Object.defineProperty(exports, "createLoopDelayMeter", {
10597
11672
  return createLoopDelayMeter;
10598
11673
  }
10599
11674
  });
11675
+ Object.defineProperty(exports, "createMeshQueueReader", {
11676
+ enumerable: true,
11677
+ get: function() {
11678
+ return createMeshQueueReader;
11679
+ }
11680
+ });
11681
+ Object.defineProperty(exports, "createOutboundQueueGate", {
11682
+ enumerable: true,
11683
+ get: function() {
11684
+ return createOutboundQueueGate;
11685
+ }
11686
+ });
10600
11687
  Object.defineProperty(exports, "createParentUnownedCallHandler", {
10601
11688
  enumerable: true,
10602
11689
  get: function() {
@@ -10669,6 +11756,12 @@ Object.defineProperty(exports, "deserializeTypedArrays", {
10669
11756
  return deserializeTypedArrays;
10670
11757
  }
10671
11758
  });
11759
+ Object.defineProperty(exports, "diffCapActionCensus", {
11760
+ enumerable: true,
11761
+ get: function() {
11762
+ return diffCapActionCensus;
11763
+ }
11764
+ });
10672
11765
  Object.defineProperty(exports, "diffEventPlane", {
10673
11766
  enumerable: true,
10674
11767
  get: function() {
@@ -10711,6 +11804,18 @@ Object.defineProperty(exports, "formatHeapSpaces", {
10711
11804
  return formatHeapSpaces;
10712
11805
  }
10713
11806
  });
11807
+ Object.defineProperty(exports, "formatMeshQueue", {
11808
+ enumerable: true,
11809
+ get: function() {
11810
+ return formatMeshQueue;
11811
+ }
11812
+ });
11813
+ Object.defineProperty(exports, "formatOutboundQueueVerdict", {
11814
+ enumerable: true,
11815
+ get: function() {
11816
+ return formatOutboundQueueVerdict;
11817
+ }
11818
+ });
10714
11819
  Object.defineProperty(exports, "formatSocketPlane", {
10715
11820
  enumerable: true,
10716
11821
  get: function() {
@@ -10855,6 +11960,12 @@ Object.defineProperty(exports, "readMoleculerFanoutMode", {
10855
11960
  return readMoleculerFanoutMode;
10856
11961
  }
10857
11962
  });
11963
+ Object.defineProperty(exports, "readSocketQueues", {
11964
+ enumerable: true,
11965
+ get: function() {
11966
+ return readSocketQueues;
11967
+ }
11968
+ });
10858
11969
  Object.defineProperty(exports, "reclaimIntervalMs", {
10859
11970
  enumerable: true,
10860
11971
  get: function() {