@voidly/agent-sdk 3.3.0 → 3.3.1

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.d.ts CHANGED
@@ -364,7 +364,11 @@ declare class VoidlyAgent {
364
364
  messageType?: string;
365
365
  unreadOnly?: boolean;
366
366
  }): Promise<DecryptedMessage[]>;
367
- /** Decrypt raw message objects (shared by receive(), SSE, WebSocket transports) */
367
+ /** Decrypt raw message objects (shared by receive(), SSE, WebSocket transports)
368
+ * Returns decrypted messages AND IDs of messages that failed to decrypt.
369
+ * Failed messages should be marked as read on the relay — they are permanently
370
+ * undecryptable and will poison the queue if left unread (Signal-style handling).
371
+ */
368
372
  private _decryptMessages;
369
373
  /**
370
374
  * Delete a message by ID (must be sender or recipient).
package/dist/index.js CHANGED
@@ -4220,6 +4220,7 @@ var VoidlyAgent = class _VoidlyAgent {
4220
4220
  } catch {
4221
4221
  }
4222
4222
  }
4223
+ const effectiveConfig = !mlkemSk && config?.postQuantum !== false ? { ...config, postQuantum: false } : config;
4223
4224
  const agent = new _VoidlyAgent({
4224
4225
  did: creds.did,
4225
4226
  apiKey: creds.apiKey,
@@ -4230,7 +4231,7 @@ var VoidlyAgent = class _VoidlyAgent {
4230
4231
  },
4231
4232
  mlkemPublicKey: mlkemPk,
4232
4233
  mlkemSecretKey: mlkemSk
4233
- }, config);
4234
+ }, effectiveConfig);
4234
4235
  if (creds.ratchetStates) {
4235
4236
  for (const [pairId, rs] of Object.entries(creds.ratchetStates)) {
4236
4237
  try {
@@ -4784,16 +4785,37 @@ var VoidlyAgent = class _VoidlyAgent {
4784
4785
  throw new Error(`Receive failed: ${err.error?.message || err.error || res.statusText}`);
4785
4786
  }
4786
4787
  const data = await res.json();
4787
- return this._decryptMessages(data.messages);
4788
+ const { decrypted, failedIds } = await this._decryptMessages(data.messages);
4789
+ if (failedIds.length > 0) {
4790
+ try {
4791
+ await this.markReadBatch(failedIds);
4792
+ } catch {
4793
+ for (const id of failedIds) {
4794
+ try {
4795
+ await this.markRead(id);
4796
+ } catch {
4797
+ }
4798
+ }
4799
+ }
4800
+ }
4801
+ return decrypted;
4788
4802
  }
4789
- /** Decrypt raw message objects (shared by receive(), SSE, WebSocket transports) */
4803
+ /** Decrypt raw message objects (shared by receive(), SSE, WebSocket transports)
4804
+ * Returns decrypted messages AND IDs of messages that failed to decrypt.
4805
+ * Failed messages should be marked as read on the relay — they are permanently
4806
+ * undecryptable and will poison the queue if left unread (Signal-style handling).
4807
+ */
4790
4808
  async _decryptMessages(rawMessages) {
4791
4809
  const decrypted = [];
4810
+ const failedIds = [];
4792
4811
  const resetPeers = /* @__PURE__ */ new Set();
4793
4812
  for (const msg of rawMessages) {
4794
4813
  try {
4795
4814
  if (this._seenMessageIds.has(msg.id)) continue;
4796
- if (resetPeers.has(msg.from)) continue;
4815
+ if (resetPeers.has(msg.from)) {
4816
+ failedIds.push(msg.id);
4817
+ continue;
4818
+ }
4797
4819
  let senderEncPub;
4798
4820
  let senderSignPubBytes = null;
4799
4821
  if (msg.sender_encryption_key) {
@@ -4929,6 +4951,7 @@ var VoidlyAgent = class _VoidlyAgent {
4929
4951
  const skip = targetStep - state.recvStep;
4930
4952
  if (skip > MAX_SKIP) {
4931
4953
  this._decryptFailCount++;
4954
+ failedIds.push(msg.id);
4932
4955
  const peerForSkip = msg.from || "unknown";
4933
4956
  const prevSkipFails = this._peerDecryptFails.get(peerForSkip) || 0;
4934
4957
  this._peerDecryptFails.set(peerForSkip, prevSkipFails + 1);
@@ -4964,6 +4987,7 @@ var VoidlyAgent = class _VoidlyAgent {
4964
4987
  }
4965
4988
  if (!rawPlaintext) {
4966
4989
  this._decryptFailCount++;
4990
+ failedIds.push(msg.id);
4967
4991
  const senderForFail = msg.from || "unknown";
4968
4992
  const prevFails = this._peerDecryptFails.get(senderForFail) || 0;
4969
4993
  this._peerDecryptFails.set(senderForFail, prevFails + 1);
@@ -5044,6 +5068,7 @@ var VoidlyAgent = class _VoidlyAgent {
5044
5068
  }
5045
5069
  if (this.requireSignatures && !signatureValid) {
5046
5070
  this._decryptFailCount++;
5071
+ failedIds.push(msg.id);
5047
5072
  const peerForSig = msg.from || "unknown";
5048
5073
  const prevSigFails = this._peerDecryptFails.get(peerForSig) || 0;
5049
5074
  this._peerDecryptFails.set(peerForSig, prevSigFails + 1);
@@ -5073,6 +5098,7 @@ var VoidlyAgent = class _VoidlyAgent {
5073
5098
  });
5074
5099
  } catch {
5075
5100
  this._decryptFailCount++;
5101
+ failedIds.push(msg.id);
5076
5102
  const peerForCatch = msg.from || "unknown";
5077
5103
  const prevCatchFails = this._peerDecryptFails.get(peerForCatch) || 0;
5078
5104
  this._peerDecryptFails.set(peerForCatch, prevCatchFails + 1);
@@ -5087,7 +5113,7 @@ var VoidlyAgent = class _VoidlyAgent {
5087
5113
  this._persistRatchetState().catch(() => {
5088
5114
  });
5089
5115
  }
5090
- return decrypted;
5116
+ return { decrypted, failedIds };
5091
5117
  }
5092
5118
  // ─── Message Management ─────────────────────────────────────────────────────
5093
5119
  /**
@@ -6443,7 +6469,13 @@ var VoidlyAgent = class _VoidlyAgent {
6443
6469
  if (eventType === "message" && dataStr) {
6444
6470
  try {
6445
6471
  const rawMsg = JSON.parse(dataStr);
6446
- const decrypted = await this._decryptMessages([rawMsg]);
6472
+ const { decrypted, failedIds } = await this._decryptMessages([rawMsg]);
6473
+ for (const id of failedIds) {
6474
+ try {
6475
+ await this.markRead(id);
6476
+ } catch {
6477
+ }
6478
+ }
6447
6479
  if (decrypted.length > 0) {
6448
6480
  consecutiveEmpty = 0;
6449
6481
  sseFailures = 0;
package/dist/index.mjs CHANGED
@@ -4210,6 +4210,7 @@ var VoidlyAgent = class _VoidlyAgent {
4210
4210
  } catch {
4211
4211
  }
4212
4212
  }
4213
+ const effectiveConfig = !mlkemSk && config?.postQuantum !== false ? { ...config, postQuantum: false } : config;
4213
4214
  const agent = new _VoidlyAgent({
4214
4215
  did: creds.did,
4215
4216
  apiKey: creds.apiKey,
@@ -4220,7 +4221,7 @@ var VoidlyAgent = class _VoidlyAgent {
4220
4221
  },
4221
4222
  mlkemPublicKey: mlkemPk,
4222
4223
  mlkemSecretKey: mlkemSk
4223
- }, config);
4224
+ }, effectiveConfig);
4224
4225
  if (creds.ratchetStates) {
4225
4226
  for (const [pairId, rs] of Object.entries(creds.ratchetStates)) {
4226
4227
  try {
@@ -4774,16 +4775,37 @@ var VoidlyAgent = class _VoidlyAgent {
4774
4775
  throw new Error(`Receive failed: ${err.error?.message || err.error || res.statusText}`);
4775
4776
  }
4776
4777
  const data = await res.json();
4777
- return this._decryptMessages(data.messages);
4778
+ const { decrypted, failedIds } = await this._decryptMessages(data.messages);
4779
+ if (failedIds.length > 0) {
4780
+ try {
4781
+ await this.markReadBatch(failedIds);
4782
+ } catch {
4783
+ for (const id of failedIds) {
4784
+ try {
4785
+ await this.markRead(id);
4786
+ } catch {
4787
+ }
4788
+ }
4789
+ }
4790
+ }
4791
+ return decrypted;
4778
4792
  }
4779
- /** Decrypt raw message objects (shared by receive(), SSE, WebSocket transports) */
4793
+ /** Decrypt raw message objects (shared by receive(), SSE, WebSocket transports)
4794
+ * Returns decrypted messages AND IDs of messages that failed to decrypt.
4795
+ * Failed messages should be marked as read on the relay — they are permanently
4796
+ * undecryptable and will poison the queue if left unread (Signal-style handling).
4797
+ */
4780
4798
  async _decryptMessages(rawMessages) {
4781
4799
  const decrypted = [];
4800
+ const failedIds = [];
4782
4801
  const resetPeers = /* @__PURE__ */ new Set();
4783
4802
  for (const msg of rawMessages) {
4784
4803
  try {
4785
4804
  if (this._seenMessageIds.has(msg.id)) continue;
4786
- if (resetPeers.has(msg.from)) continue;
4805
+ if (resetPeers.has(msg.from)) {
4806
+ failedIds.push(msg.id);
4807
+ continue;
4808
+ }
4787
4809
  let senderEncPub;
4788
4810
  let senderSignPubBytes = null;
4789
4811
  if (msg.sender_encryption_key) {
@@ -4919,6 +4941,7 @@ var VoidlyAgent = class _VoidlyAgent {
4919
4941
  const skip = targetStep - state.recvStep;
4920
4942
  if (skip > MAX_SKIP) {
4921
4943
  this._decryptFailCount++;
4944
+ failedIds.push(msg.id);
4922
4945
  const peerForSkip = msg.from || "unknown";
4923
4946
  const prevSkipFails = this._peerDecryptFails.get(peerForSkip) || 0;
4924
4947
  this._peerDecryptFails.set(peerForSkip, prevSkipFails + 1);
@@ -4954,6 +4977,7 @@ var VoidlyAgent = class _VoidlyAgent {
4954
4977
  }
4955
4978
  if (!rawPlaintext) {
4956
4979
  this._decryptFailCount++;
4980
+ failedIds.push(msg.id);
4957
4981
  const senderForFail = msg.from || "unknown";
4958
4982
  const prevFails = this._peerDecryptFails.get(senderForFail) || 0;
4959
4983
  this._peerDecryptFails.set(senderForFail, prevFails + 1);
@@ -5034,6 +5058,7 @@ var VoidlyAgent = class _VoidlyAgent {
5034
5058
  }
5035
5059
  if (this.requireSignatures && !signatureValid) {
5036
5060
  this._decryptFailCount++;
5061
+ failedIds.push(msg.id);
5037
5062
  const peerForSig = msg.from || "unknown";
5038
5063
  const prevSigFails = this._peerDecryptFails.get(peerForSig) || 0;
5039
5064
  this._peerDecryptFails.set(peerForSig, prevSigFails + 1);
@@ -5063,6 +5088,7 @@ var VoidlyAgent = class _VoidlyAgent {
5063
5088
  });
5064
5089
  } catch {
5065
5090
  this._decryptFailCount++;
5091
+ failedIds.push(msg.id);
5066
5092
  const peerForCatch = msg.from || "unknown";
5067
5093
  const prevCatchFails = this._peerDecryptFails.get(peerForCatch) || 0;
5068
5094
  this._peerDecryptFails.set(peerForCatch, prevCatchFails + 1);
@@ -5077,7 +5103,7 @@ var VoidlyAgent = class _VoidlyAgent {
5077
5103
  this._persistRatchetState().catch(() => {
5078
5104
  });
5079
5105
  }
5080
- return decrypted;
5106
+ return { decrypted, failedIds };
5081
5107
  }
5082
5108
  // ─── Message Management ─────────────────────────────────────────────────────
5083
5109
  /**
@@ -6433,7 +6459,13 @@ var VoidlyAgent = class _VoidlyAgent {
6433
6459
  if (eventType === "message" && dataStr) {
6434
6460
  try {
6435
6461
  const rawMsg = JSON.parse(dataStr);
6436
- const decrypted = await this._decryptMessages([rawMsg]);
6462
+ const { decrypted, failedIds } = await this._decryptMessages([rawMsg]);
6463
+ for (const id of failedIds) {
6464
+ try {
6465
+ await this.markRead(id);
6466
+ } catch {
6467
+ }
6468
+ }
6437
6469
  if (decrypted.length > 0) {
6438
6470
  consecutiveEmpty = 0;
6439
6471
  sseFailures = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidly/agent-sdk",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "E2E encrypted agent-to-agent communication SDK — Double Ratchet, X3DH, deniable auth, ML-KEM-768 post-quantum, SSE streaming, ratchet persistence, multi-relay federation",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",