@agentvault/muse-bridge 0.1.33 → 0.1.34

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.
Files changed (2) hide show
  1. package/dist/index.js +78 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11799,11 +11799,11 @@ async function joinWithFirstMatchingKp(welcomeBytes, candidates) {
11799
11799
  return null;
11800
11800
  }
11801
11801
  function ownIdentity() {
11802
- const v2 = true ? "0.23.60" : FALLBACK;
11802
+ const v2 = true ? "0.23.61" : FALLBACK;
11803
11803
  return `${PACKAGE}@${v2}`;
11804
11804
  }
11805
11805
  function buildSha() {
11806
- const raw = true ? "628e3ba" : "";
11806
+ const raw = true ? "129e537" : "";
11807
11807
  const cleaned = (raw ?? "").trim();
11808
11808
  return !cleaned || cleaned === "unknown" ? null : cleaned;
11809
11809
  }
@@ -71707,6 +71707,17 @@ var init_dist = __esm({
71707
71707
  * self-heal rather than be logged and dropped. */
71708
71708
  _mlsCommitFailCounts = /* @__PURE__ */ new Map();
71709
71709
  static MAX_MLS_DECRYPT_FAILS = 3;
71710
+ /**
71711
+ * #1518: ids of MLS application frames this process has already decrypted
71712
+ * (server-assigned `mls_messages.id`, the same on the live delivery pull and
71713
+ * on a `/sync` / reconnect replay). A replayed frame cannot be decrypted a
71714
+ * second time — forward secrecy deleted its key — so without this every
71715
+ * reconnect after a backend deploy fed already-read messages into the
71716
+ * decrypt-failure counter and re-admitted the agent (+2 epochs). Insertion
71717
+ * ordered; the oldest ids fall out past DECRYPTED_ID_CAP.
71718
+ */
71719
+ _decryptedMessageIds = /* @__PURE__ */ new Map();
71720
+ static DECRYPTED_ID_CAP = 5e3;
71710
71721
  /**
71711
71722
  * #1084: consecutive resyncs that found NOTHING to apply, per group.
71712
71723
  *
@@ -73912,7 +73923,7 @@ ${text}`,
73912
73923
  */
73913
73924
  sendActivitySpan(spanData) {
73914
73925
  if (!this._ws || this._ws.readyState !== WebSocket.OPEN) return;
73915
- const pluginVersion = true ? "0.23.60" : "0.0.0-dev";
73926
+ const pluginVersion = true ? "0.23.61" : "0.0.0-dev";
73916
73927
  const agentName = this.config.agentName ?? "Agent";
73917
73928
  const resource = {
73918
73929
  "service.name": "agentvault-agent",
@@ -76122,7 +76133,7 @@ ${text}`,
76122
76133
  agentVersion: this.config.agentVersion ?? "0.0.0",
76123
76134
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
76124
76135
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
76125
- pluginVersion: true ? "0.23.60" : "0.0.0-dev"
76136
+ pluginVersion: true ? "0.23.61" : "0.0.0-dev"
76126
76137
  });
76127
76138
  this._telemetryReporter.startAutoFlush(3e4);
76128
76139
  }
@@ -76454,7 +76465,7 @@ ${text}`,
76454
76465
  agentVersion: this.config.agentVersion ?? "0.0.0",
76455
76466
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
76456
76467
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
76457
- pluginVersion: true ? "0.23.60" : "0.0.0-dev"
76468
+ pluginVersion: true ? "0.23.61" : "0.0.0-dev"
76458
76469
  });
76459
76470
  this._telemetryReporter.startAutoFlush(3e4);
76460
76471
  }
@@ -76890,9 +76901,14 @@ ${text}`,
76890
76901
  if (groupId) void this._requestWelcomeSelfHeal(groupId);
76891
76902
  return;
76892
76903
  }
76904
+ if (this._alreadyDecrypted(data.message_id)) {
76905
+ console.debug(`[SecureChannel] MLS 1:1 frame ${String(data.message_id).slice(0, 8)} already read \u2014 replay skipped`);
76906
+ return;
76907
+ }
76893
76908
  try {
76894
76909
  const cipherBytes = new Uint8Array(Buffer.from(data.payload, "hex"));
76895
76910
  const result = await mgr.decrypt(cipherBytes);
76911
+ this._rememberDecrypted(data.message_id);
76896
76912
  if (mgrKey) this._mlsDecryptFailCounts.delete(mgrKey);
76897
76913
  const mlsGroupId = (convGroupId ? this._persisted?.mlsGroups?.[convGroupId]?.mlsGroupId : null) ?? this._persisted?.mlsConversations?.[convId]?.mlsGroupId ?? groupId;
76898
76914
  if (mlsGroupId) {
@@ -77915,9 +77931,14 @@ ${baseText}`;
77915
77931
  }
77916
77932
  return;
77917
77933
  }
77934
+ if (this._alreadyDecrypted(data.message_id)) {
77935
+ console.debug(`[SecureChannel] MLS room frame ${String(data.message_id).slice(0, 8)} already read \u2014 replay skipped`);
77936
+ return;
77937
+ }
77918
77938
  try {
77919
77939
  const ciphertext = Buffer.from(data.payload, "hex");
77920
77940
  const result = await mlsGroup.decrypt(new Uint8Array(ciphertext));
77941
+ this._rememberDecrypted(data.message_id);
77921
77942
  this._mlsDecryptFailCounts.delete(resolvedRoomId);
77922
77943
  await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mlsGroup.exportState()));
