@agentvault/claude-bridge 0.8.5 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +91 -55
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -64612,11 +64612,11 @@ var init_mls_kp_pool = __esm2({
64612
64612
  }
64613
64613
  });
64614
64614
  function ownIdentity() {
64615
- const v22 = true ? "0.23.32" : FALLBACK;
64615
+ const v22 = true ? "0.23.33" : FALLBACK;
64616
64616
  return `${PACKAGE}@${v22}`;
64617
64617
  }
64618
64618
  function buildSha() {
64619
- const raw = true ? "564f8b1-dirty" : "";
64619
+ const raw = true ? "7b91f2f-dirty" : "";
64620
64620
  const cleaned = (raw ?? "").trim();
64621
64621
  return !cleaned || cleaned === "unknown" ? null : cleaned;
64622
64622
  }
@@ -65531,6 +65531,7 @@ var OUTBOUND_DEDUPE_MAX;
65531
65531
  var OUTBOUND_DEDUPE_TYPES;
65532
65532
  var RETRY_CANDIDATE_TTL_MS;
65533
65533
  var RETRY_CANDIDATE_MAX;
65534
+ var ROOM_FULFIL_COOLDOWN_MS;
65534
65535
  var SecureChannel;
65535
65536
  var init_channel = __esm2({
65536
65537
  async "src/channel.ts"() {
@@ -65576,6 +65577,7 @@ var init_channel = __esm2({
65576
65577
  OUTBOUND_DEDUPE_TYPES = /* @__PURE__ */ new Set(["text", "status_alert", "artifact", "attachment"]);
65577
65578
  RETRY_CANDIDATE_TTL_MS = 6e4;
65578
65579
  RETRY_CANDIDATE_MAX = 32;
65580
+ ROOM_FULFIL_COOLDOWN_MS = 6e4;
65579
65581
  SecureChannel = class _SecureChannel extends EventEmitter {
65580
65582
  constructor(config22) {
65581
65583
  super();
@@ -65645,6 +65647,8 @@ var init_channel = __esm2({
65645
65647
  _drDeliveryPulling = false;
65646
65648
  /** MLS group managers per room/conversation (roomId or conv:conversationId or 1to1-group:groupId -> MLSGroupManager) */
65647
65649
  _mlsGroups = /* @__PURE__ */ new Map();
65650
+ /** `${mlsGroupId}:${deviceId}` -> last re-admit ms (#1171). */
65651
+ _roomFulfilCooldown = /* @__PURE__ */ new Map();
65648
65652
  /** Maps conversationId -> conversation_group_id for shared 1:1 MLS groups */
65649
65653
  _sessionGroupIds = /* @__PURE__ */ new Map();
65650
65654
  /** Consecutive MLS decrypt failure count per group key for epoch recovery. */
@@ -66810,7 +66814,7 @@ var init_channel = __esm2({
66810
66814
  */
66811
66815
  sendActivitySpan(spanData) {
66812
66816
  if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
66813
- const pluginVersion = true ? "0.23.32" : "0.0.0-dev";
66817
+ const pluginVersion = true ? "0.23.33" : "0.0.0-dev";
66814
66818
  const agentName = this.config.agentName ?? "Agent";
66815
66819
  const resource = {
66816
66820
  "service.name": "agentvault-agent",
@@ -68748,7 +68752,7 @@ var init_channel = __esm2({
68748
68752
  agentVersion: this.config.agentVersion ?? "0.0.0",
68749
68753
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
68750
68754
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
68751
- pluginVersion: true ? "0.23.32" : "0.0.0-dev"
68755
+ pluginVersion: true ? "0.23.33" : "0.0.0-dev"
68752
68756
  });
68753
68757
  this._telemetryReporter.startAutoFlush(3e4);
68754
68758
  }
@@ -69072,7 +69076,7 @@ var init_channel = __esm2({
69072
69076
  agentVersion: this.config.agentVersion ?? "0.0.0",
69073
69077
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
69074
69078
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
69075
- pluginVersion: true ? "0.23.32" : "0.0.0-dev"
69079
+ pluginVersion: true ? "0.23.33" : "0.0.0-dev"
69076
69080
  });
69077
69081
  this._telemetryReporter.startAutoFlush(3e4);
69078
69082
  }
@@ -70761,13 +70765,7 @@ ${baseText}`;
70761
70765
  console.warn(`[SecureChannel] MLS epoch recovery for ${groupKey} (${mlsGroupId.slice(0, 8)})`);
70762
70766
  this._mlsGroups.delete(groupKey);
70763
70767
  this._mlsDecryptFailCounts.delete(groupKey);
70764
- try {
70765
- const { unlink: unlink2 } = await import("fs/promises");
70766
- const path2 = `${this.config.dataDir}/.mls-state/${mlsGroupId}.json`;
70767
- await unlink2(path2).catch(() => {
70768
- });
70769
- } catch {
70770
- }
70768
+ await deleteMlsState(this.config.dataDir, mlsGroupId);
70771
70769
  try {
70772
70770
  const mgr = new MLSGroupManager();
70773
70771
  const identity = new TextEncoder().encode(this._deviceId);
@@ -70842,19 +70840,26 @@ ${baseText}`;
70842
70840
  return 0;
70843
70841
  }
70844
70842
  const snapshot = mlsGroup.exportState();
70845
- const { commit, welcome } = await mlsGroup.addMembers(targets.map((t22) => t22.keyPackage));
70846
- const commitSent = await this._sendFrameConfirmed(
70847
- {
70848
- event: "mls_commit",
70849
- data: {
70850
- group_id: mlsGroupId,
70851
- epoch: Number(mlsGroup.epoch),
70852
- payload: Buffer.from(commit).toString("hex")
70853
- }
70854
- },
70855
- `${label} commit`
70856
- );
70857
- if (!commitSent) {
70843
+ const { commits, welcome } = await mlsGroup.reAdmitMembers(targets.map((t22) => t22.keyPackage));
70844
+ let sentCommits = 0;
70845
+ for (const c22 of commits) {
70846
+ const ok2 = await this._sendFrameConfirmed(
70847
+ {
70848
+ event: "mls_commit",
70849
+ data: {
70850
+ group_id: mlsGroupId,
70851
+ // The epoch this commit PRODUCED. After two commits the manager
70852
+ // only knows the last one, which is why the result carries them.
70853
+ epoch: Number(c22.epoch),
70854
+ payload: Buffer.from(c22.commit).toString("hex")
70855
+ }
70856
+ },
70857
+ `${label} commit ${sentCommits + 1}/${commits.length}`
70858
+ );
70859
+ if (!ok2) break;
70860
+ sentCommits++;
70861
+ }
70862
+ if (sentCommits === 0) {
70858
70863
  mlsGroup.importState(snapshot);
70859
70864
  console.warn(
70860
70865
  `[SecureChannel] ${label}: commit was NOT written \u2014 rolled back to epoch ${Number(mlsGroup.epoch)}, nothing fulfilled, requests stay pending`
@@ -70862,6 +70867,12 @@ ${baseText}`;
70862
70867
  return 0;
70863
70868
  }
70864
70869
  await saveMlsState(this.config.dataDir, mlsGroupId, JSON.stringify(mlsGroup.exportState()));
70870
+ if (sentCommits < commits.length) {
70871
+ console.warn(
70872
+ `[SecureChannel] ${label}: only ${sentCommits}/${commits.length} commits reached the wire \u2014 requests stay PENDING for the next pull`
70873
+ );
70874
+ return 0;
70875
+ }
70865
70876
  if (!welcome) {
70866
70877
  console.warn(
70867
70878
  `[SecureChannel] ${label}: addMembers produced NO Welcome \u2014 ${targets.length} request(s) left pending rather than marked fulfilled`
@@ -71590,38 +71601,63 @@ ${baseText}`;
71590
71601
  { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
71591
71602
  );
71592
71603
  if (!pendingRes.ok) continue;
71593
- const pending = await pendingRes.json();
71604
+ let pending = await pendingRes.json();
71594
71605
  if (pending.length === 0) continue;
71595
71606
  console.log(`[SecureChannel] ${pending.length} pending Welcome requests for room ${roomId.slice(0, 8)}`);
71607
+ const nowMs = Date.now();
71608
+ const fresh = pending.filter((r22) => {
71609
+ const key = `${roomState.mlsGroupId}:${r22.requesting_device_id}`;
71610
+ const last = this._roomFulfilCooldown.get(key) ?? 0;
71611
+ if (nowMs - last < ROOM_FULFIL_COOLDOWN_MS) {
71612
+ console.log(
71613
+ `[SecureChannel] skipping ${r22.requesting_device_id.slice(0, 8)} in room ${roomId.slice(0, 8)} \u2014 re-admitted ${Math.round((nowMs - last) / 1e3)}s ago`
71614
+ );
71615
+ return false;
71616
+ }
71617
+ return true;
71618
+ });
71619
+ if (fresh.length === 0) continue;
71620
+ for (const r22 of fresh) {
71621
+ this._roomFulfilCooldown.set(`${roomState.mlsGroupId}:${r22.requesting_device_id}`, nowMs);
71622
+ }
71623
+ pending = fresh;
71624
+ const deviceIds = pending.map((r22) => r22.requesting_device_id);
71625
+ const kpRes = await fetch(
71626
+ `${this.config.apiUrl}/api/v1/mls/key-packages/batch?device_ids=${deviceIds.join(",")}`,
71627
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
71628
+ );
71629
+ if (!kpRes.ok) continue;
71630
+ const kpData = await kpRes.json();
71631
+ const roomTargets = [];
71596
71632
  for (const req of pending) {
71597
- try {
71598
- const kpRes = await fetch(
71599
- `${this.config.apiUrl}/api/v1/mls/key-packages/batch?device_ids=${req.requesting_device_id}`,
71600
- { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
71633
+ const kpHex = kpData[req.requesting_device_id];
71634
+ if (!kpHex) {
71635
+ console.warn(`[SecureChannel] No KeyPackage for ${req.requesting_device_id.slice(0, 8)}, skipping`);
71636
+ continue;
71637
+ }
71638
+ const kpBytes = new Uint8Array(Buffer.from(kpHex, "hex"));
71639
+ roomTargets.push({
71640
+ requestId: req.id,
71641
+ deviceId: req.requesting_device_id,
71642
+ keyPackage: MLSGroupManager.deserializeKeyPackage(kpBytes)
71643
+ });
71644
+ }
71645
+ if (roomTargets.length === 0) continue;
71646
+ try {
71647
+ const done = await this._fulfilWelcomeRequests({
71648
+ mlsGroup,
71649
+ mlsGroupId: roomState.mlsGroupId,
71650
+ label: `room ${roomId.slice(0, 8)}`,
71651
+ welcomeFields: { room_id: roomId },
71652
+ targets: roomTargets
71653
+ });
71654
+ if (done > 0) {
71655
+ console.log(
71656
+ `[SecureChannel] Batched Welcome for ${done}/${roomTargets.length} device(s) in room ${roomId.slice(0, 8)} (epoch=${mlsGroup.epoch})`
71601
71657
  );
71602
- if (!kpRes.ok) continue;
71603
- const kpData = await kpRes.json();
71604
- const kpHex = kpData[req.requesting_device_id];
71605
- if (!kpHex) continue;
71606
- const kpBytes = new Uint8Array(Buffer.from(kpHex, "hex"));
71607
- const memberKp = MLSGroupManager.deserializeKeyPackage(kpBytes);
71608
- const done = await this._fulfilWelcomeRequests({
71609
- mlsGroup,
71610
- mlsGroupId: roomState.mlsGroupId,
71611
- label: `room ${roomId.slice(0, 8)}`,
71612
- welcomeFields: { room_id: roomId },
71613
- targets: [{
71614
- requestId: req.id,
71615
- deviceId: req.requesting_device_id,
71616
- keyPackage: memberKp
71617
- }]
71618
- });
71619
- if (done > 0) {
71620
- console.log(`[SecureChannel] Fulfilled Welcome for ${req.requesting_device_id.slice(0, 8)} in room ${roomId.slice(0, 8)}`);
71621
- }
71622
- } catch (fulfillErr) {
71623
- console.warn(`[SecureChannel] Welcome fulfill failed for ${req.requesting_device_id.slice(0, 8)}:`, fulfillErr);
71624
71658
  }
71659
+ } catch (fulfillErr) {
71660
+ console.warn(`[SecureChannel] Batched Welcome failed for room ${roomId.slice(0, 8)}:`, fulfillErr);
71625
71661
  }
71626
71662
  } catch {
71627
71663
  }
@@ -99432,7 +99468,7 @@ var init_index = __esm2({
99432
99468
  await init_skill_telemetry();
99433
99469
  await init_policy_enforcer();
99434
99470
  init_room_protocol();
99435
- VERSION = true ? "0.23.32" : "0.0.0-dev";
99471
+ VERSION = true ? "0.23.33" : "0.0.0-dev";
99436
99472
  }
99437
99473
  });
99438
99474
  await init_index();
@@ -135836,7 +135872,7 @@ async function main() {
135836
135872
  "[bridge] warning: passing the invite token on the command line is visible to other local users via 'ps'. Prefer: AV_INVITE_TOKEN=\u2026 npx @agentvault/claude-bridge"
135837
135873
  );
135838
135874
  }
135839
- logLine(`[bridge] version: ${true ? "0.8.5" : "dev"}`);
135875
+ logLine(`[bridge] version: ${true ? "0.8.6" : "dev"}`);
135840
135876
  logLine(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
135841
135877
  logLine(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
135842
135878
  if (cfg.armRoom) {
@@ -135868,7 +135904,7 @@ async function main() {
135868
135904
  // its default would render "@agentvault/agentvault@0.7.x" — the wrong
135869
135905
  // package name attached to the bridge's version number, which is worse
135870
135906
  // than either alone.
135871
- clientVersion: `@agentvault/claude-bridge@${true ? "0.8.5" : "dev"}`
135907
+ clientVersion: `@agentvault/claude-bridge@${true ? "0.8.6" : "dev"}`
135872
135908
  });
135873
135909
  const agentSystemPrompt = cfg.systemPrompt ?? `You are ${cfg.agentName}, an AI agent on AgentVault. You talk with your owner in 1:1 direct messages and collaborate with other agents in shared rooms. That is your identity \u2014 introduce yourself by that name and do not claim to be any other agent. To speak, call the say tool. In a 1:1 with your owner, reply to what they say. In a room you see every message \u2014 call say only when you have something worth adding, and otherwise stay silent. Keep messages concise.`;
135874
135910
  const deviceJwt = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvault/claude-bridge",
3
- "version": "0.8.5",
3
+ "version": "0.8.6",
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",