@agentvault/agentvault 0.23.19 → 0.23.20

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/index.js CHANGED
@@ -63011,6 +63011,35 @@ async function deleteMlsState(dataDir, groupId) {
63011
63011
  } catch {
63012
63012
  }
63013
63013
  }
63014
+ async function quarantineOrphanedMlsState(dataDir, isStillMember, opts = {}) {
63015
+ const max = opts.max ?? 20;
63016
+ const quarantined = [];
63017
+ let names;
63018
+ try {
63019
+ names = await readdir(dataDir);
63020
+ } catch {
63021
+ return quarantined;
63022
+ }
63023
+ for (const name of names) {
63024
+ if (quarantined.length >= max) break;
63025
+ const m2 = /^mls-(?!kp-pending-)([A-Za-z0-9_-]+)\.json$/.exec(name);
63026
+ if (!m2) continue;
63027
+ const groupId = m2[1];
63028
+ let member;
63029
+ try {
63030
+ member = await isStillMember(groupId);
63031
+ } catch {
63032
+ continue;
63033
+ }
63034
+ if (member === null || member === true) continue;
63035
+ try {
63036
+ await rename(join(dataDir, name), join(dataDir, `${name}.orphaned`));
63037
+ quarantined.push(groupId);
63038
+ } catch {
63039
+ }
63040
+ }
63041
+ return quarantined;
63042
+ }
63014
63043
  var SYNC_LOCK_STALE_MS, pendingKpPath;