77923
77944
  if (result.isHandshake || !result.plaintext) {
@@ -79095,16 +79116,57 @@ ${baseText}`;
79095
79116
  }
79096
79117
  return await mlsGroup.encrypt(plaintextBytes);
79097
79118
  }
79119
+ /** #1518: is the server answering at all? Bounded; any failure is "no". */
79120
+ async _serverReachable() {
79121
+ try {
79122
+ const res = await fetch(`${this.config.apiUrl}/health`, { signal: AbortSignal.timeout(5e3) });
79123
+ return res.ok;
79124
+ } catch {
79125
+ return false;
79126
+ }
79127
+ }
79128
+ /** #1518: the server is down — don't re-key (that needs the same server).
79129
+ * Clear the run so the next failures ask again. Reported as handled. */
79130
+ _deferResync(groupKey, groupId, why) {
79131
+ this._mlsDecryptFailCounts.delete(groupKey);
79132
+ console.warn(
79133
+ `[SecureChannel] Resync for ${groupId.slice(0, 8)}: server unreachable (${why}) \u2014 NOT re-keying; will retry (#1518)`
79134
+ );
79135
+ return true;
79136
+ }
79137
+ /** #1518: has this process already decrypted the frame with this id? */
79138
+ _alreadyDecrypted(messageId) {
79139
+ return typeof messageId === "string" && messageId !== "" && this._decryptedMessageIds.has(messageId);
79140
+ }
79141
+ /** #1518: remember a decrypted frame's id (bounded; oldest dropped). */
79142
+ _rememberDecrypted(messageId) {
79143
+ if (typeof messageId !== "string" || messageId === "") return;
79144
+ this._decryptedMessageIds.set(messageId, true);
79145
+ if (this._decryptedMessageIds.size > _SecureChannel.DECRYPTED_ID_CAP) {
79146
+ const oldest = this._decryptedMessageIds.keys().next().value;
79147
+ if (oldest !== void 0) this._decryptedMessageIds.delete(oldest);
79148
+ }
79149
+ }
79098
79150
  async _resyncOnDivergence(groupKey, groupId) {
79099
79151
  const mgr = this._mlsGroups.get(groupKey);
79100
79152
  if (!mgr?.isInitialized) return false;
79101
79153
  const fromEpoch = Number(mgr.epoch);
79102
79154
  try {
79103
- const res = await fetch(
79104
- `${this.config.apiUrl}/api/v1/mls/groups/${groupId}/sync?last_epoch=${fromEpoch}`,
79105
- { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
79106
- );
79155
+ let res;
79156
+ try {
79157
+ res = await fetch(
79158
+ `${this.config.apiUrl}/api/v1/mls/groups/${groupId}/sync?last_epoch=${fromEpoch}`,
79159
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
79160
+ );
79161
+ } catch (netErr) {
79162
+ if (!await this._serverReachable()) return this._deferResync(groupKey, groupId, String(netErr));
79163
+ console.warn(`[SecureChannel] Resync-on-divergence failed for ${groupId.slice(0, 8)}:`, netErr);
79164
+ return false;
79165
+ }
79107
79166
  if (!res.ok) {
79167
+ if ((res.status >= 500 || res.status === 429) && !await this._serverReachable()) {
79168
+ return this._deferResync(groupKey, groupId, `HTTP ${res.status}`);
79169
+ }
79108
79170
  console.warn(`[SecureChannel] Resync /sync failed for ${groupId.slice(0, 8)}: ${res.status}`);
79109
79171
  return false;
79110
79172
  }
@@ -80219,9 +80281,14 @@ ${baseText}`;
80219
80281
  console.warn(`[SecureChannel] MLS group not loaded for A2A ${a2aChannelId.slice(0, 8)}`);
80220
80282
  return;
80221
80283
  }
80284
+ if (this._alreadyDecrypted(data.message_id)) {
80285
+ console.debug(`[SecureChannel] MLS A2A frame ${String(data.message_id).slice(0, 8)} already read \u2014 replay skipped`);
80286
+ return;
80287
+ }
80222
80288
  try {
80223
80289
  const ciphertext = new Uint8Array(Buffer.from(data.payload, "hex"));
80224
80290
  const result = await mlsGroup.decrypt(ciphertext);
80291
+ this._rememberDecrypted(data.message_id);
80225
80292
  this._mlsDecryptFailCounts.delete(`a2a:${a2aChannelId}`);
80226
80293
  await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mlsGroup.exportState()));
80227
80294
  if (result.isHandshake || !result.plaintext) {
@@ -102872,7 +102939,7 @@ data:
102872
102939
  await init_policy_enforcer();
102873
102940
  init_room_protocol();
102874
102941
  init_tool_telemetry();
102875
- VERSION = true ? "0.23.60" : "0.0.0-dev";
102942
+ VERSION = true ? "0.23.61" : "0.0.0-dev";
102876
102943
  }
102877
102944
  });
102878
102945
  await init_index();
@@ -105070,7 +105137,7 @@ function mkdir700(path2) {
105070
105137
  chmodSync5(path2, 448);
105071
105138
  }
105072
105139
  function packageVersion() {
105073
- return true ? "0.1.33" : void 0;
105140
+ return true ? "0.1.34" : void 0;
105074
105141
  }
105075
105142
  function helpText(version2) {
105076
105143
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvault/muse-bridge",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "type": "module",
5
5
  "description": "AgentVault Muse Bridge — daemon bridging a Muse agent into E2E-encrypted AgentVault direct messages and rooms.",
6
6
  "main": "dist/index.js",