@zlink-systems/framework 0.31.0 → 0.32.1

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 (181) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Actors/ZLinkActor.d.ts +2 -0
  3. package/dist/contracts/Actors/ZLinkSessionBindingIdentity.d.ts +6 -0
  4. package/dist/contracts/Actors/ZLinkSessionBindingIdentity.js +2 -0
  5. package/dist/contracts/Actors/index.d.ts +1 -0
  6. package/dist/contracts/Actors/index.js +1 -0
  7. package/dist/contracts/Common/payload-slab.d.ts +4 -0
  8. package/dist/contracts/Common/payload-slab.js +30 -0
  9. package/dist/contracts/Handlers/Contexts.d.ts +1 -0
  10. package/dist/internal.d.ts +9 -0
  11. package/dist/internal.js +23 -1
  12. package/dist/runtime/actors/actor-client.d.ts +1 -1
  13. package/dist/runtime/actors/actor-client.js +32 -23
  14. package/dist/runtime/actors/actor-context.js +1 -1
  15. package/dist/runtime/actors/actor-creation.js +1 -1
  16. package/dist/runtime/actors/actor-mailbox.d.ts +1 -1
  17. package/dist/runtime/actors/actor-runtime-state.d.ts +3 -2
  18. package/dist/runtime/actors/actor-runtime-state.js +24 -8
  19. package/dist/runtime/actors/index.d.ts +3 -3
  20. package/dist/runtime/actors/index.js +19 -32
  21. package/dist/runtime/actors/spot-actor-dispatch.d.ts +4 -2
  22. package/dist/runtime/actors/spot-actor-dispatch.js +2 -1
  23. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +1 -0
  24. package/dist/runtime/application-jobs/application-ingress-record-owner.js +3 -0
  25. package/dist/runtime/backend/contracts/index.d.ts +2 -0
  26. package/dist/runtime/backend/mesh-completion-table.d.ts +1 -0
  27. package/dist/runtime/backend/mesh-completion-table.js +1 -0
  28. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +5 -1
  29. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +7 -2
  30. package/dist/runtime/backend/node/node-raw-binding-port.js +17 -11
  31. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +2 -2
  32. package/dist/runtime/backend/node/node-raw-mesh-backend.js +15 -8
  33. package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +7 -6
  34. package/dist/runtime/channels/channel-dispatch-pipeline.js +1 -0
  35. package/dist/runtime/channels/channel-dispatchers.d.ts +7 -3
  36. package/dist/runtime/channels/channel-dispatchers.js +27 -19
  37. package/dist/runtime/channels/channel-envelope.js +543 -23
  38. package/dist/runtime/channels/channel-receive-loops.d.ts +9 -6
  39. package/dist/runtime/channels/channel-receive-loops.js +59 -18
  40. package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +22 -3
  41. package/dist/runtime/channels/channel-socket-registry.d.ts +22 -4
  42. package/dist/runtime/diagnostics/runtime-metrics.d.ts +1 -1
  43. package/dist/runtime/diagnostics/runtime-metrics.js +2 -1
  44. package/dist/runtime/execution/serial-execution-queue.d.ts +26 -13
  45. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +27 -11
  46. package/dist/runtime/foundation/raw-service-mesh-runtime.js +29 -25
  47. package/dist/runtime/foundation/service-liveness-registry.d.ts +1 -1
  48. package/dist/runtime/foundation/service-mailbox.d.ts +3 -1
  49. package/dist/runtime/foundation/service-mailbox.js +4 -1
  50. package/dist/runtime/foundation/service-runtime-contracts.d.ts +3 -1
  51. package/dist/runtime/foundation/service-stateful-registry.d.ts +2 -0
  52. package/dist/runtime/foundation/service-stateful-registry.js +28 -12
  53. package/dist/runtime/foundation/service-stateful-runtime.d.ts +51 -19
  54. package/dist/runtime/foundation/service-stateful-runtime.js +54 -5
  55. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +3 -0
  56. package/dist/runtime/foundation/service-stateful-wire-codec.js +15 -0
  57. package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
  58. package/dist/runtime/foundation/service-wire-constants.generated.js +12 -0
  59. package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +2 -0
  60. package/dist/runtime/foundation/service-wire-m6a-codec.js +17 -7
  61. package/dist/runtime/host/actor-placement-coordinator.d.ts +1 -1
  62. package/dist/runtime/host/actor-transfer-runtime.d.ts +3 -3
  63. package/dist/runtime/host/application-job-queue.d.ts +1 -1
  64. package/dist/runtime/host/index.d.ts +89 -36
  65. package/dist/runtime/host/index.js +101 -7
  66. package/dist/runtime/host/location-runtime-owner.d.ts +2 -2
  67. package/dist/runtime/host/service-relocation-host-runtime.d.ts +181 -44
  68. package/dist/runtime/host/service-relocation-host-runtime.js +47 -13
  69. package/dist/runtime/host/spot-address-transport.js +50 -42
  70. package/dist/runtime/locations/actor-location-claims.js +9 -10
  71. package/dist/runtime/locations/auto-connect-loop.d.ts +0 -1
  72. package/dist/runtime/locations/auto-connect-loop.js +25 -19
  73. package/dist/runtime/locations/auto-connect-planner.js +2 -5
  74. package/dist/runtime/locations/auto-connect-reconciler.d.ts +1 -0
  75. package/dist/runtime/locations/auto-connect-reconciler.js +35 -23
  76. package/dist/runtime/locations/in-memory-location-store.d.ts +0 -2
  77. package/dist/runtime/locations/in-memory-location-store.js +1 -38
  78. package/dist/runtime/locations/location-store-repository.d.ts +0 -1
  79. package/dist/runtime/locations/location-store-repository.js +13 -115
  80. package/dist/runtime/locations/runtime.d.ts +3 -3
  81. package/dist/runtime/locations/runtime.js +12 -16
  82. package/dist/runtime/messaging/creation-payload-codec.js +2 -2
  83. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +4 -0
  84. package/dist/runtime/protocol/service_wire_codec.generated.js +17 -7
  85. package/dist/runtime/spots/index.d.ts +20 -5
  86. package/dist/runtime/spots/index.js +14 -0
  87. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +3 -3
  88. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +1 -0
  89. package/dist/runtime/spots/spot-actor-packet-dispatch.js +13 -7
  90. package/dist/runtime/spots/spot-actor-packet-drain.d.ts +1 -0
  91. package/dist/runtime/spots/spot-actor-packet-drain.js +12 -5
  92. package/dist/runtime/spots/spot-entry-activation.d.ts +2 -1
  93. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +8 -6
  94. package/dist/runtime/spots/spot-node-runtime-manager.js +2 -0
  95. package/dist/runtime/spots/spot-route-packet-dispatch.js +7 -4
  96. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +3 -1
  97. package/dist/runtime/spots/spot-serial-executor.d.ts +2 -1
  98. package/dist/runtime/spots/spot-subscription-dispatch.js +3 -1
  99. package/dist/runtime/spots/spot-timer.d.ts +1 -1
  100. package/dist/runtime/streams/actor-session-binding-registry.d.ts +9 -3
  101. package/dist/runtime/streams/actor-session-binding-registry.js +17 -0
  102. package/dist/runtime/streams/index.d.ts +3 -3
  103. package/dist/runtime/streams/index.js +22 -2
  104. package/dist/runtime/streams/managed-stream.js +2 -1
  105. package/dist/runtime/streams/session-calls.d.ts +1 -1
  106. package/dist/runtime/streams/session-calls.js +3 -9
  107. package/dist/runtime/streams/session-context.d.ts +2 -2
  108. package/dist/runtime/streams/session-context.js +2 -2
  109. package/dist/runtime/streams/stream-session-runtime.d.ts +1 -0
  110. package/dist/runtime/streams/stream-session-runtime.js +37 -12
  111. package/package.json +3 -3
  112. package/src/contracts/Actors/ZLinkActor.ts +6 -0
  113. package/src/contracts/Actors/ZLinkSessionBindingIdentity.ts +7 -0
  114. package/src/contracts/Actors/index.ts +1 -0
  115. package/src/contracts/Common/payload-slab.ts +29 -0
  116. package/src/contracts/Handlers/Contexts.ts +1 -0
  117. package/src/internal.ts +17 -0
  118. package/src/runtime/actors/actor-client.ts +48 -23
  119. package/src/runtime/actors/actor-context.ts +1 -1
  120. package/src/runtime/actors/actor-creation.ts +1 -1
  121. package/src/runtime/actors/actor-mailbox.ts +1 -1
  122. package/src/runtime/actors/actor-runtime-state.ts +28 -10
  123. package/src/runtime/actors/index.ts +30 -35
  124. package/src/runtime/actors/spot-actor-dispatch.ts +4 -3
  125. package/src/runtime/application-jobs/application-ingress-record-owner.ts +4 -0
  126. package/src/runtime/backend/contracts/index.ts +2 -0
  127. package/src/runtime/backend/mesh-completion-table.ts +2 -0
  128. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +5 -1
  129. package/src/runtime/backend/node/node-raw-binding-port.ts +20 -7
  130. package/src/runtime/backend/node/node-raw-mesh-backend.ts +18 -10
  131. package/src/runtime/channels/channel-dispatch-pipeline.ts +6 -5
  132. package/src/runtime/channels/channel-dispatchers.ts +39 -19
  133. package/src/runtime/channels/channel-envelope.ts +547 -20
  134. package/src/runtime/channels/channel-receive-loops.ts +93 -36
  135. package/src/runtime/channels/channel-runtime-lifecycle.ts +2 -2
  136. package/src/runtime/channels/channel-socket-registry.ts +3 -3
  137. package/src/runtime/diagnostics/runtime-metrics.ts +3 -1
  138. package/src/runtime/execution/serial-execution-queue.ts +13 -13
  139. package/src/runtime/foundation/raw-service-mesh-runtime.ts +40 -39
  140. package/src/runtime/foundation/service-liveness-registry.ts +1 -1
  141. package/src/runtime/foundation/service-mailbox.ts +6 -1
  142. package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
  143. package/src/runtime/foundation/service-stateful-registry.ts +39 -14
  144. package/src/runtime/foundation/service-stateful-runtime.ts +87 -21
  145. package/src/runtime/foundation/service-stateful-wire-codec.ts +18 -0
  146. package/src/runtime/foundation/service-wire-constants.generated.ts +10 -0
  147. package/src/runtime/foundation/service-wire-m6a-codec.ts +29 -7
  148. package/src/runtime/host/actor-placement-coordinator.ts +1 -1
  149. package/src/runtime/host/actor-transfer-runtime.ts +3 -3
  150. package/src/runtime/host/application-job-queue.ts +1 -1
  151. package/src/runtime/host/index.ts +183 -44
  152. package/src/runtime/host/location-runtime-owner.ts +2 -2
  153. package/src/runtime/host/service-relocation-host-runtime.ts +101 -65
  154. package/src/runtime/host/spot-address-transport.ts +67 -61
  155. package/src/runtime/locations/actor-location-claims.ts +14 -14
  156. package/src/runtime/locations/auto-connect-loop.ts +24 -16
  157. package/src/runtime/locations/auto-connect-planner.ts +2 -5
  158. package/src/runtime/locations/auto-connect-reconciler.ts +37 -24
  159. package/src/runtime/locations/in-memory-location-store.ts +1 -74
  160. package/src/runtime/locations/location-store-repository.ts +32 -153
  161. package/src/runtime/locations/runtime.ts +16 -28
  162. package/src/runtime/messaging/creation-payload-codec.ts +3 -3
  163. package/src/runtime/protocol/service_wire_codec.generated.ts +18 -8
  164. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  165. package/src/runtime/spots/index.ts +28 -4
  166. package/src/runtime/spots/spot-actor-admission-coordinator.ts +1 -1
  167. package/src/runtime/spots/spot-actor-packet-dispatch.ts +16 -6
  168. package/src/runtime/spots/spot-actor-packet-drain.ts +21 -6
  169. package/src/runtime/spots/spot-entry-activation.ts +1 -1
  170. package/src/runtime/spots/spot-node-runtime-manager.ts +8 -6
  171. package/src/runtime/spots/spot-route-packet-dispatch.ts +8 -4
  172. package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +3 -1
  173. package/src/runtime/spots/spot-serial-executor.ts +1 -1
  174. package/src/runtime/spots/spot-subscription-dispatch.ts +3 -1
  175. package/src/runtime/spots/spot-timer.ts +1 -1
  176. package/src/runtime/streams/actor-session-binding-registry.ts +30 -3
  177. package/src/runtime/streams/index.ts +30 -4
  178. package/src/runtime/streams/managed-stream.ts +2 -1
  179. package/src/runtime/streams/session-calls.ts +9 -14
  180. package/src/runtime/streams/session-context.ts +6 -11
  181. package/src/runtime/streams/stream-session-runtime.ts +49 -20
