agent-comms 3.5.0 → 3.7.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 (72) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +1 -1
  4. package/dist/core/bridge-mesh.d.ts +2 -2
  5. package/dist/core/bridge-mesh.d.ts.map +1 -1
  6. package/dist/core/bridge-mesh.js +4 -4
  7. package/dist/core/bridge-mesh.js.map +1 -1
  8. package/dist/core/capability-bubble-up.d.ts +69 -0
  9. package/dist/core/capability-bubble-up.d.ts.map +1 -0
  10. package/dist/core/capability-bubble-up.js +138 -0
  11. package/dist/core/capability-bubble-up.js.map +1 -0
  12. package/dist/core/coordinator-gateway.d.ts +25 -0
  13. package/dist/core/coordinator-gateway.d.ts.map +1 -0
  14. package/dist/core/coordinator-gateway.js +34 -0
  15. package/dist/core/coordinator-gateway.js.map +1 -0
  16. package/dist/core/gossip-directory.d.ts +8 -0
  17. package/dist/core/gossip-directory.d.ts.map +1 -0
  18. package/dist/core/gossip-directory.js +16 -0
  19. package/dist/core/gossip-directory.js.map +1 -0
  20. package/dist/core/hub-session.d.ts +5 -1
  21. package/dist/core/hub-session.d.ts.map +1 -1
  22. package/dist/core/hub-session.js +18 -2
  23. package/dist/core/hub-session.js.map +1 -1
  24. package/dist/core/mesh-store-shared.d.ts +2 -0
  25. package/dist/core/mesh-store-shared.d.ts.map +1 -1
  26. package/dist/core/mesh-store-shared.js +2 -0
  27. package/dist/core/mesh-store-shared.js.map +1 -1
  28. package/dist/core/mesh-store.d.ts +3 -1
  29. package/dist/core/mesh-store.d.ts.map +1 -1
  30. package/dist/core/mesh-store.js +21 -2
  31. package/dist/core/mesh-store.js.map +1 -1
  32. package/dist/core/peer-lifecycle.d.ts +4 -1
  33. package/dist/core/peer-lifecycle.d.ts.map +1 -1
  34. package/dist/core/peer-lifecycle.js +1 -0
  35. package/dist/core/peer-lifecycle.js.map +1 -1
  36. package/dist/core/transport.d.ts +8 -0
  37. package/dist/core/transport.d.ts.map +1 -1
  38. package/dist/core/wire-mesh-transport.d.ts +2 -2
  39. package/dist/core/wire-mesh-transport.d.ts.map +1 -1
  40. package/dist/core/wire-mesh-transport.js +11 -12
  41. package/dist/core/wire-mesh-transport.js.map +1 -1
  42. package/dist/test/bridge-mesh.test.js +18 -0
  43. package/dist/test/bridge-mesh.test.js.map +1 -1
  44. package/dist/test/capability-bubble-up.test.d.ts +2 -0
  45. package/dist/test/capability-bubble-up.test.d.ts.map +1 -0
  46. package/dist/test/capability-bubble-up.test.js +350 -0
  47. package/dist/test/capability-bubble-up.test.js.map +1 -0
  48. package/dist/test/coordinator-gateway.test.d.ts +2 -0
  49. package/dist/test/coordinator-gateway.test.d.ts.map +1 -0
  50. package/dist/test/coordinator-gateway.test.js +88 -0
  51. package/dist/test/coordinator-gateway.test.js.map +1 -0
  52. package/dist/test/gossip-directory.test.d.ts +2 -0
  53. package/dist/test/gossip-directory.test.d.ts.map +1 -0
  54. package/dist/test/gossip-directory.test.js +67 -0
  55. package/dist/test/gossip-directory.test.js.map +1 -0
  56. package/dist/test/hub-helpers.d.ts +8 -0
  57. package/dist/test/hub-helpers.d.ts.map +1 -1
  58. package/dist/test/hub-helpers.js +132 -2
  59. package/dist/test/hub-helpers.js.map +1 -1
  60. package/dist/test/hub-mode-session.integration.test.js +47 -124
  61. package/dist/test/hub-mode-session.integration.test.js.map +1 -1
  62. package/dist/test/mesh-smoke.runner.js +3 -1
  63. package/dist/test/mesh-smoke.runner.js.map +1 -1
  64. package/dist/test/mesh-store-orchestration.test.js +57 -0
  65. package/dist/test/mesh-store-orchestration.test.js.map +1 -1
  66. package/dist/test/peer-lifecycle.test.js +8 -0
  67. package/dist/test/peer-lifecycle.test.js.map +1 -1
  68. package/dist/test/wire-mesh-transport-hub.test.d.ts +5 -0
  69. package/dist/test/wire-mesh-transport-hub.test.d.ts.map +1 -0
  70. package/dist/test/wire-mesh-transport-hub.test.js +50 -0
  71. package/dist/test/wire-mesh-transport-hub.test.js.map +1 -0
  72. package/package.json +1 -1
@@ -15,7 +15,7 @@
15
15
  "url": "https://github.com/ExaDev/agent-comms.git"
16
16
  },
17
17
  "description": "Cross-harness LLM agent communication mesh — rooms, DMs, and presence",
18
- "version": "3.5.0"
18
+ "version": "3.7.0"
19
19
  }
20
20
  ]
21
21
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-comms",
3
3
  "description": "Cross-harness LLM agent communication mesh — rooms, DMs, and presence",
