@zlink-systems/framework 0.24.0 → 0.26.0

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 (215) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Channels/IZLinkFanoutClient.d.ts +0 -6
  3. package/dist/contracts/RouteMesh/RuntimeTopology.d.ts +8 -0
  4. package/dist/internal.d.ts +1 -0
  5. package/dist/internal.js +1 -0
  6. package/dist/runtime/activation-admission.d.ts +29 -0
  7. package/dist/runtime/activation-admission.js +61 -0
  8. package/dist/runtime/actors/actor-context.js +6 -6
  9. package/dist/runtime/actors/actor-handoff.d.ts +9 -3
  10. package/dist/runtime/actors/actor-handoff.js +28 -17
  11. package/dist/runtime/actors/actor-local-native-join.js +2 -18
  12. package/dist/runtime/actors/actor-remote-joiner.d.ts +2 -2
  13. package/dist/runtime/actors/actor-remote-joiner.js +6 -8
  14. package/dist/runtime/actors/actor-runtime-contracts.d.ts +4 -2
  15. package/dist/runtime/actors/actor-source-transfer.d.ts +5 -5
  16. package/dist/runtime/actors/deferred-join-completion.d.ts +14 -0
  17. package/dist/runtime/actors/deferred-join-completion.js +20 -0
  18. package/dist/runtime/actors/index.d.ts +1 -1
  19. package/dist/runtime/actors/index.js +15 -5
  20. package/dist/runtime/admission.d.ts +5 -1
  21. package/dist/runtime/admission.js +4 -0
  22. package/dist/runtime/backend/contracts/index.d.ts +2 -10
  23. package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -9
  24. package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +1 -2
  25. package/dist/runtime/backend/node/node-backend-adapter-factory.js +4 -18
  26. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +6 -3
  27. package/dist/runtime/backend/node/node-backend-adapter-support.js +12 -28
  28. package/dist/runtime/backend/node/node-event-loop-poller.d.ts +1 -1
  29. package/dist/runtime/backend/node/node-event-loop-poller.js +8 -2
  30. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +1 -1
  31. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -3
  32. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +12 -9
  33. package/dist/runtime/backend/node/node-raw-mesh-backend.js +61 -29
  34. package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
  35. package/dist/runtime/backend/raw-binding-port.d.ts +12 -0
  36. package/dist/runtime/channels/channel-clients.d.ts +0 -1
  37. package/dist/runtime/channels/channel-clients.js +0 -8
  38. package/dist/runtime/channels/channel-outbound-operations.js +2 -10
  39. package/dist/runtime/channels/channel-runtime-manager.d.ts +2 -5
  40. package/dist/runtime/channels/channel-runtime-manager.js +1 -12
  41. package/dist/runtime/channels/channel-socket-registry.d.ts +17 -4
  42. package/dist/runtime/channels/channel-socket-registry.js +49 -62
  43. package/dist/runtime/channels/channel-transports.d.ts +0 -3
  44. package/dist/runtime/channels/fanout-location-runtime.js +3 -9
  45. package/dist/runtime/execution/index.d.ts +18 -8
  46. package/dist/runtime/execution/index.js +55 -29
  47. package/dist/runtime/execution/serial-execution-queue.d.ts +5 -6
  48. package/dist/runtime/execution/serial-execution-queue.js +49 -15
  49. package/dist/runtime/foundation/listener-records.d.ts +11 -0
  50. package/dist/runtime/foundation/listener-records.js +20 -0
  51. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +10 -21
  52. package/dist/runtime/foundation/raw-service-mesh-runtime.js +66 -392
  53. package/dist/runtime/foundation/service-authority-payload-codec.d.ts +1 -0
  54. package/dist/runtime/foundation/service-authority-payload-codec.js +8 -0
  55. package/dist/runtime/foundation/service-maintenance-runtime.js +23 -2
  56. package/dist/runtime/foundation/service-relocation-coordinator.d.ts +9 -0
  57. package/dist/runtime/foundation/service-relocation-coordinator.js +14 -1
  58. package/dist/runtime/foundation/service-relocation-object-owner.d.ts +8 -0
  59. package/dist/runtime/foundation/service-relocation-object-owner.js +16 -4
  60. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +6 -13
  61. package/dist/runtime/foundation/service-stateful-runtime.d.ts +10 -7
  62. package/dist/runtime/foundation/service-stateful-runtime.js +100 -60
  63. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +9 -4
  64. package/dist/runtime/foundation/service-stateful-wire-codec.js +153 -90
  65. package/dist/runtime/foundation/service-topology-registry.d.ts +1 -2
  66. package/dist/runtime/foundation/service-topology-registry.js +6 -30
  67. package/dist/runtime/host/actor-transfer-runtime.d.ts +17 -15
  68. package/dist/runtime/host/actor-transfer-runtime.js +73 -86
  69. package/dist/runtime/host/index.d.ts +6 -2
  70. package/dist/runtime/host/index.js +63 -37
  71. package/dist/runtime/host/remote-bound-session-relay.js +4 -3
  72. package/dist/runtime/host/route-mesh-runtime.d.ts +2 -0
  73. package/dist/runtime/host/route-mesh-runtime.js +7 -5
  74. package/dist/runtime/host/service-relocation-host-runtime.d.ts +50 -8
  75. package/dist/runtime/host/service-relocation-host-runtime.js +343 -270
  76. package/dist/runtime/host/spot-runtime-options-factory.d.ts +7 -4
  77. package/dist/runtime/host/spot-runtime-options-factory.js +4 -8
  78. package/dist/runtime/host/stateful-authority-route-runtime.js +6 -2
  79. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +11 -3
  80. package/dist/runtime/host/user-spot-creation-coordinator.js +85 -87
  81. package/dist/runtime/locations/in-memory-provider-location-store.js +8 -6
  82. package/dist/runtime/locations/lifecycle.d.ts +1 -1
  83. package/dist/runtime/locations/lifecycle.js +2 -2
  84. package/dist/runtime/locations/resolvers.js +7 -2
  85. package/dist/runtime/locations/spot-location-claims.d.ts +2 -2
  86. package/dist/runtime/locations/spot-location-claims.js +3 -27
  87. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +1 -0
  88. package/dist/runtime/protocol/service_wire_codec.generated.js +5 -1
  89. package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +1 -0
  90. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +3 -2
  91. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +3 -3
  92. package/dist/runtime/spots/formal-remote-actor-admission-registry.js +4 -4
  93. package/dist/runtime/spots/formal-remote-actor-transfer-registry.d.ts +3 -3
  94. package/dist/runtime/spots/index.d.ts +25 -19
  95. package/dist/runtime/spots/index.js +207 -205
  96. package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
  97. package/dist/runtime/spots/spot-activation-registry.js +34 -31
  98. package/dist/runtime/spots/spot-activation-state.d.ts +2 -11
  99. package/dist/runtime/spots/spot-activation-state.js +4 -17
  100. package/dist/runtime/spots/spot-activation.d.ts +8 -6
  101. package/dist/runtime/spots/spot-activation.js +54 -49
  102. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -1
  103. package/dist/runtime/spots/spot-actor-admission-coordinator.js +1 -1
  104. package/dist/runtime/spots/spot-actor-join-dispatch.d.ts +2 -2
  105. package/dist/runtime/spots/spot-actor-join-dispatch.js +3 -5
  106. package/dist/runtime/spots/spot-actor-lifecycle-drain.js +2 -2
  107. package/dist/runtime/spots/spot-actor-membership.d.ts +4 -0
  108. package/dist/runtime/spots/spot-actor-membership.js +26 -12
  109. package/dist/runtime/spots/spot-entry-activation.d.ts +1 -2
  110. package/dist/runtime/spots/spot-entry-activation.js +0 -1
  111. package/dist/runtime/spots/spot-manager-public.js +1 -1
  112. package/dist/runtime/spots/spot-native-actor-join-admission.d.ts +1 -1
  113. package/dist/runtime/spots/spot-native-actor-join-admission.js +2 -2
  114. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +5 -5
  115. package/dist/runtime/spots/spot-node-runtime-manager.js +7 -11
  116. package/dist/runtime/spots/spot-outbound.js +1 -29
  117. package/dist/runtime/spots/spot-routed-actor-admission.d.ts +1 -0
  118. package/dist/runtime/spots/spot-routed-actor-admission.js +10 -0
  119. package/dist/runtime/spots/spot-routed-frame-dispatch.d.ts +2 -6
  120. package/dist/runtime/spots/spot-routed-frame-dispatch.js +2 -70
  121. package/dist/runtime/spots/spot-runtime-ports.d.ts +3 -5
  122. package/dist/runtime/spots/spot-serial-executor.d.ts +5 -0
  123. package/dist/runtime/spots/spot-serial-executor.js +8 -1
  124. package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
  125. package/dist/runtime/spots/spot-serial-turn-executor.js +8 -4
  126. package/dist/runtime/streams/actor-session-binding-registry.d.ts +13 -15
  127. package/dist/runtime/streams/actor-session-binding-registry.js +19 -171
  128. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +3 -4
  129. package/dist/runtime/streams/bound-session-service.js +1 -18
  130. package/dist/runtime/streams/index.d.ts +2 -0
  131. package/dist/runtime/streams/index.js +19 -19
  132. package/dist/runtime/streams/session-serial-executor.js +2 -2
  133. package/package.json +3 -3
  134. package/src/contracts/Channels/IZLinkFanoutClient.ts +0 -7
  135. package/src/contracts/RouteMesh/RuntimeTopology.ts +10 -0
  136. package/src/internal.ts +1 -0
  137. package/src/runtime/activation-admission.ts +78 -0
  138. package/src/runtime/actors/actor-context.ts +6 -6
  139. package/src/runtime/actors/actor-handoff.ts +31 -23
  140. package/src/runtime/actors/actor-local-native-join.ts +3 -42
  141. package/src/runtime/actors/actor-remote-joiner.ts +6 -25
  142. package/src/runtime/actors/actor-runtime-contracts.ts +4 -10
  143. package/src/runtime/actors/actor-source-transfer.ts +5 -18
  144. package/src/runtime/actors/deferred-join-completion.ts +40 -0
  145. package/src/runtime/actors/index.ts +22 -9
  146. package/src/runtime/admission.ts +5 -1
  147. package/src/runtime/backend/contracts/index.ts +2 -13
  148. package/src/runtime/backend/mesh-actor-session-node-adapter.ts +2 -10
  149. package/src/runtime/backend/node/node-backend-adapter-factory.ts +5 -25
  150. package/src/runtime/backend/node/node-backend-adapter-support.ts +9 -29
  151. package/src/runtime/backend/node/node-event-loop-poller.ts +14 -2
  152. package/src/runtime/backend/node/node-raw-binding-port.ts +17 -3
  153. package/src/runtime/backend/node/node-raw-mesh-backend.ts +65 -32
  154. package/src/runtime/backend/node/node-socket-backend-adapter.ts +2 -2
  155. package/src/runtime/backend/raw-binding-port.ts +13 -0
  156. package/src/runtime/channels/channel-clients.ts +0 -11
  157. package/src/runtime/channels/channel-outbound-operations.ts +3 -11
  158. package/src/runtime/channels/channel-runtime-manager.ts +4 -15
  159. package/src/runtime/channels/channel-socket-registry.ts +69 -71
  160. package/src/runtime/channels/channel-transports.ts +0 -3
  161. package/src/runtime/channels/fanout-location-runtime.ts +4 -16
  162. package/src/runtime/execution/index.ts +68 -30
  163. package/src/runtime/execution/serial-execution-queue.ts +52 -20
  164. package/src/runtime/foundation/listener-records.ts +21 -0
  165. package/src/runtime/foundation/raw-service-mesh-runtime.ts +66 -466
  166. package/src/runtime/foundation/service-authority-payload-codec.ts +10 -0
  167. package/src/runtime/foundation/service-maintenance-runtime.ts +30 -9
  168. package/src/runtime/foundation/service-relocation-coordinator.ts +13 -0
  169. package/src/runtime/foundation/service-relocation-object-owner.ts +18 -5
  170. package/src/runtime/foundation/service-session-binding-ingress-port.ts +6 -14
  171. package/src/runtime/foundation/service-stateful-runtime.ts +151 -89
  172. package/src/runtime/foundation/service-stateful-wire-codec.ts +203 -121
  173. package/src/runtime/foundation/service-topology-registry.ts +8 -48
  174. package/src/runtime/host/actor-transfer-runtime.ts +94 -166
  175. package/src/runtime/host/index.ts +119 -54
  176. package/src/runtime/host/remote-bound-session-relay.ts +4 -3
  177. package/src/runtime/host/route-mesh-runtime.ts +9 -5
  178. package/src/runtime/host/service-relocation-host-runtime.ts +495 -376
  179. package/src/runtime/host/spot-runtime-options-factory.ts +24 -13
  180. package/src/runtime/host/stateful-authority-route-runtime.ts +8 -4
  181. package/src/runtime/host/user-spot-creation-coordinator.ts +147 -142
  182. package/src/runtime/locations/in-memory-provider-location-store.ts +11 -7
  183. package/src/runtime/locations/lifecycle.ts +3 -2
  184. package/src/runtime/locations/resolvers.ts +19 -13
  185. package/src/runtime/locations/spot-location-claims.ts +5 -34
  186. package/src/runtime/protocol/service_wire_codec.generated.ts +9 -2
  187. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +4 -4
  188. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +6 -6
  189. package/src/runtime/spots/formal-remote-actor-transfer-registry.ts +3 -3
  190. package/src/runtime/spots/index.ts +341 -295
  191. package/src/runtime/spots/spot-activation-registry.ts +41 -31
  192. package/src/runtime/spots/spot-activation-state.ts +4 -16
  193. package/src/runtime/spots/spot-activation.ts +102 -80
  194. package/src/runtime/spots/spot-actor-admission-coordinator.ts +2 -2
  195. package/src/runtime/spots/spot-actor-join-dispatch.ts +6 -7
  196. package/src/runtime/spots/spot-actor-lifecycle-drain.ts +20 -18
  197. package/src/runtime/spots/spot-actor-membership.ts +81 -17
  198. package/src/runtime/spots/spot-entry-activation.ts +0 -3
  199. package/src/runtime/spots/spot-manager-public.ts +8 -4
  200. package/src/runtime/spots/spot-native-actor-join-admission.ts +2 -2
  201. package/src/runtime/spots/spot-node-runtime-manager.ts +12 -18
  202. package/src/runtime/spots/spot-outbound.ts +4 -47
  203. package/src/runtime/spots/spot-routed-actor-admission.ts +20 -0
  204. package/src/runtime/spots/spot-routed-frame-dispatch.ts +6 -76
  205. package/src/runtime/spots/spot-runtime-ports.ts +6 -17
  206. package/src/runtime/spots/spot-serial-executor.ts +9 -1
  207. package/src/runtime/spots/spot-serial-turn-executor.ts +10 -4
  208. package/src/runtime/streams/actor-session-binding-registry.ts +20 -308
  209. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +2 -12
  210. package/src/runtime/streams/bound-session-service.ts +2 -36
  211. package/src/runtime/streams/index.ts +29 -27
  212. package/src/runtime/streams/session-serial-executor.ts +2 -2
  213. package/dist/runtime/actors/deferred-join-accepted-journal.d.ts +0 -47
  214. package/dist/runtime/actors/deferred-join-accepted-journal.js +0 -992
  215. package/src/runtime/actors/deferred-join-accepted-journal.ts +0 -1346
