@agentunion/fastaun 0.5.19 → 0.5.20

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 (99) hide show
  1. package/CHANGELOG.md +129 -78
  2. package/_packed_docs/CHANGELOG.md +129 -78
  3. package/_packed_docs/INDEX.md +201 -195
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +93 -90
  5. package/_packed_docs/agent.md/SCHEMA.md +7 -7
  6. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +11 -11
  7. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1072 -1072
  8. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +676 -676
  9. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +8 -12
  10. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +443 -0
  11. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
  12. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +658 -0
  13. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
  14. package/_packed_docs/protocol/agent.md/SCHEMA.md +1 -1
  15. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +1 -1
  16. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +67 -63
  17. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
  18. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +35 -8
  19. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
  20. package/_packed_docs/sdk/09-group-rpc-manual.md +258 -254
  21. package/_packed_docs/sdk/09-message-rpc-manual.md +7 -4
  22. package/_packed_docs/sdk/09-storage-rpc-manual.md +18 -26
  23. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +61 -52
  24. package/_packed_docs/sdk/INDEX.md +92 -80
  25. package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
  26. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
  27. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +922 -0
  28. package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
  29. package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +300 -0
  30. package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +382 -0
  31. package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -0
  32. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +476 -0
  33. package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
  34. package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
  35. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +173 -0
  36. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +211 -0
  37. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/345/256/236/347/216/260/347/247/273/346/244/215/346/214/207/345/215/227.md +331 -0
  38. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +207 -0
  39. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/344/273/243/347/240/201/345/256/241/346/237/245/346/212/245/345/221/212.md +252 -0
  40. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/345/256/236/346/226/275/350/256/241/345/210/222.md +156 -0
  41. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/346/226/271/346/241/210.md +273 -0
  42. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/350/257/246/347/273/206/350/256/276/350/256/241.md +211 -0
  43. package/dist/agent-md-schema.d.ts +13 -1
  44. package/dist/agent-md-schema.js +61 -33
  45. package/dist/agent-md-schema.js.map +1 -1
  46. package/dist/agent-md.d.ts +8 -1
  47. package/dist/agent-md.js +243 -119
  48. package/dist/agent-md.js.map +1 -1
  49. package/dist/aid-store.d.ts +29 -0
  50. package/dist/aid-store.js +179 -34
  51. package/dist/aid-store.js.map +1 -1
  52. package/dist/aid.js +18 -13
  53. package/dist/aid.js.map +1 -1
  54. package/dist/auth.js +74 -2
  55. package/dist/auth.js.map +1 -1
  56. package/dist/cert-utils.d.ts +43 -1
  57. package/dist/cert-utils.js +144 -56
  58. package/dist/cert-utils.js.map +1 -1
  59. package/dist/client/delivery.d.ts +9 -0
  60. package/dist/client/delivery.js +179 -11
  61. package/dist/client/delivery.js.map +1 -1
  62. package/dist/client/rpc-pipeline.d.ts +1 -0
  63. package/dist/client/rpc-pipeline.js +125 -82
  64. package/dist/client/rpc-pipeline.js.map +1 -1
  65. package/dist/client/v2-e2ee.js +95 -89
  66. package/dist/client/v2-e2ee.js.map +1 -1
  67. package/dist/client.js +48 -19
  68. package/dist/client.js.map +1 -1
  69. package/dist/events.d.ts +5 -3
  70. package/dist/events.js +25 -8
  71. package/dist/events.js.map +1 -1
  72. package/dist/facades.js +5 -5
  73. package/dist/facades.js.map +1 -1
  74. package/dist/index.d.ts +2 -1
  75. package/dist/index.js.map +1 -1
  76. package/dist/keystore/aid-db.d.ts +1 -1
  77. package/dist/keystore/aid-db.js +11 -2
  78. package/dist/keystore/aid-db.js.map +1 -1
  79. package/dist/keystore/index.d.ts +1 -1
  80. package/dist/keystore/local-token-store.d.ts +1 -1
  81. package/dist/keystore/local-token-store.js +2 -2
  82. package/dist/keystore/local-token-store.js.map +1 -1
  83. package/dist/org-protocol.js +77 -13
  84. package/dist/org-protocol.js.map +1 -1
  85. package/dist/ownership-v21.d.ts +26 -8
  86. package/dist/ownership-v21.js +147 -70
  87. package/dist/ownership-v21.js.map +1 -1
  88. package/dist/ownership-workflow.d.ts +5 -0
  89. package/dist/ownership-workflow.js +366 -56
  90. package/dist/ownership-workflow.js.map +1 -1
  91. package/dist/tools/cross-sdk-agent.d.ts +2 -1
  92. package/dist/tools/cross-sdk-agent.js +109 -6
  93. package/dist/tools/cross-sdk-agent.js.map +1 -1
  94. package/dist/transport.d.ts +1 -0
  95. package/dist/transport.js +10 -0
  96. package/dist/transport.js.map +1 -1
  97. package/dist/version.d.ts +1 -1
  98. package/dist/version.js +1 -1
  99. package/package.json +1 -1