@@ -8,6 +8,7 @@ const internal_observation_1 = require("../internal-observation");
8
8
  const node_os_1 = require("node:os");
9
9
  const application_ingress_record_owner_1 = require("../application-jobs/application-ingress-record-owner");
10
10
  const submission_result_1 = require("../messaging/submission-result");
11
+ const payload_slab_1 = require("../../contracts/Common/payload-slab");
11
12
  const runtime_values_1 = require("../backend/runtime-values");
12
13
  const service_wire_codec_generated_1 = require("../protocol/service_wire_codec.generated");
13
14
  const service_wire_binary_primitives_1 = require("./service-wire-binary-primitives");
@@ -774,28 +775,13 @@ class RawServiceMeshRuntime {
774
775
  replyGenericProtocolError(received) {
775
776
  if (received.requestSeq === undefined || received.parts.length === 0)
776
777
  return false;
777
- try {
778
- const header = (0, service_wire_m6a_codec_1.decodeHeader)(received.parts[0]);
779
- const correlation = header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.nodeRequest
780
- ? (0, service_wire_m6a_codec_1.decodeNodeRequestHeader)(received.parts[0])
781
- : header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.channelRequest
782
- ? (0, service_wire_m6a_codec_1.decodeChannelRequestHeader)(received.parts[0]).correlation
783
- : undefined;
784
- if (correlation === undefined)
785
- return false;
786
- const reply = [
787
- (0, service_wire_m6a_codec_1.encodeReplyHeader)(correlation, runtime_values_1.RequestResult.ProtocolError, service_wire_constants_generated_1.ServiceWireFrameworkErrorCode.requestProtocolError)
788
- ];
789
- if (received.reply === undefined)
790
- return false;
791
- received.reply(reply);
792
- return true;
793
- }
794
- catch (error) {
795
- if (error instanceof service_wire_m6a_codec_1.ServiceWireProtocolError)
796
- return false;
797
- throw error;
798
- }
778
+ const correlation = (0, service_wire_constants_generated_1.recoverRequestCorrelation)(received.parts[0]);
779
+ if (correlation === undefined || received.reply === undefined)
780
+ return false;
781
+ received.reply([
782
+ (0, service_wire_m6a_codec_1.encodeReplyHeader)(correlation, runtime_values_1.RequestResult.ProtocolError, service_wire_constants_generated_1.ServiceWireFrameworkErrorCode.requestProtocolError)
783
+ ]);
784
+ return true;
799
785
  }
800
786
  processReceived(received, nowMs, applicationJobOwner) {
801
787
  const peer = this.topology.admittedPeer(received.sourceRid);
@@ -1081,12 +1067,29 @@ class RawServiceMeshRuntime {
1081
1067
  ];
1082
1068
  this.startControlSend(probe.nodeRoutingId, service_wire_m6a_codec_1.M6aServiceWireCommand.livenessProbe, () => this.submitControl(probe.nodeRoutingId, parts));
1083
1069
  }
1070
+ const failures = [];
1084
1071
  for (const nodeRoutingId of result.timedOutNodes) {
1085
1072
  const peer = this.topology.peer(nodeRoutingId);
1086
1073
  if (peer === undefined)
1087
1074
  continue;
1088
- this.removePeer(peer, () => this.requireStarted().disconnectRid(nodeRoutingId));
1075
+ try {
1076
+ this.removePeer(peer, () => {
1077
+ try {
1078
+ this.requireStarted().disconnectRid(nodeRoutingId);
1079
+ }
1080
+ catch (error) {
1081
+ failures.push(error);
1082
+ }
1083
+ });
1084
+ }
1085
+ catch (error) {
1086
+ failures.push(error);
1087
+ }
1089
1088
  }
1089
+ if (failures.length === 1)
1090
+ throw failures[0];
1091
+ if (failures.length > 1)
1092
+ throw new AggregateError(failures, 'Liveness peer cleanup failed.');
1090
1093
  return result;
1091
1094
  }