@@ -1,9 +1,6 @@
1
- import { randomUUID } from 'node:crypto';
2
1
  import type {
3
2
  ZLinkRawBindingPort,
4
3
  ZLinkRawHostPort,
5
- ZLinkRawMonitorRecord,
6
- ZLinkRawMonitorPort,
7
4
  ZLinkRawReceivedRecord,
8
5
  ZLinkRawRouterPort
9
6
  } from '../backend/raw-binding-port';
@@ -119,21 +116,9 @@ export interface RawServiceMeshRuntimeOptions {
119
116
  }) => void;
120
117
  }
121
118
 
122
- const MONITOR_DISCONNECTED = 0x0200;
123
- const MONITOR_CONNECTION_READY = 0x1000;
124
- const MONITOR_CONNECTION_READY_EDGE = 1;
125
119
  // Framework error code 13 (RequestTargetNotFound) is encoded as 14 on a
126
120
  // RequestResult.NotFound reply. Boundary transport results keep failureCode 0.
127
121
  const REQUEST_TARGET_NOT_FOUND_FAILURE_CODE = 14;
128
- type PhysicalConnectionDirection = 'inbound' | 'outbound' | 'unknown';
129
-
130
- interface PhysicalConnectionCandidate {
131
- readonly connectionId: string;
132
- readonly direction: PhysicalConnectionDirection;
133
- readonly discriminator: string;
134
- readonly localAddress: string;
135
- readonly remoteAddress: string;
136
- }
137
122
 
