@agentvault/agentvault 0.20.26 → 0.20.28

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
@@ -62722,6 +62722,43 @@ var init_dist = __esm({
62722
62722
  // src/mls-state.ts
62723
62723
  import { mkdir, readFile, writeFile, rm } from "node:fs/promises";
62724
62724
  import { join } from "node:path";
62725
+ function syncLockPath(dataDir, channelId) {
62726
+ const safe = channelId.replace(/[^a-zA-Z0-9_-]/g, "_");
62727
+ return join(dataDir, `mls-a2a-sync-${safe}.lock`);
62728
+ }
62729
+ async function acquireA2ASyncLock(dataDir, channelId) {
62730
+ await mkdir(dataDir, { recursive: true, mode: DIR_MODE });
62731
+ const lockFile = syncLockPath(dataDir, channelId);
62732
+ const content = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
62733
+ try {
62734
+ await writeFile(lockFile, content, { encoding: "utf-8", mode: FILE_MODE, flag: "wx" });
62735
+ return true;
62736
+ } catch (err) {
62737
+ if (err.code !== "EEXIST") throw err;
62738
+ try {
62739
+ const raw = await readFile(lockFile, "utf-8");
62740
+ const parsed = JSON.parse(raw);
62741
+ if (typeof parsed.timestamp === "number" && Date.now() - parsed.timestamp > SYNC_LOCK_STALE_MS) {
62742
+ await rm(lockFile).catch(() => {
62743
+ });
62744
+ try {
62745
+ await writeFile(lockFile, content, { encoding: "utf-8", mode: FILE_MODE, flag: "wx" });
62746
+ return true;
62747
+ } catch {
62748
+ return false;
62749
+ }
62750
+ }
62751
+ } catch {
62752
+ }
62753
+ return false;
62754
+ }
62755
+ }
62756
+ async function releaseA2ASyncLock(dataDir, channelId) {
62757
+ try {
62758
+ await rm(syncLockPath(dataDir, channelId));
62759
+ } catch {
62760
+ }
62761
+ }
62725
62762
  function mlsFileName(groupId) {
62726
62763
  const safe = groupId.replace(/[^a-zA-Z0-9_-]/g, "_");
62727
62764
  return `mls-${safe}.json`;
@@ -62734,10 +62771,10 @@ async function hasPendingWelcome(dataDir, groupId) {
62734
62771
  return false;
62735
62772
  }
62736
62773
  }
62737
- async function savePendingKpBundle(dataDir, groupId, kp) {
62774
+ async function savePendingKpBundle(dataDir, groupId, kp, serializePublicFn) {
62738
62775
  await mkdir(dataDir, { recursive: true, mode: DIR_MODE });
62739
62776
  const serialized = {
62740
- publicPackage: Buffer.from(kp.publicPackage).toString("base64"),
62777
+ publicPackageBytes: Buffer.from(serializePublicFn(kp.publicPackage)).toString("base64"),
62741
62778
  privatePackage: {
62742
62779
  initPrivateKey: Buffer.from(kp.privatePackage.initPrivateKey).toString("base64"),
62743
62780
  hpkePrivateKey: Buffer.from(kp.privatePackage.hpkePrivateKey).toString("base64"),
@@ -62751,7 +62788,7 @@ async function loadPendingKpBundle(dataDir, groupId) {
62751
62788
  const raw = await readFile(pendingKpPath(dataDir, groupId), "utf-8");
62752
62789
  const parsed = JSON.parse(raw);
62753
62790
  return {
62754
- publicPackage: new Uint8Array(Buffer.from(parsed.publicPackage, "base64")),
62791
+ publicPackageBytes: new Uint8Array(Buffer.from(parsed.publicPackageBytes, "base64")),
62755
62792
  privatePackage: {
62756
62793
  initPrivateKey: new Uint8Array(Buffer.from(parsed.privatePackage.initPrivateKey, "base64")),
62757
62794
  hpkePrivateKey: new Uint8Array(Buffer.from(parsed.privatePackage.hpkePrivateKey, "base64")),
@@ -62788,12 +62825,13 @@ async function deleteMlsState(dataDir, groupId) {
62788
62825
  } catch {
62789
62826
  }
62790
62827
  }
62791
- var FILE_MODE, DIR_MODE, pendingKpPath;
62828
+ var FILE_MODE, DIR_MODE, SYNC_LOCK_STALE_MS, pendingKpPath;
62792
62829
  var init_mls_state = __esm({
62793
62830
  "src/mls-state.ts"() {
62794
62831
  "use strict";
62795
62832
  FILE_MODE = 384;
62796
62833
  DIR_MODE = 448;
62834
+ SYNC_LOCK_STALE_MS = 5 * 60 * 1e3;
62797
62835
  pendingKpPath = (dataDir, groupId) => join(dataDir, `mls-kp-pending-${groupId.replace(/[^a-zA-Z0-9_-]/g, "_")}.json`);
62798
62836
  }
62799
62837
  });
@@ -65243,6 +65281,10 @@ var init_channel = __esm({
65243
65281
  }
65244
65282
  const memberMlsGroup = chState.mlsGroupId ? this._mlsGroups.get(`a2a:${chId}`) : void 0;
65245
65283
  if (chState.role === "member" && !memberMlsGroup?.isInitialized) {
65284
+ if (!await acquireA2ASyncLock(this.config.dataDir, chId)) {
65285
+ console.log(`[SecureChannel] A2A ${chId.slice(0, 8)} sync: skipping \u2014 another instance holds the lock`);
65286
+ continue;
65287
+ }
65246
65288
  try {
65247
65289
  let groupId = chState.mlsGroupId;
65248
65290
  let memberIds = [];
@@ -65252,11 +65294,15 @@ var init_channel = __esm({
65252
65294
  { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
65253
65295
  );
65254
65296
  if (!groupRes.ok) {
65297
+ await releaseA2ASyncLock(this.config.dataDir, chId);
65255
65298
  continue;
65256
65299
  }
65257
65300
  const groupData = await groupRes.json();
65258
65301
  groupId = groupData.group_id;
65259
- if (!groupId) continue;
65302
+ if (!groupId) {
65303
+ await releaseA2ASyncLock(this.config.dataDir, chId);
65304
+ continue;
65305
+ }
65260
65306
  memberIds = groupData.member_device_ids || [];
65261
65307
  chState.mlsGroupId = groupId;
65262
65308
  await this._persistState();
@@ -65284,7 +65330,7 @@ var init_channel = __esm({
65284
65330
  const identity = new TextEncoder().encode(this._persisted.deviceId);
65285
65331
  const kp = await mgr.generateKeyPackage(identity);
65286
65332
  this._pendingMlsKpBundle = kp;
65287
- await savePendingKpBundle(this.config.dataDir, groupId, kp);
65333
+ await savePendingKpBundle(this.config.dataDir, groupId, kp, MLSGroupManager.serializeKeyPackage);
65288
65334
  const kpBytes = MLSGroupManager.serializeKeyPackage(kp.publicPackage);
65289
65335
  await fetch(`${this.config.apiUrl}/api/v1/mls/key-packages`, {
65290
65336
  method: "POST",
@@ -65309,6 +65355,8 @@ var init_channel = __esm({
65309
65355
  `[SecureChannel] A2A ${chId.slice(0, 8)} member sync fallback: published KeyPackage + requested Welcome (group=${groupId.slice(0, 8)})`
65310
65356
  );
65311
65357
  } catch (err) {
65358
+ await releaseA2ASyncLock(this.config.dataDir, chId).catch(() => {
65359
+ });
65312
65360
  console.warn(`[AgentVault] Sync fallback member MLS join failed for ${chId.slice(0, 8)}:`, err);
65313
65361
  }
65314
65362
  }
@@ -67148,17 +67196,26 @@ ${messageText}`;
67148
67196
  async _handleMlsWelcome(data) {
67149
67197
  const groupId = data.group_id;
67150
67198
  const conversationId = data.conversation_id;
67151
- const persistedKpAvailable = await hasPendingWelcome(this.config.dataDir, groupId);
67152
- const kpSource = this._pendingMlsKpBundle ? "pending" : persistedKpAvailable ? "persisted" : this._mlsKeyPackage ? "connect" : "generated";
67199
+ const kpSource = this._pendingMlsKpBundle ? "pending" : this._mlsKeyPackage ? "connect" : await hasPendingWelcome(this.config.dataDir, groupId) ? "persisted" : "generated";
67153
67200
  console.log(`[SecureChannel] Received MLS welcome for group ${groupId?.slice(0, 8)}${conversationId ? ` conv=${conversationId.slice(0, 8)}` : ""} kpSource=${kpSource}`);
67154
67201
  try {
67155
67202
  const welcomeBytes = new Uint8Array(Buffer.from(data.payload, "hex"));
67156
67203
  const mgr = new MLSGroupManager();
67157
- const persistedKp = await loadPendingKpBundle(this.config.dataDir, groupId);
67158
- const kp = this._pendingMlsKpBundle ?? persistedKp ?? this._mlsKeyPackage ?? await (async () => {
67204
+ let kp = this._pendingMlsKpBundle ?? this._mlsKeyPackage;
67205
+ if (!kp) {
67206
+ const persisted = await loadPendingKpBundle(this.config.dataDir, groupId);
67207
+ if (persisted) {
67208
+ kp = {
67209
+ publicPackage: MLSGroupManager.deserializeKeyPackage(persisted.publicPackageBytes),
67210
+ privatePackage: persisted.privatePackage
67211
+ };
67212
+ console.log(`[SecureChannel] Loaded persisted KP bundle for group ${groupId?.slice(0, 8)}`);
67213
+ }
67214
+ }
67215
+ if (!kp) {
67159
67216
  const identity = new TextEncoder().encode(this._deviceId);
67160
- return mgr.generateKeyPackage(identity);
67161
- })();
67217
+ kp = await mgr.generateKeyPackage(identity);
67218
+ }
67162
67219
  console.log(`[SecureChannel] Welcome joinFromWelcome: group=${groupId?.slice(0, 8)} kpSource=${kpSource} welcomeLen=${welcomeBytes.length}`);
67163
67220
  await mgr.joinFromWelcome(welcomeBytes, kp);
67164
67221
  if (conversationId) {
@@ -67194,6 +67251,8 @@ ${messageText}`;
67194
67251
  await this._persistState();
67195
67252
  await clearPendingWelcome(this.config.dataDir, groupId).catch(() => {
67196
67253
  });
67254
+ await releaseA2ASyncLock(this.config.dataDir, channelId).catch(() => {
67255
+ });
67197
67256
  this._pendingMlsKpBundle = void 0;
67198
67257
  console.log(`[SecureChannel] Joined MLS group for A2A ${channelId.slice(0, 8)} via Welcome (epoch=${mgr.epoch})`);
67199
67258
  if (this.config.onA2AChannelReady && entry.conversationId) {
@@ -67224,11 +67283,16 @@ ${messageText}`;
67224
67283
  this._mlsGroups.set(`a2a:${a2aChannelId}`, mgr);
67225
67284
  await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mgr.exportState()));
67226
67285
  await this._persistState();
67286
+ await releaseA2ASyncLock(this.config.dataDir, a2aChannelId).catch(() => {
67287
+ });
67227
67288
  console.log(`[SecureChannel] Joined MLS group for A2A ${a2aChannelId.slice(0, 8)} via Welcome (new channel, epoch=${mgr.epoch})`);
67228
67289
  return;
67229
67290
  }
67230
67291
  console.warn(`[SecureChannel] MLS welcome for unmatched group ${groupId?.slice(0, 8)} (no room/A2A/conv match)`);
67231
67292
  } catch (err) {
67293
+ const failedA2aId = data.a2a_channel_id;
67294
+ if (failedA2aId) await releaseA2ASyncLock(this.config.dataDir, failedA2aId).catch(() => {
67295
+ });
67232
67296
  console.error(`[SecureChannel] MLS welcome processing failed (kpSource=${kpSource}):`, err);
67233
67297
  throw err;
67234
67298
  }
@@ -94860,7 +94924,7 @@ var init_index = __esm({
94860
94924
  init_skill_invoker();
94861
94925
  await init_skill_telemetry();
94862
94926
  await init_policy_enforcer();
94863
- VERSION = true ? "0.20.26" : "0.0.0-dev";
94927
+ VERSION = true ? "0.20.28" : "0.0.0-dev";
94864
94928
  }
94865
94929
  });
94866
94930
  await init_index();