@masons/agent-network 0.5.32 → 0.5.322

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 (30) hide show
  1. package/dist/broker/broker-daemon.d.ts +17 -0
  2. package/dist/broker/broker-daemon.d.ts.map +1 -1
  3. package/dist/broker/broker-daemon.js +126 -1
  4. package/dist/broker/entry.d.ts.map +1 -1
  5. package/dist/broker/entry.js +7 -1
  6. package/dist/broker/network-presence-changed-event-types.d.ts +8 -0
  7. package/dist/broker/network-presence-changed-event-types.d.ts.map +1 -0
  8. package/dist/broker/network-presence-changed-event-types.js +1 -0
  9. package/dist/broker/network-presence-emitter.d.ts +22 -0
  10. package/dist/broker/network-presence-emitter.d.ts.map +1 -0
  11. package/dist/broker/network-presence-emitter.js +150 -0
  12. package/dist/broker/network-presence.d.ts.map +1 -1
  13. package/dist/broker/received-message-correlation-cache.d.ts +23 -0
  14. package/dist/broker/received-message-correlation-cache.d.ts.map +1 -0
  15. package/dist/broker/received-message-correlation-cache.js +114 -0
  16. package/dist/broker/runtime-endpoint-port.d.ts +12 -0
  17. package/dist/broker/runtime-endpoint-port.d.ts.map +1 -1
  18. package/dist/broker/undispatched-changed-event-types.d.ts +29 -0
  19. package/dist/broker/undispatched-changed-event-types.d.ts.map +1 -0
  20. package/dist/broker/undispatched-changed-event-types.js +14 -0
  21. package/dist/broker/undispatched-emitter.d.ts +22 -0
  22. package/dist/broker/undispatched-emitter.d.ts.map +1 -0
  23. package/dist/broker/undispatched-emitter.js +149 -0
  24. package/dist/runtime-endpoint-client.d.ts +10 -0
  25. package/dist/runtime-endpoint-client.d.ts.map +1 -1
  26. package/dist/runtime-endpoint-client.js +79 -0
  27. package/dist/version.d.ts +1 -1
  28. package/dist/version.d.ts.map +1 -1
  29. package/dist/version.js +1 -1
  30. package/package.json +1 -1
@@ -7,11 +7,13 @@ import type { ControlAck } from "./control-event-types.js";
7
7
  import { type EndpointState } from "./endpoint-state-machine.js";
8
8
  import type { BrokerLogger } from "./logger.js";
9
9
  import { type NetworkPresence } from "./network-presence.js";
10
+ import type { NetworkPresenceChangedEvent } from "./network-presence-changed-event-types.js";
10
11
  import type { BrokerPaths } from "./paths.js";
11
12
  import { type BufferedMessage } from "./reconnecting-buffer.js";
12
13
  import type { RuntimeEndpointPort } from "./runtime-endpoint-port.js";
13
14
  import type { ServicesEventClient } from "./services-event-client.js";
14
15
  import type { SpawnDriverRegistry } from "./spawn-driver.js";
16
+ import { type UndispatchedChangedEvent } from "./undispatched-changed-event-types.js";
15
17
  import { type UndispatchedInbox } from "./undispatched-inbox.js";
16
18
  export interface BrokerDaemonOptions {
17
19
  paths: BrokerPaths;
@@ -35,6 +37,18 @@ export interface BrokerDaemonOptions {
35
37
  logger: BrokerLogger;
36
38
  }) => ServicesEventClient;
37
39
  postControlAck?: (ack: ControlAck) => Promise<void>;
40
+ replyCorrelationTtlMs?: number;
41
+ replyCorrelationCapPerEndpoint?: number;
42
+ undispatchedChangedPost?: (event: UndispatchedChangedEvent) => Promise<import("./runtime-endpoint-port.js").EmitOutcome>;
43
+ undispatchedChangedCapacity?: number;
44
+ undispatchedChangedBackoffInitialMs?: number;
45
+ undispatchedChangedBackoffMaxMs?: number;
46
+ undispatchedChangedMaxRetries?: number;
47
+ networkPresenceChangedPost?: (event: NetworkPresenceChangedEvent) => Promise<import("./runtime-endpoint-port.js").EmitOutcome>;
48
+ networkPresenceChangedCapacity?: number;
49
+ networkPresenceChangedBackoffInitialMs?: number;
50
+ networkPresenceChangedBackoffMaxMs?: number;
51
+ networkPresenceChangedMaxRetries?: number;
38
52
  }
39
53
  export interface RunningBroker {
40
54
  bearerToken: string;
@@ -47,6 +61,9 @@ export interface RunningBroker {
47
61
  networkPresence(): NetworkPresence;
48
62
  pendingSpawnCount(): number;
49
63
  retryQueueSize(): number;
64
+ replyCorrelationCacheSize(endpoint_id: string): number;
65
+ undispatchedChangedQueueSize(): number;
66
+ networkPresenceChangedQueueSize(): number;
50
67
  }
51
68
  export declare function startBrokerDaemon(opts: BrokerDaemonOptions): Promise<RunningBroker>;
52
69
  export declare function readPluginPidHeader(req: IncomingMessage): number | null;
@@ -1 +1 @@
1
- {"version":3,"file":"broker-daemon.d.ts","sourceRoot":"","sources":["../../src/broker/broker-daemon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"broker-daemon.d.ts","sourceRoot":"","sources":["../../src/broker/broker-daemon.ts"],"names":[],"mappings":"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"}
@@ -7,12 +7,16 @@ import { DEFAULT_GRACE_MS, transition, } from "./endpoint-state-machine.js";
7
7
  import { createGraceTimerManager } from "./grace-timer.js";
8
8
  import { BrokerHttpError, pushToPlugin, startIPCServer, } from "./ipc-server.js";
9
9
  import { transitionPresence, } from "./network-presence.js";
10
+ import { createNetworkPresenceEmitter, postNetworkPresenceChangedViaPort, } from "./network-presence-emitter.js";
10
11
  import { isPluginAlive as defaultIsPluginAlive } from "./plugin-liveness.js";
12
+ import { createReceivedMessageCorrelationCache, DEFAULT_TTL_MS as DEFAULT_REPLY_CORRELATION_TTL_MS, } from "./received-message-correlation-cache.js";
11
13
  import { createReconnectingBufferManager, } from "./reconnecting-buffer.js";
12
14
  import { RoutingTable } from "./routing-table.js";
13
15
  import { createSpawnCorrelationManager, createSpawnRateLimiter, } from "./spawn-correlation.js";
14
16
  import { TASK_HINT_MAX_LENGTH, updateTaskHint } from "./task-hint-handler.js";
15
17
  import { createTransitionStateRetryQueue } from "./transition-state-retry-queue.js";
18
+ import { CONTENT_PREVIEW_MAX_CODEPOINTS, truncateContentPreview, } from "./undispatched-changed-event-types.js";
19
+ import { createUndispatchedChangedEmitter, postUndispatchedChangedViaPort, } from "./undispatched-emitter.js";
16
20
  import { createUndispatchedInbox, } from "./undispatched-inbox.js";
17
21
  export async function startBrokerDaemon(opts) {
18
22
  const { paths, asNodeId: _asNodeIdReservedForA3, connector, apiPort, logger, closedEndpointLookup, livenessProbe = defaultIsPluginAlive, spawnDriverRegistry, spawnFn = spawnChild, servicesEventClientFactory, } = opts;
@@ -55,6 +59,46 @@ export async function startBrokerDaemon(opts) {
55
59
  });
56
60
  },
57
61
  });
