@agentunion/fastaun-browser 0.5.16 → 0.5.17

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 (58) hide show
  1. package/CHANGELOG.md +309 -274
  2. package/_packed_docs/CHANGELOG.md +309 -274
  3. package/_packed_docs/INDEX.md +8 -2
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +2 -0
  5. package/_packed_docs/agent.md/SCHEMA.md +15 -15
  6. package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1178 -1178
  7. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1213 -1213
  8. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +58 -43
  9. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +80 -17
  10. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +27 -6
  11. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +7 -4
  12. package/_packed_docs/sdk/09-group-rpc-manual.md +167 -35
  13. package/_packed_docs/sdk/09-message-rpc-manual.md +72 -42
  14. package/_packed_docs/sdk/09-storage-rpc-manual.md +23 -14
  15. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +16 -12
  16. package/_packed_docs/sdk/INDEX.md +12 -12
  17. package/dist/agent-md.d.ts +2 -0
  18. package/dist/agent-md.d.ts.map +1 -1
  19. package/dist/agent-md.js +7 -1
  20. package/dist/agent-md.js.map +1 -1
  21. package/dist/bundle.js +2274 -1789
  22. package/dist/client/delivery.d.ts +18 -0
  23. package/dist/client/delivery.d.ts.map +1 -1
  24. package/dist/client/delivery.js +147 -5
  25. package/dist/client/delivery.js.map +1 -1
  26. package/dist/client/group-state.d.ts +4 -0
  27. package/dist/client/group-state.d.ts.map +1 -1
  28. package/dist/client/group-state.js +63 -0
  29. package/dist/client/group-state.js.map +1 -1
  30. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  31. package/dist/client/rpc-pipeline.js +37 -4
  32. package/dist/client/rpc-pipeline.js.map +1 -1
  33. package/dist/client/v2-e2ee.d.ts +7 -1
  34. package/dist/client/v2-e2ee.d.ts.map +1 -1
  35. package/dist/client/v2-e2ee.js +180 -25
  36. package/dist/client/v2-e2ee.js.map +1 -1
  37. package/dist/client.d.ts.map +1 -1
  38. package/dist/client.js +60 -12
  39. package/dist/client.js.map +1 -1
  40. package/dist/facades.d.ts +5 -0
  41. package/dist/facades.d.ts.map +1 -1
  42. package/dist/facades.js +15 -0
  43. package/dist/facades.js.map +1 -1
  44. package/dist/group-fs.d.ts.map +1 -1
  45. package/dist/group-fs.js +8 -5
  46. package/dist/group-fs.js.map +1 -1
  47. package/dist/storage/vfs.d.ts +1 -0
  48. package/dist/storage/vfs.d.ts.map +1 -1
  49. package/dist/storage/vfs.js +68 -5
  50. package/dist/storage/vfs.js.map +1 -1
  51. package/dist/tools/cross-sdk-agent.js +34 -3
  52. package/dist/tools/cross-sdk-agent.js.map +1 -1
  53. package/dist/transport.js +34 -34
  54. package/dist/types.d.ts +16 -1
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/package.json +10 -10
@@ -948,6 +948,12 @@ export class V2E2EECoordinator {
948
948
  await client._publishAppEvent(eventName, safeEvent, 'pending_retry');
949
949
  continue;
950
950
  }
951
+ if (!entry.groupId && String(entry.msg.storage ?? '') === 'volatile') {
952
+ await this.publishPendingV2VolatileMessage(entry.msg, plaintext, 'pending_retry');
953
+ client._v2SenderIKPending.delete(key);
954
+ client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
955
+ continue;
956
+ }
951
957
  client._v2SenderIKPending.delete(key);
952
958
  const seq = Number(entry.msg.seq ?? 0);
953
959
  if (entry.groupId) {
@@ -967,10 +973,35 @@ export class V2E2EECoordinator {
967
973
  client._v2SenderIKFetching.delete(fetchKey);
968
974
  }
969
975
  }
