agent-comms 8.15.9 → 8.15.10

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 (184) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/bridges/user/web/server.d.ts.map +1 -1
  4. package/dist/bridges/user/web/server.js +44 -24
  5. package/dist/bridges/user/web/server.js.map +1 -1
  6. package/dist/bridges/user/web/test/web-assets.unit.test.d.ts +5 -0
  7. package/dist/bridges/user/web/test/web-assets.unit.test.d.ts.map +1 -0
  8. package/dist/bridges/user/web/test/web-assets.unit.test.js +90 -0
  9. package/dist/bridges/user/web/test/web-assets.unit.test.js.map +1 -0
  10. package/dist/bridges/user/web/test/web-server-unbuilt-frontend.integration.test.d.ts +2 -0
  11. package/dist/bridges/user/web/test/web-server-unbuilt-frontend.integration.test.d.ts.map +1 -0
  12. package/dist/bridges/user/web/test/web-server-unbuilt-frontend.integration.test.js +75 -0
  13. package/dist/bridges/user/web/test/web-server-unbuilt-frontend.integration.test.js.map +1 -0
  14. package/dist/bridges/user/web/web-assets.d.ts +28 -0
  15. package/dist/bridges/user/web/web-assets.d.ts.map +1 -0
  16. package/dist/bridges/user/web/web-assets.js +67 -0
  17. package/dist/bridges/user/web/web-assets.js.map +1 -0
  18. package/package.json +8 -2
  19. package/src/account-cli.ts +195 -0
  20. package/src/account-join-cli.ts +191 -0
  21. package/src/bridges/cc-peer/approval-commands.ts +82 -0
  22. package/src/bridges/cc-peer/bridge.ts +138 -0
  23. package/src/bridges/cc-peer/default-front.ts +58 -0
  24. package/src/bridges/cc-peer/front-controller.ts +160 -0
  25. package/src/bridges/cc-peer/front-relay.ts +191 -0
  26. package/src/bridges/cc-peer/front-runtime.ts +167 -0
  27. package/src/bridges/cc-peer/front.ts +59 -0
  28. package/src/bridges/cc-peer/reply-aliases.ts +90 -0
  29. package/src/bridges/cc-peer/run.ts +100 -0
  30. package/src/bridges/cc-peer/shared-peer.ts +28 -0
  31. package/src/bridges/claude-code/channel.ts +318 -0
  32. package/src/bridges/codex/tool.ts +132 -0
  33. package/src/bridges/mcp/server.ts +137 -0
  34. package/src/bridges/opencode/plugin.ts +130 -0
  35. package/src/bridges/pi/extension.ts +330 -0
  36. package/src/bridges/registry.ts +17 -0
  37. package/src/bridges/user/cli.ts +87 -0
  38. package/src/bridges/user/controller.ts +345 -0
  39. package/src/bridges/user/tui.ts +300 -0
  40. package/src/bridges/user/web/actions.ts +173 -0
  41. package/src/bridges/user/web/contract.ts +222 -0
  42. package/src/bridges/user/web/dist/bundle.js +49 -0
  43. package/src/bridges/user/web/dist/icons/icon-192x192.svg +9 -0
  44. package/src/bridges/user/web/dist/icons/icon-512x512.svg +9 -0
  45. package/src/bridges/user/web/dist/icons/icon-96x96.svg +9 -0
  46. package/src/bridges/user/web/dist/index.html +21 -0
  47. package/src/bridges/user/web/dist/manifest.json +26 -0
  48. package/src/bridges/user/web/dist/mesh-worker.js +64 -0
  49. package/src/bridges/user/web/dist/sw.js +1 -0
  50. package/src/bridges/user/web/event-publisher.ts +114 -0
  51. package/src/bridges/user/web/port-discovery.ts +49 -0
  52. package/src/bridges/user/web/reads.ts +35 -0
  53. package/src/bridges/user/web/router.ts +158 -0
  54. package/src/bridges/user/web/server.ts +782 -0
  55. package/src/bridges/user/web/vite.config.ts +71 -0
  56. package/src/bridges/user/web/web-access.ts +404 -0
  57. package/src/bridges/user/web/web-assets.ts +90 -0
  58. package/src/bridges/user/web/web-beacon.ts +69 -0
  59. package/src/bridges/user/web/web-console-static.ts +90 -0
  60. package/src/cli-main.ts +635 -0
  61. package/src/cli.ts +13 -0
  62. package/src/core/account-bundle.ts +264 -0
  63. package/src/core/account-join.ts +289 -0
  64. package/src/core/account-ledger-crypto.ts +154 -0
  65. package/src/core/account-ledger-store.ts +66 -0
  66. package/src/core/account-ledger.ts +385 -0
  67. package/src/core/agent-details.ts +216 -0
  68. package/src/core/agent-list-format.ts +97 -0
  69. package/src/core/agent-registry.ts +282 -0
  70. package/src/core/agent-table.ts +58 -0
  71. package/src/core/atomic-file.ts +46 -0
  72. package/src/core/bind-retry.ts +38 -0
  73. package/src/core/bridge-mesh.ts +176 -0
  74. package/src/core/bridge.ts +718 -0
  75. package/src/core/capability-ask.ts +162 -0
  76. package/src/core/capability-bubble-up.ts +237 -0
  77. package/src/core/comms-store.ts +130 -0
  78. package/src/core/connection-approval.ts +134 -0
  79. package/src/core/connection-code-tool.ts +92 -0
  80. package/src/core/connection-code.ts +296 -0
  81. package/src/core/coordinator-role.ts +224 -0
  82. package/src/core/data-directory.ts +32 -0
  83. package/src/core/data-frame-routing.ts +309 -0
  84. package/src/core/default-command.ts +12 -0
  85. package/src/core/delegation-policy.ts +41 -0
  86. package/src/core/delivery-engine.ts +649 -0
  87. package/src/core/device-membership-verification.ts +125 -0
  88. package/src/core/device-membership.ts +130 -0
  89. package/src/core/directory-admission.ts +155 -0
  90. package/src/core/discovery-mdns.ts +247 -0
  91. package/src/core/discovery-tailscale.ts +224 -0
  92. package/src/core/discovery.ts +303 -0
  93. package/src/core/display-name.ts +73 -0
  94. package/src/core/dm-grant-actions.ts +72 -0
  95. package/src/core/dm-send-delegation.ts +73 -0
  96. package/src/core/dm-token-verification.ts +76 -0
  97. package/src/core/election-sessions.ts +135 -0
  98. package/src/core/error-reporting.ts +85 -0
  99. package/src/core/first-contact.ts +242 -0
  100. package/src/core/gateway-trust-actions.ts +184 -0
  101. package/src/core/gateway-trust.ts +305 -0
  102. package/src/core/gossip-directory.ts +276 -0
  103. package/src/core/gossip-extensions.ts +123 -0
  104. package/src/core/group-proofs.ts +205 -0
  105. package/src/core/handshake.ts +354 -0
  106. package/src/core/hub-forwarding.ts +24 -0
  107. package/src/core/hub-link.ts +176 -0
  108. package/src/core/hub-session.ts +374 -0
  109. package/src/core/identity-store.ts +715 -0
  110. package/src/core/identity.ts +489 -0
  111. package/src/core/index.ts +65 -0
  112. package/src/core/issuer-grouping.ts +165 -0
  113. package/src/core/issuer-identity-file.ts +187 -0
  114. package/src/core/lan-web-actions.ts +34 -0
  115. package/src/core/lan-web-uis.ts +55 -0
  116. package/src/core/ledger-lock.ts +81 -0
  117. package/src/core/listener-registry.ts +133 -0
  118. package/src/core/loopback-address.ts +22 -0
  119. package/src/core/machine-identity.ts +77 -0
  120. package/src/core/machine-list-groups.ts +53 -0
  121. package/src/core/mcp-registry-retry.ts +69 -0
  122. package/src/core/mcp-registry-version.ts +18 -0
  123. package/src/core/membership-proof.ts +139 -0
  124. package/src/core/membership-proofs.ts +149 -0
  125. package/src/core/mesh-graph-trace-tool.ts +81 -0
  126. package/src/core/mesh-graph.ts +89 -0
  127. package/src/core/mesh-network-actions.ts +183 -0
  128. package/src/core/mesh-store-options.ts +23 -0
  129. package/src/core/mesh-store-shared.ts +134 -0
  130. package/src/core/mesh-store.ts +1114 -0
  131. package/src/core/name-claim.ts +145 -0
  132. package/src/core/naming-actions.ts +90 -0
  133. package/src/core/naming.ts +138 -0
  134. package/src/core/nanoid.ts +20 -0
  135. package/src/core/network-interfaces.ts +24 -0
  136. package/src/core/package-version.ts +34 -0
  137. package/src/core/peer-lifecycle.ts +377 -0
  138. package/src/core/peer-link-error.ts +41 -0
  139. package/src/core/peer-versions.ts +98 -0
  140. package/src/core/pending-connection.ts +53 -0
  141. package/src/core/petnames.ts +121 -0
  142. package/src/core/pgp-keyserver.ts +32 -0
  143. package/src/core/pid-lock-file.ts +122 -0
  144. package/src/core/push-manager.ts +76 -0
  145. package/src/core/random-id.ts +9 -0
  146. package/src/core/receiver-set.ts +42 -0
  147. package/src/core/relay-role.ts +182 -0
  148. package/src/core/relay-selection.ts +120 -0
  149. package/src/core/relay-server.ts +198 -0
  150. package/src/core/relay-uplink.ts +78 -0
  151. package/src/core/request-timeouts.ts +37 -0
  152. package/src/core/room-discovery.ts +48 -0
  153. package/src/core/room-lifecycle.ts +838 -0
  154. package/src/core/room-lookup.ts +41 -0
  155. package/src/core/room-messaging.ts +247 -0
  156. package/src/core/room-notice-log.ts +61 -0
  157. package/src/core/room-path.ts +84 -0
  158. package/src/core/room-protocol.ts +893 -0
  159. package/src/core/room-router.ts +180 -0
  160. package/src/core/room-token-verification.ts +85 -0
  161. package/src/core/room-wire-extensions.ts +60 -0
  162. package/src/core/self-name-claim.ts +67 -0
  163. package/src/core/send-outcome.ts +69 -0
  164. package/src/core/shutdown-signals.ts +57 -0
  165. package/src/core/stale-agent-checker.ts +108 -0
  166. package/src/core/store.ts +704 -0
  167. package/src/core/token-text.ts +40 -0
  168. package/src/core/tool.ts +853 -0
  169. package/src/core/transport-events.ts +73 -0
  170. package/src/core/transport-queries.ts +54 -0
  171. package/src/core/transport.ts +367 -0
  172. package/src/core/types.ts +550 -0
  173. package/src/core/user-identity.ts +290 -0
  174. package/src/core/vapid.ts +81 -0
  175. package/src/core/version-check.ts +147 -0
  176. package/src/core/version-report-actions.ts +79 -0
  177. package/src/core/web-beacon.ts +31 -0
  178. package/src/core/web-push.ts +322 -0
  179. package/src/core/wire-mesh-core-version.ts +25 -0
  180. package/src/core/wire-mesh-identity.ts +36 -0
  181. package/src/core/wire-mesh-transport-options.ts +82 -0
  182. package/src/core/wire-mesh-transport.ts +1007 -0
  183. package/src/core/wire-protocol.ts +153 -0
  184. package/src/core/ws-dial.ts +191 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Whole-file writes that a concurrent reader never observes half done, shared by the per-slot identity store (identity-store.ts), the grouping-issuer identity files (issuer-identity-file.ts) and the petnames file (petnames.ts). Every bridge on the machine may read these files while another bridge is writing them, so a reader must see either the complete previous content or the complete new content, never an empty or partial file.
