@agentvault/agentvault 0.20.41 → 0.20.43

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
@@ -63719,6 +63719,9 @@ var init_channel = __esm({
63719
63719
  _mlsGroups = /* @__PURE__ */ new Map();
63720
63720
  /** Maps conversationId -> conversation_group_id for shared 1:1 MLS groups */
63721
63721
  _sessionGroupIds = /* @__PURE__ */ new Map();
63722
+ /** Consecutive MLS decrypt failure count per group key for epoch recovery. */
63723
+ _mlsDecryptFailCounts = /* @__PURE__ */ new Map();
63724
+ static MAX_MLS_DECRYPT_FAILS = 3;
63722
63725
  /** Cached MLS KeyPackage bundle for this device (regenerated on each connect). */
63723
63726
  _mlsKeyPackage = null;
63724
63727
  /** Pending KeyPackage bundle from request-Welcome flow (used by _handleMlsWelcome). */
@@ -66434,6 +66437,50 @@ var init_channel = __esm({
66434
66437
  topicId = parsed.topicId ?? topicId;
66435
66438
  } catch {
66436
66439
  }
66440
+ if (messageType === "history_catchup_request") {
66441
+ try {
66442
+ const request = JSON.parse(plaintext);
66443
+ const history = this._persisted?.messageHistory ?? [];
66444
+ let filtered = history;
66445
+ if (request.since_ts) {
66446
+ const sinceTime = new Date(request.since_ts).getTime();
66447
+ filtered = history.filter((e) => new Date(e.ts).getTime() > sinceTime);
66448
+ }
66449
+ if (filtered.length > 100) filtered = filtered.slice(-100);
66450
+ const responsePayload = JSON.stringify({
66451
+ type: "history_catchup_response",
66452
+ target_device_id: request.requesting_device_id,
66453
+ messages: filtered.map((e) => ({
66454
+ sender: e.sender,
66455
+ sender_name: e.sender === "agent" ? this.config.agentName ?? "Agent" : "Owner",
66456
+ text: e.text,
66457
+ ts: e.ts
66458
+ })),
66459
+ no_history: filtered.length === 0
66460
+ });
66461
+ if (mgr && mlsGroupId) {
66462
+ const responseBytes = new TextEncoder().encode(responsePayload);
66463
+ const cipher = await mgr.encrypt(responseBytes);
66464
+ await saveMlsState(this.config.dataDir, mlsGroupId, JSON.stringify(mgr.exportState()));
66465
+ if (this._ws) {
66466
+ this._ws.send(JSON.stringify({
66467
+ event: "message_mls",
66468
+ data: {
66469
+ group_id: mlsGroupId,
66470
+ epoch: Number(mgr.epoch),
66471
+ payload: Buffer.from(cipher).toString("hex"),
66472
+ message_type: "history_catchup_response"
66473
+ }
66474
+ }));
66475
+ }
66476
+ console.log(`[SecureChannel] Sent 1:1 history catchup: ${filtered.length} messages to ${request.requesting_device_id?.slice(0, 8)}`);
66477
+ }
66478
+ } catch (catchupErr) {
66479
+ console.warn("[SecureChannel] 1:1 history catchup failed:", catchupErr);
66480
+ }
66481
+ return;
66482
+ }
66483
+ if (messageType === "history_catchup_response") return;
66437
66484
  if (convId) {
66438
66485
  const session = this._sessions.get(convId);
66439
66486
  if (session && !session.activated) {
@@ -66456,7 +66503,16 @@ var init_channel = __esm({
66456
66503
  this.config.onMessage(text, metadata);
66457
66504
  }
66458
66505
  } catch (decryptErr) {
66459
- console.error(`[SecureChannel] MLS 1:1 decrypt failed for conv ${convId?.slice(0, 8)}:`, decryptErr);
66506
+ const failKey = mgrKey || `unknown:${groupId}`;
66507
+ const count = (this._mlsDecryptFailCounts.get(failKey) ?? 0) + 1;
66508
+ this._mlsDecryptFailCounts.set(failKey, count);
66509
+ console.error(`[SecureChannel] MLS 1:1 decrypt failed (${count}/${_SecureChannel.MAX_MLS_DECRYPT_FAILS}) for ${failKey}:`, decryptErr);
66510
+ if (count >= _SecureChannel.MAX_MLS_DECRYPT_FAILS) {
66511
+ const recoveryGroupId = (convGroupId ? this._persisted?.mlsGroups?.[convGroupId]?.mlsGroupId : null) ?? this._persisted?.mlsConversations?.[convId]?.mlsGroupId ?? groupId;
66512
+ if (recoveryGroupId && failKey) {
66513
+ await this._triggerMlsEpochRecovery(failKey, recoveryGroupId);
66514
+ }
66515
+ }
66460
66516
  }
66461
66517
  }
66462
66518
  async _handleIncomingMessage(msgData) {
@@ -67277,7 +67333,13 @@ ${messageText}`;
67277
67333
  console.error("[SecureChannel] onMessage callback error (MLS):", err);
67278
67334
  });
67279
67335
  } catch (err) {
67280
- console.error(`[SecureChannel] MLS room decrypt failed for ${resolvedRoomId.slice(0, 8)}:`, err);
67336
+ const failKey = resolvedRoomId;
67337
+ const count = (this._mlsDecryptFailCounts.get(failKey) ?? 0) + 1;
67338
+ this._mlsDecryptFailCounts.set(failKey, count);
67339
+ console.error(`[SecureChannel] MLS room decrypt failed (${count}/${_SecureChannel.MAX_MLS_DECRYPT_FAILS}) for ${resolvedRoomId.slice(0, 8)}:`, err);
67340
+ if (count >= _SecureChannel.MAX_MLS_DECRYPT_FAILS && groupId) {
67341
+ await this._triggerMlsEpochRecovery(resolvedRoomId, groupId);
67342
+ }
67281
67343
  }
67282
67344
  }
67283
67345
  /**
@@ -67419,6 +67481,44 @@ ${messageText}`;
67419
67481
  console.log(`[SecureChannel] Applied ${applied} buffered commits, now epoch=${mgr.epoch}`);
67420
67482
  }
67421
67483
  }
67484
+ /**
67485
+ * MLS epoch recovery for shared 1:1 groups.
67486
+ * Clears local state, publishes fresh KeyPackage, and requests Welcome.
67487
+ */
67488
+ async _triggerMlsEpochRecovery(groupKey, mlsGroupId) {
67489
+ console.warn(`[SecureChannel] MLS epoch recovery for ${groupKey} (${mlsGroupId.slice(0, 8)})`);
67490
+ this._mlsGroups.delete(groupKey);
67491
+ this._mlsDecryptFailCounts.delete(groupKey);
67492
+ try {
67493
+ const { unlink: unlink2 } = await import("fs/promises");
67494
+ const path = `${this.config.dataDir}/.mls-state/${mlsGroupId}.json`;
67495
+ await unlink2(path).catch(() => {
67496
+ });
67497
+ } catch {
67498
+ }
67499
+ try {
67500
+ const mgr = new MLSGroupManager();
67501
+ const identity = new TextEncoder().encode(this._deviceId);
67502
+ const kp = await mgr.generateKeyPackage(identity);
67503
+ const kpBytes = MLSGroupManager.serializeKeyPackage(kp.publicPackage);
67504
+ const kpHex = Buffer.from(kpBytes).toString("hex");
67505
+ await fetch(`${this.config.apiUrl}/api/v1/mls/key-packages`, {
67506
+ method: "POST",
67507
+ headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
67508
+ body: JSON.stringify({ key_package: kpHex, device_id: this._deviceId })
67509
+ });
67510
+ this._pendingMlsKpBundle = kp;
67511
+ await savePendingKpBundle(this.config.dataDir, mlsGroupId, kp, MLSGroupManager.serializeKeyPackage);
67512
+ await fetch(`${this.config.apiUrl}/api/v1/mls/groups/${mlsGroupId}/request-welcome`, {
67513
+ method: "POST",
67514
+ headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
67515
+ body: JSON.stringify({ device_id: this._deviceId })
67516
+ });
67517
+ console.log(`[SecureChannel] Epoch recovery: Welcome requested for group ${mlsGroupId.slice(0, 8)}`);
67518
+ } catch (err) {
67519
+ console.error(`[SecureChannel] Epoch recovery failed for ${mlsGroupId.slice(0, 8)}:`, err);
67520
+ }
67521
+ }
67422
67522
  async _handleMlsWelcome(data) {
67423
67523
  const groupId = data.group_id;
67424
67524
  const conversationId = data.conversation_id;
@@ -67634,6 +67734,14 @@ ${messageText}`;
67634
67734
  } catch (err) {
67635
67735
  console.warn(`[SecureChannel] Delivery ${msg.message_type} processing failed:`, err);
67636
67736
  nackedIds.push(msg.queue_id);
67737
+ if (msg.group_id && (msg.message_type === "commit" || msg.message_type === "application")) {
67738
+ const failKey = msg.conversation_group_id ? `1to1-group:${msg.conversation_group_id}` : `group:${msg.group_id}`;
67739
+ const count = (this._mlsDecryptFailCounts.get(failKey) ?? 0) + 1;
67740
+ this._mlsDecryptFailCounts.set(failKey, count);
67741
+ if (count >= _SecureChannel.MAX_MLS_DECRYPT_FAILS) {
67742
+ await this._triggerMlsEpochRecovery(failKey, msg.group_id);
67743
+ }
67744
+ }
67637
67745
  }
67638
67746
  }
67639
67747
  if (nackedIds.length > 0) {
@@ -68323,7 +68431,13 @@ ${messageText}`;
68323
68431
  this.config.onA2AMessage(a2aMsg);
68324
68432
  }
68325
68433
  } catch (err) {
68326
- console.error(`[SecureChannel] MLS A2A decrypt failed for ${a2aChannelId.slice(0, 8)}:`, err);
68434
+ const failKey = `a2a:${a2aChannelId}`;
68435
+ const count = (this._mlsDecryptFailCounts.get(failKey) ?? 0) + 1;
68436
+ this._mlsDecryptFailCounts.set(failKey, count);
68437
+ console.error(`[SecureChannel] MLS A2A decrypt failed (${count}/${_SecureChannel.MAX_MLS_DECRYPT_FAILS}) for ${a2aChannelId.slice(0, 8)}:`, err);
68438
+ if (count >= _SecureChannel.MAX_MLS_DECRYPT_FAILS && groupId) {
68439
+ await this._triggerMlsEpochRecovery(failKey, groupId);
68440
+ }
68327
68441
  }
68328
68442
  }
68329
68443
  /**
@@ -95498,7 +95612,7 @@ var init_index = __esm({
95498
95612
  init_skill_invoker();
95499
95613
  await init_skill_telemetry();
95500
95614
  await init_policy_enforcer();
95501
- VERSION = true ? "0.20.41" : "0.0.0-dev";
95615
+ VERSION = true ? "0.20.43" : "0.0.0-dev";
95502
95616
  }
95503
95617
  });
95504
95618
  await init_index();