@agentvault/agentvault 0.20.23 → 0.20.25

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
@@ -62726,6 +62726,48 @@ function mlsFileName(groupId) {
62726
62726
  const safe = groupId.replace(/[^a-zA-Z0-9_-]/g, "_");
62727
62727
  return `mls-${safe}.json`;
62728
62728
  }
62729
+ async function hasPendingWelcome(dataDir, groupId) {
62730
+ try {
62731
+ await readFile(pendingKpPath(dataDir, groupId));
62732
+ return true;
62733
+ } catch {
62734
+ return false;
62735
+ }
62736
+ }
62737
+ async function savePendingKpBundle(dataDir, groupId, kp) {
62738
+ await mkdir(dataDir, { recursive: true, mode: DIR_MODE });
62739
+ const serialized = {
62740
+ publicPackage: Buffer.from(kp.publicPackage).toString("base64"),
62741
+ privatePackage: {
62742
+ initPrivateKey: Buffer.from(kp.privatePackage.initPrivateKey).toString("base64"),
62743
+ hpkePrivateKey: Buffer.from(kp.privatePackage.hpkePrivateKey).toString("base64"),
62744
+ signaturePrivateKey: Buffer.from(kp.privatePackage.signaturePrivateKey).toString("base64")
62745
+ }
62746
+ };
62747
+ await writeFile(pendingKpPath(dataDir, groupId), JSON.stringify(serialized), { encoding: "utf-8", mode: FILE_MODE });
62748
+ }
62749
+ async function loadPendingKpBundle(dataDir, groupId) {
62750
+ try {
62751
+ const raw = await readFile(pendingKpPath(dataDir, groupId), "utf-8");
62752
+ const parsed = JSON.parse(raw);
62753
+ return {
62754
+ publicPackage: new Uint8Array(Buffer.from(parsed.publicPackage, "base64")),
62755
+ privatePackage: {
62756
+ initPrivateKey: new Uint8Array(Buffer.from(parsed.privatePackage.initPrivateKey, "base64")),
62757
+ hpkePrivateKey: new Uint8Array(Buffer.from(parsed.privatePackage.hpkePrivateKey, "base64")),
62758
+ signaturePrivateKey: new Uint8Array(Buffer.from(parsed.privatePackage.signaturePrivateKey, "base64"))
62759
+ }
62760
+ };
62761
+ } catch {
62762
+ return null;
62763
+ }
62764
+ }
62765
+ async function clearPendingWelcome(dataDir, groupId) {
62766
+ try {
62767
+ await rm(pendingKpPath(dataDir, groupId));
62768
+ } catch {
62769
+ }
62770
+ }
62729
62771
  async function saveMlsState(dataDir, groupId, state) {
62730
62772
  await mkdir(dataDir, { recursive: true, mode: DIR_MODE });
62731
62773
  const filePath = join(dataDir, mlsFileName(groupId));
@@ -62746,12 +62788,13 @@ async function deleteMlsState(dataDir, groupId) {
62746
62788
  } catch {
62747
62789
  }
62748
62790
  }
62749
- var FILE_MODE, DIR_MODE;
62791
+ var FILE_MODE, DIR_MODE, pendingKpPath;
62750
62792
  var init_mls_state = __esm({
62751
62793
  "src/mls-state.ts"() {
62752
62794
  "use strict";
62753
62795
  FILE_MODE = 384;
62754
62796
  DIR_MODE = 448;
62797
+ pendingKpPath = (dataDir, groupId) => join(dataDir, `mls-kp-pending-${groupId.replace(/[^a-zA-Z0-9_-]/g, "_")}.json`);
62755
62798
  }
62756
62799
  });
62757
62800
 
