@agentvault/agentvault 0.20.41 → 0.20.42

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) {
@@ -67419,6 +67475,44 @@ ${messageText}`;
67419
67475
  console.log(`[SecureChannel] Applied ${applied} buffered commits, now epoch=${mgr.epoch}`);
67420
67476
  }
67421
67477
  }
67478
+ /**
67479
+ * MLS epoch recovery for shared 1:1 groups.
67480
+ * Clears local state, publishes fresh KeyPackage, and requests Welcome.
67481
+ */
67482
+ async _triggerMlsEpochRecovery(groupKey, mlsGroupId) {
67483
+ console.warn(`[SecureChannel] MLS epoch recovery for ${groupKey} (${mlsGroupId.slice(0, 8)})`);
67484
+ this._mlsGroups.delete(groupKey);
67485
+ this._mlsDecryptFailCounts.delete(groupKey);
67486
+ try {
67487
+ const { unlink: unlink2 } = await import("fs/promises");
67488
+ const path = `${this.config.dataDir}/.mls-state/${mlsGroupId}.json`;
67489
+ await unlink2(path).catch(() => {
67490
+ });
67491
+ } catch {
67492
+ }
67493
+ try {
67494
+ const mgr = new MLSGroupManager();
67495
+ const identity = new TextEncoder().encode(this._deviceId);
67496
+ const kp = await mgr.generateKeyPackage(identity);
67497
+ const kpBytes = MLSGroupManager.serializeKeyPackage(kp.publicPackage);
67498
+ const kpHex = Buffer.from(kpBytes).toString("hex");
67499
+ await fetch(`${this.config.apiUrl}/api/v1/mls/key-packages`, {
67500
+ method: "POST",
67501
+ headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
67502
+ body: JSON.stringify({ key_package: kpHex, device_id: this._deviceId })
67503
+ });
67504
+ this._pendingMlsKpBundle = kp;
67505
+ await savePendingKpBundle(this.config.dataDir, mlsGroupId, kp, MLSGroupManager.serializeKeyPackage);
67506
+ await fetch(`${this.config.apiUrl}/api/v1/mls/groups/${mlsGroupId}/request-welcome`, {
67507
+ method: "POST",
67508
+ headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
67509
+ body: JSON.stringify({ device_id: this._deviceId })
67510
+ });
67511
+ console.log(`[SecureChannel] Epoch recovery: Welcome requested for group ${mlsGroupId.slice(0, 8)}`);
67512
+ } catch (err) {
67513
+ console.error(`[SecureChannel] Epoch recovery failed for ${mlsGroupId.slice(0, 8)}:`, err);
67514
+ }
67515
+ }
67422
67516
  async _handleMlsWelcome(data) {
67423
67517
  const groupId = data.group_id;
67424
67518
  const conversationId = data.conversation_id;
@@ -67634,6 +67728,14 @@ ${messageText}`;
67634
67728
  } catch (err) {
67635
67729
  console.warn(`[SecureChannel] Delivery ${msg.message_type} processing failed:`, err);
67636
67730
  nackedIds.push(msg.queue_id);
67731
+ if (msg.group_id && (msg.message_type === "commit" || msg.message_type === "application")) {
67732
+ const failKey = msg.conversation_group_id ? `1to1-group:${msg.conversation_group_id}` : `group:${msg.group_id}`;
67733
+ const count = (this._mlsDecryptFailCounts.get(failKey) ?? 0) + 1;
67734
+ this._mlsDecryptFailCounts.set(failKey, count);
67735
+ if (count >= _SecureChannel.MAX_MLS_DECRYPT_FAILS) {
67736
+ await this._triggerMlsEpochRecovery(failKey, msg.group_id);
67737
+ }
67738
+ }
67637
67739
  }
67638
67740
  }
67639
67741
  if (nackedIds.length > 0) {
@@ -95498,7 +95600,7 @@ var init_index = __esm({
95498
95600
  init_skill_invoker();
95499
95601
  await init_skill_telemetry();
95500
95602
  await init_policy_enforcer();
95501
- VERSION = true ? "0.20.41" : "0.0.0-dev";
95603
+ VERSION = true ? "0.20.42" : "0.0.0-dev";
95502
95604
  }
95503
95605
  });
95504
95606
  await init_index();