976
+ async publishPendingV2VolatileMessage(rawMessage, plaintext, source) {
977
+ const delivery = this.client._delivery;
978
+ const claim = delivery.beginVolatileDelivery(rawMessage);
979
+ if (claim === 'inflight')
980
+ return;
981
+ if (claim === 'seen') {
982
+ await delivery.ackVolatileMessages([rawMessage], true, false);
983
+ return;
984
+ }
985
+ try {
986
+ const publicMessage = delivery.publicVolatileMessage(plaintext);
987
+ for (const key of ['storage', 'volatile_cursor', 'created_at', 'expires_at', 'persist']) {
988
+ if (key in rawMessage)
989
+ publicMessage[key] = rawMessage[key];
990
+ }
991
+ const appEvent = delivery.p2pAppEventForMessage(publicMessage);
992
+ await this.client._publishAppEvent(appEvent.event, appEvent.payload, source);
993
+ delivery.markVolatileSeen(rawMessage);
994
+ }
995
+ finally {
996
+ delivery.finishVolatileDelivery(rawMessage);
997
+ }
998
+ await delivery.ackVolatileMessages([rawMessage], true, false);
999
+ }
970
1000
  async sendV2(to, payload, opts) {
971
1001
  const client = this.client;
972
1002
  await client._ensureV2SessionReady('message.send', 'V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)');
973
1003
  const toAid = String(to ?? '').trim();
1004
+ const targetDeviceId = String(opts?.deviceId ?? '').trim();
974
1005
  if (!toAid)
975
1006
  throw new ValidationError("message.send requires 'to'");
976
1007
  if (!isJsonObject(payload))
@@ -981,6 +1012,7 @@ export class V2E2EECoordinator {
981
1012
  payload,
982
1013
  messageId: opts?.messageId,
983
1014
  timestamp: opts?.timestamp,
1015
+ deviceId: targetDeviceId || undefined,
984
1016
  protectedHeaders: opts?.protectedHeaders,
985
1017
  context: opts?.context,
986
1018
  useCache,
@@ -989,6 +1021,8 @@ export class V2E2EECoordinator {
989
1021
  to: toAid,
990
1022
  payload: envelope,
991
1023
  message_id: opts?.messageId,
1024
+ ...(opts?.ttl !== undefined ? { ttl: opts.ttl } : {}),
1025
+ ...(targetDeviceId ? { device_id: targetDeviceId } : {}),
992
1026
  encrypt: false,
993
1027
  _skip_send_result_envelope: true,
994
1028
  });
@@ -1232,6 +1266,7 @@ export class V2E2EECoordinator {
1232
1266
  const client = this.client;
1233
1267
  await client._ensureV2SessionReady('message.pull');
1234
1268
  const ns = client._aid ? `p2p:${client._aid}` : '';
1269
+ let volatileCursor = String(opts?.afterVolatileCursor ?? client._delivery.volatilePullCursor() ?? '').trim();
1235
1270
  if (ns)
1236
1271
  client._delivery.onPullStarted?.(ns);
1237
1272
  if (opts?.windowMode === 'tail') {
@@ -1249,7 +1284,7 @@ export class V2E2EECoordinator {
1249
1284
  }
1250
1285
  if (ns && !opts?.gateLocked) {
1251
1286
  const key = pullGateKeyForClient(client, 'message.v2.pull', {
1252
- after_seq: afterSeq, force: opts?.force === true, limit,
1287
+ after_seq: afterSeq, after_volatile_cursor: volatileCursor, force: opts?.force === true, limit,
1253
1288
  }, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
1254
1289
  return await client._runPullSerialized(key, () => this.pullV2Impl(afterSeq, limit, {
1255
1290
  ...(opts ?? {}),
@@ -1260,7 +1295,7 @@ export class V2E2EECoordinator {
1260
1295
  let nextAfterSeq = opts?.force ? afterSeq : (afterSeq || (ns ? client._seqTracker.getContiguousSeq(ns) : 0));
1261
1296
  let pullGateKey = ns
1262
1297
  ? pullGateKeyForClient(client, 'message.v2.pull', {
1263
- after_seq: afterSeq, force: opts?.force === true, limit,
1298
+ after_seq: afterSeq, after_volatile_cursor: volatileCursor, force: opts?.force === true, limit,
1264
1299
  }, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`)
1265
1300
  : '';
1266
1301
  const deferredServerCursor = ns ? this.pendingForwardCursor(ns, client._seqTracker.getContiguousSeq(ns)) : 0;
@@ -1271,16 +1306,30 @@ export class V2E2EECoordinator {
1271
1306
  const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
1272
1307
  while (pageCount < maxPages) {
1273
1308
  pageCount += 1;
1309
+ const volatileCursorBeforePage = volatileCursor;
1274
1310
  const ackUpToSeq = pendingAckSeq;
1275
1311
  const pendingInlineGeneration = ackUpToSeq > 0
1276
1312
  ? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null
1277
1313
  : null;
1278
- const result = await client._callRawV2Rpc('message.v2.pull', {
1279
- after_seq: nextAfterSeq,
1280
- limit,
1281
- ...(opts?.force ? { force: true } : {}),
1282
- ...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
1283
- }, pullGateKey);
1314
+ let result;
1315
+ try {
1316
+ result = await client._callRawV2Rpc('message.v2.pull', {
1317
+ after_seq: nextAfterSeq,
1318
+ limit,
1319
+ ...(volatileCursor ? { after_volatile_cursor: volatileCursor } : {}),
1320
+ ...(opts?.force ? { force: true } : {}),
1321
+ ...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
1322
+ }, pullGateKey);
1323
+ }
1324
+ catch (exc) {
1325
+ const text = exc instanceof Error ? exc.message : String(exc);
1326
+ if (!volatileCursor || !text.toLowerCase().includes('volatile cursor epoch mismatch'))
1327
+ throw exc;
1328
+ client._delivery.resetVolatilePullCursor();
1329
+ volatileCursor = '';
1330
+ pageCount -= 1;
1331
+ continue;
1332
+ }
1284
1333
  if (ackUpToSeq > 0) {
1285
1334
  const actualAckSeq = client._delivery?.resolveP2PPullAckSeq?.(result, ackUpToSeq) ?? 0;
1286
1335
  if (actualAckSeq >= ackUpToSeq) {
@@ -1299,6 +1348,12 @@ export class V2E2EECoordinator {
1299
1348
  }
1300
1349
  }
1301
1350
  const pageMessages = (Array.isArray(result?.messages) ? result.messages : []);
1351
+ const volatileMessages = client._delivery.volatileMessages(pageMessages);
1352
+ if (volatileMessages.length > 0) {
1353
+ decrypted.push(...await this.publishV2VolatileMessages(volatileMessages, 'pull', true));
1354
+ volatileCursor = client._delivery.volatilePullCursor();
1355
+ }
1356
+ const volatileAdvanced = Boolean(volatileCursor) && volatileCursor !== volatileCursorBeforePage;
1302
1357
  const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
1303
1358
  const cursor = isJsonObject(result.cursor) ? result.cursor : null;
1304
1359
  const hasMoreField = Object.prototype.hasOwnProperty.call(result, 'has_more');
@@ -1477,7 +1532,7 @@ export class V2E2EECoordinator {
1477
1532
  const rawCount = messages.length;
1478
1533
  if (blockedSeq > 0)
1479
1534
  break;
1480
- const shouldContinue = shouldContinueForwardPage({
1535
+ let shouldContinue = shouldContinueForwardPage({
1481
1536
  rawCount,
1482
1537
  nextAfterSeq,
1483
1538
  nextAfter,
@@ -1489,11 +1544,16 @@ export class V2E2EECoordinator {
1489
1544
  observedUpper: typeof client._seqTracker.getMaxSeenSeq === 'function'
1490
1545
  ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter,
1491
1546
  });
1547
+ const volatileHasMoreField = Object.prototype.hasOwnProperty.call(result, 'volatile_has_more');
1548
+ const volatileHasMore = result.volatile_has_more === true;
1549
+ if (!shouldContinue && volatileAdvanced && (volatileHasMore || hasMore || (!volatileHasMoreField && limit > 0 && pageMessages.length >= limit))) {
1550
+ shouldContinue = true;
1551
+ }
1492
1552
  if (!shouldContinue)
1493
1553
  break;
1494
1554
  if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
1495
1555
  const nextKey = pullGateKeyForClient(client, 'message.v2.pull', {
1496
- after_seq: nextAfter, force: opts?.force === true, limit,
1556
+ after_seq: nextAfter, after_volatile_cursor: volatileCursor, force: opts?.force === true, limit,
1497
1557
  }, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
1498
1558
  await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
1499
1559
  pullGateKey = nextKey;
@@ -1513,6 +1573,73 @@ export class V2E2EECoordinator {
1513
1573
  client._delivery.onPullWorkSettled?.();
1514
1574
  return deduplicatePulledResults(decrypted);
1515
1575
  }
1576
+ async publishV2VolatileMessages(messages, source, advanceCursor) {
1577
+ const client = this.client;
1578
+ if (!client._v2Session)
1579
+ return [];
1580
+ const processed = [];
1581
+ const plaintextMessages = [];
1582
+ let complete = true;
1583
+ for (const rawMessage of messages) {
1584
+ const claim = client._delivery.beginVolatileDelivery(rawMessage);
1585
+ if (claim === 'seen') {
1586
+ processed.push(rawMessage);
1587
+ continue;
1588
+ }
1589
+ if (claim === 'inflight') {
1590
+ complete = false;
1591
+ continue;
1592
+ }
1593
+ let plaintext;
1594
+ try {
1595
+ if (client._isEncryptedPushMessage(rawMessage)) {
1596
+ plaintext = await client._decryptV2Message(rawMessage, true, true, true, true, undefined, false, source);
1597
+ }
1598
+ else {
1599
+ const legacy = isJsonObject(rawMessage.legacy_v1)
1600
+ ? rawMessage.legacy_v1
1601
+ : {};
1602
+ plaintext = {
1603
+ message_id: String(rawMessage.message_id ?? ''),
1604
+ from: String(rawMessage.from_aid ?? rawMessage.from ?? ''),
1605
+ to: String(legacy.to ?? rawMessage.to ?? client._aid ?? ''),
1606
+ type: String(rawMessage.type ?? ''),
1607
+ timestamp: rawMessage.t_server ?? rawMessage.timestamp,
1608
+ payload: legacy.payload !== undefined ? legacy.payload : rawMessage.payload,
1609
+ encrypted: false,
1610
+ };
1611
+ for (const key of ['context', 'protected_headers', 'headers', 'client_signature']) {
1612
+ if (key in rawMessage)
1613
+ plaintext[key] = rawMessage[key];
1614
+ }
1615
+ if (rawMessage.sender_device_id !== undefined)
1616
+ plaintext.sender_device_id = rawMessage.sender_device_id;
1617
+ attachGatewayProximity(plaintext, rawMessage);
1618
+ }
1619
+ if (!plaintext) {
1620
+ complete = false;
1621
+ continue;
1622
+ }
1623
+ const publicMessage = client._delivery.publicVolatileMessage(plaintext);
1624
+ for (const key of ['storage', 'volatile_cursor', 'created_at', 'expires_at', 'persist']) {
1625
+ if (key in rawMessage)
1626
+ publicMessage[key] = rawMessage[key];
1627
+ }
1628
+ const appEvent = client._delivery.p2pAppEventForMessage(publicMessage);
1629
+ await client._publishAppEvent(appEvent.event, appEvent.payload, source);
1630
+ client._delivery.markVolatileSeen(rawMessage);
1631
+ processed.push(rawMessage);
1632
+ plaintextMessages.push(publicMessage);
1633
+ }
1634
+ finally {
1635
+ client._delivery.finishVolatileDelivery(rawMessage);
1636
+ }
1637
+ }
1638
+ if (processed.length > 0) {
1639
+ await client._delivery.ackVolatileMessages(processed, true, advanceCursor && complete && processed.length === messages.length);
1640
+ }
1641
+ return plaintextMessages;
1642
+ }
1516
1643
  async confirmForwardP2PAck(upToSeq, inlineGeneration) {
1517
1644
  const ns = this.client._aid ? `p2p:${this.client._aid}` : '';
1518
1645
  try {
@@ -1528,41 +1655,51 @@ export class V2E2EECoordinator {
1528
1655
  throw exc;
1529
1656
  }
1530
1657
  }
1531
- async ackV2(upToSeq, inlineGeneration) {
1532
- return this.ackV2WithConfirmation(upToSeq, inlineGeneration, false);
1658
+ async ackV2(upToSeq, inlineGeneration, volatileCursors = [], volatileIds = []) {
1659
+ return this.ackV2WithConfirmation(upToSeq, inlineGeneration, false, volatileCursors, volatileIds);
1533
1660
  }
1534
1661
  async ackV2ForForward(upToSeq, inlineGeneration) {
1535
1662
  return this.ackV2WithConfirmation(upToSeq, inlineGeneration, true);
1536
1663
  }
1537
- async ackV2WithConfirmation(upToSeq, inlineGeneration, requireServerWatermark) {
1664
+ async ackV2WithConfirmation(upToSeq, inlineGeneration, requireServerWatermark, volatileCursors = [], volatileIds = []) {
1538
1665
  const client = this.client;
1539
1666
  const ns = client._aid ? `p2p:${client._aid}` : '';
1540
1667
  let seq = upToSeq ?? (ns ? client._seqTracker.getContiguousSeq(ns) : 0);
1541
1668
  if (typeof seq !== 'number' || !Number.isSafeInteger(seq) || seq <= 0)
1669
+ seq = 0;
1670
+ if (seq <= 0 && volatileCursors.length === 0 && volatileIds.length === 0)
1542
1671
  return { acked: 0 };
1543
- seq = client._clampAckSeq('message.v2.ack', 'up_to_seq', ns, seq);
1544
- if (!Number.isSafeInteger(seq) || seq <= 0)
1672
+ if (seq > 0)
1673
+ seq = client._clampAckSeq('message.v2.ack', 'up_to_seq', ns, seq);
1674
+ if ((!Number.isSafeInteger(seq) || seq <= 0) && volatileCursors.length === 0 && volatileIds.length === 0) {
1545
1675
  return { acked: 0 };
1676
+ }
1546
1677
  // ACK 发出前绑定当时的 inline pending;迟到响应不得结算重置后新建的 pending。
1547
1678
  const ackGeneration = inlineGeneration ?? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null;
1548
1679
  const v2SessionAtStart = client._v2Session;
1549
1680
  const v2RuntimeGenerationAtStart = Number(client._v2RuntimeGeneration ?? 0);
1550
- const raw = await client._callRawV2Rpc('message.v2.ack', { up_to_seq: seq });
1681
+ const raw = await client._callRawV2Rpc('message.v2.ack', {
1682
+ ...(seq > 0 ? { up_to_seq: seq } : {}),
1683
+ ...(volatileCursors.length > 0 ? { volatile_cursors: volatileCursors } : {}),
1684
+ ...(volatileIds.length > 0 ? { volatile_ids: volatileIds } : {}),
1685
+ });
1551
1686
  const result = isJsonObject(raw)
1552
1687
  ? { ...raw }
1553
1688
  : { result: raw };
1554
- const actualAckSeq = requireServerWatermark
1555
- ? (client._delivery?.resolveForwardAckSeq?.(result, seq) ?? 0)
1556
- : (client._delivery?.resolveActualAckSeq?.(result, seq) ?? seq);
1557
- if (requireServerWatermark && actualAckSeq < seq) {
1689
+ const actualAckSeq = seq > 0
1690
+ ? (requireServerWatermark
1691
+ ? (client._delivery?.resolveForwardAckSeq?.(result, seq) ?? 0)
1692
+ : (client._delivery?.resolveActualAckSeq?.(result, seq) ?? seq))
1693
+ : 0;
1694
+ if (seq > 0 && requireServerWatermark && actualAckSeq < seq) {
1558
1695
  throw new E2EEError(`message.v2.ack server ACK watermark ${actualAckSeq} is below requested ${seq}`);
1559
1696
  }
1560
1697
  result.ack_seq = actualAckSeq;
1561
1698
  result.success = true;
1562
- if (ackGeneration !== null) {
1699
+ if (seq > 0 && ackGeneration !== null) {
1563
1700
  client._delivery?.completePendingP2PInlineAckUpTo?.(ns, actualAckSeq, ackGeneration);
1564
1701
  }
1565
- if (v2SessionAtStart
1702
+ if (seq > 0 && v2SessionAtStart
1566
1703
  && client._v2Session === v2SessionAtStart
1567
1704
  && Number(client._v2RuntimeGeneration ?? 0) === v2RuntimeGenerationAtStart) {
1568
1705
  try {
@@ -2546,7 +2683,6 @@ export class V2E2EECoordinator {
2546
2683
  seq: msg.seq ?? null,
2547
2684
  timestamp: msg.timestamp ?? msg.t_server ?? null,
2548
2685
  device_id: msg.device_id ?? null,
2549
- slot_id: msg.slot_id ?? null,
2550
2686
  _decrypt_error: 'encrypted push payload is not decryptable on raw push path',
2551
2687
  _decrypt_stage: 'push_envelope',
2552
2688
  };
@@ -2590,13 +2726,14 @@ export class V2E2EECoordinator {
2590
2726
  encrypted: true,
2591
2727
  e2ee: e2eeMeta,
2592
2728
  };
2729
+ const senderDeviceId = String(aad.from_device ?? '').trim();
2730
+ if (senderDeviceId)
2731
+ result.sender_device_id = senderDeviceId;
2593
2732
  result.direction = fromAid && fromAid === client._aid ? 'outbound_sync' : 'inbound';
2594
2733
  if (msg.t_server !== undefined)
2595
2734
  result.t_server = msg.t_server;
2596
2735
  if (msg.device_id !== undefined)
2597
2736
  result.device_id = msg.device_id;
2598
- if (msg.slot_id !== undefined)
2599
- result.slot_id = msg.slot_id;
2600
2737
  attachGatewayProximity(result, msg);
2601
2738
  if (group) {
2602
2739
  result.group_id = msg.group_id ?? aad.group_id ?? envelope.group_id ?? null;
@@ -2674,6 +2811,10 @@ export class V2E2EECoordinator {
2674
2811
  if (peerDevices.length === 0) {
2675
2812
  throw new E2EEError(`V2 bootstrap: no devices found for ${to}`);
2676
2813
  }
2814
+ const targetDeviceId = String(opts.deviceId ?? '').trim();
2815
+ if (targetDeviceId && !peerDevices.some((dev) => String(dev.device_id ?? '').trim() === targetDeviceId)) {
2816
+ throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
2817
+ }
2677
2818
  let targetSet = useCache ? this.getTargetSetCache(to) : undefined;
2678
2819
  if (targetSet) {
2679
2820
  targetSet = this.cloneP2PTargetSet(targetSet);
@@ -2751,6 +2892,20 @@ export class V2E2EECoordinator {
2751
2892
  });
2752
2893
  targetSet = this.cloneP2PTargetSet(targetSet);
2753
2894
  }
2895
+ if (targetDeviceId) {
2896
+ let peerTargetFound = false;
2897
+ targetSet.targets = targetSet.targets.filter((target) => {
2898
+ const isPeerTarget = target.aid === to
2899
+ && (target.deviceId === targetDeviceId || (wrapPolicy?.scope === 'aid' && !target.deviceId))
2900
+ && target.role !== 'self_sync';
2901
+ if (isPeerTarget)
2902
+ peerTargetFound = true;
2903
+ return isPeerTarget || target.role === 'self_sync';
2904
+ });
2905
+ if (!peerTargetFound) {
2906
+ throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
2907
+ }
2908
+ }
2754
2909
  const sender = await session.getSenderIdentity();
2755
2910
  const envelope = await encryptP2PMessage(sender, targetSet, opts.payload, { messageId: opts.messageId, timestamp: opts.timestamp, protectedHeaders: opts.protectedHeaders, context: opts.context });
2756
2911
  return envelope;