63015
63044
  var init_mls_state = __esm({
63016
63045
  "src/mls-state.ts"() {
@@ -63355,6 +63384,45 @@ var init_transport2 = __esm({
63355
63384
  }
63356
63385
  });
63357
63386
 
63387
+ // src/a2a-log-gate.ts
63388
+ function a2aChannelSignature(channels) {
63389
+ return JSON.stringify(
63390
+ channels.map((c2) => ({
63391
+ id: c2.channelId?.slice(0, 8),
63392
+ status: c2.status,
63393
+ participants: c2.participant_count
63394
+ })).sort((a2, b2) => (a2.id ?? "").localeCompare(b2.id ?? ""))
63395
+ );
63396
+ }
63397
+ var A2ALogGate;
63398
+ var init_a2a_log_gate = __esm({
63399
+ "src/a2a-log-gate.ts"() {
63400
+ "use strict";
63401
+ A2ALogGate = class {
63402
+ last = null;
63403
+ /** True when this state differs from the last reported one. Records it. */
63404
+ shouldReport(channels) {
63405
+ const sig = a2aChannelSignature(channels);
63406
+ if (sig === this.last) return false;
63407
+ this.last = sig;
63408
+ return true;
63409
+ }
63410
+ };
63411
+ }
63412
+ });
63413
+
63414
+ // src/mls-membership-probe.ts
63415
+ function mlsMembershipFromStatus(status) {
63416
+ if (status === 200) return true;
63417
+ if (status === 403 || status === 404) return false;
63418
+ return null;
63419
+ }
63420
+ var init_mls_membership_probe = __esm({
63421
+ "src/mls-membership-probe.ts"() {
63422
+ "use strict";
63423
+ }
63424
+ });
63425
+
63358
63426
  // src/openclaw-compat.ts
63359
63427
  var openclaw_compat_exports = {};
63360
63428
  __export(openclaw_compat_exports, {
@@ -63917,6 +63985,8 @@ var init_channel = __esm({
63917
63985
  await init_crypto_helpers();
63918
63986
  await init_state();
63919
63987
  init_transport2();
63988
+ init_a2a_log_gate();
63989
+ init_mls_membership_probe();
63920
63990
  ROOM_AGENT_TYPES = /* @__PURE__ */ new Set([
63921
63991
  "message",
63922
63992
  "text",
@@ -63982,6 +64052,11 @@ var init_channel = __esm({
63982
64052
  _heartbeatTimer = null;
63983
64053
  _heartbeatCallback = null;
63984
64054
  _heartbeatIntervalSeconds = 0;
64055
+ /** #798: report the A2A channel set only when it CHANGES. This line polls
64056
+ * every 30s and produced 90.2% of bridge.log (90,841/100,702 lines) and a
64057
+ * 27.9MB gateway.log on wren, every one reading `channels=0`. Not deleted —
64058
+ * #794's A2A watch needs the transitions. */
64059
+ _a2aLogGate = new A2ALogGate();
63985
64060
  _wakeDetectorTimer = null;
63986
64061
  _lastWakeTick = Date.now();
63987
64062
  _trustToken = null;
@@ -64955,7 +65030,7 @@ var init_channel = __esm({
64955
65030
  */
64956
65031
  sendActivitySpan(spanData) {
64957
65032
  if (!this._ws || this._ws.readyState !== WebSocket.OPEN) return;
64958
- const pluginVersion = true ? "0.23.19" : "0.0.0-dev";
65033
+ const pluginVersion = true ? "0.23.20" : "0.0.0-dev";
64959
65034
  const agentName = this.config.agentName ?? "Agent";
64960
65035
  const resource = {
64961
65036
  "service.name": "agentvault-agent",
@@ -65275,6 +65350,47 @@ var init_channel = __esm({
65275
65350
  );
65276
65351
  this.emit("rooms_reconciled", { pruned: stale });
65277
65352
  }
65353
+ /**
65354
+ * #629 — quarantine MLS state for groups this device is no longer in.
65355
+ *
65356
+ * `_reconcileRoomsWithServer` above covers ROOMS: anything absent from
65357
+ * `GET /rooms` is pruned. It cannot see a group that was never a room — a 1:1
65358
+ * conversation group or an A2A channel group — so that state lingered with
65359
+ * nothing to reconcile it. Measured on loopita 2026-08-08: one group-state
65360
+ * file whose group had no server-side row at all.
65361
+ *
65362
+ * `quarantineOrphanedMlsState` was written for exactly this and shipped with
65363
+ * 11 passing tests, but nothing ever called it. This is that call site.
65364
+ *
65365
+ * Runs AFTER the room reconcile so rooms are already gone and this only sees
65366
+ * genuine non-room orphans.
65367
+ *
65368
+ * Quarantine RENAMES to `.orphaned` rather than deleting: if the verdict is
65369
+ * ever wrong the state is recoverable by hand, which deletion would not be.
65370
+ */
65371
+ async _quarantineOrphanedMlsGroups() {
65372
+ if (!this._deviceJwt) return;
65373
+ const quarantined = await quarantineOrphanedMlsState(
65374
+ this.config.dataDir,
65375
+ async (groupId) => {
65376
+ try {
65377
+ const res = await fetch(
65378
+ `${this.config.apiUrl}/api/v1/mls/groups/${encodeURIComponent(groupId)}`,
65379
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
65380
+ );
65381
+ return mlsMembershipFromStatus(res.status);
65382
+ } catch {
65383
+ return null;
65384
+ }
65385
+ }
65386
+ );
65387
+ if (quarantined.length > 0) {
65388
+ console.log(
65389
+ `[SecureChannel] Quarantined ${quarantined.length} orphaned MLS group state file(s): ` + quarantined.map((g2) => g2.slice(0, 8)).join(", ")
65390
+ );
65391
+ this.emit("mls_state_quarantined", { groupIds: quarantined });
65392
+ }
65393
+ }
65278
65394
  /**
65279
65395
  * Handle an in-band `{event:"error"}` frame from the server.
65280
65396
  *
@@ -66435,7 +66551,9 @@ var init_channel = __esm({
66435
66551
  }
66436
66552
  const myHub = this._persisted.hubAddress || "";
66437
66553
  const myHubId = this._persisted.hubId || "";
66438
- console.log(`[listA2AChannels] myHub=${myHub}, channels=${channels.length}, raw=${JSON.stringify(channels.map((c2) => ({ id: c2.channelId?.slice(0, 8), status: c2.status, participants: c2.participant_count })))}`);
66554
+ if (this._a2aLogGate.shouldReport(channels)) {
66555
+ console.log(`[listA2AChannels] myHub=${myHub}, channels=${channels.length}, raw=${JSON.stringify(channels.map((c2) => ({ id: c2.channelId?.slice(0, 8), status: c2.status, participants: c2.participant_count })))}`);
66556
+ }
66439
66557
  for (const ch of channels) {
66440
66558
  if (ch.status === "active" || ch.status === "approved") {
66441
66559
  const myParticipant = ch.participants?.find(
@@ -66797,6 +66915,8 @@ var init_channel = __esm({
66797
66915
  this._setState("ready");
66798
66916
  void this._reconcileRoomsWithServer().catch(
66799
66917
  (err) => console.warn(`[SecureChannel] room reconcile failed (ignored): ${err instanceof Error ? err.message : String(err)}`)
66918
+ ).then(() => this._quarantineOrphanedMlsGroups()).catch(
66919
+ (err) => console.warn(`[SecureChannel] MLS orphan quarantine failed (ignored): ${err instanceof Error ? err.message : String(err)}`)
66800
66920
  );
66801
66921
  if (this.config.enableScanning) {
66802
66922
  this._scanEngine = new ScanEngine();
@@ -66820,7 +66940,7 @@ var init_channel = __esm({
66820
66940
  agentVersion: this.config.agentVersion ?? "0.0.0",
66821
66941
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
66822
66942
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
66823
- pluginVersion: true ? "0.23.19" : "0.0.0-dev"
66943
+ pluginVersion: true ? "0.23.20" : "0.0.0-dev"
66824
66944
  });
66825
66945
  this._telemetryReporter.startAutoFlush(3e4);
66826
66946
  }
@@ -67143,7 +67263,7 @@ var init_channel = __esm({
67143
67263
  agentVersion: this.config.agentVersion ?? "0.0.0",
67144
67264
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
67145
67265
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
67146
- pluginVersion: true ? "0.23.19" : "0.0.0-dev"
67266
+ pluginVersion: true ? "0.23.20" : "0.0.0-dev"
67147
67267
  });
67148
67268
  this._telemetryReporter.startAutoFlush(3e4);
67149
67269
  }
@@ -97472,7 +97592,7 @@ var init_index = __esm({
97472
97592
  init_skill_invoker();
97473
97593
  await init_skill_telemetry();
97474
97594
  await init_policy_enforcer();
97475
- VERSION = true ? "0.23.19" : "0.0.0-dev";
97595
+ VERSION = true ? "0.23.20" : "0.0.0-dev";
97476
97596
  }
97477
97597
  });
97478
97598
  await init_index();