@zlink-systems/framework 0.29.0 → 0.30.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 (168) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Configuration/Builders.d.ts +1 -2
  3. package/dist/contracts/Configuration/Configs.d.ts +0 -5
  4. package/dist/contracts/Configuration/RegistrationBuilders.js +6 -4
  5. package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -6
  6. package/dist/contracts/Configuration/RegistrationValidators.js +4 -4
  7. package/dist/contracts/Configuration/index.d.ts +1 -1
  8. package/dist/internal.d.ts +1 -0
  9. package/dist/internal.js +3 -1
  10. package/dist/runtime/actors/actor-handoff.d.ts +3 -2
  11. package/dist/runtime/actors/actor-handoff.js +9 -4
  12. package/dist/runtime/actors/actor-runtime-contracts.d.ts +1 -0
  13. package/dist/runtime/actors/index.d.ts +2 -0
  14. package/dist/runtime/actors/index.js +21 -6
  15. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +3 -1
  16. package/dist/runtime/application-jobs/application-ingress-record-owner.js +6 -1
  17. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +3 -1
  18. package/dist/runtime/application-jobs/application-job-queue-scope.js +5 -0
  19. package/dist/runtime/application-jobs/contracts.d.ts +5 -1
  20. package/dist/runtime/backend/contracts/index.d.ts +10 -19
  21. package/dist/runtime/backend/mesh-completion-table.d.ts +6 -4
  22. package/dist/runtime/backend/mesh-completion-table.js +28 -18
  23. package/dist/runtime/backend/mesh-dispatch-pump.js +21 -25
  24. package/dist/runtime/backend/node/node-backend-adapter-factory.js +6 -1
  25. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +3 -3
  26. package/dist/runtime/backend/node/node-backend-adapter-support.js +24 -29
  27. package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -9
  28. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
  29. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -6
  30. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +10 -12
  31. package/dist/runtime/backend/node/node-raw-mesh-backend.js +64 -98
  32. package/dist/runtime/backend/node/node-socket-backend-adapter.js +10 -5
  33. package/dist/runtime/backend/raw-binding-port.d.ts +9 -2
  34. package/dist/runtime/channels/channel-receive-loops.js +3 -3
  35. package/dist/runtime/channels/channel-runtime-lifecycle.js +2 -1
  36. package/dist/runtime/channels/channel-socket-registry.d.ts +10 -3
  37. package/dist/runtime/channels/channel-socket-registry.js +59 -82
  38. package/dist/runtime/diagnostics/message-flow.d.ts +1 -0
  39. package/dist/runtime/diagnostics/message-flow.js +3 -3
  40. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +15 -1
  41. package/dist/runtime/foundation/raw-service-mesh-runtime.js +193 -64
  42. package/dist/runtime/foundation/service-liveness-registry.d.ts +20 -8
  43. package/dist/runtime/foundation/service-liveness-registry.js +57 -44
  44. package/dist/runtime/foundation/service-mailbox.d.ts +3 -0
  45. package/dist/runtime/foundation/service-mailbox.js +2 -1
  46. package/dist/runtime/foundation/service-stateful-runtime.d.ts +7 -6
  47. package/dist/runtime/foundation/service-stateful-runtime.js +60 -41
  48. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +5 -2
  49. package/dist/runtime/foundation/service-stateful-wire-codec.js +43 -61
  50. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -3
  51. package/dist/runtime/foundation/service-topology-registry.js +9 -4
  52. package/dist/runtime/handlers/handler-instance-scope.js +1 -1
  53. package/dist/runtime/host/actor-packet-relay.js +7 -7
  54. package/dist/runtime/host/actor-placement-coordinator.d.ts +2 -1
  55. package/dist/runtime/host/actor-placement-coordinator.js +2 -1
  56. package/dist/runtime/host/actor-runtime-options-factory.d.ts +2 -1
  57. package/dist/runtime/host/actor-runtime-options-factory.js +2 -1
  58. package/dist/runtime/host/application-job-queue.d.ts +11 -4
  59. package/dist/runtime/host/application-job-queue.js +66 -10
  60. package/dist/runtime/host/entry-actor-runtime.js +1 -1
  61. package/dist/runtime/host/index.d.ts +1 -1
  62. package/dist/runtime/host/index.js +18 -13
  63. package/dist/runtime/host/instance-activation-authority.d.ts +2 -1
  64. package/dist/runtime/host/instance-activation-authority.js +89 -25
  65. package/dist/runtime/host/service-relocation-host-runtime.js +2 -2
  66. package/dist/runtime/host/spot-address-transport.d.ts +1 -0
  67. package/dist/runtime/host/spot-address-transport.js +25 -12
  68. package/dist/runtime/host/spot-runtime-options-factory.js +2 -2
  69. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +1 -0
  70. package/dist/runtime/host/user-spot-creation-coordinator.js +1 -0
  71. package/dist/runtime/locations/domain-store-contract.d.ts +3 -2
  72. package/dist/runtime/locations/in-memory-authority-store.d.ts +5 -2
  73. package/dist/runtime/locations/in-memory-authority-store.js +107 -14
  74. package/dist/runtime/locations/in-memory-location-store.d.ts +3 -3
  75. package/dist/runtime/locations/in-memory-location-store.js +31 -13
  76. package/dist/runtime/locations/internal-location-contracts.d.ts +8 -0
  77. package/dist/runtime/locations/location-store-repository.d.ts +4 -2
  78. package/dist/runtime/locations/location-store-repository.js +209 -33
  79. package/dist/runtime/locations/resolvers.d.ts +2 -1
  80. package/dist/runtime/locations/resolvers.js +10 -1
  81. package/dist/runtime/messaging/submission-result.d.ts +1 -1
  82. package/dist/runtime/messaging/submission-result.js +2 -2
  83. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +2 -0
  84. package/dist/runtime/spots/index.d.ts +4 -3
  85. package/dist/runtime/spots/index.js +14 -14
  86. package/dist/runtime/spots/spot-activation.d.ts +2 -1
  87. package/dist/runtime/spots/spot-activation.js +4 -4
  88. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +2 -1
  89. package/dist/runtime/spots/spot-actor-admission-coordinator.js +11 -10
  90. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +4 -1
  91. package/dist/runtime/spots/spot-actor-packet-dispatch.js +7 -5
  92. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  93. package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +1 -0
  94. package/dist/runtime/spots/spot-entry-activation.d.ts +1 -0
  95. package/dist/runtime/spots/spot-entry-activation.js +6 -1
  96. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +3 -1
  97. package/dist/runtime/spots/spot-node-runtime-manager.js +10 -11
  98. package/dist/runtime/spots/spot-routing-internal.d.ts +6 -0
  99. package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -1
  100. package/dist/runtime/spots/spot-serial-executor.d.ts +1 -0
  101. package/dist/runtime/spots/spot-serial-executor.js +10 -1
  102. package/dist/runtime/streams/stream-session-runtime.js +1 -1
  103. package/package.json +3 -3
  104. package/src/contracts/Configuration/Builders.ts +1 -2
  105. package/src/contracts/Configuration/Configs.ts +0 -6
  106. package/src/contracts/Configuration/RegistrationBuilders.ts +8 -12
  107. package/src/contracts/Configuration/RegistrationTypes.ts +1 -6
  108. package/src/contracts/Configuration/RegistrationValidators.ts +9 -10
  109. package/src/contracts/Configuration/index.ts +1 -6
  110. package/src/internal.ts +1 -0
  111. package/src/runtime/actors/actor-handoff.ts +13 -4
  112. package/src/runtime/actors/actor-runtime-contracts.ts +4 -0
  113. package/src/runtime/actors/index.ts +77 -42
  114. package/src/runtime/application-jobs/application-ingress-record-owner.ts +12 -2
  115. package/src/runtime/application-jobs/application-job-queue-scope.ts +6 -1
  116. package/src/runtime/application-jobs/contracts.ts +14 -1
  117. package/src/runtime/backend/contracts/index.ts +10 -18
  118. package/src/runtime/backend/mesh-completion-table.ts +36 -19
  119. package/src/runtime/backend/mesh-dispatch-pump.ts +19 -22
  120. package/src/runtime/backend/node/node-backend-adapter-factory.ts +12 -5
  121. package/src/runtime/backend/node/node-backend-adapter-support.ts +31 -29
  122. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +3 -10
  123. package/src/runtime/backend/node/node-raw-binding-port.ts +19 -10
  124. package/src/runtime/backend/node/node-raw-mesh-backend.ts +81 -111
  125. package/src/runtime/backend/node/node-socket-backend-adapter.ts +12 -8
  126. package/src/runtime/backend/raw-binding-port.ts +12 -2
  127. package/src/runtime/channels/channel-receive-loops.ts +3 -3
  128. package/src/runtime/channels/channel-runtime-lifecycle.ts +7 -1
  129. package/src/runtime/channels/channel-socket-registry.ts +84 -130
  130. package/src/runtime/diagnostics/message-flow.ts +1 -1
  131. package/src/runtime/foundation/raw-service-mesh-runtime.ts +268 -70
  132. package/src/runtime/foundation/service-liveness-registry.ts +68 -59
  133. package/src/runtime/foundation/service-mailbox.ts +7 -1
  134. package/src/runtime/foundation/service-stateful-runtime.ts +109 -77
  135. package/src/runtime/foundation/service-stateful-wire-codec.ts +55 -79
  136. package/src/runtime/foundation/service-topology-registry.ts +13 -7
  137. package/src/runtime/handlers/handler-instance-scope.ts +2 -1
  138. package/src/runtime/host/actor-packet-relay.ts +7 -0
  139. package/src/runtime/host/actor-placement-coordinator.ts +8 -2
  140. package/src/runtime/host/actor-runtime-options-factory.ts +7 -0
  141. package/src/runtime/host/application-job-queue.ts +77 -10
  142. package/src/runtime/host/entry-actor-runtime.ts +1 -0
  143. package/src/runtime/host/index.ts +30 -7
  144. package/src/runtime/host/instance-activation-authority.ts +132 -27
  145. package/src/runtime/host/service-relocation-host-runtime.ts +2 -0
  146. package/src/runtime/host/spot-address-transport.ts +45 -22
  147. package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
  148. package/src/runtime/host/user-spot-creation-coordinator.ts +8 -0
  149. package/src/runtime/locations/domain-store-contract.ts +6 -2
  150. package/src/runtime/locations/in-memory-authority-store.ts +153 -12
  151. package/src/runtime/locations/in-memory-location-store.ts +39 -13
  152. package/src/runtime/locations/internal-location-contracts.ts +10 -0
  153. package/src/runtime/locations/location-store-repository.ts +295 -36
  154. package/src/runtime/locations/resolvers.ts +25 -2
  155. package/src/runtime/messaging/submission-result.ts +2 -2
  156. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +2 -0
  157. package/src/runtime/spots/index.ts +16 -0
  158. package/src/runtime/spots/spot-activation.ts +5 -0
  159. package/src/runtime/spots/spot-actor-admission-coordinator.ts +13 -0
  160. package/src/runtime/spots/spot-actor-packet-dispatch.ts +10 -0
  161. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  162. package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +1 -0
  163. package/src/runtime/spots/spot-entry-activation.ts +7 -0
  164. package/src/runtime/spots/spot-node-runtime-manager.ts +14 -12
  165. package/src/runtime/spots/spot-routing-internal.ts +14 -0
  166. package/src/runtime/spots/spot-runtime-ports.ts +2 -0
  167. package/src/runtime/spots/spot-serial-executor.ts +10 -4
  168. package/src/runtime/streams/stream-session-runtime.ts +1 -1
