@zlink-systems/framework 0.22.0 → 0.23.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 (100) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +1 -0
  3. package/dist/contracts/Streams/IZLinkSession.d.ts +2 -1
  4. package/dist/runtime/actors/actor-context.js +4 -4
  5. package/dist/runtime/actors/actor-join-deferred-scope.d.ts +0 -1
  6. package/dist/runtime/actors/actor-join-deferred-scope.js +0 -18
  7. package/dist/runtime/actors/deferred-join-accepted-journal.js +2 -6
  8. package/dist/runtime/backend/contracts/index.d.ts +1 -0
  9. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +3 -0
  10. package/dist/runtime/backend/node/node-raw-mesh-backend.js +21 -8
  11. package/dist/runtime/channels/channel-dispatch-pipeline.js +2 -2
  12. package/dist/runtime/channels/channel-transports.js +3 -9
  13. package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -0
  14. package/dist/runtime/channels/dispatch-error-reporter.js +6 -38
  15. package/dist/runtime/diagnostics/dispatch-error-details.d.ts +6 -0
  16. package/dist/runtime/diagnostics/dispatch-error-details.js +55 -0
  17. package/dist/runtime/diagnostics/flow-context.d.ts +2 -1
  18. package/dist/runtime/diagnostics/flow-context.js +2 -2
  19. package/dist/runtime/diagnostics/message-flow.js +11 -3
  20. package/dist/runtime/foundation/actor-join-recovery-codec.js +0 -4
  21. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +4 -2
  22. package/dist/runtime/foundation/raw-service-mesh-runtime.js +34 -9
  23. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +1 -0
  24. package/dist/runtime/foundation/service-stateful-runtime.d.ts +4 -0
  25. package/dist/runtime/foundation/service-stateful-runtime.js +46 -2
  26. package/dist/runtime/foundation/service-topology-registry.d.ts +1 -0
  27. package/dist/runtime/foundation/service-topology-registry.js +12 -4
  28. package/dist/runtime/host/index.js +1 -1
  29. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +20 -18
  30. package/dist/runtime/spots/index.js +1 -1
  31. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +2 -1
  32. package/dist/runtime/spots/spot-actor-packet-dispatch.js +6 -1
  33. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  34. package/dist/runtime/spots/spot-node-runtime-manager.js +5 -1
  35. package/dist/runtime/spots/spot-route-packet-dispatch.js +1 -1
  36. package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +1 -1
  37. package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
  38. package/dist/runtime/spots/spot-timer.js +1 -1
  39. package/dist/runtime/streams/actor-session-binding-registry.d.ts +21 -8
  40. package/dist/runtime/streams/actor-session-binding-registry.js +71 -42
  41. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +1 -0
  42. package/dist/runtime/streams/bound-actor-relay-sender.js +2 -1
  43. package/dist/runtime/streams/bound-session-response-target.js +8 -1
  44. package/dist/runtime/streams/bound-session-service.d.ts +1 -0
  45. package/dist/runtime/streams/bound-session-service.js +6 -3
  46. package/dist/runtime/streams/index.js +8 -1
  47. package/dist/runtime/streams/managed-stream.d.ts +3 -0
  48. package/dist/runtime/streams/managed-stream.js +20 -1
  49. package/dist/runtime/streams/protocol.d.ts +5 -1
  50. package/dist/runtime/streams/protocol.js +41 -4
  51. package/dist/runtime/streams/session-actor-coordinator.js +3 -4
  52. package/dist/runtime/streams/session-context.d.ts +9 -2
  53. package/dist/runtime/streams/session-context.js +20 -4
  54. package/dist/runtime/streams/session-local-actors.d.ts +3 -1
  55. package/dist/runtime/streams/session-local-actors.js +17 -2
  56. package/dist/runtime/streams/stream-frame-factory.d.ts +1 -1
  57. package/dist/runtime/streams/stream-frame-factory.js +3 -2
  58. package/dist/runtime/streams/stream-session-runtime.js +42 -12
  59. package/package.json +3 -3
  60. package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +1 -0
  61. package/src/contracts/Streams/IZLinkSession.ts +2 -1
  62. package/src/runtime/actors/actor-context.ts +4 -4
  63. package/src/runtime/actors/actor-join-deferred-scope.ts +0 -32
  64. package/src/runtime/actors/deferred-join-accepted-journal.ts +2 -6
  65. package/src/runtime/backend/contracts/index.ts +4 -0
  66. package/src/runtime/backend/node/node-raw-mesh-backend.ts +39 -11
  67. package/src/runtime/channels/channel-dispatch-pipeline.ts +4 -2
  68. package/src/runtime/channels/channel-transports.ts +3 -9
  69. package/src/runtime/channels/dispatch-error-reporter.ts +7 -45
  70. package/src/runtime/diagnostics/dispatch-error-details.ts +62 -0
  71. package/src/runtime/diagnostics/flow-context.ts +6 -4
  72. package/src/runtime/diagnostics/message-flow.ts +12 -3
  73. package/src/runtime/foundation/actor-join-recovery-codec.ts +0 -4
  74. package/src/runtime/foundation/raw-service-mesh-runtime.ts +54 -13
  75. package/src/runtime/foundation/service-session-binding-ingress-port.ts +1 -0
  76. package/src/runtime/foundation/service-stateful-runtime.ts +64 -5
  77. package/src/runtime/foundation/service-topology-registry.ts +18 -5
  78. package/src/runtime/host/index.ts +1 -1
  79. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +18 -17
  80. package/src/runtime/spots/index.ts +2 -1
  81. package/src/runtime/spots/spot-actor-packet-dispatch.ts +20 -3
  82. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  83. package/src/runtime/spots/spot-node-runtime-manager.ts +5 -1
  84. package/src/runtime/spots/spot-route-packet-dispatch.ts +2 -1
  85. package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +2 -1
  86. package/src/runtime/spots/spot-subscription-dispatch.ts +2 -1
  87. package/src/runtime/spots/spot-timer.ts +6 -1
  88. package/src/runtime/streams/actor-session-binding-registry.ts +150 -63
  89. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +1 -0
  90. package/src/runtime/streams/bound-actor-relay-sender.ts +10 -2
  91. package/src/runtime/streams/bound-session-response-target.ts +10 -1
  92. package/src/runtime/streams/bound-session-service.ts +13 -3
  93. package/src/runtime/streams/index.ts +8 -1
  94. package/src/runtime/streams/managed-stream.ts +34 -12
  95. package/src/runtime/streams/protocol.ts +44 -5
  96. package/src/runtime/streams/session-actor-coordinator.ts +12 -4
  97. package/src/runtime/streams/session-context.ts +31 -4
  98. package/src/runtime/streams/session-local-actors.ts +19 -3
  99. package/src/runtime/streams/stream-frame-factory.ts +4 -2
  100. package/src/runtime/streams/stream-session-runtime.ts +58 -13
