@agentvault/claude-bridge 0.3.0 → 0.3.2
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/dist/bridge.d.ts +17 -0
- package/dist/index.js +83 -0
- package/package.json +1 -1
package/dist/bridge.d.ts
CHANGED
|
@@ -9,13 +9,30 @@ export interface RoomMessage {
|
|
|
9
9
|
export interface MessageMeta {
|
|
10
10
|
roomId?: string;
|
|
11
11
|
}
|
|
12
|
+
/** #392 room hush (advisory) — emitted by SecureChannel from the backend's
|
|
13
|
+
* `room_hushed` event. ``hushedUntil`` is an ISO string, or null when cleared. */
|
|
14
|
+
export interface RoomHushed {
|
|
15
|
+
roomId: string;
|
|
16
|
+
hushedUntil: string | null;
|
|
17
|
+
}
|
|
12
18
|
export interface RoomChannel {
|
|
13
19
|
on(ev: "room_message", cb: (e: RoomMessage) => void): unknown;
|
|
14
20
|
on(ev: "message", cb: (text: string, metadata: MessageMeta) => void): unknown;
|
|
21
|
+
on(ev: "room_hushed", cb: (e: RoomHushed) => void): unknown;
|
|
15
22
|
on(ev: "error", cb: (err: unknown) => void): unknown;
|
|
16
23
|
sendToRoom(roomId: string, text: string): Promise<void>;
|
|
17
24
|
send(text: string): Promise<void>;
|
|
18
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* #392 cooperative quiet: tracks per-room hush windows so the native agent holds
|
|
28
|
+
* (does not room_say) while the owner has hushed the room. Advisory — the agent
|
|
29
|
+
* cooperates; the hard backstops are owner mute / remove (enforced server-side).
|
|
30
|
+
*/
|
|
31
|
+
export declare class RoomHushState {
|
|
32
|
+
private until;
|
|
33
|
+
set(roomId: string, hushedUntilIso: string | null): void;
|
|
34
|
+
isHushed(roomId: string, now?: number): boolean;
|
|
35
|
+
}
|
|
19
36
|
export interface RoomSession {
|
|
20
37
|
/** `reply` is the immutable reply sink captured for THIS message (see
|
|
21
38
|
* ActiveTarget.snapshotReply) — the session invokes it when Claude answers. */
|
package/dist/index.js
CHANGED
|
@@ -66743,6 +66743,19 @@ var init_channel = __esm({
|
|
|
66743
66743
|
}
|
|
66744
66744
|
});
|
|
66745
66745
|
}
|
|
66746
|
+
if (data.event === "room_hushed") {
|
|
66747
|
+
this.emit("room_hushed", {
|
|
66748
|
+
roomId: data.data?.room_id,
|
|
66749
|
+
hushedUntil: data.data?.hushed_until ?? null
|
|
66750
|
+
});
|
|
66751
|
+
}
|
|
66752
|
+
if (data.event === "room_advisory") {
|
|
66753
|
+
this.emit("room_advisory", {
|
|
66754
|
+
roomId: data.data?.room_id,
|
|
66755
|
+
kind: data.data?.kind,
|
|
66756
|
+
message: data.data?.message
|
|
66757
|
+
});
|
|
66758
|
+
}
|
|
66746
66759
|
if (data.event === "policy_blocked") {
|
|
66747
66760
|
this.emit("policy_blocked", data.data);
|
|
66748
66761
|
}
|
|
@@ -68497,6 +68510,21 @@ ${messageText}`;
|
|
|
68497
68510
|
return;
|
|
68498
68511
|
}
|
|
68499
68512
|
}
|
|
68513
|
+
if (welcomeRoomId && this._persisted) {
|
|
68514
|
+
await this._registerRoomFromWelcome(
|
|
68515
|
+
welcomeRoomId,
|
|
68516
|
+
groupId,
|
|
68517
|
+
mgr,
|
|
68518
|
+
data.room_name
|
|
68519
|
+
);
|
|
68520
|
+
this._pendingMlsKpBundle = void 0;
|
|
68521
|
+
await releaseA2ASyncLock(this.config.dataDir, `room-kp-${groupId}`).catch(() => {
|
|
68522
|
+
});
|
|
68523
|
+
console.log(
|
|
68524
|
+
`[SecureChannel] Registered room ${welcomeRoomId.slice(0, 8)} from unmatched Welcome (epoch=${mgr.epoch})`
|
|
68525
|
+
);
|
|
68526
|
+
return;
|
|
68527
|
+
}
|
|
68500
68528
|
const a2aChannelId = data.a2a_channel_id;
|
|
68501
68529
|
for (const [channelId, entry] of Object.entries(this._persisted?.a2aChannels ?? {})) {
|
|
68502
68530
|
if (entry.mlsGroupId === groupId || channelId === a2aChannelId) {
|
|
@@ -68553,6 +68581,33 @@ ${messageText}`;
|
|
|
68553
68581
|
throw err;
|
|
68554
68582
|
}
|
|
68555
68583
|
}
|
|
68584
|
+
/**
|
|
68585
|
+
* Self-bootstrap a room entry + MLS group mapping from a Welcome whose room is
|
|
68586
|
+
* not yet persisted. Happens when we were added to an EXISTING room and the
|
|
68587
|
+
* `room_joined` that registers the room raced behind (or was lost relative to)
|
|
68588
|
+
* the Welcome. Without a room entry the Welcome room-match loop can't match, and
|
|
68589
|
+
* every subsequent room message is dropped with "No room found for MLS group".
|
|
68590
|
+
* Mirrors the A2A self-bootstrap fallback. The backend now also sends the new
|
|
68591
|
+
* device its own `room_joined` (PR #412); this is defense-in-depth for the race
|
|
68592
|
+
* where that delivery is missed. A later `room_joined` enriches name/members.
|
|
68593
|
+
*/
|
|
68594
|
+
async _registerRoomFromWelcome(roomId, groupId, mgr, roomName) {
|
|
68595
|
+
if (!this._persisted) return;
|
|
68596
|
+
if (!this._persisted.rooms) this._persisted.rooms = {};
|
|
68597
|
+
const existing = this._persisted.rooms[roomId];
|
|
68598
|
+
this._persisted.rooms[roomId] = {
|
|
68599
|
+
roomId,
|
|
68600
|
+
name: existing?.name ?? (roomName ?? ""),
|
|
68601
|
+
conversationIds: existing?.conversationIds ?? [],
|
|
68602
|
+
members: existing?.members ?? [],
|
|
68603
|
+
mlsGroupId: groupId,
|
|
68604
|
+
lastMlsMessageTs: existing?.lastMlsMessageTs
|
|
68605
|
+
};
|
|
68606
|
+
this._mlsGroups.set(roomId, mgr);
|
|
68607
|
+
await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mgr.exportState()));
|
|
68608
|
+
await this._persistState();
|
|
68609
|
+
await this._applyBufferedMlsCommits(groupId, mgr);
|
|
68610
|
+
}
|
|
68556
68611
|
/**
|
|
68557
68612
|
* Pull pending MLS messages from the delivery queue and process them.
|
|
68558
68613
|
* Called on WS connect, on mls_delivery ping, and every 30s heartbeat.
|
|
@@ -131587,6 +131642,23 @@ var PersistentClaudeSession = class {
|
|
|
131587
131642
|
};
|
|
131588
131643
|
|
|
131589
131644
|
// src/bridge.ts
|
|
131645
|
+
var RoomHushState = class {
|
|
131646
|
+
until = /* @__PURE__ */ new Map();
|
|
131647
|
+
set(roomId, hushedUntilIso) {
|
|
131648
|
+
if (!roomId) return;
|
|
131649
|
+
if (!hushedUntilIso) {
|
|
131650
|
+
this.until.delete(roomId);
|
|
131651
|
+
return;
|
|
131652
|
+
}
|
|
131653
|
+
const t7 = new Date(hushedUntilIso).getTime();
|
|
131654
|
+
if (Number.isNaN(t7)) return;
|
|
131655
|
+
this.until.set(roomId, t7);
|
|
131656
|
+
}
|
|
131657
|
+
isHushed(roomId, now = Date.now()) {
|
|
131658
|
+
const t7 = this.until.get(roomId);
|
|
131659
|
+
return t7 !== void 0 && now < t7;
|
|
131660
|
+
}
|
|
131661
|
+
};
|
|
131590
131662
|
var ActiveTarget = class {
|
|
131591
131663
|
target = null;
|
|
131592
131664
|
setRoom(roomId) {
|
|
@@ -131654,8 +131726,19 @@ function wireBridge(channel, session, target, opts = {}) {
|
|
|
131654
131726
|
const msg = err instanceof Error ? err.message : String(err);
|
|
131655
131727
|
log(`channel error (auto-reconnecting): ${msg}`);
|
|
131656
131728
|
});
|
|
131729
|
+
const hush = new RoomHushState();
|
|
131730
|
+
channel.on("room_hushed", (e7) => {
|
|
131731
|
+
hush.set(e7.roomId, e7.hushedUntil);
|
|
131732
|
+
log(
|
|
131733
|
+
e7.hushedUntil ? `room ${e7.roomId.slice(0, 8)} hushed until ${e7.hushedUntil} \u2014 holding` : `room ${e7.roomId.slice(0, 8)} hush cleared`
|
|
131734
|
+
);
|
|
131735
|
+
});
|
|
131657
131736
|
channel.on("room_message", (e7) => {
|
|
131658
131737
|
if (opts.roomFilter && e7.roomId !== opts.roomFilter) return;
|
|
131738
|
+
if (hush.isHushed(e7.roomId)) {
|
|
131739
|
+
log(`inbound from ${e7.senderName} in ${e7.roomId.slice(0, 8)} \u2014 room hushed, holding (not replying)`);
|
|
131740
|
+
return;
|
|
131741
|
+
}
|
|
131659
131742
|
log(`inbound from ${e7.senderName} in ${e7.roomId.slice(0, 8)}`);
|
|
131660
131743
|
target.setRoom(e7.roomId);
|
|
131661
131744
|
session.push(`[${e7.senderName}]: ${e7.plaintext}`, target.snapshotReply(channel, log));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgentVault Claude Bridge — daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
|
|
6
6
|
"main": "dist/index.js",
|