@@ -113,13 +113,13 @@ function deduplicateForwardPageMessages(value, afterSeq) {
113
113
  function contiguousPullSeq(afterSeq, seqs, proof) {
114
114
  const pulledMax = seqs.length > 0 ? Math.max(...seqs) : 0;
115
115
  const serverProof = safeNonNegativeSeq(proof);
116
- return Math.max(afterSeq, pulledMax, serverProof ?? 0);
116
+ return Math.max(afterSeq, pulledMax, pulledMax > 0 ? 0 : serverProof ?? 0);
117
117
  }
118
118
  function shouldContinueForwardPage({ rawCount, nextAfterSeq, nextAfter, limit, hasMoreField, hasMore, serverHead, observedUpper, pendingAckSeq, }) {
119
119
  // 无新 seq 或游标未前进时必须停止,避免错误元数据造成循环。
120
120
  if (rawCount <= 0 || nextAfter <= nextAfterSeq)
121
121
  return false;
122
- if (hasMore || serverHead > nextAfter || observedUpper > nextAfter || pendingAckSeq > 0)
122
+ if (hasMore || serverHead > nextAfter || observedUpper > nextAfter)
123
123
  return true;
124
124
  // 仅旧服务缺失 has_more 时,以满页保留兼容续拉。
125
125
  return !hasMoreField && limit > 0 && rawCount >= limit;
@@ -917,10 +917,14 @@ export class V2E2EECoordinator {
917
917
  });
918
918
  }
919
919
  }
920
+ const targetDeviceId = String(opts.deviceId ?? '').trim();
921
+ if (useCache && targetDeviceId && !peerDevices.some((dev) => String(dev.device_id ?? '').trim() === targetDeviceId)) {
922
+ this.deleteBootstrapCacheEntry(to);
923
+ return this.buildV2P2PEnvelope({ ...opts, useCache: false });
924
+ }
920
925
  if (peerDevices.length === 0) {
921
926
  throw new E2EEError(`V2 bootstrap: no devices found for ${to}`);
922
927
  }
923
- const targetDeviceId = String(opts.deviceId ?? '').trim();
924
928
  if (targetDeviceId && !peerDevices.some((dev) => String(dev.device_id ?? '').trim() === targetDeviceId)) {
925
929
  throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
926
930
  }
@@ -1007,6 +1011,10 @@ export class V2E2EECoordinator {
1007
1011
  targetSet = this.cloneP2PTargetSet(targetSet);
1008
1012
  }
1009
1013
  if (targetSet.targets.length === 0) {
1014
+ if (useCache && targetDeviceId) {
1015
+ this.deleteBootstrapCacheEntry(to);
1016
+ return this.buildV2P2PEnvelope({ ...opts, useCache: false });
1017
+ }
1010
1018
  throw new E2EEError(`V2 bootstrap: no usable devices found for ${to}`);
1011
1019
  }
1012
1020
  if (targetDeviceId) {
@@ -1020,6 +1028,10 @@ export class V2E2EECoordinator {
1020
1028
  return isPeerTarget || target.role === 'self_sync';
1021
1029
  });
1022
1030
  if (!peerTargetFound) {
1031
+ if (useCache) {
1032
+ this.deleteBootstrapCacheEntry(to);
1033
+ return this.buildV2P2PEnvelope({ ...opts, useCache: false });
1034
+ }
1023
1035
  throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
1024
1036
  }