@@ -63639,9 +63682,6 @@ var init_channel = __esm({
63639
63682
  _mlsKeyPackage = null;
63640
63683
  /** Pending KeyPackage bundle from request-Welcome flow (used by _handleMlsWelcome). */
63641
63684
  _pendingMlsKpBundle;
63642
- /** Channels that have already had their sync fallback KeyPackage published this connect cycle.
63643
- * Prevents multiple agents from overwriting each other's KP for the same channel. */
63644
- _pendingWelcomeChannels = /* @__PURE__ */ new Set();
63645
63685
  /** In-memory credential store for renter-provided credentials (never persisted). */
63646
63686
  _credentialStore = new CredentialStore();
63647
63687
  /** Dedup buffer for A2A message IDs (prevents double-delivery via direct + Redis) */
@@ -65202,8 +65242,7 @@ var init_channel = __esm({
65202
65242
  }
65203
65243
  }
65204
65244
  const memberMlsGroup = chState.mlsGroupId ? this._mlsGroups.get(`a2a:${chId}`) : void 0;
65205
- if (chState.role === "member" && !memberMlsGroup?.isInitialized && !this._pendingWelcomeChannels.has(chId)) {
65206
- this._pendingWelcomeChannels.add(chId);
65245
+ if (chState.role === "member" && !memberMlsGroup?.isInitialized) {
65207
65246
  try {
65208
65247
  let groupId = chState.mlsGroupId;
65209
65248
  let memberIds = [];
@@ -65237,10 +65276,16 @@ var init_channel = __esm({
65237
65276
  if (memberIds.includes(this._persisted.deviceId)) {
65238
65277
  console.log(`[SecureChannel] A2A ${chId.slice(0, 8)} member in server membership but no local MLS state \u2014 requesting Welcome`);
65239
65278
  }
65279
+ if (await hasPendingWelcome(this.config.dataDir, groupId)) {
65280
+ console.log(`[SecureChannel] A2A ${chId.slice(0, 8)} sync fallback: skipping \u2014 Welcome already pending for group=${groupId.slice(0, 8)}`);
65281
+ continue;
65282
+ }
65283
+ await setPendingWelcome(this.config.dataDir, groupId);
65240
65284
  const mgr = new MLSGroupManager();
65241
65285
  const identity = new TextEncoder().encode(this._persisted.deviceId);
65242
65286
  const kp = await mgr.generateKeyPackage(identity);
65243
65287
  this._pendingMlsKpBundle = kp;
65288
+ await savePendingKpBundle(this.config.dataDir, groupId, kp);
65244
65289
  const kpBytes = MLSGroupManager.serializeKeyPackage(kp.publicPackage);
65245
65290
  await fetch(`${this.config.apiUrl}/api/v1/mls/key-packages`, {
65246
65291
  method: "POST",
@@ -66115,7 +66160,6 @@ var init_channel = __esm({
66115
66160
  clearInterval(this._deliveryHeartbeat);
66116
66161
  this._deliveryHeartbeat = null;
66117
66162
  }
66118
- this._pendingWelcomeChannels.clear();
66119
66163
  if (this._stopped) return;
66120
66164
  this._setState("disconnected");
66121
66165
  this._scheduleReconnect();
@@ -67105,12 +67149,14 @@ ${messageText}`;
67105
67149
  async _handleMlsWelcome(data) {
67106
67150
  const groupId = data.group_id;
67107
67151
  const conversationId = data.conversation_id;
67108
- const kpSource = this._pendingMlsKpBundle ? "pending" : this._mlsKeyPackage ? "connect" : "generated";
67152
+ const persistedKpAvailable = await hasPendingWelcome(this.config.dataDir, groupId);
67153
+ const kpSource = this._pendingMlsKpBundle ? "pending" : persistedKpAvailable ? "persisted" : this._mlsKeyPackage ? "connect" : "generated";
67109
67154
  console.log(`[SecureChannel] Received MLS welcome for group ${groupId?.slice(0, 8)}${conversationId ? ` conv=${conversationId.slice(0, 8)}` : ""} kpSource=${kpSource}`);
67110
67155
  try {
67111
67156
  const welcomeBytes = new Uint8Array(Buffer.from(data.payload, "hex"));
67112
67157
  const mgr = new MLSGroupManager();
67113
- const kp = this._pendingMlsKpBundle ?? this._mlsKeyPackage ?? await (async () => {
67158
+ const persistedKp = await loadPendingKpBundle(this.config.dataDir, groupId);
67159
+ const kp = this._pendingMlsKpBundle ?? persistedKp ?? this._mlsKeyPackage ?? await (async () => {
67114
67160
  const identity = new TextEncoder().encode(this._deviceId);
67115
67161
  return mgr.generateKeyPackage(identity);
67116
67162
  })();
@@ -67147,7 +67193,8 @@ ${messageText}`;
67147
67193
  this._mlsGroups.set(`a2a:${channelId}`, mgr);
67148
67194
  await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mgr.exportState()));
67149
67195
  await this._persistState();
67150
- this._pendingWelcomeChannels.delete(channelId);
67196
+ await clearPendingWelcome(this.config.dataDir, groupId).catch(() => {
67197
+ });
67151
67198
  this._pendingMlsKpBundle = void 0;
67152
67199
  console.log(`[SecureChannel] Joined MLS group for A2A ${channelId.slice(0, 8)} via Welcome (epoch=${mgr.epoch})`);
67153
67200
  if (this.config.onA2AChannelReady && entry.conversationId) {
@@ -94814,7 +94861,7 @@ var init_index = __esm({
94814
94861
  init_skill_invoker();
94815
94862
  await init_skill_telemetry();
94816
94863
  await init_policy_enforcer();
94817
- VERSION = true ? "0.20.23" : "0.0.0-dev";
94864
+ VERSION = true ? "0.20.25" : "0.0.0-dev";
94818
94865
  }
94819
94866
  });
94820
94867
  await init_index();