62
+ const replyCorrelationCache = createReceivedMessageCorrelationCache({
63
+ ttlMs: opts.replyCorrelationTtlMs,
64
+ capPerEndpoint: opts.replyCorrelationCapPerEndpoint,
65
+ });
66
+ const replyCorrelationTtl = opts.replyCorrelationTtlMs ?? DEFAULT_REPLY_CORRELATION_TTL_MS;
67
+ const replyCorrelationSweepInterval = Math.max(Math.floor(replyCorrelationTtl / 4), 60_000);
68
+ const replyCorrelationSweepTimer = setInterval(() => {
69
+ const evicted = replyCorrelationCache.sweep();
70
+ if (evicted > 0) {
71
+ logger.info("reply_correlation_cache_sweep", { evicted });
72
+ }
73
+ }, replyCorrelationSweepInterval);
74
+ replyCorrelationSweepTimer.unref?.();
75
+ const undispatchedEmitter = createUndispatchedChangedEmitter(opts.undispatchedChangedPost ?? postUndispatchedChangedViaPort(apiPort), logger, {
76
+ capacity: opts.undispatchedChangedCapacity,
77
+ backoffInitialMs: opts.undispatchedChangedBackoffInitialMs,
78
+ backoffMaxMs: opts.undispatchedChangedBackoffMaxMs,
79
+ maxRetries: opts.undispatchedChangedMaxRetries,
80
+ });
81
+ const emitUndispatched = (event) => {
82
+ undispatchedEmitter.enqueue(event);
83
+ };
84
+ const networkPresenceEmitter = createNetworkPresenceEmitter(opts.networkPresenceChangedPost ??
85
+ postNetworkPresenceChangedViaPort(apiPort), logger, {
86
+ capacity: opts.networkPresenceChangedCapacity,
87
+ backoffInitialMs: opts.networkPresenceChangedBackoffInitialMs,
88
+ backoffMaxMs: opts.networkPresenceChangedBackoffMaxMs,
89
+ maxRetries: opts.networkPresenceChangedMaxRetries,
90
+ });
91
+ const emitNetworkPresence = (event) => {
92
+ networkPresenceEmitter.enqueue(event);
93
+ };
94
+ const recordInboundCorrelation = (endpoint_id, metadata) => {
95
+ const c = metadata && typeof metadata.correlation_id === "string"
96
+ ? metadata.correlation_id
97
+ : undefined;
98
+ if (!c)
99
+ return;
100
+ replyCorrelationCache.record(endpoint_id, c);
101
+ };
58
102
  let networkPresence = "offline";
59
103
  let presenceGraceTimer;
60
104
  const applyPresenceTransition = (event) => {
@@ -85,6 +129,13 @@ export async function startBrokerDaemon(opts) {
85
129
  presence: effect.presence,
86
130
  reason: effect.reason,
87
131
  });
132
+ emitNetworkPresence({
133
+ type: "network_presence_changed",
134
+ version: 1,
135
+ state: effect.presence,
136
+ ts: Date.now(),
137
+ reason: effect.reason,
138
+ });
88
139
  if (effect.presence === "online") {
89
140
  void retryQueue.flush(async (entry) => {
90
141
  await apiPort.transitionState(entry.endpoint_id, entry.params);
@@ -175,6 +226,7 @@ export async function startBrokerDaemon(opts) {
175
226
  break;
176
227
  case "remove_from_registry":
177
228
  registry.unregister(endpoint_id);
229
+ replyCorrelationCache.forgetEndpoint(endpoint_id);
178
230
  void apiPort.unregister(endpoint_id).catch(() => {
179
231
  });
180
232
  break;
@@ -199,6 +251,13 @@ export async function startBrokerDaemon(opts) {
199
251
  inbox_size: undispatchedInbox.size(),
200
252
  inbox_capacity: undispatchedInbox.capacity(),
201
253
  });
254
+ emitUndispatched({
255
+ type: "undispatched_changed",
256
+ version: 1,
257
+ action: "remove",
258
+ undispatched_id: event.undispatched_id,
259
+ remove_reason: "lost_at_capacity_during_redispatch_bounce",
260
+ });
202
261
  }
203
262
  return {
204
263
  ok: false,
@@ -216,6 +275,7 @@ export async function startBrokerDaemon(opts) {
216
275
  dispatched_by: "passport",
217
276
  },
218
277
  };
278
+ recordInboundCorrelation(target.endpoint_id, stamped.metadata);
219
279
  if (target.state === "active") {
220
280
  pushToPlugin(target.ipc_ws, stamped);
221
281
  }
@@ -237,6 +297,13 @@ export async function startBrokerDaemon(opts) {
237
297
  target_endpoint_id: event.target_endpoint_id,
238
298
  target_state: target.state,
239
299
  });
300
+ emitUndispatched({
301
+ type: "undispatched_changed",
302
+ version: 1,
303
+ action: "dispatch",
304
+ undispatched_id: event.undispatched_id,
305
+ dispatched_to_endpoint_id: event.target_endpoint_id,
306
+ });
240
307
  return { ok: true };
241
308
  });
