@agentunion/fastaun 0.4.13 → 0.5.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.
Files changed (182) hide show
  1. package/CHANGELOG.md +547 -393
  2. package/_packed_docs/CHANGELOG-validators.md +144 -0
  3. package/_packed_docs/CHANGELOG.md +547 -393
  4. package/_packed_docs/INDEX.md +159 -84
  5. package/_packed_docs/KITE_DOCS_GUIDE.md +48 -31
  6. package/_packed_docs/agent.md/SCHEMA.md +2 -0
  7. package/_packed_docs/agent.md/examples/codeagent-claudecode.md +2 -0
  8. package/_packed_docs/agent.md/examples/human-developer.md +2 -0
  9. package/_packed_docs/agent.md/examples/openclaw-lobster.md +2 -0
  10. package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +2 -0
  11. 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 +2 -0
  12. package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +266 -686
  13. package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +331 -0
  14. package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +2 -0
  15. package/_packed_docs/design/AUNClient/346/213/206/345/210/206/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +2 -0
  16. package/_packed_docs/design/E2EE_V2/347/256/200/345/214/226/344/270/2721DH/345/212/240Per-AID_Wrap/346/226/271/346/241/210.md +2 -0
  17. package/_packed_docs/design//350/267/250/350/257/255/350/250/200/345/256/271/345/231/250E2E/346/265/213/350/257/225/346/226/271/346/241/210.md +2 -0
  18. package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +2 -0
  19. package/_packed_docs/protocol/00A-/350/256/276/350/256/241/345/216/237/345/210/231-/344/270/272Agent/350/200/214/347/224/237.md +2 -0
  20. package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +2 -0
  21. package/_packed_docs/protocol/02-/350/257/201/344/271/246/344/270/216/344/277/241/344/273/273/344/275/223/347/263/273.md +2 -0
  22. package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +2 -0
  23. package/_packed_docs/protocol/04-Peer-/345/255/220/345/215/217/350/256/256.md +2 -0
  24. package/_packed_docs/protocol/05-Relay-/345/255/220/345/215/217/350/256/256.md +2 -0
  25. package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +2 -0
  26. package/_packed_docs/protocol/08-AUN-E2EE-Group.md +296 -900
  27. package/_packed_docs/protocol/08-AUN-E2EE.md +2 -0
  28. package/_packed_docs/protocol/09-/345/256/211/345/205/250/350/200/203/350/231/221.md +2 -0
  29. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +66 -173
  30. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +55 -0
  31. package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +2 -0
  32. package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +2 -0
  33. package/_packed_docs/protocol/14-/344/272/244/344/272/222/346/234/272/345/210/266-/345/223/215/345/272/224/346/250/241/345/274/217/344/270/216/350/207/252/344/270/273/346/250/241/345/274/217.md +2 -0
  34. package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +2 -0
  35. package/_packed_docs/protocol/16-/347/263/273/347/273/237/347/233/256/345/275/225/344/277/235/346/212/244/346/226/271/346/241/210.md +177 -0
  36. package/_packed_docs/protocol/README.md +4 -1
  37. package/_packed_docs/protocol/agent.md/SCHEMA.md +2 -0
  38. package/_packed_docs/protocol/agent.md/examples/codeagent-claudecode.md +2 -0
  39. package/_packed_docs/protocol/agent.md/examples/human-developer.md +2 -0
  40. package/_packed_docs/protocol/agent.md/examples/openclaw-lobster.md +2 -0
  41. package/_packed_docs/protocol/aun-docs-guide.md +2 -0
  42. package/_packed_docs/protocol/index.md +10 -3
  43. package/_packed_docs/protocol//350/215/211/346/241/210-agent.md/347/255/276/345/220/215/345/215/217/350/256/256.md +2 -0
  44. package/_packed_docs/protocol//350/215/211/346/241/210-/346/213/222/347/273/235/344/277/241/345/217/267/345/215/217/350/256/256.md +2 -0
  45. package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +2 -0
  46. package/_packed_docs/protocol//351/231/204/345/275/225B-/346/211/251/345/261/225/346/200/247/346/214/207/345/215/227.md +2 -0
  47. package/_packed_docs/protocol//351/231/204/345/275/225C-/347/247/201/351/222/245/347/256/241/347/220/206/344/270/216/350/272/253/344/273/275/346/201/242/345/244/215.md +2 -0
  48. package/_packed_docs/protocol//351/231/204/345/275/225D-Root_CA_/346/262/273/347/220/206/346/234/272/345/210/266.md +2 -0
  49. package/_packed_docs/protocol//351/231/204/345/275/225E-Root_CA_/345/207/206/345/205/245/346/265/201/347/250/213.md +2 -0
  50. package/_packed_docs/protocol//351/231/204/345/275/225F-Issuer_CA_/347/224/263/350/257/267/346/265/201/347/250/213.md +2 -0
  51. package/_packed_docs/protocol//351/231/204/345/275/225G-AID_/345/255/244/345/204/277/351/242/204/351/230/262/344/270/216/346/225/221/346/217/264/346/234/272/345/210/266.md +2 -0
  52. package/_packed_docs/protocol//351/231/204/345/275/225H-Identity/346/234/215/345/212/241/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  53. package/_packed_docs/protocol//351/231/204/345/275/225I-/350/267/250/345/237/237/346/266/210/346/201/257/350/267/257/347/224/261/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  54. package/_packed_docs/protocol//351/231/204/345/275/225J-/345/256/242/346/210/267/347/253/257/346/216/245/345/205/245/347/244/272/344/276/213.md +2 -0
  55. package/_packed_docs/protocol//351/231/204/345/275/225K-Agent_Web/345/217/221/347/216/260/345/215/217/350/256/256.md +2 -0
  56. package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  57. package/_packed_docs/protocol//351/231/204/345/275/225M-JWT/350/256/244/350/257/201/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  58. package/_packed_docs/protocol//351/231/204/345/275/225N-/345/210/206/345/270/203/345/274/217Trace/345/215/217/350/256/256.md +2 -0
  59. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +2 -0
  60. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +2 -0
  61. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +2 -0
  62. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +2 -0
  63. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +6 -252
  64. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +219 -177
  65. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +4 -1
  66. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +2 -0
  67. package/_packed_docs/sdk/09-collab-rpc-manual.md +581 -0
  68. package/_packed_docs/sdk/09-custody-api-manual.md +2 -0
  69. package/_packed_docs/sdk/09-group-rpc-manual.md +301 -634
  70. package/_packed_docs/sdk/09-message-rpc-manual.md +2 -0
  71. package/_packed_docs/sdk/09-meta-rpc-manual.md +2 -0
  72. package/_packed_docs/sdk/09-payload-reference.md +2 -0
  73. package/_packed_docs/sdk/09-proxy-rpc-manual.md +2 -0
  74. package/_packed_docs/sdk/09-storage-rpc-manual.md +984 -274
  75. package/_packed_docs/sdk/09-stream-rpc-manual.md +2 -0
  76. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +36 -23
  77. package/_packed_docs/sdk/E2EE_V2/346/266/210/346/201/257/351/200/232/344/277/241/346/227/266/345/272/217/345/233/276.md +2 -0
  78. package/_packed_docs/sdk/INDEX.md +47 -30
  79. package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +2 -0
  80. package/_packed_docs/sdk/README.md +17 -15
  81. package/dist/aid-store.d.ts +25 -0
  82. package/dist/aid-store.js +80 -0
  83. package/dist/aid-store.js.map +1 -1
  84. package/dist/client/delivery.d.ts +11 -0
  85. package/dist/client/delivery.js +208 -25
  86. package/dist/client/delivery.js.map +1 -1
  87. package/dist/client/group-state.js +33 -11
  88. package/dist/client/group-state.js.map +1 -1
  89. package/dist/client/rpc-pipeline.d.ts +1 -0
  90. package/dist/client/rpc-pipeline.js +177 -65
  91. package/dist/client/rpc-pipeline.js.map +1 -1
  92. package/dist/client/v2-e2ee.d.ts +6 -1
  93. package/dist/client/v2-e2ee.js +199 -130
  94. package/dist/client/v2-e2ee.js.map +1 -1
  95. package/dist/client.d.ts +37 -1
  96. package/dist/client.js +491 -44
  97. package/dist/client.js.map +1 -1
  98. package/dist/collab/client.d.ts +46 -0
  99. package/dist/collab/client.js +136 -0
  100. package/dist/collab/client.js.map +1 -0
  101. package/dist/collab/errors.d.ts +15 -0
  102. package/dist/collab/errors.js +60 -0
  103. package/dist/collab/errors.js.map +1 -0
  104. package/dist/collab/index.d.ts +3 -0
  105. package/dist/collab/index.js +4 -0
  106. package/dist/collab/index.js.map +1 -0
  107. package/dist/collab/types.d.ts +110 -0
  108. package/dist/collab/types.js +2 -0
  109. package/dist/collab/types.js.map +1 -0
  110. package/dist/crypto.d.ts +2 -0
  111. package/dist/crypto.js +6 -0
  112. package/dist/crypto.js.map +1 -1
  113. package/dist/errors.d.ts +0 -12
  114. package/dist/errors.js +9 -25
  115. package/dist/errors.js.map +1 -1
  116. package/dist/facades.d.ts +94 -0
  117. package/dist/facades.js +255 -0
  118. package/dist/facades.js.map +1 -0
  119. package/dist/group-fs.d.ts +122 -0
  120. package/dist/group-fs.js +467 -0
  121. package/dist/group-fs.js.map +1 -0
  122. package/dist/group-id.d.ts +9 -12
  123. package/dist/group-id.js +41 -63
  124. package/dist/group-id.js.map +1 -1
  125. package/dist/group-resources.d.ts +31 -0
  126. package/dist/group-resources.js +84 -0
  127. package/dist/group-resources.js.map +1 -0
  128. package/dist/index.d.ts +9 -3
  129. package/dist/index.js +11 -1
  130. package/dist/index.js.map +1 -1
  131. package/dist/keystore/aid-db.d.ts +1 -0
  132. package/dist/keystore/aid-db.js +43 -43
  133. package/dist/keystore/aid-db.js.map +1 -1
  134. package/dist/keystore/local-identity-store.d.ts +16 -0
  135. package/dist/keystore/local-identity-store.js +190 -18
  136. package/dist/keystore/local-identity-store.js.map +1 -1
  137. package/dist/keystore/local-token-store.d.ts +0 -13
  138. package/dist/keystore/local-token-store.js +0 -105
  139. package/dist/keystore/local-token-store.js.map +1 -1
  140. package/dist/logger.d.ts +2 -0
  141. package/dist/logger.js +4 -0
  142. package/dist/logger.js.map +1 -1
  143. package/dist/register-flow.d.ts +1 -0
  144. package/dist/register-flow.js +3 -0
  145. package/dist/register-flow.js.map +1 -1
  146. package/dist/service-proxy.d.ts +1 -0
  147. package/dist/service-proxy.js +17 -4
  148. package/dist/service-proxy.js.map +1 -1
  149. package/dist/storage/errors.d.ts +27 -0
  150. package/dist/storage/errors.js +65 -0
  151. package/dist/storage/errors.js.map +1 -0
  152. package/dist/storage/index.d.ts +1 -0
  153. package/dist/storage/index.js +2 -0
  154. package/dist/storage/index.js.map +1 -0
  155. package/dist/storage/lowlevel.d.ts +321 -0
  156. package/dist/storage/lowlevel.js +475 -0
  157. package/dist/storage/lowlevel.js.map +1 -0
  158. package/dist/storage/types.d.ts +57 -0
  159. package/dist/storage/types.js +116 -0
  160. package/dist/storage/types.js.map +1 -0
  161. package/dist/storage/vfs.d.ts +166 -0
  162. package/dist/storage/vfs.js +524 -0
  163. package/dist/storage/vfs.js.map +1 -0
  164. package/dist/tools/cross-sdk-agent.js +708 -1
  165. package/dist/tools/cross-sdk-agent.js.map +1 -1
  166. package/dist/transport.d.ts +5 -0
  167. package/dist/transport.js +124 -21
  168. package/dist/transport.js.map +1 -1
  169. package/dist/types.d.ts +38 -0
  170. package/dist/v2/session/session.d.ts +2 -0
  171. package/dist/v2/session/session.js +53 -23
  172. package/dist/v2/session/session.js.map +1 -1
  173. package/dist/v2/state/commitment.d.ts +1 -1
  174. package/dist/v2/state/commitment.js +4 -2
  175. package/dist/v2/state/commitment.js.map +1 -1
  176. package/dist/validators.d.ts +32 -0
  177. package/dist/validators.js +128 -0
  178. package/dist/validators.js.map +1 -0
  179. package/dist/version.d.ts +1 -1
  180. package/dist/version.js +1 -1
  181. package/dist/version.js.map +1 -1
  182. package/package.json +1 -1
