@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
@@ -9,6 +9,7 @@ const MESSAGE_RECALL_SEEN_LIMIT = 10_000;
9
9
  const GROUP_RECALL_SEEN_LIMIT = 10_000;
10
10
  const VOLATILE_SEEN_LIMIT = 10_000;
11
11
  const SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS = 200;
12
+ const BACKGROUND_PULL_RETRY_DELAY_MS = 250;
12
13
  const DELIVERY_CHANGED_MESSAGE_EVENTS = new Set([
13
14
  'message.received',
14
15
  'group.message_created',
@@ -80,6 +81,11 @@ export class MessageDeliveryEngine {
80
81
  pendingGroupPullUpper = null;
81
82
  onlineUnreadHintTargets = null;
82
83
  onlineUnreadHintTasks = null;
84
+ backgroundPullNeeds = new Map();
85
+ backgroundPullTasks = new Set();
86
+ backgroundPullCheckQueued = false;
87
+ backgroundPullTimer = null;
88
+ backgroundPullTimerAt = 0;
83
89
  pendingP2PInlineAcks = null;
84
90
  pendingGroupInlineAcks = null;
85
91
  pendingGroupInlineAckNamespaces = null;
@@ -422,6 +428,13 @@ export class MessageDeliveryEngine {
422
428
  this.deliveryChangedGeneration += 1;
423
429
  this.pendingDeliveryChanges = null;
424
430
  this.inlineAckGeneration += 1;
431
+ if (this.backgroundPullTimer)
432
+ clearTimeout(this.backgroundPullTimer);
433
+ this.backgroundPullTimer = null;
434
+ this.backgroundPullTimerAt = 0;
435
+ this.backgroundPullNeeds.clear();
436
+ this.backgroundPullTasks.clear();
437
+ this.backgroundPullCheckQueued = false;
425
438
  void this.runtime.client._rpcPipeline?.invalidatePulls?.();
426
439
  this.pendingP2PInlineAcks = null;
427
440
  this.pendingGroupInlineAcks = null;
@@ -693,6 +706,7 @@ export class MessageDeliveryEngine {
693
706
  }
694
707
  endRealtimeSync(ns) {
695
708
  this.realtimeSyncing?.delete(ns);
709
+ this.requestBackgroundPullRecovery();
696
710
  this.runtime.client._rpcPipeline?.requestPullDrainedCheck?.();
697
711
  }
698
712
  hasPendingPull(ns) {
@@ -703,6 +717,7 @@ export class MessageDeliveryEngine {
703
717
  }
704
718
  hasAnyPendingPull() {
705
719
  return [...(this.pendingGroupPullUpper?.values() ?? [])].some((seq) => seq > 0)
720
+ || this.backgroundPullNeeds.size > 0
706
721
  || Boolean(this.onlineUnreadHintTasks?.size)
707
722
  || Boolean(this.realtimeSyncing?.size)
708
723
  || Boolean(this.runtime.client._v2E2EE?.hasPendingSenderIKWork?.());
@@ -710,12 +725,124 @@ export class MessageDeliveryEngine {
710
725
  onPullGateIdle(ns) {
711
726
  if (!ns)
712
727
  return;
728
+ this.requestBackgroundPullRecovery();
713
729
  if (ns.startsWith('p2p:')) {
714
730
  this.runtime.client._schedulePendingP2pPullIfNeeded?.(ns, 'pull-gate-idle');
715
731
  return;
716
732
  }
717
733
  this.schedulePendingGroupPullIfNeeded(ns, 'pull-gate-idle');
718
734
  }
735
+ recordBackgroundPullNeed(ns, throughSeq = 0, probe = false) {
736
+ const client = this.runtime.client;
737
+ if (client._sessionOptions?.background_sync === false || !client._aid
738
+ || !(ns === `p2p:${client._aid}` || ns.startsWith('group:') || ns.startsWith('group_event:')))
739
+ return;
740
+ const need = this.backgroundPullNeeds.get(ns) ?? {
741
+ throughSeq: 0, hasMore: false, probe, lastAfter: -1, retryAt: 0,
742
+ };
743
+ need.throughSeq = Math.max(need.throughSeq, throughSeq);
744
+ need.probe ||= probe;
745
+ this.backgroundPullNeeds.set(ns, need);
746
+ }
747
+ observeBackgroundPullResponse(ns, afterSeq, result, limit = 50) {
748
+ if (!isJsonObject(result))
749
+ return;
750
+ const response = result;
751
+ const rows = ns.startsWith('group_event:') ? response.events : response.messages;
752
+ if (!Array.isArray(rows))
753
+ return;
754
+ const cursor = isJsonObject(response.cursor)
755
+ ? response.cursor : null;
756
+ const throughSeq = Math.max(positiveSafeSequenceHint(cursor?.latest_seq), positiveSafeSequenceHint(response.latest_seq), positiveSafeSequenceHint(response.latest_message_seq), positiveSafeSequenceHint(response.latest_event_seq), ...rows.map((row) => row && typeof row === 'object' && row.storage !== 'volatile'
757
+ ? positiveSafeSequenceHint(row.seq ?? row.event_seq) : 0));
758
+ this.recordBackgroundPullNeed(ns, throughSeq);
759
+ const need = this.backgroundPullNeeds.get(ns);
760
+ if (!need || afterSeq < need.lastAfter)
761
+ return;
762
+ need.lastAfter = afterSeq;
763
+ need.hasMore = response.has_more === true || positiveSafeSequenceHint(response.remaining) > 0
764
+ || (!('has_more' in response) && limit > 0 && rows.length >= limit);
765
+ need.probe = false;
766
+ }
767
+ finishBackgroundPull(ns, generation, afterSeq, failed) {
768
+ if (generation !== this.captureInlineAckGeneration())
769
+ return;
770
+ const need = this.backgroundPullNeeds.get(ns);
771
+ if (need) {
772
+ if (failed)
773
+ need.probe = true;
774
+ else
775
+ need.probe = false;
776
+ need.retryAt = failed || this.syncState(ns).ack <= afterSeq
777
+ ? Date.now() + BACKGROUND_PULL_RETRY_DELAY_MS : 0;
778
+ }
779
+ this.requestBackgroundPullRecovery();
780
+ }
781
+ requestBackgroundPullRecovery() {
782
+ if (this.backgroundPullCheckQueued || this.backgroundPullNeeds.size === 0)
783
+ return;
784
+ this.backgroundPullCheckQueued = true;
785
+ const generation = this.captureInlineAckGeneration();
786
+ queueMicrotask(() => {
787
+ this.backgroundPullCheckQueued = false;
788
+ if (generation !== this.captureInlineAckGeneration())
789
+ return;
790
+ const client = this.runtime.client;
791
+ if (client._sessionOptions?.background_sync === false || client.state !== ConnectionState.READY || client._closing)
792
+ return;
793
+ let retryAt = Infinity;
794
+ for (const [ns, need] of this.backgroundPullNeeds) {
795
+ if (this.backgroundPullTasks.has(ns) || this.realtimeSyncing?.has(ns) || this.onlineUnreadHintTasks?.has(ns))
796
+ continue;
797
+ const eventScope = ns.startsWith('group_event:');
798
+ const filling = [...(client._gapFillDone?.keys?.() ?? [])].some((key) => eventScope
799
+ ? String(key) === `group_event_pull:${ns}`
800
+ : ns.startsWith('p2p:') ? String(key).startsWith('p2p:') : String(key).startsWith(`group_msg:${ns.slice(6)}:`));
801
+ if (filling || (!eventScope && client._rpcPipeline?.hasPullActivity?.(ns, false))
802
+ || (eventScope && ['pulling', 'processing', 'pulled'].includes(client._rpcPipeline?.getPullLifecycle?.(ns))))
803
+ continue;
804
+ if (this.syncState(ns).ack >= need.throughSeq && !need.hasMore && !need.probe) {
805
+ this.backgroundPullNeeds.delete(ns);
806
+ continue;
807
+ }
808
+ if (need.retryAt > Date.now()) {
809
+ retryAt = Math.min(retryAt, need.retryAt);
810
+ continue;
811
+ }
812
+ this.backgroundPullTasks.add(ns);
813
+ const task = Promise.resolve().then(() => {
814
+ if (generation !== this.captureInlineAckGeneration())
815
+ return;
816
+ return eventScope ? this.fillGroupEventGap(ns.slice('group_event:'.length))
817
+ : ns.startsWith('group:') ? this.fillGroupGap(ns.slice(6)) : this.fillP2pGap();
818
+ }).finally(() => {
819
+ if (generation !== this.captureInlineAckGeneration())
820
+ return;
821
+ this.backgroundPullTasks.delete(ns);
822
+ this.requestBackgroundPullRecovery();
823
+ });
824
+ client._safeAsync(task);
825
+ }
826
+ if (this.backgroundPullNeeds.size === 0) {
827
+ if (this.backgroundPullTimer)
828
+ clearTimeout(this.backgroundPullTimer);
829
+ this.backgroundPullTimer = null;
830
+ this.backgroundPullTimerAt = 0;
831
+ client._rpcPipeline?.requestPullDrainedCheck?.();
832
+ }
833
+ if (Number.isFinite(retryAt) && (!this.backgroundPullTimer || retryAt < this.backgroundPullTimerAt)) {
834
+ if (this.backgroundPullTimer)
835
+ clearTimeout(this.backgroundPullTimer);
836
+ this.backgroundPullTimerAt = retryAt;
837
+ this.backgroundPullTimer = setTimeout(() => {
838
+ this.backgroundPullTimer = null;
839
+ this.backgroundPullTimerAt = 0;
840
+ this.requestBackgroundPullRecovery();
841
+ }, Math.max(0, retryAt - Date.now()));
842
+ this.backgroundPullTimer.unref?.();
843
+ }
844
+ });
845
+ }
719
846
  onForegroundPullsDrained() {
720
847
  const client = this.runtime.client;
721
848
  if (!client._aid || client._sessionOptions?.background_sync === false
@@ -1340,6 +1467,7 @@ export class MessageDeliveryEngine {
1340
1467
  if (seqNum > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
1341
1468
  this.fillGroupGap(groupId).catch((exc) => client._clientLog.warn(`background recall gap fill failed: ${formatDeliveryError(exc)}`));
1342
1469
  }
1470
+ await this.publishGroupRecallTombstone(groupId, seq, wrapped);
1343
1471
  }
1344
1472
  publishAppEvent(event, payload, source = 'direct', deliveryContext) {
1345
1473
  const client = this.runtime.client;
@@ -1625,7 +1753,6 @@ export class MessageDeliveryEngine {
1625
1753
  if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
1626
1754
  this.fillP2pGap().catch((exc) => client._clientLog.warn(`background recall gap fill failed: ${formatDeliveryError(exc)}`));
1627
1755
  }
1628
- return;
1629
1756
  }
1630
1757
  await this.publishMessageRecallTombstone(seq, msg);
1631
1758
  }
@@ -1826,12 +1953,15 @@ export class MessageDeliveryEngine {
1826
1953
  return;
1827
1954
  const ns = `p2p:${client._aid}`;
1828
1955
  const afterSeq = client._seqTracker.getContiguousSeq(ns);
1956
+ const generation = this.captureInlineAckGeneration();
1829
1957
  const dedupKey = `p2p:${afterSeq}`;
1830
1958
  if (client._gapFillDone.has(dedupKey))
1831
1959
  return;
1832
1960
  client._gapFillDone.set(dedupKey, Date.now());
1961
+ this.recordBackgroundPullNeed(ns, 0, true);
1833
1962
  client._clientLog.debug(`P2P message gap fill start: after_seq=${afterSeq}`);
1834
1963
  let filled = 0;
1964
+ let failed = false;
1835
1965
  try {
1836
1966
  // Gap Fill 本身就是 Forward Pull,必须沿用协调器的完整事务:
1837
1967
  // 原始页提交成功后由协调器负责 Piggyback/终端 ACK。
@@ -1844,11 +1974,15 @@ export class MessageDeliveryEngine {
1844
1974
  client._clientLog.debug(`P2P message gap fill done: after_seq=${afterSeq}, filled=${filled}`);
1845
1975
  }
1846
1976
  catch (exc) {
1977
+ failed = true;
1847
1978
  client._clientLog.warn(`P2P message gap fill failed: ${formatDeliveryError(exc)}`);
1848
1979
  }
1849
1980
  finally {
1981
+ if (generation !== this.captureInlineAckGeneration())
1982
+ return;
1850
1983
  client._gapFillDone.delete(dedupKey);
1851
1984
  client._schedulePendingP2pPullIfNeeded?.(ns, 'gap-fill-complete');
1985
+ this.finishBackgroundPull(ns, generation, afterSeq, failed);
1852
1986
  }
1853
1987
  }
1854
1988
  async fillGroupGap(groupId) {
@@ -1860,12 +1994,15 @@ export class MessageDeliveryEngine {
1860
1994
  return;
1861
1995
  const ns = `group:${groupId}`;
1862
1996
  const afterSeq = client._seqTracker.getContiguousSeq(ns);
1997
+ const generation = this.captureInlineAckGeneration();
1863
1998
  const dedupKey = `group_msg:${groupId}:${afterSeq}`;
1864
1999
  if (client._gapFillDone.has(dedupKey))
1865
2000
  return;
1866
2001
  client._gapFillDone.set(dedupKey, Date.now());
2002
+ this.recordBackgroundPullNeed(ns, 0, true);
1867
2003
  client._clientLog.debug(`group message gap fill start: group=${groupId}, after_seq=${afterSeq}`);
1868
2004
  let filled = 0;
2005
+ let failed = false;
1869
2006
  try {
1870
2007
  const operation = () => client._pullGroupV2(groupId, afterSeq, 50, { gateLocked: true });
1871
2008
  const messages = client._rpcPipeline
@@ -1876,14 +2013,15 @@ export class MessageDeliveryEngine {
1876
2013
  client._clientLog.debug(`group message gap fill done: group=${groupId}, after_seq=${afterSeq}, filled=${filled}`);
1877
2014
  }
1878
2015
  catch (exc) {
2016
+ failed = true;
1879
2017
  client._clientLog.warn(`group message gap fill failed: ${formatDeliveryError(exc)}`);
1880
2018
  }
1881
2019
  finally {
2020
+ if (generation !== this.captureInlineAckGeneration())
2021
+ return;
1882
2022
  client._gapFillDone.delete(dedupKey);
1883
- if (filled > 0 && client._seqTracker.getContiguousSeq(ns) > afterSeq) {
1884
- void this.fillGroupGap(groupId);
1885
- }
1886
2023
  this.schedulePendingGroupPullIfNeeded(ns, 'gap-fill-complete');
2024
+ this.finishBackgroundPull(ns, generation, afterSeq, failed);
1887
2025
  }
1888
2026
  }
1889
2027
  scheduleRealtimeWindowForward(ns, groupId = '') {
@@ -1944,6 +2082,7 @@ export class MessageDeliveryEngine {
1944
2082
  }
1945
2083
  }
1946
2084
  client._gapFillDone.set(dedupKey, Date.now());
2085
+ this.recordBackgroundPullNeed(ns, client._seqTracker.getMaxSeenSeq(ns), true);
1947
2086
  this.syncStarted(ns);
1948
2087
  let filled = 0;
1949
2088
  let continuationAfterSeq = 0;
@@ -1974,6 +2113,9 @@ export class MessageDeliveryEngine {
1974
2113
  }
1975
2114
  if (!isJsonObject(result))
1976
2115
  return;
2116
+ if (generation !== this.captureInlineAckGeneration())
2117
+ return;
2118
+ this.observeBackgroundPullResponse(ns, nextAfterSeq, result);
1977
2119
  const events = result.events;
1978
2120
  if (!Array.isArray(events))
1979
2121
  return;
@@ -2085,6 +2227,7 @@ export class MessageDeliveryEngine {
2085
2227
  const nextAfter = Math.max(eventSeqs.length > 0 ? Math.max(...eventSeqs) : nextAfterSeq, nextAfterSeq);
2086
2228
  const pageAdvanced = eventObjects.length > 0 && nextAfter > nextAfterSeq;
2087
2229
  const needsMore = result.has_more === true
2230
+ || positiveSafeSequenceHint(result.remaining) > 0
2088
2231
  || client._seqTracker.getMaxSeenSeq(ns) > nextAfter;
2089
2232
  if (singlePage && pageAdvanced && needsMore) {
2090
2233
  continuationAfterSeq = nextAfter;
@@ -2103,19 +2246,22 @@ export class MessageDeliveryEngine {
2103
2246
  client._clientLog.warn(`group event gap fill failed: ${formatDeliveryError(exc)}`);
2104
2247
  }
2105
2248
  finally {
2106
- client._gapFillDone.delete(dedupKey);
2249
+ if (generation === this.captureInlineAckGeneration())
2250
+ client._gapFillDone.delete(dedupKey);
2107
2251
  if (legacyGateToken !== null && typeof client._releasePullGate === 'function') {
2108
2252
  client._releasePullGate(ns, legacyGateToken);
2109
2253
  }
2254
+ if (generation !== this.captureInlineAckGeneration())
2255
+ return;
2256
+ if (dissolved)
2257
+ this.backgroundPullNeeds.delete(ns);
2110
2258
  if (!dissolved && singlePage && continuationAfterSeq > afterSeq) {
2111
2259
  this.enqueueOnlineUnreadEventHint({
2112
2260
  group_id: groupId,
2113
2261
  _online_hint_after_event_seq: continuationAfterSeq,
2114
2262
  });
2115
2263
  }
2116
- else if (!dissolved && !singlePage && filled > 0 && client._seqTracker.getContiguousSeq(ns) > afterSeq) {
2117
- void this.fillGroupEventGap(groupId);
2118
- }
2264
+ this.finishBackgroundPull(ns, generation, afterSeq, failed);
2119
2265
  if (!client._rpcPipeline)
2120
2266
  this.syncStopped(failed ? 'aborted' : 'pull_drained');
2121
2267
  }
@@ -2125,6 +2271,7 @@ export class MessageDeliveryEngine {
2125
2271
  const groupId = String(data.group_id ?? '').trim();
2126
2272
  if (!groupId || client._sessionOptions?.background_sync === false)
2127
2273
  return;
2274
+ this.recordBackgroundPullNeed(`group_event:${groupId}`, positiveSafeSequenceHint(data.event_seq), true);
2128
2275
  const rawAfter = Number(data._online_hint_after_event_seq ?? 0);
2129
2276
  const afterSeq = Number.isSafeInteger(rawAfter) && rawAfter > 0 ? rawAfter : 0;
2130
2277
  client._safeAsync(this.fillGroupEventGap(groupId, {
@@ -3197,6 +3344,7 @@ export class MessageDeliveryEngine {
3197
3344
  }
3198
3345
  async onV2PushNotification(data, expectedVersion, inlineGeneration) {
3199
3346
  const client = this.runtime.client;
3347
+ const syncGeneration = this.captureInlineAckGeneration();
3200
3348
  if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
3201
3349
  return;
3202
3350
  const pushData = isJsonObject(data) ? data : null;
@@ -3251,6 +3399,7 @@ export class MessageDeliveryEngine {
3251
3399
  this.retryPendingP2PInlineAck(ns, pushSeq, 'covered inline push');
3252
3400
  return;
3253
3401
  }
3402
+ this.recordBackgroundPullNeed(ns, pushSeq);
3254
3403
  const pullActive = client._rpcPipeline?.hasPullActivity?.(ns) === true;
3255
3404
  if (pullActive || !this.beginRealtimeSync(ns)) {
3256
3405
  client._recordPendingP2pPull(ns, pushSeq);
@@ -3267,7 +3416,9 @@ export class MessageDeliveryEngine {
3267
3416
  client._clientLog.warn(`V2 P2P push auto-sync failed: ${formatDeliveryError(exc)}`);
3268
3417
  }
3269
3418
  finally {
3270
- if (inlineGeneration === undefined || this.inlineGenerationIsCurrent(inlineGeneration)) {
3419
+ if (syncGeneration === this.captureInlineAckGeneration()
3420
+ && (inlineGeneration === undefined || this.inlineGenerationIsCurrent(inlineGeneration))) {
3421
+ this.finishBackgroundPull(ns, syncGeneration, this.syncState(ns).ack, false);
3271
3422
  this.endRealtimeSync(ns);
3272
3423
  client._schedulePendingP2pPullIfNeeded?.(ns, 'push-auto-pull-complete');
3273
3424
  }
@@ -3279,6 +3430,9 @@ export class MessageDeliveryEngine {
3279
3430
  if (!groupId)
3280
3431
  return;
3281
3432
  const ns = `group:${groupId}`;
3433
+ const generation = this.captureInlineAckGeneration();
3434
+ const afterSeq = this.syncState(ns).ack;
3435
+ this.recordBackgroundPullNeed(ns, positiveSafeSequenceHint(data.seq));
3282
3436
  const targets = this.onlineUnreadHintTargets ?? new Map();
3283
3437
  const tasks = this.onlineUnreadHintTasks ?? new Map();
3284
3438
  this.onlineUnreadHintTargets = targets;
@@ -3286,10 +3440,13 @@ export class MessageDeliveryEngine {
3286
3440
  targets.set(ns, Math.max(targets.get(ns) ?? 0, positiveSafeSequenceHint(data.seq)));
3287
3441
  if (tasks.has(ns))
3288
3442
  return;
3443
+ let attemptAfterSeq = afterSeq;
3289
3444
  const task = Promise.resolve().then(async () => {
3445
+ let failed = false;
3290
3446
  try {
3291
3447
  while (true) {
3292
3448
  const ackBefore = this.syncState(ns).ack;
3449
+ attemptAfterSeq = ackBefore;
3293
3450
  await this.runGroupForwardRecovery(groupId, ns, 50, true, targets.get(ns) ?? 0);
3294
3451
  const ackAfter = this.syncState(ns).ack;
3295
3452
  if (ackAfter >= (targets.get(ns) ?? 0) || ackAfter <= ackBefore)
@@ -3297,12 +3454,14 @@ export class MessageDeliveryEngine {
3297
3454
  }
3298
3455
  }
3299
3456
  catch (exc) {
3457
+ failed = true;
3300
3458
  client._clientLog.debug(`online unread hint background Forward failed: ns=${ns} err=${formatDeliveryError(exc)}`);
3301
3459
  }
3302
3460
  finally {
3303
3461
  targets.delete(ns);
3304
3462
  if (tasks.get(ns) === task)
3305
3463
  tasks.delete(ns);
3464
+ this.finishBackgroundPull(ns, generation, attemptAfterSeq, failed);
3306
3465
  client._rpcPipeline?.requestPullDrainedCheck?.();
3307
3466
  }
3308
3467
  });
@@ -3311,6 +3470,7 @@ export class MessageDeliveryEngine {
3311
3470
  }
3312
3471
  async onRawGroupV2MessageCreated(data, expectedVersion, inlineGeneration) {
3313
3472
  const client = this.runtime.client;
3473
+ const syncGeneration = this.captureInlineAckGeneration();
3314
3474
  if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
3315
3475
  return;
3316
3476
  if (!isJsonObject(data)) {
@@ -3373,6 +3533,7 @@ export class MessageDeliveryEngine {
3373
3533
  }
3374
3534
  return;
3375
3535
  }
3536
+ this.recordBackgroundPullNeed(ns, seq);
3376
3537
  const pullActive = client._rpcPipeline?.hasPullActivity?.(ns) === true;
3377
3538
  if (pullActive || !this.beginRealtimeSync(ns)) {
3378
3539
  this.recordPendingGroupPull(ns, seq);
@@ -3388,7 +3549,9 @@ export class MessageDeliveryEngine {
3388
3549
  return;
3389
3550
  client._clientLog.warn(`V2 group push auto-sync failed: group=${groupId} err=${formatDeliveryError(exc)}`);
3390
3551
  }).finally(() => {
3391
- if (inlineGeneration === undefined || this.inlineGenerationIsCurrent(inlineGeneration)) {
3552
+ if (syncGeneration === this.captureInlineAckGeneration()
3553
+ && (inlineGeneration === undefined || this.inlineGenerationIsCurrent(inlineGeneration))) {
3554
+ this.finishBackgroundPull(ns, syncGeneration, this.syncState(ns).ack, false);
3392
3555
  this.endRealtimeSync(ns);
3393
3556
  this.schedulePendingGroupPullIfNeeded(ns, 'push-auto-pull-complete');
3394
3557
  }
@@ -3450,9 +3613,14 @@ export class MessageDeliveryEngine {
3450
3613
  if (!client._aid || !client._v2Session)
3451
3614
  return;
3452
3615
  const ns = `p2p:${client._aid}`;
3616
+ const generation = this.captureInlineAckGeneration();
3617
+ const afterSeq = this.syncState(ns).ack;
3618
+ this.recordBackgroundPullNeed(ns, positiveSafeSequenceHint(data.seq), true);
3619
+ let failed = false;
3453
3620
  client._safeAsync(this.runP2PForwardRecovery(ns, 50, true).catch((exc) => {
3621
+ failed = true;
3454
3622
  client._clientLog.debug(`P2P online unread hint background Forward failed: ns=${ns} err=${formatDeliveryError(exc)}`);
3455
- }));
3623
+ }).finally(() => this.finishBackgroundPull(ns, generation, afterSeq, failed)));
3456
3624
  }
3457
3625
  async saveSeqTrackerState() {
3458
3626
  const client = this.runtime.client;