3
+ */
4
+
5
+ import * as fs from "node:fs";
6
+
7
+ /** A sibling path unique to this process and call, on the same filesystem as filePath so a rename or link from it is atomic. */
8
+ function temporarySibling(filePath: string): string {
9
+ return `${filePath}.${String(process.pid)}.${String(Math.random()).slice(2)}.tmp`;
10
+ }
11
+
12
+ /**
13
+ * Writes content to filePath atomically: the full content is written to a temporary sibling file first, then renamed into place. rename() on the same filesystem is atomic, so a concurrent reader of filePath always sees either the complete previous content or the complete new content, never a truncated or partially-written file, unlike a bare writeFileSync, whose own open(O_TRUNC)-then-write leaves a real window where a concurrent reader can observe an empty or partial file.
14
+ */
15
+ export function writeFileAtomic(
16
+ filePath: string,
17
+ content: string,
18
+ mode: number,
19
+ ): void {
20
+ const tmpFile = temporarySibling(filePath);
21
+ fs.writeFileSync(tmpFile, content, { encoding: "utf-8", mode });
22
+ fs.renameSync(tmpFile, filePath);
23
+ }
24
+
25
+ /**
26
+ * Creates filePath with content only if nothing exists there yet, atomically: the full content is written to a temporary sibling first and then hard-linked into place. link() fails with EEXIST when the name already exists and never replaces it, and the name appears only once its content is complete, unlike open with the "wx" flag followed by a write, which leaves the file empty for a moment. Returns false when another writer created filePath first, leaving that writer's file untouched.
27
+ */
28
+ export function createFileExclusive(
29
+ filePath: string,
30
+ content: string,
31
+ mode: number,
32
+ ): boolean {
33
+ const tmpFile = temporarySibling(filePath);
34
+ fs.writeFileSync(tmpFile, content, { encoding: "utf-8", mode });
35
+ try {
36
+ fs.linkSync(tmpFile, filePath);
37
+ return true;
38
+ } catch (err) {
39
+ if (err instanceof Error && "code" in err && err.code === "EEXIST") {
40
+ return false;
41
+ }
42
+ throw err;
43
+ } finally {
44
+ fs.unlinkSync(tmpFile);
45
+ }
46
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * retryOnAddrInUse — bind-retry backoff for WireMeshTransport.becomeCoordinator (agent-comms#170). Split out of wire-mesh-transport.ts to keep that file under the repo's max-lines cap, matching the same split-out-a-collaborator convention peer-lifecycle.ts and friends already use.
3
+ */
4
+
5
+ /** Number of extra attempts becomeCoordinator makes after an initial EADDRINUSE before giving up. Covers PeerLifecycle's own graceful coordinator handover: the outgoing coordinator's listening socket may not have finished releasing the coordinator port yet when the successor's own become_coordinator handler tries to rebind it moments later. Retrying a plain server bind carries none of the risk documented elsewhere against retrying a TLS connectToCoordinator (a Node TLS session-cache bug that can freeze the event loop) -- this is ordinary bind-after-close backoff, the same class of transient conflict any two processes racing to acquire the same port would need to tolerate. */
6
+ export const BECOME_COORDINATOR_BIND_RETRIES = 5;
7
+ /** Delay between each becomeCoordinator bind retry -- five retries at this interval gives ~150ms of tolerance, comparable to the crash-race path's own documented "~100ms recovery". */
8
+ export const BECOME_COORDINATOR_BIND_RETRY_DELAY_MS = 30;
9
+
10
+ /** Whether an error is a port-already-bound failure, the one condition a bind is worth retrying (and, for PeerLifecycle's crash-race takeover, the one that means another survivor won the race rather than that binding is impossible). Matched on the message because Node surfaces the code there for every listen-path failure this codebase sees, including the ones wrapped by wire-mesh-core's own transport adapters, which carry no `code` property of their own. */
11
+ export function isAddrInUse(error: unknown): boolean {
12
+ const message = error instanceof Error ? error.message : String(error);
13
+ return message.includes("EADDRINUSE");
14
+ }
15
+
16
+ /** Whether an error is a nothing-is-listening dial failure. Distinguished from every other dial failure because it alone means the port is genuinely free, which is what tells PeerLifecycle's takeover that re-entering the bind race is worth it rather than that the coordinator is there but unreachable. Matched on the message for the same reason isAddrInUse is. */
17
+ export function isConnectionRefused(error: unknown): boolean {
18
+ const message = error instanceof Error ? error.message : String(error);
19
+ return message.includes("ECONNREFUSED");
20
+ }
21
+
22
+ /** Retries `attempt` while it keeps rejecting with an EADDRINUSE-shaped error, up to `retries` further attempts, waiting `delayMs` between each. Any other error, or exhausting the retry budget, rethrows immediately -- never silently swallowed. A pure, transport-agnostic helper (no socket knowledge of its own) so becomeCoordinator's bind-retry race is fast and deterministic to test directly. */
23
+ export async function retryOnAddrInUse<T>(
24
+ attempt: () => Promise<T>,
25
+ retries: number,
26
+ delayMs: number,
27
+ ): Promise<T> {
28
+ for (let tryNumber = 0; ; tryNumber++) {
29
+ try {
30
+ return await attempt();
31
+ } catch (error) {
32
+ if (!isAddrInUse(error) || tryNumber >= retries) throw error;
33
+ await new Promise<void>((resolve) => {
34
+ setTimeout(resolve, delayMs);
35
+ });
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Shared bootstrap for a bridge's own mesh participation: load or create this bridge's persisted identity, construct a MeshStore wired to WireMeshTransport, and build the CommsTool that sits on top of it. Every bridge previously repeated this same four-line block against TlsTransport/identity.fingerprint directly; this factory is the single place that wiring lives now, so the substrate a bridge runs on is a one-line change here rather than six repeated ones.
3
+ *
4
+ * peerId is deviceIdToHex(identity.deviceId), not identity.fingerprint -- WireMeshTransport's own session bookkeeping is keyed by device-id, so MeshStore's own notion of "this peer's id" has to be the same value for the two to correlate. Every existing agent id changes the first time a bridge starts through this factory: there is no migration path, since the value is a hash of genuinely different bytes (device-id is SHA-256(raw public key); fingerprint is SHA-256(certificate DER)) -- a hard cutover, already established as correct when identity.ts first grew deviceId, not relitigated here.
5
+ *
6
+ * Split into a synchronous half (wireBridgeMesh) and an async half (attachMintingIdentity, wrapping toIdentityPort's WebCrypto import) because deriving the IdentityPort MeshStore mints room-membership grants against is unavoidably async, but not every bridge entry point can await one inline -- a plugin loader that calls its extension's default export synchronously (e.g. pi's own) cannot. createBridgeMeshSync exposes both halves for that case, deferring attachIdentity() to wherever the bridge's own lifecycle first has an async context (its own session-start hook), which is always well before the bridge does anything identity-dependent like createRoom. createBridgeMesh remains the convenient all-in-one for every bridge whose own entry point is already async.
7
+ *
8
+ * Also starts this bridge's own VersionDriftChecker (agent-comms#166) and wires its result into the CommsTool it builds, so every real bridge gets npm release-drift reporting on whoami/update for free from this one construction point, with no per-bridge wiring. fetchLatestVersion is exposed purely for tests -- every real caller omits it and gets VersionDriftChecker's own default (a real npm registry lookup); a test that would otherwise trigger real network I/O on every createBridgeMesh call injects a fake resolver instead.
9
+ *
10
+ * createBridgeMeshSync/createBridgeMesh own loadOrCreateIdentity's slot lock on the caller's behalf; createBridgeMeshSyncFromIdentity/createBridgeMeshFromIdentity take an already-loaded identity instead and never touch the lock at all -- the cc-peer front (agent-comms#157) uses these directly, via loadIdentityForFront's lock-free load, to build a mesh identity for a not-yet-live session's slot while leaving that slot's own lock free for its real bridge to acquire normally later.
11
+ *
12
+ * Passes slot through to MeshStore's own constructor (agent-comms#186) so the gatewayTrust allowlist it builds loads whatever remote device-ids were trusted before the last restart, and persists every subsequent addTrustedGateway/removeTrustedGateway back to the identity directory's one shared trust file, which every other store on the machine reads as well (agent-comms#293). The same slot backs MeshStore's connectionCodes ledger (agent-comms#188), which stays per slot.
13
+ *
14
+ * The returned store's own getCcPeerVersion field starts undefined and is read live (never snapshotted) by both WireMeshTransport's gossip tick and CommsTool's whoami/update/list_agents -- front-runtime.ts and bridges/cc-peer/run.ts set it once, right after this factory returns, rather than this factory taking it as a parameter: only those two call sites ever have a value for it, so threading it through every createBridgeMesh* signature here would be dead weight on every other caller (agent-comms#198).
15
+ */
16
+
17
+ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
18
+ import { createSystemClock } from "wire-mesh-core/adapters/system-clock";
19
+ import { createRevocationView } from "wire-mesh-core/domain/revocation-view";
20
+ import { createNodeFsStorage } from "wire-mesh-core/adapters/node-fs-storage";
21
+ import { MeshStore } from "./mesh-store.js";
22
+ import { resolveHubUrl } from "./mesh-store-shared.js";
23
+ import { FIRST_CONTACT_PORT } from "./first-contact.js";
24
+ import { CommsTool } from "./tool.js";
25
+ import { WireMeshTransport } from "./wire-mesh-transport.js";
26
+ import { loadOrCreateIdentity, oplogDirFor } from "./identity-store.js";
27
+ import { openAccountLedger } from "./account-ledger-store.js";
28
+ import type { IdentitySlot } from "./identity-store.js";
29
+ import { toIdentityPort } from "./wire-mesh-identity.js";
30
+ import type { PeerIdentity } from "./identity.js";
31
+ import { VersionDriftChecker } from "./version-check.js";
32
+ import { getOwnPackageVersion } from "./package-version.js";
33
+ import { loadOrCreateUserIdentity } from "./user-identity.js";
34
+ import { loadOrCreateMachineIdentity } from "./machine-identity.js";
35
+ import { resolveDataDir } from "./data-directory.js";
36
+
37
+ export interface BridgeMesh {
38
+ store: MeshStore;
39
+ tool: CommsTool;
40
+ }
41
+
42
+ export interface BridgeMeshSync extends BridgeMesh {
43
+ /** Derives and wires this store's IdentityPort (the collaborator createRoom and every other identity-using method require). Must be awaited before any such method runs; safe to call from the bridge's own first async lifecycle hook rather than its (necessarily synchronous) entry point. */
44
+ attachIdentity: () => Promise<void>;
45
+ }
46
+
47
+ /** coordinatorPort/hubUrl thread straight into MeshStore's own constructor, hubUrl defaulting here and nowhere below to resolveHubUrl() (AGENT_COMMS_HUB_URL, else the production hub); fetchLatestVersion is exposed purely for tests -- every real caller omits it and gets VersionDriftChecker's own default (a real npm registry lookup). firstContactPort defaults to FIRST_CONTACT_PORT, the default-on coordinator-free formation presence (agent-comms#341); a bridge-level test overrides it with an OS-assigned free port so test bridges never cross-discover each other. */
48
+ export interface BridgeMeshOptions {
49
+ coordinatorPort?: number | undefined;
50
+ hubUrl?: string | undefined;
51
+ fetchLatestVersion?: (() => Promise<string | undefined>) | undefined;
52
+ firstContactPort?: number | undefined;
53
+ }
54
+
55
+ /** Seams that let a test run a real bridge against an isolated mesh; every real launch omits them. */
56
+ export interface BridgeRunOptions {
57
+ /** Ports and hub for the bridge's own mesh store; see BridgeMeshOptions. */
58
+ mesh?: BridgeMeshOptions | undefined;
59
+ /** The identity slot. Defaults to this process's working directory under the bridge's own harness. */
60
+ slot?: IdentitySlot | undefined;
61
+ }
62
+
63
+ /** 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. */
64
+ export function createBridgeMeshSync(
65
+ slot: Readonly<IdentitySlot>,
66
+ options?: BridgeMeshOptions,
67
+ ): BridgeMeshSync {
68
+ return createBridgeMeshSyncFromIdentity(
69
+ loadOrCreateIdentity(slot),
70
+ slot,
71
+ options,
72
+ );
73
+ }
74
+
75
+ /**
76
+ * The same synchronous construction as createBridgeMeshSync, but taking an already-loaded identity rather than calling loadOrCreateIdentity itself -- see this file's own header comment for who this is for. Every other caller should go through createBridgeMeshSync/createBridgeMesh above, which own the lock on the caller's behalf.
77
+ */
78
+ export function createBridgeMeshSyncFromIdentity(
79
+ identity: PeerIdentity,
80
+ slot: Readonly<IdentitySlot>,
81
+ options?: BridgeMeshOptions,
82
+ ): BridgeMeshSync {
83
+ const {
84
+ coordinatorPort,
85
+ hubUrl = resolveHubUrl(),
86
+ fetchLatestVersion,
87
+ firstContactPort = FIRST_CONTACT_PORT,
88
+ } = options ?? {};
89
+ // Account and machine keys are shared within the configured data directory, not scoped to the device's harness/cwd. Legacy slot.dir still overrides only the device's own files.
90
+ const userIdentityOptions = { dir: resolveDataDir({ dir: slot.dataDir }) };
91
+ const userIdentity = loadOrCreateUserIdentity(userIdentityOptions);
92
+ const machineIdentity = loadOrCreateMachineIdentity(userIdentityOptions);
93
+ const store = new MeshStore({
94
+ coordinatorPort,
95
+ hubUrl,
96
+ slot,
97
+ firstContactPort,
98
+ });
99
+ store.peerId = deviceIdToHex(Uint8Array.from(identity.deviceId));
100
+ // 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.
101
+ const dataStorage = createNodeFsStorage({ dir: oplogDirFor(slot) });
102
+ const transport = new WireMeshTransport(store.events, identity, {
103
+ roomVerbHandlers: store.roomVerbHandlers,
104
+ roomJoinApprovalTimeoutMs: store.roomJoinApprovalTimeoutMs,
105
+ verifyMembership: async (claim) => store.membership.verify(claim),
106
+ getCurrentPresence: () => store.selfStatus,
107
+ getHostedRooms: () => store.hostedRooms,
108
+ dataStorage,
109
+ getSelfAgentAdvert: () => store.selfAgentAdvert,
110
+ accountReplication: {
111
+ getLedger: () => store.accountLedger,
112
+ isAccountMember: async (claim) => store.membership.isAccountMember(claim),
113
+ },
114
+ gatewayTrust: store.gatewayTrust,
115
+ });
116
+ transport.getCcPeerVersion = () => store.getCcPeerVersion?.();
117
+ store.setTransport(transport);
118
+ const versionChecker = new VersionDriftChecker({
119
+ currentVersion: getOwnPackageVersion(),
120
+ ...(fetchLatestVersion !== undefined ? { fetchLatestVersion } : {}),
121
+ });
122
+ versionChecker.start();
123
+ const tool = new CommsTool(store, {
124
+ discovery: store.discovery,
125
+ naming: store.naming,
126
+ getNewerVersionIfAny: () => versionChecker.getNewerVersionIfAny(),
127
+ });
128
+ const revocation = createRevocationView();
129
+ return {
130
+ store,
131
+ tool,
132
+ attachIdentity: async () => {
133
+ const clock = createSystemClock();
134
+ store.setIdentity({
135
+ identity: await toIdentityPort(identity),
136
+ clock,
137
+ slot,
138
+ revocation,
139
+ dataStorage,
140
+ userIdentity: await toIdentityPort(userIdentity),
141
+ accountLedger: await openAccountLedger({
142
+ userIdentityOptions,
143
+ userIdentity,
144
+ clock,
145
+ }),
146
+ userIdentityOptions,
147
+ machineIdentity: await toIdentityPort(machineIdentity),
148
+ machineIdentityOptions: userIdentityOptions,
149
+ });
150
+ },
151
+ };
152
+ }
153
+
154
+ export async function createBridgeMesh(
155
+ slot: Readonly<IdentitySlot>,
156
+ options?: BridgeMeshOptions,
157
+ ): Promise<BridgeMesh> {
158
+ const { store, tool, attachIdentity } = createBridgeMeshSync(slot, options);
159
+ await attachIdentity();
160
+ return { store, tool };
161
+ }
162
+
163
+ /** The async, already-loaded-identity counterpart to createBridgeMesh, mirroring createBridgeMeshSyncFromIdentity's relationship to createBridgeMeshSync. */
164
+ export async function createBridgeMeshFromIdentity(
165
+ identity: PeerIdentity,
166
+ slot: Readonly<IdentitySlot>,
167
+ options?: BridgeMeshOptions,
168
+ ): Promise<BridgeMesh> {
169
+ const { store, tool, attachIdentity } = createBridgeMeshSyncFromIdentity(
170
+ identity,
171
+ slot,
172
+ options,
173
+ );
174
+ await attachIdentity();
175
+ return { store, tool };
176
+ }