@@ -1,9 +1,10 @@
1
1
  import { slotIsolationKey } from '../config.js';
2
2
  import { normalizeGroupId } from '../group-id.js';
3
- import { isJsonObject } from '../types.js';
3
+ import { ConnectionState, isJsonObject } from '../types.js';
4
4
  const PUSHED_SEQS_LIMIT = 50_000;
5
5
  const PENDING_ORDERED_LIMIT = 50_000;
6
6
  const GROUP_RECALL_SEEN_LIMIT = 10_000;
7
+ const SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS = 200;
7
8
  const APP_MESSAGE_ENVELOPE_KEYS = [
8
9
  'module_id', 'message_type', 'type', 'kind', 'version',
9
10
  'from', 'from_aid', 'sender_aid', 'to', 'to_aid', 'group_id',
@@ -95,7 +96,8 @@ export class MessageDeliveryEngine {
95
96
  const eventPayload = payload;
96
97
  client._groupState?.handleGroupChangedV2Membership?.(eventPayload);
97
98
  if (eventPayload.action === 'dissolved') {
98
- const groupId = normalizeGroupId(String(eventPayload.group_id ?? '')) || String(eventPayload.group_id ?? '').trim();
99
+ const rawGroupId = eventPayload.group_aid ?? eventPayload.group_id ?? '';
100
+ const groupId = normalizeGroupId(String(rawGroupId)) || String(rawGroupId).trim();
99
101
  if (groupId)
100
102
  client._cleanupDissolvedGroup?.(groupId);
101
103
  }
@@ -495,7 +497,7 @@ export class MessageDeliveryEngine {
495
497
  .catch((e) => { client._clientLog.debug(`group recall auto-ack failed: group=${groupId} ${formatDeliveryError(e)}`); });
496
498
  }
497
499
  if (contig !== contigBefore)
498
- this.saveSeqTrackerState();
500
+ this.persistSeq(ns);
499
501
  }
500
502
  publishAppEvent(event, payload, source = 'direct') {
501
503
  const client = this.runtime.client;
@@ -565,6 +567,8 @@ export class MessageDeliveryEngine {
565
567
  }
566
568
  logMessageDebug(stage, source, event, message, opts = {}) {
567
569
  const client = this.runtime.client;
570
+ if (client._isMessageDebugEnabled?.() === false)
571
+ return;
568
572
  const record = {
569
573
  stage,
570
574
  source,
@@ -666,7 +670,7 @@ export class MessageDeliveryEngine {
666
670
  .catch((e) => { client._clientLog.debug(`P2P auto-ack failed: ${formatDeliveryError(e)}`); });
667
671
  }
668
672
  if (contigAfter !== contigBefore)
669
- this.saveSeqTrackerState();
673
+ this.persistSeq(ns);
670
674
  if (encryptedPush)
671
675
  return;
672
676
  }
@@ -743,7 +747,7 @@ export class MessageDeliveryEngine {
743
747
  .catch((e) => { client._clientLog.debug(`group recall auto-ack failed: group=${groupId} ${formatDeliveryError(e)}`); });
744
748
  }
745
749
  if (contigAfter !== contigBefore)
746
- this.saveSeqTrackerState();
750
+ this.persistSeq(ns);
747
751
  void published;
748
752
  return;
749
753
  }
@@ -766,7 +770,7 @@ export class MessageDeliveryEngine {
766
770
  .catch((e) => { client._clientLog.debug(`group message auto-ack failed: group=${groupId} ${formatDeliveryError(e)}`); });
767
771
  }
768
772
  if (contigAfter !== contigBefore)
769
- this.saveSeqTrackerState();
773
+ this.persistSeq(ns);
770
774
  if (encryptedPush)
771
775
  return;
772
776
  }
@@ -822,7 +826,7 @@ export class MessageDeliveryEngine {
822
826
  this.prunePushedSeqs(ns);
823
827
  if (client._seqTracker.getContiguousSeq(ns) !== afterSeq) {
824
828
  await this.drainOrderedMessages(ns, undefined, true);
825
- this.saveSeqTrackerState();
829
+ this.persistSeq(ns);
826
830
  }
827
831
  const contig = client._seqTracker.getContiguousSeq(ns);
828
832
  if (contig > 0 && contig !== afterSeq) {
@@ -836,9 +840,6 @@ export class MessageDeliveryEngine {
836
840
  finally {
837
841
  client._gapFillDone.delete(dedupKey);
838
842
  client._releasePullGate(ns, token);
839
- if (filled > 0 && client._seqTracker.getContiguousSeq(ns) > afterSeq) {
840
- void this.fillP2pGap();
841
- }
842
843
  }
843
844
  }
844
845
  async fillGroupGap(groupId) {
@@ -865,7 +866,7 @@ export class MessageDeliveryEngine {
865
866
  this.prunePushedSeqs(ns);
866
867
  if (client._seqTracker.getContiguousSeq(ns) !== afterSeq) {
867
868
  await this.drainOrderedMessages(ns, undefined, true);
868
- this.saveSeqTrackerState();
869
+ this.persistSeq(ns);
869
870
  }
870
871
  client._clientLog.debug(`group message gap fill done: group=${groupId}, after_seq=${afterSeq}, filled=${filled}`);
871
872
  }
@@ -910,6 +911,7 @@ export class MessageDeliveryEngine {
910
911
  device_id: client._deviceId,
911
912
  limit: 50,
912
913
  _pull_gate_locked: true,
914
+ _rpc_background: true,
913
915
  });
914
916
  if (!isJsonObject(result))
915
917
  return;
@@ -962,7 +964,7 @@ export class MessageDeliveryEngine {
962
964
  const ackContig = client._seqTracker.getContiguousSeq(ns);
963
965
  await this.drainOrderedMessages(ns);
964
966
  if (ackContig !== pageContigBefore && !hasDissolvedEvent) {
965
- this.saveSeqTrackerState();
967
+ this.persistSeq(ns);
966
968
  }
967
969
  if (eventObjects.length > 0 && ackContig > 0 && ackContig !== pageContigBefore) {
968
970
  const ackSeq = this.clampAckSeq('group.ack_events', 'event_seq', ns, ackContig);
@@ -998,6 +1000,7 @@ export class MessageDeliveryEngine {
998
1000
  }
999
1001
  async handleGroupChangedEventSeq(data, groupId) {
1000
1002
  const client = this.runtime.client;
1003
+ groupId = normalizeGroupId(groupId) || String(groupId ?? '').trim();
1001
1004
  let needPull = false;
1002
1005
  const rawEventSeq = data.event_seq;
1003
1006
  const eventSeq = Number(rawEventSeq);
@@ -1017,6 +1020,7 @@ export class MessageDeliveryEngine {
1017
1020
  const contigBefore = client._seqTracker.getContiguousSeq(ns);
1018
1021
  if (eventSeq <= contigBefore || client._pushedSeqs.get(ns)?.has(eventSeq)) {
1019
1022
  client._clientLog.debug(`group.changed skipped duplicate/stale: group=${groupId}, event_seq=${eventSeq}, contiguous=${contigBefore}`);
1023
+ this.fireGroupEventAck(groupId, Math.min(eventSeq, contigBefore > 0 ? contigBefore : eventSeq), 'covered push');
1020
1024
  return;
1021
1025
  }
1022
1026
  this.enqueueOrderedMessage(ns, 'group.changed', eventSeq, data);
@@ -1025,7 +1029,7 @@ export class MessageDeliveryEngine {
1025
1029
  await this.drainOrderedMessages(ns);
1026
1030
  if (ackContig > 0 && ackContig !== contigBefore) {
1027
1031
  if (data.action !== 'dissolved')
1028
- this.saveSeqTrackerState();
1032
+ this.persistSeq(ns);
1029
1033
  const ackSeq = this.clampAckSeq('group.ack_events', 'event_seq', ns, ackContig);
1030
1034
  client._transport.call('group.ack_events', {
1031
1035
  group_id: groupId,
@@ -1042,6 +1046,53 @@ export class MessageDeliveryEngine {
1042
1046
  });
1043
1047
  }
1044
1048
  }
1049
+ fireGroupEventAck(groupId, eventSeq, reason) {
1050
+ const client = this.runtime.client;
1051
+ const gid = normalizeGroupId(groupId) || String(groupId ?? '').trim();
1052
+ if (!gid)
1053
+ return;
1054
+ const ns = `group_event:${gid}`;
1055
+ const ackSeq = this.clampAckSeq('group.ack_events', 'event_seq', ns, Number(eventSeq) || 0);
1056
+ if (ackSeq <= 0)
1057
+ return;
1058
+ const params = {
1059
+ group_id: gid,
1060
+ event_seq: ackSeq,
1061
+ device_id: client._deviceId,
1062
+ slot_id: client._slotId,
1063
+ _rpc_background: true,
1064
+ };
1065
+ try {
1066
+ Promise.resolve(client.call('group.ack_events', params)).catch((e) => {
1067
+ client._clientLog.debug(`group event ${reason} ack failed: group=${gid} ${formatDeliveryError(e)}`);
1068
+ });
1069
+ }
1070
+ catch (e) {
1071
+ client._clientLog.debug(`group event ${reason} ack failed: group=${gid} ${formatDeliveryError(e)}`);
1072
+ }
1073
+ }
1074
+ fireGroupV2Ack(groupId, upToSeq, reason) {
1075
+ const client = this.runtime.client;
1076
+ const gid = normalizeGroupId(groupId) || String(groupId ?? '').trim();
1077
+ if (!gid)
1078
+ return;
1079
+ const ns = `group:${gid}`;
1080
+ const ackSeq = this.clampAckSeq('group.v2.ack', 'up_to_seq', ns, Number(upToSeq) || 0);
1081
+ if (ackSeq <= 0)
1082
+ return;
1083
+ try {
1084
+ Promise.resolve(client.call('group.v2.ack', {
1085
+ group_id: gid,
1086
+ up_to_seq: ackSeq,
1087
+ _rpc_background: true,
1088
+ })).catch((e) => {
1089
+ client._clientLog.debug(`group.v2 ${reason} ack failed: group=${gid} ${formatDeliveryError(e)}`);
1090
+ });
1091
+ }
1092
+ catch (e) {
1093
+ client._clientLog.debug(`group.v2 ${reason} ack failed: group=${gid} ${formatDeliveryError(e)}`);
1094
+ }
1095
+ }
1045
1096
  isSelfJoinGroupChanged(data) {
1046
1097
  const action = String(data.action ?? '').trim();
1047
1098
  if (!['member_added', 'joined', 'join_approved', 'invite_code_used'].includes(action))
@@ -1083,7 +1134,7 @@ export class MessageDeliveryEngine {
1083
1134
  const newContig = client._seqTracker.getContiguousSeq(ns);
1084
1135
  const needPull = pushSeq > newContig && !published;
1085
1136
  if (newContig !== contigBefore) {
1086
- client._saveSeqTrackerState();
1137
+ this.persistSeq(ns);
1087
1138
  }
1088
1139
  if (newContig > 0 && newContig !== contigBefore) {
1089
1140
  const ackSeq = this.clampAckSeq('message.v2.ack', 'up_to_seq', ns, newContig);
@@ -1155,7 +1206,7 @@ export class MessageDeliveryEngine {
1155
1206
  this.runtime.delivery.setOnlineUnreadHintDrainActive(true);
1156
1207
  try {
1157
1208
  while (client._onlineUnreadHintQueue.size > 0) {
1158
- if (client.state !== 'ready')
1209
+ if (client.state !== ConnectionState.READY)
1159
1210
  return;
1160
1211
  if (client._sessionOptions?.background_sync === false)
1161
1212
  return;
@@ -1208,8 +1259,9 @@ export class MessageDeliveryEngine {
1208
1259
  client._seqTracker.updateMaxSeen(ns, seq);
1209
1260
  const contigBefore = client._seqTracker.getContiguousSeq(ns);
1210
1261
  client._clientLog.debug(`_onRawGroupV2MessageCreated enter: group=${groupId}, seq=${seq}, contiguous=${contigBefore}, max_seen=${client._seqTracker.getMaxSeenSeq(ns)}`);
1211
- if (contigBefore === seq) {
1262
+ if (contigBefore === seq || (eventKind === 'group.online_unread_hint' && contigBefore > seq)) {
1212
1263
  client._clientLog.debug(`_onRawGroupV2MessageCreated duplicate push already covered: group=${groupId} seq=${seq}`);
1264
+ this.fireGroupV2Ack(groupId, seq, 'covered push');
1213
1265
  return;
1214
1266
  }
1215
1267
  const afterSeq = client._repairPushContiguousBound(ns, seq, false, '_raw.group.v2.message_created');
@@ -1327,23 +1379,132 @@ export class MessageDeliveryEngine {
1327
1379
  }
1328
1380
  saveSeqTrackerState() {
1329
1381
  const client = this.runtime.client;
1330
- if (!client._aid)
1382
+ const context = this.currentSeqTrackerContext();
1383
+ if (!context) {
1384
+ this.flushSeqTrackerPending();
1331
1385
  return;
1386
+ }
1332
1387
  const state = client._seqTracker.exportState();
1333
- if (Object.keys(state).length === 0)
1388
+ const pending = this.seqTrackerPending();
1389
+ if (pending.size > 0 && client._seqTrackerPendingPersistContext === context) {
1390
+ const liveNamespaces = new Set(Object.keys(state));
1391
+ for (const ns of [...pending.keys()]) {
1392
+ if (!liveNamespaces.has(ns))
1393
+ pending.delete(ns);
1394
+ }
1395
+ if (pending.size === 0)
1396
+ client._seqTrackerPendingPersistContext = null;
1397
+ }
1398
+ if (Object.keys(state).length > 0)
1399
+ this.mergeSeqTrackerPending(context, state);
1400
+ this.flushSeqTrackerPending();
1401
+ }
1402
+ persistSeq(ns, forceSeq) {
1403
+ const client = this.runtime.client;
1404
+ const context = this.currentSeqTrackerContext();
1405
+ if (!context) {
1406
+ this.flushSeqTrackerPending();
1407
+ return;
1408
+ }
1409
+ const seq = forceSeq ?? client._seqTracker.getContiguousSeq(ns);
1410
+ if (!ns || !Number.isFinite(seq) || seq <= 0)
1411
+ return;
1412
+ if (forceSeq !== undefined) {
1413
+ client._seqTracker.forceContiguousSeq(ns, seq);
1414
+ }
1415
+ this.mergeSeqTrackerPending(context, { [ns]: seq });
1416
+ this.scheduleSeqTrackerFlush();
1417
+ }
1418
+ flushSeqTrackerPending() {
1419
+ const client = this.runtime.client;
1420
+ this.cancelSeqTrackerFlushTimer();
1421
+ const context = client._seqTrackerPendingPersistContext;
1422
+ const pending = this.seqTrackerPending();
1423
+ if (!context || pending.size === 0) {
1424
+ client._seqTrackerPendingPersistContext = null;
1425
+ return;
1426
+ }
1427
+ const [aid, deviceId, slotId] = JSON.parse(context);
1428
+ const state = Object.fromEntries(pending.entries());
1429
+ pending.clear();
1430
+ client._seqTrackerPendingPersistContext = null;
1431
+ this.writeSeqTrackerState(aid, deviceId, slotId, state);
1432
+ }
1433
+ dropSeqTrackerPending(ns) {
1434
+ const client = this.runtime.client;
1435
+ const pending = this.seqTrackerPending();
1436
+ pending.delete(ns);
1437
+ if (pending.size === 0) {
1438
+ client._seqTrackerPendingPersistContext = null;
1439
+ this.cancelSeqTrackerFlushTimer();
1440
+ }
1441
+ }
1442
+ currentSeqTrackerContext() {
1443
+ const client = this.runtime.client;
1444
+ if (!client._aid)
1445
+ return null;
1446
+ return JSON.stringify([client._aid, client._deviceId, client._slotId]);
1447
+ }
1448
+ seqTrackerPending() {
1449
+ const client = this.runtime.client;
1450
+ if (!(client._seqTrackerPendingPersist instanceof Map)) {
1451
+ client._seqTrackerPendingPersist = new Map();
1452
+ }
1453
+ return client._seqTrackerPendingPersist;
1454
+ }
1455
+ mergeSeqTrackerPending(context, state) {
1456
+ const client = this.runtime.client;
1457
+ const pending = this.seqTrackerPending();
1458
+ const pendingContext = client._seqTrackerPendingPersistContext;
1459
+ if (pendingContext && pendingContext !== context && pending.size > 0) {
1460
+ this.flushSeqTrackerPending();
1461
+ }
1462
+ client._seqTrackerPendingPersistContext = context;
1463
+ for (const [ns, seq] of Object.entries(state)) {
1464
+ const seqNum = Number(seq);
1465
+ if (ns && Number.isFinite(seqNum) && seqNum > 0) {
1466
+ pending.set(ns, seqNum);
1467
+ }
1468
+ }
1469
+ }
1470
+ scheduleSeqTrackerFlush() {
1471
+ const client = this.runtime.client;
1472
+ const pending = this.seqTrackerPending();
1473
+ if (pending.size === 0)
1474
+ return;
1475
+ if (client._seqTrackerFlushTimer)
1476
+ return;
1477
+ client._seqTrackerFlushTimer = setTimeout(() => {
1478
+ client._seqTrackerFlushTimer = null;
1479
+ this.flushSeqTrackerPending();
1480
+ }, SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS);
1481
+ }
1482
+ cancelSeqTrackerFlushTimer() {
1483
+ const client = this.runtime.client;
1484
+ if (client._seqTrackerFlushTimer) {
1485
+ clearTimeout(client._seqTrackerFlushTimer);
1486
+ client._seqTrackerFlushTimer = null;
1487
+ }
1488
+ }
1489
+ writeSeqTrackerState(aid, deviceId, slotId, state) {
1490
+ const client = this.runtime.client;
1491
+ if (!aid || Object.keys(state).length === 0)
1334
1492
  return;
1335
1493
  try {
1336
1494
  const saveFn = client._tokenStore.saveSeq;
1337
1495
  if (typeof saveFn === 'function') {
1338
1496
  for (const [ns, seq] of Object.entries(state)) {
1339
- saveFn.call(client._tokenStore, client._aid, client._deviceId, client._slotId, ns, seq);
1497
+ saveFn.call(client._tokenStore, aid, deviceId, slotId, ns, seq);
1340
1498
  }
1341
1499
  return;
1342
1500
  }
1343
1501
  const updater = client._tokenStore.updateInstanceState;
1344
1502
  if (typeof updater === 'function') {
1345
- updater.call(client._tokenStore, client._aid, client._deviceId, client._slotId, (metadata) => {
1346
- metadata.seq_tracker_state = state;
1503
+ updater.call(client._tokenStore, aid, deviceId, slotId, (metadata) => {
1504
+ const existing = isJsonObject(metadata.seq_tracker_state)
1505
+ ? { ...metadata.seq_tracker_state }
1506
+ : {};
1507
+ metadata.seq_tracker_state = { ...existing, ...state };
1347
1508
  return metadata;
1348
1509
  });
1349
1510
  }
@@ -1353,9 +1514,9 @@ export class MessageDeliveryEngine {
1353
1514
  client._clientLog.warn(`save SeqTracker state failed: ${error}`);
1354
1515
  client._dispatcher.publish('seq_tracker.persist_error', {
1355
1516
  phase: 'save',
1356
- aid: client._aid,
1357
- device_id: client._deviceId,
1358
- slot_id: client._slotId,
1517
+ aid,
1518
+ device_id: deviceId,
1519
+ slot_id: slotId,
1359
1520
  error: String(error),
1360
1521
  }).catch(() => { });
1361
1522
  }
@@ -1365,6 +1526,7 @@ export class MessageDeliveryEngine {
1365
1526
  if (!client._aid || !ns)
1366
1527
  return;
1367
1528
  const seq = client._seqTracker.getContiguousSeq(ns);
1529
+ this.dropSeqTrackerPending(ns);
1368
1530
  try {
1369
1531
  if (seq > 0 && typeof client._tokenStore.saveSeq === 'function') {
1370
1532
  client._tokenStore.saveSeq(client._aid, client._deviceId, client._slotId, ns, seq);
@@ -1375,6 +1537,18 @@ export class MessageDeliveryEngine {
1375
1537
  deleteSeq.call(client._tokenStore, client._aid, client._deviceId, client._slotId, ns);
1376
1538
  return;
1377
1539
  }
1540
+ const updater = client._tokenStore.updateInstanceState;
1541
+ if (seq <= 0 && typeof updater === 'function') {
1542
+ updater.call(client._tokenStore, client._aid, client._deviceId, client._slotId, (metadata) => {
1543
+ if (isJsonObject(metadata.seq_tracker_state)) {
1544
+ const next = { ...metadata.seq_tracker_state };
1545
+ delete next[ns];
1546
+ metadata.seq_tracker_state = next;
1547
+ }
1548
+ return metadata;
1549
+ });
1550
+ return;
1551
+ }
1378
1552
  if (seq > 0) {
1379
1553
  this.saveSeqTrackerState();
1380
1554
  }
@@ -1434,6 +1608,7 @@ export class MessageDeliveryEngine {
1434
1608
  const queue = client._pendingOrderedMsgs.get(ns);
1435
1609
  if (!queue || queue.size === 0)
1436
1610
  return;
1611
+ let delivered = false;
1437
1612
  while (true) {
1438
1613
  const contig = client._seqTracker.getContiguousSeq(ns);
1439
1614
  const ready = [...queue.keys()]
@@ -1462,10 +1637,14 @@ export class MessageDeliveryEngine {
1462
1637
  await this.publishOrderedQueueItem(ns, item.event, seq, item.payload, 'ordered-drain', pullResponse);
1463
1638
  this.markPublishedSeq(ns, seq);
1464
1639
  client._markOrderedSeqDelivered(ns, seq);
1640
+ delivered = true;
1465
1641
  client._clientLog.debug(`publish ordered drain delivered: ns=${ns}, seq=${seq}, event=${item.event}`);
1466
1642
  }
1467
- if (queue.size === 0)
1643
+ if (queue.size === 0) {
1468
1644
  client._pendingOrderedMsgs.delete(ns);
1645
+ if (delivered)
1646
+ this.saveSeqTrackerState();
1647
+ }
1469
1648
  }
1470
1649
  async publishOrderedMessage(event, ns, seq, payload) {
1471
1650
  const client = this.runtime.client;
@@ -1511,6 +1690,8 @@ export class MessageDeliveryEngine {
1511
1690
  client._markOrderedSeqDelivered(ns, seqNum);
1512
1691
  client._clientLog.debug(`publish ordered delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
1513
1692
  await this.drainOrderedMessages(ns);
1693
+ if (!client._pendingOrderedMsgs.get(ns))
1694
+ this.saveSeqTrackerState();
1514
1695
  return true;
1515
1696
  }
1516
1697
  async publishOrderedGroupRecall(ns, seq, message) {
@@ -1530,6 +1711,8 @@ export class MessageDeliveryEngine {
1530
1711
  this.markPublishedSeq(ns, seqNum);
1531
1712
  client._markOrderedSeqDelivered?.(ns, seqNum);
1532
1713
  await this.drainOrderedMessages(ns);
1714
+ if (!client._pendingOrderedMsgs.get(ns))
1715
+ this.saveSeqTrackerState();
1533
1716
  return true;
1534
1717
  }
1535
1718
  async publishPulledMessage(event, ns, seq, payload) {