agent-comms 2.1.0 → 2.3.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.
@@ -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": "2.1.0"
18
+ "version": "2.3.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": "2.1.0",
4
+ "version": "2.3.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-2.1.0-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v2.1.0)
5
+ [![version](https://img.shields.io/badge/version-2.3.0-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v2.3.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.
@@ -1,10 +1,11 @@
1
1
  /**
2
- * Persistent bridge identity: load-or-create the TLS key material for a (harness, cwd) slot so the certificate fingerprint and therefore the peer and agent ID survives restarts.
2
+ * Persistent bridge identity: load-or-create the TLS key material for a (harness, cwd) slot so the device-id -- and therefore the peer and agent ID -- survives restarts. Also persists a per-room capability token set in the same slot, so a room membership grant survives a restart the same way the identity it was minted against does.
3
3
  *
4
- * Mesh state stays in memory and on the wire; the only thing on disk is this local credential, the same trust model as an SSH key. A lock file holding a PID keeps two live bridges in one slot from sharing an identity, which would put duplicate peer IDs on the mesh; the second bridge runs with an ephemeral identity (the behaviour before persistence) instead. Bridges without a graceful shutdown hook can skip releasing the lock: a stale lock is detected by probing the recorded PID, the same way the coordinator probes for stale agents.
4
+ * Mesh state stays in memory and on the wire; the only thing on disk is this local credential (plus, now, the tokens minted against it), the same trust model as an SSH key. A lock file holding a PID keeps two live bridges in one slot from sharing an identity, which would put duplicate peer IDs on the mesh; the second bridge runs with an ephemeral identity (the behaviour before persistence) instead. Bridges without a graceful shutdown hook can skip releasing the lock: a stale lock is detected by probing the recorded PID, the same way the coordinator probes for stale agents.
5
5
  *
6
- * Persisting the key material rather than a bare agent ID is what makes restarts work: delivery routing fires when agentId === peerId, and peerId is the live certificate fingerprint, so an ID without its key can never match the running peer.
6
+ * Persisting the key material rather than a bare agent ID is what makes restarts work: delivery routing fires when agentId === peerId, and peerId is deviceIdToHex(identity.deviceId), so an ID without its key can never match the running peer. Renewal near certificate expiry currently generates an entirely fresh key pair rather than re-certifying the existing one, which silently rotates the device-id (and so invalidates any persisted room tokens bound to it) -- a real, separately tracked gap (#68), not addressed here.
7
7
  */
8
+ import type { CapabilityToken } from "wire-mesh-core/generated/protocol";
8
9
  import type { PeerIdentity } from "./identity.js";
9
10
  /** A bridge's identity slot: one persisted identity per harness and cwd. */
10
11
  export interface IdentitySlot {
@@ -21,4 +22,14 @@ export declare function loadOrCreateIdentity(slot: IdentitySlot): PeerIdentity;
21
22
  * Release the slot lock on graceful shutdown. A lock held by another PID (taken over after this process crashed and restarted) is left alone.
22
23
  */
23
24
  export declare function releaseIdentityLock(slot: IdentitySlot): void;
25
+ /**
26
+ * Every room-membership capability token persisted for this slot, keyed by room path. Empty if the slot has never had one saved (or the identity file doesn't exist yet -- call loadOrCreateIdentity first).
27
+ */
28
+ export declare function loadRoomTokens(slot: IdentitySlot): Record<string, CapabilityToken>;
29
+ /**
30
+ * Persists a capability token for the given room path, surviving a restart the same way the identity it was minted against does. Overwrites any token already saved for that room; leaves every other room's token and the identity's own key material untouched.
31
+ */
32
+ export declare function saveRoomToken(slot: IdentitySlot, roomPath: string, token: CapabilityToken): void;
33
+ /** Removes the persisted token for one room path, if any. A no-op if none was saved for that path. */
34
+ export declare function deleteRoomToken(slot: IdentitySlot, roomPath: string): void;
24
35
  //# sourceMappingURL=identity-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity-store.d.ts","sourceRoot":"","sources":["../../src/core/identity-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAkFD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAgBrE;AAsCD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAK5D"}
1
+ {"version":3,"file":"identity-store.d.ts","sourceRoot":"","sources":["../../src/core/identity-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAOzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAuJD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAgBrE;AAsCD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAK5D;AAuBD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,YAAY,GACjB,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CASjC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,eAAe,GACrB,IAAI,CAaN;AAED,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQ1E"}
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Persistent bridge identity: load-or-create the TLS key material for a (harness, cwd) slot so the certificate fingerprint and therefore the peer and agent ID survives restarts.
2
+ * Persistent bridge identity: load-or-create the TLS key material for a (harness, cwd) slot so the device-id -- and therefore the peer and agent ID -- survives restarts. Also persists a per-room capability token set in the same slot, so a room membership grant survives a restart the same way the identity it was minted against does.
3
3
  *
4
- * Mesh state stays in memory and on the wire; the only thing on disk is this local credential, the same trust model as an SSH key. A lock file holding a PID keeps two live bridges in one slot from sharing an identity, which would put duplicate peer IDs on the mesh; the second bridge runs with an ephemeral identity (the behaviour before persistence) instead. Bridges without a graceful shutdown hook can skip releasing the lock: a stale lock is detected by probing the recorded PID, the same way the coordinator probes for stale agents.
4
+ * Mesh state stays in memory and on the wire; the only thing on disk is this local credential (plus, now, the tokens minted against it), the same trust model as an SSH key. A lock file holding a PID keeps two live bridges in one slot from sharing an identity, which would put duplicate peer IDs on the mesh; the second bridge runs with an ephemeral identity (the behaviour before persistence) instead. Bridges without a graceful shutdown hook can skip releasing the lock: a stale lock is detected by probing the recorded PID, the same way the coordinator probes for stale agents.
5
5
  *
6
- * Persisting the key material rather than a bare agent ID is what makes restarts work: delivery routing fires when agentId === peerId, and peerId is the live certificate fingerprint, so an ID without its key can never match the running peer.
6
+ * Persisting the key material rather than a bare agent ID is what makes restarts work: delivery routing fires when agentId === peerId, and peerId is deviceIdToHex(identity.deviceId), so an ID without its key can never match the running peer. Renewal near certificate expiry currently generates an entirely fresh key pair rather than re-certifying the existing one, which silently rotates the device-id (and so invalidates any persisted room tokens bound to it) -- a real, separately tracked gap (#68), not addressed here.
7
7
  */
8
8
  import * as fs from "node:fs";
9
9
  import * as os from "node:os";
@@ -11,6 +11,21 @@ import * as path from "node:path";
11
11
  import { generateIdentity, getCertificateFingerprint, deriveDeviceId, CERTIFICATE_VALIDITY_MS, } from "./identity.js";
12
12
  /** Renew during the final twelfth of the certificate's validity. */
13
13
  const RENEWAL_MARGIN_MS = CERTIFICATE_VALIDITY_MS / 12;
14
+ function isSerializedCapabilityToken(value) {
15
+ if (!Array.isArray(value) || value.length !== 4)
16
+ return false;
17
+ const protectedHeader = value[0];
18
+ const unprotectedHeader = value[1];
19
+ const payload = value[2];
20
+ const signature = value[3];
21
+ if (typeof protectedHeader !== "string")
22
+ return false;
23
+ if (typeof unprotectedHeader !== "object" || unprotectedHeader === null)
24
+ return false;
25
+ if (payload !== null && typeof payload !== "string")
26
+ return false;
27
+ return typeof signature === "string";
28
+ }
14
29
  function isStoredIdentity(value) {
15
30
  if (typeof value !== "object" || value === null)
16
31
  return false;
@@ -18,9 +33,52 @@ function isStoredIdentity(value) {
18
33
  !("certificate" in value) ||
19
34
  !("expiresAt" in value))
20
35
  return false;
21
- return (typeof value.privateKey === "string" &&
22
- typeof value.certificate === "string" &&
23
- typeof value.expiresAt === "string");
36
+ if (typeof value.privateKey !== "string" ||
37
+ typeof value.certificate !== "string" ||
38
+ typeof value.expiresAt !== "string")
39
+ return false;
40
+ if (!("roomTokens" in value))
41
+ return true;
42
+ const { roomTokens } = value;
43
+ if (typeof roomTokens !== "object" || roomTokens === null)
44
+ return false;
45
+ return Object.values(roomTokens).every(isSerializedCapabilityToken);
46
+ }
47
+ function serializeToken(token) {
48
+ const [protectedHeader, unprotectedHeader, payload, signature] = token;
49
+ const serializedUnprotected = {
50
+ ...(unprotectedHeader["1"] !== undefined
51
+ ? { "1": unprotectedHeader["1"] }
52
+ : {}),
53
+ ...(unprotectedHeader["4"] !== undefined
54
+ ? { "4": Buffer.from(unprotectedHeader["4"]).toString("base64") }
55
+ : {}),
56
+ };
57
+ return [
58
+ Buffer.from(protectedHeader).toString("base64"),
59
+ serializedUnprotected,
60
+ payload === null ? null : Buffer.from(payload).toString("base64"),
61
+ Buffer.from(signature).toString("base64"),
62
+ ];
63
+ }
64
+ function deserializeToken(serialized) {
65
+ const [protectedHeader, unprotectedHeader, payload, signature] = serialized;
66
+ const deserializedUnprotected = {
67
+ ...(unprotectedHeader["1"] !== undefined
68
+ ? { "1": unprotectedHeader["1"] }
69
+ : {}),
70
+ ...(unprotectedHeader["4"] !== undefined
71
+ ? {
72
+ "4": Uint8Array.from(Buffer.from(unprotectedHeader["4"], "base64")),
73
+ }
74
+ : {}),
75
+ };
76
+ return [
77
+ Uint8Array.from(Buffer.from(protectedHeader, "base64")),
78
+ deserializedUnprotected,
79
+ payload === null ? null : Uint8Array.from(Buffer.from(payload, "base64")),
80
+ Uint8Array.from(Buffer.from(signature, "base64")),
81
+ ];
24
82
  }
25
83
  /** Replace path separators and other filesystem-hostile characters. */
26
84
  function slugifyCwd(cwd) {
@@ -127,4 +185,58 @@ export function releaseIdentityLock(slot) {
127
185
  fs.rmSync(lockFile, { force: true });
128
186
  }
129
187
  }
188
+ /** Reads the slot's raw stored identity record, or undefined if the file is missing or unparseable. Used by the room-token functions below to read-modify-write only the roomTokens field, leaving the persisted key material exactly as it is. */
189
+ function readStoredIdentity(identityFile) {
190
+ let parsed;
191
+ try {
192
+ parsed = JSON.parse(fs.readFileSync(identityFile, "utf-8"));
193
+ }
194
+ catch {
195
+ return undefined;
196
+ }
197
+ return isStoredIdentity(parsed) ? parsed : undefined;
198
+ }
199
+ function writeStoredIdentity(identityFile, stored) {
200
+ fs.writeFileSync(identityFile, `${JSON.stringify(stored, null, 2)}\n`, {
201
+ encoding: "utf-8",
202
+ mode: 0o600,
203
+ });
204
+ }
205
+ /**
206
+ * Every room-membership capability token persisted for this slot, keyed by room path. Empty if the slot has never had one saved (or the identity file doesn't exist yet -- call loadOrCreateIdentity first).
207
+ */
208
+ export function loadRoomTokens(slot) {
209
+ const { identityFile } = slotPaths(slot);
210
+ const stored = readStoredIdentity(identityFile);
211
+ const serialized = stored?.roomTokens ?? {};
212
+ const tokens = {};
213
+ for (const [roomPath, token] of Object.entries(serialized)) {
214
+ tokens[roomPath] = deserializeToken(token);
215
+ }
216
+ return tokens;
217
+ }
218
+ /**
219
+ * Persists a capability token for the given room path, surviving a restart the same way the identity it was minted against does. Overwrites any token already saved for that room; leaves every other room's token and the identity's own key material untouched.
220
+ */
221
+ export function saveRoomToken(slot, roomPath, token) {
222
+ const { identityFile } = slotPaths(slot);
223
+ const stored = readStoredIdentity(identityFile);
224
+ if (stored === undefined) {
225
+ throw new Error(`no identity persisted for this slot yet -- call loadOrCreateIdentity first (${identityFile})`);
226
+ }
227
+ const roomTokens = {
228
+ ...stored.roomTokens,
229
+ [roomPath]: serializeToken(token),
230
+ };
231
+ writeStoredIdentity(identityFile, { ...stored, roomTokens });
232
+ }
233
+ /** Removes the persisted token for one room path, if any. A no-op if none was saved for that path. */
234
+ export function deleteRoomToken(slot, roomPath) {
235
+ const { identityFile } = slotPaths(slot);
236
+ const stored = readStoredIdentity(identityFile);
237
+ if (stored?.roomTokens === undefined)
238
+ return;
239
+ const roomTokens = Object.fromEntries(Object.entries(stored.roomTokens).filter(([path]) => path !== roomPath));
240
+ writeStoredIdentity(identityFile, { ...stored, roomTokens });
241
+ }
130
242
  //# sourceMappingURL=identity-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity-store.js","sourceRoot":"","sources":["../../src/core/identity-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"identity-store.js","sourceRoot":"","sources":["../../src/core/identity-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * core/room's six verifier obligations (spec/room.cddl), layered on top of verifyCapabilityToken (obligations 2, 4, and 5 -- bearer match, ordinary token-claims checks, and delegations-remaining narrowing -- already live there). This module adds the two obligations specific to room-shaped scopes: the chain must terminate at the correct root for the path's own shape (1), and the token's own scope must actually name the room the request claims to act on (3). Obligation 6 (refuse an unrecognised content-type/kind) is a message-handling concern, not a token-verification one, and belongs to the room verb router instead.
3
+ */
4
+ import { type TokenVerdictReason, type VerifyCapabilityTokenOptions } from "wire-mesh-core/domain/tokens";
5
+ import type { CapabilityToken, DeviceId, TokenClaims } from "wire-mesh-core/generated/protocol";
6
+ export type RoomTokenVerdictReason = TokenVerdictReason | "wrong_scope_kind" | "wrong_scope_path" | "wrong_chain_root";
7
+ export type RoomTokenVerdict = {
8
+ ok: true;
9
+ claims: TokenClaims;
10
+ } | {
11
+ ok: false;
12
+ reason: RoomTokenVerdictReason;
13
+ };
14
+ export interface VerifyRoomTokenOptions extends Omit<VerifyCapabilityTokenOptions, "expectedBearer"> {
15
+ /** The peer identity actually authenticated on the arriving connection (obligation 2) -- never a relay-asserted or gossip-derived value. Mandatory here: every room verb requires a bearer, unlike verifyCapabilityToken's own optional field for callers presenting a token to authorise themselves rather than a specific counterparty. */
16
+ expectedBearer: DeviceId;
17
+ /** The room path this request claims to act on (obligation 3) -- must equal the token's own scope.path, and its own shape determines the chain root obligation 1 requires. */
18
+ roomPath: string;
19
+ }
20
+ /**
21
+ * Verifies a `room:member` capability token against all six of core/room's verifier obligations. Delegates obligations 2/4/5 to verifyCapabilityToken directly; adds obligation 3 (scope.kind/scope.path must match the room being acted on) and obligation 1 (the chain's root must be the room's own owner for a named room, or the verifying identity itself for a DM -- never either named participant directly, since a DM token minted by anyone other than the verifier would let a sender self-issue authority to message a stranger unsolicited).
22
+ */
23
+ export declare function verifyRoomToken(token: CapabilityToken, options: VerifyRoomTokenOptions): Promise<RoomTokenVerdict>;
24
+ //# sourceMappingURL=room-token-verification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-token-verification.d.ts","sourceRoot":"","sources":["../../src/core/room-token-verification.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,4BAA4B,EAClC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,WAAW,EACZ,MAAM,mCAAmC,CAAC;AAG3C,MAAM,MAAM,sBAAsB,GAC9B,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACjC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAElD,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAClD,4BAA4B,EAC5B,gBAAgB,CACjB;IACC,6UAA6U;IAC7U,cAAc,EAAE,QAAQ,CAAC;IACzB,8KAA8K;IAC9K,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CA4B3B"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * core/room's six verifier obligations (spec/room.cddl), layered on top of verifyCapabilityToken (obligations 2, 4, and 5 -- bearer match, ordinary token-claims checks, and delegations-remaining narrowing -- already live there). This module adds the two obligations specific to room-shaped scopes: the chain must terminate at the correct root for the path's own shape (1), and the token's own scope must actually name the room the request claims to act on (3). Obligation 6 (refuse an unrecognised content-type/kind) is a message-handling concern, not a token-verification one, and belongs to the room verb router instead.
3
+ */
4
+ import { verifyCapabilityToken, } from "wire-mesh-core/domain/tokens";
5
+ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
6
+ import { parseRoomPath } from "./room-path.js";
7
+ /**
8
+ * Verifies a `room:member` capability token against all six of core/room's verifier obligations. Delegates obligations 2/4/5 to verifyCapabilityToken directly; adds obligation 3 (scope.kind/scope.path must match the room being acted on) and obligation 1 (the chain's root must be the room's own owner for a named room, or the verifying identity itself for a DM -- never either named participant directly, since a DM token minted by anyone other than the verifier would let a sender self-issue authority to message a stranger unsolicited).
9
+ */
10
+ export async function verifyRoomToken(token, options) {
11
+ const verdict = await verifyCapabilityToken(token, {
12
+ identity: options.identity,
13
+ clock: options.clock,
14
+ revocation: options.revocation,
15
+ expectedBearer: options.expectedBearer,
16
+ });
17
+ if (!verdict.ok) {
18
+ return verdict;
19
+ }
20
+ if (verdict.claims.scope.kind !== "room") {
21
+ return { ok: false, reason: "wrong_scope_kind" };
22
+ }
23
+ if (verdict.claims.scope.path !== options.roomPath) {
24
+ return { ok: false, reason: "wrong_scope_path" };
25
+ }
26
+ const parsed = parseRoomPath(options.roomPath);
27
+ const expectedRootHex = parsed.kind === "owner-named"
28
+ ? parsed.owner
29
+ : deviceIdToHex(options.identity.deviceId);
30
+ if (deviceIdToHex(verdict.rootIssuer) !== expectedRootHex) {
31
+ return { ok: false, reason: "wrong_chain_root" };
32
+ }
33
+ return { ok: true, claims: verdict.claims };
34
+ }
35
+ //# sourceMappingURL=room-token-verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-token-verification.js","sourceRoot":"","sources":["../../src/core/room-token-verification.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,qBAAqB,GAGtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAMhE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAsB/C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAsB,EACtB,OAA+B;IAE/B,MAAM,OAAO,GAAG,MAAM,qBAAqB,CAAC,KAAK,EAAE;QACjD,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC;QACnD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,eAAe,GACnB,MAAM,CAAC,IAAI,KAAK,aAAa;QAC3B,CAAC,CAAC,MAAM,CAAC,KAAK;QACd,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,eAAe,EAAE,CAAC;QAC1D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Unit tests for per-room capability token persistence (core/identity-store).
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=room-token-store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-token-store.test.d.ts","sourceRoot":"","sources":["../../src/test/room-token-store.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Unit tests for per-room capability token persistence (core/identity-store).
3
+ */
4
+ import * as assert from "node:assert/strict";
5
+ import * as fs from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import path from "node:path";
8
+ import { test } from "node:test";
9
+ import { loadOrCreateIdentity, loadRoomTokens, saveRoomToken, deleteRoomToken, releaseIdentityLock, } from "../core/identity-store.js";
10
+ function tempSlot(harness) {
11
+ const dir = fs.mkdtempSync(path.join(tmpdir(), "agent-comms-room-token-test-"));
12
+ return { slot: { harness, cwd: "/tmp/project", dir }, dir };
13
+ }
14
+ function buf(bytes) {
15
+ return Uint8Array.from(bytes);
16
+ }
17
+ const TOKEN_A = [
18
+ buf([1, 2, 3]),
19
+ {},
20
+ buf([4, 5, 6]),
21
+ buf([7, 8, 9]),
22
+ ];
23
+ const TOKEN_B = [
24
+ buf([10, 11]),
25
+ { 1: -7 },
26
+ null,
27
+ buf([12, 13, 14]),
28
+ ];
29
+ const ROOM_A = "aa".repeat(32) + "/general";
30
+ const ROOM_B = "bb".repeat(32) + "/other-room";
31
+ void test("loadRoomTokens returns an empty map before anything is saved", () => {
32
+ const { slot } = tempSlot("pi");
33
+ loadOrCreateIdentity(slot);
34
+ assert.deepEqual(loadRoomTokens(slot), {});
35
+ releaseIdentityLock(slot);
36
+ });
37
+ void test("saveRoomToken persists a token retrievable by loadRoomTokens", () => {
38
+ const { slot } = tempSlot("claude-code");
39
+ loadOrCreateIdentity(slot);
40
+ saveRoomToken(slot, ROOM_A, TOKEN_A);
41
+ const tokens = loadRoomTokens(slot);
42
+ assert.deepEqual(tokens[ROOM_A], TOKEN_A);
43
+ releaseIdentityLock(slot);
44
+ });
45
+ void test("saveRoomToken round-trips a token whose payload is null", () => {
46
+ const { slot } = tempSlot("codex");
47
+ loadOrCreateIdentity(slot);
48
+ saveRoomToken(slot, ROOM_B, TOKEN_B);
49
+ const tokens = loadRoomTokens(slot);
50
+ assert.deepEqual(tokens[ROOM_B], TOKEN_B);
51
+ releaseIdentityLock(slot);
52
+ });
53
+ void test("saveRoomToken for a second room does not disturb the first", () => {
54
+ const { slot } = tempSlot("mcp");
55
+ loadOrCreateIdentity(slot);
56
+ saveRoomToken(slot, ROOM_A, TOKEN_A);
57
+ saveRoomToken(slot, ROOM_B, TOKEN_B);
58
+ const tokens = loadRoomTokens(slot);
59
+ assert.deepEqual(tokens[ROOM_A], TOKEN_A);
60
+ assert.deepEqual(tokens[ROOM_B], TOKEN_B);
61
+ releaseIdentityLock(slot);
62
+ });
63
+ void test("saveRoomToken overwrites an existing token for the same room", () => {
64
+ const { slot } = tempSlot("opencode");
65
+ loadOrCreateIdentity(slot);
66
+ saveRoomToken(slot, ROOM_A, TOKEN_A);
67
+ saveRoomToken(slot, ROOM_A, TOKEN_B);
68
+ const tokens = loadRoomTokens(slot);
69
+ assert.deepEqual(tokens[ROOM_A], TOKEN_B);
70
+ assert.equal(Object.keys(tokens).length, 1);
71
+ releaseIdentityLock(slot);
72
+ });
73
+ void test("deleteRoomToken removes only the named room's token", () => {
74
+ const { slot } = tempSlot("user");
75
+ loadOrCreateIdentity(slot);
76
+ saveRoomToken(slot, ROOM_A, TOKEN_A);
77
+ saveRoomToken(slot, ROOM_B, TOKEN_B);
78
+ deleteRoomToken(slot, ROOM_A);
79
+ const tokens = loadRoomTokens(slot);
80
+ assert.equal(ROOM_A in tokens, false);
81
+ assert.deepEqual(tokens[ROOM_B], TOKEN_B);
82
+ releaseIdentityLock(slot);
83
+ });
84
+ void test("room tokens survive reloading the identity file across a fresh load", () => {
85
+ const { slot } = tempSlot("pi");
86
+ loadOrCreateIdentity(slot);
87
+ saveRoomToken(slot, ROOM_A, TOKEN_A);
88
+ releaseIdentityLock(slot);
89
+ // A fresh process re-loading the same slot.
90
+ loadOrCreateIdentity(slot);
91
+ const tokens = loadRoomTokens(slot);
92
+ assert.deepEqual(tokens[ROOM_A], TOKEN_A);
93
+ releaseIdentityLock(slot);
94
+ });
95
+ void test("saving a room token does not disturb the persisted key material", () => {
96
+ const { slot } = tempSlot("claude-code");
97
+ const identity = loadOrCreateIdentity(slot);
98
+ saveRoomToken(slot, ROOM_A, TOKEN_A);
99
+ const reloaded = loadOrCreateIdentity(slot);
100
+ assert.equal(reloaded.privateKey, identity.privateKey);
101
+ assert.equal(reloaded.certificate, identity.certificate);
102
+ releaseIdentityLock(slot);
103
+ });
104
+ //# sourceMappingURL=room-token-store.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-token-store.test.js","sourceRoot":"","sources":["../../src/test/room-token-store.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=room-token-verification.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-token-verification.test.d.ts","sourceRoot":"","sources":["../../src/test/room-token-verification.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,212 @@
1
+ import { webcrypto } from "node:crypto";
2
+ import { describe, it } from "node:test";
3
+ import assert from "node:assert/strict";
4
+ import { createNodeIdentity } from "wire-mesh-core/adapters/node-identity";
5
+ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
6
+ import { mintCapabilityToken, mintRevocationEntry, } from "wire-mesh-core/domain/tokens";
7
+ import { createRevocationView } from "wire-mesh-core/domain/revocation-view";
8
+ import { ownerNamedRoomPath, dmRoomPath } from "../core/room-path.js";
9
+ import { verifyRoomToken } from "../core/room-token-verification.js";
10
+ const ES256 = -7;
11
+ const HOUR_MS = 3_600_000;
12
+ function buf(bytes) {
13
+ return Uint8Array.from(bytes);
14
+ }
15
+ async function generateEs256Identity() {
16
+ const keyPair = await webcrypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
17
+ const publicKeyBytes = new Uint8Array(await webcrypto.subtle.exportKey("raw", keyPair.publicKey));
18
+ return createNodeIdentity(keyPair.privateKey, publicKeyBytes, ES256);
19
+ }
20
+ function fixedClock(atMs) {
21
+ return { now: () => atMs };
22
+ }
23
+ let issuedTokenIds = 0;
24
+ function nextTokenId() {
25
+ issuedTokenIds += 1;
26
+ return buf([issuedTokenIds]);
27
+ }
28
+ const NOW_MS = 1_893_456_000_000;
29
+ const EXPIRES_MS = NOW_MS + HOUR_MS;
30
+ async function mintRoomMemberToken(issuer, bearer, roomPath, tokenId = nextTokenId()) {
31
+ const verdict = await mintCapabilityToken({
32
+ identity: issuer,
33
+ clock: fixedClock(NOW_MS),
34
+ tokenId,
35
+ bearer: bearer.deviceId,
36
+ capability: "room:member",
37
+ scope: { kind: "room", path: roomPath },
38
+ expires: EXPIRES_MS,
39
+ delegationsRemaining: 0,
40
+ });
41
+ if (!verdict.ok)
42
+ throw new Error(`mint failed: ${verdict.reason}`);
43
+ return verdict.token;
44
+ }
45
+ describe("verifyRoomToken", () => {
46
+ it("accepts a token rooted at the named room's own owner", async () => {
47
+ const owner = await generateEs256Identity();
48
+ const member = await generateEs256Identity();
49
+ const roomPath = ownerNamedRoomPath(deviceIdToHex(owner.deviceId), "general");
50
+ const token = await mintRoomMemberToken(owner, member, roomPath);
51
+ const verdict = await verifyRoomToken(token, {
52
+ identity: owner,
53
+ clock: fixedClock(NOW_MS),
54
+ revocation: createRevocationView(),
55
+ expectedBearer: member.deviceId,
56
+ roomPath,
57
+ });
58
+ assert.equal(verdict.ok, true);
59
+ });
60
+ it("refuses a token rooted at the wrong device for a named room", async () => {
61
+ const owner = await generateEs256Identity();
62
+ const impostor = await generateEs256Identity();
63
+ const member = await generateEs256Identity();
64
+ const roomPath = ownerNamedRoomPath(deviceIdToHex(owner.deviceId), "general");
65
+ // Minted by the impostor, not the room's actual owner -- rootIssuer will be the impostor's own device-id, not the path's owner.
66
+ const token = await mintRoomMemberToken(impostor, member, roomPath);
67
+ const verdict = await verifyRoomToken(token, {
68
+ identity: owner,
69
+ clock: fixedClock(NOW_MS),
70
+ revocation: createRevocationView(),
71
+ expectedBearer: member.deviceId,
72
+ roomPath,
73
+ });
74
+ assert.equal(verdict.ok, false);
75
+ if (!verdict.ok)
76
+ assert.equal(verdict.reason, "wrong_chain_root");
77
+ });
78
+ it("accepts a DM token rooted at the verifying identity itself", async () => {
79
+ const me = await generateEs256Identity();
80
+ const them = await generateEs256Identity();
81
+ const roomPath = dmRoomPath(deviceIdToHex(me.deviceId), deviceIdToHex(them.deviceId));
82
+ // I (the verifier) issue the token that lets `them` send to me.
83
+ const token = await mintRoomMemberToken(me, them, roomPath);
84
+ const verdict = await verifyRoomToken(token, {
85
+ identity: me,
86
+ clock: fixedClock(NOW_MS),
87
+ revocation: createRevocationView(),
88
+ expectedBearer: them.deviceId,
89
+ roomPath,
90
+ });
91
+ assert.equal(verdict.ok, true);
92
+ });
93
+ it("refuses a DM token rooted at neither participant nor the verifier", async () => {
94
+ const me = await generateEs256Identity();
95
+ const them = await generateEs256Identity();
96
+ const stranger = await generateEs256Identity();
97
+ const roomPath = dmRoomPath(deviceIdToHex(me.deviceId), deviceIdToHex(them.deviceId));
98
+ // A stranger self-issues a token for this DM path -- must never verify, since the DM path's only acceptable root is the verifier itself.
99
+ const token = await mintRoomMemberToken(stranger, them, roomPath);
100
+ const verdict = await verifyRoomToken(token, {
101
+ identity: me,
102
+ clock: fixedClock(NOW_MS),
103
+ revocation: createRevocationView(),
104
+ expectedBearer: them.deviceId,
105
+ roomPath,
106
+ });
107
+ assert.equal(verdict.ok, false);
108
+ if (!verdict.ok)
109
+ assert.equal(verdict.reason, "wrong_chain_root");
110
+ });
111
+ it("refuses a token scoped to a different room path", async () => {
112
+ const owner = await generateEs256Identity();
113
+ const member = await generateEs256Identity();
114
+ const actualRoomPath = ownerNamedRoomPath(deviceIdToHex(owner.deviceId), "general");
115
+ const otherRoomPath = ownerNamedRoomPath(deviceIdToHex(owner.deviceId), "other-room");
116
+ const token = await mintRoomMemberToken(owner, member, actualRoomPath);
117
+ const verdict = await verifyRoomToken(token, {
118
+ identity: owner,
119
+ clock: fixedClock(NOW_MS),
120
+ revocation: createRevocationView(),
121
+ expectedBearer: member.deviceId,
122
+ roomPath: otherRoomPath,
123
+ });
124
+ assert.equal(verdict.ok, false);
125
+ if (!verdict.ok)
126
+ assert.equal(verdict.reason, "wrong_scope_path");
127
+ });
128
+ it("refuses a token scoped to a non-room capability", async () => {
129
+ const owner = await generateEs256Identity();
130
+ const member = await generateEs256Identity();
131
+ const roomPath = ownerNamedRoomPath(deviceIdToHex(owner.deviceId), "general");
132
+ const verdict1 = await mintCapabilityToken({
133
+ identity: owner,
134
+ clock: fixedClock(NOW_MS),
135
+ tokenId: nextTokenId(),
136
+ bearer: member.deviceId,
137
+ capability: "exec:pty",
138
+ scope: { kind: "folder" },
139
+ expires: EXPIRES_MS,
140
+ });
141
+ if (!verdict1.ok)
142
+ throw new Error(`mint failed: ${verdict1.reason}`);
143
+ const verdict = await verifyRoomToken(verdict1.token, {
144
+ identity: owner,
145
+ clock: fixedClock(NOW_MS),
146
+ revocation: createRevocationView(),
147
+ expectedBearer: member.deviceId,
148
+ roomPath,
149
+ });
150
+ assert.equal(verdict.ok, false);
151
+ if (!verdict.ok)
152
+ assert.equal(verdict.reason, "wrong_scope_kind");
153
+ });
154
+ it("refuses a token bearing a different device than the authenticated connection", async () => {
155
+ const owner = await generateEs256Identity();
156
+ const member = await generateEs256Identity();
157
+ const impostor = await generateEs256Identity();
158
+ const roomPath = ownerNamedRoomPath(deviceIdToHex(owner.deviceId), "general");
159
+ const token = await mintRoomMemberToken(owner, member, roomPath);
160
+ const verdict = await verifyRoomToken(token, {
161
+ identity: owner,
162
+ clock: fixedClock(NOW_MS),
163
+ revocation: createRevocationView(),
164
+ expectedBearer: impostor.deviceId,
165
+ roomPath,
166
+ });
167
+ assert.equal(verdict.ok, false);
168
+ if (!verdict.ok)
169
+ assert.equal(verdict.reason, "bearer_mismatch");
170
+ });
171
+ it("refuses an expired token", async () => {
172
+ const owner = await generateEs256Identity();
173
+ const member = await generateEs256Identity();
174
+ const roomPath = ownerNamedRoomPath(deviceIdToHex(owner.deviceId), "general");
175
+ const token = await mintRoomMemberToken(owner, member, roomPath);
176
+ const verdict = await verifyRoomToken(token, {
177
+ identity: owner,
178
+ clock: fixedClock(EXPIRES_MS + HOUR_MS),
179
+ revocation: createRevocationView(),
180
+ expectedBearer: member.deviceId,
181
+ roomPath,
182
+ });
183
+ assert.equal(verdict.ok, false);
184
+ if (!verdict.ok)
185
+ assert.equal(verdict.reason, "expired");
186
+ });
187
+ it("refuses a revoked token", async () => {
188
+ const owner = await generateEs256Identity();
189
+ const member = await generateEs256Identity();
190
+ const roomPath = ownerNamedRoomPath(deviceIdToHex(owner.deviceId), "general");
191
+ const tokenId = nextTokenId();
192
+ const token = await mintRoomMemberToken(owner, member, roomPath, tokenId);
193
+ const revocation = createRevocationView();
194
+ const entry = await mintRevocationEntry({
195
+ identity: owner,
196
+ tokenId,
197
+ revokedAt: NOW_MS,
198
+ });
199
+ await revocation.record(entry, { identity: owner });
200
+ const verdict = await verifyRoomToken(token, {
201
+ identity: owner,
202
+ clock: fixedClock(NOW_MS),
203
+ revocation,
204
+ expectedBearer: member.deviceId,
205
+ roomPath,
206
+ });
207
+ assert.equal(verdict.ok, false);
208
+ if (!verdict.ok)
209
+ assert.equal(verdict.reason, "revoked");
210
+ });
211
+ });
212
+ //# sourceMappingURL=room-token-verification.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-token-verification.test.js","sourceRoot":"","sources":["../../src/test/room-token-verification.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAI7E,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAErE,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;AACjB,MAAM,OAAO,GAAG,SAAS,CAAC;AAE1B,SAAS,GAAG,CAAC,KAAqC;IAChD,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,KAAK,UAAU,qBAAqB;IAClC,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,WAAW,CAChD,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,EACtC,IAAI,EACJ,CAAC,MAAM,EAAE,QAAQ,CAAC,CACnB,CAAC;IACF,MAAM,cAAc,GAAG,IAAI,UAAU,CACnC,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,CAC3D,CAAC;IACF,OAAO,kBAAkB,CAAC,OAAO,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED,IAAI,cAAc,GAAG,CAAC,CAAC;AACvB,SAAS,WAAW;IAClB,cAAc,IAAI,CAAC,CAAC;IACpB,OAAO,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,MAAM,GAAG,iBAAiB,CAAC;AACjC,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpC,KAAK,UAAU,mBAAmB,CAChC,MAAoB,EACpB,MAAoB,EACpB,QAAgB,EAChB,UAAmC,WAAW,EAAE;IAEhD,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;QACxC,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;QACzB,OAAO;QACP,MAAM,EAAE,MAAM,CAAC,QAAQ;QACvB,UAAU,EAAE,aAAa;QACzB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE;QACvC,OAAO,EAAE,UAAU;QACnB,oBAAoB,EAAE,CAAC;KACxB,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACnE,OAAO,OAAO,CAAC,KAAK,CAAC;AACvB,CAAC;AAED,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,KAAK,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,kBAAkB,CACjC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7B,SAAS,CACV,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE;YAC3C,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,MAAM,CAAC,QAAQ;YAC/B,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,KAAK,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC5C,MAAM,QAAQ,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,kBAAkB,CACjC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7B,SAAS,CACV,CAAC;QACF,gIAAgI;QAChI,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAEpE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE;YAC3C,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,MAAM,CAAC,QAAQ;YAC/B,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,EAAE,GAAG,MAAM,qBAAqB,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,UAAU,CACzB,aAAa,CAAC,EAAE,CAAC,QAAQ,CAAC,EAC1B,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAC7B,CAAC;QACF,gEAAgE;QAChE,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAE5D,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE;YAC3C,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,IAAI,CAAC,QAAQ;YAC7B,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,EAAE,GAAG,MAAM,qBAAqB,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAG,UAAU,CACzB,aAAa,CAAC,EAAE,CAAC,QAAQ,CAAC,EAC1B,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAC7B,CAAC;QACF,yIAAyI;QACzI,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAElE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE;YAC3C,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,IAAI,CAAC,QAAQ;YAC7B,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,KAAK,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,cAAc,GAAG,kBAAkB,CACvC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7B,SAAS,CACV,CAAC;QACF,MAAM,aAAa,GAAG,kBAAkB,CACtC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7B,YAAY,CACb,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;QAEvE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE;YAC3C,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,MAAM,CAAC,QAAQ;YAC/B,QAAQ,EAAE,aAAa;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,KAAK,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,kBAAkB,CACjC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7B,SAAS,CACV,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC;YACzC,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,OAAO,EAAE,WAAW,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC,QAAQ;YACvB,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,OAAO,EAAE,UAAU;SACpB,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAErE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,KAAK,EAAE;YACpD,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,MAAM,CAAC,QAAQ;YAC/B,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,KAAK,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAG,kBAAkB,CACjC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7B,SAAS,CACV,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE;YAC3C,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,QAAQ,CAAC,QAAQ;YACjC,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,KAAK,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,kBAAkB,CACjC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7B,SAAS,CACV,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE;YAC3C,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,UAAU,CAAC,UAAU,GAAG,OAAO,CAAC;YACvC,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,MAAM,CAAC,QAAQ;YAC/B,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,KAAK,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,kBAAkB,CACjC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7B,SAAS,CACV,CAAC;QACF,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAE1E,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC;YACtC,QAAQ,EAAE,KAAK;YACf,OAAO;YACP,SAAS,EAAE,MAAM;SAClB,CAAC,CAAC;QACH,MAAM,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QAEpD,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE;YAC3C,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU;YACV,cAAc,EAAE,MAAM,CAAC,QAAQ;YAC/B,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-comms",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Cross-harness communication mesh for LLM agents — rooms, DMs, presence, and real-time push delivery over TCP",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.33.0",
@@ -97,7 +97,7 @@
97
97
  "cbor2": "2.3.0",
98
98
  "preact": "10.29.7",
99
99
  "typebox": "1.3.6",
100
- "wire-mesh-core": "1.0.1",
100
+ "wire-mesh-core": "1.0.2",
101
101
  "ws": "8.21.1",
102
102
  "zod": "4.4.3"
103
103
  },