138
123
  const livenessCodec = createServiceWireCodec({
139
124
  magic: [0x5a, 0x4d],
@@ -141,9 +126,6 @@ const livenessCodec = createServiceWireCodec({
141
126
  commands: M6aServiceWireCommand
142
127
  });
143
128
 
144
- const MAX_PENDING_MONITOR_EVENTS = 8192;
145
- const MAX_UNRESOLVED_CONNECTION_CANDIDATES = 4096;
146
-
147
129
  /**
148
130
  * RouteMesh M6A runtime built only on the public raw binding package.
149
131
  * It owns protocol, admission, mailbox, request and liveness state.
@@ -166,20 +148,11 @@ export class RawServiceMeshRuntime {
166
148
  >();
167
149
  private readonly endpointOnlyPeers = new Set<string>();
168
150
  private readonly peerConnectionIntentRemoved = new Set<(nodeRoutingId: string) => void>();
169
- private readonly connectionCandidates = new Map<
170
- string,
171
- Map<string, PhysicalConnectionCandidate>
172
- >();
173
- private readonly unresolvedConnectionCandidates: PhysicalConnectionCandidate[] = [];
174
- private readonly connectionIds = new Map<string, string>();
175
151
  /**
176
- * The monitor drain updates this fence before the event reaches the
177
- * normal runtime handler. `null` means that the last observed pair is no
178
- * longer usable by the application route.
152
+ * The last observed Core selected route of each RID (Core ROUTER §10.1):
153
+ * RID -> route generation. Only `observeSelectedRoutes` replaces it.
179
154
  */
180
- private readonly monitorConnectionStates = new Map<string, string | null>();
181
- private monitorEvents: ZLinkRawMonitorRecord[] = [];
182
- private monitorDrainBuffer: ZLinkRawMonitorRecord[] = [];
155
+ private selectedRoutes = new Map<string, bigint>();
183
156
  private readonly bindingPort: ZLinkRawBindingPort;
184
157
  private readonly applicationJobQueue: ApplicationJobQueuePort;
185
158
  private readonly peerAdmissionSealed?: () => boolean;
@@ -192,7 +165,6 @@ export class RawServiceMeshRuntime {
192
165
  private descriptor: ServiceNodeDescriptor;
193
166
  private host?: ZLinkRawHostPort;
194
167
  private router?: ZLinkRawRouterPort;
195
- private monitor?: ZLinkRawMonitorPort;
196
168
  private nextCorrelation = 1n;
197
169
  private serviceIngress?: RawServiceIngressHandler;
198
170
  private closed = false;
@@ -239,8 +211,6 @@ export class RawServiceMeshRuntime {
239
211
  };
240
212
  this.topology.publishLocal(next);
241
213
  this.descriptor = next;
242
- this.monitor = router.monitor();
243
- this.pullMonitorEvents();
244
214
  this.host = host;
245
215
  this.router = router;
246
216
  } catch (error) {
@@ -387,27 +357,25 @@ export class RawServiceMeshRuntime {
387
357
  }
388
358
 
389
359
  isPeerRouteReady(nodeRoutingId: string, lifecycleGeneration?: bigint): boolean {
360
+ // Admission is bound to the observed selected route: the route observer
361
+ // removes the peer when that route ends or is replaced.
390
362
  const peer = this.topology.peer(nodeRoutingId);
391
- const monitorConnection = this.monitorConnectionStates.get(nodeRoutingId);
392
- const applicationRouteReady =
393
- monitorConnection === undefined || monitorConnection === peer?.connectionId;
394
- const ready =
363
+ return (
395
364
  peer !== undefined &&
396
365
  (lifecycleGeneration === undefined ||
397
366
  peer.descriptor.lifecycleGeneration === lifecycleGeneration) &&
398
- applicationRouteReady &&
399
- this.liveness.isReady(nodeRoutingId, peer.connectionId);
400
- return ready;
367
+ this.liveness.isReady(nodeRoutingId, peer.connectionId)
368
+ );
401
369
  }
402
370
 
403
371
  async announceExpectedPeers(): Promise<number> {
404
372
  let accepted = 0;
405
373
  for (const nodeRoutingId of this.expectedPeers.keys()) {
406
- // Admission is a one-time fence for the current physical connection.
374
+ // Admission is a one-time fence for the current selected route.
407
375
  // Re-sending Hello after the peer is admitted would re-run admission on
408
376
  // every poll and reset the liveness record before application traffic
409
- // can use the route. A disconnected peer is removed by monitor handling
410
- // and remains eligible for the next admission attempt.
377
+ // can use the route. A peer whose selected route ended is removed by the
378
+ // route observer and remains eligible for the next admission attempt.
411
379
  if (this.topology.peer(nodeRoutingId) !== undefined) continue;
412
380
  if (await this.announcePeer(nodeRoutingId)) accepted++;
413
381
  }
@@ -629,7 +597,6 @@ export class RawServiceMeshRuntime {
629
597
  nowMs = performance.now(),
630
598
  observe?: RawServicePumpObserver
631
599
  ): Promise<RawServicePumpResult> {
632
- this.pullMonitorEvents();
633
600
  return this.receiveOne(nowMs, observe);
634
601
  }
635
602
 
@@ -639,7 +606,7 @@ export class RawServiceMeshRuntime {
639
606
 
640
607
  /** Returns whether a receive budget ended before no-data; idle ticks only maintain peers. */
641
608
  async pumpBatch(receiveReady = true): Promise<boolean> {
642
- await this.drainMonitorEvents();
609
+ await this.observeSelectedRoutes();
643
610
  const startedAtMs = performance.now();
644
611
  let messages = 0;
645
612
  let bytes = 0;
@@ -711,24 +678,6 @@ export class RawServiceMeshRuntime {
711
678
  }
712
679
  }
713
680
 
714
- private pullMonitorEvents(): void {
715
- this.monitor?.drain((event) => {
716
- this.observeMonitorEvent(event);
717
- if (this.monitorEvents.length < MAX_PENDING_MONITOR_EVENTS) {
718
- this.monitorEvents.push(event);
719
- return;
720
- }
721
- const existingIndex = this.monitorEvents.findIndex((candidate) =>
722
- sameMonitorConnection(candidate, event)
723
- );
724
- if (existingIndex >= 0) this.monitorEvents[existingIndex] = event;
725
- else {
726
- this.monitorEvents.shift();
727
- this.monitorEvents.push(event);
728
- }
729
- });
730
- }
731
-
732
681
  private reportProtocolError(
733
682
  received: import('../backend/node/node-raw-binding-port').ZLinkRawReceivedRecord
734
683
  ): void {
@@ -812,11 +761,12 @@ export class RawServiceMeshRuntime {
812
761
  await this.send(received.sourceRid, [encodeReject(3)]);
813
762
  return 'infrastructure';
814
763
  }
815
- const connection = this.currentConnectionCandidate(
816
- received.sourceRid,
817
- descriptor.advertisedEndpoint
764
+ const result = this.admitPeer(
765
+ descriptor,
766
+ routeConnectionId(received.routeGeneration),
767
+ nowMs,
768
+ expected
818
769
  );
819
- const result = this.admitPeer(descriptor, connection, nowMs, expected);
820
770
  if (result !== 'admitted' && result !== 'notRequired') {
821
771
  const detail =
822
772
  result === 'invalidDescriptor'
@@ -849,10 +799,8 @@ export class RawServiceMeshRuntime {
849
799
  if (header.command === M6aServiceWireCommand.reject) {
850
800
  if (received.parts.length !== 1) return 'protocolError';
851
801
  const reason = decodeReject(received.parts[0]!);
852
- // A reject belongs to one physical candidate, but the wire record does
853
- // not carry that candidate's connection id. Keep an already admitted
854
- // peer: bilateral manual connect can reject the duplicate after the
855
- // other candidate has become Ready.
802
+ // Keep an already admitted peer: a reject of an earlier Hello on the
803
+ // same selected route does not end the admission that succeeded.
856
804
  if (reason === 4 && this.topology.peer(received.sourceRid) === undefined) {
857
805
  const expected = this.expectedPeers.get(received.sourceRid);
858
806
  const local = this.topology.localDescriptor();
@@ -1011,125 +959,44 @@ export class RawServiceMeshRuntime {
1011
959
  for (const nodeRoutingId of result.timedOutNodes) {
1012
960
  const peer = this.topology.peer(nodeRoutingId);
1013
961
  if (peer === undefined) continue;
1014
- // Liveness timeout is a semantic peer removal. Route topology,
1015
- // physical candidate state, and auto-connect intent must be retired by
1016
- // the same owner so a failed endpoint cannot be selected again.
1017
- this.removeConnectionCandidate(nodeRoutingId, peer.connectionId);
962
+ // Liveness timeout is a semantic peer removal of the admitted route.
1018
963
  this.removePeer(peer);
1019
964
  }
1020
965
  return result;
1021
966
  }
1022
967
 
1023
- async drainMonitorEvents(nowMs = performance.now()): Promise<number> {
1024
- this.pullMonitorEvents();
1025
- let handled = 0;
1026
- // Detach the current batch so monitor pulls that run while an event is
1027
- // being handled append to the next batch without copying or reindexing it.
1028
- const events = this.monitorEvents;
1029
- this.monitorEvents = this.monitorDrainBuffer;
1030
- this.monitorDrainBuffer = events;
1031
- this.monitorEvents.length = 0;
1032
- for (const event of events) {
1033
- handled++;
1034
- const nodeRoutingId = event.routingId ?? this.expectedPeerRoutingId(event.remoteAddress);
1035
- if (event.event === MONITOR_CONNECTION_READY && isConnectionReadyEdge(event)) {
1036
- if (nodeRoutingId === undefined) {
1037
- const candidate = this.createUnresolvedConnectionCandidate(event);
1038
- const alreadyQueued = this.unresolvedConnectionCandidates.some(
1039
- (value) => value.connectionId === candidate.connectionId
1040
- );
1041
- if (
1042
- !alreadyQueued &&
1043
- this.unresolvedConnectionCandidates.length < MAX_UNRESOLVED_CONNECTION_CANDIDATES
1044
- ) {
1045
- this.unresolvedConnectionCandidates.push(candidate);
1046
- }
1047
- continue;
1048
- }
1049
- if (!this.acceptsExpectedMonitorEndpoint(nodeRoutingId, event)) {
1050
- continue;
1051
- }
1052
- const candidate = this.createConnectionCandidate(nodeRoutingId, event);
1053
- const existingPeer = this.topology.peer(nodeRoutingId);
1054
- if (existingPeer !== undefined && existingPeer.connectionId === candidate.connectionId) {
1055
- this.monitorConnectionStates.set(nodeRoutingId, candidate.connectionId);
1056
- continue;
1057
- }
1058
- if (existingPeer !== undefined) {
1059
- if (this.liveness.isReady(nodeRoutingId, existingPeer.connectionId)) {
1060
- const existingCandidate = this.connectionCandidates
1061
- .get(nodeRoutingId)
1062
- ?.get(existingPeer.connectionId);
1063
- if (this.isDiscoveredLifecycleReplacement(existingPeer, nodeRoutingId)) {
1064
- // A current discovery descriptor with the same RID but a
1065
- // different lifecycle token denotes a replacement process.
1066
- // Keep its physical pair provisional until its Hello passes
1067
- // the descriptor fence; the old ready peer remains semantic
1068
- // current until then.
1069
- let provisionalCandidates = this.connectionCandidates.get(nodeRoutingId);
1070
- if (provisionalCandidates === undefined) {
1071
- provisionalCandidates = new Map();
1072
- this.connectionCandidates.set(nodeRoutingId, provisionalCandidates);
1073
- }
1074
- provisionalCandidates.set(candidate.connectionId, candidate);
1075
- await this.announcePeer(nodeRoutingId);
1076
- continue;
1077
- }
1078
- if (existingCandidate?.connectionId === candidate.connectionId) {
1079
- this.removeConnectionCandidate(nodeRoutingId, candidate.connectionId);
1080
- this.monitorConnectionStates.set(nodeRoutingId, existingPeer.connectionId);
1081
- continue;
1082
- }
1083
- // A non-replacement ready route is the current semantic owner.
1084
- // A second physical candidate may be admitted only after the
1085
- // current lease expires; this also prevents a late old-process
1086
- // reconnect from replacing the ready replacement by arrival order.
1087
- this.removeConnectionCandidate(nodeRoutingId, candidate.connectionId);
1088
- continue;
1089
- }
1090
- // Keep the physical candidate until the wire descriptor decides
1091
- // whether it is the current logical peer. READY events can race
1092
- // with DISCONNECTED events during reconnect; rejecting the READY
1093
- // here can terminate the replacement before admission evaluates its
1094
- // lifecycle generation.
1095
- let provisionalCandidates = this.connectionCandidates.get(nodeRoutingId);
1096
- if (provisionalCandidates === undefined) {
1097
- provisionalCandidates = new Map();
1098
- this.connectionCandidates.set(nodeRoutingId, provisionalCandidates);
1099
- }
1100
- provisionalCandidates.set(candidate.connectionId, candidate);
1101
- this.connectionIds.set(nodeRoutingId, candidate.connectionId);
1102
- await this.announcePeer(nodeRoutingId);
1103
- continue;
1104
- }
1105
- let candidates = this.connectionCandidates.get(nodeRoutingId);
1106
- if (candidates === undefined) {
1107
- candidates = new Map();
1108
- this.connectionCandidates.set(nodeRoutingId, candidates);
1109
- }
1110
- candidates.set(candidate.connectionId, candidate);
1111
- // Before wire admission the monitor candidate is the only physical
1112
- // route evidence available to the admission message.
1113
- this.connectionIds.set(nodeRoutingId, candidate.connectionId);
1114
- await this.announcePeer(nodeRoutingId);
1115
- } else if (event.event === MONITOR_DISCONNECTED && nodeRoutingId !== undefined) {
1116
- const peer = this.topology.peer(nodeRoutingId);
1117
- const disconnectedId = monitorConnectionId(event, nodeRoutingId);
1118
- this.removeConnectionCandidate(nodeRoutingId, disconnectedId);
1119
- if (peer !== undefined && peer.connectionId === disconnectedId) {
1120
- this.removePeer(peer);
1121
- }
1122
- } else if (event.event === MONITOR_DISCONNECTED) {
1123
- const disconnectedId = monitorConnectionId(event);
1124
- const index = this.unresolvedConnectionCandidates.findIndex(
1125
- (candidate) => candidate.connectionId === disconnectedId
1126
- );
1127
- if (index >= 0) this.unresolvedConnectionCandidates.splice(index, 1);
968
+ /**
969
+ * The socket's one route observer (Core ROUTER §10.1). It replaces the
970
+ * observation with Core's selected-route snapshot, ends the admission of
971
+ * every RID whose observed route disappeared or was replaced, and starts the
972
+ * handshake of every newly selected route. Returns the number of changes.
973
+ */
974
+ async observeSelectedRoutes(): Promise<number> {
975
+ const router = this.router;
976
+ if (router === undefined || this.closed) return 0;
977
+ const observed = new Map<string, bigint>();
978
+ for (const route of router.routesSnapshot()) {
979
+ observed.set(route.routingId, route.routeGeneration);
980
+ }
981
+ let changes = 0;
982
+ for (const [nodeRoutingId, generation] of this.selectedRoutes) {
983
+ if (observed.get(nodeRoutingId) === generation) continue;
984
+ changes++;
985
+ const peer = this.topology.peer(nodeRoutingId);
986
+ if (peer !== undefined && peer.connectionId === routeConnectionId(generation)) {
987
+ this.removePeer(peer);
1128
988
  }
1129
989
  }
1130
- this.monitorDrainBuffer.length = 0;
1131
- void nowMs;
1132
- return handled;
990
+ const selected: string[] = [];
991
+ for (const [nodeRoutingId, generation] of observed) {
992
+ if (this.selectedRoutes.get(nodeRoutingId) === generation) continue;
993
+ if (!this.selectedRoutes.has(nodeRoutingId)) changes++;
994
+ selected.push(nodeRoutingId);
995
+ }
996
+ this.selectedRoutes = observed;
997
+ // A new selected route admits only through a new handshake.
998
+ for (const nodeRoutingId of selected) await this.announcePeer(nodeRoutingId);
999
+ return changes;
1133
1000
  }
1134
1001
 
1135
1002
  close(): void {
@@ -1145,8 +1012,6 @@ export class RawServiceMeshRuntime {
1145
1012
  if (router !== undefined) {
1146
1013
  this.applicationJobQueue.unregisterReceiveFlowTarget?.(router);
1147
1014
  }
1148
- this.monitor?.close();
1149
- this.monitor = undefined;
1150
1015
  const host = this.host;
1151
1016
  this.router = undefined;
1152
1017
  this.host = undefined;
@@ -1241,17 +1106,13 @@ export class RawServiceMeshRuntime {
1241
1106
  })().catch((error) => this.operations.fail(pending.id, error));
1242
1107
  return pending;
1243
1108
  }
1244
- if (!this.isPeerRouteReady(selectedTargetNodeRoutingId)) {
1245
- // Preserve Core's distinct target-not-found result for an RID the
1246
- // topology has never observed. A known peer without a usable route is
1247
- // a Framework NotConnected result; an unknown RID is completed locally
1248
- // as RequestTargetNotFound without submitting an application frame.
1249
- const knownTarget =
1250
- this.topology.peer(selectedTargetNodeRoutingId) !== undefined ||
1251
- this.topology.knownDescriptor(selectedTargetNodeRoutingId) !== undefined;
1109
+ if (this.topology.peer(selectedTargetNodeRoutingId) === undefined) {
1110
+ // Logical target selection is the requester's decision: an RID that is
1111
+ // not an admitted peer is not a target. Whether an admitted target's
1112
+ // route can carry the request is Core's REQUEST result.
1252
1113
  this.operations.complete(pending.id, {
1253
- terminalResult: knownTarget ? RequestResult.NotConnected : RequestResult.NotFound,
1254
- failureCode: knownTarget ? 0 : REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
1114
+ terminalResult: RequestResult.NotFound,
1115
+ failureCode: REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
1255
1116
  });
1256
1117
  return pending;
1257
1118
  }
@@ -1320,7 +1181,7 @@ export class RawServiceMeshRuntime {
1320
1181
 
1321
1182
  private admitPeer(
1322
1183
  descriptor: ServiceNodeDescriptor,
1323
- connection: PhysicalConnectionCandidate,
1184
+ connectionId: string,
1324
1185
  nowMs: number,
1325
1186
  expected?: {
1326
1187
  readonly endpoint?: string;
@@ -1340,232 +1201,16 @@ export class RawServiceMeshRuntime {
1340
1201
  `RouteMesh peer '${descriptor.nodeRoutingId}' descriptor revision is stale or conflicting.`
1341
1202
  );
1342
1203
  }
1343
- const result = this.topology.admit(
1344
- descriptor,
1345
- connection.connectionId,
1346
- expected,
1347
- connection.discriminator
1348
- );
1204
+ const result = this.topology.admit(descriptor, connectionId, expected);
1349
1205
  if (result === 'admitted') {
1350
- this.connectionIds.set(descriptor.nodeRoutingId, connection.connectionId);
1351
- this.liveness.admit(descriptor.nodeRoutingId, connection.connectionId, nowMs);
1352
- this.liveness.requestProbe(descriptor.nodeRoutingId, connection.connectionId, nowMs);
1353
- this.monitorConnectionStates.set(descriptor.nodeRoutingId, connection.connectionId);
1354
- if (previous !== undefined && previous.connectionId !== connection.connectionId) {
1355
- const previousCandidate = this.connectionCandidates
1356
- .get(descriptor.nodeRoutingId)
1357
- ?.get(previous.connectionId);
1358
- if (previousCandidate !== undefined) {
1359
- this.removeConnectionCandidate(descriptor.nodeRoutingId, previousCandidate.connectionId);
1360
- }
1361
- }
1206
+ this.liveness.admit(descriptor.nodeRoutingId, connectionId, nowMs);
1207
+ this.liveness.requestProbe(descriptor.nodeRoutingId, connectionId, nowMs);
1362
1208
  } else if (result === 'notRequired' && previous !== undefined) {
1363
1209
  this.liveness.disconnect(descriptor.nodeRoutingId, previous.connectionId);
1364
- } else if (previous !== undefined) {
1365
- this.connectionIds.set(descriptor.nodeRoutingId, previous.connectionId);
1366
- if (connection.connectionId !== previous.connectionId) {
1367
- this.removeConnectionCandidate(descriptor.nodeRoutingId, connection.connectionId);
1368
- }
1369
1210
  }
1370
1211
  return result;
1371
1212
  }
1372
1213
 
1373
- private currentConnectionCandidate(
1374
- nodeRoutingId: string,
1375
- advertisedEndpoint: string
1376
- ): PhysicalConnectionCandidate {
1377
- let connectionId = this.connectionIds.get(nodeRoutingId);
1378
- let candidate =
1379
- connectionId === undefined
1380
- ? undefined
1381
- : this.connectionCandidates.get(nodeRoutingId)?.get(connectionId);
1382
- if (candidate === undefined) {
1383
- candidate = this.promoteUnresolvedConnectionCandidate(nodeRoutingId, advertisedEndpoint);
1384
- connectionId = candidate?.connectionId;
1385
- }
1386
- if (candidate === undefined) {
1387
- const localRid = this.topology.localDescriptor().nodeRoutingId;
1388
- const endpointOnly = this.endpointOnlyPeers.has(advertisedEndpoint);
1389
- const direction: PhysicalConnectionDirection = this.expectedPeers.has(nodeRoutingId)
1390
- ? 'outbound'
1391
- : endpointOnly
1392
- ? localRid.localeCompare(nodeRoutingId) <= 0
1393
- ? 'outbound'
1394
- : 'inbound'
1395
- : 'inbound';
1396
- const initiator =
1397
- endpointOnly && localRid.localeCompare(nodeRoutingId) > 0
1398
- ? nodeRoutingId
1399
- : direction === 'outbound'
1400
- ? localRid
1401
- : nodeRoutingId;
1402
- connectionId = `unmonitored:${direction}:${randomUUID()}`;
1403
- candidate = {
1404
- connectionId,
1405
- direction,
1406
- discriminator: `initiator:${initiator}`,
1407
- localAddress: '',
1408
- remoteAddress: advertisedEndpoint
1409
- };
1410
- let candidates = this.connectionCandidates.get(nodeRoutingId);
1411
- if (candidates === undefined) {
1412
- candidates = new Map();
1413
- this.connectionCandidates.set(nodeRoutingId, candidates);
1414
- }
1415
- candidates.set(connectionId, candidate);
1416
- this.connectionIds.set(nodeRoutingId, connectionId);
1417
- }
1418
- return candidate;
1419
- }
1420
-
1421
- private createConnectionCandidate(
1422
- nodeRoutingId: string,
1423
- event: ZLinkRawMonitorRecord
1424
- ): PhysicalConnectionCandidate {
1425
- const local = this.topology.localDescriptor();
1426
- const expected = this.expectedPeers.get(nodeRoutingId);
1427
- const direction: PhysicalConnectionDirection =
1428
- event.localAddress === local.advertisedEndpoint
1429
- ? 'inbound'
1430
- : expected?.endpoint === event.remoteAddress
1431
- ? 'outbound'
1432
- : this.endpointOnlyPeers.has(event.remoteAddress)
1433
- ? 'outbound'
1434
- : 'unknown';
1435
- const initiator =
1436
- direction === 'outbound'
1437
- ? local.nodeRoutingId
1438
- : direction === 'inbound'
1439
- ? nodeRoutingId
1440
- : undefined;
1441
- return {
1442
- connectionId: monitorConnectionId(event, nodeRoutingId),
1443
- direction,
1444
- discriminator:
1445
- initiator === undefined
1446
- ? `unknown:${monitorConnectionId(event, nodeRoutingId)}`
1447
- : `initiator:${initiator}`,
1448
- localAddress: event.localAddress,
1449
- remoteAddress: event.remoteAddress
1450
- };
1451
- }
1452
-
1453
- private createUnresolvedConnectionCandidate(
1454
- event: ZLinkRawMonitorRecord
1455
- ): PhysicalConnectionCandidate {
1456
- const connectionId = monitorConnectionId(event);
1457
- const local = this.topology.localDescriptor();
1458
- const direction: PhysicalConnectionDirection =
1459
- this.endpointOnlyPeers.has(event.remoteAddress) ||
1460
- this.expectedPeerRoutingId(event.remoteAddress) !== undefined
1461
- ? 'outbound'
1462
- : event.localAddress === local.advertisedEndpoint
1463
- ? 'inbound'
1464
- : 'unknown';
1465
- return {
1466
- connectionId,
1467
- direction,
1468
- discriminator: `unresolved:${connectionId}`,
1469
- localAddress: event.localAddress,
1470
- remoteAddress: event.remoteAddress
1471
- };
1472
- }
1473
-
1474
- private promoteUnresolvedConnectionCandidate(
1475
- nodeRoutingId: string,
1476
- advertisedEndpoint: string
1477
- ): PhysicalConnectionCandidate | undefined {
1478
- const relevant = this.unresolvedConnectionCandidates
1479
- .map((candidate, index) => ({ candidate, index }))
1480
- .filter(
1481
- ({ candidate }) =>
1482
- candidate.remoteAddress === advertisedEndpoint || candidate.direction === 'inbound'
1483
- );
1484
- if (relevant.length === 0) return undefined;
1485
- const directions = new Set(relevant.map((value) => value.candidate.direction));
1486
- const localRid = this.topology.localDescriptor().nodeRoutingId;
1487
- const preferredDirection: PhysicalConnectionDirection =
1488
- directions.has('inbound') && directions.has('outbound')
1489
- ? localRid.localeCompare(nodeRoutingId) <= 0
1490
- ? 'outbound'
1491
- : 'inbound'
1492
- : relevant.at(-1)!.candidate.direction;
1493
- const selected =
1494
- [...relevant].reverse().find((value) => value.candidate.direction === preferredDirection) ??
1495
- relevant.at(-1)!;
1496
- this.unresolvedConnectionCandidates.splice(selected.index, 1);
1497
- const initiator =
1498
- selected.candidate.direction === 'outbound'
1499
- ? localRid
1500
- : selected.candidate.direction === 'inbound'
1501
- ? nodeRoutingId
1502
- : undefined;
1503
- const candidate: PhysicalConnectionCandidate = {
1504
- ...selected.candidate,
1505
- discriminator:
1506
- initiator === undefined
1507
- ? `unknown:${selected.candidate.connectionId}`
1508
- : `initiator:${initiator}`
1509
- };
1510
- let candidates = this.connectionCandidates.get(nodeRoutingId);
1511
- if (candidates === undefined) {
1512
- candidates = new Map();
1513
- this.connectionCandidates.set(nodeRoutingId, candidates);
1514
- }
1515
- candidates.set(candidate.connectionId, candidate);
1516
- this.connectionIds.set(nodeRoutingId, candidate.connectionId);
1517
- return candidate;
1518
- }
1519
-
1520
- private removeConnectionCandidate(nodeRoutingId: string, connectionId: string): void {
1521
- const candidates = this.connectionCandidates.get(nodeRoutingId);
1522
- candidates?.delete(connectionId);
1523
- if (candidates?.size === 0) this.connectionCandidates.delete(nodeRoutingId);
1524
- if (this.connectionIds.get(nodeRoutingId) === connectionId) {
1525
- this.connectionIds.delete(nodeRoutingId);
1526
- }
1527
- }
1528
-
1529
- private isDiscoveredLifecycleReplacement(
1530
- admitted: AdmittedServicePeer,
1531
- nodeRoutingId: string
1532
- ): boolean {
1533
- const expected = this.expectedPeers.get(nodeRoutingId);
1534
- // A same-RID replacement is permitted only from a complete discovery
1535
- // fence. Manual/endpoint-only intent has no authority to replace a ready
1536
- // peer, and lifecycle generations are opaque equality tokens.
1537
- return (
1538
- expected?.endpoint !== undefined &&
1539
- expected.securityIdentity !== undefined &&
1540
- expected.lifecycleGeneration !== undefined &&
1541
- admitted.descriptor.lifecycleGeneration !== expected.lifecycleGeneration
1542
- );
1543
- }
1544
-
1545
- private expectedPeerRoutingId(endpoint: string): string | undefined {
1546
- for (const [nodeRoutingId, expected] of this.expectedPeers) {
1547
- if (expected.endpoint === endpoint) return nodeRoutingId;
1548
- }
1549
- return undefined;
1550
- }
1551
-
1552
- private acceptsExpectedMonitorEndpoint(
1553
- nodeRoutingId: string,
1554
- event: Pick<ZLinkRawMonitorRecord, 'remoteAddress' | 'routingId'>
1555
- ): boolean {
1556
- // A monitor address is the physical socket address, which may be an
1557
- // ephemeral port. Once Core has resolved the peer routing ID, that ID is
1558
- // the semantic identity; comparing the physical address with the
1559
- // discovery advertised endpoint would reject valid READY events.
1560
- if (event.routingId === nodeRoutingId) return true;
1561
- const expected = this.expectedPeers.get(nodeRoutingId);
1562
- return (
1563
- expected === undefined ||
1564
- expected.endpoint === undefined ||
1565
- expected.endpoint === event.remoteAddress
1566
- );
1567
- }
1568
-
1569
1214
  private retireNotRequiredExpectedPeer(nodeRoutingId: string, advertisedEndpoint?: string): void {
1570
1215
  const expected = this.expectedPeers.get(nodeRoutingId);
1571
1216
  const endpoint =
@@ -1597,9 +1242,6 @@ export class RawServiceMeshRuntime {
1597
1242
  }
1598
1243
 
1599
1244
  private removePeer(peer: AdmittedServicePeer): void {
1600
- if (this.monitorConnectionStates.get(peer.descriptor.nodeRoutingId) === peer.connectionId) {
1601
- this.monitorConnectionStates.set(peer.descriptor.nodeRoutingId, null);
1602
- }
1603
1245
  this.topology.disconnect(peer.descriptor.nodeRoutingId, peer.connectionId);
1604
1246
  this.liveness.disconnect(peer.descriptor.nodeRoutingId, peer.connectionId);
1605
1247
  this.onPeerDisconnected?.(
@@ -1609,25 +1251,6 @@ export class RawServiceMeshRuntime {
1609
1251
  );
1610
1252
  }
1611
1253
 
1612
- private observeMonitorEvent(event: ZLinkRawMonitorRecord): void {
1613
- const nodeRoutingId = event.routingId ?? this.expectedPeerRoutingId(event.remoteAddress);
1614
- if (nodeRoutingId === undefined) return;
1615
- const connectionId = monitorConnectionId(event, nodeRoutingId);
1616
- if (event.event === MONITOR_CONNECTION_READY && isConnectionReadyEdge(event)) {
1617
- if (!this.acceptsExpectedMonitorEndpoint(nodeRoutingId, event)) return;
1618
- // The event is queued for admission. Updating monitor state here would
1619
- // allow a late stale READY to overwrite the admitted replacement.
1620
- return;
1621
- }
1622
- if (
1623
- event.event === MONITOR_DISCONNECTED &&
1624
- this.monitorConnectionStates.get(nodeRoutingId) === connectionId &&
1625
- this.topology.peer(nodeRoutingId)?.connectionId === connectionId
1626
- ) {
1627
- this.monitorConnectionStates.set(nodeRoutingId, null);
1628
- }
1629
- }
1630
-
1631
1254
  private requireStarted(): ZLinkRawRouterPort {
1632
1255
  if (this.router === undefined) throw new Error('Raw service runtime is not started.');
1633
1256
  return this.router;
@@ -1680,35 +1303,12 @@ function isAlreadyDisconnectedError(error: unknown): boolean {
1680
1303
  return (error as { readonly nativeErrno?: unknown }).nativeErrno === 2;
1681
1304
  }
1682
1305
 
1683
- function monitorConnectionId(
1684
- event: ZLinkRawMonitorRecord,
1685
- resolvedNodeRoutingId = event.routingId ?? ''
1686
- ): string {
1687
- if (event.connectionId !== undefined && event.connectionId !== 0n) {
1688
- return JSON.stringify([resolvedNodeRoutingId, 'connection', event.connectionId.toString()]);
1689
- }
1690
- return JSON.stringify([
1691
- // Monitor `value` is event-specific: CONNECTION_READY reports the
1692
- // socket's ready count and DISCONNECTED reports its reason. It is not a
1693
- // connection identity. The public monitor record has no native
1694
- // connection id, so use the stable routing and endpoint tuple instead.
1695
- resolvedNodeRoutingId,
1696
- event.localAddress,
1697
- event.remoteAddress
1698
- ]);
1699
- }
1700
-
1701
- function sameMonitorConnection(left: ZLinkRawMonitorRecord, right: ZLinkRawMonitorRecord): boolean {
1702
- return (
1703
- monitorConnectionId(left) === monitorConnectionId(right) &&
1704
- left.remoteAddress === right.remoteAddress
1705
- );
1706
- }
1707
-
1708
- function isConnectionReadyEdge(event: ZLinkRawMonitorRecord): boolean {
1709
- return event.flags === undefined
1710
- ? event.value > 0n
1711
- : (event.flags & MONITOR_CONNECTION_READY_EDGE) !== 0;
1306
+ /**
1307
+ * An admission belongs to one Core selected route. Its connection identity is
1308
+ * that route's generation, an opaque token compared only for equality.
1309
+ */
1310
+ function routeConnectionId(routeGeneration: bigint): string {
1311
+ return `route:${routeGeneration.toString()}`;
1712
1312
  }
1713
1313
 
1714
1314
  function describeInvalidAdmissionDescriptor(