agent-comms 6.0.0 → 8.0.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 (165) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +5 -3
  4. package/dist/bridges/claude-code/channel.d.ts.map +1 -1
  5. package/dist/bridges/claude-code/channel.js +2 -2
  6. package/dist/bridges/claude-code/channel.js.map +1 -1
  7. package/dist/bridges/codex/tool.d.ts.map +1 -1
  8. package/dist/bridges/codex/tool.js +2 -2
  9. package/dist/bridges/codex/tool.js.map +1 -1
  10. package/dist/bridges/mcp/server.d.ts.map +1 -1
  11. package/dist/bridges/mcp/server.js +2 -2
  12. package/dist/bridges/mcp/server.js.map +1 -1
  13. package/dist/bridges/opencode/plugin.js +2 -2
  14. package/dist/bridges/opencode/plugin.js.map +1 -1
  15. package/dist/bridges/pi/extension.d.ts.map +1 -1
  16. package/dist/bridges/pi/extension.js +2 -9
  17. package/dist/bridges/pi/extension.js.map +1 -1
  18. package/dist/bridges/user/controller.d.ts +1 -1
  19. package/dist/bridges/user/controller.d.ts.map +1 -1
  20. package/dist/bridges/user/controller.js +4 -4
  21. package/dist/bridges/user/controller.js.map +1 -1
  22. package/dist/bridges/user/web/server.d.ts +5 -11
  23. package/dist/bridges/user/web/server.d.ts.map +1 -1
  24. package/dist/bridges/user/web/server.js +15 -14
  25. package/dist/bridges/user/web/server.js.map +1 -1
  26. package/dist/bridges/user/web/test/bridge-web-server.integration.test.d.ts +2 -0
  27. package/dist/bridges/user/web/test/bridge-web-server.integration.test.d.ts.map +1 -0
  28. package/dist/bridges/user/web/test/bridge-web-server.integration.test.js +47 -0
  29. package/dist/bridges/user/web/test/bridge-web-server.integration.test.js.map +1 -0
  30. package/dist/core/bridge-mesh.d.ts +1 -1
  31. package/dist/core/bridge-mesh.d.ts.map +1 -1
  32. package/dist/core/bridge-mesh.js +2 -1
  33. package/dist/core/bridge-mesh.js.map +1 -1
  34. package/dist/core/gateway-trust.d.ts +5 -5
  35. package/dist/core/gateway-trust.d.ts.map +1 -1
  36. package/dist/core/gateway-trust.js +5 -5
  37. package/dist/core/gateway-trust.js.map +1 -1
  38. package/dist/core/gossip-directory.d.ts +1 -1
  39. package/dist/core/gossip-directory.d.ts.map +1 -1
  40. package/dist/core/gossip-directory.js +4 -2
  41. package/dist/core/gossip-directory.js.map +1 -1
  42. package/dist/core/hub-forwarding.d.ts +4 -18
  43. package/dist/core/hub-forwarding.d.ts.map +1 -1
  44. package/dist/core/hub-forwarding.js +2 -41
  45. package/dist/core/hub-forwarding.js.map +1 -1
  46. package/dist/core/hub-link.d.ts +58 -0
  47. package/dist/core/hub-link.d.ts.map +1 -0
  48. package/dist/core/hub-link.js +118 -0
  49. package/dist/core/hub-link.js.map +1 -0
  50. package/dist/core/hub-session.d.ts +14 -15
  51. package/dist/core/hub-session.d.ts.map +1 -1
  52. package/dist/core/hub-session.js +30 -51
  53. package/dist/core/hub-session.js.map +1 -1
  54. package/dist/core/mesh-graph.d.ts +1 -1
  55. package/dist/core/mesh-graph.js +1 -1
  56. package/dist/core/mesh-store.d.ts +20 -5
  57. package/dist/core/mesh-store.d.ts.map +1 -1
  58. package/dist/core/mesh-store.js +52 -26
  59. package/dist/core/mesh-store.js.map +1 -1
  60. package/dist/core/peer-lifecycle.d.ts +2 -5
  61. package/dist/core/peer-lifecycle.d.ts.map +1 -1
  62. package/dist/core/peer-lifecycle.js +0 -1
  63. package/dist/core/peer-lifecycle.js.map +1 -1
  64. package/dist/core/receiver-set.d.ts +15 -0
  65. package/dist/core/receiver-set.d.ts.map +1 -0
  66. package/dist/core/receiver-set.js +32 -0
  67. package/dist/core/receiver-set.js.map +1 -0
  68. package/dist/core/room-router.d.ts +2 -2
  69. package/dist/core/room-router.d.ts.map +1 -1
  70. package/dist/core/room-router.js +2 -32
  71. package/dist/core/room-router.js.map +1 -1
  72. package/dist/core/shutdown-signals.d.ts +1 -1
  73. package/dist/core/shutdown-signals.js +1 -1
  74. package/dist/core/tool.d.ts +1 -1
  75. package/dist/core/tool.d.ts.map +1 -1
  76. package/dist/core/tool.js +1 -1
  77. package/dist/core/tool.js.map +1 -1
  78. package/dist/core/transport.d.ts +7 -7
  79. package/dist/core/transport.d.ts.map +1 -1
  80. package/dist/core/wire-mesh-transport.d.ts +7 -3
  81. package/dist/core/wire-mesh-transport.d.ts.map +1 -1
  82. package/dist/core/wire-mesh-transport.js +39 -22
  83. package/dist/core/wire-mesh-transport.js.map +1 -1
  84. package/dist/test/bridge-mesh.integration.test.js +15 -1
  85. package/dist/test/bridge-mesh.integration.test.js.map +1 -1
  86. package/dist/test/chat-controller-mesh-errors.integration.test.js +7 -1
  87. package/dist/test/chat-controller-mesh-errors.integration.test.js.map +1 -1
  88. package/dist/test/coordinator-failover-hub.integration.test.d.ts +5 -0
  89. package/dist/test/coordinator-failover-hub.integration.test.d.ts.map +1 -0
  90. package/dist/test/coordinator-failover-hub.integration.test.js +78 -0
  91. package/dist/test/coordinator-failover-hub.integration.test.js.map +1 -0
  92. package/dist/test/coordinator-failover.integration.test.js +3 -6
  93. package/dist/test/coordinator-failover.integration.test.js.map +1 -1
  94. package/dist/test/coordinator-failover.unit.test.js +0 -7
  95. package/dist/test/coordinator-failover.unit.test.js.map +1 -1
  96. package/dist/test/gateway-trust.integration.test.d.ts +1 -1
  97. package/dist/test/gateway-trust.integration.test.js +26 -2
  98. package/dist/test/gateway-trust.integration.test.js.map +1 -1
  99. package/dist/test/gateway-trust.unit.test.js +1 -1
  100. package/dist/test/hub-exposure.integration.test.d.ts +5 -0
  101. package/dist/test/hub-exposure.integration.test.d.ts.map +1 -0
  102. package/dist/test/hub-exposure.integration.test.js +121 -0
  103. package/dist/test/hub-exposure.integration.test.js.map +1 -0
  104. package/dist/test/hub-helpers.d.ts +20 -3
  105. package/dist/test/hub-helpers.d.ts.map +1 -1
  106. package/dist/test/hub-helpers.js +43 -3
  107. package/dist/test/hub-helpers.js.map +1 -1
  108. package/dist/test/hub-link.unit.test.d.ts +2 -0
  109. package/dist/test/hub-link.unit.test.d.ts.map +1 -0
  110. package/dist/test/hub-link.unit.test.js +210 -0
  111. package/dist/test/hub-link.unit.test.js.map +1 -0
  112. package/dist/test/hub-peer-per-store.integration.test.d.ts +9 -0
  113. package/dist/test/hub-peer-per-store.integration.test.d.ts.map +1 -0
  114. package/dist/test/hub-peer-per-store.integration.test.js +211 -0
  115. package/dist/test/hub-peer-per-store.integration.test.js.map +1 -0
  116. package/dist/test/hub-room-domain-verb-trust-bypass.integration.test.js +2 -2
  117. package/dist/test/hub-room-domain-verb-trust-bypass.integration.test.js.map +1 -1
  118. package/dist/test/hub-session-close.integration.test.js +1 -1
  119. package/dist/test/hub-session-close.integration.test.js.map +1 -1
  120. package/dist/test/hub-session-dispatch.unit.test.js +1 -66
  121. package/dist/test/hub-session-dispatch.unit.test.js.map +1 -1
  122. package/dist/test/mesh-graph-trace.integration.test.js +2 -2
  123. package/dist/test/mesh-graph-trace.integration.test.js.map +1 -1
  124. package/dist/test/mesh-smoke.runner.js +1 -3
  125. package/dist/test/mesh-smoke.runner.js.map +1 -1
  126. package/dist/test/mesh-store-delivery-listeners.integration.test.d.ts +2 -0
  127. package/dist/test/mesh-store-delivery-listeners.integration.test.d.ts.map +1 -0
  128. package/dist/test/mesh-store-delivery-listeners.integration.test.js +160 -0
  129. package/dist/test/mesh-store-delivery-listeners.integration.test.js.map +1 -0
  130. package/dist/test/mesh-store-orchestration.unit.test.js +11 -41
  131. package/dist/test/mesh-store-orchestration.unit.test.js.map +1 -1
  132. package/dist/test/peer-lifecycle.unit.test.js +0 -8
  133. package/dist/test/peer-lifecycle.unit.test.js.map +1 -1
  134. package/dist/test/principal-membership-trust.integration.test.js +8 -1
  135. package/dist/test/principal-membership-trust.integration.test.js.map +1 -1
  136. package/dist/test/room-join-by-bare-name.integration.test.js +1 -1
  137. package/dist/test/room-join-by-bare-name.integration.test.js.map +1 -1
  138. package/dist/test/test-transport.d.ts +1 -1
  139. package/dist/test/test-transport.d.ts.map +1 -1
  140. package/dist/test/test-transport.js +1 -1
  141. package/dist/test/test-transport.js.map +1 -1
  142. package/dist/test/wire-mesh-transport-hub.integration.test.d.ts +1 -1
  143. package/dist/test/wire-mesh-transport-hub.integration.test.js +96 -9
  144. package/dist/test/wire-mesh-transport-hub.integration.test.js.map +1 -1
  145. package/package.json +1 -1
  146. package/dist/core/coordinator-gateway.d.ts +0 -25
  147. package/dist/core/coordinator-gateway.d.ts.map +0 -1
  148. package/dist/core/coordinator-gateway.js +0 -34
  149. package/dist/core/coordinator-gateway.js.map +0 -1
  150. package/dist/test/coordinator-gateway.unit.test.d.ts +0 -2
  151. package/dist/test/coordinator-gateway.unit.test.d.ts.map +0 -1
  152. package/dist/test/coordinator-gateway.unit.test.js +0 -88
  153. package/dist/test/coordinator-gateway.unit.test.js.map +0 -1
  154. package/dist/test/gateway-forwarding.integration.test.d.ts +0 -9
  155. package/dist/test/gateway-forwarding.integration.test.d.ts.map +0 -1
  156. package/dist/test/gateway-forwarding.integration.test.js +0 -226
  157. package/dist/test/gateway-forwarding.integration.test.js.map +0 -1
  158. package/dist/test/hub-forwarding.unit.test.d.ts +0 -2
  159. package/dist/test/hub-forwarding.unit.test.d.ts.map +0 -1
  160. package/dist/test/hub-forwarding.unit.test.js +0 -102
  161. package/dist/test/hub-forwarding.unit.test.js.map +0 -1
  162. package/dist/test/hub-todevice-routing.integration.test.d.ts +0 -7
  163. package/dist/test/hub-todevice-routing.integration.test.d.ts.map +0 -1
  164. package/dist/test/hub-todevice-routing.integration.test.js +0 -121
  165. package/dist/test/hub-todevice-routing.integration.test.js.map +0 -1
