agent-comms 2.2.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.2.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.2.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.2.0-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v2.2.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":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,uBAAuB,GACxB,MAAM,eAAe,CAAC;AAWvB,oEAAoE;AACpE,MAAM,iBAAiB,GAAG,uBAAuB,GAAG,EAAE,CAAC;AAQvD,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,IACE,CAAC,CAAC,YAAY,IAAI,KAAK,CAAC;QACxB,CAAC,CAAC,aAAa,IAAI,KAAK,CAAC;QACzB,CAAC,CAAC,WAAW,IAAI,KAAK,CAAC;QAEvB,OAAO,KAAK,CAAC;IACf,OAAO,CACL,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;QACpC,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;QACrC,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CACpC,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,SAAS,CAAC,IAAkB;IAKnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,YAAY,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IACjE,OAAO;QACL,GAAG;QACH,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC;QAC5C,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,SAAS,WAAW,CAAC,QAAgB;IACnC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB;IACjC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,eAAe,CAAC,YAAoB,EAAE,QAAsB;IACnE,MAAM,MAAM,GAAmB;QAC7B,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,uBAAuB,CAAC,CAAC,WAAW,EAAE;KACxE,CAAC;IACF,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QACrE,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAkB;IACrD,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACxD,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEpD,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,OAAO,CAAC,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACzE,OAAO,CAAC,KAAK,CACX,8BAA8B,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,GAAG,yBAAyB,MAAM,CAAC,MAAM,CAAC,sCAAsC,CACrI,CAAC;QACF,OAAO,gBAAgB,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,QAAQ,GACZ,kBAAkB,CAAC,YAAY,CAAC,IAAI,cAAc,CAAC,YAAY,CAAC,CAAC;IACnE,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpB,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,mFAAmF;AACnF,SAAS,kBAAkB,CAAC,YAAoB;IAC9C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAEhD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,iBAAiB,EAAE,CAAC;QAC1E,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,OAAO;YACL,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,WAAW,EAAE,yBAAyB,CAAC,MAAM,CAAC,WAAW,CAAC;YAC1D,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC,UAAU,CAAC;SAC5C,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CACX,mCAAmC,YAAY,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,gCAAgC,CACnJ,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,YAAoB;IAC1C,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,eAAe,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACxC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAkB;IACpD,MAAM,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,WAAW,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1C,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"identity-store.js","sourceRoot":"","sources":["../../src/core/identity-store.ts"],"names":[],"mappings":"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"}
@@ -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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-comms",
3
- "version": "2.2.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",