agent-comms 5.0.6 → 5.1.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 (87) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +41 -29
  4. package/dist/bridges/cc-peer/run.d.ts.map +1 -1
  5. package/dist/bridges/cc-peer/run.js +11 -13
  6. package/dist/bridges/cc-peer/run.js.map +1 -1
  7. package/dist/bridges/claude-code/channel.d.ts.map +1 -1
  8. package/dist/bridges/claude-code/channel.js +2 -6
  9. package/dist/bridges/claude-code/channel.js.map +1 -1
  10. package/dist/bridges/codex/tool.d.ts.map +1 -1
  11. package/dist/bridges/codex/tool.js +12 -2
  12. package/dist/bridges/codex/tool.js.map +1 -1
  13. package/dist/bridges/mcp/server.d.ts.map +1 -1
  14. package/dist/bridges/mcp/server.js +12 -2
  15. package/dist/bridges/mcp/server.js.map +1 -1
  16. package/dist/bridges/opencode/plugin.d.ts.map +1 -1
  17. package/dist/bridges/opencode/plugin.js +13 -2
  18. package/dist/bridges/opencode/plugin.js.map +1 -1
  19. package/dist/core/bind-retry.d.ts +4 -0
  20. package/dist/core/bind-retry.d.ts.map +1 -1
  21. package/dist/core/bind-retry.js +11 -2
  22. package/dist/core/bind-retry.js.map +1 -1
  23. package/dist/core/hub-session.d.ts +8 -1
  24. package/dist/core/hub-session.d.ts.map +1 -1
  25. package/dist/core/hub-session.js +23 -22
  26. package/dist/core/hub-session.js.map +1 -1
  27. package/dist/core/index.d.ts +1 -0
  28. package/dist/core/index.d.ts.map +1 -1
  29. package/dist/core/index.js +1 -0
  30. package/dist/core/index.js.map +1 -1
  31. package/dist/core/listener-registry.d.ts +8 -0
  32. package/dist/core/listener-registry.d.ts.map +1 -1
  33. package/dist/core/listener-registry.js +15 -0
  34. package/dist/core/listener-registry.js.map +1 -1
  35. package/dist/core/mesh-store.d.ts +2 -0
  36. package/dist/core/mesh-store.d.ts.map +1 -1
  37. package/dist/core/mesh-store.js +19 -17
  38. package/dist/core/mesh-store.js.map +1 -1
  39. package/dist/core/peer-lifecycle.d.ts +15 -1
  40. package/dist/core/peer-lifecycle.d.ts.map +1 -1
  41. package/dist/core/peer-lifecycle.js +84 -2
  42. package/dist/core/peer-lifecycle.js.map +1 -1
  43. package/dist/core/shutdown-signals.d.ts +22 -0
  44. package/dist/core/shutdown-signals.d.ts.map +1 -0
  45. package/dist/core/shutdown-signals.js +40 -0
  46. package/dist/core/shutdown-signals.js.map +1 -0
  47. package/dist/core/transport.d.ts +2 -0
  48. package/dist/core/transport.d.ts.map +1 -1
  49. package/dist/core/wire-mesh-transport.d.ts +5 -0
  50. package/dist/core/wire-mesh-transport.d.ts.map +1 -1
  51. package/dist/core/wire-mesh-transport.js +45 -55
  52. package/dist/core/wire-mesh-transport.js.map +1 -1
  53. package/dist/test/coordinator-failover.integration.test.d.ts +5 -0
  54. package/dist/test/coordinator-failover.integration.test.d.ts.map +1 -0
  55. package/dist/test/coordinator-failover.integration.test.js +127 -0
  56. package/dist/test/coordinator-failover.integration.test.js.map +1 -0
  57. package/dist/test/coordinator-failover.unit.test.d.ts +5 -0
  58. package/dist/test/coordinator-failover.unit.test.d.ts.map +1 -0
  59. package/dist/test/coordinator-failover.unit.test.js +231 -0
  60. package/dist/test/coordinator-failover.unit.test.js.map +1 -0
  61. package/dist/test/delivery-engine-delivery.unit.test.js +1 -0
  62. package/dist/test/delivery-engine-delivery.unit.test.js.map +1 -1
  63. package/dist/test/delivery-engine-directed-notify.unit.test.js +1 -0
  64. package/dist/test/delivery-engine-directed-notify.unit.test.js.map +1 -1
  65. package/dist/test/delivery-engine.unit.test.js +1 -0
  66. package/dist/test/delivery-engine.unit.test.js.map +1 -1
  67. package/dist/test/hub-session-close.integration.test.d.ts +5 -0
  68. package/dist/test/hub-session-close.integration.test.d.ts.map +1 -0
  69. package/dist/test/hub-session-close.integration.test.js +38 -0
  70. package/dist/test/hub-session-close.integration.test.js.map +1 -0
  71. package/dist/test/mesh-store-orchestration.unit.test.js +1 -0
  72. package/dist/test/mesh-store-orchestration.unit.test.js.map +1 -1
  73. package/dist/test/peer-lifecycle.unit.test.js +9 -1
  74. package/dist/test/peer-lifecycle.unit.test.js.map +1 -1
  75. package/dist/test/room-join-admission.integration.test.js +1 -0
  76. package/dist/test/room-join-admission.integration.test.js.map +1 -1
  77. package/dist/test/room-send-retry-queue.integration.test.js +1 -0
  78. package/dist/test/room-send-retry-queue.integration.test.js.map +1 -1
  79. package/dist/test/shutdown-signals.unit.test.d.ts +5 -0
  80. package/dist/test/shutdown-signals.unit.test.d.ts.map +1 -0
  81. package/dist/test/shutdown-signals.unit.test.js +110 -0
  82. package/dist/test/shutdown-signals.unit.test.js.map +1 -0
  83. package/dist/test/test-transport.d.ts +2 -2
  84. package/dist/test/test-transport.d.ts.map +1 -1
  85. package/dist/test/test-transport.js +2 -2
  86. package/dist/test/test-transport.js.map +1 -1
  87. package/package.json +1 -1