@@ -70,7 +70,8 @@ export enum ZLinkStreamHeaderFlags {
70
70
  HasMetadata = 0x02,
71
71
  PayloadCompressed = 0x04,
72
72
  HasCorrelationId = 0x08,
73
- HasFlowId = 0x10
73
+ HasFlowId = 0x10,
74
+ HasActorSlot = 0x20
74
75
  }
75
76
 
76
77
  export enum ZLinkStreamCloseReasonCode {
@@ -95,6 +96,7 @@ export interface ZLinkStreamFrameHeader {
95
96
  readonly correlationId?: string;
96
97
  readonly flowId?: string;
97
98
  readonly flowOrigin?: ZLinkFlowOrigin;
99
+ readonly actorSlot?: number;
98
100
  }
99
101
 
100
102
  export type ZLinkStreamReplyMessageKind =
@@ -139,6 +141,28 @@ export function encodeStreamControlFrame(name: string): Uint8Array {
139
141
  );
140
142
  }
141
143
 
144
+ export function encodeActorBoundFrame(actorSlot: number, actorId: string): Uint8Array {
145
+ const actorIdBytes = utf8Encode(actorId);
146
+ if (actorIdBytes.length < 1 || actorIdBytes.length > 0xff) {
147
+ throw new Error('Actor id length is invalid for a STREAM binding control packet.');
148
+ }
149
+ const payload = new Uint8Array(4 + actorIdBytes.length);
150
+ payload[0] = 1;
151
+ payload[1] = actorSlot >>> 8;
152
+ payload[2] = actorSlot & 0xff;
153
+ payload[3] = actorIdBytes.length;
154
+ payload.set(actorIdBytes, 4);
155
+ return encodeStreamFrame(controlHeader('$zlink.actor.bound'), payload);
156
+ }
157
+
158
+ export function encodeActorUnboundFrame(actorSlot: number): Uint8Array {
159
+ const payload = new Uint8Array(3);
160
+ payload[0] = 1;
161
+ payload[1] = actorSlot >>> 8;
162
+ payload[2] = actorSlot & 0xff;
163
+ return encodeStreamFrame(controlHeader('$zlink.actor.unbound'), payload);
164
+ }
165
+
142
166
  export function encodeSessionClosingFrame(
143
167
  diagnostic = '',
144
168
  reason = ZLinkStreamCloseReasonCode.ServerDrain
@@ -176,9 +200,10 @@ export function encodeStreamHeader(header: ZLinkStreamFrameHeader): Uint8Array {
176
200
  const hasMetadata = header.metadata.size > 0;
177
201
  const hasCorrelation = header.correlationId !== undefined && header.correlationId.length > 0;
178
202
  const hasFlow = header.flowId !== undefined || header.flowOrigin !== undefined;
203
+ const hasActorSlot = header.actorSlot !== undefined;
179
204
  if (
180
205
  header.kind === ZLinkStreamMessageKind.Control &&
181
- (hasCorrelation || hasRequestSeq || hasMetadata || hasFlow)
206
+ (hasCorrelation || hasRequestSeq || hasMetadata || hasFlow || hasActorSlot)
182
207
  ) {
183
208
  throw new Error(
184
209
  'Control packet must not contain a request sequence, metadata, correlation id, or flow id.'
@@ -195,7 +220,8 @@ export function encodeStreamHeader(header: ZLinkStreamFrameHeader): Uint8Array {
195
220
  metadata: header.metadata,
196
221
  correlationId: header.correlationId,
197
222
  flowId: header.flowId,
198
- flowOrigin: encodeFlowOrigin(header.flowOrigin)
223
+ flowOrigin: encodeFlowOrigin(header.flowOrigin),
224
+ actorSlot: header.actorSlot
199
225
  });
200
226
  }
201
227
 
@@ -207,9 +233,10 @@ export function decodeStreamHeader(header: Uint8Array, flowEnabled = true): ZLin
207
233
  const hasMetadata = (flags & ZLinkStreamHeaderFlags.HasMetadata) !== 0;
208
234
  const hasCorrelation = (flags & ZLinkStreamHeaderFlags.HasCorrelationId) !== 0;
209
235
  const hasFlow = (flags & ZLinkStreamHeaderFlags.HasFlowId) !== 0;
236
+ const hasActorSlot = (flags & ZLinkStreamHeaderFlags.HasActorSlot) !== 0;
210
237
  if (
211
238
  kind === ZLinkStreamMessageKind.Control &&
212
- (hasCorrelation || hasRequestSeq || hasMetadata || hasFlow)
239
+ (hasCorrelation || hasRequestSeq || hasMetadata || hasFlow || hasActorSlot)
213
240
  ) {
214
241
  throw new Error(
215
242
  'Control packet must not contain a request sequence, metadata, correlation id, or flow id.'
@@ -227,7 +254,8 @@ export function decodeStreamHeader(header: Uint8Array, flowEnabled = true): ZLin
227
254
  metadata,
228
255
  correlationId: decoded.correlationId,
229
256
  flowId: decoded.flowId,
230
- flowOrigin: decodeFlowOrigin(decoded.flowOrigin)
257
+ flowOrigin: decodeFlowOrigin(decoded.flowOrigin),
258
+ actorSlot: decoded.actorSlot
231
259
  };
232
260
  }
233
261
 
@@ -275,10 +303,21 @@ export function createStreamReplyHeader(
275
303
  // observation-only and is not copied into the reply when tracing is Off
276
304
  // (spec 27 §4, §7).
277
305
  correlationId: requestHeader.correlationId,
306
+ actorSlot: requestHeader.actorSlot,
278
307
  ...(includeFlow ? { flowId: requestHeader.flowId, flowOrigin: requestHeader.flowOrigin } : {})
279
308
  };
280
309
  }
281
310
 
311
+ function controlHeader(name: string): ZLinkStreamFrameHeader {
312
+ return {
313
+ kind: ZLinkStreamMessageKind.Control,
314
+ codec: ZLinkStreamCodec.Raw,
315
+ flags: ZLinkStreamHeaderFlags.None,
316
+ name,
317
+ metadata: EMPTY_STREAM_METADATA
318
+ };
319
+ }
320
+
282
321
  function encodeFlowOrigin(origin: ZLinkFlowOrigin | undefined): number | undefined {
283
322
  return origin === undefined
284
323
  ? undefined
@@ -133,7 +133,6 @@ export class ZLinkSessionActorCoordinator {
133
133
  reuseActor ??
134
134
  new DefaultZLinkSessionActor(this.sessionActorRuntime, boundActorRef, bindingToken);
135
135
  const sessionIdentity = String(this.actorBindingRoutingId(context));
136
- sessionActor.updateRef(boundActorRef);
137
136
  if (previous === undefined) {
138
137
  if (releaseSeal !== undefined) {
139
138
  throw createInternalFrameworkException(
@@ -142,7 +141,14 @@ export class ZLinkSessionActorCoordinator {
142
141
  true
143
142
  );
144
143
  }
145
- await this.routes.bind(context, sessionActor, bindingToken, authorityFence, sessionIdentity);
144
+ await this.routes.bind(
145
+ context,
146
+ sessionActor,
147
+ bindingToken,
148
+ authorityFence,
149
+ sessionIdentity,
150
+ () => sessionActor.updateRef(boundActorRef)
151
+ );
146
152
  } else if (releaseSeal !== undefined) {
147
153
  await this.routes.replaceAndReleaseSeal(
148
154
  previous,
@@ -151,7 +157,8 @@ export class ZLinkSessionActorCoordinator {
151
157
  bindingToken,
152
158
  releaseSeal.sealId,
153
159
  authorityFence,
154
- sessionIdentity
160
+ sessionIdentity,
161
+ () => sessionActor.updateRef(boundActorRef)
155
162
  );
156
163
  } else {
157
164
  await this.routes.replace(
@@ -160,7 +167,8 @@ export class ZLinkSessionActorCoordinator {
160
167
  sessionActor,
161
168
  bindingToken,
162
169
  authorityFence,
163
- sessionIdentity
170
+ sessionIdentity,
171
+ () => sessionActor.updateRef(boundActorRef)
164
172
  );
165
173
  }
166
174
  if (
@@ -45,6 +45,8 @@ import { releaseApplicationJobPermitBeforeHandler } from '../application-jobs/ap
45
45
  export interface ZLinkSessionContextStream extends ZLinkStream {
46
46
  writeRaw(payload: Message, flags?: number): boolean;
47
47
  submitRaw(payload: Message, signal?: AbortSignal, timeoutMs?: number): Promise<ZLinkSubmitResult>;
48
+ enqueueActorBound?(actorSlot: number, actorId: string): Promise<void>;
49
+ enqueueActorUnbound?(actorSlot: number): Promise<void>;
48
50
  }
49
51
 
50
52
  interface ZLinkSessionContextRuntime {
@@ -344,8 +346,31 @@ export class DefaultZLinkSessionContext implements ZLinkSessionContext {
344
346
  return this.localActors.find(actorId);
345
347
  }
346
348
 
347
- bindLocal(actor: DefaultZLinkSessionActor, token: string): void {
348
- this.localActors.bind(actor, token);
349
+ actorForSlot(actorSlot: number | undefined): DefaultZLinkSessionActor | undefined {
350
+ if (actorSlot === undefined) return undefined;
351
+ return this.localActors.findBySlot(actorSlot);
352
+ }
353
+
354
+ get actorSlotControls():
355
+ | {
356
+ enqueueBound(actorSlot: number, actorId: string): Promise<void>;
357
+ enqueueUnbound(actorSlot: number): Promise<void>;
358
+ }
359
+ | undefined {
360
+ if (
361
+ this.stream.enqueueActorBound === undefined ||
362
+ this.stream.enqueueActorUnbound === undefined
363
+ ) {
364
+ return undefined;
365
+ }
366
+ return {
367
+ enqueueBound: (actorSlot, actorId) => this.stream.enqueueActorBound!(actorSlot, actorId),
368
+ enqueueUnbound: (actorSlot) => this.stream.enqueueActorUnbound!(actorSlot)
369
+ };
370
+ }
371
+
372
+ bindLocal(actor: DefaultZLinkSessionActor, token: string, actorSlot?: number): void {
373
+ this.localActors.bind(actor, token, actorSlot);
349
374
  }
350
375
 
351
376
  unbindLocal(actorId: string, token: string): void {
@@ -547,12 +572,14 @@ export class DefaultZLinkSessionActor implements ZLinkSessionActor {
547
572
  const SESSION_DISPATCH_HEADERS = new WeakMap<ZLinkSessionDispatchContext, ZLinkStreamFrameHeader>();
548
573
 
549
574
  export function createSessionDispatchContext(
550
- header: ZLinkStreamFrameHeader
575
+ header: ZLinkStreamFrameHeader,
576
+ actor?: DefaultZLinkSessionActor
551
577
  ): ZLinkSessionDispatchContext {
552
578
  const dispatch: ZLinkSessionDispatchContext = {
553
579
  packetName: header.name,
554
580
  metadata: header.metadata,
555
- canReply: header.requestSeq !== undefined
581
+ canReply: header.requestSeq !== undefined,
582
+ ...(actor === undefined ? {} : { actor })
556
583
  };
557
584
  SESSION_DISPATCH_HEADERS.set(dispatch, header);
558
585
  return dispatch;
@@ -3,7 +3,8 @@ export interface ZLinkSessionLocalActor {
3
3
  }
4
4
 
5
5
  export class ZLinkSessionLocalActorBindings<TActor extends ZLinkSessionLocalActor> {
6
- private readonly actors = new Map<string, { actor: TActor; token: string }>();
6
+ private readonly actors = new Map<string, { actor: TActor; token: string; actorSlot?: number }>();
7
+ private readonly actorIdsBySlot = new Map<number, string>();
7
8
 
8
9
  snapshot(): readonly TActor[] {
9
10
  const snapshot: TActor[] = [];
@@ -17,14 +18,29 @@ export class ZLinkSessionLocalActorBindings<TActor extends ZLinkSessionLocalActo
17
18
  return this.actors.get(actorId)?.actor;
18
19
  }
19
20
 
20
- bind(actor: TActor, token: string): void {
21
- this.actors.set(actor.actorId, { actor, token });
21
+ findBySlot(actorSlot: number): TActor | undefined {
22
+ const actorId = this.actorIdsBySlot.get(actorSlot);
23
+ return actorId === undefined ? undefined : this.actors.get(actorId)?.actor;
24
+ }
25
+
26
+ bind(actor: TActor, token: string, actorSlot?: number): void {
27
+ const previous = this.actors.get(actor.actorId);
28
+ if (previous?.actorSlot !== undefined && previous.actorSlot !== actorSlot) {
29
+ this.actorIdsBySlot.delete(previous.actorSlot);
30
+ }
31
+ this.actors.set(actor.actorId, { actor, token, actorSlot });
32
+ if (actorSlot !== undefined) {
33
+ this.actorIdsBySlot.set(actorSlot, actor.actorId);
34
+ }
22
35
  }
23
36
 
24
37
  unbind(actorId: string, token: string): void {
25
38
  const current = this.actors.get(actorId);
26
39
  if (current?.token === token) {
27
40
  this.actors.delete(actorId);
41
+ if (current.actorSlot !== undefined) {
42
+ this.actorIdsBySlot.delete(current.actorSlot);
43
+ }
28
44
  }
29
45
  }
30
46
  }
@@ -74,7 +74,8 @@ export class ZLinkStreamFrameMessageFactory {
74
74
  compressed: boolean,
75
75
  requestSeq: bigint | undefined,
76
76
  payload: unknown,
77
- correlationId?: string
77
+ correlationId?: string,
78
+ actorSlot?: number
78
79
  ): Message {
79
80
  const flow =
80
81
  (this.options.flowCreationEnabled?.() ?? true)
@@ -97,7 +98,8 @@ export class ZLinkStreamFrameMessageFactory {
97
98
  metadata,
98
99
  correlationId,
99
100
  flowId: flow?.flowId,
100
- flowOrigin: flow?.flowOrigin
101
+ flowOrigin: flow?.flowOrigin,
102
+ actorSlot
101
103
  })
102
104
  );
103
105
  }
@@ -4,7 +4,6 @@ import {
4
4
  } from '../framework-errors-internal';
5
5
  import type { ZLinkMessageSerializer, RoutingId, ZLinkSession } from '../../contracts';
6
6
  import type { ZLinkProviderResolver } from '../../contracts/Common/ZLinkProviderResolver';
7
- import { ZLinkFrameworkErrorKind, ZLinkFrameworkException } from '../../contracts';
8
7
  import { ZLinkSocketNativeEventType } from '../diagnostics/internal-event-contracts';
9
8
  import {
10
9
  ZLinkRuntimeMessageFlowOutcome as ZLinkMessageFlowOutcome,
@@ -16,6 +15,7 @@ import {
16
15
  import type { Message } from '../../contracts/Common/Message';
17
16
  import { throwIfAborted } from '../abort';
18
17
  import { ZLinkDispatchErrorReporter, ZLinkRouteDisconnectedError } from '../channels';
18
+ import { boundSessionErrorPayload } from './bound-session-response-target';
19
19
  import { flowIfEnabled } from '../diagnostics';
20
20
  import type { ZLinkRuntimeMetrics } from '../diagnostics';
21
21
  import { wrapFrameworkPayloadMessage } from '../messaging/payload-codec';
@@ -477,6 +477,55 @@ export class ZLinkStreamSessionRuntime {
477
477
  if (this.context.tryCompleteResponse(decodedHeader, dispatchPayload)) {
478
478
  return;
479
479
  }
480
+ const dispatchActor = this.context.actorForSlot(decodedHeader.actorSlot);
481
+ if (decodedHeader.actorSlot !== undefined && dispatchActor === undefined) {
482
+ const staleFlowEnabled = this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true;
483
+ await runWithFlow(
484
+ createInboundFlow(
485
+ decodedHeader.flowId,
486
+ decodedHeader.flowOrigin,
487
+ staleFlowEnabled,
488
+ staleFlowEnabled ? this.stream.sessionId : undefined
489
+ ),
490
+ async () => {
491
+ if (decodedHeader.kind === ZLinkStreamMessageKind.Request) {
492
+ const error = createInternalFrameworkException(
493
+ ZLinkFrameworkInternalErrorKind.InvalidOperation,
494
+ 'Actor slot is not a current binding.'
495
+ );
496
+ this.options.dispatchErrors?.report({
497
+ surface: ZLinkDispatchErrorSurface.StreamSession,
498
+ messageKind: ZLinkDispatchMessageKind.Request,
499
+ reason: ZLinkDispatchErrorReason.StaleTarget,
500
+ action: ZLinkDispatchErrorAction.ReplyError,
501
+ packetName: decodedHeader.name,
502
+ sourceRid:
503
+ this.context.routingId === undefined ? undefined : String(this.context.routingId),
504
+ correlationId: decodedHeader.correlationId,
505
+ flowId: decodedHeader.flowId,
506
+ flowOrigin: decodedHeader.flowOrigin,
507
+ error
508
+ });
509
+ await this.replyDispatchError(decodedHeader, error);
510
+ } else {
511
+ flowIfEnabled(
512
+ this.options.dispatchErrors?.flow,
513
+ ZLinkMessageFlowOutcome.Dropped
514
+ )?.trace({
515
+ outcome: ZLinkMessageFlowOutcome.Dropped,
516
+ surface: ZLinkDispatchErrorSurface.StreamSession,
517
+ messageKind: ZLinkDispatchMessageKind.Send,
518
+ packetName: decodedHeader.name,
519
+ correlationId: decodedHeader.correlationId,
520
+ sourceRid:
521
+ this.context.routingId === undefined ? undefined : String(this.context.routingId),
522
+ errorReason: ZLinkDispatchErrorReason.StaleTarget
523
+ });
524
+ }
525
+ }
526
+ );
527
+ return;
528
+ }
480
529
  this.context.enterDispatch(decodedHeader);
481
530
  enteredDispatch = true;
482
531
  const session = await this.requireSession();
@@ -486,11 +535,13 @@ export class ZLinkStreamSessionRuntime {
486
535
  : ZLinkDispatchMessageKind.Send;
487
536
  const streamCorr = decodedHeader.correlationId;
488
537
  const inboundHeader = decodedHeader;
538
+ const flowEnabled = this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true;
489
539
  await runWithFlow(
490
540
  createInboundFlow(
491
541
  inboundHeader.flowId,
492
542
  inboundHeader.flowOrigin,
493
- this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true
543
+ flowEnabled,
544
+ flowEnabled ? this.stream.sessionId : undefined
494
545
  ),
495
546
  async () => {
496
547
  flowIfEnabled(this.options.dispatchErrors?.flow, ZLinkMessageFlowOutcome.Received)?.trace(
@@ -529,7 +580,7 @@ export class ZLinkStreamSessionRuntime {
529
580
  this.context.routingId === undefined ? undefined : String(this.context.routingId)
530
581
  });
531
582
  await session.onDispatch?.(
532
- createSessionDispatchContext(inboundHeader),
583
+ createSessionDispatchContext(inboundHeader, dispatchActor),
533
584
  wrapFrameworkPayloadMessage(
534
585
  dispatchPayload,
535
586
  this.options.messageSerializers,
@@ -694,15 +745,7 @@ export class ZLinkStreamSessionRuntime {
694
745
  ZLinkStreamMessageKind.Error,
695
746
  new Map(),
696
747
  false,
697
- {
698
- code:
699
- error instanceof ZLinkFrameworkException
700
- ? ZLinkFrameworkErrorKind[error.kind]
701
- : error instanceof Error
702
- ? error.constructor.name
703
- : 'RemoteError',
704
- message: error instanceof Error ? error.message : String(error)
705
- }
748
+ boundSessionErrorPayload(error)
706
749
  );
707
750
  try {
708
751
  if (!this.context.stream.writeRaw(message, ZLINK_SEND_DONT_WAIT)) {
@@ -740,7 +783,8 @@ export class ZLinkStreamSessionRuntime {
740
783
  : ZLinkDispatchMessageKind.Send,
741
784
  packetName: packetName.length === 0 ? undefined : packetName,
742
785
  correlationId,
743
- sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId)
786
+ sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId),
787
+ streamSessionId: this.stream.sessionId
744
788
  });
745
789
  }
746
790
 
@@ -874,6 +918,7 @@ export class ZLinkStreamSessionRuntime {
874
918
  packetName: header.name === '' ? undefined : header.name,
875
919
  correlationId: header.correlationId,
876
920
  sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId),
921
+ streamSessionId: this.stream.sessionId,
877
922
  flowId: flowEnabled ? header.flowId : undefined,
878
923
  flowOrigin: flowEnabled ? header.flowOrigin : undefined,
879
924
  errorReason: ZLinkDispatchErrorReason.Shutdown