@agentvault/claude-bridge 0.7.10 → 0.7.11

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 +84 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -63791,6 +63791,35 @@ async function deleteMlsState(dataDir, groupId) {
63791
63791
  } catch {
63792
63792
  }
63793
63793
  }
63794
+ async function quarantineOrphanedMlsState(dataDir, isStillMember, opts = {}) {
63795
+ const max = opts.max ?? 20;
63796
+ const quarantined = [];
63797
+ let names;
63798
+ try {
63799
+ names = await readdir(dataDir);
63800
+ } catch {
63801
+ return quarantined;
63802
+ }
63803
+ for (const name of names) {
63804
+ if (quarantined.length >= max) break;
63805
+ const m22 = /^mls-(?!kp-pending-)([A-Za-z0-9_-]+)\.json$/.exec(name);
63806
+ if (!m22) continue;
63807
+ const groupId = m22[1];
63808
+ let member;
63809
+ try {
63810
+ member = await isStillMember(groupId);
63811
+ } catch {
63812
+ continue;
63813
+ }
63814
+ if (member === null || member === true) continue;
63815
+ try {
63816
+ await rename(join(dataDir, name), join(dataDir, `${name}.orphaned`));
63817
+ quarantined.push(groupId);
63818
+ } catch {
63819
+ }
63820
+ }
63821
+ return quarantined;
63822
+ }
63794
63823
  var SYNC_LOCK_STALE_MS;
63795
63824
  var pendingKpPath;
63796
63825
  var init_mls_state = __esm2({
@@ -64149,6 +64178,16 @@ var init_a2a_log_gate = __esm2({
64149
64178
  };
64150
64179
  }
64151
64180
  });
64181
+ function mlsMembershipFromStatus(status) {
64182
+ if (status === 200) return true;
64183
+ if (status === 403 || status === 404) return false;
64184
+ return null;
64185
+ }
64186
+ var init_mls_membership_probe = __esm2({
64187
+ "src/mls-membership-probe.ts"() {
64188
+ "use strict";
64189
+ }
64190
+ });
64152
64191
  var openclaw_compat_exports = {};
64153
64192
  __export2(openclaw_compat_exports, {
64154
64193
  AGENT_EVENT_CANDIDATES: () => AGENT_EVENT_CANDIDATES,
@@ -64709,6 +64748,7 @@ var init_channel = __esm2({
64709
64748
  await init_state();
64710
64749
  init_transport2();
64711
64750
  init_a2a_log_gate();
64751
+ init_mls_membership_probe();
64712
64752
  ROOM_AGENT_TYPES = /* @__PURE__ */ new Set([
64713
64753
  "message",
64714
64754
  "text",
@@ -66072,6 +66112,47 @@ var init_channel = __esm2({
66072
66112
  );
66073
66113
  this.emit("rooms_reconciled", { pruned: stale });
66074
66114
  }
66115
+ /**
66116
+ * #629 — quarantine MLS state for groups this device is no longer in.
66117
+ *
66118
+ * `_reconcileRoomsWithServer` above covers ROOMS: anything absent from
66119
+ * `GET /rooms` is pruned. It cannot see a group that was never a room — a 1:1
66120
+ * conversation group or an A2A channel group — so that state lingered with
66121
+ * nothing to reconcile it. Measured on loopita 2026-08-08: one group-state
66122
+ * file whose group had no server-side row at all.
66123
+ *
66124
+ * `quarantineOrphanedMlsState` was written for exactly this and shipped with
66125
+ * 11 passing tests, but nothing ever called it. This is that call site.
66126
+ *
66127
+ * Runs AFTER the room reconcile so rooms are already gone and this only sees
66128
+ * genuine non-room orphans.
66129
+ *
66130
+ * Quarantine RENAMES to `.orphaned` rather than deleting: if the verdict is
66131
+ * ever wrong the state is recoverable by hand, which deletion would not be.
66132
+ */
66133
+ async _quarantineOrphanedMlsGroups() {
66134
+ if (!this._deviceJwt) return;
66135
+ const quarantined = await quarantineOrphanedMlsState(
66136
+ this.config.dataDir,
66137
+ async (groupId) => {
66138
+ try {
66139
+ const res = await fetch(
66140
+ `${this.config.apiUrl}/api/v1/mls/groups/${encodeURIComponent(groupId)}`,
66141
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
66142
+ );
66143
+ return mlsMembershipFromStatus(res.status);
66144
+ } catch {
66145
+ return null;
66146
+ }
66147
+ }
66148
+ );
66149
+ if (quarantined.length > 0) {
66150
+ console.log(
66151
+ `[SecureChannel] Quarantined ${quarantined.length} orphaned MLS group state file(s): ` + quarantined.map((g22) => g22.slice(0, 8)).join(", ")
66152
+ );
66153
+ this.emit("mls_state_quarantined", { groupIds: quarantined });
66154
+ }
66155
+ }
66075
66156
  /**
66076
66157
  * Handle an in-band `{event:"error"}` frame from the server.
66077
66158
  *
@@ -67596,6 +67677,8 @@ var init_channel = __esm2({
67596
67677
  this._setState("ready");
67597
67678
  void this._reconcileRoomsWithServer().catch(
67598
67679
  (err) => console.warn(`[SecureChannel] room reconcile failed (ignored): ${err instanceof Error ? err.message : String(err)}`)
67680
+ ).then(() => this._quarantineOrphanedMlsGroups()).catch(
67681
+ (err) => console.warn(`[SecureChannel] MLS orphan quarantine failed (ignored): ${err instanceof Error ? err.message : String(err)}`)
67599
67682
  );
67600
67683
  if (this.config.enableScanning) {
67601
67684
  this._scanEngine = new ScanEngine();
@@ -134185,7 +134268,7 @@ async function main() {
134185
134268
  "[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"
134186
134269
  );
134187
134270
  }
134188
- logLine(`[bridge] version: ${true ? "0.7.10" : "dev"}`);
134271
+ logLine(`[bridge] version: ${true ? "0.7.11" : "dev"}`);
134189
134272
  logLine(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
134190
134273
  logLine(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
134191
134274
  if (cfg.armRoom) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvault/claude-bridge",
3
- "version": "0.7.10",
3
+ "version": "0.7.11",
4
4
  "type": "module",
5
5
  "description": "AgentVault Claude Bridge \u2014 daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
6
6
  "main": "dist/index.js",