agent-comms 2.20.2 → 2.21.1
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/core/agent-registry.d.ts +38 -0
- package/dist/core/agent-registry.d.ts.map +1 -0
- package/dist/core/agent-registry.js +117 -0
- package/dist/core/agent-registry.js.map +1 -0
- package/dist/core/connection-approval.d.ts +48 -0
- package/dist/core/connection-approval.d.ts.map +1 -0
- package/dist/core/connection-approval.js +89 -0
- package/dist/core/connection-approval.js.map +1 -0
- package/dist/core/delivery-engine.d.ts +82 -0
- package/dist/core/delivery-engine.d.ts.map +1 -0
- package/dist/core/delivery-engine.js +464 -0
- package/dist/core/delivery-engine.js.map +1 -0
- package/dist/core/federation-bridge.d.ts +32 -0
- package/dist/core/federation-bridge.d.ts.map +1 -0
- package/dist/core/federation-bridge.js +113 -0
- package/dist/core/federation-bridge.js.map +1 -0
- package/dist/core/mesh-store-shared.d.ts +39 -0
- package/dist/core/mesh-store-shared.d.ts.map +1 -0
- package/dist/core/mesh-store-shared.js +32 -0
- package/dist/core/mesh-store-shared.js.map +1 -0
- package/dist/core/mesh-store.d.ts +65 -256
- package/dist/core/mesh-store.d.ts.map +1 -1
- package/dist/core/mesh-store.js +211 -1917
- package/dist/core/mesh-store.js.map +1 -1
- package/dist/core/peer-lifecycle.d.ts +36 -0
- package/dist/core/peer-lifecycle.d.ts.map +1 -0
- package/dist/core/peer-lifecycle.js +85 -0
- package/dist/core/peer-lifecycle.js.map +1 -0
- package/dist/core/room-lifecycle.d.ts +82 -0
- package/dist/core/room-lifecycle.d.ts.map +1 -0
- package/dist/core/room-lifecycle.js +459 -0
- package/dist/core/room-lifecycle.js.map +1 -0
- package/dist/core/room-messaging.d.ts +31 -0
- package/dist/core/room-messaging.d.ts.map +1 -0
- package/dist/core/room-messaging.js +120 -0
- package/dist/core/room-messaging.js.map +1 -0
- package/dist/core/room-protocol.d.ts +85 -0
- package/dist/core/room-protocol.d.ts.map +1 -0
- package/dist/core/room-protocol.js +479 -0
- package/dist/core/room-protocol.js.map +1 -0
- package/dist/core/room-wire-extensions.d.ts +36 -0
- package/dist/core/room-wire-extensions.d.ts.map +1 -0
- package/dist/core/room-wire-extensions.js +52 -0
- package/dist/core/room-wire-extensions.js.map +1 -0
- package/dist/core/stale-agent-checker.d.ts +28 -0
- package/dist/core/stale-agent-checker.d.ts.map +1 -0
- package/dist/core/stale-agent-checker.js +77 -0
- package/dist/core/stale-agent-checker.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoomLifecycle — room CRUD and the requester's own outbound half of the wire protocol: create/get/list, join/leave (local CRDT mutation for an already-known room, or a real wire-level room.join/room.leave round trip via joinRemoteRoom/leaveRemoteRoom when this store's own identity isn't yet admitted), invite/decline, membership-grant revocation, kick, and destroy. Split out of mesh-store.ts to reduce it under the repo's max-lines cap.
|
|
3
|
+
*/
|
|
4
|
+
import { deviceIdFromHex, deviceIdToHex, } from "wire-mesh-core/domain/device-id";
|
|
5
|
+
import { mintCapabilityToken, mintRevocationEntry, } from "wire-mesh-core/domain/tokens";
|
|
6
|
+
import { roomJoinOkSchema, roomMembersOkSchema, } from "wire-mesh-core/generated/protocol";
|
|
7
|
+
import { dmRoomPath, ownerNamedRoomPath, parseRoomPath, slugRoomName, } from "./room-path.js";
|
|
8
|
+
import { ROOM_MEMBER_CAPABILITY } from "./room-token-verification.js";
|
|
9
|
+
import { deleteIssuedRoomGrant, deleteRoomToken, loadIssuedRoomGrant, loadRoomTokens, saveIssuedRoomGrant, saveRoomToken, } from "./identity-store.js";
|
|
10
|
+
import { randomId } from "./random-id.js";
|
|
11
|
+
import { CommsError } from "./store.js";
|
|
12
|
+
import { ROOM_TOKEN_LIFETIME_MS } from "./mesh-store-shared.js";
|
|
13
|
+
import { inviterAgentExtension, parseRoomStateExtension, roomStateExtension, } from "./room-wire-extensions.js";
|
|
14
|
+
export class RoomLifecycle {
|
|
15
|
+
deps;
|
|
16
|
+
constructor(deps) {
|
|
17
|
+
this.deps = deps;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Mints and persists the room owner's own self-signed room:member grant: issuer = bearer = owner, no parent, delegationsRemaining: 0. This is deliberately NOT the parent every later member grant chains through -- a delegations-remaining: 0 parent cannot mint any child at all (mintCapabilityToken refuses a child whose own delegationsRemaining isn't strictly less than its parent's, and there is no value less than 0), so a later join/invite grant is its own independent, parent-less, owner-issued root-level token instead (still satisfying the "chain roots at the path's own owner" obligation, since rootIssuer is just the token's own issuer when it carries no parent). This root grant exists purely so the owner has a token to present for its own room actions, uniformly with every other member, per the design's own "every code path that checks membership does the same thing regardless of who it is checking" reasoning.
|
|
21
|
+
*/
|
|
22
|
+
async mintOwnerRootGrant(roomPath, owner) {
|
|
23
|
+
const { identity, clock, slot } = this.deps.requireIdentity();
|
|
24
|
+
const verdict = await mintCapabilityToken({
|
|
25
|
+
identity,
|
|
26
|
+
clock,
|
|
27
|
+
tokenId: randomId(),
|
|
28
|
+
bearer: deviceIdFromHex(owner),
|
|
29
|
+
capability: "room:member",
|
|
30
|
+
scope: { kind: "room", path: roomPath },
|
|
31
|
+
expires: clock.now() + ROOM_TOKEN_LIFETIME_MS,
|
|
32
|
+
delegationsRemaining: 0,
|
|
33
|
+
});
|
|
34
|
+
if (!verdict.ok) {
|
|
35
|
+
throw new Error(`MeshStore: failed to mint room owner grant for ${roomPath}: ${verdict.reason}`);
|
|
36
|
+
}
|
|
37
|
+
saveRoomToken(slot, roomPath, verdict.token);
|
|
38
|
+
}
|
|
39
|
+
async createRoom(opts) {
|
|
40
|
+
// slugRoomName sanitises an arbitrary caller-supplied name (e.g. from a live create_room tool call, not just an internal cwd basename) into the room-path grammar's [A-Za-z0-9_-]+ charset -- createRoom is the one choke point every room creation goes through, so this is the right place to do it rather than trusting every caller to have pre-slugged, the way the old bare-name id never required at all.
|
|
41
|
+
const slugName = slugRoomName(opts.name);
|
|
42
|
+
const localName = opts.type === "secret" ? `_${slugName}` : slugName;
|
|
43
|
+
const id = ownerNamedRoomPath(opts.owner, localName);
|
|
44
|
+
if (this.deps.rooms.has(id))
|
|
45
|
+
throw new CommsError(`Room ${id} already exists`, "ROOM_EXISTS");
|
|
46
|
+
// Every room creation this store performs is local: opts.owner is always this bridge's own identity (create_room's caller passes ctx.agentId, and one bridge process is one agent is one device), so minting the owner's own root grant here always signs under the identity this store was wired with, never someone else's.
|
|
47
|
+
await this.mintOwnerRootGrant(id, opts.owner);
|
|
48
|
+
const room = {
|
|
49
|
+
id,
|
|
50
|
+
version: 1,
|
|
51
|
+
name: slugName,
|
|
52
|
+
type: opts.type,
|
|
53
|
+
owner: opts.owner,
|
|
54
|
+
createdAt: new Date().toISOString(),
|
|
55
|
+
description: opts.description,
|
|
56
|
+
members: [opts.owner],
|
|
57
|
+
invited: [],
|
|
58
|
+
memberJoins: { [opts.owner]: 1 },
|
|
59
|
+
memberLeaves: {},
|
|
60
|
+
invitedJoins: {},
|
|
61
|
+
invitedLeaves: {},
|
|
62
|
+
federated: opts.federated ?? false,
|
|
63
|
+
};
|
|
64
|
+
this.deps.rooms.set(id, room);
|
|
65
|
+
this.deps.messages.set(id, []);
|
|
66
|
+
await this.deps.deliveryEngine.broadcastPatch({
|
|
67
|
+
type: "room_upsert",
|
|
68
|
+
room,
|
|
69
|
+
});
|
|
70
|
+
return room;
|
|
71
|
+
}
|
|
72
|
+
async getRoom(id) {
|
|
73
|
+
await Promise.resolve();
|
|
74
|
+
return this.deps.rooms.get(id);
|
|
75
|
+
}
|
|
76
|
+
async listRooms(requesterId) {
|
|
77
|
+
await Promise.resolve();
|
|
78
|
+
const result = [];
|
|
79
|
+
for (const room of this.deps.rooms.values()) {
|
|
80
|
+
if (room.type === "secret" && !room.members.includes(requesterId))
|
|
81
|
+
continue;
|
|
82
|
+
result.push(room);
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The remote-join path: roomPath names an owner-named room this store has never seen replicated, so joining it means sending a real wire-level room.join request to the room's own owner and persisting whatever grant comes back, rather than mutating already-known local state. Only ever called for this store's own local agent (one bridge is one agent is one device, per the design's own organising fact) -- there is no wire mechanism by which this node could join a room on a different local agent's behalf.
|
|
88
|
+
*/
|
|
89
|
+
async joinRemoteRoom(roomPath, agentId) {
|
|
90
|
+
if (agentId !== this.deps.getPeerId()) {
|
|
91
|
+
throw new CommsError(`Room ${roomPath} not found`, "ROOM_NOT_FOUND");
|
|
92
|
+
}
|
|
93
|
+
const parsed = parseRoomPath(roomPath);
|
|
94
|
+
if (parsed.kind !== "owner-named") {
|
|
95
|
+
throw new CommsError(`Room ${roomPath} not found`, "ROOM_NOT_FOUND");
|
|
96
|
+
}
|
|
97
|
+
const outcome = await this.deps
|
|
98
|
+
.requireTransport()
|
|
99
|
+
.sendRoomRequest(parsed.owner, { verb: ROOM_MEMBER_CAPABILITY, params: { verb: "room.join" } }, { kind: "room", path: roomPath });
|
|
100
|
+
if (outcome.result !== "ok") {
|
|
101
|
+
throw new CommsError(`Join request for ${roomPath} was refused (${outcome.code})`, "JOIN_REFUSED");
|
|
102
|
+
}
|
|
103
|
+
const parsedOutcome = roomJoinOkSchema.safeParse(outcome);
|
|
104
|
+
if (!parsedOutcome.success) {
|
|
105
|
+
throw new CommsError(`Join response for ${roomPath} was malformed`, "MALFORMED_RESPONSE");
|
|
106
|
+
}
|
|
107
|
+
const { "granted-token": grantedToken, members: memberList } = parsedOutcome.data;
|
|
108
|
+
const { slot } = this.deps.requireIdentity();
|
|
109
|
+
saveRoomToken(slot, roomPath, grantedToken);
|
|
110
|
+
const members = memberList.map((member) => deviceIdToHex(member.device));
|
|
111
|
+
const roomState = parseRoomStateExtension(parsedOutcome.data["room-state"]);
|
|
112
|
+
const room = {
|
|
113
|
+
id: roomPath,
|
|
114
|
+
version: 1,
|
|
115
|
+
name: roomState?.name ?? parsed.localName,
|
|
116
|
+
// Falls back to "public" only against a peer running without the room-state extension (an older version); this joiner is always in `members` by construction regardless, so a stale "public" classification here never hides the room from its own member -- the one place type is read (listRooms' secret-room filter).
|
|
117
|
+
type: roomState?.type ?? "public",
|
|
118
|
+
owner: parsed.owner,
|
|
119
|
+
createdAt: new Date().toISOString(),
|
|
120
|
+
description: roomState?.description ?? "",
|
|
121
|
+
members,
|
|
122
|
+
invited: [],
|
|
123
|
+
memberJoins: Object.fromEntries(members.map((member) => [member, 1])),
|
|
124
|
+
memberLeaves: {},
|
|
125
|
+
invitedJoins: {},
|
|
126
|
+
invitedLeaves: {},
|
|
127
|
+
federated: false,
|
|
128
|
+
};
|
|
129
|
+
this.deps.rooms.set(roomPath, room);
|
|
130
|
+
this.deps.messages.set(roomPath, []);
|
|
131
|
+
return room;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Refreshes this store's own local copy of a named room's membership and room-state via a real room.members request (P3.6): the same information room.join's own response carries at admission time, available on demand for a member whose local copy may have drifted (a kick, an invite, a rename since it joined). Sent to the room's own owner, the authoritative source for that room's real state. Named rooms only, matching joinRemoteRoom's own restriction -- a DM's "membership" is already fully known from the path itself (the sorted pair of exactly two participants), and this codebase has no Room-object representation for a DM to refresh into (DM state lives in this.dms, keyed by message history, not this.rooms). Throws if this store holds no room:member token for roomPath -- refreshing membership presupposes already being a member, the same NOT_A_MEMBER contract sendRoomMessageDirected already uses.
|
|
135
|
+
*/
|
|
136
|
+
async refreshRoomMembers(roomPath) {
|
|
137
|
+
const parsed = parseRoomPath(roomPath);
|
|
138
|
+
if (parsed.kind !== "owner-named") {
|
|
139
|
+
throw new CommsError(`Room ${roomPath} not found`, "ROOM_NOT_FOUND");
|
|
140
|
+
}
|
|
141
|
+
const { slot } = this.deps.requireIdentity();
|
|
142
|
+
const token = loadRoomTokens(slot)[roomPath];
|
|
143
|
+
if (token === undefined) {
|
|
144
|
+
throw new CommsError(`No room:member token for ${roomPath}`, "NOT_MEMBER");
|
|
145
|
+
}
|
|
146
|
+
const outcome = await this.deps
|
|
147
|
+
.requireTransport()
|
|
148
|
+
.sendRoomRequest(parsed.owner, { verb: ROOM_MEMBER_CAPABILITY, params: { verb: "room.members" } }, { kind: "room", path: roomPath }, token);
|
|
149
|
+
if (outcome.result !== "ok") {
|
|
150
|
+
throw new CommsError(`room.members refresh for ${roomPath} failed (${outcome.code})`, "REFRESH_FAILED");
|
|
151
|
+
}
|
|
152
|
+
const parsedOutcome = roomMembersOkSchema.safeParse(outcome);
|
|
153
|
+
if (!parsedOutcome.success) {
|
|
154
|
+
throw new CommsError(`room.members response for ${roomPath} was malformed`, "MALFORMED_RESPONSE");
|
|
155
|
+
}
|
|
156
|
+
const members = parsedOutcome.data.members.map((member) => deviceIdToHex(member.device));
|
|
157
|
+
const roomState = parseRoomStateExtension(parsedOutcome.data["room-state"]);
|
|
158
|
+
const existing = this.deps.rooms.get(roomPath);
|
|
159
|
+
const room = {
|
|
160
|
+
id: roomPath,
|
|
161
|
+
version: (existing?.version ?? 0) + 1,
|
|
162
|
+
name: roomState?.name ?? existing?.name ?? parsed.localName,
|
|
163
|
+
type: roomState?.type ?? existing?.type ?? "public",
|
|
164
|
+
owner: parsed.owner,
|
|
165
|
+
createdAt: existing?.createdAt ?? new Date().toISOString(),
|
|
166
|
+
description: roomState?.description ?? existing?.description ?? "",
|
|
167
|
+
members,
|
|
168
|
+
invited: existing?.invited ?? [],
|
|
169
|
+
memberJoins: Object.fromEntries(members.map((member) => [member, 1])),
|
|
170
|
+
memberLeaves: {},
|
|
171
|
+
invitedJoins: existing?.invitedJoins ?? {},
|
|
172
|
+
invitedLeaves: existing?.invitedLeaves ?? {},
|
|
173
|
+
federated: existing?.federated ?? false,
|
|
174
|
+
};
|
|
175
|
+
this.deps.rooms.set(roomPath, room);
|
|
176
|
+
return room;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The requester's own half of section 6's two-round DM consent flow: sends an ungated room.join scoped to dmRoomPath(this, counterpart) directly to the counterpart, records having initiated it so the counterpart's own reciprocal room.join back auto-approves rather than surfacing as a fresh, unsolicited request, and persists whatever grant comes back. Deliberately outside the CommsStore interface, like connection approval, since it is a wire-mesh-specific concern FileStore has no equivalent for. Safe to call again for the same counterpart later (e.g. after an earlier request expired or was rejected) -- it always sends a fresh request rather than checking for an existing token first.
|
|
180
|
+
*/
|
|
181
|
+
async requestDmAccess(counterpart) {
|
|
182
|
+
const dmPath = dmRoomPath(this.deps.getPeerId(), counterpart);
|
|
183
|
+
this.deps.dmRequestsInitiatedByMe.add(dmPath);
|
|
184
|
+
const outcome = await this.deps
|
|
185
|
+
.requireTransport()
|
|
186
|
+
.sendRoomRequest(counterpart, { verb: ROOM_MEMBER_CAPABILITY, params: { verb: "room.join" } }, { kind: "room", path: dmPath });
|
|
187
|
+
if (outcome.result !== "ok") {
|
|
188
|
+
throw new CommsError(`DM access request to ${counterpart} was refused (${outcome.code})`, "JOIN_REFUSED");
|
|
189
|
+
}
|
|
190
|
+
const parsedOutcome = roomJoinOkSchema.safeParse(outcome);
|
|
191
|
+
if (!parsedOutcome.success) {
|
|
192
|
+
throw new CommsError(`DM access response from ${counterpart} was malformed`, "MALFORMED_RESPONSE");
|
|
193
|
+
}
|
|
194
|
+
const { slot } = this.deps.requireIdentity();
|
|
195
|
+
saveRoomToken(slot, dmPath, parsedOutcome.data["granted-token"]);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Joins a room. For this store's own identity, "already known locally" is not the right gate for skipping real admission: the legacy full-state-sync replicates a room's metadata to every mesh-connected peer the moment it's created, well before that peer has ever been admitted, so a room already present in this.rooms says nothing about whether this store actually holds a valid room:member token for it. The real gate is that token's presence -- absent, this always goes through joinRemoteRoom's real wire-level admission regardless of what this.rooms already knows, so a peer that merely heard about a room never mistakes hearing about it for having joined it. Joining on behalf of a DIFFERENT agentId (this store's own convergence/admin bookkeeping, exercised directly by state-sync-convergence.test.ts) is untouched -- that's a pure local CRDT mutation with no admission concept at all.
|
|
199
|
+
*/
|
|
200
|
+
async joinRoom(roomId, agentId) {
|
|
201
|
+
const peerId = this.deps.getPeerId();
|
|
202
|
+
if (agentId === peerId) {
|
|
203
|
+
const { slot } = this.deps.requireIdentity();
|
|
204
|
+
if (loadRoomTokens(slot)[roomId] === undefined) {
|
|
205
|
+
return this.joinRemoteRoom(roomId, agentId);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const room = this.deps.rooms.get(roomId);
|
|
209
|
+
if (!room)
|
|
210
|
+
return this.joinRemoteRoom(roomId, agentId);
|
|
211
|
+
const alreadyMember = room.members.includes(agentId);
|
|
212
|
+
if (!alreadyMember && room.type !== "public") {
|
|
213
|
+
if (!room.invited.includes(agentId) && room.owner !== agentId) {
|
|
214
|
+
throw new CommsError(`Not invited to room ${roomId}`, "NOT_INVITED");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
this.deps.deliveryEngine.bump(room);
|
|
218
|
+
this.deps.deliveryEngine.recordMemberOp(room, "member", "join", agentId);
|
|
219
|
+
if (alreadyMember || room.type !== "public") {
|
|
220
|
+
// Consuming an invitation (or re-joining) retires the invited entry.
|
|
221
|
+
this.deps.deliveryEngine.recordMemberOp(room, "invited", "leave", agentId);
|
|
222
|
+
}
|
|
223
|
+
this.deps.deliveryEngine.refreshMembership(room);
|
|
224
|
+
this.deps.rooms.set(roomId, room);
|
|
225
|
+
const agent = this.deps.agents.get(agentId);
|
|
226
|
+
if (agent && !agent.subscribedRooms.includes(roomId)) {
|
|
227
|
+
agent.subscribedRooms.push(roomId);
|
|
228
|
+
this.deps.deliveryEngine.bump(agent);
|
|
229
|
+
this.deps.agents.set(agentId, agent);
|
|
230
|
+
await this.deps.deliveryEngine.broadcastPatch({
|
|
231
|
+
type: "agent_upsert",
|
|
232
|
+
agent,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
await this.deps.deliveryEngine.broadcastPatch({
|
|
236
|
+
type: "room_upsert",
|
|
237
|
+
room,
|
|
238
|
+
});
|
|
239
|
+
// Send current member list to the joining agent
|
|
240
|
+
const members = [];
|
|
241
|
+
for (const memberId of room.members) {
|
|
242
|
+
const memberAgent = this.deps.agents.get(memberId);
|
|
243
|
+
if (memberAgent) {
|
|
244
|
+
members.push({
|
|
245
|
+
id: memberAgent.id,
|
|
246
|
+
name: memberAgent.name,
|
|
247
|
+
status: memberAgent.status,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
await this.deps.deliveryEngine.deliverLocallyAndBroadcast(agentId, {
|
|
252
|
+
type: "room_members",
|
|
253
|
+
room: roomId,
|
|
254
|
+
members,
|
|
255
|
+
});
|
|
256
|
+
// Notify existing members of the join
|
|
257
|
+
await this.deps.deliveryEngine.deliverToRoom(roomId, {
|
|
258
|
+
type: "member_joined",
|
|
259
|
+
room: roomId,
|
|
260
|
+
agent: agentId,
|
|
261
|
+
}, agentId);
|
|
262
|
+
// Notify federated links if the room is federated
|
|
263
|
+
if (room.federated) {
|
|
264
|
+
const agentName = agent?.name ?? agentId;
|
|
265
|
+
await this.deps.federation.broadcastRoomJoin(roomId, agentId, agentName);
|
|
266
|
+
}
|
|
267
|
+
return room;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Leaves a room. For this store's own identity leaving a room it does not itself own, the local Room object is only ever this store's own static snapshot from when it joined or was invited (P3.6/P3.8) -- mutating it directly, as the legacy branch below does, would tell nobody but this store itself. The real effect needs a wire round trip to the room's own owner instead, so this always defers to leaveRemoteRoom in that case. Leaving on behalf of a DIFFERENT agentId, or the owner leaving their own room (this store's own authoritative copy), is untouched -- that is the legacy CRDT mutation state-sync-convergence.test.ts exercises directly.
|
|
271
|
+
*/
|
|
272
|
+
async leaveRoom(roomId, agentId) {
|
|
273
|
+
const room = this.deps.rooms.get(roomId);
|
|
274
|
+
if (!room)
|
|
275
|
+
throw new CommsError(`Room ${roomId} not found`, "ROOM_NOT_FOUND");
|
|
276
|
+
const peerId = this.deps.getPeerId();
|
|
277
|
+
if (agentId === peerId && room.owner !== peerId) {
|
|
278
|
+
return this.leaveRemoteRoom(roomId, room.owner);
|
|
279
|
+
}
|
|
280
|
+
this.deps.deliveryEngine.bump(room);
|
|
281
|
+
this.deps.deliveryEngine.recordMemberOp(room, "member", "leave", agentId);
|
|
282
|
+
this.deps.deliveryEngine.refreshMembership(room);
|
|
283
|
+
this.deps.rooms.set(roomId, room);
|
|
284
|
+
const agent = this.deps.agents.get(agentId);
|
|
285
|
+
if (agent) {
|
|
286
|
+
agent.subscribedRooms = agent.subscribedRooms.filter((id) => id !== roomId);
|
|
287
|
+
this.deps.agents.set(agentId, agent);
|
|
288
|
+
await this.deps.deliveryEngine.broadcastPatch({
|
|
289
|
+
type: "agent_upsert",
|
|
290
|
+
agent,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
await this.deps.deliveryEngine.broadcastPatch({
|
|
294
|
+
type: "room_upsert",
|
|
295
|
+
room,
|
|
296
|
+
});
|
|
297
|
+
await this.deps.deliveryEngine.deliverToRoom(roomId, {
|
|
298
|
+
type: "member_left",
|
|
299
|
+
room: roomId,
|
|
300
|
+
agent: agentId,
|
|
301
|
+
});
|
|
302
|
+
// Notify federated links if the room is federated
|
|
303
|
+
if (room.federated) {
|
|
304
|
+
await this.deps.federation.broadcastRoomLeave(roomId, agentId);
|
|
305
|
+
}
|
|
306
|
+
if (room.members.length === 0 && room.owner === agentId) {
|
|
307
|
+
await this.destroyRoom(roomId, agentId);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* The remote-leave path: roomPath names a room this store is a member of but does not own, so leaving for real means telling the owner over a real, wire-authenticated room.leave rather than mutating a local Room record nobody else reads. Also declineInvite's own mechanism (P3.8): the moment a target receives an invite (handleRoomInvite), it already holds a real, persisted room:member token exactly as if it had joined -- declining is simply leaving before ever really participating, and the owner's own receiving side (handleRoomLeave) tells the two cases apart by checking its own membership/invited lists, not by a separate verb. reason rides room.leave's own open extension tail so the owner can still surface a real decline reason without a second wire shape.
|
|
312
|
+
*/
|
|
313
|
+
async leaveRemoteRoom(roomPath, ownerId, reason) {
|
|
314
|
+
const { slot } = this.deps.requireIdentity();
|
|
315
|
+
const token = loadRoomTokens(slot)[roomPath];
|
|
316
|
+
if (token === undefined) {
|
|
317
|
+
throw new CommsError(`No room:member token for ${roomPath}`, "NOT_MEMBER");
|
|
318
|
+
}
|
|
319
|
+
const params = {
|
|
320
|
+
verb: "room.leave",
|
|
321
|
+
...(reason !== undefined ? { reason } : {}),
|
|
322
|
+
};
|
|
323
|
+
const outcome = await this.deps
|
|
324
|
+
.requireTransport()
|
|
325
|
+
.sendRoomRequest(ownerId, { verb: ROOM_MEMBER_CAPABILITY, params }, { kind: "room", path: roomPath }, token);
|
|
326
|
+
if (outcome.result !== "ok") {
|
|
327
|
+
throw new CommsError(`Leaving ${roomPath} failed (${outcome.code})`, "LEAVE_FAILED");
|
|
328
|
+
}
|
|
329
|
+
deleteRoomToken(slot, roomPath);
|
|
330
|
+
this.deps.rooms.delete(roomPath);
|
|
331
|
+
this.deps.messages.delete(roomPath);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Invites targetId to roomId, over a real, wire-authenticated room.invite (P3.8): mints a fresh room:member grant for the target, records its own token-id the same way admitRoomJoin does (kickFromRoom can revoke an invited member's own grant exactly as it can a joined one), and pushes the grant to the target directly in the invite request itself -- room.invite is deliberately ungated (the room's own owner needs no capability to invite, per core/room's design), so the target's own verification of the embedded token is what proves this invite is genuine, not anything about the connection it arrived on. Retains the local CRDT invited-list bookkeeping (still this store's own record of who it has invited) but no longer broadcasts it: the target learns of the invite from the real request, not a mesh-wide patch.
|
|
335
|
+
*/
|
|
336
|
+
async inviteToRoom(roomId, targetId, inviterId) {
|
|
337
|
+
const room = this.deps.rooms.get(roomId);
|
|
338
|
+
if (!room)
|
|
339
|
+
throw new CommsError(`Room ${roomId} not found`, "ROOM_NOT_FOUND");
|
|
340
|
+
if (room.owner !== inviterId)
|
|
341
|
+
throw new CommsError("Only the room owner can invite", "NOT_OWNER");
|
|
342
|
+
this.deps.deliveryEngine.bump(room);
|
|
343
|
+
if (!room.invited.includes(targetId) && !room.members.includes(targetId)) {
|
|
344
|
+
this.deps.deliveryEngine.recordMemberOp(room, "invited", "join", targetId);
|
|
345
|
+
}
|
|
346
|
+
this.deps.deliveryEngine.refreshMembership(room);
|
|
347
|
+
this.deps.rooms.set(roomId, room);
|
|
348
|
+
const { identity, clock, slot } = this.deps.requireIdentity();
|
|
349
|
+
const tokenId = randomId();
|
|
350
|
+
const verdict = await mintCapabilityToken({
|
|
351
|
+
identity,
|
|
352
|
+
clock,
|
|
353
|
+
tokenId,
|
|
354
|
+
bearer: deviceIdFromHex(targetId),
|
|
355
|
+
capability: ROOM_MEMBER_CAPABILITY,
|
|
356
|
+
scope: { kind: "room", path: roomId },
|
|
357
|
+
expires: clock.now() + ROOM_TOKEN_LIFETIME_MS,
|
|
358
|
+
delegationsRemaining: 0,
|
|
359
|
+
});
|
|
360
|
+
if (!verdict.ok) {
|
|
361
|
+
throw new CommsError(`Failed to mint an invite grant for ${targetId}`, "MINT_FAILED");
|
|
362
|
+
}
|
|
363
|
+
saveIssuedRoomGrant(slot, roomId, targetId, tokenId);
|
|
364
|
+
const inviter = this.deps.agents.get(inviterId);
|
|
365
|
+
const params = {
|
|
366
|
+
verb: "room.invite",
|
|
367
|
+
invitee: deviceIdFromHex(targetId),
|
|
368
|
+
token: verdict.token,
|
|
369
|
+
...roomStateExtension(room),
|
|
370
|
+
...(inviter !== undefined ? inviterAgentExtension(inviter) : {}),
|
|
371
|
+
};
|
|
372
|
+
const outcome = await this.deps
|
|
373
|
+
.requireTransport()
|
|
374
|
+
.sendRoomRequest(targetId, { verb: ROOM_MEMBER_CAPABILITY, params }, { kind: "room", path: roomId });
|
|
375
|
+
if (outcome.result !== "ok") {
|
|
376
|
+
throw new CommsError(`Invite to ${targetId} for ${roomId} failed (${outcome.code})`, "INVITE_FAILED");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Declines a pending invite, over the same real room.leave request leaveRemoteRoom already sends for an actual leave (P3.8): the moment this store received the invite (handleRoomInvite), it already holds a real, persisted room:member token, so declining before ever really participating is simply leaving early -- the owner's own receiving side (handleRoomLeave) tells the two cases apart from its own membership/invited lists, not from a separate verb. Uses parseRoomPath rather than any locally cached Room record to find the owner to leave, since handleRoomInvite never constructs one -- there is nothing here to read a room.owner field off in the first place.
|
|
381
|
+
*/
|
|
382
|
+
async declineInvite(roomId, agentId, reason) {
|
|
383
|
+
// Every real caller declines on its own behalf (tool.ts always passes ctx.agentId, which for a real bridge process is always this.peerId, per the design's own "one bridge is one agent is one device" organising fact) -- there is no remote-decline-on-someone-else's-behalf mechanism, so a mismatch here means the caller itself is confused about whose invite it is declining.
|
|
384
|
+
if (agentId !== this.deps.getPeerId()) {
|
|
385
|
+
throw new CommsError(`Cannot decline an invite on behalf of ${agentId}`, "NOT_SELF");
|
|
386
|
+
}
|
|
387
|
+
const parsed = parseRoomPath(roomId);
|
|
388
|
+
if (parsed.kind !== "owner-named") {
|
|
389
|
+
throw new CommsError(`Room ${roomId} not found`, "ROOM_NOT_FOUND");
|
|
390
|
+
}
|
|
391
|
+
return this.leaveRemoteRoom(roomId, parsed.owner, reason);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Revokes memberId's own room:member grant for roomId for real, if this identity ever recorded issuing one: mints a revocation-entry for its token-id, records it in this store's own RevocationView immediately, announces it to every connected peer, and forgets the issued-grant record (a later re-admission mints and records a genuinely fresh one rather than leaving a stale entry alongside it). Silently does nothing when no issued-grant record exists (a grant predating this bookkeeping, or a member who was never actually admitted a token at all) -- shared by kickFromRoom (owner-initiated) and RoomProtocol's own handleRoomLeave (member-initiated, including a decline), which calls this via a deferred closure since RoomProtocol is constructed before RoomLifecycle exists.
|
|
395
|
+
*/
|
|
396
|
+
async revokeMemberGrant(roomId, memberId) {
|
|
397
|
+
const { identity, clock, slot, revocation } = this.deps.requireIdentity();
|
|
398
|
+
const tokenId = loadIssuedRoomGrant(slot, roomId, memberId);
|
|
399
|
+
if (tokenId === undefined)
|
|
400
|
+
return;
|
|
401
|
+
const entry = await mintRevocationEntry({
|
|
402
|
+
identity,
|
|
403
|
+
tokenId,
|
|
404
|
+
revokedAt: clock.now(),
|
|
405
|
+
});
|
|
406
|
+
await revocation.record(entry, { identity });
|
|
407
|
+
await this.deps.requireTransport().broadcastRevocation([entry]);
|
|
408
|
+
deleteIssuedRoomGrant(slot, roomId, memberId);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Kicks targetId from roomId. Beyond the legacy CRDT membership removal (retired in P3.8 along with every other non-message broadcastPatch caller), this revokes the member's own room:member grant for real: mints a revocation-entry for the token-id this identity recorded when it admitted them (admitRoomJoin's own saveIssuedRoomGrant), records it in this store's own RevocationView immediately (so this identity's own future verifications see the kick without waiting on its own gossip), and announces it to every connected peer so each one's independent verification of the target's token -- not just this room's owner -- also starts failing as "revoked" from here on. Silently skips the revocation step (kick still happens; only the token-side enforcement doesn't) when no issued-grant record exists for this member, e.g. a grant predating this bookkeeping.
|
|
412
|
+
*/
|
|
413
|
+
async kickFromRoom(roomId, targetId, kickerId) {
|
|
414
|
+
const room = this.deps.rooms.get(roomId);
|
|
415
|
+
if (!room)
|
|
416
|
+
throw new CommsError(`Room ${roomId} not found`, "ROOM_NOT_FOUND");
|
|
417
|
+
if (room.owner !== kickerId)
|
|
418
|
+
throw new CommsError("Only the room owner can kick", "NOT_OWNER");
|
|
419
|
+
await this.revokeMemberGrant(roomId, targetId);
|
|
420
|
+
this.deps.deliveryEngine.bump(room);
|
|
421
|
+
this.deps.deliveryEngine.recordMemberOp(room, "member", "leave", targetId);
|
|
422
|
+
this.deps.deliveryEngine.recordMemberOp(room, "invited", "leave", targetId);
|
|
423
|
+
this.deps.deliveryEngine.refreshMembership(room);
|
|
424
|
+
this.deps.rooms.set(roomId, room);
|
|
425
|
+
await this.deps.deliveryEngine.broadcastPatch({
|
|
426
|
+
type: "room_upsert",
|
|
427
|
+
room,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Destroys roomId, revoking every member's own room:member grant for real first (P3.8) -- the same revocation machinery kickFromRoom already uses, since destroying a room out from under its members is exactly as much a membership revocation as kicking them individually would be, just for all of them at once. Room-existence notification (agent_upsert/room_delete) stays on the legacy broadcastPatch for now: replacing it needs the same broader informational-events redesign the rest of P3.8 already tracks as separate, larger work, not something this specific fix should improvise alone.
|
|
432
|
+
*/
|
|
433
|
+
async destroyRoom(roomId, agentId) {
|
|
434
|
+
const room = this.deps.rooms.get(roomId);
|
|
435
|
+
if (!room)
|
|
436
|
+
throw new CommsError(`Room ${roomId} not found`, "ROOM_NOT_FOUND");
|
|
437
|
+
if (room.owner !== agentId)
|
|
438
|
+
throw new CommsError("Only the room owner can destroy", "NOT_OWNER");
|
|
439
|
+
for (const memberId of room.members) {
|
|
440
|
+
await this.revokeMemberGrant(roomId, memberId);
|
|
441
|
+
const member = this.deps.agents.get(memberId);
|
|
442
|
+
if (member) {
|
|
443
|
+
member.subscribedRooms = member.subscribedRooms.filter((id) => id !== roomId);
|
|
444
|
+
this.deps.agents.set(memberId, member);
|
|
445
|
+
await this.deps.deliveryEngine.broadcastPatch({
|
|
446
|
+
type: "agent_upsert",
|
|
447
|
+
agent: member,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
this.deps.rooms.delete(roomId);
|
|
452
|
+
this.deps.messages.delete(roomId);
|
|
453
|
+
await this.deps.deliveryEngine.broadcastPatch({
|
|
454
|
+
type: "room_delete",
|
|
455
|
+
roomId,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
//# sourceMappingURL=room-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room-lifecycle.js","sourceRoot":"","sources":["../../src/core/room-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoomMessaging — the sending side of room messages and DMs: sendRoomMessage/sendDm fan out one directed, wire-authenticated room.send per member via RoomProtocol's own retry-queueing sendRoomRequestToMember (P3.5), and readRoomMessages reads back a room's local history. Split out of mesh-store.ts to reduce it under the repo's max-lines cap.
|
|
3
|
+
*/
|
|
4
|
+
import type { MeshStoreIdentity } from "./mesh-store-shared.js";
|
|
5
|
+
import type { RoomProtocol } from "./room-protocol.js";
|
|
6
|
+
import type { FederationManager } from "./federation.js";
|
|
7
|
+
import type { AgentIdentity, DmMessage, Room, RoomMessage, StreamingBehavior } from "./types.js";
|
|
8
|
+
/** The state and collaborators RoomMessaging needs from MeshStore. rooms/messages/dms/agents are direct references into MeshStore's own fields; roomProtocol and federation are the already-constructed instances (construction order: ... -> roomProtocol -> roomMessaging -> ...), narrowed to what sending a message or DM ever needs. */
|
|
9
|
+
export interface RoomMessagingDeps {
|
|
10
|
+
rooms: Map<string, Room>;
|
|
11
|
+
messages: Map<string, RoomMessage[]>;
|
|
12
|
+
dms: Map<string, DmMessage[]>;
|
|
13
|
+
agents: Map<string, AgentIdentity>;
|
|
14
|
+
requireIdentity: () => MeshStoreIdentity;
|
|
15
|
+
roomProtocol: Pick<RoomProtocol, "sendRoomRequestToMember">;
|
|
16
|
+
federation: Pick<FederationManager, "forwardRoomMessage">;
|
|
17
|
+
}
|
|
18
|
+
export declare class RoomMessaging {
|
|
19
|
+
private readonly deps;
|
|
20
|
+
constructor(deps: RoomMessagingDeps);
|
|
21
|
+
/**
|
|
22
|
+
* Sends a room message via a real, wire-authenticated room.send fan-out (P3.5): one directed request per member, each carrying this sender's own persisted room:member token, rather than the legacy broadcastPatch's full-state replication. A member unreachable right now is queued for retry (see sendRoomRequestToMember/flushPendingRoomRequests) instead of blocking or failing the whole send -- delivery to any one recipient is independent of every other.
|
|
23
|
+
*/
|
|
24
|
+
sendRoomMessage(roomId: string, from: string, content: string, replyTo?: string, streamingBehavior?: StreamingBehavior): Promise<RoomMessage>;
|
|
25
|
+
readRoomMessages(roomId: string, since?: string): Promise<RoomMessage[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Sends a DM via the same wire-authenticated room.send fan-out sendRoomMessage uses (P3.5): a DM is just a dm-shaped room path with exactly one other member, so it rides the identical mechanism rather than a separate one. Self-DM is the one exception -- a purely local scratchpad note that never leaves the process, so it needs no token and no wire round trip at all.
|
|
28
|
+
*/
|
|
29
|
+
sendDm(from: string, to: string, content: string, streamingBehavior?: StreamingBehavior): Promise<DmMessage>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=room-messaging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room-messaging.d.ts","sourceRoot":"","sources":["../../src/core/room-messaging.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,IAAI,EACJ,WAAW,EACX,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,6UAA6U;AAC7U,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACrC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnC,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,yBAAyB,CAAC,CAAC;IAC5D,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;CAC3D;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,iBAAiB;IAEpD;;OAEG;IACG,eAAe,CACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAChB,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,OAAO,CAAC,WAAW,CAAC;IA8DjB,gBAAgB,CACpB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,EAAE,CAAC;IAOzB;;OAEG;IACG,MAAM,CACV,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,OAAO,CAAC,SAAS,CAAC;CAoDtB"}
|