@@ -0,0 +1,118 @@
1
+ /**
2
+ * HubLink keeps one store's own session on the relay hub alive while the store should have one: it dials in the background, waits for the session to end, and dials again after a delay that doubles up to a ceiling while the hub stays unreachable. Every store owns one (agent-comms#293), because reachability from another machine has to belong to the device itself and not to whichever store on the machine happens to hold the local coordinator role.
3
+ *
4
+ * The store decides through shouldConnect whether it wants a session at all, and a change of mind, made in this process or by another one editing the shared trust file, is picked up within a poll interval, or at once through reconsider(). A store with nothing to say to another machine (nothing trusted, no agent, or a ghost agent) holds no session, so it puts nothing on the hub, not even its device id.
5
+ *
6
+ * The dial never blocks the caller: a bridge starting up offline must not wait out a connect timeout before it can serve its tools, and a hub that is down or restarting is an ordinary, recoverable state. Failures reach onError and the loop carries on.
7
+ */
8
+ /** Delay before the first redial after a failed dial or a lost session. */
9
+ export const HUB_RECONNECT_INITIAL_DELAY_MS = 1000;
10
+ /** Ceiling the redial delay doubles up to while the hub stays unreachable, so an outage is retried about once a minute rather than backing off without bound. */
11
+ export const HUB_RECONNECT_MAX_DELAY_MS = 60_000;
12
+ /** How often a link re-evaluates shouldConnect while it has nothing to react to: a store that wants no session notices it now wants one, and a store holding one notices it no longer should, without anyone calling reconsider(). Bounds how long a trust change made by another process on the machine takes to show. */
13
+ export const HUB_LINK_POLL_INTERVAL_MS = 5000;
14
+ /** Factor the redial delay grows by after each failed dial. */
15
+ const RECONNECT_DELAY_GROWTH_FACTOR = 2;
16
+ export class HubLink {
17
+ options;
18
+ initialDelayMs;
19
+ maxDelayMs;
20
+ pollIntervalMs;
21
+ stopped = false;
22
+ running;
23
+ /** Cancels the wait between dials, so stop() does not sit out a delay. */
24
+ wake;
25
+ constructor(options) {
26
+ this.options = options;
27
+ this.initialDelayMs =
28
+ options.initialDelayMs ?? HUB_RECONNECT_INITIAL_DELAY_MS;
29
+ this.maxDelayMs = options.maxDelayMs ?? HUB_RECONNECT_MAX_DELAY_MS;
30
+ this.pollIntervalMs = options.pollIntervalMs ?? HUB_LINK_POLL_INTERVAL_MS;
31
+ }
32
+ /** Begins running in the background and returns at once. A second call while running does nothing. */
33
+ start() {
34
+ if (this.running !== undefined || this.stopped)
35
+ return;
36
+ this.running = this.run();
37
+ }
38
+ /** Re-evaluates shouldConnect now instead of at the next poll, and cuts short any wait, including the delay before a redial. Call it when something shouldConnect reads has changed. */
39
+ reconsider() {
40
+ this.wake?.();
41
+ }
42
+ /** Stops, drops the live session, and resolves once the loop has ended. */
43
+ async stop() {
44
+ this.stopped = true;
45
+ this.wake?.();
46
+ await this.options.hub.disconnect();
47
+ await this.running;
48
+ }
49
+ /** A method call rather than a direct property read, so TypeScript's control-flow narrowing does not treat the flag as staying false across the awaits in run(), during which stop() can set it. */
50
+ isStopped() {
51
+ return this.stopped;
52
+ }
53
+ async run() {
54
+ let delayMs = this.initialDelayMs;
55
+ while (!this.isStopped()) {
56
+ if (!this.options.shouldConnect()) {
57
+ await this.sleep(this.pollIntervalMs);
58
+ continue;
59
+ }
60
+ let sessionEnded = false;
61
+ try {
62
+ await this.options.hub.connect(this.options.url);
63
+ if (this.options.hub.isConnected) {
64
+ delayMs = this.initialDelayMs;
65
+ this.options.onConnected();
66
+ sessionEnded = await this.holdWhileWanted();
67
+ }
68
+ }
69
+ catch (error) {
70
+ this.options.onError(error instanceof Error ? error : new Error(String(error)));
71
+ sessionEnded = true;
72
+ }
73
+ if (this.isStopped())
74
+ return;
75
+ if (!sessionEnded)
76
+ continue;
77
+ await this.sleep(delayMs);
78
+ delayMs = Math.min(delayMs * RECONNECT_DELAY_GROWTH_FACTOR, this.maxDelayMs);
79
+ }
80
+ }
81
+ /** Holds the live session until the hub ends it, resolving true, or until the store stops wanting it, when it drops the session itself and resolves false. */
82
+ async holdWhileWanted() {
83
+ const hubEnded = this.options.hub
84
+ .whenDisconnected()
85
+ .then(() => "ended");
86
+ while (!this.isStopped()) {
87
+ const outcome = await Promise.race([
88
+ hubEnded,
89
+ this.sleep(this.pollIntervalMs).then(() => "woke"),
90
+ ]);
91
+ if (outcome === "ended")
92
+ return true;
93
+ if (this.isStopped())
94
+ return false;
95
+ if (!this.options.shouldConnect()) {
96
+ await this.options.hub.disconnect();
97
+ return false;
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+ /** Waits ms, or until reconsider() or stop() cuts it short. Each wait clears the wake handle only if it is still its own, since holdWhileWanted leaves a losing wait pending and its late end must not wipe the handle of the wait that replaced it. */
103
+ async sleep(ms) {
104
+ let wakeThis;
105
+ await new Promise((resolve) => {
106
+ const timer = setTimeout(resolve, ms);
107
+ timer.unref();
108
+ wakeThis = () => {
109
+ clearTimeout(timer);
110
+ resolve();
111
+ };
112
+ this.wake = wakeThis;
113
+ });
114
+ if (this.wake === wakeThis)
115
+ this.wake = undefined;
116
+ }
117
+ }
118
+ //# sourceMappingURL=hub-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub-link.js","sourceRoot":"","sources":["../../src/core/hub-link.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,8 @@ export interface HubSessionDeps {
22
22
  onFrame: (connection: Readonly<Connection>, frame: Readonly<Frame>) => void | Promise<void>;
23
23
  /** Tracks the session for shutdown -- every session the transport ever creates, always. */
24
24
  trackForShutdown: (session: AcceptedMeshSession) => void;
25
+ /** Stops tracking a session that has ended, so a store that redials the hub for as long as it runs does not accumulate dead sessions the transport would keep gossiping to. */
26
+ untrack: (session: AcceptedMeshSession) => void;
25
27
  /** Fires with every remote (non-self) directory entry the hub's own gossip/catch-up surfaces, every time the session's directory changes (agent-comms#155's own remote-directory-merge leg) -- the transport merges these into its own mesh-wide knownDevices the same way it already merges a local peer session's directory, so a hub-learned agent surfaces in listAgents/getAgent with no separate lookup path. */
26
28
  onDirectory: (entries: readonly DirectoryEntry[]) => void;
27
29
  /** Dispatches an already-received, non-legacy-frame request (a real core/room verb: room.send, room.notify, room.join, ...) to WireMeshTransport's own roomRouter through its relayed-request entry point, which shares the room-verb dispatch a local peer session's own drainSession uses but never routes a legacy frame -- the outbound half of agent-comms#155's "remote to local" routing leg. Deferred the same lazy-`this`-capture way DeliveryEngine's own sendRoomRequestToMember closure is, since roomRouter is constructed after this class's own instance in WireMeshTransport's constructor. Declared with the identical (request, handle, origin?) shape RoomRouter.handleRequest itself has (agent-comms#216) so WireMeshTransport can keep wiring this dep as a direct method reference rather than a wrapper -- dispatchHubRequest below is what actually supplies a non-empty origin, carrying this side's own dialled hub address. */
@@ -30,17 +32,15 @@ export interface HubSessionDeps {
30
32
  isTrusted: (deviceHex: string) => boolean;
31
33
  /** Whether a gossiped directory entry may be merged (agent-comms#156, widened by #187 and #266): its device is trusted by id, or is itself a trusted principal's own device (agent-comms#192), or carries a membership proof a trusted principal vouches for. A gossiped advert is self-asserted, so the proof is what makes trusting a principal cover its devices; verifying one is cryptography, hence asynchronous. */
32
34
  admitEntries: (entries: readonly DirectoryEntry[]) => Promise<DirectoryEntry[]>;
33
- /** Forwards a room-domain manage-request on to a specific LOCAL peer session (one this gateway is directly connected to over the ordinary local mesh, keyed by device-id hex) rather than dispatching it against this gateway's own local state -- consume()'s own toDevice disambiguation (agent-comms#184, wire-mesh-core 1.48.1's IncomingManageRequest.toDevice). Returns undefined when no local session exists for that device-id, in which case consume() falls back to handleRoomRequest exactly as it always has. */
34
- forwardToLocalPeer: (deviceHex: string, command: ManageCommand, scope: Readonly<CapabilityScope>, token?: CapabilityToken) => Promise<ManageOutcome> | undefined;
35
35
  }
36
36
  export declare class HubSession {
37
37
  private readonly deps;
38
38
  private session;
39
- /** The raw connection underlying `session` -- MeshSession's own sendGossipUpdate only ever advertises this side's own single device, so advertising OTHER (local mesh) devices onto the hub (agent-comms#155's outbound leg) needs a raw gossip frame sent directly, bypassing that per-session single-self-advert limit. */
40
- private connection;
41
39
  /** The URL this side itself dialled to reach the hub -- tracePath's own local.hubAddress (agent-comms#199), the equivalent of wire-mesh-core's own TracePathOptions.localHubAddress. */
42
40
  private url;
43
41
  private readonly hubPeersKnown;
42
+ /** Resolvers of every whenDisconnected() call still waiting for the live session to end. */
43
+ private readonly disconnectWaiters;
44
44
  constructor(deps: Readonly<HubSessionDeps>);
45
45
  /** This node's own device-id, hex-encoded -- the identifier hub peers address it by. */
46
46
  ownDeviceHex(): Promise<string>;
@@ -48,6 +48,10 @@ export declare class HubSession {
48
48
  get isConnected(): boolean;
49
49
  /** Whether the given session object is this side's own currently-held hub session -- lets WireMeshTransport distinguish the hub's session from an ordinary local-peer session within its own allSessions bookkeeping (agent-comms#156's own readvertiseGossip gate needs this) without this class ever exposing the raw session object itself. */
50
50
  ownsSession(session: AcceptedMeshSession): boolean;
51
+ /** Resolves once no hub session is live: at once when none is, otherwise when the current one ends, whether the hub closed it or disconnect() dropped it. HubLink waits on this to know when to dial again. */
52
+ whenDisconnected(): Promise<void>;
53
+ /** Forgets the held session and releases everyone waiting in whenDisconnected(). */
54
+ private clearSession;
51
55
  /** Drops the held hub connection. A no-op if none is live (connect() was never called, disconnect() already ran, or the hub itself already closed the session). */
52
56
  disconnect(): Promise<void>;
53
57
  /** The device ids (hex) of peers discovered through the hub's gossiped directory. */
@@ -57,36 +61,31 @@ export declare class HubSession {
57
61
  /**
58
62
  * The single consumer of one hub session's own event stream: merges the hub's directory (its catch-up arrives as the first event, and it is refreshed on every subsequent one) and, once the session ends, drops the held connection so isConnected answers honestly.
59
63
  *
60
- * Single is the operative word. wire-mesh-core delivers each event to exactly one waiting reader, so two concurrent loops over the same stream split it between them: the closed event reaches whichever of them happened to be waiting first. When that was the directory loop, the connection stayed recorded as live for the rest of the process, and every later advertiseDevices threw on a socket that was already gone -- once per directory change, which is what filled a replacement coordinator's stderr with "connection is closed" after the previous coordinator was killed.
64
+ * Single is the operative word. wire-mesh-core delivers each event to exactly one waiting reader, so two concurrent loops over the same stream split it between them, and the closed event reaches whichever happened to be waiting first. If that is the directory loop, the connection stays recorded as live for the rest of the process.
61
65
  *
62
66
  * Every trusted remote entry is surfaced via onDirectory, so the transport can merge it into its own mesh-wide knownDevices (agent-comms#155's remote-directory-merge leg). Filtered to admitEntries (agent-comms#156, widened by agent-comms#187's principal allowlist per agent-comms#192 and by #266's membership proofs) before either hubPeersKnown tracking or onDirectory sees it: an untrusted device's gossiped presence is not merely withheld from listAgents, it is never even recorded as "known" here, so nothing downstream can act on it via any path this class exposes.
63
67
  */
64
68
  private consumeEvents;
65
- /** Consumes one hub session's inbound relayed manage-requests until it ends, dispatching each in arrival order to dispatchHubRequest -- see that function's own doc for the actual per-request trust decision. Split out purely so the decision itself is directly unit-testable against fake requests/deps (hub-session-dispatch.test.ts), the same reason hub-forwarding.ts's own forwardAdvertsToHub/pushHubCatchUp are standalone functions rather than private methods. Passes this.url through as dispatchHubRequest's own hubAddress -- this side's own dialled address for the hub every request on this specific session arrived over (agent-comms#216), the answering-side equivalent of tracePath's own local.hubAddress above. */
69
+ /** Consumes one hub session's inbound relayed manage-requests until it ends, dispatching each in arrival order to dispatchHubRequest -- see that function's own doc for the actual per-request trust decision. Split out purely so the decision itself is directly unit-testable against fake requests/deps (hub-session-dispatch.test.ts), the same reason hub-forwarding.ts's own routeRoomRequestViaHub is a standalone function rather than a private method. Passes this.url through as dispatchHubRequest's own hubAddress -- this side's own dialled address for the hub every request on this specific session arrived over (agent-comms#216), the answering-side equivalent of tracePath's own local.hubAddress above. */
66
70
  private consume;
67
- /** Gossips a raw `gossip` frame carrying every given entry's own advert onto the hub, over the raw connection rather than sendGossipUpdate (which can only ever advertise this side's own single device) -- the outbound leg of agent-comms#155's gateway forwarding. relay-hub.ts registers each advert's own `device` field against the CONNECTION it arrives on, so every entry passed here becomes reachable, cross-machine, as "via this gateway" -- callers are responsible for only ever passing entries that are actually meant to be advertised (WireMeshTransport filters to local, visible-agent-bearing entries before calling this). Throws if this side isn't currently connected to a hub, matching sendToPeer's own contract -- callers gate on isConnected first. */
68
- advertiseDevices(entries: readonly DirectoryEntry[]): Promise<void>;
69
71
  /** Sends a real core/room manage-request to a specific hub-reachable peer, through the hub's relay-connect/relay-data pairing -- the local-to-remote leg of agent-comms#155's routing, mirroring MeshTransport.sendRoomRequest's own not_connected/outcome contract so WireMeshTransport can fall back to this uniformly when memberId isn't a local peer session. Bounded by HUB_ROOM_REQUEST_TIMEOUT_MS (see its own doc) since an unreachable target-device is silently dropped by the hub with no error frame, unlike a local session's own connection-level failure. */
70
72
  sendRoomRequest(peerDeviceHex: string, command: ManageCommand, scope: Readonly<CapabilityScope>, token?: CapabilityToken): Promise<ManageOutcome>;
71
73
  /** Sends path.trace to a specific hub-reachable peer, through the hub's relay-connect/relay-data pairing -- WireMeshTransport.meshTrace's own hub-relayed fallback (agent-comms#199), mirroring sendRoomRequest's own not_connected contract and HUB_ROOM_REQUEST_TIMEOUT_MS default for the identical reason (an unreachable target-device is silently dropped by the hub, not answered with an error). Delegates the actual send/RTT-measurement/response-parsing to wire-mesh-core's own tracePath, reporting this.url (the address this side itself dialled to reach the hub) as local.hubAddress. */
72
74
  tracePath(peerDeviceHex: string, timeoutMs?: number): Promise<MeshTraceResult>;
73
75
  }
74
76
  /** The slice of HubSessionDeps dispatchHubRequest actually needs. Named so its own tests, and consume()'s call site, don't repeat the same Pick inline. */
75
- export type HubRequestDispatchDeps = Pick<HubSessionDeps, "isTrusted" | "handleRoomRequest" | "forwardToLocalPeer">;
77
+ export type HubRequestDispatchDeps = Pick<HubSessionDeps, "isTrusted" | "handleRoomRequest">;
76
78
  /**
77
- * Decides what to do with one inbound relayed manage-request, keeping every downstream consumer's handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests) rather than by this gateway, exactly as it already did for the legacy opaque-frame path and now also for a real room-domain verb (agent-comms#155's "remote to local" leg).
79
+ * Decides what to do with one inbound relayed manage-request, keeping every downstream consumer's handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests) rather than by the hub.
78
80
  *
79
- * A request with no fromDevice at all is refused before anything below sees it. A room-domain verb's own downstream handler (room-router.ts's resolveHandle, then a capability-token check such as verifyRoomToken) calls deviceIdFromHex on whatever handle.id it's given, which throws on anything that isn't valid hex rather than answering with an ordinary unauthorized outcome. Answering unauthorized here, before that, is what keeps this a clean, fast failure for the request's own caller instead of an unhandled rejection that would kill this session's whole drain loop.
81
+ * A request with no fromDevice at all is refused before anything below sees it. A room-domain verb's own downstream handler (a capability-token check such as verifyRoomToken) calls deviceIdFromHex on whatever handle.id it's given, which throws on anything that isn't valid hex rather than answering with an ordinary unauthorized outcome. Answering unauthorized here, before that, is what keeps this a clean, fast failure for the request's own caller instead of an unhandled rejection that would kill this session's whole drain loop.
80
82
  *
81
83
  * Only room-domain verbs are accepted over a relay at all. The legacy FRAME_VERB carriage (an opaque MeshMessage, which local peer sessions still use for mesh-state gossip) is refused here with unsupported_verb whoever sent it and whether or not that sender is trusted: a relayed sender has passed neither connect_request approval nor the coordinator's own membership, so it has no business feeding state_sync or state_update into this side's mesh state, and nothing on the relay path has any other use for a bare frame. Refusing the verb wholesale is the allow-list; there is no per-message-type list to keep in step with new MeshMessage variants (agent-comms#169, agent-comms#268). The router's own handleRelayedRequest refuses a frame too, so the local branch below cannot deliver one by construction; the check here is what stops a frame being forwarded on to a local peer session, whose own drain loop would otherwise treat it as an ordinary local frame.
82
84
  *
83
- * A real room-domain verb (room.send, room.join, room.notify, ...) is never gated on isTrusted at all (agent-comms#192): it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over, and already principal-aware since agent-comms#187 (device-membership-verification.ts's own chain-walk). Stacking the coarse bare-device allowlist in front of that check would be redundant, not protective, and for room.join specifically (the one deliberately ungated verb, whose own security model is a human decision) it would actively defeat the protocol's own intended design by never letting an untrusted device reach that approval step at all. onKnownPeer is still only called here for a sender isTrusted already recognises: reaching this branch has not yet had its own token verified (that happens inside handleRoomRequest/forwardToLocalPeer), so hubPeersKnown stays "gateway-trusted hub peers", never "every device that has sent a syntactically valid room-domain request".
84
- *
85
- * Multi-device gateway routing (agent-comms#184, wire-mesh-core 1.48.1's own IncomingManageRequest.toDevice, read directly from each relay-data frame's own to-device field rather than guessed from pairing state): when the request carries a toDevice naming a different device than ownDeviceHex, the command is first re-stamped with an on-behalf-of params field naming the true sender before forwardToLocalPeer forwards it on to that device's own local mesh session (one this gateway is directly connected to, never merely gossiped-about); room-router.ts's own resolveHandle reads that field back out on the receiving end, so the forwarded request is attributed to the true remote sender there, never to this gateway. Its outcome is relayed straight back. Only when forwardToLocalPeer finds no such local session (toDevice is absent, matches ownDeviceHex, or names a device this gateway doesn't actually front) does the request fall through to handleRoomRequest, dispatched against this side's own local mesh state, the same fallback a sender still on a pre-#184 wire-mesh-core (never stamping toDevice at all) already relied on -- and it's exactly that fallback call which carries hubAddress on to handleRoomRequest as origin.relayHubAddress (agent-comms#216), never the forwardToLocalPeer branch: a request forwarded on to a different local device is answered by that device's own session, not this gateway's hub session, so this gateway's own dialled hub address would be the wrong fact to attach to it.
85
+ * A real room-domain verb (room.send, room.join, room.notify, ...) is never gated on isTrusted at all (agent-comms#192): it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over, and already principal-aware since agent-comms#187 (device-membership-verification.ts's own chain-walk). Stacking the coarse bare-device allowlist in front of that check would be redundant, not protective, and for room.join specifically (the one deliberately ungated verb, whose own security model is a human decision) it would actively defeat the protocol's own intended design by never letting an untrusted device reach that approval step at all. onKnownPeer is still only called here for a sender isTrusted already recognises: reaching this branch has not yet had its own token verified (that happens inside handleRoomRequest), so hubPeersKnown stays "gateway-trusted hub peers", never "every device that has sent a syntactically valid room-domain request".
86
86
  */
87
87
  export declare function dispatchHubRequest(options: {
88
88
  request: IncomingManageRequest;
89
- ownDeviceHex: string;
90
89
  deps: Readonly<HubRequestDispatchDeps>;
91
90
  onKnownPeer: (deviceHex: string) => void;
92
91
  hubAddress?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"hub-session.d.ts","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hub-session.d.ts","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"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"}
@@ -10,11 +10,11 @@ import { DOMAIN, FRAME_VERB } from "./wire-mesh-transport.js";
10
10
  export class HubSession {
11
11
  deps;
12
12
  session;
13
- /** The raw connection underlying `session` -- MeshSession's own sendGossipUpdate only ever advertises this side's own single device, so advertising OTHER (local mesh) devices onto the hub (agent-comms#155's outbound leg) needs a raw gossip frame sent directly, bypassing that per-session single-self-advert limit. */
14
- connection;
15
13
  /** The URL this side itself dialled to reach the hub -- tracePath's own local.hubAddress (agent-comms#199), the equivalent of wire-mesh-core's own TracePathOptions.localHubAddress. */
16
14
  url;
17
15
  hubPeersKnown = new Set();
16
+ /** Resolvers of every whenDisconnected() call still waiting for the live session to end. */
17
+ disconnectWaiters = [];
18
18
  constructor(deps) {
19
19
  this.deps = deps;
20
20
  }
@@ -31,14 +31,29 @@ export class HubSession {
31
31
  ownsSession(session) {
32
32
  return this.session === session;
33
33
  }
34
+ /** Resolves once no hub session is live: at once when none is, otherwise when the current one ends, whether the hub closed it or disconnect() dropped it. HubLink waits on this to know when to dial again. */
35
+ async whenDisconnected() {
36
+ if (this.session === undefined)
37
+ return;
38
+ await new Promise((resolve) => {
39
+ this.disconnectWaiters.push(resolve);
40
+ });
41
+ }
42
+ /** Forgets the held session and releases everyone waiting in whenDisconnected(). */
43
+ clearSession() {
44
+ if (this.session !== undefined)
45
+ this.deps.untrack(this.session);
46
+ this.session = undefined;
47
+ this.url = undefined;
48
+ for (const resolve of this.disconnectWaiters.splice(0))
49
+ resolve();
50
+ }
34
51
  /** Drops the held hub connection. A no-op if none is live (connect() was never called, disconnect() already ran, or the hub itself already closed the session). */
35
52
  async disconnect() {
36
53
  const session = this.session;
37
54
  if (session === undefined)
38
55
  return;
39
- this.session = undefined;
40
- this.connection = undefined;
41
- this.url = undefined;
56
+ this.clearSession();
42
57
  await session.close();
43
58
  }
44
59
  /** The device ids (hex) of peers discovered through the hub's gossiped directory. */
@@ -62,19 +77,18 @@ export class HubSession {
62
77
  return;
63
78
  }
64
79
  this.session = session;
65
- this.connection = connection;
66
80
  this.url = url;
67
81
  this.deps.trackForShutdown(session);
68
82
  void this.consumeEvents(session, deviceIdToHex(identity.deviceId)).catch((error) => {
69
83
  // The merge loop must not die silently: a failure here would stop this side learning of every remote device until the hub is redialled.
70
84
  this.deps.events.onError?.(error instanceof Error ? error : new Error(String(error)));
71
85
  });
72
- this.consume(session, deviceIdToHex(identity.deviceId));
86
+ this.consume(session);
73
87
  }
74
88
  /**
75
89
  * The single consumer of one hub session's own event stream: merges the hub's directory (its catch-up arrives as the first event, and it is refreshed on every subsequent one) and, once the session ends, drops the held connection so isConnected answers honestly.
76
90
  *
77
- * Single is the operative word. wire-mesh-core delivers each event to exactly one waiting reader, so two concurrent loops over the same stream split it between them: the closed event reaches whichever of them happened to be waiting first. When that was the directory loop, the connection stayed recorded as live for the rest of the process, and every later advertiseDevices threw on a socket that was already gone -- once per directory change, which is what filled a replacement coordinator's stderr with "connection is closed" after the previous coordinator was killed.
91
+ * Single is the operative word. wire-mesh-core delivers each event to exactly one waiting reader, so two concurrent loops over the same stream split it between them, and the closed event reaches whichever happened to be waiting first. If that is the directory loop, the connection stays recorded as live for the rest of the process.
78
92
  *
79
93
  * Every trusted remote entry is surfaced via onDirectory, so the transport can merge it into its own mesh-wide knownDevices (agent-comms#155's remote-directory-merge leg). Filtered to admitEntries (agent-comms#156, widened by agent-comms#187's principal allowlist per agent-comms#192 and by #266's membership proofs) before either hubPeersKnown tracking or onDirectory sees it: an untrusted device's gossiped presence is not merely withheld from listAgents, it is never even recorded as "known" here, so nothing downstream can act on it via any path this class exposes.
80
94
  */
@@ -94,22 +108,18 @@ export class HubSession {
94
108
  }
95
109
  }
96
110
  finally {
97
- if (this.session === session) {
98
- this.session = undefined;
99
- this.connection = undefined;
100
- this.url = undefined;
101
- }
111
+ if (this.session === session)
112
+ this.clearSession();
102
113
  }
103
114
  }
104
- /** Consumes one hub session's inbound relayed manage-requests until it ends, dispatching each in arrival order to dispatchHubRequest -- see that function's own doc for the actual per-request trust decision. Split out purely so the decision itself is directly unit-testable against fake requests/deps (hub-session-dispatch.test.ts), the same reason hub-forwarding.ts's own forwardAdvertsToHub/pushHubCatchUp are standalone functions rather than private methods. Passes this.url through as dispatchHubRequest's own hubAddress -- this side's own dialled address for the hub every request on this specific session arrived over (agent-comms#216), the answering-side equivalent of tracePath's own local.hubAddress above. */
105
- consume(session, ownDeviceHex) {
115
+ /** Consumes one hub session's inbound relayed manage-requests until it ends, dispatching each in arrival order to dispatchHubRequest -- see that function's own doc for the actual per-request trust decision. Split out purely so the decision itself is directly unit-testable against fake requests/deps (hub-session-dispatch.test.ts), the same reason hub-forwarding.ts's own routeRoomRequestViaHub is a standalone function rather than a private method. Passes this.url through as dispatchHubRequest's own hubAddress -- this side's own dialled address for the hub every request on this specific session arrived over (agent-comms#216), the answering-side equivalent of tracePath's own local.hubAddress above. */
116
+ consume(session) {
106
117
  void (async () => {
107
118
  for await (const request of session.incomingManageRequests) {
108
119
  if (this.deps.isShuttingDown())
109
120
  break;
110
121
  await dispatchHubRequest({
111
122
  request,
112
- ownDeviceHex,
113
123
  deps: this.deps,
114
124
  onKnownPeer: (hex) => {
115
125
  this.hubPeersKnown.add(hex);
@@ -119,17 +129,6 @@ export class HubSession {
119
129
  }
120
130
  })();
121
131
  }
122
- /** Gossips a raw `gossip` frame carrying every given entry's own advert onto the hub, over the raw connection rather than sendGossipUpdate (which can only ever advertise this side's own single device) -- the outbound leg of agent-comms#155's gateway forwarding. relay-hub.ts registers each advert's own `device` field against the CONNECTION it arrives on, so every entry passed here becomes reachable, cross-machine, as "via this gateway" -- callers are responsible for only ever passing entries that are actually meant to be advertised (WireMeshTransport filters to local, visible-agent-bearing entries before calling this). Throws if this side isn't currently connected to a hub, matching sendToPeer's own contract -- callers gate on isConnected first. */
123
- async advertiseDevices(entries) {
124
- const connection = this.connection;
125
- if (connection === undefined) {
126
- throw new Error("not connected to a hub");
127
- }
128
- await connection.send({
129
- type: "gossip",
130
- peers: entries.map((entry) => entry.advert),
131
- });
132
- }
133
132
  /** Sends a real core/room manage-request to a specific hub-reachable peer, through the hub's relay-connect/relay-data pairing -- the local-to-remote leg of agent-comms#155's routing, mirroring MeshTransport.sendRoomRequest's own not_connected/outcome contract so WireMeshTransport can fall back to this uniformly when memberId isn't a local peer session. Bounded by HUB_ROOM_REQUEST_TIMEOUT_MS (see its own doc) since an unreachable target-device is silently dropped by the hub with no error frame, unlike a local session's own connection-level failure. */
134
133
  async sendRoomRequest(peerDeviceHex, command, scope, token) {
135
134
  const session = this.session;
@@ -165,18 +164,16 @@ function hexToBytes(hex) {
165
164
  return bytes;
166
165
  }
167
166
  /**
168
- * Decides what to do with one inbound relayed manage-request, keeping every downstream consumer's handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests) rather than by this gateway, exactly as it already did for the legacy opaque-frame path and now also for a real room-domain verb (agent-comms#155's "remote to local" leg).
167
+ * Decides what to do with one inbound relayed manage-request, keeping every downstream consumer's handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests) rather than by the hub.
169
168
  *
170
- * A request with no fromDevice at all is refused before anything below sees it. A room-domain verb's own downstream handler (room-router.ts's resolveHandle, then a capability-token check such as verifyRoomToken) calls deviceIdFromHex on whatever handle.id it's given, which throws on anything that isn't valid hex rather than answering with an ordinary unauthorized outcome. Answering unauthorized here, before that, is what keeps this a clean, fast failure for the request's own caller instead of an unhandled rejection that would kill this session's whole drain loop.
169
+ * A request with no fromDevice at all is refused before anything below sees it. A room-domain verb's own downstream handler (a capability-token check such as verifyRoomToken) calls deviceIdFromHex on whatever handle.id it's given, which throws on anything that isn't valid hex rather than answering with an ordinary unauthorized outcome. Answering unauthorized here, before that, is what keeps this a clean, fast failure for the request's own caller instead of an unhandled rejection that would kill this session's whole drain loop.
171
170
  *
172
171
  * Only room-domain verbs are accepted over a relay at all. The legacy FRAME_VERB carriage (an opaque MeshMessage, which local peer sessions still use for mesh-state gossip) is refused here with unsupported_verb whoever sent it and whether or not that sender is trusted: a relayed sender has passed neither connect_request approval nor the coordinator's own membership, so it has no business feeding state_sync or state_update into this side's mesh state, and nothing on the relay path has any other use for a bare frame. Refusing the verb wholesale is the allow-list; there is no per-message-type list to keep in step with new MeshMessage variants (agent-comms#169, agent-comms#268). The router's own handleRelayedRequest refuses a frame too, so the local branch below cannot deliver one by construction; the check here is what stops a frame being forwarded on to a local peer session, whose own drain loop would otherwise treat it as an ordinary local frame.
173
172
  *
174
- * A real room-domain verb (room.send, room.join, room.notify, ...) is never gated on isTrusted at all (agent-comms#192): it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over, and already principal-aware since agent-comms#187 (device-membership-verification.ts's own chain-walk). Stacking the coarse bare-device allowlist in front of that check would be redundant, not protective, and for room.join specifically (the one deliberately ungated verb, whose own security model is a human decision) it would actively defeat the protocol's own intended design by never letting an untrusted device reach that approval step at all. onKnownPeer is still only called here for a sender isTrusted already recognises: reaching this branch has not yet had its own token verified (that happens inside handleRoomRequest/forwardToLocalPeer), so hubPeersKnown stays "gateway-trusted hub peers", never "every device that has sent a syntactically valid room-domain request".
175
- *
176
- * Multi-device gateway routing (agent-comms#184, wire-mesh-core 1.48.1's own IncomingManageRequest.toDevice, read directly from each relay-data frame's own to-device field rather than guessed from pairing state): when the request carries a toDevice naming a different device than ownDeviceHex, the command is first re-stamped with an on-behalf-of params field naming the true sender before forwardToLocalPeer forwards it on to that device's own local mesh session (one this gateway is directly connected to, never merely gossiped-about); room-router.ts's own resolveHandle reads that field back out on the receiving end, so the forwarded request is attributed to the true remote sender there, never to this gateway. Its outcome is relayed straight back. Only when forwardToLocalPeer finds no such local session (toDevice is absent, matches ownDeviceHex, or names a device this gateway doesn't actually front) does the request fall through to handleRoomRequest, dispatched against this side's own local mesh state, the same fallback a sender still on a pre-#184 wire-mesh-core (never stamping toDevice at all) already relied on -- and it's exactly that fallback call which carries hubAddress on to handleRoomRequest as origin.relayHubAddress (agent-comms#216), never the forwardToLocalPeer branch: a request forwarded on to a different local device is answered by that device's own session, not this gateway's hub session, so this gateway's own dialled hub address would be the wrong fact to attach to it.
173
+ * A real room-domain verb (room.send, room.join, room.notify, ...) is never gated on isTrusted at all (agent-comms#192): it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over, and already principal-aware since agent-comms#187 (device-membership-verification.ts's own chain-walk). Stacking the coarse bare-device allowlist in front of that check would be redundant, not protective, and for room.join specifically (the one deliberately ungated verb, whose own security model is a human decision) it would actively defeat the protocol's own intended design by never letting an untrusted device reach that approval step at all. onKnownPeer is still only called here for a sender isTrusted already recognises: reaching this branch has not yet had its own token verified (that happens inside handleRoomRequest), so hubPeersKnown stays "gateway-trusted hub peers", never "every device that has sent a syntactically valid room-domain request".
177
174
  */
178
175
  export async function dispatchHubRequest(options) {
179
- const { request, ownDeviceHex, deps, onKnownPeer, hubAddress } = options;
176
+ const { request, deps, onKnownPeer, hubAddress } = options;
180
177
  if (request.command.verb === FRAME_VERB) {
181
178
  await request
182
179
  .respond({ result: "error", code: "unsupported_verb" })
@@ -193,24 +190,6 @@ export async function dispatchHubRequest(options) {
193
190
  const handle = { id: senderHex };
194
191
  if (deps.isTrusted(senderHex))
195
192
  onKnownPeer(senderHex);
196
- const toDeviceHex = request.toDevice !== undefined
197
- ? deviceIdToHex(request.toDevice)
198
- : undefined;
199
- if (toDeviceHex !== undefined && toDeviceHex !== ownDeviceHex) {
200
- const forwardedCommand = {
201
- ...request.command,
202
- params: {
203
- ...request.command.params,
204
- "on-behalf-of": senderHex,
205
- },
206
- };
207
- const forwarded = deps.forwardToLocalPeer(toDeviceHex, forwardedCommand, request.scope, request.token);
208
- if (forwarded !== undefined) {
209
- const outcome = await forwarded;
210
- await request.respond(outcome).catch(() => undefined);
211
- return;
212
- }
213
- }
214
193
  await deps.handleRoomRequest(request, handle, hubAddress !== undefined ? { relayHubAddress: hubAddress } : {});
215
194
  }
216
195
  //# sourceMappingURL=hub-session.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hub-session.js","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hub-session.js","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,7 @@ import type { HubSession } from "./hub-session.js";
7
7
  import type { RoomRequestOrigin } from "./room-router.js";
8
8
  import type { ConnectionHandle, MeshGraph, MeshTraceResult } from "./transport.js";
9
9
  /**
10
- * Assembles this side's own best-effort view of the mesh's connection graph out of knownDevices (WireMeshTransport's own mesh-wide gossip aggregation, the identical map listKnownDevices already reads), converted to DirectoryEntry[] the same way hub-forwarding.ts's own pushHubCatchUp already does for its unrelated purpose. Device-ids are hex-encoded, never raw wire-mesh-core DeviceId bytes, matching listKnownDevices' own precedent.
10
+ * Assembles this side's own best-effort view of the mesh's connection graph out of knownDevices (WireMeshTransport's own mesh-wide gossip aggregation, the identical map listKnownDevices already reads), converted to DirectoryEntry[]. Device-ids are hex-encoded, never raw wire-mesh-core DeviceId bytes, matching listKnownDevices' own precedent.
11
11
  *
12
12
  * assembleTopologyGraph's own nodes list is only devices this side holds a directory entry (gossip advert) for -- its edges are deliberately "unreconciled by design" (see its own doc comment) and can name a `to`/`via` endpoint reported by another device's advert that this side has never itself gossiped with directly, so that endpoint never gets its own directory entry and is absent from nodes. A MeshGraph whose edges reference an undeclared node isn't well-formed for any consumer (a force-directed layout, a table, anything), so every edge endpoint is unioned into the returned nodes list here, not left for each consumer to work around individually.
13
13
  */
@@ -5,7 +5,7 @@ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
5
5
  import { assembleTopologyGraph } from "wire-mesh-core/domain/topology";
6
6
  import { buildPathTraceResponse, tracePath, } from "wire-mesh-core/domain/path-trace";
7
7
  /**
8
- * Assembles this side's own best-effort view of the mesh's connection graph out of knownDevices (WireMeshTransport's own mesh-wide gossip aggregation, the identical map listKnownDevices already reads), converted to DirectoryEntry[] the same way hub-forwarding.ts's own pushHubCatchUp already does for its unrelated purpose. Device-ids are hex-encoded, never raw wire-mesh-core DeviceId bytes, matching listKnownDevices' own precedent.
8
+ * Assembles this side's own best-effort view of the mesh's connection graph out of knownDevices (WireMeshTransport's own mesh-wide gossip aggregation, the identical map listKnownDevices already reads), converted to DirectoryEntry[]. Device-ids are hex-encoded, never raw wire-mesh-core DeviceId bytes, matching listKnownDevices' own precedent.
9
9
  *
10
10
  * assembleTopologyGraph's own nodes list is only devices this side holds a directory entry (gossip advert) for -- its edges are deliberately "unreconciled by design" (see its own doc comment) and can name a `to`/`via` endpoint reported by another device's advert that this side has never itself gossiped with directly, so that endpoint never gets its own directory entry and is absent from nodes. A MeshGraph whose edges reference an undeclared node isn't well-formed for any consumer (a force-directed layout, a table, anything), so every edge endpoint is unioned into the returned nodes list here, not left for each consumer to work around individually.
11
11
  */
@@ -26,7 +26,7 @@ import type { AgentIdentity, AgentStatus, ConnectionCode, DeliveryEvent, MeshVis
26
26
  export interface MeshStoreOptions {
27
27
  /** Localhost TCP port the coordinator role is contested on. Defaults to DEFAULT_COORDINATOR_PORT. */
28
28
  readonly coordinatorPort?: number | undefined;
29
- /** Remote gateway hub this store dials whenever it holds the local coordinator role. Defaults to DEFAULT_HUB_URL. */
29
+ /** The relay hub this store holds its own session on from init until shutdown, so it is reachable from other machines whatever role it plays locally. Left out, the store never contacts a hub: only the bridge entry points name the production hub (bridge-mesh.ts), so a store built by anything else, a test included, stays local. */
30
30
  readonly hubUrl?: string | undefined;
31
31
  /** How long a room.join (including a first-contact DM request) may await a human decision, on this store as the receiver and on the transport it is wired to as the sender. Defaults to ROOM_JOIN_APPROVAL_TIMEOUT_MS; a test shortens it. */
32
32
  readonly roomJoinApprovalTimeoutMs?: number | undefined;
@@ -62,7 +62,7 @@ export declare class MeshStore implements CommsStore {
62
62
  discovery: DiscoveryManager;
63
63
  /** The cross-machine trust boundary (agent-comms#156), constructed once in the constructor below (mirroring discovery above) and shared with WireMeshTransport by every construction site (bridge-mesh.ts, test-transport.ts) that passes it into WireMeshTransport's own constructor, so store.addTrustedGateway() and the transport's own hub-forwarding/hub-session gates read the exact same set. Persists across restarts (agent-comms#186) when a slot is passed to this store's own constructor; stays in-memory only, exactly as before, for every construction site that omits one. Public so those construction sites can reach it; addTrustedGateway/removeTrustedGateway/listTrustedGateways below are the methods CommsTool actually calls through MeshOnlyFeatures. */
64
64
  readonly gatewayTrust: GatewayTrust;
65
- /** The connection-code generate/redeem pair (agent-comms#188) bootstrapping gatewayTrust above between two devices with no existing mesh connection. Persists across restarts the same way gatewayTrust does, sharing the same slot passed to this store's own constructor. generateConnectionCode/redeemConnectionCode below are the methods CommsTool actually calls through MeshOnlyFeatures; redeemConnectionCode is also where a successful redemption's deviceId gets fed into gatewayTrust.add, the actual point of this whole bootstrap. */
65
+ /** The connection-code generate/redeem pair (agent-comms#188) bootstrapping gatewayTrust above between two devices with no existing mesh connection. Persists across restarts per slot, from the slot passed to this store's own constructor, whereas gatewayTrust is shared by every slot in the slot's identity directory. generateConnectionCode/redeemConnectionCode below are the methods CommsTool actually calls through MeshOnlyFeatures; redeemConnectionCode is also where a successful redemption's deviceId gets fed into gatewayTrust.add, the actual point of this whole bootstrap. */
66
66
  private readonly connectionCodes;
67
67
  private readonly deliveryEngine;
68
68
  private readonly roomProtocol;
@@ -72,7 +72,6 @@ export declare class MeshStore implements CommsStore {
72
72
  private readonly connectionApproval;
73
73
  private readonly capabilityAskAdmission;
74
74
  private readonly staleAgentChecker;
75
- private readonly coordinatorGateway;
76
75
  private readonly peerLifecycle;
77
76
  /** This store's own current AgentStatus, synchronously -- the value WireMeshTransport's presence re-advertisement timer reads on every tick. undefined before registerAgent has ever run (no self agent record exists yet), in which case there is nothing yet to advertise. */
78
77
  get selfStatus(): AgentStatus | undefined;
@@ -80,17 +79,33 @@ export declare class MeshStore implements CommsStore {
80
79
  get hostedRooms(): readonly HostedRoomAdvert[];
81
80
  /** This store's own gossip-safe agent-identity advert, synchronously, in the shape WireMeshTransport's own gossip re-advertisement timer reads on every tick -- the write half of P3.8's eventual agent register/update/offline retirement (agent-comms#48). undefined before registerAgent has ever run (nothing to advertise yet), or when this agent's own visibility isn't "visible" -- gossip already reaches every connected peer regardless of mesh-approval status (see wire-mesh-transport.ts's own allSessions/quarantine comments), so advertising a hidden or ghost agent's identity this way would leak exactly what those visibility levels exist to withhold. */
82
81
  get selfAgentAdvert(): AgentSelfAdvert | undefined;
82
+ /**
83
+ * Whether this store should hold a hub session right now. It has something to say to another machine only when it has an agent that is not a ghost and its machine trusts at least one remote device or principal. Until then, holding a session would put its device id on a public hub for nobody's benefit, and a ghost agent is by definition not reachable from anywhere. A hidden agent does hold one, and advertises only the device advert every session opens with, which is what lets it be reached by device id.
84
+ */
85
+ private wantsHubSession;
86
+ /** Has the hub session re-evaluate wantsHubSession now, after a change to the agent or to the trust that it reads. Changes made by another process reach it within the link's poll interval instead. */
87
+ private reconsiderHub;
83
88
  /** Whether the mesh has a live coordinator connection. */
84
89
  get connected(): boolean;
85
90
  onDelivery: ((agentId: string, event: DeliveryEvent) => void | Promise<void>) | undefined;
91
+ private readonly deliveryReceivers;
92
+ /**
93
+ * Observes every delivery event this store's own agent receives, alongside onDelivery, and returns a function that stops observing. Several parties can share one store this way, a bridge's own delivery handler and the web UI it serves, where a single assignable onDelivery would let whichever assigned last silence the other. A listener that throws or rejects is reported through onError and does not stop the others.
94
+ */
95
+ addDeliveryListener(listener: (agentId: string, event: DeliveryEvent) => void | Promise<void>): () => void;
86
96
  /** Fires for every state patch — both locally generated and remote. */
87
97
  onPatch: ((patch: MeshStatePatch) => void | Promise<void>) | undefined;
98
+ private readonly patchReceivers;
99
+ /**
100
+ * Observes every state patch, alongside onPatch, and returns a function that stops observing. The counterpart of addDeliveryListener: the web UI a bridge serves publishes patches from the bridge's own store, and must not take over the one assignable onPatch. A listener that throws or rejects is reported through onError and does not stop the others.
101
+ */
102
+ addPatchListener(listener: (patch: MeshStatePatch) => void | Promise<void>): () => void;
88
103
  /**
89
104
  * Fires for a transport-level error a caller may want to observe (a connection rejected for presenting a certificate that doesn't match its claimed peer ID, this store's own inability to join or create a mesh, etc). Left undefined by default (matching onDelivery/onPatch): a caller that wants visibility sets it, exactly like those two.
90
105
  */
91
106
  onError: ((error: Error) => void) | undefined;
92
107
  /**
93
- * Fires whenever this store's own coordinator role changes: true right after becoming coordinator (a fresh bind in init(), or a takeover via PeerLifecycle.handleBecomeCoordinator), false right before shutdown() drops it. There is no live "lost the role to someone else while still running" case today -- CoordinatorGateway.onLostCoordinator is only ever called from shutdown(), so this callback mirrors that same lifecycle. Left undefined by default (matching onDelivery/onPatch/onError): a caller that wants to react to owning the coordinator role -- e.g. bridge-mesh.ts starting/stopping the cc-peer front (agent-comms#157), a Node/filesystem-specific capability that has no place in this transport-agnostic core -- sets it, exactly like those three.
108
+ * Fires whenever this store's own coordinator role changes: true right after becoming coordinator (a fresh bind in init(), or a takeover via PeerLifecycle.handleBecomeCoordinator), false right before shutdown() drops it. There is no live "lost the role to someone else while still running" case today: the role is only ever given up in shutdown(), so this callback mirrors that lifecycle. Left undefined by default (matching onDelivery/onPatch/onError): a caller that wants to react to owning the coordinator role -- e.g. bridge-mesh.ts starting/stopping the cc-peer front (agent-comms#157), a Node/filesystem-specific capability that has no place in this transport-agnostic core -- sets it, exactly like those three.
94
109
  */
95
110
  onCoordinatorRoleChanged: ((isCoordinator: boolean) => void | Promise<void>) | undefined;
96
111
  /** This process's own currently-running cc-peer package version, when it is actually fronting a cc-peer session or running the one-shot `bridge cc-peer` command (agent-comms#198) -- undefined by default (matching onDelivery/onPatch/onError/onCoordinatorRoleChanged), since every other bridge never loads the cc-peer package at all. Set once, right after construction, by front-runtime.ts/bridges/cc-peer/run.ts (a plain closure over cc-peer's own exported CC_PEER_VERSION constant) -- read by both this store's own WireMeshTransport construction (bridge-mesh.ts, for the gossiped versions advert) and CommsTool (for whoami/update/list_agents' own self-report), so the one field backs both surfaces. */
@@ -220,7 +235,7 @@ export declare class MeshStore implements CommsStore {
220
235
  setVisibility(level: MeshVisibility, adapter?: string): Promise<void>;
221
236
  /** Get current mesh discovery visibility. */
222
237
  getVisibility(adapter?: string): MeshVisibility;
223
- /** Trusts a remote device-id (hex): this store's own gateway (once it becomes the coordinator) will advertise onto the hub, merge this device's gossiped directory entries, dispatch its relayed requests, and route outbound hub requests to it. See GatewayTrust's own class doc for why trust is keyed per device-id rather than per remote machine, and why it doesn't survive a restart. Trusting a device is itself a route to it appearing, so anything queued for it is retried immediately rather than waiting out the hub's next gossip tick. */
238
+ /** Trusts a remote device-id (hex): this store will advertise itself on the hub, merge this device's gossiped directory entries, dispatch its relayed requests, and route outbound hub requests to it. The trust is shared by every store on the machine and survives a restart. See GatewayTrust's own class doc for why it is keyed per device-id rather than per remote machine. Trusting a device is itself a route to it appearing, so anything queued for it is retried immediately rather than waiting out the hub's next gossip tick. */
224
239
  addTrustedGateway(deviceHex: string): void;
225
240
  /** Withdraws trust from a remote device-id (hex). A no-op if it was never trusted. */
226
241
  removeTrustedGateway(deviceHex: string): void;