@@ -2,9 +2,18 @@
2
2
  * PeerLifecycle — the TransportEvents callbacks around a peer's own comings and goings on the mesh: peer-list/peer-joined bookkeeping, the coordinator's own new-peer introduction handshake, post-connection state-sync, inbound data-message routing (state_sync/state_update), taking over as coordinator, and peer disconnection. Split out of mesh-store.ts to reduce it under the repo's max-lines cap.
3
3
  */
4
4
  import { normaliseWireState } from "./wire-protocol.js";
5
+ import { isAddrInUse, isConnectionRefused } from "./bind-retry.js";
5
6
  import { COORDINATOR_HOST } from "./mesh-store-shared.js";
7
+ /** The message an unknown thrown value is reported as, so a takeover or rejoin failure reads the same whether the transport rejected with an Error or something else. */
8
+ function describe(error) {
9
+ return error instanceof Error ? error.message : String(error);
10
+ }
11
+ /** How many times a peer that has lost its coordinator alternates between binding the vacated port and joining whoever bound it first. More than one round is genuinely needed: a graceful handover names its successor before the outgoing coordinator's own listener closes, so a bind attempted in that window fails on a port that is about to be free, and the join it falls back to finds nothing listening a moment later. */
12
+ const COORDINATOR_TAKEOVER_ROUNDS = 5;
6
13
  export class PeerLifecycle {
7
14
  deps;
15
+ /** Guards against a second contest running while the first is still binding or rejoining -- a coordinator whose process dies can close more than one session to this side, and each close arrives as its own disconnect. */
16
+ contestingCoordinatorRole = false;
8
17
  constructor(deps) {
9
18
  this.deps = deps;
10
19
  }
@@ -70,8 +79,12 @@ export class PeerLifecycle {
70
79
  await this.deps
71
80
  .requireTransport()
72
81
  .becomeCoordinator(COORDINATOR_HOST, this.deps.coordinatorPort);
73
- this.deps.peerInfo.clear();
74
82
  const peerId = this.deps.getPeerId();
83
+ // peerList never names the successor itself (neither the graceful handover nor the crash race includes it), so this side's own entry is carried across the reset rather than dropped: handleIntroduction answers every later joiner with exactly these entries, and a joiner that is never told the coordinator's own data port never dials it.
84
+ const self = this.deps.peerInfo.get(peerId);
85
+ this.deps.peerInfo.clear();
86
+ if (self !== undefined)
87
+ this.deps.peerInfo.set(peerId, self);
75
88
  for (const peer of peerList) {
76
89
  this.deps.peerInfo.set(peer.id, peer);
77
90
  void this.deps.requireTransport().connectToPeer(peer, peerId);
@@ -81,7 +94,76 @@ export class PeerLifecycle {
81
94
  await this.deps.onCoordinatorRoleChanged?.();
82
95
  }
83
96
  handlePeerDisconnected(handle) {
84
- this.deps.peerInfo.delete(handle.id);
97
+ void this.handlePeerDeparture(handle.id);
98
+ }
99
+ /** The awaitable form of handlePeerDisconnected, which TransportEvents forces to be synchronous. A departing peer leaves the peer list; when it was this side's own coordinator, its vacated role is contested before anything else, so whoever wins is already coordinator by the time the departed peer's agent record is retired. */
100
+ async handlePeerDeparture(peerId) {
101
+ this.deps.peerInfo.delete(peerId);
102
+ if (peerId === this.deps.getCoordinatorPeerId()) {
103
+ await this.contestCoordinatorRole(peerId);
104
+ }
105
+ await this.retireDepartedAgent(peerId);
106
+ }
107
+ /** Races every other surviving peer to rebind the coordinator port, which is what makes a coordinator CRASH recoverable at all: unlike the graceful handover, a killed coordinator names no successor, so each survivor contests independently and the operating system's own exclusive bind decides the single winner. The winner runs the ordinary takeover path (handleBecomeCoordinator) over the peers it still holds connections to; a loser -- the one case where the bind fails specifically because the port is already taken -- re-introduces itself to the winner so it is a full member of the new mesh again, with its own data port known to whoever joins next. Direct peer-to-peer data connections are untouched throughout, so traffic between survivors never depends on the outcome of this race. */
108
+ async contestCoordinatorRole(lostCoordinatorId) {
109
+ const transport = this.deps.requireTransport();
110
+ if (transport.isCoordinator || this.contestingCoordinatorRole)
111
+ return;
112
+ this.contestingCoordinatorRole = true;
113
+ try {
114
+ const selfId = this.deps.getPeerId();
115
+ const survivors = [...this.deps.peerInfo.values()].filter((peer) => peer.id !== selfId && peer.id !== lostCoordinatorId);
116
+ for (let round = 0; round < COORDINATOR_TAKEOVER_ROUNDS; round++) {
117
+ try {
118
+ await this.handleBecomeCoordinator(survivors);
119
+ return;
120
+ }
121
+ catch (error) {
122
+ if (!isAddrInUse(error)) {
123
+ this.deps.onError?.(new Error(`PeerLifecycle: could not take over the vacated coordinator role: ${describe(error)}`));
124
+ return;
125
+ }
126
+ }
127
+ // A graceful handover racing this crash race can have made this side the coordinator while the bind above was in flight: the bind then fails against this side's own freshly bound listener, and dialling it would be this peer introducing itself to itself. Re-read through requireTransport() rather than the narrowed local, which TypeScript still believes is false from the guard at the top of this method.
128
+ if (this.deps.requireTransport().isCoordinator)
129
+ return;
130
+ if ((await this.rejoinUnderNewCoordinator(selfId)) !== "nothing-listening")
131
+ return;
132
+ }
133
+ this.deps.onError?.(new Error("PeerLifecycle: the vacated coordinator port kept changing hands and this peer neither took it nor found anyone holding it"));
134
+ }
135
+ finally {
136
+ this.contestingCoordinatorRole = false;
137
+ }
138
+ }
139
+ /** Re-introduces this side to whichever survivor won the bind race, over a fresh coordinator connection to the same well-known port. Without it this peer would keep its existing data connections but be unknown to the new coordinator, so it would never appear in the peer list handed to the next joiner. A refused dial is reported as "nothing-listening" rather than as an error: it means the winner released the port again (or never finished taking it), which the caller answers by re-entering the bind race. Any other failure is reported here, since it says the coordinator is there and this peer still cannot reach it. */
140
+ async rejoinUnderNewCoordinator(selfId) {
141
+ const transport = this.deps.requireTransport();
142
+ try {
143
+ await transport.connectToCoordinator(COORDINATOR_HOST, this.deps.coordinatorPort, selfId, transport.dataPort);
144
+ return "joined";
145
+ }
146
+ catch (error) {
147
+ if (isConnectionRefused(error))
148
+ return "nothing-listening";
149
+ this.deps.onError?.(new Error(`PeerLifecycle: lost the coordinator bind race and could not rejoin under the new coordinator: ${describe(error)}`));
150
+ return "failed";
151
+ }
152
+ }
153
+ /** Marks a departed peer's own agent offline and announces it, but only from the coordinator -- the same single-authority rule StaleAgentChecker's PID probe already follows, so a departure produces one announcement rather than one per surviving peer. An agent's id is its peer's id (AgentRegistry.registerAgent), so the departed peer names its own record directly; the PID probe remains the backstop for an agent whose process dies without its session closing. */
154
+ async retireDepartedAgent(peerId) {
155
+ if (!this.deps.requireTransport().isCoordinator)
156
+ return;
157
+ const agent = this.deps.agents.get(peerId);
158
+ if (agent === undefined || agent.status === "offline")
159
+ return;
160
+ agent.status = "offline";
161
+ this.deps.agents.set(peerId, agent);
162
+ await this.deps.deliveryEngine.notifyRoomsOfStatus(peerId, "offline");
163
+ await this.deps.deliveryEngine.broadcastPatch({
164
+ type: "agent_offline",
165
+ agentId: peerId,
166
+ });
85
167
  }
86
168
  /** Graceful coordinator handover (agent-comms#170): called by MeshStore.shutdown() while the transport is still up. A no-op unless this side currently holds the coordinator role and at least one other peer remains connected -- neither condition is this class's own business to log or report on, since a solo coordinator shutting down or a non-coordinator peer shutting down are both entirely ordinary. When both hold, picks the longest-running remaining peer (the one with the earliest recorded PeerInfo.startedAt, matching the README's documented policy) as the successor and sends it become_coordinator carrying every other remaining peer -- exactly the peerList shape handleBecomeCoordinator already expects (it dials each entry itself; the successor doesn't need to be told about itself). The crash-race path (each surviving peer independently racing to rebind the coordinator port) is a separate mechanism and untouched by this method. */
87
169
  async sendCoordinatorHandover() {
@@ -1 +1 @@
1
- {"version":3,"file":"peer-lifecycle.js","sourceRoot":"","sources":["../../src/core/peer-lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"peer-lifecycle.js","sourceRoot":"","sources":["../../src/core/peer-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The termination-signal handling every bridge needs, in one place. A bridge that exits without running MeshStore.shutdown() never hands its coordinator role on, so if it held that role the mesh is left with no coordinator until the surviving peers notice and race for the port -- recoverable, but it loses whatever the coordinator process alone was doing (the stale-agent probe, the gateway hub connection, the cc-peer front) for as long as the race takes.
3
+ *
4
+ * Which signals a process should answer is not in question; what differs is what it may do afterwards. A bridge that owns its own process exits. A bridge loaded into someone else's process (a plugin) must not: registering any listener for SIGINT or SIGTERM removes Node's own default terminate behaviour, so a plugin that handled the signal and then did nothing would silently swallow its host's Ctrl-C. Such a bridge re-raises instead, which reaches the host's own handler if it has one and terminates the process as usual if it does not.
5
+ */
6
+ /** What a bridge does once its shutdown has run. "exit" ends the process and suits a bridge that owns it. "reraise" re-sends the same signal with this handler removed, so a bridge loaded into a host process leaves the decision to the host rather than exiting on its behalf. */
7
+ export type ShutdownDisposition = "exit" | "reraise";
8
+ export interface ShutdownSignalOptions {
9
+ /** The orderly shutdown itself, normally MeshStore.shutdown() plus whatever else the bridge owns. It is never expected to throw: a rejection here is reported to onError and the process still goes on to exit or re-raise. */
10
+ shutdown: () => Promise<void>;
11
+ /** See ShutdownDisposition. */
12
+ disposition: ShutdownDisposition;
13
+ /** Reports a shutdown that failed part way. Optional, since a bridge whose only sink is a protocol stream on stdout may have nowhere useful to write during its own exit. */
14
+ onError?: ((error: Error) => void) | undefined;
15
+ }
16
+ /**
17
+ * Installs one handler per termination signal, each running `shutdown` once and then exiting or re-raising per `disposition`. Registered with `once`, so a second signal of the same kind while the shutdown is still in flight takes the default action rather than starting a second one -- which is also what makes "reraise" terminate rather than loop.
18
+ *
19
+ * Returns a function that removes every handler it installed, for a caller that shuts down by some other route (a host's own lifecycle hook, or a test) and does not want the handlers outliving it.
20
+ */
21
+ export declare function installShutdownSignalHandlers(options: Readonly<ShutdownSignalOptions>): () => void;
22
+ //# sourceMappingURL=shutdown-signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown-signals.d.ts","sourceRoot":"","sources":["../../src/core/shutdown-signals.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,qRAAqR;AACrR,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,+NAA+N;IAC/N,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,+BAA+B;IAC/B,WAAW,EAAE,mBAAmB,CAAC;IACjC,6KAA6K;IAC7K,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAChD;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GACvC,MAAM,IAAI,CAyBZ"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The termination-signal handling every bridge needs, in one place. A bridge that exits without running MeshStore.shutdown() never hands its coordinator role on, so if it held that role the mesh is left with no coordinator until the surviving peers notice and race for the port -- recoverable, but it loses whatever the coordinator process alone was doing (the stale-agent probe, the gateway hub connection, the cc-peer front) for as long as the race takes.
3
+ *
4
+ * Which signals a process should answer is not in question; what differs is what it may do afterwards. A bridge that owns its own process exits. A bridge loaded into someone else's process (a plugin) must not: registering any listener for SIGINT or SIGTERM removes Node's own default terminate behaviour, so a plugin that handled the signal and then did nothing would silently swallow its host's Ctrl-C. Such a bridge re-raises instead, which reaches the host's own handler if it has one and terminates the process as usual if it does not.
5
+ */
6
+ /** The termination signals a bridge answers. SIGHUP matters as much as the other two here: a bridge started from a terminal that closes receives only SIGHUP, and without it that exit is silently ungraceful. */
7
+ const SHUTDOWN_SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];
8
+ /** The exit status a bridge reports when it shut down in answer to a signal: this is an orderly exit that did exactly what it was asked to. */
9
+ const CLEAN_EXIT_CODE = 0;
10
+ /**
11
+ * Installs one handler per termination signal, each running `shutdown` once and then exiting or re-raising per `disposition`. Registered with `once`, so a second signal of the same kind while the shutdown is still in flight takes the default action rather than starting a second one -- which is also what makes "reraise" terminate rather than loop.
12
+ *
13
+ * Returns a function that removes every handler it installed, for a caller that shuts down by some other route (a host's own lifecycle hook, or a test) and does not want the handlers outliving it.
14
+ */
15
+ export function installShutdownSignalHandlers(options) {
16
+ const { shutdown, disposition, onError } = options;
17
+ const installed = SHUTDOWN_SIGNALS.map((signal) => {
18
+ const handler = () => {
19
+ void shutdown()
20
+ .catch((error) => {
21
+ onError?.(error instanceof Error ? error : new Error(String(error)));
22
+ })
23
+ .finally(() => {
24
+ if (disposition === "exit") {
25
+ process.exit(CLEAN_EXIT_CODE);
26
+ return;
27
+ }
28
+ process.kill(process.pid, signal);
29
+ });
30
+ };
31
+ process.once(signal, handler);
32
+ return { signal, handler };
33
+ });
34
+ return () => {
35
+ for (const { signal, handler } of installed) {
36
+ process.off(signal, handler);
37
+ }
38
+ };
39
+ }
40
+ //# sourceMappingURL=shutdown-signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown-signals.js","sourceRoot":"","sources":["../../src/core/shutdown-signals.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,kNAAkN;AAClN,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAElE,+IAA+I;AAC/I,MAAM,eAAe,GAAG,CAAC,CAAC;AAc1B;;;;GAIG;AACH,MAAM,UAAU,6BAA6B,CAC3C,OAAwC;IAExC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACnD,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QAChD,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,KAAK,QAAQ,EAAE;iBACZ,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBACxB,OAAO,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACZ,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;oBAC3B,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;oBAC9B,OAAO;gBACT,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACpC,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,EAAE;QACV,KAAK,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,SAAS,EAAE,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -102,6 +102,8 @@ export interface MeshTransport {
102
102
  readonly isCoordinator: boolean;
103
103
  /** Whether this instance has a live connection to a coordinator. */
104
104
  readonly hasCoordinatorConnection: boolean;
105
+ /** The peer ID of the coordinator this instance currently answers to, or undefined when it is the coordinator itself or has never reached one. Survives that coordinator's session closing, so an onPeerDisconnected handler can tell the coordinator's own departure apart from an ordinary peer's and contest the vacated role. */
106
+ readonly coordinatorPeerId: string | undefined;
105
107
  /**
106
108
  * Start the data server on an OS-assigned port.
107
109
  * Resolves when the server is listening.
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/core/transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/core/transport.ts"],"names":[],"mappings":"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"}
@@ -80,9 +80,12 @@ export declare class WireMeshTransport implements MeshTransport {
80
80
  get dataPort(): number;
81
81
  get isCoordinator(): boolean;
82
82
  get hasCoordinatorConnection(): boolean;
83
+ get coordinatorPeerId(): string | undefined;
83
84
  private handleAcceptedConnection;
84
85
  /** Reads a not-yet-trusted session's requests until it's promoted (an `introduce`, handled and trusted immediately, matching the pre-existing coordinator-handoff trust boundary) or a `connect_request` arrives, at which point this same loop iteration blocks on the human decision Promise stored in pendingConnections -- resumed in place by acceptConnection/rejectConnection (or by watchForDisconnect, if the requester disconnects first) -- rather than ever stopping and later re-entering the session's incomingManageRequests from a second call, which would require assuming a fresh access yields a distinct, independently-advancing iterator rather than resuming the one already in progress. Anything else arriving before introduce/connect_request is a protocol violation from an unapproved peer and is refused and closed rather than routed. */
85
86
  private consumeQuarantined;
87
+ /** Opens a mesh session over an already-established connection, wired to this transport's own frame dispatch and advertised addresses. Every session this transport owns is built exactly this way, whether the connection was accepted by a listener or dialled at a peer, at the coordinator, or at a remote gateway, so the wiring lives here rather than being restated at each call site. */
88
+ private openSession;
86
89
  /** Delegates the whole post-approval drain loop to roomRouter -- this transport no longer decodes or routes a MeshMessage itself, only hands the session off. Also starts this session's own independent revocationAnnouncements drain, since a gossiped revocation is not a manage-request and has no verb for roomRouter to dispatch. Derives roomRouter's own allowOnBehalfOf gate itself, from whether this SPECIFIC handle's peer identity matches coordinatorDeviceHex -- never from which method established this particular session (mesh formation's reciprocal connectToPeer means the same coordinator device can just as easily reach this transport over an accepted connection as over the one connectToCoordinator itself dialled; see coordinatorDeviceHex's own field comment). A coordinator-less instance (coordinatorDeviceHex still undefined, e.g. this transport IS the coordinator) never allows it for anything. */
87
90
  private consumeIncoming;
88
91
  /** Reads every revocation-entry this session's peer announces, for as long as the session lives, handing each one to MeshStore via onRevocationAnnounce -- one call per entry, matching revocationAnnouncements' own per-entry flattening of a revocation-announce frame's entries array. */
@@ -95,6 +98,8 @@ export declare class WireMeshTransport implements MeshTransport {
95
98
  becomeCoordinator(host: string, port: number): Promise<void>;
96
99
  connectToPeer(peer: Readonly<PeerInfo>, _ownPeerId: string): Promise<void>;
97
100
  send(handle: Readonly<ConnectionHandle>, message: MeshMessage): Promise<void>;
101
+ /** Sends one opaque MeshMessage frame over a session under the ordinary network deadline. The deadline is what makes it safe to await: wire-mesh-core waits forever for a manage-response when given none, so a frame sent to a peer whose socket has already died but whose close this side has not yet observed would never settle, and MeshStore.shutdown() awaits exactly such a broadcast on its way out. */
102
+ private sendFrame;
98
103
  broadcast(message: MeshMessage): Promise<void>;
99
104
  broadcastRevocation(entries: readonly RevocationEntry[]): Promise<void>;
100
105
  sendRoomRequest(memberId: string, command: ManageCommand, scope: Readonly<CapabilityScope>, token?: CapabilityToken): Promise<ManageOutcome>;
@@ -1 +1 @@
1
- {"version":3,"file":"wire-mesh-transport.d.ts","sourceRoot":"","sources":["../../src/core/wire-mesh-transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wire-mesh-transport.d.ts","sourceRoot":"","sources":["../../src/core/wire-mesh-transport.ts"],"names":[],"mappings":"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"}
@@ -8,12 +8,11 @@
8
8
  * connect_request's accept/reject flow maps onto sendManageRequest's own request/response round trip directly, rather than a separate pair of connect_accepted/connect_rejected messages: manage-request-frame already tolerates arbitrary latency between a request and its response, so "waiting for a human to approve" is simply a manage-response that hasn't been sent yet, not a protocol gap needing its own mechanism. connect_request therefore holds its own IncomingManageRequest open (rather than responding immediately) until acceptConnection/rejectConnection is actually called.
9
9
  */
10
10
  import { createTlsTransport } from "wire-mesh-core/adapters/tls-transport";
11
- import * as bindRetry from "./bind-retry.js";
12
- import { ROOM_JOIN_APPROVAL_TIMEOUT_MS, manageRequestTimeoutMs, } from "./request-timeouts.js";
11
+ import { ROOM_JOIN_APPROVAL_TIMEOUT_MS, ROOM_REQUEST_TIMEOUT_MS, manageRequestTimeoutMs, } from "./request-timeouts.js";
13
12
  import { connectWsUrl } from "./ws-dial.js";
14
13
  import { HubSession } from "./hub-session.js";
15
14
  import { GatewayTrust } from "./gateway-trust.js";
16
- import { advertisedListenerAddresses, LISTENER_ID_LENGTH, listenerPort, listTrackedListeners, registerListener, unregisterListener, } from "./listener-registry.js";
15
+ import { advertisedListenerAddresses, listenerPort, listTrackedListeners, registerDefaultListener, registerListener, unregisterListener, } from "./listener-registry.js";
17
16
  import { findPresenceAdvert, listKnownDevicesEntries, mergeKnownDevices, startGossipInterval, } from "./gossip-directory.js";
18
17
  import { directoryAdmission } from "./directory-admission.js";
19
18
  import { connectHubGateway, forwardAdvertsToHub, makeSendToLocalPeer, routeRoomRequestViaHub, } from "./hub-forwarding.js";
@@ -22,7 +21,6 @@ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
22
21
  import { computeMeshGraph, handlePathTraceRequest, traceMeshPath, } from "./mesh-graph.js";
23
22
  import { routeDataFrame } from "./data-frame-routing.js";
24
23
  import { toIdentityPort } from "./wire-mesh-identity.js";
25
- import { nanoid } from "./nanoid.js";
26
24
  import { createRoomRouter, extractMessage, } from "./room-router.js";
27
25
  // ---------------------------------------------------------------------------
28
26
  // Constants
@@ -69,7 +67,7 @@ export class WireMeshTransport {
69
67
  // -- Coordinator listeners (the well-known bootstrap port, plus any additional adapters addListener creates) --
70
68
  coordinatorListeners = new Map();
71
69
  defaultListenerId;
72
- // -- The session dialled via connectToCoordinator, when this instance is not itself the coordinator -- and that same coordinator's own device-id hex, set alongside it and never cleared: consumeIncoming's own allowOnBehalfOf gate (see its doc comment) compares every session's authenticated peer identity against the hex, not merely "was this the specific session connectToCoordinator itself dialled", since mesh formation's own reciprocal connectToPeer can just as easily reach this transport over an accepted connection to the identical coordinator device.
70
+ // -- The session dialled via connectToCoordinator, when this instance is not itself the coordinator -- and that same coordinator's own device-id hex, set alongside it: consumeIncoming's own allowOnBehalfOf gate (see its doc comment) compares every session's authenticated peer identity against the hex, not merely "was this the specific session connectToCoordinator itself dialled", since mesh formation's own reciprocal connectToPeer can just as easily reach this transport over an accepted connection to the identical coordinator device. The session is dropped when it closes and the hex when this side takes the coordinator role itself, so coordinatorPeerId below names a coordinator this instance genuinely still answers to.
73
71
  coordinatorSession;
74
72
  coordinatorDeviceHex;
75
73
  // -- The hub relay mode (agent-comms#151), owning its own file under the max-lines cap: see hub-session.ts for the full connection model.
@@ -199,6 +197,9 @@ export class WireMeshTransport {
199
197
  get hasCoordinatorConnection() {
200
198
  return this.coordinatorSession !== undefined;
201
199
  }
200
+ get coordinatorPeerId() {
201
+ return this.coordinatorDeviceHex;
202
+ }
202
203
  // -----------------------------------------------------------------------
203
204
  // Connection acceptance -- shared by every listener (coordinator or data)
204
205
  // -----------------------------------------------------------------------
@@ -214,13 +215,7 @@ export class WireMeshTransport {
214
215
  return;
215
216
  }
216
217
  const deviceIdHex = deviceIdToHex(peerDeviceId);
217
- const identity = await this.identityReady;
218
- const session = await acceptMeshSession(connection, identity, [DOMAIN], {
219
- onFrame: async (conn, frame) => {
220
- await this.handleDataFrame(conn, frame);
221
- },
222
- addresses: this.advertisedAddresses,
223
- });
218
+ const session = await this.openSession(connection);
224
219
  if (this.isShuttingDown()) {
225
220
  await session.close();
226
221
  return;
@@ -316,6 +311,16 @@ export class WireMeshTransport {
316
311
  }
317
312
  })();
318
313
  }
314
+ /** Opens a mesh session over an already-established connection, wired to this transport's own frame dispatch and advertised addresses. Every session this transport owns is built exactly this way, whether the connection was accepted by a listener or dialled at a peer, at the coordinator, or at a remote gateway, so the wiring lives here rather than being restated at each call site. */
315
+ async openSession(connection) {
316
+ const identity = await this.identityReady;
317
+ return acceptMeshSession(connection, identity, [DOMAIN], {
318
+ onFrame: async (conn, frame) => {
319
+ await this.handleDataFrame(conn, frame);
320
+ },
321
+ addresses: this.advertisedAddresses,
322
+ });
323
+ }
319
324
  /** Delegates the whole post-approval drain loop to roomRouter -- this transport no longer decodes or routes a MeshMessage itself, only hands the session off. Also starts this session's own independent revocationAnnouncements drain, since a gossiped revocation is not a manage-request and has no verb for roomRouter to dispatch. Derives roomRouter's own allowOnBehalfOf gate itself, from whether this SPECIFIC handle's peer identity matches coordinatorDeviceHex -- never from which method established this particular session (mesh formation's reciprocal connectToPeer means the same coordinator device can just as easily reach this transport over an accepted connection as over the one connectToCoordinator itself dialled; see coordinatorDeviceHex's own field comment). A coordinator-less instance (coordinatorDeviceHex still undefined, e.g. this transport IS the coordinator) never allows it for anything. */
320
325
  consumeIncoming(session, handle) {
321
326
  const allowOnBehalfOf = handle.id === this.coordinatorDeviceHex;
@@ -344,6 +349,9 @@ export class WireMeshTransport {
344
349
  if (wasTracked)
345
350
  this.peerSessions.delete(deviceIdHex);
346
351
  this.allSessions.delete(session);
352
+ // Dropping the closed coordinator session is what makes hasCoordinatorConnection answer honestly once the coordinator process is gone; coordinatorDeviceHex deliberately survives, so the onPeerDisconnected fired just below can still be recognised as the coordinator's own departure rather than an ordinary peer's.
353
+ if (this.coordinatorSession === session)
354
+ this.coordinatorSession = undefined;
347
355
  // A requester disconnecting before a human decides must unblock consumeQuarantined's own still-suspended loop iteration -- otherwise that promise, and the closure awaiting it, never settle.
348
356
  const pending = this.pendingConnections.get(deviceIdHex);
349
357
  if (pending !== undefined) {
@@ -391,13 +399,7 @@ export class WireMeshTransport {
391
399
  // -----------------------------------------------------------------------
392
400
  async connectToCoordinator(host, port, peerId, dataPort) {
393
401
  const connection = await this.wireTransport.connect(`${host}:${String(port)}`);
394
- const identity = await this.identityReady;
395
- const session = await acceptMeshSession(connection, identity, [DOMAIN], {
396
- onFrame: async (conn, frame) => {
397
- await this.handleDataFrame(conn, frame);
398
- },
399
- addresses: this.advertisedAddresses,
400
- });
402
+ const session = await this.openSession(connection);
401
403
  this.coordinatorSession = session;
402
404
  const coordinatorDeviceId = connection.peerDeviceId;
403
405
  if (coordinatorDeviceId !== undefined) {
@@ -410,28 +412,25 @@ export class WireMeshTransport {
410
412
  this.watchForDisconnect(session, handle, handle.id);
411
413
  }
412
414
  // Fire-and-forget, matching the previous transport's own contract: this resolves once the introduction is sent, not once a response arrives -- peer_list/peer_joined/become_coordinator arrive asynchronously via the normal dispatch path above, independent of this call's own promise.
413
- void session
414
- .sendManageRequest(buildCommand({ method: "introduce", peerId, dataPort }), FRAME_SCOPE)
415
- .catch(() => undefined);
415
+ void this.sendFrame(session, buildCommand({ method: "introduce", peerId, dataPort })).catch(() => undefined);
416
416
  }
417
417
  // -----------------------------------------------------------------------
418
418
  // MeshTransport -- Become coordinator (server side)
419
419
  // -----------------------------------------------------------------------
420
420
  async becomeCoordinator(host, port) {
421
- const id = nanoid(LISTENER_ID_LENGTH);
422
- const listener = await bindRetry.retryOnAddrInUse(async () => this.wireTransport.listen(`${host}:${String(port)}`, (connection) => {
423
- const tracked = this.coordinatorListeners.get(id);
424
- void this.handleAcceptedConnection(connection, tracked?.policy, false, true);
425
- }), bindRetry.BECOME_COORDINATOR_BIND_RETRIES, bindRetry.BECOME_COORDINATOR_BIND_RETRY_DELAY_MS);
426
- this._isCoordinator = true;
427
- this.coordinatorListeners.set(id, {
428
- listener,
429
- policy: "full",
421
+ this.defaultListenerId = await registerDefaultListener({
422
+ wireTransport: this.wireTransport,
423
+ coordinatorListeners: this.coordinatorListeners,
430
424
  host,
431
- port: listenerPort(listener),
432
- isDefault: true,
425
+ port,
426
+ onAccepted: (connection) => {
427
+ void this.handleAcceptedConnection(connection, "full", false, true);
428
+ },
433
429
  });
434
- this.defaultListenerId = id;
430
+ this._isCoordinator = true;
431
+ // This side now IS the coordinator, so it answers to no other one: leaving a departed coordinator's device-id here would keep coordinatorPeerId naming a peer whose disconnect has already been dealt with, and would keep granting that device consumeIncoming's own on-behalf-of privilege.
432
+ this.coordinatorDeviceHex = undefined;
433
+ this.coordinatorSession = undefined;
435
434
  }
436
435
  // -----------------------------------------------------------------------
437
436
  // MeshTransport -- Peer-to-peer data connections
@@ -443,9 +442,8 @@ export class WireMeshTransport {
443
442
  const connection = await this.wireTransport
444
443
  .connect(`${COORDINATOR_HOST}:${String(peer.port)}`)
445
444
  .catch((error) => {
446
- this.events.onError?.(error instanceof Error
447
- ? new Error(`connectToPeer(${peer.id}, port ${String(peer.port)}): ${error.message}`)
448
- : new Error(`connectToPeer(${peer.id}, port ${String(peer.port)}) failed`));
445
+ const reason = error instanceof Error ? error.message : String(error);
446
+ this.events.onError?.(new Error(`connectToPeer(${peer.id}, port ${String(peer.port)}): ${reason}`));
449
447
  return undefined;
450
448
  });
451
449
  if (connection === undefined || this.isShuttingDown()) {
@@ -466,13 +464,7 @@ export class WireMeshTransport {
466
464
  await connection.close();
467
465
  return;
468
466
  }
469
- const identity = await this.identityReady;
470
- const session = await acceptMeshSession(connection, identity, [DOMAIN], {
471
- onFrame: async (conn, frame) => {
472
- await this.handleDataFrame(conn, frame);
473
- },
474
- addresses: this.advertisedAddresses,
475
- });
467
+ const session = await this.openSession(connection);
476
468
  if (this.isShuttingDown()) {
477
469
  this.dataDials.delete(peer.id);
478
470
  await session.close();
@@ -490,17 +482,19 @@ export class WireMeshTransport {
490
482
  const session = this.peerSessions.get(handle.id);
491
483
  if (session === undefined)
492
484
  return;
493
- await session
494
- .sendManageRequest(buildCommand(message), FRAME_SCOPE)
495
- .catch((error) => {
485
+ await this.sendFrame(session, buildCommand(message)).catch((error) => {
496
486
  this.events.onError?.(error instanceof Error
497
487
  ? error
498
488
  : new Error(`send(${handle.id}) failed: ${String(error)}`));
499
489
  });
500
490
  }
491
+ /** Sends one opaque MeshMessage frame over a session under the ordinary network deadline. The deadline is what makes it safe to await: wire-mesh-core waits forever for a manage-response when given none, so a frame sent to a peer whose socket has already died but whose close this side has not yet observed would never settle, and MeshStore.shutdown() awaits exactly such a broadcast on its way out. */
492
+ async sendFrame(session, command) {
493
+ return session.sendManageRequest(command, FRAME_SCOPE, undefined, undefined, ROOM_REQUEST_TIMEOUT_MS);
494
+ }
501
495
  async broadcast(message) {
502
496
  const command = buildCommand(message);
503
- await Promise.all([...this.peerSessions.values()].map(async (session) => session.sendManageRequest(command, FRAME_SCOPE).catch(() => undefined)));
497
+ await Promise.all([...this.peerSessions.values()].map(async (session) => this.sendFrame(session, command).catch(() => undefined)));
504
498
  }
505
499
  async broadcastRevocation(entries) {
506
500
  await Promise.all([...this.peerSessions.values()].map(async (session) => session.sendRevocationAnnounce(entries).catch(() => undefined)));
@@ -542,13 +536,7 @@ export class WireMeshTransport {
542
536
  const connection = isWsUrl
543
537
  ? await connectWsUrl(host)
544
538
  : await this.wireTransport.connect(`${host}:${String(port)}`);
545
- const identity = await this.identityReady;
546
- const session = await acceptMeshSession(connection, identity, [DOMAIN], {
547
- onFrame: async (conn, frame) => {
548
- await this.handleDataFrame(conn, frame);
549
- },
550
- addresses: this.advertisedAddresses,
551
- });
539
+ const session = await this.openSession(connection);
552
540
  const outcome = await session.sendManageRequest(buildCommand({
553
541
  method: "connect_request",
554
542
  peerId,
@@ -651,6 +639,8 @@ export class WireMeshTransport {
651
639
  // -----------------------------------------------------------------------
652
640
  async shutdown() {
653
641
  this.shutDown = true;
642
+ // A shut-down transport holds no listener, so it is no longer the coordinator -- leaving the flag set would have a second shutdown pass try to hand the role on, and would have isCoordinator lie to anything that outlives the transport.
643
+ this._isCoordinator = false;
654
644
  if (this.gossipInterval !== undefined) {
655
645
  clearInterval(this.gossipInterval);
656
646
  this.gossipInterval = undefined;