1092
1095
  /**
@@ -1168,7 +1171,7 @@ class RawServiceMeshRuntime {
1168
1171
  const header = channelName === undefined
1169
1172
  ? (0, service_wire_m6a_codec_1.encodeNodeRequestHeader)(correlation)
1170
1173
  : (0, service_wire_m6a_codec_1.encodeChannelRequestHeader)(correlation, channelName);
1171
- const encodedPayload = this.applicationFrame(payload);
1174
+ let encodedPayload = this.applicationFrame(payload);
1172
1175
  let selectedTargetNodeRoutingId = targetNodeRoutingId;
1173
1176
  if (selectedTargetNodeRoutingId !== this.descriptor.nodeRoutingId &&
1174
1177
  !this.isPeerRouteReady(selectedTargetNodeRoutingId)) {
@@ -1195,6 +1198,7 @@ class RawServiceMeshRuntime {
1195
1198
  }
1196
1199
  }
1197
1200
  if (selectedTargetNodeRoutingId === this.descriptor.nodeRoutingId) {
1201
+ encodedPayload = (0, payload_slab_1.copyPayloadBytes)(encodedPayload);
1198
1202
  // The local admission path aborts its capacity wait on the terminal
1199
1203
  // Promise, so a local request always keeps the Promise entry.
1200
1204
  const pending = requestOperation === undefined
@@ -1317,7 +1321,7 @@ class RawServiceMeshRuntime {
1317
1321
  return Buffer.isBuffer(payload) ? payload : (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload);
1318
1322
  }
1319
1323
  applicationPayload(payload) {
1320
- return Buffer.isBuffer(payload) ? (0, service_wire_m6a_codec_1.decodeApplicationPayloadView)(payload) : payload;
1324
+ return Buffer.isBuffer(payload) ? (0, service_wire_m6a_codec_1.decodeApplicationPayload)(payload) : payload;
1321
1325
  }
1322
1326
  admitPeer(descriptor, connectionId, nowMs, expected, command = service_wire_m6a_codec_1.M6aServiceWireCommand.hello, descriptorBytes) {
1323
1327
  const previous = this.topology.admittedPeer(descriptor.nodeRoutingId);
@@ -25,7 +25,7 @@ export declare class ServiceLivenessConnection {
25
25
  export declare class ServiceLivenessRegistry {
26
26
  private readonly probeIntervalMs;
27
27
  private readonly peerTimeoutMs;
28
- private readonly peers;
28
+ readonly peers: Map<string, ServiceLivenessConnection>;
29
29
  private nextProbeId;
30
30
  /**
31
31
  * The earliest probe or timeout among admitted peers. Received traffic only
@@ -6,6 +6,7 @@ export interface ServiceMailboxDispatchOwner {
6
6
  prepareDispatch(record: ServiceMailboxRecord, signal: AbortSignal): boolean | Promise<boolean>;
7
7
  }
8
8
  export interface ServiceMailboxRecord {
9
+ receivedAt?: Date;
9
10
  readonly sourcePreparationOwner?: ServiceMailboxDispatchOwner;
10
11
  readonly ingress?: RawServiceIngressRecord;
11
12
  readonly owner: string;
@@ -42,6 +43,7 @@ export interface ServiceMailboxRelocationSeal {
42
43
  /** Level-triggered queues with one active application claim per owner. */
