@zlink-systems/framework 0.29.1 → 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 (152) 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/application-jobs/application-ingress-record-owner.d.ts +3 -1
  13. package/dist/runtime/application-jobs/application-ingress-record-owner.js +6 -1
  14. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +3 -1
  15. package/dist/runtime/application-jobs/application-job-queue-scope.js +5 -0
  16. package/dist/runtime/application-jobs/contracts.d.ts +5 -1
  17. package/dist/runtime/backend/contracts/index.d.ts +10 -19
  18. package/dist/runtime/backend/mesh-completion-table.d.ts +6 -4
  19. package/dist/runtime/backend/mesh-completion-table.js +28 -18
  20. package/dist/runtime/backend/mesh-dispatch-pump.js +21 -25
  21. package/dist/runtime/backend/node/node-backend-adapter-factory.js +6 -1
  22. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +3 -3
  23. package/dist/runtime/backend/node/node-backend-adapter-support.js +24 -29
  24. package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -9
  25. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
  26. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -6
  27. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +10 -12
  28. package/dist/runtime/backend/node/node-raw-mesh-backend.js +64 -98
  29. package/dist/runtime/backend/node/node-socket-backend-adapter.js +10 -5
  30. package/dist/runtime/backend/raw-binding-port.d.ts +9 -2
  31. package/dist/runtime/channels/channel-receive-loops.js +3 -3
  32. package/dist/runtime/channels/channel-runtime-lifecycle.js +2 -1
  33. package/dist/runtime/channels/channel-socket-registry.d.ts +10 -3
  34. package/dist/runtime/channels/channel-socket-registry.js +59 -82
  35. package/dist/runtime/diagnostics/message-flow.d.ts +1 -0
  36. package/dist/runtime/diagnostics/message-flow.js +3 -3
  37. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +15 -1
  38. package/dist/runtime/foundation/raw-service-mesh-runtime.js +193 -64
  39. package/dist/runtime/foundation/service-liveness-registry.d.ts +20 -8
  40. package/dist/runtime/foundation/service-liveness-registry.js +57 -44
  41. package/dist/runtime/foundation/service-mailbox.d.ts +3 -0
  42. package/dist/runtime/foundation/service-mailbox.js +2 -1
  43. package/dist/runtime/foundation/service-stateful-runtime.d.ts +7 -6
  44. package/dist/runtime/foundation/service-stateful-runtime.js +34 -23
  45. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +5 -2
  46. package/dist/runtime/foundation/service-stateful-wire-codec.js +43 -61
  47. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -3
  48. package/dist/runtime/foundation/service-topology-registry.js +9 -4
  49. package/dist/runtime/host/actor-packet-relay.js +7 -7
  50. package/dist/runtime/host/actor-runtime-options-factory.js +1 -1
  51. package/dist/runtime/host/application-job-queue.d.ts +11 -4
  52. package/dist/runtime/host/application-job-queue.js +66 -10
  53. package/dist/runtime/host/entry-actor-runtime.js +1 -1
  54. package/dist/runtime/host/index.js +15 -11
  55. package/dist/runtime/host/instance-activation-authority.d.ts +2 -1
  56. package/dist/runtime/host/instance-activation-authority.js +89 -25
  57. package/dist/runtime/host/service-relocation-host-runtime.js +2 -2
  58. package/dist/runtime/host/spot-address-transport.d.ts +1 -0
  59. package/dist/runtime/host/spot-address-transport.js +25 -12
  60. package/dist/runtime/host/spot-runtime-options-factory.js +2 -2
  61. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +1 -0
  62. package/dist/runtime/host/user-spot-creation-coordinator.js +1 -0
  63. package/dist/runtime/locations/domain-store-contract.d.ts +2 -2
  64. package/dist/runtime/locations/in-memory-authority-store.d.ts +4 -2
  65. package/dist/runtime/locations/in-memory-authority-store.js +80 -8
  66. package/dist/runtime/locations/in-memory-location-store.d.ts +2 -2
  67. package/dist/runtime/locations/in-memory-location-store.js +19 -3
  68. package/dist/runtime/locations/internal-location-contracts.d.ts +3 -0
  69. package/dist/runtime/locations/location-store-repository.d.ts +3 -2
  70. package/dist/runtime/locations/location-store-repository.js +165 -22
  71. package/dist/runtime/locations/resolvers.d.ts +2 -1
  72. package/dist/runtime/locations/resolvers.js +10 -1
  73. package/dist/runtime/messaging/submission-result.d.ts +1 -1
  74. package/dist/runtime/messaging/submission-result.js +2 -2
  75. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +2 -0
  76. package/dist/runtime/spots/index.d.ts +4 -3
  77. package/dist/runtime/spots/index.js +14 -14
  78. package/dist/runtime/spots/spot-activation.d.ts +2 -1
  79. package/dist/runtime/spots/spot-activation.js +4 -4
  80. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +2 -1
  81. package/dist/runtime/spots/spot-actor-admission-coordinator.js +11 -10
  82. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +4 -1
  83. package/dist/runtime/spots/spot-actor-packet-dispatch.js +7 -5
  84. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  85. package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +1 -0
  86. package/dist/runtime/spots/spot-entry-activation.js +3 -1
  87. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +2 -1
  88. package/dist/runtime/spots/spot-node-runtime-manager.js +4 -11
  89. package/dist/runtime/spots/spot-routing-internal.d.ts +6 -0
  90. package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -1
  91. package/dist/runtime/streams/stream-session-runtime.js +1 -1
  92. package/package.json +3 -3
  93. package/src/contracts/Configuration/Builders.ts +1 -2
  94. package/src/contracts/Configuration/Configs.ts +0 -6
  95. package/src/contracts/Configuration/RegistrationBuilders.ts +8 -12
  96. package/src/contracts/Configuration/RegistrationTypes.ts +1 -6
  97. package/src/contracts/Configuration/RegistrationValidators.ts +9 -10
  98. package/src/contracts/Configuration/index.ts +1 -6
  99. package/src/internal.ts +1 -0
  100. package/src/runtime/actors/actor-handoff.ts +13 -4
  101. package/src/runtime/application-jobs/application-ingress-record-owner.ts +12 -2
  102. package/src/runtime/application-jobs/application-job-queue-scope.ts +6 -1
  103. package/src/runtime/application-jobs/contracts.ts +14 -1
  104. package/src/runtime/backend/contracts/index.ts +10 -18
  105. package/src/runtime/backend/mesh-completion-table.ts +36 -19
  106. package/src/runtime/backend/mesh-dispatch-pump.ts +19 -22
  107. package/src/runtime/backend/node/node-backend-adapter-factory.ts +12 -5
  108. package/src/runtime/backend/node/node-backend-adapter-support.ts +31 -29
  109. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +3 -10
  110. package/src/runtime/backend/node/node-raw-binding-port.ts +19 -10
  111. package/src/runtime/backend/node/node-raw-mesh-backend.ts +81 -111
  112. package/src/runtime/backend/node/node-socket-backend-adapter.ts +12 -8
  113. package/src/runtime/backend/raw-binding-port.ts +12 -2
  114. package/src/runtime/channels/channel-receive-loops.ts +3 -3
  115. package/src/runtime/channels/channel-runtime-lifecycle.ts +7 -1
  116. package/src/runtime/channels/channel-socket-registry.ts +84 -130
  117. package/src/runtime/diagnostics/message-flow.ts +1 -1
  118. package/src/runtime/foundation/raw-service-mesh-runtime.ts +268 -70
  119. package/src/runtime/foundation/service-liveness-registry.ts +68 -59
  120. package/src/runtime/foundation/service-mailbox.ts +7 -1
  121. package/src/runtime/foundation/service-stateful-runtime.ts +61 -35
  122. package/src/runtime/foundation/service-stateful-wire-codec.ts +55 -79
  123. package/src/runtime/foundation/service-topology-registry.ts +13 -7
  124. package/src/runtime/host/actor-packet-relay.ts +7 -0
  125. package/src/runtime/host/actor-runtime-options-factory.ts +1 -0
  126. package/src/runtime/host/application-job-queue.ts +77 -10
  127. package/src/runtime/host/entry-actor-runtime.ts +1 -0
  128. package/src/runtime/host/index.ts +24 -5
  129. package/src/runtime/host/instance-activation-authority.ts +132 -27
  130. package/src/runtime/host/service-relocation-host-runtime.ts +2 -0
  131. package/src/runtime/host/spot-address-transport.ts +45 -22
  132. package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
  133. package/src/runtime/host/user-spot-creation-coordinator.ts +8 -0
  134. package/src/runtime/locations/domain-store-contract.ts +6 -2
  135. package/src/runtime/locations/in-memory-authority-store.ts +113 -9
  136. package/src/runtime/locations/in-memory-location-store.ts +25 -3
  137. package/src/runtime/locations/internal-location-contracts.ts +4 -0
  138. package/src/runtime/locations/location-store-repository.ts +220 -15
  139. package/src/runtime/locations/resolvers.ts +25 -2
  140. package/src/runtime/messaging/submission-result.ts +2 -2
  141. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +2 -0
  142. package/src/runtime/spots/index.ts +16 -0
  143. package/src/runtime/spots/spot-activation.ts +5 -0
  144. package/src/runtime/spots/spot-actor-admission-coordinator.ts +13 -0
  145. package/src/runtime/spots/spot-actor-packet-dispatch.ts +10 -0
  146. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  147. package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +1 -0
  148. package/src/runtime/spots/spot-entry-activation.ts +3 -0
  149. package/src/runtime/spots/spot-node-runtime-manager.ts +7 -12
  150. package/src/runtime/spots/spot-routing-internal.ts +14 -0
  151. package/src/runtime/spots/spot-runtime-ports.ts +2 -0
  152. package/src/runtime/streams/stream-session-runtime.ts +1 -1
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApplicationJobQueue = exports.ApplicationJobQueuePermit = void 0;
4
4
  exports.resolveApplicationJobQueueConfiguration = resolveApplicationJobQueueConfiguration;