4
- "version": "3.5.0",
4
+ "version": "3.7.0",
5
5
  "author": {
6
6
  "name": "ExaDev"
7
7
  },
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![GitHub](https://img.shields.io/badge/GitHub-181717?logo=github&logoColor=white)](https://github.com/ExaDev/agent-comms)
4
4
  [![npm](https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/agent-comms)
5
- [![version](https://img.shields.io/badge/version-3.5.0-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v3.5.0)
5
+ [![version](https://img.shields.io/badge/version-3.7.0-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v3.7.0)
6
6
  [![CI](https://img.shields.io/github/actions/workflow/status/ExaDev/agent-comms/ci.yml?branch=main)](https://github.com/ExaDev/agent-comms/actions)
7
7
 
8
8
  Cross-harness communication mesh for LLM agents: rooms, DMs, presence, and visibility over TCP with zero filesystem dependencies.
@@ -17,6 +17,6 @@ export interface BridgeMeshSync extends BridgeMesh {
17
17
  attachIdentity: () => Promise<void>;
18
18
  }
19
19
  /** The synchronous half of bridge construction: everything loadOrCreateIdentity's own synchronous key material makes possible. Use this directly only when the calling entry point cannot await inline (see this file's own header comment); every other caller should use createBridgeMesh below. */
20
- export declare function createBridgeMeshSync(slot: Readonly<IdentitySlot>, coordinatorPort?: number): BridgeMeshSync;
21
- export declare function createBridgeMesh(slot: Readonly<IdentitySlot>, coordinatorPort?: number): Promise<BridgeMesh>;
20
+ export declare function createBridgeMeshSync(slot: Readonly<IdentitySlot>, coordinatorPort?: number, hubUrl?: string): BridgeMeshSync;
21
+ export declare function createBridgeMesh(slot: Readonly<IdentitySlot>, coordinatorPort?: number, hubUrl?: string): Promise<BridgeMesh>;
22
22
  //# sourceMappingURL=bridge-mesh.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-mesh.d.ts","sourceRoot":"","sources":["../../src/core/bridge-mesh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,iSAAiS;IACjS,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,sSAAsS;AACtS,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC5B,eAAe,CAAC,EAAE,MAAM,GACvB,cAAc,CAkChB;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC5B,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,UAAU,CAAC,CAOrB"}
1
+ {"version":3,"file":"bridge-mesh.d.ts","sourceRoot":"","sources":["../../src/core/bridge-mesh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,iSAAiS;IACjS,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,sSAAsS;AACtS,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC5B,eAAe,CAAC,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,GACd,cAAc,CAkChB;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC5B,eAAe,CAAC,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,CAAC,CAQrB"}
@@ -15,9 +15,9 @@ import { WireMeshTransport } from "./wire-mesh-transport.js";
15
15
  import { loadOrCreateIdentity, oplogDirFor } from "./identity-store.js";
16
16
  import { toIdentityPort } from "./wire-mesh-identity.js";
17
17
  /** The synchronous half of bridge construction: everything loadOrCreateIdentity's own synchronous key material makes possible. Use this directly only when the calling entry point cannot await inline (see this file's own header comment); every other caller should use createBridgeMesh below. */
18
- export function createBridgeMeshSync(slot, coordinatorPort) {
18
+ export function createBridgeMeshSync(slot, coordinatorPort, hubUrl) {
19
19
  const identity = loadOrCreateIdentity(slot);
20
- const store = new MeshStore(coordinatorPort);
20
+ const store = new MeshStore(coordinatorPort, hubUrl);
21
21
  store.peerId = deviceIdToHex(Uint8Array.from(identity.deviceId));
22
22
  // One shared dataStorage instance for both the transport's own data-domain frame responder and the store's own durable-send mint path (P5, agent-comms#50) -- oplogDirFor(slot) needs only the slot, not the async identity below, so this can be constructed synchronously right here.
23
23
  const dataStorage = createNodeFsStorage({ dir: oplogDirFor(slot) });
@@ -38,8 +38,8 @@ export function createBridgeMeshSync(slot, coordinatorPort) {
38
38
  },
39
39
  };
40
40
  }
41
- export async function createBridgeMesh(slot, coordinatorPort) {
42
- const { store, tool, attachIdentity } = createBridgeMeshSync(slot, coordinatorPort);
41
+ export async function createBridgeMesh(slot, coordinatorPort, hubUrl) {
42
+ const { store, tool, attachIdentity } = createBridgeMeshSync(slot, coordinatorPort, hubUrl);
43
43
  await attachIdentity();
44
44
  return { store, tool };
45
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-mesh.js","sourceRoot":"","sources":["../../src/core/bridge-mesh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAExE,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAYzD,sSAAsS;AACtS,MAAM,UAAU,oBAAoB,CAClC,IAA4B,EAC5B,eAAwB;IAExB,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,eAAe,CAAC,CAAC;IAC7C,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjE,wRAAwR;IACxR,MAAM,WAAW,GAAG,mBAAmB,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpE,KAAK,CAAC,YAAY,CAChB,IAAI,iBAAiB,CACnB,KAAK,CAAC,MAAM,EACZ,QAAQ,EACR,KAAK,CAAC,gBAAgB,EACtB,SAAS,EACT,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EACtB,SAAS,EACT,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,EACvB,WAAW,EACX,GAAG,EAAE,CAAC,KAAK,CAAC,eAAe,CAC5B,CACF,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,OAAO;QACL,KAAK;QACL,IAAI;QACJ,cAAc,EAAE,KAAK,IAAI,EAAE;YACzB,KAAK,CAAC,WAAW,CAAC;gBAChB,QAAQ,EAAE,MAAM,cAAc,CAAC,QAAQ,CAAC;gBACxC,KAAK,EAAE,iBAAiB,EAAE;gBAC1B,IAAI;gBACJ,UAAU;gBACV,WAAW;aACZ,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAA4B,EAC5B,eAAwB;IAExB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,oBAAoB,CAC1D,IAAI,EACJ,eAAe,CAChB,CAAC;IACF,MAAM,cAAc,EAAE,CAAC;IACvB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"bridge-mesh.js","sourceRoot":"","sources":["../../src/core/bridge-mesh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAExE,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAYzD,sSAAsS;AACtS,MAAM,UAAU,oBAAoB,CAClC,IAA4B,EAC5B,eAAwB,EACxB,MAAe;IAEf,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACrD,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjE,wRAAwR;IACxR,MAAM,WAAW,GAAG,mBAAmB,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpE,KAAK,CAAC,YAAY,CAChB,IAAI,iBAAiB,CACnB,KAAK,CAAC,MAAM,EACZ,QAAQ,EACR,KAAK,CAAC,gBAAgB,EACtB,SAAS,EACT,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EACtB,SAAS,EACT,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,EACvB,WAAW,EACX,GAAG,EAAE,CAAC,KAAK,CAAC,eAAe,CAC5B,CACF,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,OAAO;QACL,KAAK;QACL,IAAI;QACJ,cAAc,EAAE,KAAK,IAAI,EAAE;YACzB,KAAK,CAAC,WAAW,CAAC;gBAChB,QAAQ,EAAE,MAAM,cAAc,CAAC,QAAQ,CAAC;gBACxC,KAAK,EAAE,iBAAiB,EAAE;gBAC1B,IAAI;gBACJ,UAAU;gBACV,WAAW;aACZ,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAA4B,EAC5B,eAAwB,EACxB,MAAe;IAEf,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,oBAAoB,CAC1D,IAAI,EACJ,eAAe,EACf,MAAM,CACP,CAAC;IACF,MAAM,cAAc,EAAE,CAAC;IACvB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The routing policy for the ask tier's escalation path (agent-comms#164): walking a held capability token's own issuer chain when the immediate holder cannot grant a request itself. wire-mesh's capability-request primitive (domain/capability-request.ts) already carries the ask/held-open/decide/mint mechanics of a single hop; gossip-advertised grant candidates (domain/grant-candidates.ts) already answer "who nearer than the root can grant" for a requester picking its first hop. Neither decides what an intermediate device, having received a request it cannot fully satisfy from its own authority, should do next: that per-hop decision, and the handler that acts on it by minting a delegated child or forwarding the same ask further up the chain, is what this module adds.
3
+ *
4
+ * Two responsibilities, split the same way capability-request.ts already splits its own: resolveBubbleUpRoute is the pure decision (grant from held depth, forward to the held token's own issuer, or no-route), and createBubbleUpCapabilityRequestHandler is the wire-facing orchestration that acts on it. Deliberately built directly on wire-mesh's exported mintCapabilityToken rather than reusing createCapabilityRequestHandler's own decide(): that primitive's accept path always mints a root token (no `parent`), which is correct for the primitive's own reference consumer (a room owner minting membership tokens directly) but cannot express "mint a narrower child of the token I already hold", the one thing granting from delegated depth actually requires.
5
+ */
6
+ import type { MeshSession, IncomingManageRequest } from "wire-mesh-core/domain/mesh-session";
7
+ import { type RevocationCheck } from "wire-mesh-core/domain/tokens";
8
+ import { type CapabilityScope, type CapabilityToken, type DeviceId } from "wire-mesh-core/generated/protocol";
9
+ import type { Clock } from "wire-mesh-core/ports/clock";
10
+ import type { IdentityPort } from "wire-mesh-core/ports/identity";
11
+ export interface ResolveBubbleUpRouteOptions {
12
+ /** This device's own held credential establishing its authority over `capability`/`scope`, if it holds one at all. A device with none is either the capability's own structural root (which mints fresh from its own identity, outside this module's concern -- see wire-mesh's own createCapabilityRequestHandler for that path) or genuinely has nothing to grant or forward from. */
13
+ heldToken: CapabilityToken | undefined;
14
+ /** This device's own identity: both the crypto primitives heldToken is verified with, and (via `.deviceId`) the bearer heldToken must actually name. */
15
+ identity: IdentityPort;
16
+ clock: Clock;
17
+ revocation: RevocationCheck;
18
+ /** The capability and scope the incoming ask is for -- forwarded unchanged from the request this route decides for. */
19
+ capability: string;
20
+ scope: Readonly<CapabilityScope>;
21
+ /** The expiry and delegation depth a grant would carry if minted at this hop, checked against heldToken's own narrowing bounds via canGrant. The caller (see createBubbleUpCapabilityRequestHandler) owns this policy; this module only evaluates it. */
22
+ expires: number;
23
+ delegationsRemaining?: number;
24
+ }
25
+ export type BubbleUpRoute = {
26
+ kind: "grant";
27
+ token: CapabilityToken;
28
+ } | {
29
+ kind: "forward";
30
+ target: DeviceId;
31
+ } | {
32
+ kind: "no-route";
33
+ reason: "no-held-token" | "held-token-invalid" | "self-issued";
34
+ };
35
+ /**
36
+ * Decides, for one device holding (or not holding) a capability token, whether it can grant a request for `capability`/`scope`/`expires`/`delegationsRemaining` from its own held depth, must forward the same ask to whoever issued its own token, or has no route to a grantor at all.
37
+ *
38
+ * heldToken is independently verified here (signature, expiry, revocation, and that this device is genuinely its bearer) via verifyCapabilityToken rather than trusted as already-checked: a caller's own held-token cache can go stale (expiry, a since-recorded revocation) between when it was stored and when this decision runs, and granting or forwarding on an invalid credential would be a real authority leak, not merely a stale-read symptom.
39
+ *
40
+ * Ordering matches the issue's own framing (agent-comms#164): a device with real remaining depth grants outright (checked via canGrant, the same narrowing arithmetic mintCapabilityToken itself enforces, so this decision and the mint it authorises can never disagree); otherwise, absent a nearer route this module has no way to discover on its own (see grant-candidates.ts for the requester's own gossip-based shortcut, deliberately a separate concern), the only further hop toward the root a token's own claims expose is its issuer. Forwarding to that issuer when it is this very device (a self-issued token, which canGrant already found could not satisfy the request) would loop forever, so that case reports no-route instead.
41
+ */
42
+ export declare function resolveBubbleUpRoute(options: Readonly<ResolveBubbleUpRouteOptions>): Promise<BubbleUpRoute>;
43
+ export interface CreateBubbleUpCapabilityRequestHandlerOptions {
44
+ /** The capability this handler grants or forwards asks for, checked against the incoming request's own `params.capability`. One handler is constructed per capability, the same convention capability-request.ts's own createCapabilityRequestHandler already establishes. */
45
+ capability: string;
46
+ identity: IdentityPort;
47
+ clock: Clock;
48
+ revocation: RevocationCheck;
49
+ /** The peer device-id authenticated on this session's own connection -- the requester, and so the bearer of whichever token this handler ultimately mints (whether granted from own depth or re-delegated from an upstream grant). */
50
+ bearerDevice: DeviceId;
51
+ /** The expiry and delegation depth this handler mints with, at either hop (its own grant, or its re-delegation of an upstream one). A fixed per-handler policy, not per-request: the wire-level capability-request carries no requested expiry or depth of its own (management.cddl leaves both to the granter), matching the same policy CapabilityGrantDecision.expires/delegationsRemaining already leaves to the domain in wire-mesh's base primitive. */
52
+ expires: number;
53
+ delegationsRemaining?: number;
54
+ /** Looks up this device's own currently held credential for `capability`, if any, at the moment each request is handled -- a function rather than a static value since a held token can be minted, renewed, or revoked over the handler's own lifetime. */
55
+ ownToken: () => CapabilityToken | undefined;
56
+ /** Used to forward an ask this device cannot grant itself further up the issuer chain. Only sendManageRequest is used, so a caller can supply a minimal stand-in in tests rather than a full MeshSession. */
57
+ session: Pick<MeshSession, "sendManageRequest">;
58
+ /** Bounds how long a forwarded ask waits for the next hop's own answer, forwarded directly to sendManageRequest's own identically-named parameter. Absent, a forward waits with no time limit of its own, exactly as sendManageRequest already behaves. */
59
+ forwardTimeoutMs?: number;
60
+ }
61
+ /**
62
+ * Builds a reusable handler for one capability's incoming capability-requests that answers each one by walking the issuer chain: grant a delegated child from this device's own held depth when resolveBubbleUpRoute says it can, forward the identical ask to the held token's own issuer and re-delegate whatever grant comes back when it cannot, or refuse outright when there is no route to a grantor at all.
63
+ *
64
+ * Parses and validates the incoming request the same way capability-request.ts's own createCapabilityRequestHandler does (wrong capability or malformed payload refuses `{result:"error",code:"malformed"}`; an already-expired `valid-until` refuses `{result:"error",code:"expired"}`), so a requester using the ordinary requestCapability wrapper sees identical wire behaviour from either handler.
65
+ *
66
+ * A forward's own upstream outcome is relayed verbatim on refusal (the exact code and message an intermediate or root grantor sent), rather than collapsed to a generic denial, so the original requester learns exactly what stopped the chain. A forward's own granted token is independently re-verified (expectedBearer this device's own identity) before being trusted as a mint parent: mintCapabilityToken's own `parent` handling only checks structural narrowing against the decoded claims, not the token's signature, expiry, or revocation status, so an unverified inbound grant would otherwise let a malicious or buggy upstream peer hand this device a bogus credential to unknowingly re-delegate.
67
+ */
68
+ export declare function createBubbleUpCapabilityRequestHandler(options: Readonly<CreateBubbleUpCapabilityRequestHandlerOptions>): (incoming: Readonly<IncomingManageRequest>) => Promise<void>;
69
+ //# sourceMappingURL=capability-bubble-up.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-bubble-up.d.ts","sourceRoot":"","sources":["../../src/core/capability-bubble-up.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,WAAW,EACX,qBAAqB,EACtB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,QAAQ,EACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGlE,MAAM,WAAW,2BAA2B;IAC1C,wXAAwX;IACxX,SAAS,EAAE,eAAe,GAAG,SAAS,CAAC;IACvC,wJAAwJ;IACxJ,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,eAAe,CAAC;IAC5B,uHAAuH;IACvH,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACjC,yPAAyP;IACzP,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,GACrC;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,eAAe,GAAG,oBAAoB,GAAG,aAAa,CAAC;CAChE,CAAC;AAEN;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,QAAQ,CAAC,2BAA2B,CAAC,GAC7C,OAAO,CAAC,aAAa,CAAC,CAuCxB;AAED,MAAM,WAAW,6CAA6C;IAC5D,8QAA8Q;IAC9Q,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,eAAe,CAAC;IAC5B,sOAAsO;IACtO,YAAY,EAAE,QAAQ,CAAC;IACvB,8bAA8b;IAC9b,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,2PAA2P;IAC3P,QAAQ,EAAE,MAAM,eAAe,GAAG,SAAS,CAAC;IAC5C,6MAA6M;IAC7M,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAChD,2PAA2P;IAC3P,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,QAAQ,CAAC,6CAA6C,CAAC,GAC/D,CAAC,QAAQ,EAAE,QAAQ,CAAC,qBAAqB,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAgF9D"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * The routing policy for the ask tier's escalation path (agent-comms#164): walking a held capability token's own issuer chain when the immediate holder cannot grant a request itself. wire-mesh's capability-request primitive (domain/capability-request.ts) already carries the ask/held-open/decide/mint mechanics of a single hop; gossip-advertised grant candidates (domain/grant-candidates.ts) already answer "who nearer than the root can grant" for a requester picking its first hop. Neither decides what an intermediate device, having received a request it cannot fully satisfy from its own authority, should do next: that per-hop decision, and the handler that acts on it by minting a delegated child or forwarding the same ask further up the chain, is what this module adds.
3
+ *
4
+ * Two responsibilities, split the same way capability-request.ts already splits its own: resolveBubbleUpRoute is the pure decision (grant from held depth, forward to the held token's own issuer, or no-route), and createBubbleUpCapabilityRequestHandler is the wire-facing orchestration that acts on it. Deliberately built directly on wire-mesh's exported mintCapabilityToken rather than reusing createCapabilityRequestHandler's own decide(): that primitive's accept path always mints a root token (no `parent`), which is correct for the primitive's own reference consumer (a room owner minting membership tokens directly) but cannot express "mint a narrower child of the token I already hold", the one thing granting from delegated depth actually requires.
5
+ */
6
+ import { buildCapabilityRequestCommand } from "wire-mesh-core/domain/capability-request";
7
+ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
8
+ import { canGrant, mintCapabilityToken, verifyCapabilityToken, } from "wire-mesh-core/domain/tokens";
9
+ import { capabilityGrantOkSchema, capabilityRequestSchema, } from "wire-mesh-core/generated/protocol";
10
+ import { randomId } from "./random-id.js";
11
+ /**
12
+ * Decides, for one device holding (or not holding) a capability token, whether it can grant a request for `capability`/`scope`/`expires`/`delegationsRemaining` from its own held depth, must forward the same ask to whoever issued its own token, or has no route to a grantor at all.
13
+ *
14
+ * heldToken is independently verified here (signature, expiry, revocation, and that this device is genuinely its bearer) via verifyCapabilityToken rather than trusted as already-checked: a caller's own held-token cache can go stale (expiry, a since-recorded revocation) between when it was stored and when this decision runs, and granting or forwarding on an invalid credential would be a real authority leak, not merely a stale-read symptom.
15
+ *
16
+ * Ordering matches the issue's own framing (agent-comms#164): a device with real remaining depth grants outright (checked via canGrant, the same narrowing arithmetic mintCapabilityToken itself enforces, so this decision and the mint it authorises can never disagree); otherwise, absent a nearer route this module has no way to discover on its own (see grant-candidates.ts for the requester's own gossip-based shortcut, deliberately a separate concern), the only further hop toward the root a token's own claims expose is its issuer. Forwarding to that issuer when it is this very device (a self-issued token, which canGrant already found could not satisfy the request) would loop forever, so that case reports no-route instead.
17
+ */
18
+ export async function resolveBubbleUpRoute(options) {
19
+ if (options.heldToken === undefined) {
20
+ return { kind: "no-route", reason: "no-held-token" };
21
+ }
22
+ const verdict = await verifyCapabilityToken(options.heldToken, {
23
+ identity: options.identity,
24
+ clock: options.clock,
25
+ revocation: options.revocation,
26
+ expectedBearer: options.identity.deviceId,
27
+ });
28
+ if (!verdict.ok) {
29
+ return { kind: "no-route", reason: "held-token-invalid" };
30
+ }
31
+ const canGrantDirectly = await canGrant(options.heldToken, options.identity.deviceId, {
32
+ capability: options.capability,
33
+ scope: options.scope,
34
+ expires: options.expires,
35
+ ...(options.delegationsRemaining !== undefined
36
+ ? { delegationsRemaining: options.delegationsRemaining }
37
+ : {}),
38
+ }, options.clock.now());
39
+ if (canGrantDirectly) {
40
+ return { kind: "grant", token: options.heldToken };
41
+ }
42
+ if (deviceIdToHex(verdict.claims.issuer) ===
43
+ deviceIdToHex(options.identity.deviceId)) {
44
+ return { kind: "no-route", reason: "self-issued" };
45
+ }
46
+ return { kind: "forward", target: verdict.claims.issuer };
47
+ }
48
+ /**
49
+ * Builds a reusable handler for one capability's incoming capability-requests that answers each one by walking the issuer chain: grant a delegated child from this device's own held depth when resolveBubbleUpRoute says it can, forward the identical ask to the held token's own issuer and re-delegate whatever grant comes back when it cannot, or refuse outright when there is no route to a grantor at all.
50
+ *
51
+ * Parses and validates the incoming request the same way capability-request.ts's own createCapabilityRequestHandler does (wrong capability or malformed payload refuses `{result:"error",code:"malformed"}`; an already-expired `valid-until` refuses `{result:"error",code:"expired"}`), so a requester using the ordinary requestCapability wrapper sees identical wire behaviour from either handler.
52
+ *
53
+ * A forward's own upstream outcome is relayed verbatim on refusal (the exact code and message an intermediate or root grantor sent), rather than collapsed to a generic denial, so the original requester learns exactly what stopped the chain. A forward's own granted token is independently re-verified (expectedBearer this device's own identity) before being trusted as a mint parent: mintCapabilityToken's own `parent` handling only checks structural narrowing against the decoded claims, not the token's signature, expiry, or revocation status, so an unverified inbound grant would otherwise let a malicious or buggy upstream peer hand this device a bogus credential to unknowingly re-delegate.
54
+ */
55
+ export function createBubbleUpCapabilityRequestHandler(options) {
56
+ return async function handleBubbleUpCapabilityRequest(incoming) {
57
+ const parsed = capabilityRequestSchema.safeParse(incoming.command.params);
58
+ if (!parsed.success || parsed.data.capability !== options.capability) {
59
+ await incoming.respond({ result: "error", code: "malformed" });
60
+ return;
61
+ }
62
+ const validUntil = parsed.data["valid-until"];
63
+ if (validUntil !== undefined && validUntil <= options.clock.now()) {
64
+ await incoming.respond({ result: "error", code: "expired" });
65
+ return;
66
+ }
67
+ const route = await resolveBubbleUpRoute({
68
+ heldToken: options.ownToken(),
69
+ identity: options.identity,
70
+ clock: options.clock,
71
+ revocation: options.revocation,
72
+ capability: options.capability,
73
+ scope: incoming.scope,
74
+ expires: options.expires,
75
+ ...(options.delegationsRemaining !== undefined
76
+ ? { delegationsRemaining: options.delegationsRemaining }
77
+ : {}),
78
+ });
79
+ if (route.kind === "grant") {
80
+ await mintAndRespond(options, incoming, route.token);
81
+ return;
82
+ }
83
+ if (route.kind === "forward") {
84
+ const forwardOutcome = await options.session.sendManageRequest(buildCapabilityRequestCommand(options.capability, validUntil), incoming.scope, route.target, undefined, options.forwardTimeoutMs);
85
+ if (forwardOutcome.result !== "ok") {
86
+ await incoming.respond(forwardOutcome);
87
+ return;
88
+ }
89
+ const parsedGrant = capabilityGrantOkSchema.safeParse(forwardOutcome);
90
+ if (!parsedGrant.success) {
91
+ await incoming.respond({ result: "error", code: "malformed" });
92
+ return;
93
+ }
94
+ const upstreamVerdict = await verifyCapabilityToken(parsedGrant.data["granted-token"], {
95
+ identity: options.identity,
96
+ clock: options.clock,
97
+ revocation: options.revocation,
98
+ expectedBearer: options.identity.deviceId,
99
+ });
100
+ if (!upstreamVerdict.ok) {
101
+ await incoming.respond({
102
+ result: "error",
103
+ code: upstreamVerdict.reason,
104
+ });
105
+ return;
106
+ }
107
+ await mintAndRespond(options, incoming, parsedGrant.data["granted-token"]);
108
+ return;
109
+ }
110
+ await incoming.respond({
111
+ result: "error",
112
+ code: "denied",
113
+ message: `no route to a grantor for "${options.capability}"`,
114
+ });
115
+ };
116
+ }
117
+ /** Mints a delegated child of `parent` (this device's own held token, or a freshly re-verified upstream grant) for the requester, and responds with it -- shared by both createBubbleUpCapabilityRequestHandler branches that end in a mint, so the two can never drift into two different ideas of what the resulting token should carry. */
118
+ async function mintAndRespond(options, incoming, parent) {
119
+ const verdict = await mintCapabilityToken({
120
+ identity: options.identity,
121
+ clock: options.clock,
122
+ tokenId: randomId(),
123
+ bearer: options.bearerDevice,
124
+ capability: options.capability,
125
+ scope: incoming.scope,
126
+ expires: options.expires,
127
+ ...(options.delegationsRemaining !== undefined
128
+ ? { delegationsRemaining: options.delegationsRemaining }
129
+ : {}),
130
+ parent,
131
+ });
132
+ if (!verdict.ok) {
133
+ await incoming.respond({ result: "error", code: "mint_failed" });
134
+ return;
135
+ }
136
+ await incoming.respond({ result: "ok", "granted-token": verdict.token });
137
+ }
138
+ //# sourceMappingURL=capability-bubble-up.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-bubble-up.js","sourceRoot":"","sources":["../../src/core/capability-bubble-up.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * CoordinatorGateway — attaches the cross-machine gateway role to this machine's local coordinator (agent-comms#154, agent-comms#153's first leg). A bridge that becomes the mesh's local coordinator, whether by a fresh bind (MeshStore's own init()) or a takeover (PeerLifecycle's own handleBecomeCoordinator), also becomes the machine's gateway: it dials the hub and holds the connection for as long as it holds the coordinator role. Losing the role, gracefully or by crash, drops the connection; the next coordinator re-dials as part of taking over. Hub-side state is therefore rebuilt from scratch on every takeover -- messages in flight during the gap are lost, the same loss class as a coordinator crash today, now on the data path. Forwarding local agents onto the hub and merging its directory back (agent-comms#155) is deliberately not this class's concern; it owns only the connection lifecycle.
3
+ */
4
+ export interface CoordinatorGatewayDeps {
5
+ /** The hub URL this machine's gateway dials -- configuration, defaulting to DEFAULT_HUB_URL (mesh-store-shared.ts). */
6
+ hubUrl: string;
7
+ /** Dials the hub. Backed by the transport's own optional connectHub -- a transport with no gateway capability is never asked to redial by anything else in this class. */
8
+ connectHub: (url: string) => Promise<void>;
9
+ /** Drops the held hub connection, if any. Backed by the transport's own optional disconnectHub. */
10
+ disconnectHub: () => Promise<void>;
11
+ /** Reports a hub-dial failure. Never invoked for anything else -- onBecameCoordinator's own guarantee (see its doc comment) is that a hub problem is always reported this way, never thrown, so this is the only signal a caller gets that the gateway role didn't actually connect. */
12
+ onError?: (error: Error) => void;
13
+ }
14
+ export declare class CoordinatorGateway {
15
+ private readonly deps;
16
+ private connected;
17
+ constructor(deps: Readonly<CoordinatorGatewayDeps>);
18
+ /** Whether this side currently holds the gateway role: it has dialled the hub and has not since lost coordinator status. */
19
+ get isConnected(): boolean;
20
+ /** Dials the hub for this machine's gateway role. Idempotent: a call while already connected is a no-op, since nothing in this codebase's own coordinator-election machinery re-fires "became coordinator" without an intervening onLostCoordinator -- this guard is defensive, not a known double-fire path. Never throws: local coordinator election (the whole reason this side is calling this at all) must not depend on the hub being reachable, so a dial failure is reported via deps.onError and swallowed here, leaving isConnected false so a later onBecameCoordinator call retries rather than being blocked by the earlier failure's own idempotency guard. */
21
+ onBecameCoordinator(): Promise<void>;
22
+ /** Drops this machine's held hub connection. A no-op if this side never became the gateway, or already lost the role -- MeshStore.shutdown() calls this unconditionally regardless of coordinator status, so this guard is what makes that safe rather than a redundant extra close. */
23
+ onLostCoordinator(): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=coordinator-gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-gateway.d.ts","sourceRoot":"","sources":["../../src/core/coordinator-gateway.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,sBAAsB;IACrC,uHAAuH;IACvH,MAAM,EAAE,MAAM,CAAC;IACf,0KAA0K;IAC1K,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,mGAAmG;IACnG,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,wRAAwR;IACxR,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,qBAAa,kBAAkB;IAGjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,SAAS,CAAS;gBAEG,IAAI,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IAEnE,4HAA4H;IAC5H,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,6oBAA6oB;IACvoB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAY1C,wRAAwR;IAClR,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;CAKzC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * CoordinatorGateway — attaches the cross-machine gateway role to this machine's local coordinator (agent-comms#154, agent-comms#153's first leg). A bridge that becomes the mesh's local coordinator, whether by a fresh bind (MeshStore's own init()) or a takeover (PeerLifecycle's own handleBecomeCoordinator), also becomes the machine's gateway: it dials the hub and holds the connection for as long as it holds the coordinator role. Losing the role, gracefully or by crash, drops the connection; the next coordinator re-dials as part of taking over. Hub-side state is therefore rebuilt from scratch on every takeover -- messages in flight during the gap are lost, the same loss class as a coordinator crash today, now on the data path. Forwarding local agents onto the hub and merging its directory back (agent-comms#155) is deliberately not this class's concern; it owns only the connection lifecycle.
3
+ */
4
+ export class CoordinatorGateway {
5
+ deps;
6
+ connected = false;
7
+ constructor(deps) {
8
+ this.deps = deps;
9
+ }
10
+ /** Whether this side currently holds the gateway role: it has dialled the hub and has not since lost coordinator status. */
11
+ get isConnected() {
12
+ return this.connected;
13
+ }
14
+ /** Dials the hub for this machine's gateway role. Idempotent: a call while already connected is a no-op, since nothing in this codebase's own coordinator-election machinery re-fires "became coordinator" without an intervening onLostCoordinator -- this guard is defensive, not a known double-fire path. Never throws: local coordinator election (the whole reason this side is calling this at all) must not depend on the hub being reachable, so a dial failure is reported via deps.onError and swallowed here, leaving isConnected false so a later onBecameCoordinator call retries rather than being blocked by the earlier failure's own idempotency guard. */
15
+ async onBecameCoordinator() {
16
+ if (this.connected)
17
+ return;
18
+ try {
19
+ await this.deps.connectHub(this.deps.hubUrl);
20
+ this.connected = true;
21
+ }
22
+ catch (error) {
23
+ this.deps.onError?.(error instanceof Error ? error : new Error(String(error)));
24
+ }
25
+ }
26
+ /** Drops this machine's held hub connection. A no-op if this side never became the gateway, or already lost the role -- MeshStore.shutdown() calls this unconditionally regardless of coordinator status, so this guard is what makes that safe rather than a redundant extra close. */
27
+ async onLostCoordinator() {
28
+ if (!this.connected)
29
+ return;
30
+ this.connected = false;
31
+ await this.deps.disconnectHub();
32
+ }
33
+ }
34
+ //# sourceMappingURL=coordinator-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-gateway.js","sourceRoot":"","sources":["../../src/core/coordinator-gateway.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,MAAM,OAAO,kBAAkB;IAGA;IAFrB,SAAS,GAAG,KAAK,CAAC;IAE1B,YAA6B,IAAsC;QAAtC,SAAI,GAAJ,IAAI,CAAkC;IAAG,CAAC;IAEvE,4HAA4H;IAC5H,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,6oBAA6oB;IAC7oB,KAAK,CAAC,mBAAmB;QACvB,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CACjB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,wRAAwR;IACxR,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAC5B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;IAClC,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * mergeKnownDevices — the mesh-wide gossip-directory aggregation WireMeshTransport's own listKnownDevices reads from. Split out purely to keep wire-mesh-transport.ts under the repo's max-lines cap, the same reason connection-approval.ts, room-router.ts, hub-session.ts, and peer-lifecycle.ts were each split from their own owning file.
3
+ */
4
+ import type { DirectoryEntry } from "wire-mesh-core/domain/mesh-session";
5
+ import type { PeerAdvert } from "wire-mesh-core/generated/protocol";
6
+ /** Merges one session event's own directory into the mesh-wide knownDevices view (mutated in place), keeping the newer advert (by snapshot-seconds) whenever a device-id is already known from an earlier event or a different session. */
7
+ export declare function mergeKnownDevices(knownDevices: Map<string, PeerAdvert>, directory: readonly DirectoryEntry[]): void;
8
+ //# sourceMappingURL=gossip-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gossip-directory.d.ts","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAEpE,2OAA2O;AAC3O,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACrC,SAAS,EAAE,SAAS,cAAc,EAAE,GACnC,IAAI,CAWN"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * mergeKnownDevices — the mesh-wide gossip-directory aggregation WireMeshTransport's own listKnownDevices reads from. Split out purely to keep wire-mesh-transport.ts under the repo's max-lines cap, the same reason connection-approval.ts, room-router.ts, hub-session.ts, and peer-lifecycle.ts were each split from their own owning file.
3
+ */
4
+ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
5
+ /** Merges one session event's own directory into the mesh-wide knownDevices view (mutated in place), keeping the newer advert (by snapshot-seconds) whenever a device-id is already known from an earlier event or a different session. */
6
+ export function mergeKnownDevices(knownDevices, directory) {
7
+ for (const entry of directory) {
8
+ const deviceIdHex = deviceIdToHex(entry.device);
9
+ const existing = knownDevices.get(deviceIdHex);
10
+ if (existing === undefined ||
11
+ entry.advert["snapshot-seconds"] >= existing["snapshot-seconds"]) {
12
+ knownDevices.set(deviceIdHex, entry.advert);
13
+ }
14
+ }
15
+ }
16
+ //# sourceMappingURL=gossip-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gossip-directory.js","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAIhE,2OAA2O;AAC3O,MAAM,UAAU,iBAAiB,CAC/B,YAAqC,EACrC,SAAoC;IAEpC,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC/C,IACE,QAAQ,KAAK,SAAS;YACtB,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,QAAQ,CAAC,kBAAkB,CAAC,EAChE,CAAC;YACD,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -27,11 +27,15 @@ export declare class HubSession {
27
27
  constructor(deps: Readonly<HubSessionDeps>);
28
28
  /** This node's own device-id, hex-encoded -- the identifier hub peers address it by. */
29
29
  ownDeviceHex(): Promise<string>;
30
+ /** Whether a hub session is currently live (connect() has resolved and disconnect() hasn't run since, and the far end hasn't closed it -- watchDisconnect clears this.session when the hub's own event stream reports closed). */
31
+ get isConnected(): boolean;
32
+ /** 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). */
33
+ disconnect(): Promise<void>;
30
34
  /** The device ids (hex) of peers discovered through the hub's gossiped directory. */
31
35
  peers(): readonly string[];
32
36
  /** Dials the hub and participates as a peer (see the class doc for the discovery and routing model). */
33
37
  connect(url: string): Promise<void>;
34
- /** Dispatches inbound relayed manage-requests: each is handled with a handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests), so onMessage and every downstream consumer see the true origin, never the hub. Replies ride respond()'s own relay routing back. */
38
+ /** Dispatches inbound relayed manage-requests: each is handled with a handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests), so onMessage and every downstream consumer see the true origin, never the hub. Replies ride respond()'s own relay routing back. A state_sync/state_update is dropped before ever reaching onMessage/applyPatch -- see isStateMutatingMessage's own doc for why: the hub has no per-peer admission control yet (that lands in agent-comms#156), so accepting one from an arbitrary hub peer would let it directly patch this side's mesh state (a security review finding on agent-comms#169, which is what first wired a hub connection into production's default coordinator path at all). */
35
39
  private consume;
36
40
  private watchDisconnect;
37
41
  /** Sends one message to a hub-discovered peer through the hub's relay pairing. */
@@ -1 +1 @@
1
- {"version":3,"file":"hub-session.d.ts","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKtD,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,aAAa,EAAE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAClC,yHAAyH;IACzH,cAAc,EAAE,MAAM,OAAO,CAAC;IAC9B,wEAAwE;IACxE,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,sIAAsI;IACtI,OAAO,EAAE,CACP,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAChC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,KACnB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,2FAA2F;IAC3F,gBAAgB,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC1D;AAED,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;gBAEtB,IAAI,EAAE,QAAQ,CAAC,cAAc,CAAC;IAE3D,wFAAwF;IAClF,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAKrC,qFAAqF;IACrF,KAAK,IAAI,SAAS,MAAM,EAAE;IAI1B,wGAAwG;IAClG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCzC,oSAAoS;IACpS,OAAO,CAAC,OAAO;YAkBD,eAAe;IAS7B,kFAAkF;IAC5E,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAW7E"}
1
+ {"version":3,"file":"hub-session.d.ts","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKtD,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,aAAa,EAAE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAClC,yHAAyH;IACzH,cAAc,EAAE,MAAM,OAAO,CAAC;IAC9B,wEAAwE;IACxE,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,sIAAsI;IACtI,OAAO,EAAE,CACP,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAChC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,KACnB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,2FAA2F;IAC3F,gBAAgB,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC1D;AAED,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;gBAEtB,IAAI,EAAE,QAAQ,CAAC,cAAc,CAAC;IAE3D,wFAAwF;IAClF,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAKrC,kOAAkO;IAClO,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,mKAAmK;IAC7J,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC,qFAAqF;IACrF,KAAK,IAAI,SAAS,MAAM,EAAE;IAI1B,wGAAwG;IAClG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCzC,guBAAguB;IAChuB,OAAO,CAAC,OAAO;YAkBD,eAAe;IAS7B,kFAAkF;IAC5E,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAW7E"}
@@ -18,6 +18,18 @@ export class HubSession {
18
18
  const identity = await this.deps.identityReady;
19
19
  return deviceIdToHex(identity.deviceId);
20
20
  }
21
+ /** Whether a hub session is currently live (connect() has resolved and disconnect() hasn't run since, and the far end hasn't closed it -- watchDisconnect clears this.session when the hub's own event stream reports closed). */
22
+ get isConnected() {
23
+ return this.session !== undefined;
24
+ }
25
+ /** 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). */
26
+ async disconnect() {
27
+ const session = this.session;
28
+ if (session === undefined)
29
+ return;
30
+ this.session = undefined;
31
+ await session.close();
32
+ }
21
33
  /** The device ids (hex) of peers discovered through the hub's gossiped directory. */
22
34
  peers() {
23
35
  return [...this.hubPeersKnown];
@@ -60,7 +72,7 @@ export class HubSession {
60
72
  await this.watchDisconnect(session);
61
73
  })();
62
74
  }
63
- /** Dispatches inbound relayed manage-requests: each is handled with a handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests), so onMessage and every downstream consumer see the true origin, never the hub. Replies ride respond()'s own relay routing back. */
75
+ /** Dispatches inbound relayed manage-requests: each is handled with a handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests), so onMessage and every downstream consumer see the true origin, never the hub. Replies ride respond()'s own relay routing back. A state_sync/state_update is dropped before ever reaching onMessage/applyPatch -- see isStateMutatingMessage's own doc for why: the hub has no per-peer admission control yet (that lands in agent-comms#156), so accepting one from an arbitrary hub peer would let it directly patch this side's mesh state (a security review finding on agent-comms#169, which is what first wired a hub connection into production's default coordinator path at all). */
64
76
  consume(session) {
65
77
  void (async () => {
66
78
  for await (const request of session.incomingManageRequests) {
@@ -71,7 +83,7 @@ export class HubSession {
71
83
  : "hub-peer";
72
84
  this.hubPeersKnown.add(senderHex);
73
85
  const message = extractMessage(request.command);
74
- if (message !== undefined) {
86
+ if (message !== undefined && !isStateMutatingMessage(message)) {
75
87
  this.deps.events.onMessage({ id: senderHex }, message);
76
88
  }
77
89
  await request.respond({ result: "ok" }).catch(() => undefined);
@@ -103,4 +115,8 @@ function hexToBytes(hex) {
103
115
  }
104
116
  return bytes;
105
117
  }
118
+ /** A state_sync or state_update carries authority to directly overwrite or patch this side's own mesh state (agents, rooms, messages, deliveries) -- on an ordinary peer session that authority is meaningful because the peer already passed connect_request/introduce approval or the coordinator's own trusted mesh membership. A hub-relayed sender has passed neither: today's hub accepts any self-generated identity and gates nothing per-peer (agent-comms#156's own future deliverable), so treating its state_sync/state_update as equally authoritative would let an arbitrary hub peer inject an outcome indistinguishable from a genuine mesh event, e.g. a spoofed inbound delivery. Every other legacy message method this session might relay is already inert on receipt (PeerLifecycle's own handleDataMessage only reacts to these two), so filtering exactly these two is a complete fix for this specific path, not a partial one. */
119
+ function isStateMutatingMessage(message) {
120
+ return message.method === "state_sync" || message.method === "state_update";
121
+ }
106
122
  //# 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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"}
@@ -28,6 +28,8 @@ export type RoomJoinDecision = {
28
28
  export declare const MAX_QUEUED_DELIVERIES_PER_AGENT = 100;
29
29
  /** The coordinator's own bind host -- always loopback, since the mesh coordinator role only ever needs to be reachable from other local peers on this machine. Shared between mesh-store.ts's own init() and PeerLifecycle's handleBecomeCoordinator. */
30
30
  export declare const COORDINATOR_HOST = "127.0.0.1";
31
+ /** The production relay hub this machine's gateway dials once it becomes the local mesh coordinator (agent-comms#154). Configuration: MeshStore's own constructor accepts an override, threaded from createBridgeMesh/createBridgeMeshSync, for tests and any future non-default deployment -- this is only the default. */
32
+ export declare const DEFAULT_HUB_URL = "wss://mesh.exadev.io/";
31
33
  /**
32
34
  * Merge an incoming append-only message history with the local one, returning a new array: entries the local list does not have are appended, and read receipts are unioned on the ones it does. Local ordering is preserved; unseen entries are appended. Neither input array (nor its entries) is mutated -- the caller is responsible for storing the returned array back wherever the local history is kept.
33
35
  */
@@ -1 +1 @@
1
- {"version":3,"file":"mesh-store-shared.d.ts","sourceRoot":"","sources":["../../src/core/mesh-store-shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,oeAAoe;AACpe,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,WAAW,EAAE,eAAe,CAAC;CAC9B;AAUD,eAAO,MAAM,sBAAsB,QAKpB,CAAC;AAEhB,qKAAqK;AACrK,MAAM,MAAM,gBAAgB,GAC1B;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,yPAAyP;AACzP,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAO5C;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,EAC1C,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAgBlD"}
1
+ {"version":3,"file":"mesh-store-shared.d.ts","sourceRoot":"","sources":["../../src/core/mesh-store-shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,oeAAoe;AACpe,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,WAAW,EAAE,eAAe,CAAC;CAC9B;AAUD,eAAO,MAAM,sBAAsB,QAKpB,CAAC;AAEhB,qKAAqK;AACrK,MAAM,MAAM,gBAAgB,GAC1B;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,yPAAyP;AACzP,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAE5C,4TAA4T;AAC5T,eAAO,MAAM,eAAe,0BAA0B,CAAC;AAOvD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,EAC1C,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAgBlD"}
@@ -20,6 +20,8 @@ export const ROOM_TOKEN_LIFETIME_MS = ROOM_TOKEN_LIFETIME_DAYS *
20
20
  export const MAX_QUEUED_DELIVERIES_PER_AGENT = 100;
21
21
  /** The coordinator's own bind host -- always loopback, since the mesh coordinator role only ever needs to be reachable from other local peers on this machine. Shared between mesh-store.ts's own init() and PeerLifecycle's handleBecomeCoordinator. */
22
22
  export const COORDINATOR_HOST = "127.0.0.1";
23
+ /** The production relay hub this machine's gateway dials once it becomes the local mesh coordinator (agent-comms#154). Configuration: MeshStore's own constructor accepts an override, threaded from createBridgeMesh/createBridgeMeshSync, for tests and any future non-default deployment -- this is only the default. */
24
+ export const DEFAULT_HUB_URL = "wss://mesh.exadev.io/";
23
25
  /** Shallow-clones an entry together with its own `readBy` array, so a merged history never shares mutable array references with either input it was built from. */
24
26
  function cloneWithReadBy(entry) {
25
27
  return { ...entry, readBy: [...entry.readBy] };
@@ -1 +1 @@
1
- {"version":3,"file":"mesh-store-shared.js","sourceRoot":"","sources":["../../src/core/mesh-store-shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAiBH;;GAEG;AACH,MAAM,wBAAwB,GAAG,EAAE,CAAC;AACpC,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,CAAC,MAAM,sBAAsB,GACjC,wBAAwB;IACxB,aAAa;IACb,gBAAgB;IAChB,kBAAkB;IAClB,aAAa,CAAC;AAMhB;;GAEG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAEnD,yPAAyP;AACzP,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAE5C,mKAAmK;AACnK,SAAS,eAAe,CAAiC,KAAQ;IAC/D,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAEnC,KAAmB,EAAE,QAAsB;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YAChC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"mesh-store-shared.js","sourceRoot":"","sources":["../../src/core/mesh-store-shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAiBH;;GAEG;AACH,MAAM,wBAAwB,GAAG,EAAE,CAAC;AACpC,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,CAAC,MAAM,sBAAsB,GACjC,wBAAwB;IACxB,aAAa;IACb,gBAAgB;IAChB,kBAAkB;IAClB,aAAa,CAAC;AAMhB;;GAEG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAEnD,yPAAyP;AACzP,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAE5C,4TAA4T;AAC5T,MAAM,CAAC,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAEvD,mKAAmK;AACnK,SAAS,eAAe,CAAiC,KAAQ;IAC/D,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAEnC,KAAmB,EAAE,QAAsB;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YAChC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -19,6 +19,7 @@ export declare class MeshStore implements CommsStore {
19
19
  peerId: string;
20
20
  readonly startedAt: string;
21
21
  readonly coordinatorPort: number;
22
+ private readonly hubUrl;
22
23
  private readonly agents;
23
24
  private readonly rooms;
24
25
  private readonly messages;
@@ -42,6 +43,7 @@ export declare class MeshStore implements CommsStore {
42
43
  private readonly agentRegistry;
43
44
  private readonly connectionApproval;
44
45
  private readonly staleAgentChecker;
46
+ private readonly coordinatorGateway;
45
47
  private readonly peerLifecycle;
46
48
  /** 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. */
47
49
  get selfStatus(): AgentStatus | undefined;
@@ -60,7 +62,7 @@ export declare class MeshStore implements CommsStore {
60
62
  onError: ((error: Error) => void) | undefined;
61
63
  /** Serialise the full mesh state for state_sync messages. */
62
64
  serialise(): SerialisedState;
63
- constructor(coordinatorPort?: number);
65
+ constructor(coordinatorPort?: number, hubUrl?: string);
64
66
  /** Sets the transport (e.g. WireMeshTransport for encrypted connections). Must be called before init() or any other transport-using method. */
65
67
  setTransport(transport: Readonly<MeshTransport>): void;
66
68
  /** The set transport, or throws if setTransport() hasn't been called yet -- the single place every transport-using method reads through, so the "must call setTransport() first" contract is enforced at one boundary rather than checked ad hoc at each call site. */