43
44
  export declare class ServiceMailbox {
44
45
  private readonly onReady?;
46
+ private readonly receivedAtClock;
45
47
  private readonly application;
46
48
  private readonly infrastructure;
47
49
  private readonly relocationSeals;
@@ -49,7 +51,7 @@ export declare class ServiceMailbox {
49
51
  private nextClaimSerial;
50
52
  private nextRelocationSerial;
51
53
  private closed;
52
- constructor(onReady?: ((domain: ServiceMailboxDomain) => void) | undefined);
54
+ constructor(onReady?: ((domain: ServiceMailboxDomain) => void) | undefined, receivedAtClock?: () => Date);
53
55
  releaseClaimedPayload(record: ServiceMailboxRecord): void;
54
56
  tryEnqueue(record: ServiceMailboxRecord): boolean;
55
57
  tryClaim(domain: ServiceMailboxDomain, messageBudget: number, byteBudget: number): ServiceMailboxClaim | undefined;
@@ -4,6 +4,7 @@ exports.ServiceMailbox = void 0;
4
4
  /** Level-triggered queues with one active application claim per owner. */
5
5
  class ServiceMailbox {
6
6
  onReady;
7
+ receivedAtClock;
7
8
  application;
8
9
  infrastructure;
9
10
  relocationSeals = new Map();
@@ -11,8 +12,9 @@ class ServiceMailbox {
11
12
  nextClaimSerial = 1n;
12
13
  nextRelocationSerial = 1n;
13
14
  closed = false;
14
- constructor(onReady) {
15
+ constructor(onReady, receivedAtClock = () => new Date()) {
15
16
  this.onReady = onReady;
17
+ this.receivedAtClock = receivedAtClock;
16
18
  // Host ApplicationJobQueue permits own ordinary-record admission. The
17
19
  // Framework-owned payload lifetime does not retain Core HWM credit. A
18
20
  // second mailbox cap would reject already-admitted work before the shared
@@ -32,6 +34,7 @@ class ServiceMailbox {
32
34
  const bytes = retainedBytes(record);
33
35
  if (this.closed)
34
36
  return false;
37
+ record.receivedAt ??= this.receivedAtClock();
35
38
  if (record.domain === 'application') {
36
39
  if (this.relocatedOwners.has(record.owner))
37
40
  return false;
@@ -214,6 +214,7 @@ export interface ZLinkMessageFollowOrigin {
214
214
  readonly target?: import('./service-stateful-wire-codec').ServiceActorRouteFence;
215
215
  }
216
216
  export interface ReceiveRecord {
217
+ readonly receivedAt?: Date;
217
218
  /** Resolve record prerequisites in owner order, outside an application handler turn. */
218
219
  readonly prepareDispatch?: (this: ReceiveRecord, signal: AbortSignal) => boolean | Promise<boolean>;
219
220
  readonly statefulMailboxRecord?: import('./service-mailbox').ServiceMailboxRecord;
@@ -234,6 +235,7 @@ export interface ReceiveRecord {
234
235
  readonly kindData: ReceiveKindData | null;
235
236
  readonly terminalResult: number;
236
237
  readonly failureErrno: number;
238
+ readonly failureMessage?: string;
237
239
  readonly parts: Message[];
238
240
  /** Returns whether the local operation can still accept a lifecycle reply. */
239
241
  readonly isPending?: () => boolean;
@@ -251,7 +253,7 @@ export interface ReceiveRecord {
251
253
  reply(parts: MessageLike | readonly MessageLike[], flags?: number): SubmitResult;
252
254
  replyActorJoin(joinResult: number, parts: MessageLike | readonly MessageLike[], flags?: number, replyContentType?: string): SubmitResult;
253
255
  /** Emits a typed command-20 terminal for a lifecycle request before admission. */
254
- replyFailure?(terminalResult: number, failureCode: number): SubmitResult;
256
+ replyFailure?(terminalResult: number, failureCode: number, tail?: import('./service-stateful-wire-codec').ServiceStatefulReplyTail): SubmitResult;
255
257
  }
256
258
  export interface ClaimReceiveResult {
257
259
  readonly ok: boolean;
@@ -119,6 +119,8 @@ export declare class ServiceStatefulRegistry {
119
119
  installSessionBinding(binding: ServiceSessionBinding): ServiceSessionBinding;
120
120
  binding(actor: ServiceActorRef): ServiceSessionBinding | undefined;
121
121
  private bindingCore;
122
+ /** The Actor owner admits each relayed Session sequence once per binding. */
123
+ acceptBoundSessionSequence(actor: ServiceActorRef, expectedBindingGeneration: bigint, sequence: bigint): boolean;
122
124
  unbindSession(actor: ServiceActorRef, expectedBindingGeneration: bigint, expectedSessionRid?: string, expectedSessionOwnerNodeRid?: string): boolean;
123
125
  validateBoundSession(actor: ServiceActorRef, expectedBindingGeneration: bigint): ServiceSessionBinding;
124
126
  reserve(objectKind: ServiceObjectReservation['objectKind'], key: string, stableType: string, attempt: bigint): ServiceReservationResult;
@@ -246,7 +246,7 @@ class ServiceStatefulRegistry {
246
246
  });
247
247
  requireText(binding.sessionOwnerNodeRid, 'sessionOwnerNodeRid');
248
248
  requirePositive(binding.bindingGeneration, 'bindingGeneration');
249
- const current = this.bindings.get(actorKey(binding.actor));
249
+ const current = this.bindings.get(actorKey(binding.actor))?.binding;
250
250
  if (current !== undefined &&
251
251
  current.sessionOwnerNodeRid === binding.sessionOwnerNodeRid &&
252
252
  current.sessionOwnerNodeGeneration === binding.sessionOwnerNodeGeneration &&
@@ -257,21 +257,34 @@ class ServiceStatefulRegistry {
257
257
  throw new ServiceStaleGenerationError('binding', binding.actor.actorId);
258
258
  }
259
259
  const accepted = Object.freeze({ ...binding });
260
- this.bindings.set(actorKey(binding.actor), accepted);
260
+ this.bindings.set(actorKey(binding.actor), { binding: accepted, lastAcceptedSequence: 0n });
261
261
  return accepted;
262
262
  }
263
263
  binding(actor) {
264
264
  return this.bindingCore(actor);
265
265
  }
266
266
  bindingCore(actor) {
267
- const binding = this.bindings.get(actorKey(actor));
267
+ const binding = this.bindings.get(actorKey(actor))?.binding;
268
268
  return binding?.actor.generation === actor.generation ? binding : undefined;
269
269
  }
270
+ /** The Actor owner admits each relayed Session sequence once per binding. */
271
+ acceptBoundSessionSequence(actor, expectedBindingGeneration, sequence) {
272
+ const entry = this.bindings.get(actorKey(actor));
273
+ if (entry === undefined ||
274
+ entry.binding.actor.generation !== actor.generation ||
275
+ entry.binding.bindingGeneration !== expectedBindingGeneration ||
276
+ sequence <= entry.lastAcceptedSequence) {
277
+ return false;
278
+ }
279
+ entry.lastAcceptedSequence = sequence;
280
+ return true;
281
+ }
270
282
  unbindSession(actor, expectedBindingGeneration, expectedSessionRid, expectedSessionOwnerNodeRid) {
271
283
  const key = actorKey(actor);
272
- const binding = this.bindings.get(key);
273
- if (binding === undefined)
284
+ const entry = this.bindings.get(key);
285
+ if (entry === undefined)
274
286
  return false;
287
+ const binding = entry.binding;
275
288
  if (binding.bindingGeneration !== expectedBindingGeneration ||
276
289
  (expectedSessionRid !== undefined &&
277
290
  !(0, routing_id_1.routingIdsEqual)(binding.sessionRid, expectedSessionRid)) ||
@@ -387,14 +400,17 @@ class ServiceStatefulRegistry {
387
400
  }
388
401
  updateBindingMembership(actor) {
389
402
  const key = actorKey(actor.ref);
390
- const binding = this.bindings.get(key);
391
- if (binding === undefined)
403
+ const entry = this.bindings.get(key);
404
+ if (entry === undefined)
392
405
  return;
393
- this.bindings.set(key, Object.freeze({
394
- ...binding,
395
- actor: actor.ref,
396
- membershipEpoch: actor.membershipEpoch
397
- }));
406
+ this.bindings.set(key, {
407
+ binding: Object.freeze({
408
+ ...entry.binding,
409
+ actor: actor.ref,
410
+ membershipEpoch: actor.membershipEpoch
411
+ }),
412
+ lastAcceptedSequence: entry.lastAcceptedSequence
413
+ });
398
414
  }
399
415
  }
400
416
  exports.ServiceStatefulRegistry = ServiceStatefulRegistry;
@@ -3,12 +3,13 @@ import type { RequestRuntime } from './request-runtime';
3
3
  import { inspectRuntime } from '../internal-observation';
4
4
  import type { ZLinkAuthoritySnapshot } from '../locations/internal-location-contracts';
5
5
  import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
6
- import type { RawServiceMeshRuntime } from './raw-service-mesh-runtime';
6
+ import { type ActorJoin28 } from '../protocol/service_wire_pilot_codec.generated';
7
+ import type { RawServiceIngressRecord, RawServiceMeshRuntime, RawServicePumpResult } from './raw-service-mesh-runtime';
7
8
  import type { ServiceInstanceActivationRecoveryEnvelope } from './service-instance-activation-recovery-codec';
8
9
  import type { ServiceMailboxRecord } from './service-mailbox';
9
10
  import { type ReceiveKindData, type ServiceStreamSessionBinding, type StreamSessionActorAuthorityFence } from './service-runtime-contracts';
10
- import { ServiceStatefulRegistry, type ServiceActorRef, type ServiceActorState, type ServiceSessionBinding, type ServiceSpotRef, type ServiceSpotState } from './service-stateful-registry';
11
- import { type ServiceActorCreateRecord, type ServiceActorRouteFence, type ServiceDirectSpotRouteFence, type ServiceInstanceActivationTarget, type ServiceInstanceColdActivationTarget, type ServiceInstanceRouteFence, type ServiceRetiredBoundSessionRouteFence, type ServiceStatefulReplyTail, type ServiceStatefulWireRecord, type ServiceUserSpotCloseRecord, type ServiceUserSpotCreateRecord } from './service-stateful-wire-codec';
11
+ import { ServiceStatefulRegistry, ServiceTerminalOperationRegistry, type ServiceActorRef, type ServiceActorState, type ServiceSessionBinding, type ServiceSpotRef, type ServiceSpotState } from './service-stateful-registry';
12
+ import { type ServiceActorCreateRecord, type ServiceActorRouteFence, type ServiceDirectSpotRouteFence, type ServiceInstanceActivationTarget, type ServiceInstanceColdActivationTarget, type ServiceInstanceRouteFence, type ServiceRetiredBoundSessionRouteFence, type ServiceSpotRouteFence, type ServiceStatefulReplyTail, type ServiceStatefulWireRecord, type ServiceUserSpotCloseRecord, type ServiceUserSpotCreateRecord } from './service-stateful-wire-codec';
12
13
  import { type ServiceApplicationPayload } from './service-wire-m6a-codec';
13
14
  export interface ServiceSpotMessageFollowSeal {
14
15
  readonly key: string;
@@ -17,6 +18,7 @@ export interface ServiceSpotMessageFollowSeal {
17
18
  export interface ServiceStatefulResult {
18
19
  readonly terminalResult: number;
19
20
  readonly failureCode: number;
21
+ readonly failureMessage?: string;
20
22
  readonly payload?: ServiceApplicationPayload;
21
23
  readonly kindData?: ReceiveKindData;
22
24
  readonly applicationMetadata?: Buffer;
@@ -248,7 +250,7 @@ export declare class ServiceStatefulRuntime {
248
250
  }>;
249
251
  private static readonly detachedIngressScope;
250
252
  readonly registry: ServiceStatefulRegistry;
251
- private readonly operations;
253
+ readonly operations: ServiceTerminalOperationRegistry<ServiceStatefulResult, bigint>;
252
254
  private readonly sessionDeliveries;
253
255
  /**
254
256
  * A local Actor owner can replace its own session binding before the
@@ -284,7 +286,12 @@ export declare class ServiceStatefulRuntime {
284
286
  private dispatchErrors?;
285
287
  private sessionOwnerDescriptors?;
286
288
  private dispatchErrorMeshName?;
287
- private readonly admittedUserSpotOperations;
289
+ readonly admittedUserSpotOperations: Map<string, {
290
+ readonly request: string;
291
+ readonly replayExpiresAtMs: number;
292
+ readonly result: Promise<ServiceUserSpotOperationResult>;
293
+ settled: boolean;
294
+ }>;
288
295
  private readonly pendingInstanceActivations;
289
296
  private closed;
290
297
  constructor(raw: RawServiceMeshRuntime, nodeRid: string, nodeGeneration: bigint, onPendingOperationsChanged?: () => void, requests?: RequestRuntime | undefined);
@@ -397,31 +404,56 @@ export declare class ServiceStatefulRuntime {
397
404
  close(): void;
398
405
  cancelPendingOperations(reason: unknown): void;
399
406
  private ingress;
400
- private handleIngress;
407
+ handleIngress(ingress: RawServiceIngressRecord, record: ServiceStatefulWireRecord, payloadFrame: Buffer | undefined, metadataFrame?: Buffer, canonicalActorJoin?: ActorJoin28, canonicalActorJoinPayload?: ServiceApplicationPayload): Promise<RawServicePumpResult>;
401
408
  private enqueueInstanceSpot;
402
409
  private needsInstanceApplicationMaterialization;
403
- private enqueueActivatedInstanceSpot;
404
- private continueMissingInstanceActivation;
410
+ enqueueActivatedInstanceSpot(ingress: RawServiceIngressRecord, record: Extract<ServiceStatefulWireRecord, {
411
+ readonly kind: 'instanceSpot';
412
+ }>, payloadFrame: Buffer, spot: Pick<ServiceSpotState, 'ref'>, localReply?: NonNullable<ServiceStatefulMailboxData['reply']>, onTerminalCompletion?: NonNullable<ServiceStatefulMailboxData['onTerminalCompletion']>, metadataFrame?: Buffer, onHandlerTurnStarted?: () => void, applicationTargetOverride?: ServiceInstanceApplicationTarget): RawServicePumpResult;
413
+ continueMissingInstanceActivation(ingress: RawServiceIngressRecord, record: Extract<ServiceStatefulWireRecord, {
414
+ readonly kind: 'instanceSpot';
415
+ readonly activation: 'missing';
416
+ }>, payloadFrame: Buffer, localReply?: NonNullable<ServiceStatefulMailboxData['reply']>, metadataFrame?: Buffer): Promise<void>;
405
417
  private continueReadyInstanceMaterialization;
406
418
  /**
407
419
  * Spot messaging §4 step 12 with §7 step 1: a Close waits for the first terminal
408
420
  * record only of a message whose handler turn started before it; a message queued
409
421
  * behind that Close is not counted, so it cannot hold the Close it follows.
410
422
  */
411
- private activationTerminalCompletion;
412
- private instanceApplicationTerminalCompletion;
423
+ activationTerminalCompletion(target: ServiceInstanceActivationTarget, route: ServiceInstanceRouteFence): {
424
+ readonly onTerminalCompletion: () => Promise<void>;
425
+ readonly onHandlerTurnStarted: () => void;
426
+ };
427
+ instanceApplicationTerminalCompletion(target: ServiceInstanceApplicationTarget): (() => Promise<void>) | undefined;
413
428
  private instanceApplicationTarget;
414
429
  private beginInstanceAuthorityOperation;
415
430
  private completeInstanceAuthorityOperation;
416
431
  private completeInstanceApplicationWaitersIfQuiescent;
417
- private finishMissingInstanceActivationFailure;
432
+ finishMissingInstanceActivationFailure(ingress: RawServiceIngressRecord, record: Extract<ServiceStatefulWireRecord, {
433
+ readonly kind: 'instanceSpot';
434
+ readonly activation: 'missing';
435
+ }>, error: unknown, localReply?: NonNullable<ServiceStatefulMailboxData['reply']>, payloadFrame?: Buffer): void;
418
436
  private requireInstanceActivation;
419
437
  private requireReadyInstanceApplicationRoute;
420
- private validateInstanceIngress;
438
+ validateInstanceIngress(ingress: RawServiceIngressRecord, record: Extract<ServiceStatefulWireRecord, {
439
+ readonly kind: 'instanceSpot';
440
+ }>): void;
421
441
  private activateMissingInstance;
422
442
  private localReadyInstance;
423
- private activateMissingInstanceAsync;
424
- private runMissingInstanceActivation;
443
+ activateMissingInstanceAsync(record: Extract<ServiceStatefulWireRecord, {
444
+ readonly kind: 'instanceSpot';
445
+ readonly activation: 'missing';
446
+ }>, payloadFrame: Buffer, metadataFrame: Buffer | undefined, admit: (activation: {
447
+ readonly spot: Pick<ServiceSpotState, 'ref'>;
448
+ readonly route: ServiceInstanceRouteFence;
449
+ }) => void): Promise<unknown>;
450
+ runMissingInstanceActivation(record: Extract<ServiceStatefulWireRecord, {
451
+ readonly kind: 'instanceSpot';
452
+ readonly activation: 'missing';
453
+ }>, payloadFrame: Buffer, metadataFrame?: Buffer): Promise<{
454
+ readonly spot: Pick<ServiceSpotState, 'ref'>;
455
+ readonly route: ServiceInstanceRouteFence;
456
+ }>;
425
457
  private runMissingInstanceActivationCore;
426
458
  private acceptsLocalMissingInstancePlacement;
427
459
  private reconcileReadyInstanceProjection;
@@ -447,7 +479,7 @@ export declare class ServiceStatefulRuntime {
447
479
  private traceRejectedBoundSessionSend;
448
480
  private prepareRequestSource;
449
481
  prepareDispatch(record: ServiceMailboxRecord, signal: AbortSignal): boolean | Promise<boolean>;
450
- private enqueueApplicationFrame;
482
+ enqueueApplicationFrame(ingress: RawServiceIngressRecord, owner: string, payloadFrame: Buffer, stateful: ServiceStatefulMailboxData): RawServicePumpResult;
451
483
  private enqueueLifecycleFrame;
452
484
  private enqueueFrame;
453
485
  private enqueueLogicalMulticast;
@@ -465,13 +497,13 @@ export declare class ServiceStatefulRuntime {
465
497
  private submitOneWay;
466
498
  private submitRequest;
467
499
  private submitActorJoin;
468
- private submitLocalRequest;
500
+ submitLocalRequest(ingress: RawServiceIngressRecord, pending: ServiceStatefulPendingOperation, operationKind: 'spotRequest' | 'actorRequest' | 'actorLookup' | 'actorDestroy' | 'actorJoin' | 'streamBind' | 'streamUnbind' | 'instanceSpotRequest', actor?: ServiceActorRef, deadlineUnixMs?: bigint, receiver?: import('./operation-registry').OperationObserver<unknown, unknown>): Promise<void>;
469
501
  private completeRemoteReply;
470
502
  private resultFromReply;
471
- private validateActorFence;
472
- private validateActorMessageFence;
503
+ validateActorFence(fence: ServiceActorRouteFence): ServiceActorState;
504
+ validateActorMessageFence(fence: ServiceActorRouteFence): ServiceActorState;
473
505
  private validateSpotFence;
474
- private validateDirectSpotFence;
506
+ validateDirectSpotFence(fence: ServiceSpotRouteFence): ServiceSpotState;
475
507
  private holdOrRelaySpotMessage;
476
508
  private restoreSpotMessageFollowRecord;
477
509
  private drainSpotMessageFollow;
@@ -33,6 +33,7 @@ const service_stateful_registry_1 = require("./service-stateful-registry");
33
33
  const service_stateful_wire_codec_1 = require("./service-stateful-wire-codec");
34
34
  const service_wire_binary_primitives_1 = require("./service-wire-binary-primitives");
35
35
  const service_wire_constants_generated_1 = require("./service-wire-constants.generated");
36
+ const payload_slab_1 = require("../../contracts/Common/payload-slab");
36
37
  const service_wire_m6a_codec_1 = require("./service-wire-m6a-codec");
37
38
  const MAX_NODE_TIMER_DELAY_MS = 2_147_483_647;
38
39
  const SPOT_MOVING = 34;
@@ -1339,7 +1340,7 @@ class ServiceStatefulRuntime {
1339
1340
  ? record.parts.length >= 2
1340
1341
  ? record.parts[record.parts.length - 1]
1341
1342
  : undefined
1342
- : (0, service_wire_m6a_codec_1.encodeApplicationPayload)(canonicalPayload);
1343
+ : (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)(canonicalPayload);
1343
1344
  try {
1344
1345
  return await this.handleIngress(record, decoded, payloadFrame, metadataFrame, canonicalActorJoin, canonicalPayload);
1345
1346
  }
@@ -1455,6 +1456,48 @@ class ServiceStatefulRuntime {
1455
1456
  binding.sessionOwnerNodeRid !== ingress.sourceRoutingId) {
1456
1457
  throw new service_stateful_registry_1.ServiceStaleGenerationError('binding', record.target.actor.actorId);
1457
1458
  }
1459
+ if (!this.registry.acceptBoundSessionSequence(record.target.actor, record.boundSession.bindingGeneration, record.boundSession.sequence)) {
1460
+ const error = new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.InvalidOperation, 'Bound Session relay sequence does not increase.');
1461
+ const reporter = this.dispatchErrors;
1462
+ if (record.kind === 'actorRequest') {
1463
+ if (reporter?.captureEnabled() === true) {
1464
+ reporter.report({
1465
+ surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.StreamSession,
1466
+ messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Request,
1467
+ reason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget,
1468
+ action: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.ReplyError,
1469
+ meshName: this.dispatchErrorMeshName,
1470
+ sourceRid: ingress.sourceRoutingId,
1471
+ actorId: record.target.actor.actorId,
1472
+ correlationId: record.correlation?.toString(),
1473
+ error
1474
+ });
1475
+ }
1476
+ const result = failure(error);
1477
+ this.replyWire(ingress, record.correlation, result.terminalResult, result.failureCode);
1478
+ return 'infrastructure';
1479
+ }
1480
+ const tracePoint = (0, message_flow_1.flowIfEnabled)(reporter?.flow, ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped);
1481
+ if (tracePoint !== undefined) {
1482
+ tracePoint.trace({
1483
+ outcome: ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped,
1484
+ surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.StreamSession,
1485
+ messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
1486
+ packetName: (0, service_wire_m6a_codec_1.decodeApplicationPayloadView)(payloadFrame).packetName,
1487
+ ...(this.dispatchErrorMeshName === undefined
1488
+ ? {}
1489
+ : { meshName: this.dispatchErrorMeshName }),
1490
+ ...(record.correlation === undefined
1491
+ ? {}
1492
+ : { correlationId: record.correlation.toString() }),
1493
+ sourceRid: ingress.sourceRoutingId,
1494
+ actorId: record.target.actor.actorId,
1495
+ streamSessionId: record.boundSession.sessionRid,
1496
+ errorReason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget
1497
+ });
1498
+ }
1499
+ return 'application';
1500
+ }
1458
1501
  }
1459
1502
  return this.enqueueApplicationFrame(ingress, `actor:${actor.ref.actorId}\0${actor.ref.generation}`, payloadFrame, {
1460
1503
  receiveKind: record.kind === 'actorSend' ? service_runtime_contracts_1.ReceiveKind.ActorSend : service_runtime_contracts_1.ReceiveKind.ActorRequest,
@@ -2273,7 +2316,7 @@ class ServiceStatefulRuntime {
2273
2316
  }
2274
2317
  })
2275
2318
  };
2276
- const applicationFrame = payloadFrame ?? (0, service_wire_m6a_codec_1.encodeApplicationPayload)(emptyPayload());
2319
+ const applicationFrame = payloadFrame ?? (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)(emptyPayload());
2277
2320
  return this.enqueueLifecycleFrame(ingress, `spot:${record.target.spot.spotId}`, applicationFrame, {
2278
2321
  receiveKind: service_runtime_contracts_1.ReceiveKind.SpotControl,
2279
2322
  operationKind: service_runtime_contracts_1.OperationKind.ActorJoin,
@@ -2694,7 +2737,7 @@ class ServiceStatefulRuntime {
2694
2737
  async enqueueLogicalMulticast(channelName, topic, sourceSpotId, payload, sourceNodeRid = this.nodeRid) {
2695
2738
  const applicationJobOwner = await this.raw.reserveLocalIngress();
2696
2739
  try {
2697
- await this.enqueueLogicalMulticastFrame(channelName, topic, sourceSpotId, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload), sourceNodeRid, applicationJobOwner);
2740
+ await this.enqueueLogicalMulticastFrame(channelName, topic, sourceSpotId, (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)(payload), sourceNodeRid, applicationJobOwner);
2698
2741
  }
2699
2742
  finally {
2700
2743
  applicationJobOwner.close();
@@ -3046,6 +3089,7 @@ class ServiceStatefulRuntime {
3046
3089
  async submitOneWay(targetNodeRid, parts) {
3047
3090
  this.requireOpen();
3048
3091
  if (targetNodeRid === this.nodeRid) {
3092
+ parts = copyRetainedServiceFrames(parts);
3049
3093
  const applicationJobOwner = await this.raw.reserveLocalIngress();
3050
3094
  if ((0, execution_1.captureZLinkExecutionTurn)() !== undefined) {
3051
3095
  // The sender is inside a Spot serial turn. Running ingress inline
@@ -3107,6 +3151,7 @@ class ServiceStatefulRuntime {
3107
3151
  }
3108
3152
  submitRequest(pending, targetNodeRid, parts, timeoutMs, operationKind, actor, onAdmitted, receiver) {
3109
3153
  if (targetNodeRid === this.nodeRid) {
3154
+ parts = copyRetainedServiceFrames(parts);
3110
3155
  const deadlineUnixMs = BigInt(Date.now() + Math.max(0, timeoutMs));
3111
3156
  const capacityStop = new AbortController();
3112
3157
  void pending.promise.then(() => capacityStop.abort(), () => capacityStop.abort());
@@ -3231,7 +3276,7 @@ class ServiceStatefulRuntime {
3231
3276
  ? ingress.parts.length < 2
3232
3277
  ? undefined
3233
3278
  : ingress.parts[1]
3234
- : (0, service_wire_m6a_codec_1.encodeApplicationPayload)({
3279
+ : (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)({
3235
3280
  packetName: canonicalActorJoin.payload.packetName,
3236
3281
  contentType: canonicalActorJoin.payload.contentType,
3237
3282
  payload: Buffer.from(canonicalActorJoin.payload.payload)
@@ -3323,7 +3368,7 @@ class ServiceStatefulRuntime {
3323
3368
  currentMembershipEpoch: (previous?.membershipEpoch ?? 0n) + 1n,
3324
3369
  resultCode: 0
3325
3370
  };
3326
- this.enqueueLifecycleFrame(ingress, `spot:${decoded.target.spot.spotId}`, payloadFrame ?? (0, service_wire_m6a_codec_1.encodeApplicationPayload)(emptyPayload()), {
3371
+ this.enqueueLifecycleFrame(ingress, `spot:${decoded.target.spot.spotId}`, payloadFrame ?? (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)(emptyPayload()), {
3327
3372
  receiveKind: service_runtime_contracts_1.ReceiveKind.SpotControl,
3328
3373
  operationKind: service_runtime_contracts_1.OperationKind.ActorJoin,
3329
3374
  correlation: decoded.correlation,
@@ -3481,6 +3526,7 @@ class ServiceStatefulRuntime {
3481
3526
  return {
3482
3527
  terminalResult,
3483
3528
  failureCode,
3529
+ ...(tail?.kind === 'streamBindRejected' ? { failureMessage: tail.rejectionMessage } : {}),
3484
3530
  ...(payload === undefined ? {} : { payload }),
3485
3531
  ...(tail?.kind === 'streamBind'
3486
3532
  ? {
@@ -4219,6 +4265,9 @@ function emptyPayload() {
4219
4265
  payload: Buffer.alloc(0)
4220
4266
  };
4221
4267
  }
4268
+ function copyRetainedServiceFrames(parts) {
4269
+ return parts.map((part) => (0, payload_slab_1.copyPayloadBytes)(part));
4270
+ }
4222
4271
  /**
4223
4272
  * The generated command-28 decoder is the canonical wire authority. Keep
4224
4273
  * the stateful mailbox's internal record shape as an adapter only; it must
@@ -515,6 +515,9 @@ export type ServiceStatefulReplyTail = {
515
515
  readonly kind: 'streamBind';
516
516
  readonly bindingGeneration: bigint;
517
517
  readonly authorityOwnerGeneration: bigint;
518
+ } | {
519
+ readonly kind: 'streamBindRejected';
520
+ readonly rejectionMessage: string;
518
521
  } | {
519
522
  readonly kind: 'userSpotCreate';
520
523
  readonly createResult: 'existing' | 'created' | 'rejected';
@@ -40,6 +40,7 @@ const service_wire_codec_generated_1 = require("../protocol/service_wire_codec.g
40
40
  const stream_wire_1 = require("@zlink-systems/stream-wire");
41
41
  const InternalDefaults_1 = require("../../contracts/Configuration/InternalDefaults");
42
42
  const framework_errors_internal_1 = require("../framework-errors-internal");
43
+ const runtime_values_1 = require("../backend/runtime-values");
43
44
  const service_wire_codec_generated_2 = require("../protocol/service_wire_codec.generated");
44
45
  const service_wire_pilot_codec_generated_1 = require("../protocol/service_wire_pilot_codec.generated");
45
46
  const routing_id_2 = require("../routing-id");
@@ -1226,6 +1227,12 @@ function decodeStatefulReply(frame, expectedCorrelation, operationKind, hasPaylo
1226
1227
  };
1227
1228
  }
1228
1229
  }
1230
+ else if (operationKind === 'streamBind' && terminalResult === runtime_values_1.RequestResult.Rejected) {
1231
+ tail = {
1232
+ kind: 'streamBindRejected',
1233
+ rejectionMessage: reader.text16('rejectionMessage')
1234
+ };
1235
+ }
1229
1236
  reader.end();
1230
1237
  return {
1231
1238
  correlation,
@@ -1275,6 +1282,8 @@ function encodeReplyTail(tail) {
1275
1282
  }
1276
1283
  case 'streamBind':
1277
1284
  return concat(u64(tail.bindingGeneration), u64(tail.authorityOwnerGeneration));
1285
+ case 'streamBindRejected':
1286
+ return concat(text16(tail.rejectionMessage, 'rejectionMessage'));
1278
1287
  case 'userSpotCreate':
1279
1288
  return concat(Buffer.of(['existing', 'created', 'rejected'].indexOf(tail.createResult) + 1), rid(tail.spotId, 'spotId'), u64(tail.objectGeneration));
1280
1289
  case 'userSpotClose':
@@ -1385,6 +1394,12 @@ function rid(value, name) {
1385
1394
  function text8(value, name) {
1386
1395
  return sized8(value, name);
1387
1396
  }
1397
+ function text16(value, name) {
1398
+ const bytes = (0, service_wire_binary_primitives_1.encodeCanonicalServiceWireText)(value, name, 0xffff, outOfRange);
1399
+ if (bytes.byteLength === 0)
1400
+ fail(`${name} must not be empty.`);
1401
+ return concat(u16(bytes.byteLength), bytes);
1402
+ }
1388
1403
  function optionalRid(value) {
1389
1404
  return value === undefined ? Buffer.of(0) : rid(value, 'sourceSpotId');
1390
1405
  }
@@ -110,3 +110,4 @@ export declare const ServiceWireExactTerminalByFailureCode: {
110
110
  readonly 35: 105;
111
111
  };
112
112
  export declare const isValidServiceWireTerminalFailure: (terminal: number, failureCode: number) => boolean;
113
+ export declare function recoverRequestCorrelation(bytes: Uint8Array): bigint | undefined;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isValidServiceWireTerminalFailure = exports.ServiceWireExactTerminalByFailureCode = exports.ServiceWireBoundaryTerminalResults = exports.ServiceWireFrameworkErrorCode = exports.ServiceWireFlag = exports.ServiceWireCommand = exports.SERVICE_FRAMEWORK_MULTIPART_CONTENT_TYPE = exports.SERVICE_FRAMEWORK_MULTIPART_PACKET_NAME = exports.SERVICE_WIRE_REQUIRED_CAPABILITY = exports.SERVICE_WIRE_METADATA_BYTES = exports.SERVICE_WIRE_MAJOR = exports.SERVICE_WIRE_MAGIC = void 0;
4
+ exports.recoverRequestCorrelation = recoverRequestCorrelation;
4
5
  // Generated from service-wire-v1.schema.json. Do not edit.
5
6
  exports.SERVICE_WIRE_MAGIC = [90, 77];
6
7
  exports.SERVICE_WIRE_MAJOR = 1;
@@ -132,3 +133,14 @@ const isValidServiceWireTerminalFailure = (terminal, failureCode) => {
132
133
  return exports.ServiceWireExactTerminalByFailureCode[failureCode] === terminal;
133
134
  };
134
135
  exports.isValidServiceWireTerminalFailure = isValidServiceWireTerminalFailure;
136
+ function recoverRequestCorrelation(bytes) {
137
+ if (bytes.length < 13
138
+ || bytes[0] !== 90 || bytes[1] !== 77
139
+ || bytes[2] !== 1
140
+ || ![17, 19, 22, 25, 26, 27, 28, 38, 47, 48, 49, 54].includes(bytes[3]))
141
+ return undefined;
142
+ let correlation = 0n;
143
+ for (let index = 5; index < 13; ++index)
144
+ correlation = (correlation << 8n) | BigInt(bytes[index]);
145
+ return correlation === 0n ? undefined : correlation;
146
+ }
@@ -78,6 +78,8 @@ export declare function decodeChannelRequestHeader(frame: Uint8Array, header?: S
78
78
  export declare function encodeReplyHeader(correlation: bigint, terminalResult?: number, failureCode?: number, tail?: Uint8Array): Buffer;
79
79
  export declare function decodeReplyHeader(frame: Uint8Array): ServiceReplyHeader;
80
80
  export declare function encodeApplicationPayload(payload: ServiceApplicationPayload): Buffer;
81
+ /** Encodes a retained internal application frame with its own backing store. */
82
+ export declare function encodeOwnedApplicationPayload(payload: ServiceApplicationPayload): Buffer;
81
83
  /**
82
84
  * Encodes the framework multipart payload directly into its M6A application
83
85
  * frame. The caller supplies borrowed message views; this function performs