@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
@@ -110,13 +110,28 @@ class ServiceMaintenanceRuntime {
110
110
  }
111
111
  async runUnits(signal, stopStartingSignal) {
112
112
  const pending = new Set();
113
+ // A unit failure is recorded before its promise leaves `pending`, so no
114
+ // failure is lost. Every started unit runs to its own terminal before the
115
+ // first failure is reported: the host result follows the settled
116
+ // authority of each unit (spec 30 §13).
117
+ const failures = [];
118
+ const reportFailure = async () => {
119
+ if (failures.length === 0)
120
+ return;
121
+ await Promise.allSettled(pending);
122
+ throw failures[0];
123
+ };
113
124
  for (;;) {
125
+ await reportFailure();
114
126
  const queued = await this.lane.run(() => this.units.length > 0);
115
- if (!queued && pending.size === 0)
127
+ if (!queued && pending.size === 0) {
128
+ await reportFailure();
116
129
  return;
130
+ }
117
131
  signal.throwIfAborted();
118
132
  if (stopStartingSignal?.aborted === true) {
119
133
  await Promise.allSettled(pending);
134
+ await reportFailure();
120
135
  stopStartingSignal.throwIfAborted();
121
136
  }
122
137
  let admitted = false;
@@ -142,7 +157,12 @@ class ServiceMaintenanceRuntime {
142
157
  continue;
143
158
  admitted = true;
144
159
  let running;
145
- running = startOutsideStateLane(() => unit.relocate(signal).finally(async () => {
160
+ running = startOutsideStateLane(() => unit
161
+ .relocate(signal)
162
+ .catch((error) => {
163
+ failures.push(error);
164
+ })
165
+ .finally(async () => {
146
166
  const publication = await this.lane.run(() => {
147
167
  this.activeOutbound--;
148
168
  pending.delete(running);
@@ -154,6 +174,7 @@ class ServiceMaintenanceRuntime {
154
174
  this.notify(claim.publication);
155
175
  }
156
176
  if (pending.size === 0) {
177
+ await reportFailure();
157
178
  if (!admitted)
158
179
  throw new Error('No relocation unit is ready to start.');
159
180
  }
@@ -17,3 +17,12 @@ export declare class ServiceRelocationPostCommitError extends Error {
17
17
  readonly cause: unknown;
18
18
  constructor(authority: ZLinkAuthoritySnapshot, staging: ServiceRelocationStaging, cause: unknown);
19
19
  }
20
+ /**
21
+ * A relocation whose settled authority leaves the host in `Error` with
22
+ * `Blocked/RelocationFailed` (spec 30 §13): the source owner lease ended
23
+ * before its Preserve fence succeeded, or units settled on both the source
24
+ * and the target.
25
+ */
26
+ export declare class ServiceRelocationAuthorityError extends Error {
27
+ constructor(message: string, options?: ErrorOptions);
28
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ServiceRelocationPostCommitError = void 0;
3
+ exports.ServiceRelocationAuthorityError = exports.ServiceRelocationPostCommitError = void 0;
4
4
  class ServiceRelocationPostCommitError extends Error {
5
5
  authority;
6
6
  staging;
@@ -14,3 +14,16 @@ class ServiceRelocationPostCommitError extends Error {
14
14
  }
15
15
  }
16
16
  exports.ServiceRelocationPostCommitError = ServiceRelocationPostCommitError;
17
+ /**
18
+ * A relocation whose settled authority leaves the host in `Error` with
19
+ * `Blocked/RelocationFailed` (spec 30 §13): the source owner lease ended
20
+ * before its Preserve fence succeeded, or units settled on both the source
21
+ * and the target.
22
+ */
23
+ class ServiceRelocationAuthorityError extends Error {
24
+ constructor(message, options) {
25
+ super(message, options);
26
+ this.name = 'ServiceRelocationAuthorityError';
27
+ }
28
+ }
29
+ exports.ServiceRelocationAuthorityError = ServiceRelocationAuthorityError;
@@ -16,6 +16,8 @@ export interface ServiceRelocationCaptureUnit {
16
16
  captureApplicationState(signal?: AbortSignal): Promise<Uint8Array>;
17
17
  commitSeal(): Promise<void> | void;
18
18
  abortSeal(): Promise<void> | void;
19
+ /** Expired-owner terminal: pending requests fail once and dispatch stays closed. */
20
+ discardSeal?(reason: unknown): Promise<void> | void;
19
21
  }
20
22
  export declare class ServiceCapturedObjectRelocation {
21
23
  readonly envelope: ServiceRelocationEnvelope;
@@ -24,6 +26,12 @@ export declare class ServiceCapturedObjectRelocation {
24
26
  constructor(envelope: ServiceRelocationEnvelope, units: readonly ServiceRelocationCaptureUnit[]);
25
27
  commitSource(): Promise<void>;
26
28
  abortSource(): Promise<void>;
29
+ /**
30
+ * The source owner lease ended before the authority settled (spec 28 §4.4):
31
+ * the source neither resumes nor commits. Retained work is dropped and
32
+ * every pending request without a terminal fails once with `reason`.
33
+ */
34
+ discardSource(reason: unknown): Promise<void>;
27
35
  }
28
36
  /** Captures the two valid relocation units: User Spot aggregate or one Actor. */
29
37
  export declare class ServiceRelocationObjectCaptureOwner {
@@ -12,8 +12,8 @@ class ServiceCapturedObjectRelocation {
12
12
  async commitSource() {
13
13
  if (this.terminal === 'committed')
14
14
  return;
15
- if (this.terminal === 'aborted') {
16
- throw new Error('Relocation source capture was already aborted.');
15
+ if (this.terminal !== 'open') {
16
+ throw new Error(`Relocation source capture was already ${this.terminal}.`);
17
17
  }
18
18
  for (const unit of this.units)
19
19
  await unit.commitSeal();
@@ -22,8 +22,8 @@ class ServiceCapturedObjectRelocation {
22
22
  async abortSource() {
23
23
  if (this.terminal === 'aborted')
24
24
  return;
25
- if (this.terminal === 'committed') {
26
- throw new Error('Committed relocation source capture cannot be aborted.');
25
+ if (this.terminal !== 'open') {
26
+ throw new Error(`A ${this.terminal} relocation source capture cannot be aborted.`);
27
27
  }
28
28
  const failures = [];
29
29
  for (const unit of [...this.units].reverse()) {
@@ -39,6 +39,18 @@ class ServiceCapturedObjectRelocation {
39
39
  throw new AggregateError(failures, 'Relocation source rollback was incomplete.');
40
40
  }
41
41
  }
42
+ /**
43
+ * The source owner lease ended before the authority settled (spec 28 §4.4):
44
+ * the source neither resumes nor commits. Retained work is dropped and
45
+ * every pending request without a terminal fails once with `reason`.
46
+ */
47
+ async discardSource(reason) {
48
+ if (this.terminal !== 'open')
49
+ return;
50
+ this.terminal = 'discarded';
51
+ for (const unit of this.units)
52
+ await unit.discardSeal?.(reason);
53
+ }
42
54
  }
43
55
  exports.ServiceCapturedObjectRelocation = ServiceCapturedObjectRelocation;
44
56
  /** Captures the two valid relocation units: User Spot aggregate or one Actor. */
@@ -2,22 +2,15 @@ export interface ServiceSessionBindingRetainedDelivery {
2
2
  deliver(): Promise<boolean>;
3
3
  fail(error: unknown): void;
4
4
  }
5
- export interface ServiceSessionBindingAdmissionClaim {
6
- readonly actorId: string;
7
- readonly objectGeneration: bigint;
8
- readonly actorNodeRid: string;
9
- readonly actorNodeGeneration: bigint;
10
- readonly authorityOwnerGeneration: bigint;
11
- readonly ownerLeaseGeneration: bigint;
12
- readonly producerNodeRid: string;
13
- readonly producerNodeGeneration: bigint;
14
- readonly sessionIdentity: string;
15
- readonly bindingGeneration: bigint;
16
- }
17
5
  export type ServiceSessionBindingAdmissionResult = 'passThrough' | 'retained' | 'rejected';
18
6
  export interface ServiceSessionBindingIngressPort {
19
7
  actorSlot(actorId: string, sessionRid: string): Promise<number | undefined>;
20
- retainOutbound(claim: ServiceSessionBindingAdmissionClaim, delivery: ServiceSessionBindingRetainedDelivery): Promise<ServiceSessionBindingAdmissionResult>;
8
+ /**
9
+ * Holds a current-binding push while its relocation seal is open
10
+ * (Session–Actor binding §8.1). The caller has already decided that the push
11
+ * names the current binding.
12
+ */
13
+ retainOutbound(actorId: string, delivery: ServiceSessionBindingRetainedDelivery): Promise<ServiceSessionBindingAdmissionResult>;
21
14
  clearOutbound(actorId: string, error: unknown): Promise<void>;
22
15
  }
23
16
  export declare function registerServiceSessionBindingIngressPort(service: object, port: ServiceSessionBindingIngressPort): void;
@@ -7,15 +7,11 @@ import { type ServiceActorRouteFence, type ServiceRetiredBoundSessionRouteFence,
7
7
  import { type ServiceApplicationPayload } from './service-wire-m6a-codec';
8
8
  import type { ServiceInstanceActivationRecoveryEnvelope } from './service-instance-activation-recovery-codec';
9
9
  import type { ServiceSessionBindingIngressPort } from './service-session-binding-ingress-port';
10
+ import { ZLinkFrameworkRuntimeState } from '../../contracts/Locations';
10
11
  export interface ServiceSpotMessageFollowSeal {
11
12
  readonly key: string;
12
13
  readonly serial: bigint;
13
14
  }
14
- export interface ServiceBoundSessionSendFence {
15
- readonly targetNodeGeneration: bigint;
16
- readonly authorityOwnerGeneration: bigint;
17
- readonly ownerLeaseGeneration: bigint;
18
- }
19
15
  export interface ServiceStatefulResult {
20
16
  readonly terminalResult: number;
21
17
  readonly failureCode: number;
@@ -223,11 +219,17 @@ export declare class ServiceStatefulRuntime {
223
219
  private messageFollowHandler?;
224
220
  private mailboxDropHandler?;
225
221
  private dispatchErrors?;
222
+ private spotAdmissionProvider?;
226
223
  private dispatchErrorMeshName?;
227
224
  private readonly admittedUserSpotOperations;
228
225
  private readonly pendingInstanceActivations;
229
226
  private closed;
230
227
  constructor(raw: RawServiceMeshRuntime, nodeRid: string, nodeGeneration: bigint);
228
+ setSpotAdmissionProvider(provider: {
229
+ readonly isClosing: (spotId: string) => boolean;
230
+ readonly runtimeState: () => ZLinkFrameworkRuntimeState;
231
+ readonly awaitCloseDecision?: (spotId: string) => Promise<void> | undefined;
232
+ }): void;
231
233
  setMailboxDropHandler(handler: (record: {
232
234
  readonly kind: 'spot_multicast' | 'actor_control' | 'actor_binding';
233
235
  readonly owner: string;
@@ -305,7 +307,7 @@ export declare class ServiceStatefulRuntime {
305
307
  sessionBindings(sessionRid: string): readonly ServiceSessionBinding[];
306
308
  allSessionBindings(): readonly ServiceSessionBinding[];
307
309
  sendSessionToActor(sessionRid: string, actor: ServiceActorRef, payload: ServiceApplicationPayload): Promise<number>;
308
- sendBoundSession(actor: ServiceActorRef, expectedBindingGeneration: bigint, payload: ServiceApplicationPayload, senderFence?: ServiceBoundSessionSendFence): Promise<number>;
310
+ sendBoundSession(actor: ServiceActorRef, expectedBindingGeneration: bigint, payload: ServiceApplicationPayload): Promise<number>;
309
311
  close(): void;
310
312
  private ingress;
311
313
  private handleIngress;
@@ -349,6 +351,7 @@ export declare class ServiceStatefulRuntime {
349
351
  private enqueueSessionBindingTombstone;
350
352
  private enqueueSessionBindingRetirement;
351
353
  private deliverBoundSession;
354
+ private traceRejectedBoundSessionSend;
352
355
  private enqueueApplicationFrame;
353
356
  private enqueueLifecycleFrame;
354
357
  private enqueueFrame;
@@ -374,6 +377,7 @@ export declare class ServiceStatefulRuntime {
374
377
  private validateActorMessageFence;
375
378
  private validateSpotFence;
376
379
  private validateDirectSpotFence;
380
+ private requireDirectSpotAdmission;
377
381
  private holdOrRelaySpotMessage;
378
382
  private restoreSpotMessageFollowRecord;
379
383
  private drainSpotMessageFollow;
@@ -384,7 +388,6 @@ export declare class ServiceStatefulRuntime {
384
388
  private peekSpotMessageFollow;
385
389
  private takeSpotMessageFollow;
386
390
  private actorFence;
387
- private tryBoundSessionSendFence;
388
391
  private tryActorFence;
389
392
  private acceptCanonicalActorFence;
390
393
  private trySpotFence;
@@ -8,16 +8,17 @@ const execution_1 = require("../execution");
8
8
  const framework_errors_internal_1 = require("../framework-errors-internal");
9
9
  const runtime_values_1 = require("../backend/runtime-values");
10
10
  const ZLinkDispatchOptions_1 = require("../../contracts/Dispatch/ZLinkDispatchOptions");
11
+ const message_flow_1 = require("../diagnostics/message-flow");
11
12
  const service_runtime_contracts_1 = require("./service-runtime-contracts");
12
13
  const message_follow_suppression_registry_1 = require("./message-follow-suppression-registry");
13
14
  const service_stateful_registry_1 = require("./service-stateful-registry");
14
15
  const service_stateful_wire_codec_1 = require("./service-stateful-wire-codec");
15
- const routing_id_1 = require("../routing-id");
16
16
  const service_wire_pilot_codec_generated_1 = require("../protocol/service_wire_pilot_codec.generated");
17
17
  const actor_join_recovery_codec_1 = require("./actor-join-recovery-codec");
18
18
  const service_wire_m6a_codec_1 = require("./service-wire-m6a-codec");
19
19
  const service_metadata_codec_1 = require("./service-metadata-codec");
20
20
  const contracts_1 = require("../../contracts");
21
+ const Locations_1 = require("../../contracts/Locations");
21
22
  const ACTOR_ROUTE_STALE = 21;
22
23
  const SPOT_MOVING = 34;
23
24
  const USER_SPOT_OPERATION_CAPACITY = 65_536;
@@ -83,6 +84,7 @@ class ServiceStatefulRuntime {
83
84
  messageFollowHandler;
84
85
  mailboxDropHandler;
85
86
  dispatchErrors;
87
+ spotAdmissionProvider;
86
88
  dispatchErrorMeshName;
87
89
  admittedUserSpotOperations = new Map();
88
90
  pendingInstanceActivations = new Map();
@@ -95,6 +97,9 @@ class ServiceStatefulRuntime {
95
97
  this.registry.createEntrySpot(nodeRid);
96
98
  raw.setServiceIngress((record) => this.ingress(record));
97
99
  }
100
+ setSpotAdmissionProvider(provider) {
101
+ this.spotAdmissionProvider = provider;
102
+ }
98
103
  setMailboxDropHandler(handler) {
99
104
  this.mailboxDropHandler = handler;
100
105
  }
@@ -669,7 +674,10 @@ class ServiceStatefulRuntime {
669
674
  if (target === undefined) {
670
675
  return runtime_values_1.SubmitResult.NotFound;
671
676
  }
672
- const header = (0, service_stateful_wire_codec_1.encodeSpotHeader)('spotSend', sourceSpotId, target);
677
+ const header = (0, service_stateful_wire_codec_1.encodeSpotHeader)('spotSend', sourceSpotId, target, undefined, {
678
+ high: this.nodeGeneration,
679
+ low: this.nextMessageFollowOperation++
680
+ });
673
681
  return this.submitOneWay(target.targetNodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)]);
674
682
  }
675
683
  requestToSpot(sourceSpotId, requested, payload, timeoutMs) {
@@ -682,7 +690,10 @@ class ServiceStatefulRuntime {
682
690
  });
683
691
  return pending;
684
692
  }
685
- const header = (0, service_stateful_wire_codec_1.encodeSpotHeader)('spotRequest', sourceSpotId, target, pending.id);
693
+ const header = (0, service_stateful_wire_codec_1.encodeSpotHeader)('spotRequest', sourceSpotId, target, pending.id, {
694
+ high: this.nodeGeneration,
695
+ low: this.nextMessageFollowOperation++
696
+ });
686
697
  this.submitRequest(pending, target.targetNodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)], timeoutMs, 'spotRequest');
687
698
  return pending;
688
699
  }
@@ -695,7 +706,7 @@ class ServiceStatefulRuntime {
695
706
  : {
696
707
  ...boundSession,
697
708
  sequence: this.nextSessionSequence++
698
- });
709
+ }, { high: this.nodeGeneration, low: this.nextMessageFollowOperation++ });
699
710
  return this.submitOneWay(target.nodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)]);
700
711
  }
701
712
  requestToActor(target, _targetNodeGeneration, _authorityOwnerGeneration, payload, timeoutMs, sourceActor, boundSession) {
@@ -713,7 +724,7 @@ class ServiceStatefulRuntime {
713
724
  : {
714
725
  ...boundSession,
715
726
  sequence: this.nextSessionSequence++
716
- });
727
+ }, { high: this.nodeGeneration, low: this.nextMessageFollowOperation++ });
717
728
  this.submitRequest(pending, target.nodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)], timeoutMs, 'actorRequest');
718
729
  return pending;
719
730
  }
@@ -1008,7 +1019,11 @@ class ServiceStatefulRuntime {
1008
1019
  bindingGeneration: delivery.binding.bindingGeneration
1009
1020
  });
1010
1021
  }
1011
- async sendBoundSession(actor, expectedBindingGeneration, payload, senderFence) {
1022
+ async sendBoundSession(actor, expectedBindingGeneration, payload) {
1023
+ // Session–Actor binding §3 item 3: the source sends by binding identity to
1024
+ // the registered route. The header's owner lifecycle fields are this node's
1025
+ // current route values, not send conditions; the Session owner alone
1026
+ // decides whether the binding is current.
1012
1027
  let binding;
1013
1028
  try {
1014
1029
  binding = this.registry.validateBoundSession(actor, expectedBindingGeneration);
@@ -1016,7 +1031,7 @@ class ServiceStatefulRuntime {
1016
1031
  catch {
1017
1032
  return runtime_values_1.SubmitResult.InvalidState;
1018
1033
  }
1019
- const actorFence = this.tryBoundSessionSendFence(actor, senderFence);
1034
+ const actorFence = this.tryActorFence(actor);
1020
1035
  if (actorFence === undefined)
1021
1036
  return runtime_values_1.SubmitResult.InvalidState;
1022
1037
  const header = (0, service_stateful_wire_codec_1.encodeBoundSessionSendHeader)(actorFence, expectedBindingGeneration);
@@ -1094,7 +1109,8 @@ class ServiceStatefulRuntime {
1094
1109
  decoded = statefulActorJoinFromCanonical(canonicalActorJoin);
1095
1110
  }
1096
1111
  else {
1097
- decoded = (0, service_stateful_wire_codec_1.decodeStatefulHeader)(record.parts[0]);
1112
+ const metadata = (record.flags & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0;
1113
+ decoded = (0, service_stateful_wire_codec_1.decodeStatefulHeader)(record.parts[0], record.parts[metadata ? 2 : 1], metadata ? record.parts[1] : undefined);
1098
1114
  }
1099
1115
  const hasPayload = [
1100
1116
  'spotSend',
@@ -1106,19 +1122,26 @@ class ServiceStatefulRuntime {
1106
1122
  'boundSessionSend'
1107
1123
  ].includes(decoded.kind);
1108
1124
  const instanceMetadata = decoded.kind === 'instanceSpot' && (record.flags & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0;
1125
+ const statefulMetadata = (decoded.kind === 'spotSend' ||
1126
+ decoded.kind === 'spotRequest' ||
1127
+ decoded.kind === 'actorSend' ||
1128
+ decoded.kind === 'actorRequest') &&
1129
+ (record.flags & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0;
1109
1130
  const userSpotInfrastructure = decoded.kind === 'userSpotCreate' ||
1110
1131
  decoded.kind === 'userSpotClose' ||
1111
1132
  decoded.kind === 'actorCreate' ||
1112
1133
  decoded.kind === 'messageFollow' ||
1113
1134
  decoded.kind === 'boundSessionReplaced';
1114
1135
  if (record.parts.length < 1 ||
1115
- record.parts.length > (instanceMetadata ? 3 : 2) ||
1116
- (hasPayload && decoded.kind !== 'actorJoin' && record.parts.length !== 2) ||
1136
+ record.parts.length > (instanceMetadata || statefulMetadata ? 3 : 2) ||
1137
+ (hasPayload &&
1138
+ decoded.kind !== 'actorJoin' &&
1139
+ record.parts.length !== ((record.flags & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0 ? 3 : 2)) ||
1117
1140
  (decoded.kind === 'instanceSpot' && record.parts.length !== (instanceMetadata ? 3 : 2)) ||
1118
1141
  (userSpotInfrastructure && record.parts.length !== 1)) {
1119
1142
  return 'protocolError';
1120
1143
  }
1121
- const metadataFrame = instanceMetadata
1144
+ const metadataFrame = instanceMetadata || statefulMetadata
1122
1145
  ? (0, service_metadata_codec_1.validateServiceMetadataFrame)(record.parts[1])
1123
1146
  : undefined;
1124
1147
  const canonicalPayload = canonicalActorJoin?.payload === undefined
@@ -1201,6 +1224,8 @@ class ServiceStatefulRuntime {
1201
1224
  }
1202
1225
  case 'spotSend':
1203
1226
  case 'spotRequest': {
1227
+ await this.spotAdmissionProvider?.awaitCloseDecision?.(record.target.spot.spotId);
1228
+ this.requireDirectSpotAdmission(record.target.spot.spotId);
1204
1229
  const followed = this.holdOrRelaySpotMessage(ingress, record);
1205
1230
  if (followed !== undefined)
1206
1231
  return followed;
@@ -2232,13 +2257,17 @@ class ServiceStatefulRuntime {
2232
2257
  return accepted;
2233
2258
  }
2234
2259
  async deliverBoundSession(ingress, record, payloadFrame) {
2260
+ // Session–Actor binding §3 item 3, §8.1: this Session owner's binding
2261
+ // record alone decides whether command 36 names the current binding
2262
+ // (ActorId, ObjectGeneration, binding generation; SessionRid is the
2263
+ // record's own). The transport boundary has already authenticated the
2264
+ // source peer, and the header's route fields are not compared here.
2265
+ if (payloadFrame === undefined)
2266
+ return 'protocolError';
2235
2267
  const delivery = this.sessionDeliveries.get(actorKey(record.actor.actor));
2236
2268
  if (delivery === undefined ||
2237
- delivery.binding.bindingGeneration !== record.expectedBindingGeneration ||
2238
- payloadFrame === undefined ||
2239
- ingress.sourceNodeGeneration === undefined ||
2240
- !(0, routing_id_1.routingIdsEqual)(ingress.sourceRoutingId, record.actor.actor.nodeRid) ||
2241
- ingress.sourceNodeGeneration !== record.actor.targetNodeGeneration) {
2269
+ delivery.binding.bindingGeneration !== record.expectedBindingGeneration) {
2270
+ this.traceRejectedBoundSessionSend(ingress, record, payloadFrame);
2242
2271
  return 'protocolError';
2243
2272
  }
2244
2273
  const sessionRid = delivery.binding.sessionRid;
@@ -2264,18 +2293,7 @@ class ServiceStatefulRuntime {
2264
2293
  settle();
2265
2294
  }
2266
2295
  };
2267
- const decision = await delivery.bindingIngress?.retainOutbound({
2268
- actorId: record.actor.actor.actorId,
2269
- objectGeneration: record.actor.actor.generation,
2270
- actorNodeRid: record.actor.actor.nodeRid,
2271
- actorNodeGeneration: record.actor.targetNodeGeneration,
2272
- authorityOwnerGeneration: record.actor.authorityOwnerGeneration,
2273
- ownerLeaseGeneration: record.actor.ownerLeaseGeneration,
2274
- producerNodeRid: ingress.sourceRoutingId,
2275
- producerNodeGeneration: ingress.sourceNodeGeneration,
2276
- sessionIdentity: sessionRid,
2277
- bindingGeneration: record.expectedBindingGeneration
2278
- }, operation);
2296
+ const decision = await delivery.bindingIngress?.retainOutbound(record.actor.actor.actorId, operation);
2279
2297
  if (decision === 'retained')
2280
2298
  return 'application';
2281
2299
  if (decision === 'rejected') {
@@ -2301,6 +2319,35 @@ class ServiceStatefulRuntime {
2301
2319
  }
2302
2320
  return 'application';
2303
2321
  }
2322
+ // Session–Actor binding §3 item 4: a push refused as not current is
2323
+ // recorded with the closed flow vocabulary (dropped, stream_session, send,
2324
+ // stale_target) instead of disappearing. Nothing is built while tracing is
2325
+ // off.
2326
+ traceRejectedBoundSessionSend(ingress, record, payloadFrame) {
2327
+ const tracePoint = (0, message_flow_1.flowIfEnabled)(this.dispatchErrors?.flow, ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped);
2328
+ if (tracePoint === undefined)
2329
+ return;
2330
+ let packetName;
2331
+ try {
2332
+ packetName = (0, service_wire_m6a_codec_1.decodeApplicationPayloadView)(payloadFrame).packetName;
2333
+ }
2334
+ catch (error) {
2335
+ // A malformed payload is still a refused push; it is traced without a
2336
+ // packet name.
2337
+ if (!(error instanceof service_wire_m6a_codec_1.ServiceWireProtocolError))
2338
+ throw error;
2339
+ }
2340
+ tracePoint.trace({
2341
+ outcome: ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped,
2342
+ surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.StreamSession,
2343
+ messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
2344
+ ...(packetName === undefined ? {} : { packetName }),
2345
+ ...(this.dispatchErrorMeshName === undefined ? {} : { meshName: this.dispatchErrorMeshName }),
2346
+ sourceRid: ingress.sourceRoutingId,
2347
+ actorId: record.actor.actor.actorId,
2348
+ errorReason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget
2349
+ });
2350
+ }
2304
2351
  enqueueApplicationFrame(ingress, owner, payloadFrame, stateful) {
2305
2352
  return this.enqueueFrame('application', ingress, owner, payloadFrame, stateful);
2306
2353
  }
@@ -2783,7 +2830,7 @@ class ServiceStatefulRuntime {
2783
2830
  applicationJobOwner
2784
2831
  };
2785
2832
  try {
2786
- this.submitLocalRequest(localIngress, pending, operationKind, actor, deadlineUnixMs);
2833
+ await this.submitLocalRequest(localIngress, pending, operationKind, actor, deadlineUnixMs);
2787
2834
  }
2788
2835
  catch (error) {
2789
2836
  this.operations.reply(pending.id, failure(error));
@@ -2854,7 +2901,7 @@ class ServiceStatefulRuntime {
2854
2901
  this.operations.fail(pending.id, error);
2855
2902
  });
2856
2903
  }
2857
- submitLocalRequest(ingress, pending, operationKind, actor, deadlineUnixMs) {
2904
+ async submitLocalRequest(ingress, pending, operationKind, actor, deadlineUnixMs) {
2858
2905
  const canonicalActorJoin = operationKind === 'actorJoin' ? (0, service_wire_pilot_codec_generated_1.decodeActorJoin28)(ingress.parts) : undefined;
2859
2906
  const decoded = canonicalActorJoin === undefined
2860
2907
  ? (0, service_stateful_wire_codec_1.decodeStatefulHeader)(ingress.parts[0])
@@ -2891,6 +2938,7 @@ class ServiceStatefulRuntime {
2891
2938
  }
2892
2939
  const localReply = (terminalResult, failureCode, replyPayload, tail) => this.operations.reply(pending.id, this.resultFromReply(terminalResult, failureCode, replyPayload, tail, this.nodeRid, actor));
2893
2940
  if (decoded.kind === 'spotRequest') {
2941
+ await this.spotAdmissionProvider?.awaitCloseDecision?.(decoded.target.spot.spotId);
2894
2942
  this.validateDirectSpotFence(decoded.target);
2895
2943
  this.enqueueApplicationFrame(ingress, `spot:${decoded.target.spot.spotId}`, payloadFrame, {
2896
2944
  receiveKind: service_runtime_contracts_1.ReceiveKind.SpotRequest,
@@ -2911,7 +2959,9 @@ class ServiceStatefulRuntime {
2911
2959
  operationKind: service_runtime_contracts_1.OperationKind.ActorRequest,
2912
2960
  correlation: decoded.correlation,
2913
2961
  targetActor: current.ref,
2914
- ...(decoded.sourceActor === undefined ? {} : { sourceActor: decoded.sourceActor }),
2962
+ ...(decoded.sourceActor === undefined
2963
+ ? {}
2964
+ : { sourceActor: { ...decoded.sourceActor, nodeRid: ingress.sourceRoutingId } }),
2915
2965
  ...(decoded.boundSession === undefined
2916
2966
  ? {}
2917
2967
  : { sourceBindingGeneration: decoded.boundSession.bindingGeneration }),
@@ -3147,6 +3197,11 @@ class ServiceStatefulRuntime {
3147
3197
  return spot;
3148
3198
  }
3149
3199
  validateDirectSpotFence(fence) {
3200
+ if (fence.targetNodeRid !== this.nodeRid ||
3201
+ fence.targetNodeGeneration !== this.nodeGeneration) {
3202
+ throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotMoving, `Spot '${fence.spot.spotId}' targets an unavailable owner route.`);
3203
+ }
3204
+ this.requireDirectSpotAdmission(fence.spot.spotId);
3150
3205
  const spot = this.registry.spot(fence.spot.spotId);
3151
3206
  if (spot === undefined) {
3152
3207
  throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RequestTargetNotFound, `Spot '${fence.spot.spotId}' has no current Ready authority.`);
@@ -3154,10 +3209,6 @@ class ServiceStatefulRuntime {
3154
3209
  if (spot.state !== 'ready') {
3155
3210
  throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotMoving, `Spot '${fence.spot.spotId}' is not Ready.`);
3156
3211
  }
3157
- if (fence.targetNodeRid !== this.nodeRid ||
3158
- fence.targetNodeGeneration !== this.nodeGeneration) {
3159
- throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotMoving, `Spot '${fence.spot.spotId}' targets an unavailable owner route.`);
3160
- }
3161
3212
  if (spot.kind === 'entry') {
3162
3213
  if (!isEntrySpotFence(fence)) {
3163
3214
  throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotMoving, `Entry Spot '${fence.spot.spotId}' has an invalid owner fence.`);
@@ -3177,9 +3228,17 @@ class ServiceStatefulRuntime {
3177
3228
  }
3178
3229
  return spot;
3179
3230
  }
3231
+ requireDirectSpotAdmission(spotId) {
3232
+ if (this.spotAdmissionProvider?.runtimeState() === Locations_1.ZLinkFrameworkRuntimeState.Draining) {
3233
+ throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RuntimeShutdown, `Spot '${spotId}' owner is Draining.`);
3234
+ }
3235
+ if (this.spotAdmissionProvider?.isClosing(spotId) === true) {
3236
+ throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RequestRejected, `Spot '${spotId}' authority is Closing.`);
3237
+ }
3238
+ }
3180
3239
  holdOrRelaySpotMessage(ingress, wire) {
3181
3240
  const state = this.spotMessageFollow.get(spotKey(wire.target.spot));
3182
- if (state === undefined || !sameDirectSpotRoute(state.source, wire.target)) {
3241
+ if (state === undefined || !sameDirectSpotRouteIdentity(state.source, wire.target)) {
3183
3242
  return undefined;
3184
3243
  }
3185
3244
  if (state.expiresAtMs !== undefined && state.expiresAtMs <= performance.now()) {
@@ -3224,9 +3283,11 @@ class ServiceStatefulRuntime {
3224
3283
  }
3225
3284
  const current = this.peekSpotMessageFollow(state);
3226
3285
  try {
3286
+ const metadata = (current.ingress.parts[0][4] & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0;
3227
3287
  const parts = [
3228
- (0, service_stateful_wire_codec_1.encodeSpotHeader)(current.wire.kind, current.wire.sourceSpotId, state.target, current.wire.correlation),
3229
- current.ingress.parts[1]
3288
+ (0, service_stateful_wire_codec_1.encodeSpotHeader)(current.wire.kind, current.wire.sourceSpotId, state.target, current.wire.correlation, current.wire.operation, current.wire.messageFollowHopCount + 1, metadata ? current.ingress.parts[1] : undefined),
3289
+ ...(metadata ? [current.ingress.parts[1]] : []),
3290
+ current.ingress.parts[metadata ? 2 : 1]
3230
3291
  ];
3231
3292
  if (current.wire.kind === 'spotSend') {
3232
3293
  if (!(await this.raw.sendService(state.target.targetNodeRid, parts)))
@@ -3341,26 +3402,6 @@ class ServiceStatefulRuntime {
3341
3402
  throw new service_stateful_registry_1.ServiceStaleGenerationError('actor', actor.actorId);
3342
3403
  return route;
3343
3404
  }
3344
- tryBoundSessionSendFence(actor, senderFence) {
3345
- if (senderFence === undefined ||
3346
- senderFence.targetNodeGeneration !== this.nodeGeneration ||
3347
- senderFence.authorityOwnerGeneration <= 0n ||
3348
- senderFence.ownerLeaseGeneration <= 0n) {
3349
- return undefined;
3350
- }
3351
- const local = actor.nodeRid === this.nodeRid ? this.registry.actor(actor.actorId) : undefined;
3352
- if (local === undefined ||
3353
- !sameActorRef(local.ref, actor) ||
3354
- local.authorityOwnerGeneration !== senderFence.authorityOwnerGeneration) {
3355
- return undefined;
3356
- }
3357
- return {
3358
- actor,
3359
- targetNodeGeneration: senderFence.targetNodeGeneration,
3360
- authorityOwnerGeneration: senderFence.authorityOwnerGeneration,
3361
- ownerLeaseGeneration: senderFence.ownerLeaseGeneration
3362
- };
3363
- }
3364
3405
  tryActorFence(actor) {
3365
3406
  const local = actor.nodeRid === this.nodeRid ? this.registry.actor(actor.actorId) : undefined;
3366
3407
  if (local !== undefined && sameActorRef(local.ref, actor)) {
@@ -3558,8 +3599,7 @@ function isEntrySpotFence(fence) {
3558
3599
  return (fence.spot.spotId === fence.targetNodeRid &&
3559
3600
  fence.spot.generation === fence.targetNodeGeneration &&
3560
3601
  fence.authorityOwnerGeneration === fence.targetNodeGeneration &&
3561
- fence.ownerLeaseGeneration === fence.targetNodeGeneration &&
3562
- fence.storeVersion === `entry:${fence.targetNodeGeneration}`);
3602
+ fence.ownerLeaseGeneration === fence.targetNodeGeneration);
3563
3603
  }
3564
3604
  function userSpotDeadline(deadlineUnixMs) {
3565
3605
  const controller = new AbortController();
@@ -278,6 +278,7 @@ export type ServiceSessionRelocationRoute = {
278
278
  readonly targetAuthorityOwnerGeneration: bigint;
279
279
  readonly targetNodeRid: string;
280
280
  readonly targetNodeGeneration: bigint;
281
+ readonly targetOwnerLeaseGeneration: bigint;
281
282
  };
282
283
  } | {
283
284
  readonly relocation: ServiceWireOperationId;
@@ -414,8 +415,10 @@ export type ServiceBoundSessionTransition = {
414
415
  export type ServiceStatefulWireRecord = {
415
416
  readonly kind: 'spotSend' | 'spotRequest';
416
417
  readonly correlation?: bigint;
418
+ readonly operation: ServiceWireOperationId;
419
+ readonly messageFollowHopCount: number;
417
420
  readonly sourceSpotId: string;
418
- readonly target: ServiceDirectSpotRouteFence;
421
+ readonly target: ServiceSpotRouteFence;
419
422
  } | {
420
423
  readonly kind: 'logicalMulticast';
421
424
  readonly channelName: string;
@@ -424,6 +427,8 @@ export type ServiceStatefulWireRecord = {
424
427
  } | {
425
428
  readonly kind: 'actorSend' | 'actorRequest';
426
429
  readonly correlation?: bigint;
430
+ readonly operation: ServiceWireOperationId;
431
+ readonly messageFollowHopCount: number;
427
432
  readonly sourceActor?: ServiceActorRef;
428
433
  readonly target: ServiceActorRouteFence;
429
434
  readonly boundSession?: ServiceBoundSessionSource;
@@ -521,8 +526,8 @@ export interface ServiceStatefulReply {
521
526
  readonly failureCode: number;
522
527
  readonly tail?: ServiceStatefulReplyTail;
523
528
  }
524
- export declare function encodeSpotHeader(kind: 'spotSend' | 'spotRequest', sourceSpotId: string, target: ServiceDirectSpotRouteFence, correlation?: bigint): Buffer;
525
- export declare function encodeActorHeader(kind: 'actorSend' | 'actorRequest', target: ServiceActorRouteFence, correlation?: bigint, sourceActor?: ServiceActorRef, boundSession?: ServiceBoundSessionSource): Buffer;
529
+ export declare function encodeSpotHeader(kind: 'spotSend' | 'spotRequest', sourceSpotId: string, target: ServiceDirectSpotRouteFence, correlation?: bigint, operation?: ServiceWireOperationId, messageFollowHopCount?: number, metadataFrame?: Uint8Array): Buffer;
530
+ export declare function encodeActorHeader(kind: 'actorSend' | 'actorRequest', target: ServiceActorRouteFence, correlation?: bigint, sourceActor?: ServiceActorRef, boundSession?: ServiceBoundSessionSource, operation?: ServiceWireOperationId, messageFollowHopCount?: number): Buffer;
526
531
  export declare function encodeLogicalMulticastHeader(channelName: string, topic: string, sourceSpotId: string): Buffer;
527
532
  export declare function encodeActorLookupHeader(correlation: bigint, actorId: string): Buffer;
528
533
  export declare function encodeActorDestroyHeader(correlation: bigint, actor: ServiceActorRouteFence): Buffer;
@@ -553,7 +558,7 @@ export declare function encodeUserSpotCreateHeader(record: Omit<ServiceUserSpotC
553
558
  export declare function encodeActorCreateHeader(record: Omit<ServiceActorCreateRecord, 'kind'>): Buffer;
554
559
  export declare function encodeUserSpotCloseHeader(record: Omit<ServiceUserSpotCloseRecord, 'kind'>): Buffer;
555
560
  export declare function encodeMessageFollowHeader(record: Omit<ServiceMessageFollowRecord, 'kind'>): Buffer;
556
- export declare function decodeStatefulHeader(frame: Uint8Array): ServiceStatefulWireRecord;
561
+ export declare function decodeStatefulHeader(frame: Uint8Array, payloadFrame?: Uint8Array, metadataFrame?: Uint8Array): ServiceStatefulWireRecord;
557
562
  export declare function encodeStatefulReply(correlation: bigint, terminalResult: number, failureCode: number, tail?: ServiceStatefulReplyTail): Buffer;
558
563
  /** Encodes canonical maintenance relocation commands 30, 31, 34, 40, 52, and 53. */
559
564
  export declare function encodeMaintenanceRelocationControl(value: ServiceMaintenanceRelocationControl): Buffer;