5
5
  exports.nodeEffectiveProcessorCount = nodeEffectiveProcessorCount;
6
+ const api_logs_1 = require("@opentelemetry/api-logs");
7
+ const message_flow_1 = require("../diagnostics/message-flow");
6
8
  const runtime_metrics_1 = require("../diagnostics/runtime-metrics");
7
9
  const Dispatch_1 = require("../../contracts/Dispatch");
8
10
  const Registration_1 = require("../../contracts/Configuration/Registration");
@@ -50,9 +52,11 @@ function resolveApplicationJobQueueConfiguration(options = {}, effectiveProcesso
50
52
  }
51
53
  class ApplicationJobQueuePermit {
52
54
  owner;
55
+ origin;
53
56
  state = 'reserved';
54
- constructor(owner) {
57
+ constructor(owner, origin) {
55
58
  this.owner = owner;
59
+ this.origin = origin;
56
60
  }
57
61
  markApplicationQueued() {
58
62
  if (this.state !== 'reserved') {
@@ -86,6 +90,9 @@ class ApplicationJobQueue {
86
90
  configuration;
87
91
  nowMs;
88
92
  handlerStartGate;
93
+ loggerFailureReporter;
94
+ localWaiters = 0;
95
+ localBacklogLogged = false;
89
96
  reservedSupplyPermits = 0n;
90
97
  queuedApplicationJobs = 0n;
91
98
  peakPermitsInUse = 0n;
@@ -104,10 +111,11 @@ class ApplicationJobQueue {
104
111
  waiters = [];
105
112
  receiveFlowTargets = new Map();
106
113
  receiveFlowController;
107
- constructor(configuration, nowMs = () => performance.now(), handlerStartGate) {
114
+ constructor(configuration, nowMs = () => performance.now(), handlerStartGate, loggerFailureReporter) {
108
115
  this.configuration = configuration;
109
116
  this.nowMs = nowMs;
110
117
  this.handlerStartGate = handlerStartGate;
118
+ this.loggerFailureReporter = loggerFailureReporter;
111
119
  this.receiveFlowController = new receive_flow_controller_1.ApplicationJobReceiveFlowController(this, (state) => state);
112
120
  }
113
121
  shouldHoldPermitBeforeHandler() {
@@ -153,15 +161,24 @@ class ApplicationJobQueue {
153
161
  this.receiveFlowTargets.delete(identity);
154
162
  this.receiveFlowController.unregister(target);
155
163
  }
156
- acquire(signal) {
164
+ acquire(signal, origin) {
165
+ return new Promise((resolve, reject) => {
166
+ const permit = this.acquireOrResume(resolve, reject, signal, origin);
167
+ if (permit !== undefined)
168
+ resolve(permit);
169
+ });
170
+ }
171
+ acquireOrResume(resume, reject, signal, origin) {
157
172
  if (signal?.aborted === true) {
158
- return Promise.reject(abortReason(signal));
173
+ reject(abortReason(signal));
174
+ return undefined;
159
175
  }
160
176
  if (this.waiters.length === 0 && this.permitsInUse() < this.limit()) {
161
- return Promise.resolve(this.reserve());
177
+ return this.reserve(origin);
162
178
  }
163
- return new Promise((resolve, reject) => {
179
+ new Promise((resolve, reject) => {
164
180
  const waiter = {
181
+ origin,
165
182
  resolve,
166
183
  reject,
167
184
  signal,
@@ -172,9 +189,15 @@ class ApplicationJobQueue {
172
189
  };
173
190
  this.waiters.push(waiter);
174
191
  this.capacityWaitCount += 1n;
192
+ const warningCount = this.updateLocalWaiters(origin, 1);
175
193
  signal?.addEventListener('abort', waiter.abort, { once: true });
176
194
  this.drainWaiters();
177
- });
195
+ if (warningCount !== undefined)
196
+ this.logLocalBacklog(warningCount);
197
+ })
198
+ .then(resume)
199
+ .catch(reject);
200
+ return undefined;
178
201
  }
179
202
  snapshot() {
180
203
  const permitsInUse = this.permitsInUse();
@@ -253,14 +276,14 @@ class ApplicationJobQueue {
253
276
  permitsInUse() {
254
277
  return this.reservedSupplyPermits + this.queuedApplicationJobs;
255
278
  }
256
- reserve(evaluatePressure = true) {
279
+ reserve(origin, evaluatePressure = true) {
257
280
  this.reservedSupplyPermits += 1n;
258
281
  const current = this.permitsInUse();
259
282
  if (current > this.peakPermitsInUse)
260
283
  this.peakPermitsInUse = current;
261
284
  if (evaluatePressure)
262
285
  this.evaluatePressure();
263
- return new ApplicationJobQueuePermit(this);
286
+ return new ApplicationJobQueuePermit(this, origin);
264
287
  }
265
288
  drainWaiters() {
266
289
  while (this.waiters.length > 0 && this.permitsInUse() < this.limit()) {
@@ -270,7 +293,7 @@ class ApplicationJobQueue {
270
293
  waiter.active = false;
271
294
  waiter.signal?.removeEventListener('abort', waiter.abort);
272
295
  this.recordCompletedWait(waiter);
273
- waiter.resolve(this.reserve(false));
296
+ waiter.resolve(this.reserve(waiter.origin, false));
274
297
  }
275
298
  }
276
299
  cancel(waiter) {
@@ -286,11 +309,44 @@ class ApplicationJobQueue {
286
309
  this.drainWaiters();
287
310
  }
288
311
  recordCompletedWait(waiter) {
312
+ this.updateLocalWaiters(waiter.origin, -1);
289
313
  if (waiter.metricsEpoch !== this.metricsEpoch)
290
314
  return;
291
315
  const durationMs = Math.max(0, this.nowMs() - waiter.startedAtMs);
292
316
  this.capacityWaitDurationSeconds += durationMs / runtime_metrics_1.MILLISECONDS_PER_SECOND;
293
317
  }
318
+ updateLocalWaiters(origin, delta) {
319
+ if (origin !== 'local')
320
+ return undefined;
321
+ this.localWaiters += delta;
322
+ if (this.localWaiters === 0)
323
+ this.localBacklogLogged = false;
324
+ if (!this.localBacklogLogged && this.localWaiters > Number(this.limit())) {
325
+ this.localBacklogLogged = true;
326
+ return this.localWaiters;
327
+ }
328
+ return undefined;
329
+ }
330
+ logLocalBacklog(localWaiters) {
331
+ try {
332
+ message_flow_1.telemetryLogger.emit({
333
+ eventName: 'zlink.runtime.host.local_job_backlog_exceeded',
334
+ timestamp: new Date(),
335
+ severityNumber: api_logs_1.SeverityNumber.WARN,
336
+ severityText: 'WARN',
337
+ body: 'Same-host jobs waiting for a host permit exceeded the application job queue maximum.',
338
+ attributes: {
339
+ source_kind: 'host',
340
+ source_name: 'zlink.runtime.host',
341
+ local_waiters: localWaiters,
342
+ effective_maximum: Number(this.limit())
343
+ }
344
+ });
345
+ }
346
+ catch (error) {
347
+ this.loggerFailureReporter?.(error);
348
+ }
349
+ }
294
350
  evaluatePressure() {
295
351
  const permitsInUse = this.permitsInUse();
296
352
  const next = this.pressureStateValue === 'running'
@@ -66,7 +66,7 @@ class ZLinkEntryActorRuntimeService {
66
66
  }
67
67
  return {
68
68
  handled: true,
69
- response: await spotManager.dispatchRoutedActorPacket(spotId, actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef)
69
+ response: await spotManager.dispatchRoutedActorPacket('remote', spotId, actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef)
70
70
  };
71
71
  }
72
72
  requireActorManager() {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ZLinkUserSpotCreationCoordinator = exports.ZLinkHostSpotAddressTransport = exports.ZLinkRelocationStateIncompatibleError = exports.ZLinkRouteMeshRuntimeCoordinator = exports.ZLinkRetiringRollbackError = exports.ZLinkDrainingStatePublishError = exports.transferIdString = exports.ZLinkActorTransferAuthorityRuntime = exports.ZLinkFrameworkRuntimeHost = void 0;
4
4
  exports.hasUnsupportedManualTopology = hasUnsupportedManualTopology;
5
5
  exports.hasExactPeerReadiness = hasExactPeerReadiness;
6
+ const application_job_queue_scope_1 = require("../application-jobs/application-job-queue-scope");
6
7
  const location_store_pages_1 = require("../locations/location-store-pages");
7
8
  const stream_wire_1 = require("@zlink-systems/stream-wire");
8
9
  const node_crypto_1 = require("node:crypto");
@@ -21,7 +22,7 @@ const actor_message_follow_context_1 = require("../actors/actor-message-follow-c
21
22
  const actor_transport_delivery_gate_1 = require("../actors/actor-transport-delivery-gate");
22
23
  const admission_1 = require("../admission");
23
24
  const application_job_queue_handler_start_gate_1 = require("../application-jobs/application-job-queue-handler-start-gate");
24
- const application_job_queue_scope_1 = require("../application-jobs/application-job-queue-scope");
25
+ const application_job_queue_scope_2 = require("../application-jobs/application-job-queue-scope");
25
26
  const backend_1 = require("../backend");
26
27
  const runtime_message_1 = require("../backend/runtime-message");
27
28
  const runtime_values_1 = require("../backend/runtime-values");
@@ -161,7 +162,7 @@ class ZLinkFrameworkRuntimeHost {
161
162
  catch {
162
163
  return false;
163
164
  }
164
- });
165
+ }, (error) => (this.errorSink ?? this.preStartErrorSink).reportRuntimeTaskException('logger-provider', error));
165
166
  this.capacityStatus = new host_capacity_status_1.HostCapacityStatusProjection(options.registration.coreHwm, this.applicationJobQueue);
166
167
  this.clientServerRuntime = new topology_runtime_projections_1.ZLinkClientServerRuntimeProjection(() => this.channelRuntime, () => this.runtimeState);
167
168
  this.fanoutRuntime = new topology_runtime_projections_1.ZLinkFanoutRuntimeProjection(() => this.channelRuntime, () => this.runtimeState);
@@ -452,12 +453,12 @@ class ZLinkFrameworkRuntimeHost {
452
453
  },
453
454
  sessionRelocationWire: () => this.serviceRelocation,
454
455
  clearRemoteActorPacketTarget: (actorId) => this.boundSessionRelay.clearRemoteActorPacketTarget(actorId),
455
- prepareApplicationJob: async (preparationSignal) => {
456
+ prepareApplicationJob: async (preparationSignal, origin) => {
456
457
  const runtimeSignal = this.executionState?.abortController.signal;
457
458
  const signal = runtimeSignal === undefined
458
459
  ? preparationSignal
459
460
  : AbortSignal.any([runtimeSignal, preparationSignal]);
460
- const permit = await this.applicationJobQueue.acquire(signal);
461
+ const permit = await this.applicationJobQueue.acquire(signal, origin);
461
462
  let state = 'ready';
462
463
  let closedReason;
463
464
  let abort;
@@ -491,7 +492,7 @@ class ZLinkFrameworkRuntimeHost {
491
492
  state = 'running';
492
493
  signal.removeEventListener('abort', abort);
493
494
  try {
494
- return await (0, application_job_queue_scope_1.runWithApplicationJobPermit)(permit, operation);
495
+ return await (0, application_job_queue_scope_2.runWithApplicationJobPermit)(permit, operation);
495
496
  }
496
497
  finally {
497
498
  state = 'closed';
@@ -1245,6 +1246,7 @@ class ZLinkFrameworkRuntimeHost {
1245
1246
  }
1246
1247
  activationNode.registerAsyncInstanceActivationAuthority?.(new instance_activation_authority_1.ZLinkInstanceActivationAuthority({
1247
1248
  store: locationStore,
1249
+ relocationPolicy: (stableType) => this.options.registration.spotNodes.get(meshName)?.instanceSpotFactoryRegistrations?.[stableType]?.relocation.kind,
1248
1250
  relocationStore: this.options.registration.locations.relocationStoreInstance,
1249
1251
  meshName,
1250
1252
  owner: () => this.locationOwner.currentRuntime?.currentOwnerToken,
@@ -1579,6 +1581,8 @@ class ZLinkFrameworkRuntimeHost {
1579
1581
  });
1580
1582
  const coordinator = (this.userSpotCoordinator = new user_spot_creation_coordinator_1.ZLinkUserSpotCreationCoordinator({
1581
1583
  store: locationStore,
1584
+ relocationPolicy: (meshName, stableType) => this.options.registration.spotNodes.get(meshName)?.spotFactoryRegistrations?.[stableType]
1585
+ ?.relocation.kind,
1582
1586
  publishReadyRoute: (meshName, route) => {
1583
1587
  this.cachedLocationSpotRouteResolver?.invalidate?.(route.spot.spotId);
1584
1588
  this.spotNodeRuntime?.meshNode(meshName)?.rememberSpotRoute?.(route);
@@ -1961,11 +1965,11 @@ class ZLinkFrameworkRuntimeHost {
1961
1965
  ownerLeaseGeneration: record.source.ownerLeaseGeneration
1962
1966
  });
1963
1967
  },
1964
- meshRecordDispatcher: (meshName, owner, record) => this.dispatchMeshRecord(meshName, owner, record, this.executionState?.abortController.signal),
1968
+ meshRecordDispatcher: (meshName, owner, record) => this.dispatchMeshRecord(application_job_queue_scope_1.currentApplicationJobOrigin, meshName, owner, record, this.executionState?.abortController.signal),
1965
1969
  activationConcurrency: (meshName) => this.activationAdmission.current(meshName)
1966
1970
  };
1967
1971
  }
1968
- async dispatchMeshRecord(meshName, owner, record, signal) {
1972
+ async dispatchMeshRecord(applicationJobOrigin, meshName, owner, record, signal) {
1969
1973
  if (record.operationKind === service_runtime_contracts_1.OperationKind.ActorJoin) {
1970
1974
  if (this.spotManager === undefined) {
1971
1975
  throw new ConfigurationException_1.ZLinkConfigurationException('MeshNode Actor join dispatch requires the Spot manager.');
@@ -2023,7 +2027,7 @@ class ZLinkFrameworkRuntimeHost {
2023
2027
  nodeRid: binding.actor.nodeRid
2024
2028
  };
2025
2029
  if (sessionOwnerIsLocal) {
2026
- (0, application_job_queue_scope_1.releaseApplicationJobPermitBeforeHandler)();
2030
+ (0, application_job_queue_scope_2.releaseApplicationJobPermitBeforeHandler)();
2027
2031
  await this.streamBindingRuntime.retireRemoteBinding(actorRef, binding.sessionRid, binding.bindingGeneration, signal);
2028
2032
  }
2029
2033
  state.retireBoundSessionBinding(binding);
@@ -2109,7 +2113,7 @@ class ZLinkFrameworkRuntimeHost {
2109
2113
  if (this.spotManager === undefined) {
2110
2114
  throw new ConfigurationException_1.ZLinkConfigurationException('MeshNode Actor dispatch requires the Spot manager.');
2111
2115
  }
2112
- return this.admission.run(meshName, 'RouteMesh Actor dispatch', () => this.spotManager.dispatchMeshActor(meshName, owner, record));
2116
+ return this.admission.run(meshName, 'RouteMesh Actor dispatch', () => this.spotManager.dispatchMeshActor(applicationJobOrigin(), meshName, owner, record));
2113
2117
  case service_runtime_contracts_1.ReceiveKind.SendReady:
2114
2118
  return Promise.resolve();
2115
2119
  default:
@@ -2143,9 +2147,9 @@ class ZLinkFrameworkRuntimeHost {
2143
2147
  try {
2144
2148
  state.taskRunner.runDetached('mesh-node-local-route-dispatch', async () => {
2145
2149
  try {
2146
- const permit = await this.applicationJobQueue.acquire(state.abortController.signal);
2150
+ const permit = await this.applicationJobQueue.acquire(state.abortController.signal, 'local');
2147
2151
  permit.markApplicationQueued();
2148
- await (0, application_job_queue_scope_1.runWithApplicationJobPermit)(permit, () => channelRuntime.dispatchLocalMeshRoute(meshName, sourceNodeRid, ownedParts));
2152
+ await (0, application_job_queue_scope_2.runWithApplicationJobPermit)(permit, () => channelRuntime.dispatchLocalMeshRoute(meshName, sourceNodeRid, ownedParts));
2149
2153
  }
2150
2154
  finally {
2151
2155
  for (const part of ownedParts)
@@ -5,6 +5,7 @@ import type { ServiceSpotState } from '../foundation/service-stateful-registry';
5
5
  import type { ServiceInstanceActivationTarget, ServiceInstanceRouteFence } from '../foundation/service-stateful-wire-codec';
6
6
  import { type ServiceInstanceActivationRecoveryEnvelope } from '../foundation/service-instance-activation-recovery-codec';
7
7
  export interface ZLinkInstanceActivationAuthorityOptions {
8
+ readonly relocationPolicy?: (stableType: string) => import('../locations/internal-location-contracts').ZLinkObjectReserveRequest['actorRelocationPolicy'];
8
9
  readonly store: ZLinkObjectCreationStore & ZLinkAuthorityStore;
9
10
  readonly relocationStore?: ZLinkRelocationStore;
10
11
  readonly meshName: string;
@@ -16,7 +17,7 @@ export declare class ZLinkInstanceActivationAuthority implements ServiceAsyncIns
16
17
  private readonly options;
17
18
  private readonly pending;
18
19
  constructor(options: ZLinkInstanceActivationAuthorityOptions);
19
- read(target: ServiceInstanceActivationTarget): Promise<ServiceInstanceAuthorityRead>;
20
+ read(target: ServiceInstanceActivationTarget, instanceIntent?: true, activation?: ServiceInstanceActivationRecoveryEnvelope, acceptsLocal?: (route: ServiceInstanceRouteFence) => boolean): Promise<ServiceInstanceAuthorityRead>;
20
21
  reserve(activation: Omit<ServiceInstanceActivationRecoveryEnvelope, 'targetMeshName'>, signal?: AbortSignal): Promise<ServiceInstanceAuthorityReserve>;
21
22
  resume(target: ServiceInstanceActivationTarget, pending: ServicePendingInstanceActivation): Promise<ServiceInstanceActivationReservation>;
22
23
  commit(target: ServiceInstanceActivationTarget, reservation: ServiceInstanceActivationReservation, spot: ServiceSpotState, deadlineUnixMs?: bigint): Promise<{
@@ -17,9 +17,71 @@ class ZLinkInstanceActivationAuthority {
17
17
  constructor(options) {
18
18
  this.options = options;
19
19
  }
20
- async read(target) {
21
- const current = await this.options.store.readAuthority(authorityKey(target.targetSpotId));
22
- return current.kind === 'snapshot' ? readyRead(current, target) : { kind: 'missing' };
20
+ async read(target, instanceIntent, activation, acceptsLocal) {
21
+ const key = authorityKey(target.targetSpotId);
22
+ const current = await this.options.store.readAuthority(key);
23
+ if (current.kind !== 'snapshot')
24
+ return { kind: 'missing' };
25
+ const ready = (0, service_authority_payload_codec_1.decodeServiceInstanceAuthorityPayload)(current.payload);
26
+ const resolved = readyRead(current, target, ready);
27
+ if (ready?.state === 'ready' &&
28
+ resolved.kind === 'ready' &&
29
+ acceptsLocal?.(resolved.route) === true) {
30
+ return resolved;
31
+ }
32
+ if (activation !== undefined &&
33
+ current.allocation.objectKind === 'instance_spot' &&
34
+ (0, routing_id_1.routingIdsEqual)(current.allocation.descriptor.rid, target.targetNodeRid) &&
35
+ current.allocation.descriptorLifecycleGeneration === target.targetNodeGeneration) {
36
+ const reference = current.pendingCreation?.requestContentReference ?? ready?.activationRecovery?.reference;
37
+ if (reference !== undefined) {
38
+ const root = await this.options.relocationStore?.read((0, relocation_blob_1.relocationBlobReference)(reference));
39
+ if (root?.kind !== 'found') {
40
+ throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.DataLost, 'Stored Instance activation is missing.');
41
+ }
42
+ let stored;
43
+ try {
44
+ stored = (0, service_instance_activation_recovery_codec_1.decodeInstanceActivationRecoveryEnvelope)(root.bytes);
45
+ }
46
+ catch (error) {
47
+ if (!(error instanceof TypeError || error instanceof RangeError))
48
+ throw error;
49
+ throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.ProtocolError, 'Stored Instance activation is invalid.');
50
+ }
51
+ if (stored.operation.high === activation.operation.high &&
52
+ stored.operation.low === activation.operation.low &&
53
+ (stored.targetMeshName !== activation.targetMeshName ||
54
+ stored.target.stableType !== target.stableType ||
55
+ stored.target.descriptorVersion !== target.descriptorVersion ||
56
+ stored.deadlineUnixMs !== activation.deadlineUnixMs ||
57
+ (stored.metadataFrame === undefined) !== (activation.metadataFrame === undefined) ||
58
+ (stored.metadataFrame !== undefined &&
59
+ activation.metadataFrame !== undefined &&
60
+ Buffer.compare(stored.metadataFrame, activation.metadataFrame) !== 0))) {
61
+ throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.ProtocolError, 'Instance route does not match the stored activation.');
62
+ }
63
+ }
64
+ }
65
+ if (instanceIntent === true &&
66
+ current.allocation.state === 'active' &&
67
+ ready?.state === 'ready') {
68
+ if (current.allocation.objectKind !== 'instance_spot' ||
69
+ current.allocation.stableType !== target.stableType) {
70
+ throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotTypeMismatch, 'Instance activation type does not match the current authority.');
71
+ }
72
+ const released = await this.options.store.releaseEndedReservation?.(key, current.storeVersion.value, undefined, {
73
+ key: { kind: 'instance_spot', globalId: target.targetSpotId },
74
+ intent: { stableType: target.stableType },
75
+ actorRelocationPolicy: this.options.relocationPolicy?.(target.stableType)
76
+ });
77
+ if (released === true)
78
+ return { kind: 'missing' };
79
+ const refreshed = await this.options.store.readAuthority(key);
80
+ return refreshed.kind === 'snapshot'
81
+ ? readyRead(refreshed, target, (0, service_authority_payload_codec_1.decodeServiceInstanceAuthorityPayload)(refreshed.payload))
82
+ : { kind: 'missing' };
83
+ }
84
+ return resolved;
23
85
  }
24
86
  async reserve(activation, signal) {
25
87
  const target = activation.target;
@@ -49,6 +111,7 @@ class ZLinkInstanceActivationAuthority {
49
111
  try {
50
112
  reserved = await this.options.store.reserve({
51
113
  key: { kind: 'instance_spot', globalId: target.targetSpotId },
114
+ actorRelocationPolicy: this.options.relocationPolicy?.(target.stableType),
52
115
  intent: {
53
116
  stableType: target.stableType,
54
117
  requestContentReference: stored.reference.value,
@@ -207,7 +270,7 @@ class ZLinkInstanceActivationAuthority {
207
270
  }
208
271
  const current = await this.options.store.readAuthority(authorityKey(target.targetSpotId));
209
272
  if (current.kind === 'snapshot') {
210
- const ready = readyRead(current, target);
273
+ const ready = readyRead(current, target, (0, service_authority_payload_codec_1.decodeServiceInstanceAuthorityPayload)(current.payload));
211
274
  if (ready.kind === 'ready') {
212
275
  this.pending.delete(reservation.token);
213
276
  return { kind: 'lost', route: ready.route };
@@ -236,7 +299,7 @@ class ZLinkInstanceActivationAuthority {
236
299
  }
237
300
  const recovery = decoded.activationRecovery;
238
301
  if (recovery === undefined)
239
- return routeFromSnapshot(current);
302
+ return routeFromSnapshot(current, decoded.spotId);
240
303
  let completed = current;
241
304
  if (recovery.replayCursor < recovery.inboxSequence) {
242
305
  const cursorStored = await this.options.store.compareExchangeAuthority(key, current.storeVersion, {
@@ -338,19 +401,16 @@ class ZLinkInstanceActivationAuthority {
338
401
  }
339
402
  }
340
403
  exports.ZLinkInstanceActivationAuthority = ZLinkInstanceActivationAuthority;
341
- function readyRead(snapshot, target) {
342
- const decoded = (0, service_authority_payload_codec_1.decodeServiceReadySpotAuthority)(snapshot.payload) ??
343
- (0, service_authority_payload_codec_1.decodeServiceClosingSpotAuthority)(snapshot.payload);
344
- const creating = (0, service_authority_payload_codec_1.decodeServiceInstanceAuthorityPayload)(snapshot.payload);
345
- if (creating?.state === 'coldActivating' &&
404
+ function readyRead(snapshot, target, decoded) {
405
+ if (decoded?.state === 'coldActivating' &&
346
406
  snapshot.allocation.objectKind === 'instance_spot' &&
347
- creating.stableType === target.stableType &&
348
- creating.spotId === target.targetSpotId &&
349
- creating.ownerId === snapshot.ownerId &&
350
- creating.ownerLeaseGeneration === snapshot.ownerLeaseGeneration &&
351
- creating.ownerMeshName === snapshot.allocation.descriptor.meshName &&
352
- creating.ownerNodeGeneration === snapshot.allocation.descriptorLifecycleGeneration &&
353
- (0, routing_id_1.routingIdsEqual)(creating.ownerNodeRid, snapshot.allocation.descriptor.rid) &&
407
+ decoded.stableType === target.stableType &&
408
+ decoded.spotId === target.targetSpotId &&
409
+ decoded.ownerId === snapshot.ownerId &&
410
+ decoded.ownerLeaseGeneration === snapshot.ownerLeaseGeneration &&
411
+ decoded.ownerMeshName === snapshot.allocation.descriptor.meshName &&
412
+ decoded.ownerNodeGeneration === snapshot.allocation.descriptorLifecycleGeneration &&
413
+ (0, routing_id_1.routingIdsEqual)(decoded.ownerNodeRid, snapshot.allocation.descriptor.rid) &&
354
414
  (0, routing_id_1.routingIdsEqual)(target.targetNodeRid, snapshot.allocation.descriptor.rid) &&
355
415
  target.targetNodeGeneration === snapshot.allocation.descriptorLifecycleGeneration) {
356
416
  return {
@@ -360,7 +420,8 @@ function readyRead(snapshot, target) {
360
420
  authority: snapshot
361
421
  };
362
422
  }
363
- if (decoded?.kind !== 'instance_spot' ||
423
+ if (decoded === undefined ||
424
+ decoded.state === 'coldActivating' ||
364
425
  decoded.stableType !== target.stableType ||
365
426
  decoded.spotId !== target.targetSpotId ||
366
427
  snapshot.allocation.objectKind !== 'instance_spot' ||
@@ -374,7 +435,7 @@ function readyRead(snapshot, target) {
374
435
  target.targetNodeGeneration !== snapshot.allocation.descriptorLifecycleGeneration) {
375
436
  return { kind: 'missing' };
376
437
  }
377
- return { kind: 'ready', route: routeFromSnapshot(snapshot), authority: snapshot };
438
+ return { kind: 'ready', route: routeFromSnapshot(snapshot, decoded.spotId), authority: snapshot };
378
439
  }
379
440
  function requireCommitIdentity(target, reservation, creating, spot, meshName) {
380
441
  if (creating.allocation.state !== 'reserved' ||
@@ -392,16 +453,19 @@ function requireCommitIdentity(target, reservation, creating, spot, meshName) {
392
453
  throw new Error('Instance activation Ready identity does not match its exact reservation.');
393
454
  }
394
455
  }
395
- function routeFromSnapshot(snapshot) {
396
- const decoded = (0, service_authority_payload_codec_1.decodeServiceReadySpotAuthority)(snapshot.payload) ??
397
- (0, service_authority_payload_codec_1.decodeServiceClosingSpotAuthority)(snapshot.payload);
398
- if (decoded?.kind !== 'instance_spot') {
399
- throw new Error('Instance authority is not Ready.');
456
+ function routeFromSnapshot(snapshot, spotId) {
457
+ if (spotId === undefined) {
458
+ const decoded = (0, service_authority_payload_codec_1.decodeServiceReadySpotAuthority)(snapshot.payload) ??
459
+ (0, service_authority_payload_codec_1.decodeServiceClosingSpotAuthority)(snapshot.payload);
460
+ if (decoded?.kind !== 'instance_spot') {
461
+ throw new Error('Instance authority is not Ready.');
462
+ }
463
+ spotId = decoded.spotId;
400
464
  }
401
465
  return {
402
466
  targetNodeRid: String(snapshot.allocation.descriptor.rid),
403
467
  targetNodeGeneration: snapshot.allocation.descriptorLifecycleGeneration,
404
- targetSpotId: decoded.spotId,
468
+ targetSpotId: spotId,
405
469
  objectGeneration: snapshot.objectGeneration,
406
470
  ownerId: snapshot.ownerId,
407
471
  authorityOwnerGeneration: snapshot.authorityOwnerGeneration,
@@ -3227,9 +3227,9 @@ class LocalTargetPort {
3227
3227
  if (runtime === undefined) {
3228
3228
  throw new Error('Relocated Entry Spot Actor dispatch is unavailable.');
3229
3229
  }
3230
- return runtime.dispatchEntryActorPacket(hidden.actor.context.actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef);
3230
+ return runtime.dispatchEntryActorPacket('remote', hidden.actor.context.actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef);
3231
3231
  }
3232
- return this.requireSpotManager().dispatchRoutedActorPacket(state.spotId, hidden.actor.context.actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef);
3232
+ return this.requireSpotManager().dispatchRoutedActorPacket('remote', state.spotId, hidden.actor.context.actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef);
3233
3233
  });
3234
3234
  hidden.replayResults.push(...results);
3235
3235
  }
@@ -31,6 +31,7 @@ export declare class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTr
31
31
  private reportInstanceRequestError;
32
32
  private traceInstanceAddress;
33
33
  private resolveExisting;
34
+ private reactivationCall;
34
35
  private selectMissingTarget;
35
36
  private validateExisting;
36
37
  private encode;
@@ -30,7 +30,10 @@ class ZLinkHostSpotAddressTransport {
30
30
  }
31
31
  async sendToSpotAddressScoped(spotId, message, call) {
32
32
  const activationDeadlineUnixMs = BigInt(Math.round(Date.now() + this.options.defaultRequestTimeoutMs));
33
- const existing = await this.resolveExisting(spotId);
33
+ const resolution = await this.resolveExisting(spotId, undefined, call);
34
+ const existing = resolution !== undefined && !('kind' in resolution) ? resolution : undefined;
35
+ if (resolution !== undefined && 'kind' in resolution)
36
+ call = this.reactivationCall(resolution, call);
34
37
  if (existing !== undefined) {
35
38
  this.validateExisting(existing, call);
36
39
  try {
@@ -64,7 +67,7 @@ class ZLinkHostSpotAddressTransport {
64
67
  if (!call.instanceSpot) {
65
68
  return { status: submission_result_1.ZLinkSubmitStatus.TargetNotFound };
66
69
  }
67
- const selected = this.selectMissingTarget(spotId, call);
70
+ const selected = this.selectMissingTarget(spotId, call, resolution);
68
71
  if (selected.kind === 'unsupported') {
69
72
  this.traceInstanceAddress(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped, ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send, spotId, message, call.initialMeshName, call.instanceSpotType, undefined, ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget);
70
73
  return { status: submission_result_1.ZLinkSubmitStatus.TargetNotFound };
@@ -91,10 +94,13 @@ class ZLinkHostSpotAddressTransport {
91
94
  const timeoutMs = call.timeoutMs ?? this.options.defaultRequestTimeoutMs;
92
95
  const deadline = createSpotAddressDeadline(timeoutMs, call.signal);
93
96
  try {
94
- const existing = await this.resolveExisting(spotId, deadline.signal);
97
+ const resolution = await this.resolveExisting(spotId, deadline.signal, call);
98
+ const existing = resolution !== undefined && !('kind' in resolution) ? resolution : undefined;
99
+ if (resolution !== undefined && 'kind' in resolution)
100
+ call = this.reactivationCall(resolution, call);
95
101
  deadline.requireRemaining();
96
102
  if (existing === undefined) {
97
- return await this.requestToMissingInstance(spotId, request, call, deadline);
103
+ return await this.requestToMissingInstance(spotId, request, call, deadline, resolution);
98
104
  }
99
105
  this.validateExisting(existing, call);
100
106
  try {
@@ -143,11 +149,11 @@ class ZLinkHostSpotAddressTransport {
143
149
  this.traceInstanceAddress(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.ReplyReceived, ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Request, spotId, request, existing.routerChannelId, existing.stableType, existing.targetNodeRid);
144
150
  return reply;
145
151
  }
146
- async requestToMissingInstance(spotId, request, call, deadline) {
152
+ async requestToMissingInstance(spotId, request, call, deadline, current) {
147
153
  if (!call.instanceSpot) {
148
154
  throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RequestTargetNotFound, `Spot '${String(spotId)}' has no Ready authority.`);
149
155
  }
150
- const selected = this.selectMissingTarget(spotId, call);
156
+ const selected = this.selectMissingTarget(spotId, call, current);
151
157
  if (selected.kind === 'capacity') {
152
158
  throw missingInstancePlacementCapacity(spotId, call.instanceSpotType);
153
159
  }
@@ -214,13 +220,15 @@ class ZLinkHostSpotAddressTransport {
214
220
  errorReason
215
221
  });
216
222
  }
217
- async resolveExisting(spotId, signal) {
223
+ async resolveExisting(spotId, signal, call) {
218
224
  const resolver = this.options.resolver();
219
225
  if (resolver === undefined) {
220
226
  throw new Error('Global Spot address resolution requires a Location Store.');
221
227
  }
222
228
  try {
223
- return await (0, abort_1.awaitWithAbort)(resolver.resolve(spotId, signal), signal);
229
+ return await (0, abort_1.awaitWithAbort)(call?.instanceSpot === true
230
+ ? resolver.resolve(spotId, signal, true)
231
+ : resolver.resolve(spotId, signal), signal);
224
232
  }
225
233
  catch (error) {
226
234
  if (error instanceof contracts_1.ZLinkFrameworkException &&
@@ -230,7 +238,11 @@ class ZLinkHostSpotAddressTransport {
230
238
  throw error;
231
239
  }
232
240
  }
233
- selectMissingTarget(spotId, call) {
241
+ reactivationCall(route, call) {
242
+ this.validateExisting(route, call);
243
+ return { ...call, initialMeshName: route.meshName, instanceSpotType: route.stableType };
244
+ }
245
+ selectMissingTarget(spotId, call, current) {
234
246
  const configuredMeshes = this.options.meshNames();
235
247
  if (call.initialMeshName !== undefined &&
236
248
  this.options.isMeshConfigured?.(call.initialMeshName) === false) {
@@ -253,11 +265,11 @@ class ZLinkHostSpotAddressTransport {
253
265
  const stableType = call.instanceSpotType ?? (distinctTypes.length === 1 ? distinctTypes[0] : undefined);
254
266
  if (stableType === undefined) {
255
267
  if (distinctTypes.length === 0)
256
- return { kind: 'unsupported' };
268
+ return { kind: current === undefined ? 'unsupported' : 'unavailable' };
257
269
  throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.InvalidOperation, 'Instance Spot type is required when multiple types are registered.');
258
270
  }
259
271
  if (canInspectPlacementTypes && !distinctTypes.includes(stableType)) {
260
- return { kind: 'unsupported' };
272
+ return { kind: current === undefined ? 'unsupported' : 'unavailable' };
261
273
  }
262
274
  let unavailable = false;
263
275
  let capacity = false;
@@ -276,6 +288,7 @@ class ZLinkHostSpotAddressTransport {
276
288
  node,
277
289
  target: {
278
290
  ...placement.target,
291
+ targetMeshName: meshName,
279
292
  targetSpotId: String(spotId),
280
293
  stableType
281
294
  }
@@ -293,7 +306,7 @@ class ZLinkHostSpotAddressTransport {
293
306
  if (capacity)
294
307
  return { kind: 'capacity' };
295
308
  if (unsupported)
296
- return { kind: 'unsupported' };
309
+ return { kind: current === undefined ? 'unsupported' : 'unavailable' };
297
310
  return { kind: 'unavailable' };
298
311
  }
299
312
  validateExisting(target, call) {
@@ -25,12 +25,12 @@ class ZLinkSpotRuntimeOptionsFactory {
25
25
  ?.notifyPrimaryEntrySpotActorLeft(actor, signal, actorRef, membershipEpoch) ??
26
26
  Promise.resolve()
27
27
  },
28
- dispatchEntryActorPacket: (actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef, requestTerminal, messageFollowOrigin) => {
28
+ dispatchEntryActorPacket: (origin, actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef, requestTerminal, messageFollowOrigin) => {
29
29
  const runtime = this.options.spotNodeRuntime();
30
30
  if (runtime === undefined) {
31
31
  throw new configuration_1.ZLinkConfigurationException('Entry Spot actor packet dispatch requires the MeshNode runtime.');
32
32
  }
33
- return runtime.dispatchEntryActorPacket(actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef, requestTerminal, messageFollowOrigin);
33
+ return runtime.dispatchEntryActorPacket(origin, actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef, requestTerminal, messageFollowOrigin);
34
34
  },
35
35
  dispatchEntryActorJoin: async (meshName, actor, handoffBacklog) => {
36
36
  const runtime = this.options.spotNodeRuntime();
@@ -5,6 +5,7 @@ import type { ZLinkAuthoritySnapshot, ZLinkLocationOwnerToken } from '../locatio
5
5
  import type { ZLinkAuthorityStore, ZLinkObjectCreationStore } from '../locations/internal-store-contracts';
6
6
  import type { ZLinkLocalSpotCreateResult } from '../spots/spot-manager-internal-contracts';
7
7
  export interface ZLinkUserSpotCreationCoordinatorOptions {
8
+ readonly relocationPolicy?: (meshName: string, stableType: string) => import('../locations/internal-location-contracts').ZLinkObjectReserveRequest['actorRelocationPolicy'];
8
9
  readonly store: ZLinkObjectCreationStore & ZLinkAuthorityStore;
9
10
  readonly target: (request: Pick<ZLinkUserSpotCreationRequest, 'meshName' | 'stableType'>, signal?: AbortSignal, excludedNodeRids?: ReadonlySet<string>) => Promise<{
10
11
  readonly meshName: string;
@@ -62,6 +62,7 @@ class ZLinkUserSpotCreationCoordinator {
62
62
  try {
63
63
  reserved = await this.options.store.reserve({
64
64
  key: { kind: 'user_spot', globalId: String(request.spotId) },
65
+ actorRelocationPolicy: this.options.relocationPolicy?.(target.meshName, request.stableType),
65
66
  intent: {
66
67
  stableType: request.stableType,
67
68
  requestContentReference: contentReference,