1025
1037
  }
@@ -1361,10 +1373,9 @@ export class V2E2EECoordinator {
1361
1373
  const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
1362
1374
  let pipelineRunning = false;
1363
1375
  let pipelineError = null;
1364
- const pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
1376
+ let pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
1365
1377
  let pipelineProducer = null;
1366
1378
  let pipelineStopped = false;
1367
- const pipelineResume = { current: null };
1368
1379
  const startPipeline = (startAfterSeq, startVolatileCursor) => {
1369
1380
  if (pipelineRunning || pageCount >= maxPages || limit < V2_PULL_MAX_PAGE_LIMIT)
1370
1381
  return;
@@ -1375,30 +1386,31 @@ export class V2E2EECoordinator {
1375
1386
  for (let produced = pageCount; produced < maxPages; produced += 1) {
1376
1387
  if (pipelineStopped)
1377
1388
  break;
1389
+ this.ensureDeliveryCurrent(ns, generation);
1378
1390
  const requestedVolatileCursor = producerVolatileCursor;
1391
+ const ackUpToSeq = pendingAckSeq;
1392
+ const inlineAckGeneration = ackUpToSeq > 0 ? client._delivery.captureP2PInlineAckGeneration() : null;
1379
1393
  const result = await client._callRawV2Rpc('message.v2.pull', {
1380
1394
  after_seq: producerAfterSeq,
1381
1395
  limit,
1396
+ ...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
1382
1397
  ...(producerVolatileCursor ? { after_volatile_cursor: producerVolatileCursor } : {}),
1383
1398
  ...(opts?.force ? { force: true } : {}),
1384
1399
  }, pullGateKey);
1385
1400
  if (pipelineStopped)
1386
1401
  break;
1402
+ this.ensureDeliveryCurrent(ns, generation);
1387
1403
  const rawPageMessages = (Array.isArray(result?.messages) ? result.messages : []);
1388
- const completed = new Promise((resolve) => { pipelineResume.current = resolve; });
1389
1404
  const enqueued = await pipelineQueue.put({
1390
1405
  result,
1391
1406
  afterSeq: producerAfterSeq,
1392
- ackUpToSeq: 0,
1393
- inlineAckGeneration: null,
1407
+ ackUpToSeq,
1408
+ inlineAckGeneration,
1394
1409
  requestedVolatileCursor,
1395
1410
  weight: rawPageMessages.length,
1396
1411
  }, rawPageMessages.length);
1397
1412
  if (!enqueued || pipelineStopped)
1398
1413
  break;
1399
- await completed;
1400
- if (pipelineStopped)
1401
- break;
1402
1414
  const volatileMessages = client._delivery.volatileMessages?.(rawPageMessages) ?? [];
1403
1415
  if (volatileMessages.length > 0)
1404
1416
  break;
@@ -1406,7 +1418,7 @@ export class V2E2EECoordinator {
1406
1418
  const seqs = messages
1407
1419
  .map((msg) => Number(msg.seq ?? 0))
1408
1420
  .filter((seq) => Number.isFinite(seq) && seq > 0);
1409
- const nextAfter = Math.max(contiguousPullSeq(producerAfterSeq, seqs, result.contiguous_seq), producerAfterSeq);
1421
+ const nextAfter = Math.max(producerAfterSeq, ...seqs);
1410
1422
  const cursor = isJsonObject(result.cursor)
1411
1423
  ? result.cursor : null;
1412
1424
  const shouldContinue = shouldContinueForwardPage({
@@ -1433,8 +1445,6 @@ export class V2E2EECoordinator {
1433
1445
  };
1434
1446
  const stopPipeline = async () => {
1435
1447
  pipelineStopped = true;
1436
- pipelineResume.current?.();
1437
- pipelineResume.current = null;
1438
1448
  pipelineQueue.close();
1439
1449
  if (pipelineProducer) {
1440
1450
  const producer = pipelineProducer;
@@ -1458,7 +1468,7 @@ export class V2E2EECoordinator {
1458
1468
  pageCount += 1;
1459
1469
  const requestedVolatileCursor = pipelinePage?.requestedVolatileCursor ?? volatileCursor;
1460
1470
  const ackUpToSeq = pipelinePage?.ackUpToSeq ?? pendingAckSeq;
1461
- const inlineAckGeneration = pipelinePage?.inlineAckGeneration ?? (ackUpToSeq > 0
1471
+ const inlineAckGeneration = pipelinePage ? pipelinePage.inlineAckGeneration : (ackUpToSeq > 0
1462
1472
  ? client._delivery.captureP2PInlineAckGeneration()
1463
1473
  : null);
1464
1474
  client._clientLog.debug(`message.v2.pull page request: page=${pageCount}, after_seq=${nextAfterSeq}, limit=${limit}, ack_up_to_seq=${ackUpToSeq}, ns=${ns || '<none>'}`);
@@ -1487,25 +1497,31 @@ export class V2E2EECoordinator {
1487
1497
  continue;
1488
1498
  }
1489
1499
  }
1500
+ this.ensureDeliveryCurrent(ns, generation);
1490
1501
  if (ackUpToSeq > 0) {
1491
1502
  const actualAckSeq = client._delivery.resolveP2PPullAckSeq(result, ackUpToSeq);
1492
1503
  if (actualAckSeq >= ackUpToSeq) {
1493
1504
  if (inlineAckGeneration !== null) {
1494
1505
  client._delivery.onP2PV2PullAckResult(result, ackUpToSeq, inlineAckGeneration);
1495
1506
  }
1496
- pendingAckSeq = 0;
1507
+ if (pendingAckSeq <= ackUpToSeq)
1508
+ pendingAckSeq = 0;
1497
1509
  lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
1498
1510
  this.clearForwardAck(ns, ackUpToSeq);
1499
1511
  }
1500
1512
  else {
1501
1513
  client._clientLog.warn(`message.v2.pull piggyback ACK below target: requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
1502
- await this.confirmForwardP2PAck(ackUpToSeq);
1503
- pendingAckSeq = 0;
1504
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
1505
1514
  }
1506
1515
  }
1507
1516
  const pageMessages = (Array.isArray(result?.messages) ? result.messages : []);
1508
1517
  const volatileMessages = client._delivery.volatileMessages?.(pageMessages) ?? [];
1518
+ if (pipelinePage && volatileMessages.length > 0) {
1519
+ if (pipelineProducer)
1520
+ await pipelineProducer;
1521
+ pipelineProducer = null;
1522
+ pipelineRunning = false;
1523
+ pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
1524
+ }
1509
1525
  if (volatileMessages.length > 0) {
1510
1526
  decrypted.push(...await this.publishV2VolatileMessages(volatileMessages, 'pull', true));
1511
1527
  volatileCursor = String(client._delivery.volatilePullCursor?.() ?? '').trim();
@@ -1531,6 +1547,13 @@ export class V2E2EECoordinator {
1531
1547
  pageMaxSeq = Math.max(...seqs);
1532
1548
  }
1533
1549
  const pageContiguousSeq = contiguousPullSeq(pageContigBefore, seqs, result.contiguous_seq);
1550
+ if (!pipelineRunning && volatileMessages.length === 0 && shouldContinueForwardPage({
1551
+ rawCount: messages.length, nextAfterSeq, nextAfter: pageMaxSeq, limit,
1552
+ hasMoreField, hasMore, serverHead,
1553
+ observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._pendingP2pPullUpper?.get?.(ns) ?? 0)),
1554
+ pendingAckSeq: 0,
1555
+ }))
1556
+ startPipeline(pageMaxSeq, volatileCursor);
1534
1557
  const deferredKeyFetches = new Map();
1535
1558
  let blockedSeq = 0;
1536
1559
  const pageDecryptedBefore = decrypted.length;
@@ -1701,18 +1724,8 @@ export class V2E2EECoordinator {
1701
1724
  || (contigAdvanced && ackSeq > knownServerAckSeq));
1702
1725
  if (ackNeeded) {
1703
1726
  this.recordForwardAck(ns, ackSeq);
1704
- const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
1705
- const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter > nextAfterSeq;
1706
- if (canContinuePage && !pipelineRunning) {
1707
- pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
1708
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
1709
- client._clientLog.debug(`message.v2.pull queued piggyback auto-ack: ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
1710
- }
1711
- else {
1712
- client._clientLog.debug(`message.v2.pull sending terminal auto-ack: ns=${ns}, ack_seq=${ackSeq}, raw_count=${messages.length}`);
1713
- await this.confirmForwardP2PAck(ackSeq);
1714
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
1715
- }
1727
+ pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
1728
+ client._clientLog.debug(`message.v2.pull queued piggyback auto-ack: ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
1716
1729
  }
1717
1730
  }
1718
1731
  if (!ns) {
@@ -1729,7 +1742,7 @@ export class V2E2EECoordinator {
1729
1742
  currentSeq: client._seqTracker.getContiguousSeq(ns),
1730
1743
  targetSeq: serverHead,
1731
1744
  });
1732
- const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
1745
+ const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
1733
1746
  const rawCount = messages.length;
1734
1747
  if (blockedSeq > 0)
1735
1748
  break;
@@ -1761,11 +1774,7 @@ export class V2E2EECoordinator {
1761
1774
  && volatileCursor !== requestedVolatileCursor;
1762
1775
  if (!shouldContinuePersistent && !shouldContinueVolatile)
1763
1776
  break;
1764
- if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
1765
- if (pendingAckSeq > 0) {
1766
- await this.confirmForwardP2PAck(pendingAckSeq);
1767
- pendingAckSeq = 0;
1768
- }
1777
+ if (shouldContinueForPipeline && volatileMessages.length === 0 && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
1769
1778
  startPipeline(nextAfter, volatileCursor);
1770
1779
  }
1771
1780
  if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
@@ -1775,14 +1784,10 @@ export class V2E2EECoordinator {
1775
1784
  await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
1776
1785
  pullGateKey = nextKey;
1777
1786
  }
1778
- const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
1787
+ const tailDelayMs = pipelineRunning ? 0 : v2PullTailDelayMs(rawCount, limit);
1779
1788
  if (tailDelayMs > 0)
1780
1789
  await v2Sleep(tailDelayMs);
1781
1790
  nextAfterSeq = nextAfter;
1782
- if (pipelinePage) {
1783
- pipelineResume.current?.();
1784
- pipelineResume.current = null;
1785
- }
1786
1791
  }
1787
1792
  }
1788
1793
  finally {
@@ -1790,11 +1795,14 @@ export class V2E2EECoordinator {
1790
1795
  }
1791
1796
  if (pageCount >= maxPages) {
1792
1797
  client._clientLog.warn(`message.v2.pull reached max_pages=${maxPages} after_seq=${nextAfterSeq}`);
1793
- if (pendingAckSeq > 0) {
1794
- client._clientLog.debug(`message.v2.pull flushing pending auto-ack after max_pages: ns=${ns}, ack_seq=${pendingAckSeq}`);
1795
- await this.confirmForwardP2PAck(pendingAckSeq);
1798
+ }
1799
+ if (pendingAckSeq > 0) {
1800
+ this.ensureDeliveryCurrent(ns, generation);
1801
+ client._clientLog.debug(`message.v2.pull flushing pending auto-ack: ns=${ns}, ack_seq=${pendingAckSeq}`);
1802
+ const ackToFlush = pendingAckSeq;
1803
+ await this.confirmForwardP2PAck(ackToFlush);
1804
+ if (pendingAckSeq <= ackToFlush)
1796
1805
  pendingAckSeq = 0;
1797
- }
1798
1806
  }
1799
1807
  client._clientLog.debug(`message.v2.pull done: requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${decrypted.length}, ns=${ns || '<none>'}`);
1800
1808
  return decrypted;
@@ -1881,8 +1889,10 @@ export class V2E2EECoordinator {
1881
1889
  }
1882
1890
  async confirmForwardP2PAck(upToSeq) {
1883
1891
  const ns = this.client._aid ? `p2p:${this.client._aid}` : '';
1892
+ const generation = Number(this.client._v2RuntimeGeneration ?? 0);
1884
1893
  try {
1885
1894
  const result = await this.ackV2ForForward(upToSeq);
1895
+ this.ensureDeliveryCurrent(ns, generation);
1886
1896
  const actualAckSeq = this.client._delivery.resolveForwardAckSeq(result, upToSeq);
1887
1897
  if (actualAckSeq < upToSeq) {
1888
1898
  throw new E2EEError(`message.v2.ack server ACK watermark ${actualAckSeq} is below requested ${upToSeq}`);
@@ -1890,7 +1900,9 @@ export class V2E2EECoordinator {
1890
1900
  this.clearForwardAck(ns, upToSeq);
1891
1901
  }
1892
1902
  catch (exc) {
1893
- this.recordForwardAck(ns, upToSeq);
1903
+ if (Number(this.client._v2RuntimeGeneration ?? 0) === generation
1904
+ && this.client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false)
1905
+ this.recordForwardAck(ns, upToSeq);
1894
1906
  throw exc;
1895
1907
  }
1896
1908
  }
@@ -2334,7 +2346,6 @@ export class V2E2EECoordinator {
2334
2346
  const pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
2335
2347
  let pipelineProducer = null;
2336
2348
  let pipelineStopped = false;
2337
- const pipelineResume = { current: null };
2338
2349
  const startPipeline = (startAfterSeq) => {
2339
2350
  if (pipelineRunning || pageCount >= maxPages || limit < V2_PULL_MAX_PAGE_LIMIT || !ownsCursor)
2340
2351
  return;
@@ -2344,35 +2355,36 @@ export class V2E2EECoordinator {
2344
2355
  for (let produced = pageCount; produced < maxPages; produced += 1) {
2345
2356
  if (pipelineStopped)
2346
2357
  break;
2358
+ this.ensureDeliveryCurrent(ns, generation);
2359
+ const ackUpToSeq = pendingAckSeq;
2360
+ const inlineAckGeneration = ackUpToSeq > 0 ? client._delivery.captureGroupInlineAckGeneration() : null;
2347
2361
  const result = await client._callRawV2Rpc('group.v2.pull', withExplicitGroupAid({
2348
2362
  group_id: wireGroupId,
2349
2363
  after_seq: producerAfterSeq,
2350
2364
  limit,
2351
2365
  ...cursorParams,
2352
2366
  ...(opts?.force ? { force: true } : {}),
2367
+ ...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
2353
2368
  }, groupAid), pullGateKey);
2354
2369
  if (pipelineStopped)
2355
2370
  break;
2371
+ this.ensureDeliveryCurrent(ns, generation);
2356
2372
  const pageMessages = (Array.isArray(result.messages) ? result.messages : []);
2357
- const completed = new Promise((resolve) => { pipelineResume.current = resolve; });
2358
2373
  const enqueued = await pipelineQueue.put({
2359
2374
  result,
2360
2375
  afterSeq: producerAfterSeq,
2361
- ackUpToSeq: 0,
2362
- inlineAckGeneration: null,
2376
+ ackUpToSeq,
2377
+ inlineAckGeneration,
2363
2378
  requestedVolatileCursor: '',
2364
2379
  weight: pageMessages.length,
2365
2380
  }, pageMessages.length);
2366
2381
  if (!enqueued || pipelineStopped)
2367
2382
  break;
2368
- await completed;
2369
- if (pipelineStopped)
2370
- break;
2371
2383
  const messages = deduplicateForwardPageMessages(pageMessages, producerAfterSeq);
2372
2384
  const seqs = messages
2373
2385
  .map((msg) => Number(msg.seq ?? 0))
2374
2386
  .filter((seq) => Number.isFinite(seq) && seq > 0);
2375
- const nextAfter = Math.max(contiguousPullSeq(producerAfterSeq, seqs, result.contiguous_seq), producerAfterSeq);
2387
+ const nextAfter = Math.max(producerAfterSeq, ...seqs);
2376
2388
  const cursor = isJsonObject(result.cursor)
2377
2389
  ? result.cursor : null;
2378
2390
  const shouldContinue = shouldContinueForwardPage({
@@ -2398,8 +2410,6 @@ export class V2E2EECoordinator {
2398
2410
  };
2399
2411
  const stopPipeline = async () => {
2400
2412
  pipelineStopped = true;
2401
- pipelineResume.current?.();
2402
- pipelineResume.current = null;
2403
2413
  pipelineQueue.close();
2404
2414
  if (pipelineProducer) {
2405
2415
  const producer = pipelineProducer;
@@ -2422,7 +2432,7 @@ export class V2E2EECoordinator {
2422
2432
  }
2423
2433
  pageCount += 1;
2424
2434
  const ackUpToSeq = ownsCursor ? (pipelinePage?.ackUpToSeq ?? pendingAckSeq) : 0;
2425
- const inlineAckGeneration = pipelinePage?.inlineAckGeneration ?? (ackUpToSeq > 0
2435
+ const inlineAckGeneration = pipelinePage ? pipelinePage.inlineAckGeneration : (ackUpToSeq > 0
2426
2436
  ? client._delivery.captureGroupInlineAckGeneration()
2427
2437
  : null);
2428
2438
  client._clientLog.debug(`group.v2.pull page request: group=${gid}, page=${pageCount}, after_seq=${nextAfterSeq}, limit=${limit}, ack_up_to_seq=${ackUpToSeq}, ns=${ns}`);
@@ -2441,21 +2451,21 @@ export class V2E2EECoordinator {
2441
2451
  ...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
2442
2452
  }, groupAid), pullGateKey);
2443
2453
  }
2454
+ if (ownsCursor)
2455
+ this.ensureDeliveryCurrent(ns, generation);
2444
2456
  if (ackUpToSeq > 0) {
2445
2457
  const actualAckSeq = client._delivery.resolveGroupPullAckSeq(result, ackUpToSeq);
2446
2458
  if (actualAckSeq >= ackUpToSeq) {
2447
2459
  if (inlineAckGeneration !== null) {
2448
2460
  client._delivery.onGroupV2PullAckResult(gid, result, ackUpToSeq, inlineAckGeneration);
2449
2461
  }
2450
- pendingAckSeq = 0;
2462
+ if (pendingAckSeq <= ackUpToSeq)
2463
+ pendingAckSeq = 0;
2451
2464
  lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
2452
2465
  this.clearForwardAck(ns, ackUpToSeq);
2453
2466
  }
2454
2467
  else {
2455
2468
  client._clientLog.warn(`group.v2.pull piggyback ACK below target: group=${gid}, requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
2456
- await this.confirmForwardGroupAck(gid, ackUpToSeq, groupAid);
2457
- pendingAckSeq = 0;
2458
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
2459
2469
  }
2460
2470
  }
2461
2471
  const resultGroupAid = String(result.group_aid ?? result.groupAid ?? '').trim();
@@ -2483,6 +2493,13 @@ export class V2E2EECoordinator {
2483
2493
  pageMaxSeq = Math.max(...seqs);
2484
2494
  }
2485
2495
  const pageContiguousSeq = contiguousPullSeq(pageContigBefore, seqs, result.contiguous_seq);
2496
+ if (!pipelineRunning && ownsCursor && shouldContinueForwardPage({
2497
+ rawCount: messages.length, nextAfterSeq, nextAfter: pageMaxSeq, limit,
2498
+ hasMoreField, hasMore, serverHead,
2499
+ observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._delivery.pendingPullUpper?.(ns) ?? 0)),
2500
+ pendingAckSeq: 0,
2501
+ }))
2502
+ startPipeline(pageMaxSeq);
2486
2503
  const deferredKeyFetches = new Map();
2487
2504
  let blockedSeq = 0;
2488
2505
  const pageDecryptedBefore = decrypted.length;
@@ -2688,18 +2705,8 @@ export class V2E2EECoordinator {
2688
2705
  || (contigAdvanced && ackSeq > knownServerCursorSeq));
2689
2706
  if (ackNeeded) {
2690
2707
  this.recordForwardAck(ns, ackSeq);
2691
- const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
2692
- const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter > nextAfterSeq && ownsCursor;
2693
- if (canContinuePage && !pipelineRunning) {
2694
- pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
2695
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
2696
- client._clientLog.debug(`group.v2.pull queued piggyback auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
2697
- }
2698
- else {
2699
- client._clientLog.debug(`group.v2.pull sending terminal auto-ack: group=${gid}, ns=${ns}, ack_seq=${ackSeq}, raw_count=${messages.length}`);
2700
- await this.confirmForwardGroupAck(gid, ackSeq, groupAid);
2701
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
2702
- }
2708
+ pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
2709
+ client._clientLog.debug(`group.v2.pull queued piggyback auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
2703
2710
  }
2704
2711
  client._delivery.syncProgress(ns, {
2705
2712
  rawCount: messages.length,
@@ -2709,7 +2716,7 @@ export class V2E2EECoordinator {
2709
2716
  currentSeq: client._seqTracker.getContiguousSeq(ns),
2710
2717
  targetSeq: serverHead,
2711
2718
  });
2712
- const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
2719
+ const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
2713
2720
  if (!ownsCursor)
2714
2721
  break;
2715
2722
  const rawCount = messages.length;
@@ -2740,10 +2747,6 @@ export class V2E2EECoordinator {
2740
2747
  if (!shouldContinue)
2741
2748
  break;
2742
2749
  if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
2743
- if (pendingAckSeq > 0) {
2744
- await this.confirmForwardGroupAck(gid, pendingAckSeq, groupAid);
2745
- pendingAckSeq = 0;
2746
- }
2747
2750
  startPipeline(nextAfter);
2748
2751
  }
2749
2752
  if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
@@ -2754,14 +2757,10 @@ export class V2E2EECoordinator {
2754
2757
  await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
2755
2758
  pullGateKey = nextKey;
2756
2759
  }
2757
- const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
2760
+ const tailDelayMs = pipelineRunning ? 0 : v2PullTailDelayMs(rawCount, limit);
2758
2761
  if (tailDelayMs > 0)
2759
2762
  await v2Sleep(tailDelayMs);
2760
2763
  nextAfterSeq = nextAfter;
2761
- if (pipelinePage) {
2762
- pipelineResume.current?.();
2763
- pipelineResume.current = null;
2764
- }
2765
2764
  }
2766
2765
  }
2767
2766
  finally {
@@ -2769,11 +2768,14 @@ export class V2E2EECoordinator {
2769
2768
  }
2770
2769
  if (pageCount >= maxPages) {
2771
2770
  client._clientLog.warn(`group.v2.pull reached max_pages=${maxPages} group=${gid} after_seq=${nextAfterSeq}`);
2772
- if (pendingAckSeq > 0) {
2773
- client._clientLog.debug(`group.v2.pull flushing pending auto-ack after max_pages: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}`);
2774
- await this.confirmForwardGroupAck(gid, pendingAckSeq, groupAid);
2771
+ }
2772
+ if (pendingAckSeq > 0) {
2773
+ this.ensureDeliveryCurrent(ns, generation);
2774
+ client._clientLog.debug(`group.v2.pull flushing pending auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}`);
2775
+ const ackToFlush = pendingAckSeq;
2776
+ await this.confirmForwardGroupAck(gid, ackToFlush, groupAid);
2777
+ if (pendingAckSeq <= ackToFlush)
2775
2778
  pendingAckSeq = 0;
2776
- }
2777
2779
  }
2778
2780
  client._clientLog.debug(`group.v2.pull done: group=${gid}, requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${decrypted.length}, ns=${ns}`);
2779
2781
  return decrypted;
@@ -2794,8 +2796,10 @@ export class V2E2EECoordinator {
2794
2796
  }
2795
2797
  async confirmForwardGroupAck(groupId, upToSeq, groupAid) {
2796
2798
  const ns = `group:${String(groupId ?? '').trim()}`;
2799
+ const generation = Number(this.client._v2RuntimeGeneration ?? 0);
2797
2800
  try {
2798
2801
  const result = await this.ackGroupV2ForForward(groupId, upToSeq, groupAid);
2802
+ this.ensureDeliveryCurrent(ns, generation);
2799
2803
  const actualAckSeq = this.client._delivery.resolveForwardAckSeq(result, upToSeq);
2800
2804
  if (actualAckSeq < upToSeq) {
2801
2805
  throw new E2EEError(`group.v2.ack server ACK watermark ${actualAckSeq} is below requested ${upToSeq}`);
@@ -2803,7 +2807,9 @@ export class V2E2EECoordinator {
2803
2807
  this.clearForwardAck(ns, upToSeq);
2804
2808
  }
2805
2809
  catch (exc) {
2806
- this.recordForwardAck(ns, upToSeq);
2810
+ if (Number(this.client._v2RuntimeGeneration ?? 0) === generation
2811
+ && this.client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false)
2812
+ this.recordForwardAck(ns, upToSeq);
2807
2813
  throw exc;
2808
2814
  }
2809
2815
  }