@@ -1,4 +1,5 @@
1
1
  import type { ApplicationJobRecordLease } from '../application-jobs/application-ingress-record-owner';
2
+ import type { ReceiveRecord } from './service-runtime-contracts';
2
3
  export type ServiceMailboxDomain = 'application' | 'infrastructure';
3
4
  export interface ServiceMailboxRecord {
4
5
  readonly owner: string;
@@ -17,6 +18,8 @@ export interface ServiceMailboxRecord {
17
18
  readonly stateful?: unknown;
18
19
  /** Host queue permit plus its ordinary Framework-owned ingress record. */
19
20
  readonly applicationJob?: ApplicationJobRecordLease;
21
+ /** Unregistered raw-pull terminal, retained by the existing mailbox owner. */
22
+ readonly completion?: ReceiveRecord;
20
23
  }
21
24
  export interface ServiceMailboxClaim {
22
25
  readonly owner: string;
@@ -24,7 +24,8 @@ class ServiceMailbox {
24
24
  record.parts = [];
25
25
  }
26
26
  tryEnqueue(record) {
27
- if (record.owner.length === 0 || record.parts.length === 0) {
27
+ if (record.owner.length === 0 ||
28
+ (record.parts.length === 0 && record.completion === undefined)) {
28
29
  throw new TypeError('Mailbox records require an owner and retained payload.');
29
30
  }
30
31
  const target = this.domain(record.domain);
@@ -5,7 +5,7 @@ import type { ServiceInstanceActivationRecoveryEnvelope } from './service-instan
5
5
  import type { ServiceMailboxRecord } from './service-mailbox';
6
6
  import { type ReceiveKindData, type ServiceStreamSessionBinding, type StreamSessionActorAuthorityFence } from './service-runtime-contracts';
7
7
  import { ServiceStatefulRegistry, type ServiceActorRef, type ServiceActorState, type ServiceSessionBinding, type ServiceSpotRef, type ServiceSpotState } from './service-stateful-registry';
8
- import { type ServiceActorCreateRecord, type ServiceActorRouteFence, type ServiceDirectSpotRouteFence, type ServiceInstanceActivationTarget, type ServiceInstanceRouteFence, type ServiceRetiredBoundSessionRouteFence, type ServiceStatefulReplyTail, type ServiceStatefulWireRecord, type ServiceUserSpotCloseRecord, type ServiceUserSpotCreateRecord } from './service-stateful-wire-codec';
8
+ 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';
9
9
  import { type ServiceApplicationPayload } from './service-wire-m6a-codec';
10
10
  export interface ServiceSpotMessageFollowSeal {
11
11
  readonly key: string;
@@ -148,7 +148,7 @@ export interface ServiceInstanceActivationAuthority {
148
148
  * operation completes; the binding callback itself never blocks.
149
149
  */
150
150
  export interface ServiceAsyncInstanceActivationAuthority {
151
- read(target: ServiceInstanceActivationTarget): Promise<ServiceInstanceAuthorityRead>;
151
+ read(target: ServiceInstanceActivationTarget, instanceIntent?: true, activation?: ServiceInstanceActivationRecoveryEnvelope, acceptsLocal?: (route: ServiceInstanceRouteFence) => boolean): Promise<ServiceInstanceAuthorityRead>;
152
152
  reserve(activation: Omit<ServiceInstanceActivationRecoveryEnvelope, 'targetMeshName'>, signal?: AbortSignal): Promise<ServiceInstanceAuthorityReserve>;
153
153
  resume(target: ServiceInstanceActivationTarget, pending: ServicePendingInstanceActivation): Promise<ServiceInstanceActivationReservation>;
154
154
  commit(target: ServiceInstanceActivationTarget, reservation: ServiceInstanceActivationReservation, spot: ServiceSpotState, deadlineUnixMs?: bigint): Promise<{
@@ -280,11 +280,11 @@ export declare class ServiceStatefulRuntime {
280
280
  readonly bindingGeneration: bigint;
281
281
  }): ServiceStatefulPendingOperation;
282
282
  sendToInstanceSpot(route: ServiceInstanceRouteFence, payload: ServiceApplicationPayload, sourceSpotId?: string, metadataFrame?: Uint8Array, instanceIntent?: boolean): Promise<number>;
283
- sendToMissingInstanceSpot(target: ServiceInstanceActivationTarget, payload: ServiceApplicationPayload, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): Promise<number>;
284
- sendToMissingInstanceSpotFrame(target: ServiceInstanceActivationTarget, payloadFrame: Buffer, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): Promise<number>;
283
+ sendToMissingInstanceSpot(target: ServiceInstanceColdActivationTarget, payload: ServiceApplicationPayload, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): Promise<number>;
284
+ sendToMissingInstanceSpotFrame(target: ServiceInstanceColdActivationTarget, payloadFrame: Buffer, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): Promise<number>;
285
285
  requestToInstanceSpot(route: ServiceInstanceRouteFence, payload: ServiceApplicationPayload, timeoutMs: number, sourceSpotId?: string, metadataFrame?: Uint8Array, instanceIntent?: boolean): ServiceStatefulPendingOperation;
286
- requestToMissingInstanceSpot(target: ServiceInstanceActivationTarget, payload: ServiceApplicationPayload, timeoutMs: number, sourceSpotId?: string, metadataFrame?: Uint8Array): ServiceStatefulPendingOperation;
287
- requestToMissingInstanceSpotFrame(target: ServiceInstanceActivationTarget, payloadFrame: Buffer, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): ServiceStatefulPendingOperation;
286
+ requestToMissingInstanceSpot(target: ServiceInstanceColdActivationTarget, payload: ServiceApplicationPayload, timeoutMs: number, sourceSpotId?: string, metadataFrame?: Uint8Array): ServiceStatefulPendingOperation;
287
+ requestToMissingInstanceSpotFrame(target: ServiceInstanceColdActivationTarget, payloadFrame: Buffer, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): ServiceStatefulPendingOperation;
288
288
  lookupRemoteActor(targetNodeRid: string, actorId: string, timeoutMs: number): ServiceStatefulPendingOperation;
289
289
  destroyActor(actor: ServiceActorRef, timeoutMs: number): ServiceStatefulPendingOperation;
290
290
  joinActor(actor: ServiceActorRef, targetNodeRid: string, targetSpot: ServiceSpotRef, targetSpotGeneration: bigint, payload: ServiceApplicationPayload | undefined, timeoutMs: number): ServiceStatefulPendingOperation;
@@ -324,6 +324,7 @@ export declare class ServiceStatefulRuntime {
324
324
  private requireReadyInstanceApplicationRoute;
325
325
  private validateInstanceIngress;
326
326
  private activateMissingInstance;
327
+ private localReadyInstance;
327
328
  private activateMissingInstanceAsync;
328
329
  private runMissingInstanceActivation;
329
330
  private acceptsLocalMissingInstancePlacement;
@@ -345,7 +345,7 @@ class ServiceStatefulRuntime {
345
345
  const record = {
346
346
  kind: 'instanceSpot',
347
347
  activation: 'missing',
348
- target,
348
+ target: { ...target, targetMeshName: envelope.targetMeshName },
349
349
  sourceNodeGeneration: envelope.sourceNodeGeneration,
350
350
  sourceNodeRid: envelope.sourceNodeRid,
351
351
  ...(envelope.sourceSpotId === undefined ? {} : { sourceSpotId: envelope.sourceSpotId }),
@@ -362,7 +362,7 @@ class ServiceStatefulRuntime {
362
362
  flags: envelope.metadataFrame === undefined ? 0 : service_stateful_wire_codec_1.M6bServiceWireFlag.metadata,
363
363
  sourceRoutingId: envelope.sourceNodeRid,
364
364
  parts: [
365
- (0, service_stateful_wire_codec_1.encodeInstanceSpotActivationHeader)(target, envelope.sourceNodeGeneration, envelope.sourceNodeRid, envelope.sourceSpotId, envelope.operationKind, envelope.operation, envelope.deadlineUnixMs, envelope.replyRouteId, envelope.metadataFrame !== undefined)
365
+ (0, service_stateful_wire_codec_1.encodeInstanceSpotActivationHeader)(record.target, envelope.sourceNodeGeneration, envelope.sourceNodeRid, envelope.sourceSpotId, envelope.operationKind, envelope.operation, envelope.deadlineUnixMs, envelope.replyRouteId, envelope.metadataFrame !== undefined)
366
366
  ],
367
367
  applicationJobOwner
368
368
  }, record, Buffer.from(envelope.applicationPayloadFrame), spot, undefined, terminal.onTerminalCompletion, envelope.metadataFrame === undefined
@@ -754,7 +754,7 @@ class ServiceStatefulRuntime {
754
754
  const timeoutMs = remainingDeadlineMs(deadlineUnixMs);
755
755
  const pending = this.operations.register(timeoutMs);
756
756
  this.submitRequest(pending, target.targetNodeRid, instanceOperationParts([
757
- (0, service_stateful_wire_codec_1.encodeInstanceSpotActivationHeader)(target, this.nodeGeneration, this.nodeRid, sourceSpotId, 'request', { high: this.nodeGeneration, low: pending.id }, deadlineUnixMs, pending.id, metadataFrame !== undefined),
757
+ (0, service_stateful_wire_codec_1.encodeInstanceSpotActivationHeader)(target, this.nodeGeneration, this.nodeRid, sourceSpotId, 'request', { high: operation_identity_1.ZLINK_NATIVE_CORRELATION_OPERATION_NAMESPACE, low: pending.id }, deadlineUnixMs, pending.id, metadataFrame !== undefined),
758
758
  payloadFrame
759
759
  ], metadataFrame), timeoutMs, 'instanceSpotRequest');
760
760
  return pending;
@@ -1436,14 +1436,15 @@ class ServiceStatefulRuntime {
1436
1436
  async continueMissingInstanceActivation(ingress, record, payloadFrame, localReply, metadataFrame) {
1437
1437
  try {
1438
1438
  this.validateInstanceIngress(ingress, record);
1439
- const activation = await this.activateMissingInstanceAsync(record, payloadFrame, metadataFrame);
1440
- if (this.closed)
1441
- return;
1442
- const terminal = this.activationTerminalCompletion(record.target, activation.route);
1443
- const admitted = this.enqueueActivatedInstanceSpot(ingress, record, payloadFrame, activation.spot, localReply, terminal.onTerminalCompletion, metadataFrame, terminal.onHandlerTurnStarted, this.instanceApplicationTarget(record, activation.route.objectGeneration));
1444
- if (admitted !== 'application') {
1445
- throw new Error('Activated Instance message was not admitted to the local queue.');
1446
- }
1439
+ await this.activateMissingInstanceAsync(record, payloadFrame, metadataFrame, (activation) => {
1440
+ if (this.closed)
1441
+ return;
1442
+ const terminal = this.activationTerminalCompletion(record.target, activation.route);
1443
+ const admitted = this.enqueueActivatedInstanceSpot(ingress, record, payloadFrame, activation.spot, localReply, terminal.onTerminalCompletion, metadataFrame, terminal.onHandlerTurnStarted, this.instanceApplicationTarget(record, activation.route.objectGeneration));
1444
+ if (admitted !== 'application') {
1445
+ throw new Error('Activated Instance message was not admitted to the local queue.');
1446
+ }
1447
+ });
1447
1448
  }
1448
1449
  catch (error) {
1449
1450
  this.finishMissingInstanceActivationFailure(ingress, record, error, localReply);
@@ -1738,12 +1739,43 @@ class ServiceStatefulRuntime {
1738
1739
  }
1739
1740
  return activation.spot;
1740
1741
  }
1741
- activateMissingInstanceAsync(record, payloadFrame, metadataFrame) {
1742
- const key = instanceActivationKey(record);
1742
+ localReadyInstance(target, route) {
1743
+ const local = this.registry.spot(target.targetSpotId);
1744
+ if (local !== undefined &&
1745
+ (local.kind !== 'instance' || local.stableType !== target.stableType)) {
1746
+ throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
1747
+ }
1748
+ return local !== undefined &&
1749
+ local.state === 'ready' &&
1750
+ routeMatchesLocal(route, local, this.nodeRid, this.nodeGeneration)
1751
+ ? local
1752
+ : undefined;
1753
+ }
1754
+ activateMissingInstanceAsync(record, payloadFrame, metadataFrame, admit) {
1755
+ const key = record.target.targetSpotId;
1743
1756
  const pending = this.pendingInstanceActivations.get(key);
1744
- if (pending !== undefined)
1745
- return pending;
1746
- const activation = this.runMissingInstanceActivation(record, payloadFrame, metadataFrame);
1757
+ const ready = pending ??
1758
+ this.runMissingInstanceActivation(record, payloadFrame, metadataFrame).then((activation) => ({
1759
+ ...activation,
1760
+ stableType: record.target.stableType
1761
+ }));
1762
+ const operation = ready.then(async (value) => {
1763
+ if (pending !== undefined) {
1764
+ await this.asyncInstanceAuthority?.read(record.target, undefined, {
1765
+ ...record,
1766
+ targetMeshName: record.target.targetMeshName,
1767
+ ...(metadataFrame === undefined ? {} : { metadataFrame }),
1768
+ applicationPayloadFrame: payloadFrame
1769
+ }, (route) => this.localReadyInstance(record.target, route) !== undefined);
1770
+ }
1771
+ if (value.stableType !== record.target.stableType) {
1772
+ throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.TypeMismatch, 'Instance Spot type does not match.');
1773
+ }
1774
+ admit(value);
1775
+ return value;
1776
+ });
1777
+ // The caller owns operation failure. Keep the activation available for later participants.
1778
+ const activation = operation.then((value) => value, () => ready);
1747
1779
  this.pendingInstanceActivations.set(key, activation);
1748
1780
  const clear = () => {
1749
1781
  if (this.pendingInstanceActivations.get(key) === activation) {
@@ -1751,7 +1783,7 @@ class ServiceStatefulRuntime {
1751
1783
  }
1752
1784
  };
1753
1785
  void activation.then(clear, clear);
1754
- return activation;
1786
+ return operation;
1755
1787
  }
1756
1788
  async runMissingInstanceActivation(record, payloadFrame, metadataFrame) {
1757
1789
  const target = record.target;
@@ -1764,8 +1796,14 @@ class ServiceStatefulRuntime {
1764
1796
  if (authority === undefined) {
1765
1797
  throw new Error('Async Instance activation authority is not registered.');
1766
1798
  }
1799
+ const activationRequest = {
1800
+ ...record,
1801
+ targetMeshName: record.target.targetMeshName,
1802
+ ...(metadataFrame === undefined ? {} : { metadataFrame }),
1803
+ applicationPayloadFrame: payloadFrame
1804
+ };
1805
+ const current = await authority.read(target, true, activationRequest, (route) => this.localReadyInstance(target, route) !== undefined);
1767
1806
  const local = this.registry.spot(target.targetSpotId);
1768
- const current = await authority.read(target);
1769
1807
  if (current.kind === 'creating' &&
1770
1808
  current.authority?.allocation.state === 'active' &&
1771
1809
  this.instanceApplicationLifecycle?.isMaterializing?.(target) === true) {
@@ -1809,13 +1847,9 @@ class ServiceStatefulRuntime {
1809
1847
  if (current.route.targetNodeGeneration !== this.nodeGeneration) {
1810
1848
  throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
1811
1849
  }
1812
- if (local !== undefined &&
1813
- (local.kind !== 'instance' || local.stableType !== target.stableType)) {
1814
- throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
1815
- }
1816
- if (local !== undefined &&
1817
- routeMatchesLocal(current.route, local, this.nodeRid, this.nodeGeneration)) {
1818
- return { spot: local, route: current.route };
1850
+ const readyLocal = this.localReadyInstance(target, current.route);
1851
+ if (readyLocal !== undefined) {
1852
+ return { spot: readyLocal, route: current.route };
1819
1853
  }
1820
1854
  return await this.reconcileReadyInstanceProjection(target, current.route, authority);
1821
1855
  }
@@ -1852,18 +1886,7 @@ class ServiceStatefulRuntime {
1852
1886
  const deadline = operationDeadline(record.deadlineUnixMs);
1853
1887
  let reserved;
1854
1888
  try {
1855
- reserved = await authority.reserve({
1856
- target,
1857
- sourceNodeRid: record.sourceNodeRid,
1858
- sourceNodeGeneration: record.sourceNodeGeneration,
1859
- ...(record.sourceSpotId === undefined ? {} : { sourceSpotId: record.sourceSpotId }),
1860
- operationKind: record.operationKind,
1861
- operation: record.operation,
1862
- ...(record.replyRouteId === undefined ? {} : { replyRouteId: record.replyRouteId }),
1863
- deadlineUnixMs: record.deadlineUnixMs,
1864
- ...(metadataFrame === undefined ? {} : { metadataFrame }),
1865
- applicationPayloadFrame: payloadFrame
1866
- }, deadline.signal);
1889
+ reserved = await authority.reserve(activationRequest, deadline.signal);
1867
1890
  }
1868
1891
  finally {
1869
1892
  deadline.close();
@@ -3855,10 +3878,6 @@ function instanceOperationKey(record) {
3855
3878
  return (`${record.sourceNodeRid}\0${record.sourceNodeGeneration}\0` +
3856
3879
  `${record.operation.high}\0${record.operation.low}`);
3857
3880
  }
3858
- function instanceActivationKey(record) {
3859
- return (`${record.target.targetNodeRid}\0${record.target.targetNodeGeneration}\0` +
3860
- `${record.target.targetSpotId}\0${record.target.stableType}`);
3861
- }
3862
3881
  function subscriptionKey(channelName, topicFilter) {
3863
3882
  if (channelName.length === 0 || topicFilter.length === 0) {
3864
3883
  throw new TypeError('Channel name and topic filter must be non-empty.');
@@ -320,6 +320,9 @@ export interface ServiceInstanceActivationTarget {
320
320
  readonly stableType: string;
321
321
  readonly descriptorVersion: string;
322
322
  }
323
+ export interface ServiceInstanceColdActivationTarget extends ServiceInstanceActivationTarget {
324
+ readonly targetMeshName: string;
325
+ }
323
326
  export interface ServiceUserSpotReservationFence {
324
327
  readonly reservationId: string;
325
328
  readonly expectedStoreVersion: string;
@@ -475,7 +478,7 @@ export type ServiceStatefulWireRecord = {
475
478
  } | {
476
479
  readonly kind: 'instanceSpot';
477
480
  readonly activation: 'missing';
478
- readonly target: ServiceInstanceActivationTarget;
481
+ readonly target: ServiceInstanceColdActivationTarget;
479
482
  readonly sourceNodeGeneration: bigint;
480
483
  readonly sourceNodeRid: string;
481
484
  readonly sourceSpotId?: string;
@@ -549,7 +552,7 @@ export declare function encodeInstanceSpotHeader(route: ServiceInstanceRouteFenc
549
552
  readonly high: bigint;
550
553
  readonly low: bigint;
551
554
  }, replyRouteId?: bigint, hasMetadata?: boolean, instanceIntent?: boolean): Buffer;
552
- export declare function encodeInstanceSpotActivationHeader(target: ServiceInstanceActivationTarget, sourceNodeGeneration: bigint, sourceNodeRid: string, sourceSpotId: string | undefined, operationKind: 'send' | 'request', operation: {
555
+ export declare function encodeInstanceSpotActivationHeader(target: ServiceInstanceColdActivationTarget, sourceNodeGeneration: bigint, sourceNodeRid: string, sourceSpotId: string | undefined, operationKind: 'send' | 'request', operation: {
553
556
  readonly high: bigint;
554
557
  readonly low: bigint;
555
558
  }, deadlineUnixMs: bigint, replyRouteId?: bigint, hasMetadata?: boolean): Buffer;
@@ -47,8 +47,6 @@ const service_wire_constants_generated_1 = require("./service-wire-constants.gen
47
47
  const service_wire_m6a_codec_1 = require("./service-wire-m6a-codec");
48
48
  const MESSAGE_FOLLOW_MAX_BODY_BYTES = 16 * 1024 * 1024;
49
49
  const MESSAGE_FOLLOW_VERSION = 1;
50
- const INSTANCE_ROUTE_VERSION = 1;
51
- const INSTANCE_ACTIVATION_VERSION = 2;
52
50
  const PREFIX_SIZE = service_wire_binary_primitives_1.SERVICE_WIRE_PREFIX_SIZE;
53
51
  const MAGIC_0 = service_wire_constants_generated_1.SERVICE_WIRE_MAGIC[0];
54
52
  const MAGIC_1 = service_wire_constants_generated_1.SERVICE_WIRE_MAGIC[1];
@@ -653,8 +651,17 @@ function encodeInstanceSpotActivationHeader(target, sourceNodeGeneration, source
653
651
  if (operationKind === 'send' && replyRouteId !== undefined) {
654
652
  throw new RangeError('Instance Spot send must not carry a reply route.');
655
653
  }
656
- const targetBody = concat(rid(target.targetNodeRid, 'targetNodeRid'), u64(target.targetNodeGeneration), rid(target.targetSpotId, 'targetSpotId'), text16(target.stableType, 'stableType'), text16(target.descriptorVersion, 'descriptorVersion'));
657
- return concat(prefix(exports.M6bServiceWireCommand.instanceSpot, hasMetadata ? exports.M6bServiceWireFlag.metadata : 0), Buffer.of(INSTANCE_ACTIVATION_VERSION), u16(targetBody.byteLength), targetBody, u64Any(sourceNodeGeneration), rid(sourceNodeRid, 'sourceNodeRid'), optionalRid(sourceSpotId), Buffer.of(operationKind === 'send' ? 1 : 2), u64Any(operation.high), u64Any(operation.low), u64(deadlineUnixMs), ...(operationKind === 'request' ? [u64(requirePositive(replyRouteId, 'replyRouteId'))] : []));
654
+ const encodedRoute = (0, service_wire_codec_generated_1.encodeInstanceRouteV1)({
655
+ routeKind: 'coldActivation',
656
+ targetNodeRid: toGeneratedRoutingId(target.targetNodeRid, 'targetNodeRid'),
657
+ targetNodeGeneration: target.targetNodeGeneration,
658
+ targetSpotId: target.targetSpotId,
659
+ targetMeshName: target.targetMeshName,
660
+ stableType: target.stableType,
661
+ targetDescriptorVersion: target.descriptorVersion,
662
+ deadlineUnixMs
663
+ }, STATEFUL_MESSAGE_CONTEXT);
664
+ return concat(prefix(exports.M6bServiceWireCommand.instanceSpot, hasMetadata ? exports.M6bServiceWireFlag.metadata : 0), encodedRoute, u64Any(sourceNodeGeneration), rid(sourceNodeRid, 'sourceNodeRid'), optionalRid(sourceSpotId), Buffer.of(operationKind === 'send' ? 1 : 2), u64Any(operation.high), u64Any(operation.low), ...(operationKind === 'request' ? [u64(requirePositive(replyRouteId, 'replyRouteId'))] : []));
658
665
  }
659
666
  function encodeUserSpotCreateHeader(record) {
660
667
  return Buffer.from((0, service_wire_pilot_codec_generated_1.encodeUserSpotCreate47)(toGeneratedUserSpotCreate(record)));
@@ -878,45 +885,27 @@ function decodeStatefulHeader(frame, payloadFrame, metadataFrame) {
878
885
  fail(`Invalid command flags '${command.flags}'.`);
879
886
  }
880
887
  const routeStart = reader.offset;
881
- const version = reader.u8('instanceRoute.version');
882
- if (version !== INSTANCE_ROUTE_VERSION && version !== INSTANCE_ACTIVATION_VERSION)
883
- fail('Unsupported Instance route version.');
888
+ reader.u8('instanceRoute.kind');
884
889
  const routeLength = reader.u16('instanceRoute.length');
885
890
  const routeEnd = reader.offset + routeLength;
886
- const generatedReadyRoute = version === INSTANCE_ROUTE_VERSION
887
- ? (0, service_wire_codec_generated_1.decodeInstanceRouteV1)(reader.bytes.subarray(routeStart, routeEnd), STATEFUL_MESSAGE_CONTEXT)
888
- : undefined;
889
- if (generatedReadyRoute !== undefined && generatedReadyRoute.routeKind !== 'ready') {
890
- fail('Invalid Ready Instance route kind.');
891
- }
892
- const commonTarget = generatedReadyRoute?.routeKind === 'ready'
893
- ? {
894
- targetNodeRid: fromGeneratedRoutingId(generatedReadyRoute.targetNodeRid, 'targetNodeRid'),
895
- targetNodeGeneration: generatedReadyRoute.targetNodeGeneration,
896
- targetSpotId: generatedReadyRoute.targetSpotId
897
- }
898
- : {
899
- targetNodeRid: reader.rid('targetNodeRid'),
900
- targetNodeGeneration: reader.nonZeroU64('targetNodeGeneration'),
901
- targetSpotId: reader.rid('targetSpotId')
902
- };
903
- const route = generatedReadyRoute?.routeKind === 'ready'
904
- ? {
905
- ...commonTarget,
906
- ...generatedReadyRoute.authority
907
- }
891
+ const generatedRoute = decodeCanonicalInstanceRoute(reader.bytes.subarray(routeStart, routeEnd));
892
+ const commonTarget = {
893
+ targetNodeRid: fromGeneratedRoutingId(generatedRoute.targetNodeRid, 'targetNodeRid'),
894
+ targetNodeGeneration: generatedRoute.targetNodeGeneration,
895
+ targetSpotId: generatedRoute.targetSpotId
896
+ };
897
+ const route = generatedRoute.routeKind === 'ready'
898
+ ? { ...commonTarget, ...generatedRoute.authority }
908
899
  : undefined;
909
- const target = version === INSTANCE_ACTIVATION_VERSION
900
+ const target = generatedRoute.routeKind === 'coldActivation'
910
901
  ? {
911
902
  ...commonTarget,
912
- stableType: reader.text16('stableType'),
913
- descriptorVersion: reader.text16('descriptorVersion')
903
+ targetMeshName: generatedRoute.targetMeshName,
904
+ stableType: generatedRoute.stableType,
905
+ descriptorVersion: generatedRoute.targetDescriptorVersion
914
906
  }
915
907
  : undefined;
916
- if (generatedReadyRoute !== undefined)
917
- reader.offset = routeEnd;
918
- if (reader.offset !== routeEnd)
919
- fail('Invalid Instance route body length.');
908
+ reader.offset = routeEnd;
920
909
  const sourceNodeGeneration = reader.nonZeroU64('sourceNodeGeneration');
921
910
  const sourceNodeRid = reader.rid('sourceNodeRid');
922
911
  const sourceSpotId = reader.optionalRid('sourceSpotId');
@@ -928,17 +917,17 @@ function decodeStatefulHeader(frame, payloadFrame, metadataFrame) {
928
917
  low: reader.u64('operation.low')
929
918
  };
930
919
  const operationKind = operationValue === 1 ? 'send' : 'request';
931
- if (version === INSTANCE_ROUTE_VERSION &&
920
+ if (generatedRoute.routeKind === 'ready' &&
932
921
  ((operationKind === 'send' && (operation.high !== 0n || operation.low !== 0n)) ||
933
922
  (operationKind === 'request' && operation.high === 0n && operation.low === 0n))) {
934
923
  fail('Invalid Instance operation identity.');
935
924
  }
936
- if (version === INSTANCE_ACTIVATION_VERSION &&
925
+ if (generatedRoute.routeKind === 'coldActivation' &&
937
926
  operation.high === 0n &&
938
927
  operation.low === 0n) {
939
928
  fail('Instance activation requires a non-zero operation identity.');
940
929
  }
941
- const deadlineUnixMs = version === INSTANCE_ACTIVATION_VERSION ? reader.nonZeroU64('deadlineUnixMs') : undefined;
930
+ const deadlineUnixMs = generatedRoute.routeKind === 'coldActivation' ? generatedRoute.deadlineUnixMs : undefined;
942
931
  const replyRouteId = operationKind === 'request' ? reader.nonZeroU64('replyRouteId') : undefined;
943
932
  reader.end();
944
933
  const common = {
@@ -950,12 +939,12 @@ function decodeStatefulHeader(frame, payloadFrame, metadataFrame) {
950
939
  operation,
951
940
  ...(replyRouteId === undefined ? {} : { replyRouteId })
952
941
  };
953
- return version === INSTANCE_ROUTE_VERSION
942
+ return generatedRoute.routeKind === 'ready'
954
943
  ? {
955
944
  ...common,
956
945
  activation: 'ready',
957
946
  route: route,
958
- instanceIntent: generatedReadyRoute.instanceIntent === 'true'
947
+ instanceIntent: generatedRoute.instanceIntent === 'true'
959
948
  }
960
949
  : {
961
950
  ...common,
@@ -1375,10 +1364,6 @@ function rid(value, name) {
1375
1364
  function text8(value, name) {
1376
1365
  return sized8(value, name);
1377
1366
  }
1378
- function text16(value, name) {
1379
- const bytes = (0, service_wire_binary_primitives_1.encodeCanonicalServiceWireText)(value, name, 0xffff, outOfRange);
1380
- return concat(u16(bytes.byteLength), bytes);
1381
- }
1382
1367
  function optionalRid(value) {
1383
1368
  return value === undefined ? Buffer.of(0) : rid(value, 'sourceSpotId');
1384
1369
  }
@@ -1428,6 +1413,16 @@ function requireFlags(actual, expected) {
1428
1413
  function concat(...parts) {
1429
1414
  return Buffer.concat(parts.map((part) => Buffer.from(part)));
1430
1415
  }
1416
+ function decodeCanonicalInstanceRoute(bytes) {
1417
+ try {
1418
+ return (0, service_wire_codec_generated_1.decodeInstanceRouteV1)(bytes, STATEFUL_MESSAGE_CONTEXT);
1419
+ }
1420
+ catch (error) {
1421
+ if (error instanceof RangeError)
1422
+ throw new service_wire_m6a_codec_1.ServiceWireProtocolError(error.message);
1423
+ throw error;
1424
+ }
1425
+ }
1431
1426
  function fail(message) {
1432
1427
  throw new service_wire_m6a_codec_1.ServiceWireProtocolError(message);
1433
1428
  }
@@ -2009,22 +2004,9 @@ class FrozenReader {
2009
2004
  }
2010
2005
  instanceRoute() {
2011
2006
  const start = this.offset;
2012
- const kind = this.u8('instanceRouteKind');
2013
- if (kind < 1 || kind > 2)
2014
- fail('Invalid Instance route kind.');
2015
- const body = this.body16('Instance route');
2016
- if (kind === INSTANCE_ROUTE_VERSION) {
2017
- (0, service_wire_codec_generated_1.decodeInstanceRouteV1)(this.bytes.subarray(start, this.offset), STATEFUL_MESSAGE_CONTEXT);
2018
- return;
2019
- }
2020
- body.rid8('targetNodeRid');
2021
- body.nonZeroU64('targetNodeGeneration');
2022
- body.text8('targetSpotId');
2023
- body.text8('targetMeshName');
2024
- body.text8('stableType');
2025
- body.text8('targetDescriptorVersion');
2026
- body.nonZeroU64('deadlineUnixMs');
2027
- body.end('Instance route');
2007
+ this.u8('instanceRouteKind');
2008
+ this.body16('Instance route');
2009
+ decodeCanonicalInstanceRoute(this.bytes.subarray(start, this.offset));
2028
2010
  }
2029
2011
  relocationObject() {
2030
2012
  const kind = this.u8('relocationObjectKind');
@@ -1,3 +1,4 @@
1
+ import type { ServiceLivenessConnection } from './service-liveness-registry';
1
2
  export type ServiceNodeState = 'preparing' | 'serving' | 'retiring' | 'draining' | 'stopped' | 'error';
2
3
  export type ServiceObjectRole = 'none' | 'client' | 'server';
3
4
  export interface ServiceChannelDescriptor {
@@ -25,7 +26,8 @@ export interface ServiceNodeDescriptor {
25
26
  }
26
27
  export interface AdmittedServicePeer {
27
28
  readonly descriptor: ServiceNodeDescriptor;
28
- readonly connectionId: string;
29
+ readonly connectionId: string | bigint;
30
+ readonly liveness?: ServiceLivenessConnection;
29
31
  }
30
32
  export interface ServicePeerAdmissionExpectation {
31
33
  readonly endpoint?: string;
@@ -44,8 +46,8 @@ export declare class ServiceTopologyRegistry {
44
46
  constructor(local: ServiceNodeDescriptor);
45
47
  localDescriptor(): ServiceNodeDescriptor;
46
48
  publishLocal(descriptor: ServiceNodeDescriptor): void;
47
- admit(descriptor: ServiceNodeDescriptor, connectionId: string, expected?: ServicePeerAdmissionExpectation): PeerAdmissionResult;
48
- disconnect(nodeRoutingId: string, connectionId: string): boolean;
49
+ admit(descriptor: ServiceNodeDescriptor, connectionId: string | bigint, expected?: ServicePeerAdmissionExpectation, admitLiveness?: () => ServiceLivenessConnection): PeerAdmissionResult;
50
+ disconnect(nodeRoutingId: string, connectionId: string | bigint): boolean;
49
51
  peer(nodeRoutingId: string): AdmittedServicePeer | undefined;
50
52
  peers(): readonly AdmittedServicePeer[];
51
53
  private peersCore;
@@ -41,10 +41,13 @@ class ServiceTopologyRegistry {
41
41
  }
42
42
  this.rebuildSelections();
43
43
  }
44
- admit(descriptor, connectionId, expected) {
44
+ admit(descriptor, connectionId, expected, admitLiveness) {
45
45
  try {
46
46
  validateDescriptor(descriptor);
47
- requireText(connectionId, 'connectionId');
47
+ if (typeof connectionId === 'string')
48
+ requireText(connectionId, 'connectionId');
49
+ else if (connectionId === 0n)
50
+ throw new TypeError('connectionId must be nonzero.');
48
51
  }
49
52
  catch (error) {
50
53
  return 'invalidDescriptor';
@@ -86,7 +89,8 @@ class ServiceTopologyRegistry {
86
89
  // that route replaces the admission of a route it replaced.
87
90
  this.peersByRid.set(descriptor.nodeRoutingId, {
88
91
  descriptor: cloneDescriptor(descriptor),
89
- connectionId
92
+ connectionId,
93
+ liveness: admitLiveness?.() ?? current?.liveness
90
94
  });
91
95
  this.remember(descriptor);
92
96
  this.rebuildSelections();
@@ -329,7 +333,8 @@ function cloneDescriptor(descriptor) {
329
333
  function clonePeer(peer) {
330
334
  return {
331
335
  descriptor: cloneDescriptor(peer.descriptor),
332
- connectionId: peer.connectionId
336
+ connectionId: peer.connectionId,
337
+ liveness: peer.liveness
333
338
  };
334
339
  }
335
340
  function sameServiceNodeDescriptor(left, right) {
@@ -158,7 +158,7 @@ class LifecycleHandlerInstanceScope {
158
158
  }
159
159
  }
160
160
  async dispose(detachedTaskRunner) {
161
- const disposeFromActiveInvocation = activeLifecycleScope.getStore() === this;
161
+ const disposeFromActiveInvocation = this.activeInvocations > 0 && activeLifecycleScope.getStore() === this;
162
162
  const disposal = this.beginDisposeCore();
163
163
  if (disposal.deferred !== undefined) {
164
164
  startOutsideLifecycleInvocation(() => {
@@ -173,8 +173,8 @@ class ZLinkActorPacketRelay {
173
173
  const state = this.options.actorManager()?.getState(relay.actorId);
174
174
  if (frameHeader.kind === protocol_1.ZLinkStreamMessageKind.Send) {
175
175
  const dispatch = state?.spotId === undefined
176
- ? this.requireSpotNodeRuntime().dispatchEntryActorPacket(relay.actorId, [header, body], false, remoteBoundSessionTarget, fallbackActorRef)
177
- : this.requireSpotManager().dispatchRoutedActorPacket(state.spotId, relay.actorId, [header, body], false, remoteBoundSessionTarget, fallbackActorRef);
176
+ ? this.requireSpotNodeRuntime().dispatchEntryActorPacket('remote', relay.actorId, [header, body], false, remoteBoundSessionTarget, fallbackActorRef)
177
+ : this.requireSpotManager().dispatchRoutedActorPacket('remote', state.spotId, relay.actorId, [header, body], false, remoteBoundSessionTarget, fallbackActorRef);
178
178
  // Spec 05 §1.3: a one-way Session Actor relay completes at relay
179
179
  // queue admission, not after the remote application turn. In
180
180
  // particular, a handler may register a deferred Join whose relocation
@@ -207,8 +207,8 @@ class ZLinkActorPacketRelay {
207
207
  frameHeader.requestSeq !== undefined &&
208
208
  relay.returnResponse !== true) {
209
209
  const dispatch = state?.spotId === undefined
210
- ? this.requireSpotNodeRuntime().dispatchEntryActorPacket(relay.actorId, [header, body], false, remoteBoundSessionTarget, fallbackActorRef)
211
- : this.requireSpotManager().dispatchRoutedActorPacket(state.spotId, relay.actorId, [header, body], false, remoteBoundSessionTarget, fallbackActorRef);
210
+ ? this.requireSpotNodeRuntime().dispatchEntryActorPacket('remote', relay.actorId, [header, body], false, remoteBoundSessionTarget, fallbackActorRef)
211
+ : this.requireSpotManager().dispatchRoutedActorPacket('remote', state.spotId, relay.actorId, [header, body], false, remoteBoundSessionTarget, fallbackActorRef);
212
212
  closeFrameMessages = false;
213
213
  const deferredPacketName = frameHeader.name;
214
214
  const deferredRequestSeq = frameHeader.requestSeq;
@@ -249,8 +249,8 @@ class ZLinkActorPacketRelay {
249
249
  };
250
250
  }
251
251
  const response = state?.spotId === undefined
252
- ? await this.requireSpotNodeRuntime().dispatchEntryActorPacket(relay.actorId, [header, body], relay.returnResponse === true, remoteBoundSessionTarget, fallbackActorRef)
253
- : await this.requireSpotManager().dispatchRoutedActorPacket(state.spotId, relay.actorId, [header, body], relay.returnResponse === true, remoteBoundSessionTarget, fallbackActorRef);
252
+ ? await this.requireSpotNodeRuntime().dispatchEntryActorPacket('remote', relay.actorId, [header, body], relay.returnResponse === true, remoteBoundSessionTarget, fallbackActorRef)
253
+ : await this.requireSpotManager().dispatchRoutedActorPacket('remote', state.spotId, relay.actorId, [header, body], relay.returnResponse === true, remoteBoundSessionTarget, fallbackActorRef);
254
254
  return {
255
255
  ok: true,
256
256
  response,
@@ -551,7 +551,7 @@ class ZLinkActorPacketRelay {
551
551
  try {
552
552
  this.options.detachedTaskRunner.runDetached('local actor packet relay', async () => {
553
553
  try {
554
- const response = await this.requireSpotManager().dispatchRoutedActorPacket(spotId, actor.actorId, [header, body], returnResponse);
554
+ const response = await this.requireSpotManager().dispatchRoutedActorPacket('local', spotId, actor.actorId, [header, body], returnResponse);
555
555
  if (!returnResponse) {
556
556
  return;
557
557
  }
@@ -1,5 +1,5 @@
1
1
  import type { ActorRef, RoutingId, ZLinkActorCreateResult } from '../../contracts';
2
- import type { ZLinkAuthoritySnapshot, ZLinkLocationOwnerToken } from '../locations/internal-location-contracts';
2
+ import type { ZLinkAuthoritySnapshot, ZLinkLocationOwnerToken, ZLinkObjectReserveRequest } from '../locations/internal-location-contracts';
3
3
  import type { ZLinkAuthorityStore, ZLinkObjectCreationStore } from '../locations/internal-store-contracts';
4
4
  import type { ServiceActorCreateRecord } from '../foundation/service-stateful-wire-codec';
5
5
  import type { ServiceUserSpotOperationResult } from '../foundation/service-stateful-runtime';
@@ -14,6 +14,7 @@ export interface ZLinkActorPlacementTarget {
14
14
  readonly sourceNodeGeneration: bigint;
15
15
  }
16
16
  export interface ZLinkActorPlacementCoordinatorOptions {
17
+ readonly actorRelocationPolicy?: (meshName: string, stableType: string) => ZLinkObjectReserveRequest['actorRelocationPolicy'];
17
18
  readonly store: ZLinkObjectCreationStore & ZLinkAuthorityStore;
18
19
  readonly target: (meshName: string | undefined, stableType: string, signal?: AbortSignal, excludedNodeRids?: ReadonlySet<string>) => Promise<ZLinkActorPlacementTarget | undefined>;
19
20
  readonly remoteCreate: (meshName: string, targetNodeRid: string, request: Omit<ServiceActorCreateRecord, 'kind' | 'correlation'>, timeoutMs: number) => Promise<ServiceUserSpotOperationResult>;
@@ -61,7 +61,8 @@ class ZLinkActorPlacementCoordinator {
61
61
  },
62
62
  target: reservationTarget(target),
63
63
  creatingPayload,
64
- capacity: { actors: 1, spots: 0 }
64
+ capacity: { actors: 1, spots: 0 },
65
+ actorRelocationPolicy: this.options.actorRelocationPolicy?.(target.meshName, stableType)
65
66
  }, deadline.signal, BigInt(deadlineUnixMs));
66
67
  const existing = existingActor(reserved, actorId, stableType);
67
68
  if (existing !== undefined) {
@@ -15,6 +15,7 @@ import type { ZLinkRuntimeAdmissionGate } from '../admission';
15
15
  import type { ZLinkRemoteActorPacketTarget } from '../actors';
16
16
  import type { ZLinkActorJoinRelocation } from '../actors/actor-join-relocation';
17
17
  export interface ZLinkActorRuntimeOptionsFactoryOptions {
18
+ readonly terminateActorActivation: (actor: ZLinkActor, terminal: () => Promise<void>) => Promise<void>;
18
19
  readonly registration: ZLinkFrameworkRegistration;
19
20
  /** Message flow tracer, so a failed deferred Join records its cause on the flow. */
20
21
  readonly messageFlow?: () => import('../diagnostics/message-flow').ZLinkMessageFlowTracer | undefined;
@@ -61,7 +62,7 @@ export interface ZLinkActorRuntimeOptionsFactoryOptions {
61
62
  export declare class ZLinkActorRuntimeOptionsFactory {
62
63
  private readonly options;
63
64
  constructor(options: ZLinkActorRuntimeOptionsFactoryOptions);
64
- createActorManagerOptions(): Pick<ZLinkActorManagerOptions, 'joinCoordinator' | 'actorMeshNameProvider' | 'actorLeaveSpot' | 'messageSerializers' | 'nativeActorNode' | 'nativeActorNodeProvider' | 'nativeActorCompletionTableProvider' | 'actorCreatedNodeRidProvider' | 'actorRefResolver' | 'actorCreatedNotifier' | 'actorDestroyedCleanup' | 'publishActorAuthority' | 'locationLifecycle' | 'boundSessionFactory' | 'actorTransferRegistry' | 'shutdownSignal' | 'metrics' | 'admission'>;
65
+ createActorManagerOptions(): Pick<ZLinkActorManagerOptions, 'joinCoordinator' | 'actorMeshNameProvider' | 'actorLeaveSpot' | 'messageSerializers' | 'nativeActorNode' | 'nativeActorNodeProvider' | 'nativeActorCompletionTableProvider' | 'actorCreatedNodeRidProvider' | 'actorRefResolver' | 'actorCreatedNotifier' | 'actorDestroyedCleanup' | 'terminateActorActivation' | 'publishActorAuthority' | 'locationLifecycle' | 'boundSessionFactory' | 'actorTransferRegistry' | 'shutdownSignal' | 'metrics' | 'admission'>;
65
66
  createActorClientOptions(): ConstructorParameters<typeof DefaultZLinkActorClient>[0];
66
67
  private actorBelongsToMesh;
67
68
  }
@@ -13,6 +13,7 @@ class ZLinkActorRuntimeOptionsFactory {
13
13
  createActorManagerOptions() {
14
14
  const actorTransferRegistry = this.options.actorTransferRegistry;
15
15
  return {
16
+ terminateActorActivation: this.options.terminateActorActivation,
16
17
  actorMeshNameProvider: this.options.actorMeshName,
17
18
  actorLeaveSpot: (meshName, spotId, actor, signal) => {
18
19
  const spotManager = this.options.spotManager();
@@ -150,7 +151,7 @@ class ZLinkActorRuntimeOptionsFactory {
150
151
  this.options.actorHandoff.isKnownStale(actor),
151
152
  handoffCapture: (meshName, actorId, parts, returnResponse, actor, deadlineUnixMs) => this.actorBelongsToMesh(meshName, actorId) &&
152
153
  !this.options.actorHandoff.isProvisional(actorId)
153
- ? this.options.actorHandoff.capture(actorId, parts, returnResponse, undefined, actor, deadlineUnixMs)
154
+ ? this.options.actorHandoff.capture('local', actorId, parts, returnResponse, undefined, actor, deadlineUnixMs)
154
155
  : undefined,
155
156
  sendErrorReporter: this.options.reportPostCommitError
156
157
  };