242
309
  controlEventDispatcher.on("spawn_request", async (event) => {
@@ -413,7 +480,27 @@ export async function startBrokerDaemon(opts) {
413
480
  incoming.source_endpoint_id !== body.endpoint_id) {
414
481
  throw new BrokerHttpError(403, "source_endpoint_id_mismatch", "metadata.source_endpoint_id must equal body.endpoint_id");
415
482
  }
416
- const correlationId = randomUUID();
483
+ const inReplyTo = typeof incoming.in_reply_to === "string"
484
+ ? incoming.in_reply_to
485
+ : undefined;
486
+ let correlationId;
487
+ if (inReplyTo) {
488
+ const resolved = replyCorrelationCache.resolve(body.endpoint_id, inReplyTo);
489
+ if (resolved.ok) {
490
+ correlationId = inReplyTo;
491
+ }
492
+ else {
493
+ logger.warn("reply_aware_fallback", {
494
+ endpoint_id: body.endpoint_id,
495
+ in_reply_to: inReplyTo,
496
+ reason: resolved.reason,
497
+ });
498
+ correlationId = randomUUID();
499
+ }
500
+ }
501
+ else {
502
+ correlationId = randomUUID();
503
+ }
417
504
  entry.correlation_ring.add(correlationId);
418
505
  const metadata = {
419
506
  ...incoming,
@@ -547,6 +634,7 @@ export async function startBrokerDaemon(opts) {
547
634
  const correlation_id = typeof meta.correlation_id === "string" ? meta.correlation_id : undefined;
548
635
  const decision = router.route({ target_endpoint_id, correlation_id });
549
636
  if (decision.kind === "endpoint") {
637
+ recordInboundCorrelation(decision.entry.endpoint_id, meta);
550
638
  if (decision.entry.state === "active") {
551
639
  pushToPlugin(decision.entry.ipc_ws, {
552
640
  event: "message_received",
@@ -613,6 +701,25 @@ export async function startBrokerDaemon(opts) {
613
701
  }
614
702
  else {
615
703
  logger.info("undispatched_added", { id: undispatched.id, reason });
704
+ const wireReason = undispatched.reason === "unaddressed"
705
+ ? "no_target_endpoint"
706
+ : undispatched.reason;
707
+ emitUndispatched({
708
+ type: "undispatched_changed",
709
+ version: 1,
710
+ action: "add",
711
+ undispatched_id: undispatched.id,
712
+ sender_address: undispatched.sender_address,
713
+ content_preview: truncateContentPreview(undispatched.content, CONTENT_PREVIEW_MAX_CODEPOINTS),
714
+ reason: wireReason,
715
+ ...(undispatched.original_target_endpoint_id !== undefined && {
716
+ target_endpoint_id_hint: undispatched.original_target_endpoint_id,
717
+ }),
718
+ ...(typeof correlation_id === "string" && {
719
+ in_reply_to: correlation_id,
720
+ }),
721
+ arrived_at: Date.parse(undispatched.arrived_at),
722
+ });
616
723
  }
617
724
  });
618
725
  connector.on("delivery_pending", ({ payload }) => {
@@ -660,12 +767,27 @@ export async function startBrokerDaemon(opts) {
660
767
  clearTimeout(presenceGraceTimer);
661
768
  presenceGraceTimer = undefined;
662
769
  }
770
+ clearInterval(replyCorrelationSweepTimer);
663
771
  if (retryQueue.size() > 0) {
664
772
  logger.warn("retry_queue_dropped_on_shutdown", {
665
773
  size: retryQueue.size(),
666
774
  reason,
667
775
  });
668
776
  }
777
+ if (undispatchedEmitter.size() > 0) {
778
+ logger.warn("undispatched_changed_queue_dropped_on_shutdown", {
779
+ size: undispatchedEmitter.size(),
780
+ reason,
781
+ });
782
+ }
783
+ await undispatchedEmitter.shutdown();
784
+ if (networkPresenceEmitter.size() > 0) {
785
+ logger.warn("network_presence_changed_queue_dropped_on_shutdown", {
786
+ size: networkPresenceEmitter.size(),
787
+ reason,
788
+ });
789
+ }
790
+ await networkPresenceEmitter.shutdown();
669
791
  const endpoints = registry.list();
670
792
  await Promise.all(endpoints.map(async (entry) => {
671
793
  try {
@@ -700,6 +822,9 @@ export async function startBrokerDaemon(opts) {
700
822
  networkPresence: () => networkPresence,
701
823
  pendingSpawnCount: () => spawnCorrelation.size(),
702
824
  retryQueueSize: () => retryQueue.size(),
825
+ undispatchedChangedQueueSize: () => undispatchedEmitter.size(),
826
+ networkPresenceChangedQueueSize: () => networkPresenceEmitter.size(),
827
+ replyCorrelationCacheSize: (endpoint_id) => replyCorrelationCache.sizeForEndpoint(endpoint_id),
703
828
  };
704
829
  }
705
830
  export function readPluginPidHeader(req) {
@@ -1 +1 @@
1
- {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"AA6EA,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMtE,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA8F9B;AAyDD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,GACnB,mBAAmB,CAkErB;AAYD,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA8F1C"}
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"AA+EA,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMtE,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA8F9B;AAyDD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,GACnB,mBAAmB,CAwGrB;AAYD,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA8F1C"}
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { pathToFileURL } from "node:url";
3
3
  import { readConfig } from "../config-fs.js";
4
4
  import { DEFAULT_API_HOST } from "../platform-client.js";
5
- import { heartbeatRuntimeEndpoint, registerRuntimeEndpoint, transitionRuntimeEndpointState, unregisterRuntimeEndpoint, } from "../runtime-endpoint-client.js";
5
+ import { emitRuntimeNetworkPresenceChanged, emitRuntimeUndispatchedChanged, heartbeatRuntimeEndpoint, registerRuntimeEndpoint, transitionRuntimeEndpointState, unregisterRuntimeEndpoint, } from "../runtime-endpoint-client.js";
6
6
  import { startBrokerDaemon } from "./broker-daemon.js";
7
7
  import { ClaudeCodeSpawnDriver } from "./claude-code-spawn-driver.js";
8
8
  import { CodexSpawnDriverStub } from "./codex-spawn-driver-stub.js";
@@ -132,6 +132,12 @@ export function buildApiPort(apiHost, runtimeKey, logger) {
132
132
  }
133
133
  }
134
134
  },
135
+ async emitUndispatchedChanged(event) {
136
+ return emitRuntimeUndispatchedChanged({ apiHost }, runtimeKey, event);
137
+ },
138
+ async emitNetworkPresenceChanged(event) {
139
+ return emitRuntimeNetworkPresenceChanged({ apiHost }, runtimeKey, event);
140
+ },
135
141
  };
136
142
  }
137
143
  function errorStatus(err) {
@@ -0,0 +1,8 @@
1
+ export interface NetworkPresenceChangedEvent {
2
+ type: "network_presence_changed";
3
+ version: 1;
4
+ state: "online" | "reconnecting" | "offline";
5
+ ts: number;
6
+ reason?: string;
7
+ }
8
+ //# sourceMappingURL=network-presence-changed-event-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-presence-changed-event-types.d.ts","sourceRoot":"","sources":["../../src/broker/network-presence-changed-event-types.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,0BAA0B,CAAC;IAGjC,OAAO,EAAE,CAAC,CAAC;IAEX,KAAK,EAAE,QAAQ,GAAG,cAAc,GAAG,SAAS,CAAC;IAG7C,EAAE,EAAE,MAAM,CAAC;IAMX,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -0,0 +1,22 @@
1
+ import type { BrokerLogger } from "./logger.js";
2
+ import type { NetworkPresenceChangedEvent } from "./network-presence-changed-event-types.js";
3
+ import type { EmitOutcome, RuntimeEndpointPort } from "./runtime-endpoint-port.js";
4
+ export declare const DEFAULT_PRESENCE_EMITTER_CAPACITY = 128;
5
+ export declare const DEFAULT_PRESENCE_EMITTER_BACKOFF_INITIAL_MS = 500;
6
+ export declare const DEFAULT_PRESENCE_EMITTER_BACKOFF_MAX_MS = 30000;
7
+ export declare const DEFAULT_PRESENCE_EMITTER_MAX_RETRIES = 5;
8
+ export interface NetworkPresenceEmitterOptions {
9
+ capacity?: number;
10
+ backoffInitialMs?: number;
11
+ backoffMaxMs?: number;
12
+ maxRetries?: number;
13
+ }
14
+ export interface NetworkPresenceEmitter {
15
+ enqueue(event: NetworkPresenceChangedEvent): boolean;
16
+ size(): number;
17
+ capacity(): number;
18
+ shutdown(): Promise<void>;
19
+ }
20
+ export declare function createNetworkPresenceEmitter(post: (event: NetworkPresenceChangedEvent) => Promise<EmitOutcome>, logger: BrokerLogger, opts?: NetworkPresenceEmitterOptions): NetworkPresenceEmitter;
21
+ export declare function postNetworkPresenceChangedViaPort(apiPort: RuntimeEndpointPort): (event: NetworkPresenceChangedEvent) => Promise<EmitOutcome>;
22
+ //# sourceMappingURL=network-presence-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-presence-emitter.d.ts","sourceRoot":"","sources":["../../src/broker/network-presence-emitter.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AAC7F,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,2CAA2C,MAAM,CAAC;AAC/D,eAAO,MAAM,uCAAuC,QAAS,CAAC;AAC9D,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAEtD,MAAM,WAAW,6BAA6B;IAE5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IAIrC,OAAO,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC;IAErD,IAAI,IAAI,MAAM,CAAC;IAEf,QAAQ,IAAI,MAAM,CAAC;IAGnB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,OAAO,CAAC,WAAW,CAAC,EAClE,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE,6BAAkC,GACvC,sBAAsB,CAyIxB;AAcD,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,mBAAmB,GAC3B,CAAC,KAAK,EAAE,2BAA2B,KAAK,OAAO,CAAC,WAAW,CAAC,CAW9D"}
@@ -0,0 +1,150 @@
1
+ export const DEFAULT_PRESENCE_EMITTER_CAPACITY = 128;
2
+ export const DEFAULT_PRESENCE_EMITTER_BACKOFF_INITIAL_MS = 500;
3
+ export const DEFAULT_PRESENCE_EMITTER_BACKOFF_MAX_MS = 30_000;
4
+ export const DEFAULT_PRESENCE_EMITTER_MAX_RETRIES = 5;
5
+ export function createNetworkPresenceEmitter(post, logger, opts = {}) {
6
+ const cap = opts.capacity ?? DEFAULT_PRESENCE_EMITTER_CAPACITY;
7
+ const initialBackoff = opts.backoffInitialMs ?? DEFAULT_PRESENCE_EMITTER_BACKOFF_INITIAL_MS;
8
+ const maxBackoff = opts.backoffMaxMs ?? DEFAULT_PRESENCE_EMITTER_BACKOFF_MAX_MS;
9
+ const maxRetries = opts.maxRetries ?? DEFAULT_PRESENCE_EMITTER_MAX_RETRIES;
10
+ if (cap <= 0)
11
+ throw new Error(`capacity must be positive: ${cap}`);
12
+ if (initialBackoff <= 0)
13
+ throw new Error(`backoffInitialMs must be positive: ${initialBackoff}`);
14
+ if (maxBackoff < initialBackoff)
15
+ throw new Error(`backoffMaxMs must be >= backoffInitialMs`);
16
+ if (maxRetries < 0)
17
+ throw new Error(`maxRetries must be non-negative: ${maxRetries}`);
18
+ const queue = [];
19
+ let draining = false;
20
+ let stopped = false;
21
+ let activeDrain = null;
22
+ let wakeSleep = null;
23
+ const sleep = (ms) => new Promise((resolve) => {
24
+ const t = setTimeout(() => {
25
+ wakeSleep = null;
26
+ resolve();
27
+ }, ms);
28
+ t.unref?.();
29
+ wakeSleep = () => {
30
+ clearTimeout(t);
31
+ wakeSleep = null;
32
+ resolve();
33
+ };
34
+ });
35
+ const drain = async () => {
36
+ while (!stopped && queue.length > 0) {
37
+ const event = queue[0];
38
+ if (!event)
39
+ break;
40
+ let attempts = 0;
41
+ while (true) {
42
+ if (stopped)
43
+ return;
44
+ let outcome;
45
+ try {
46
+ outcome = await post(event);
47
+ }
48
+ catch (err) {
49
+ outcome = {
50
+ ok: false,
51
+ terminal: false,
52
+ detail: err instanceof Error ? err.message : String(err),
53
+ };
54
+ }
55
+ if (outcome.ok)
56
+ break;
57
+ if (outcome.terminal) {
58
+ logger.warn("network_presence_changed_drop_terminal", {
59
+ state: event.state,
60
+ ts: event.ts,
61
+ reason: event.reason,
62
+ status: outcome.status,
63
+ detail: outcome.detail,
64
+ });
65
+ break;
66
+ }
67
+ attempts++;
68
+ if (attempts > maxRetries) {
69
+ logger.warn("network_presence_changed_drop_max_retries", {
70
+ state: event.state,
71
+ ts: event.ts,
72
+ attempts,
73
+ status: outcome.status,
74
+ detail: outcome.detail,
75
+ });
76
+ break;
77
+ }
78
+ const backoff = Math.min(initialBackoff * 2 ** (attempts - 1), maxBackoff);
79
+ logger.info("network_presence_changed_retry", {
80
+ state: event.state,
81
+ ts: event.ts,
82
+ attempt: attempts,
83
+ backoff_ms: backoff,
84
+ status: outcome.status,
85
+ });
86
+ await sleep(backoff);
87
+ }
88
+ queue.shift();
89
+ }
90
+ draining = false;
91
+ };
92
+ const kick = () => {
93
+ if (draining || stopped)
94
+ return;
95
+ draining = true;
96
+ activeDrain = drain().catch((err) => {
97
+ logger.error("network_presence_changed_drain_unexpected_error", {
98
+ err: err instanceof Error ? err.message : String(err),
99
+ });
100
+ draining = false;
101
+ });
102
+ };
103
+ return {
104
+ enqueue(event) {
105
+ if (stopped)
106
+ return true;
107
+ let evicted = false;
108
+ if (queue.length >= cap) {
109
+ const old = queue.shift();
110
+ if (old) {
111
+ logger.warn("network_presence_changed_queue_overflow", {
112
+ dropped_state: old.state,
113
+ dropped_ts: old.ts,
114
+ size: queue.length,
115
+ capacity: cap,
116
+ });
117
+ evicted = true;
118
+ }
119
+ }
120
+ queue.push(event);
121
+ kick();
122
+ return !evicted;
123
+ },
124
+ size() {
125
+ return queue.length;
126
+ },
127
+ capacity() {
128
+ return cap;
129
+ },
130
+ async shutdown() {
131
+ stopped = true;
132
+ wakeSleep?.();
133
+ if (activeDrain) {
134
+ await activeDrain.catch(() => { });
135
+ }
136
+ },
137
+ };
138
+ }
139
+ export function postNetworkPresenceChangedViaPort(apiPort) {
140
+ return async (event) => {
141
+ if (!apiPort.emitNetworkPresenceChanged) {
142
+ return {
143
+ ok: false,
144
+ terminal: true,
145
+ detail: "emitNetworkPresenceChanged not implemented on this apiPort",
146
+ };
147
+ }
148
+ return apiPort.emitNetworkPresenceChanged(event);
149
+ };
150
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"network-presence.d.ts","sourceRoot":"","sources":["../../src/broker/network-presence.ts"],"names":[],"mappings":"AA8BA,eAAO,MAAM,yBAAyB,QAAiB,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,cAAc,GAAG,SAAS,CAAC;AAEpE,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,qBAAqB,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,wBAAwB,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,wBAAwB,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC;AAEzB,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,oBAAoB,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,aAAa,EACpB,IAAI,GAAE,yBAA8B,GACnC,wBAAwB,CAgH1B"}
1
+ {"version":3,"file":"network-presence.d.ts","sourceRoot":"","sources":["../../src/broker/network-presence.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,yBAAyB,QAAiB,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,cAAc,GAAG,SAAS,CAAC;AAEpE,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,qBAAqB,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,wBAAwB,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,wBAAwB,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC;AAEzB,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,oBAAoB,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,aAAa,EACpB,IAAI,GAAE,yBAA8B,GACnC,wBAAwB,CAgH1B"}
@@ -0,0 +1,23 @@
1
+ export declare const DEFAULT_TTL_MS = 86400000;
2
+ export declare const DEFAULT_CAP_PER_ENDPOINT = 1000;
3
+ export interface ReceivedMessageCorrelationCacheOptions {
4
+ ttlMs?: number;
5
+ capPerEndpoint?: number;
6
+ now?: () => number;
7
+ }
8
+ export type ReplyAwareResolve = {
9
+ ok: true;
10
+ } | {
11
+ ok: false;
12
+ reason: "unknown_correlation_id" | "wrong_endpoint" | "ttl_expired";
13
+ };
14
+ export interface ReceivedMessageCorrelationCache {
15
+ record(endpoint_id: string, correlation_id: string): void;
16
+ resolve(endpoint_id: string, correlation_id: string): ReplyAwareResolve;
17
+ size(): number;
18
+ sizeForEndpoint(endpoint_id: string): number;
19
+ sweep(now?: number): number;
20
+ forgetEndpoint(endpoint_id: string): void;
21
+ }
22
+ export declare function createReceivedMessageCorrelationCache(opts?: ReceivedMessageCorrelationCacheOptions): ReceivedMessageCorrelationCache;
23
+ //# sourceMappingURL=received-message-correlation-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"received-message-correlation-cache.d.ts","sourceRoot":"","sources":["../../src/broker/received-message-correlation-cache.ts"],"names":[],"mappings":"AA8DA,eAAO,MAAM,cAAc,WAAa,CAAC;AAGzC,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,MAAM,WAAW,sCAAsC;IAErD,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,wBAAwB,GAAG,gBAAgB,GAAG,aAAa,CAAC;CACrE,CAAC;AAMN,MAAM,WAAW,+BAA+B;IAa9C,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAQ1D,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,iBAAiB,CAAC;IAGxE,IAAI,IAAI,MAAM,CAAC;IAGf,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAQ7C,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAM5B,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3C;AAED,wBAAgB,qCAAqC,CACnD,IAAI,GAAE,sCAA2C,GAChD,+BAA+B,CAgIjC"}
@@ -0,0 +1,114 @@
1
+ export const DEFAULT_TTL_MS = 86_400_000;
2
+ export const DEFAULT_CAP_PER_ENDPOINT = 1000;
3
+ export function createReceivedMessageCorrelationCache(opts = {}) {
4
+ const ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
5
+ const cap = opts.capPerEndpoint ?? DEFAULT_CAP_PER_ENDPOINT;
6
+ const clock = opts.now ?? Date.now;
7
+ if (ttlMs <= 0)
8
+ throw new Error(`ttlMs must be positive: ${ttlMs}`);
9
+ if (cap <= 0)
10
+ throw new Error(`capPerEndpoint must be positive: ${cap}`);
11
+ const perEndpoint = new Map();
12
+ const correlationOwner = new Map();
13
+ const evict = (endpoint_id, correlation_id) => {
14
+ const inner = perEndpoint.get(endpoint_id);
15
+ if (!inner)
16
+ return;
17
+ inner.delete(correlation_id);
18
+ if (inner.size === 0)
19
+ perEndpoint.delete(endpoint_id);
20
+ if (correlationOwner.get(correlation_id) === endpoint_id) {
21
+ correlationOwner.delete(correlation_id);
22
+ }
23
+ };
24
+ return {
25
+ record(endpoint_id, correlation_id) {
26
+ const priorOwner = correlationOwner.get(correlation_id);
27
+ if (priorOwner && priorOwner !== endpoint_id) {
28
+ const priorInner = perEndpoint.get(priorOwner);
29
+ priorInner?.delete(correlation_id);
30
+ if (priorInner && priorInner.size === 0) {
31
+ perEndpoint.delete(priorOwner);
32
+ }
33
+ }
34
+ let inner = perEndpoint.get(endpoint_id);
35
+ if (!inner) {
36
+ inner = new Map();
37
+ perEndpoint.set(endpoint_id, inner);
38
+ }
39
+ inner.delete(correlation_id);
40
+ inner.set(correlation_id, { received_at: clock() });
41
+ correlationOwner.set(correlation_id, endpoint_id);
42
+ while (inner.size > cap) {
43
+ const oldestKey = inner.keys().next().value;
44
+ if (oldestKey === undefined)
45
+ break;
46
+ inner.delete(oldestKey);
47
+ if (correlationOwner.get(oldestKey) === endpoint_id) {
48
+ correlationOwner.delete(oldestKey);
49
+ }
50
+ }
51
+ },
52
+ resolve(endpoint_id, correlation_id) {
53
+ const owner = correlationOwner.get(correlation_id);
54
+ if (!owner) {
55
+ return { ok: false, reason: "unknown_correlation_id" };
56
+ }
57
+ if (owner !== endpoint_id) {
58
+ return { ok: false, reason: "wrong_endpoint" };
59
+ }
60
+ const inner = perEndpoint.get(endpoint_id);
61
+ const entry = inner?.get(correlation_id);
62
+ if (!entry) {
63
+ correlationOwner.delete(correlation_id);
64
+ return { ok: false, reason: "unknown_correlation_id" };
65
+ }
66
+ if (clock() - entry.received_at >= ttlMs) {
67
+ evict(endpoint_id, correlation_id);
68
+ return { ok: false, reason: "ttl_expired" };
69
+ }
70
+ return { ok: true };
71
+ },
72
+ size() {
73
+ let total = 0;
74
+ for (const inner of perEndpoint.values())
75
+ total += inner.size;
76
+ return total;
77
+ },
78
+ sizeForEndpoint(endpoint_id) {
79
+ return perEndpoint.get(endpoint_id)?.size ?? 0;
80
+ },
81
+ sweep(now = clock()) {
82
+ let evicted = 0;
83
+ for (const [endpoint_id, inner] of perEndpoint) {
84
+ const expired = [];
85
+ for (const [correlation_id, entry] of inner) {
86
+ if (now - entry.received_at >= ttlMs) {
87
+ expired.push(correlation_id);
88
+ }
89
+ }
90
+ for (const correlation_id of expired) {
91
+ inner.delete(correlation_id);
92
+ if (correlationOwner.get(correlation_id) === endpoint_id) {
93
+ correlationOwner.delete(correlation_id);
94
+ }
95
+ evicted++;
96
+ }
97
+ if (inner.size === 0)
98
+ perEndpoint.delete(endpoint_id);
99
+ }
100
+ return evicted;
101
+ },
102
+ forgetEndpoint(endpoint_id) {
103
+ const inner = perEndpoint.get(endpoint_id);
104
+ if (!inner)
105
+ return;
106
+ for (const correlation_id of inner.keys()) {
107
+ if (correlationOwner.get(correlation_id) === endpoint_id) {
108
+ correlationOwner.delete(correlation_id);
109
+ }
110
+ }
111
+ perEndpoint.delete(endpoint_id);
112
+ },
113
+ };
114
+ }
@@ -1,8 +1,20 @@
1
1
  import type { RegisterRuntimeEndpointParams, RegisterRuntimeEndpointResponse, TransitionRuntimeEndpointStateParams } from "../runtime-endpoint-client.js";
2
+ import type { NetworkPresenceChangedEvent } from "./network-presence-changed-event-types.js";
3
+ import type { UndispatchedChangedEvent } from "./undispatched-changed-event-types.js";
4
+ export type EmitOutcome = {
5
+ ok: true;
6
+ } | {
7
+ ok: false;
8
+ terminal: boolean;
9
+ status?: number;
10
+ detail?: string;
11
+ };
2
12
  export interface RuntimeEndpointPort {
3
13
  register(params: RegisterRuntimeEndpointParams): Promise<RegisterRuntimeEndpointResponse>;
4
14
  heartbeat(endpointId: string): Promise<void>;
5
15
  unregister(endpointId: string, asNodeId?: string): Promise<void>;
6
16
  transitionState(endpointId: string, params: TransitionRuntimeEndpointStateParams): Promise<void>;
17
+ emitUndispatchedChanged?(event: UndispatchedChangedEvent): Promise<EmitOutcome>;
18
+ emitNetworkPresenceChanged?(event: NetworkPresenceChangedEvent): Promise<EmitOutcome>;
7
19
  }
8
20
  //# sourceMappingURL=runtime-endpoint-port.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-endpoint-port.d.ts","sourceRoot":"","sources":["../../src/broker/runtime-endpoint-port.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,6BAA6B,EAC7B,+BAA+B,EAC/B,oCAAoC,EACrC,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CACN,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,+BAA+B,CAAC,CAAC;IAC5C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAO7C,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAQjE,eAAe,CACb,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,oCAAoC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
1
+ {"version":3,"file":"runtime-endpoint-port.d.ts","sourceRoot":"","sources":["../../src/broker/runtime-endpoint-port.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,6BAA6B,EAC7B,+BAA+B,EAC/B,oCAAoC,EACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AAC7F,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AAWtF,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CACN,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,+BAA+B,CAAC,CAAC;IAC5C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAO7C,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAQjE,eAAe,CACb,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,oCAAoC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAAC;IAqBjB,uBAAuB,CAAC,CACtB,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,WAAW,CAAC,CAAC;IAYxB,0BAA0B,CAAC,CACzB,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB"}
@@ -0,0 +1,29 @@
1
+ export declare const CONTENT_PREVIEW_MAX_CODEPOINTS = 80;
2
+ export type UndispatchedReason = "no_target_endpoint" | "closed_session_continuation";
3
+ export type UndispatchedRemoveReason = "lost_at_capacity_during_redispatch_bounce";
4
+ interface UndispatchedChangedEventBase {
5
+ type: "undispatched_changed";
6
+ version: 1;
7
+ undispatched_id: string;
8
+ }
9
+ export interface UndispatchedAddEvent extends UndispatchedChangedEventBase {
10
+ action: "add";
11
+ sender_address: string;
12
+ content_preview: string;
13
+ reason: UndispatchedReason;
14
+ target_endpoint_id_hint?: string;
15
+ in_reply_to?: string;
16
+ arrived_at: number;
17
+ }
18
+ export interface UndispatchedDispatchEvent extends UndispatchedChangedEventBase {
19
+ action: "dispatch";
20
+ dispatched_to_endpoint_id: string;
21
+ }
22
+ export interface UndispatchedRemoveEvent extends UndispatchedChangedEventBase {
23
+ action: "remove";
24
+ remove_reason: UndispatchedRemoveReason;
25
+ }
26
+ export type UndispatchedChangedEvent = UndispatchedAddEvent | UndispatchedDispatchEvent | UndispatchedRemoveEvent;
27
+ export declare function truncateContentPreview(raw: string, n?: number): string;
28
+ export {};
29
+ //# sourceMappingURL=undispatched-changed-event-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undispatched-changed-event-types.d.ts","sourceRoot":"","sources":["../../src/broker/undispatched-changed-event-types.ts"],"names":[],"mappings":"AA6BA,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAMjD,MAAM,MAAM,kBAAkB,GAC1B,oBAAoB,GACpB,6BAA6B,CAAC;AAOlC,MAAM,MAAM,wBAAwB,GAWlC,2CAA2C,CAAC;AAG9C,UAAU,4BAA4B;IACpC,IAAI,EAAE,sBAAsB,CAAC;IAG7B,OAAO,EAAE,CAAC,CAAC;IAEX,eAAe,EAAE,MAAM,CAAC;CACzB;AAGD,MAAM,WAAW,oBAAqB,SAAQ,4BAA4B;IACxE,MAAM,EAAE,KAAK,CAAC;IAEd,cAAc,EAAE,MAAM,CAAC;IASvB,eAAe,EAAE,MAAM,CAAC;IAExB,MAAM,EAAE,kBAAkB,CAAC;IAO3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAMjC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,UAAU,EAAE,MAAM,CAAC;CACpB;AAGD,MAAM,WAAW,yBACf,SAAQ,4BAA4B;IACpC,MAAM,EAAE,UAAU,CAAC;IAQnB,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAID,MAAM,WAAW,uBAAwB,SAAQ,4BAA4B;IAC3E,MAAM,EAAE,QAAQ,CAAC;IACjB,aAAa,EAAE,wBAAwB,CAAC;CACzC;AAED,MAAM,MAAM,wBAAwB,GAChC,oBAAoB,GACpB,yBAAyB,GACzB,uBAAuB,CAAC;AAqB5B,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,CAAC,GAAE,MAAuC,GACzC,MAAM,CAYR"}
@@ -0,0 +1,14 @@
1
+ export const CONTENT_PREVIEW_MAX_CODEPOINTS = 80;
2
+ export function truncateContentPreview(raw, n = CONTENT_PREVIEW_MAX_CODEPOINTS) {
3
+ if (raw.length <= n)
4
+ return raw;
5
+ let out = "";
6
+ let count = 0;
7
+ for (const cp of raw) {
8
+ if (count >= n)
9
+ break;
10
+ out += cp;
11
+ count++;
12
+ }
13
+ return out;
14
+ }
@@ -0,0 +1,22 @@
1
+ import type { BrokerLogger } from "./logger.js";
2
+ import type { EmitOutcome, RuntimeEndpointPort } from "./runtime-endpoint-port.js";
3
+ import type { UndispatchedChangedEvent } from "./undispatched-changed-event-types.js";
4
+ export declare const DEFAULT_EMITTER_CAPACITY = 1024;
5
+ export declare const DEFAULT_EMITTER_BACKOFF_INITIAL_MS = 500;
6
+ export declare const DEFAULT_EMITTER_BACKOFF_MAX_MS = 30000;
7
+ export declare const DEFAULT_EMITTER_MAX_RETRIES = 5;
8
+ export interface UndispatchedChangedEmitterOptions {
9
+ capacity?: number;
10
+ backoffInitialMs?: number;
11
+ backoffMaxMs?: number;
12
+ maxRetries?: number;
13
+ }
14
+ export interface UndispatchedChangedEmitter {
15
+ enqueue(event: UndispatchedChangedEvent): boolean;
16
+ size(): number;
17
+ capacity(): number;
18
+ shutdown(): Promise<void>;
19
+ }
20
+ export declare function createUndispatchedChangedEmitter(post: (event: UndispatchedChangedEvent) => Promise<EmitOutcome>, logger: BrokerLogger, opts?: UndispatchedChangedEmitterOptions): UndispatchedChangedEmitter;
21
+ export declare function postUndispatchedChangedViaPort(apiPort: RuntimeEndpointPort): (event: UndispatchedChangedEvent) => Promise<EmitOutcome>;
22
+ //# sourceMappingURL=undispatched-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undispatched-emitter.d.ts","sourceRoot":"","sources":["../../src/broker/undispatched-emitter.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AAEtF,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,kCAAkC,MAAM,CAAC;AACtD,eAAO,MAAM,8BAA8B,QAAS,CAAC;AACrD,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,MAAM,WAAW,iCAAiC;IAEhD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,0BAA0B;IAIzC,OAAO,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC;IAElD,IAAI,IAAI,MAAM,CAAC;IAEf,QAAQ,IAAI,MAAM,CAAC;IAGnB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,OAAO,CAAC,WAAW,CAAC,EAC/D,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE,iCAAsC,GAC3C,0BAA0B,CA8J5B;AAaD,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,mBAAmB,GAC3B,CAAC,KAAK,EAAE,wBAAwB,KAAK,OAAO,CAAC,WAAW,CAAC,CAW3D"}
@@ -0,0 +1,149 @@
1
+ export const DEFAULT_EMITTER_CAPACITY = 1024;
2
+ export const DEFAULT_EMITTER_BACKOFF_INITIAL_MS = 500;
3
+ export const DEFAULT_EMITTER_BACKOFF_MAX_MS = 30_000;
4
+ export const DEFAULT_EMITTER_MAX_RETRIES = 5;
5
+ export function createUndispatchedChangedEmitter(post, logger, opts = {}) {
6
+ const cap = opts.capacity ?? DEFAULT_EMITTER_CAPACITY;
7
+ const initialBackoff = opts.backoffInitialMs ?? DEFAULT_EMITTER_BACKOFF_INITIAL_MS;
8
+ const maxBackoff = opts.backoffMaxMs ?? DEFAULT_EMITTER_BACKOFF_MAX_MS;
9
+ const maxRetries = opts.maxRetries ?? DEFAULT_EMITTER_MAX_RETRIES;
10
+ if (cap <= 0)
11
+ throw new Error(`capacity must be positive: ${cap}`);
12
+ if (initialBackoff <= 0)
13
+ throw new Error(`backoffInitialMs must be positive: ${initialBackoff}`);
14
+ if (maxBackoff < initialBackoff)
15
+ throw new Error(`backoffMaxMs must be >= backoffInitialMs`);
16
+ if (maxRetries < 0)
17
+ throw new Error(`maxRetries must be non-negative: ${maxRetries}`);
18
+ const queue = [];
19
+ let draining = false;
20
+ let stopped = false;
21
+ let activeDrain = null;
22
+ let wakeSleep = null;
23
+ const sleep = (ms) => new Promise((resolve) => {
24
+ const t = setTimeout(() => {
25
+ wakeSleep = null;
26
+ resolve();
27
+ }, ms);
28
+ t.unref?.();
29
+ wakeSleep = () => {
30
+ clearTimeout(t);
31
+ wakeSleep = null;
32
+ resolve();
33
+ };
34
+ });
35
+ const drain = async () => {
36
+ while (!stopped && queue.length > 0) {
37
+ const event = queue[0];
38
+ if (!event)
39
+ break;
40
+ let attempts = 0;
41
+ while (true) {
42
+ if (stopped)
43
+ return;
44
+ let outcome;
45
+ try {
46
+ outcome = await post(event);
47
+ }
48
+ catch (err) {
49
+ outcome = {
50
+ ok: false,
51
+ terminal: false,
52
+ detail: err instanceof Error ? err.message : String(err),
53
+ };
54
+ }
55
+ if (outcome.ok)
56
+ break;
57
+ if (outcome.terminal) {
58
+ logger.warn("undispatched_changed_drop_terminal", {
59
+ action: event.action,
60
+ undispatched_id: event.undispatched_id,
61
+ status: outcome.status,
62
+ detail: outcome.detail,
63
+ });
64
+ break;
65
+ }
66
+ attempts++;
67
+ if (attempts > maxRetries) {
68
+ logger.warn("undispatched_changed_drop_max_retries", {
69
+ action: event.action,
70
+ undispatched_id: event.undispatched_id,
71
+ attempts,
72
+ status: outcome.status,
73
+ detail: outcome.detail,
74
+ });
75
+ break;
76
+ }
77
+ const backoff = Math.min(initialBackoff * 2 ** (attempts - 1), maxBackoff);
78
+ logger.info("undispatched_changed_retry", {
79
+ action: event.action,
80
+ undispatched_id: event.undispatched_id,
81
+ attempt: attempts,
82
+ backoff_ms: backoff,
83
+ status: outcome.status,
84
+ });
85
+ await sleep(backoff);
86
+ }
87
+ queue.shift();
88
+ }
89
+ draining = false;
90
+ };
91
+ const kick = () => {
92
+ if (draining || stopped)
93
+ return;
94
+ draining = true;
95
+ activeDrain = drain().catch((err) => {
96
+ logger.error("undispatched_changed_drain_unexpected_error", {
97
+ err: err instanceof Error ? err.message : String(err),
98
+ });
99
+ draining = false;
100
+ });
101
+ };
102
+ return {
103
+ enqueue(event) {
104
+ if (stopped)
105
+ return true;
106
+ let evicted = false;
107
+ if (queue.length >= cap) {
108
+ const old = queue.shift();
109
+ if (old) {
110
+ logger.warn("undispatched_changed_queue_overflow", {
111
+ dropped_action: old.action,
112
+ dropped_undispatched_id: old.undispatched_id,
113
+ size: queue.length,
114
+ capacity: cap,
115
+ });
116
+ evicted = true;
117
+ }
118
+ }
119
+ queue.push(event);
120
+ kick();
121
+ return !evicted;
122
+ },
123
+ size() {
124
+ return queue.length;
125
+ },
126
+ capacity() {
127
+ return cap;
128
+ },
129
+ async shutdown() {
130
+ stopped = true;
131
+ wakeSleep?.();
132
+ if (activeDrain) {
133
+ await activeDrain.catch(() => { });
134
+ }
135
+ },
136
+ };
137
+ }
138
+ export function postUndispatchedChangedViaPort(apiPort) {
139
+ return async (event) => {
140
+ if (!apiPort.emitUndispatchedChanged) {
141
+ return {
142
+ ok: false,
143
+ terminal: true,
144
+ detail: "emitUndispatchedChanged not implemented on this apiPort",
145
+ };
146
+ }
147
+ return apiPort.emitUndispatchedChanged(event);
148
+ };
149
+ }
@@ -61,4 +61,14 @@ export declare function transitionRuntimeEndpointState(cfg: PlatformClientConfig
61
61
  export declare function unregisterRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, endpointId: string, asNodeId?: string): Promise<UnregisterRuntimeEndpointResponse>;
62
62
  export declare function listOwnedRuntimeEndpoints(cfg: PlatformClientConfig, runtimeKey: string, asNodeId: string): Promise<ListOwnedRuntimeEndpointsResponse>;
63
63
  export declare function getOwnedRuntimeEndpoint(cfg: PlatformClientConfig, runtimeKey: string, endpointId: string, asNodeId: string): Promise<OwnedRuntimeEndpointView>;
64
+ export declare function emitRuntimeUndispatchedChanged(cfg: PlatformClientConfig, runtimeKey: string, event: Readonly<Record<string, unknown>>): Promise<EmitUndispatchedChangedOutcome>;
65
+ export type EmitUndispatchedChangedOutcome = {
66
+ ok: true;
67
+ } | {
68
+ ok: false;
69
+ terminal: boolean;
70
+ status?: number;
71
+ detail?: string;
72
+ };
73
+ export declare function emitRuntimeNetworkPresenceChanged(cfg: PlatformClientConfig, runtimeKey: string, event: Readonly<Record<string, unknown>>): Promise<EmitUndispatchedChangedOutcome>;
64
74
  //# sourceMappingURL=runtime-endpoint-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-endpoint-client.d.ts","sourceRoot":"","sources":["../src/runtime-endpoint-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime-endpoint-client.d.ts","sourceRoot":"","sources":["../src/runtime-endpoint-client.ts"],"names":[],"mappings":"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"}
@@ -82,3 +82,82 @@ export async function getOwnedRuntimeEndpoint(cfg, runtimeKey, endpointId, asNod
82
82
  return handleError(res);
83
83
  return (await res.json());
84
84
  }
85
+ export async function emitRuntimeUndispatchedChanged(cfg, runtimeKey, event) {
86
+ let res;
87
+ try {
88
+ res = await fetch(`${baseUrl(cfg)}/runtime/undispatched-changed`, {
89
+ method: "POST",
90
+ headers: jsonHeaders(runtimeKey),
91
+ body: JSON.stringify(event),
92
+ });
93
+ }
94
+ catch (err) {
95
+ return {
96
+ ok: false,
97
+ terminal: false,
98
+ detail: err instanceof Error ? err.message : String(err),
99
+ };
100
+ }
101
+ if (res.status === 204)
102
+ return { ok: true };
103
+ if (res.status === 401 || res.status === 422) {
104
+ return {
105
+ ok: false,
106
+ terminal: true,
107
+ status: res.status,
108
+ detail: await readErrorDetail(res),
109
+ };
110
+ }
111
+ return {
112
+ ok: false,
113
+ terminal: false,
114
+ status: res.status,
115
+ detail: await readErrorDetail(res),
116
+ };
117
+ }
118
+ export async function emitRuntimeNetworkPresenceChanged(cfg, runtimeKey, event) {
119
+ let res;
120
+ try {
121
+ res = await fetch(`${baseUrl(cfg)}/runtime/network-presence-changed`, {
122
+ method: "POST",
123
+ headers: jsonHeaders(runtimeKey),
124
+ body: JSON.stringify(event),
125
+ });
126
+ }
127
+ catch (err) {
128
+ return {
129
+ ok: false,
130
+ terminal: false,
131
+ detail: err instanceof Error ? err.message : String(err),
132
+ };
133
+ }
134
+ if (res.status === 204)
135
+ return { ok: true };
136
+ if (res.status === 401 || res.status === 422) {
137
+ return {
138
+ ok: false,
139
+ terminal: true,
140
+ status: res.status,
141
+ detail: await readErrorDetail(res),
142
+ };
143
+ }
144
+ return {
145
+ ok: false,
146
+ terminal: false,
147
+ status: res.status,
148
+ detail: await readErrorDetail(res),
149
+ };
150
+ }
151
+ async function readErrorDetail(res) {
152
+ try {
153
+ const body = (await res.json().catch(() => null));
154
+ if (!body)
155
+ return undefined;
156
+ const code = typeof body.error === "string" ? body.error : undefined;
157
+ const message = typeof body.message === "string" ? body.message : undefined;
158
+ return [code, message].filter(Boolean).join(": ") || undefined;
159
+ }
160
+ catch {
161
+ return undefined;
162
+ }
163
+ }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const PLUGIN_VERSION = "0.5.32";
1
+ export declare const PLUGIN_VERSION = "0.5.322";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,cAAc,YAAY,CAAC"}
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const PLUGIN_VERSION = "0.5.32";
1
+ export const PLUGIN_VERSION = "0.5.322";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masons/agent-network",
3
- "version": "0.5.32",
3
+ "version": "0.5.322",
4
4
  "description": "MASONS Agent Network — connects agent runtimes to the Agent Network over MSTP. Ships an OpenClaw channel plugin (root export) and subpath exports consumed by the Claude Code plugin today (planned: Codex and future hosts).",
5
5
  "license": "MIT",
6
6
  "author": "MASONS.ai <hello@masons.ai> (https://masons.ai)",