@agentunion/fastaun 0.5.2 → 0.5.4

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 (37) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/_packed_docs/CHANGELOG.md +70 -0
  3. package/_packed_docs/INDEX.md +16 -10
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +4 -2
  5. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +202 -75
  6. package/_packed_docs/protocol/README.md +3 -2
  7. package/_packed_docs/protocol/aun-docs-guide.md +3 -2
  8. package/_packed_docs/protocol/index.md +6 -5
  9. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +20 -1
  10. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +303 -28
  11. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +30 -6
  12. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +28 -12
  13. package/_packed_docs/sdk/09-group-rpc-manual.md +126 -42
  14. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +6 -4
  15. package/_packed_docs/sdk/INDEX.md +17 -17
  16. package/dist/client/delivery.d.ts +4 -0
  17. package/dist/client/delivery.js +175 -8
  18. package/dist/client/delivery.js.map +1 -1
  19. package/dist/client/v2-e2ee.js +2 -2
  20. package/dist/client/v2-e2ee.js.map +1 -1
  21. package/dist/client.d.ts +23 -1
  22. package/dist/client.js +37 -2
  23. package/dist/client.js.map +1 -1
  24. package/dist/facades.d.ts +9 -0
  25. package/dist/facades.js +287 -33
  26. package/dist/facades.js.map +1 -1
  27. package/dist/group-index.d.ts +109 -0
  28. package/dist/group-index.js +308 -0
  29. package/dist/group-index.js.map +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +1 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/tools/cross-sdk-agent.js +3 -1
  34. package/dist/tools/cross-sdk-agent.js.map +1 -1
  35. package/dist/version.d.ts +1 -1
  36. package/dist/version.js +1 -1
  37. package/package.json +1 -1
@@ -3,6 +3,7 @@ import { normalizeGroupId } from '../group-id.js';
3
3
  import { ConnectionState, isJsonObject } from '../types.js';
4
4
  const PUSHED_SEQS_LIMIT = 50_000;
5
5
  const PENDING_ORDERED_LIMIT = 50_000;
6
+ const MESSAGE_RECALL_SEEN_LIMIT = 10_000;
6
7
  const GROUP_RECALL_SEEN_LIMIT = 10_000;
7
8
  const SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS = 200;
8
9
  const APP_MESSAGE_ENVELOPE_KEYS = [
@@ -11,6 +12,13 @@ const APP_MESSAGE_ENVELOPE_KEYS = [
11
12
  'timestamp', 'created_at', 'encrypted',
12
13
  'context', 'protected_headers', 'headers', 'payload_type',
13
14
  ];
15
+ const RECALL_PAYLOAD_KEYS = [
16
+ 'message_ids', 'target_message_seqs',
17
+ 'recalled_message_id', 'target_message_id', 'original_message_id',
18
+ 'target_seq', 'original_seq',
19
+ 'notice_message_id', 'notice_seq', 'event_seq',
20
+ 'sender_aid', 'recalled_by', 'recalled_at', 'reason',
21
+ ];
14
22
  const APP_SEND_ENVELOPE_METHODS = new Set([
15
23
  'message.send',
16
24
  'group.send',
@@ -79,6 +87,10 @@ export class MessageDeliveryEngine {
79
87
  await this.publishOrderedGroupChanged(payload, source);
80
88
  return;
81
89
  }
90
+ if (event === 'message.recalled') {
91
+ await this.publishMessageRecallTombstone(seq, payload);
92
+ return;
93
+ }
82
94
  if (pullResponse) {
83
95
  const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source));
84
96
  if (isPromiseLike(published))
@@ -308,6 +320,11 @@ export class MessageDeliveryEngine {
308
320
  event[key] = msg[key];
309
321
  }
310
322
  }
323
+ for (const key of RECALL_PAYLOAD_KEYS) {
324
+ if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
325
+ event[key] = msg[key];
326
+ }
327
+ }
311
328
  const rawIds = event.message_ids;
312
329
  let messageIds = Array.isArray(rawIds)
313
330
  ? rawIds.map((item) => String(item ?? '').trim()).filter(Boolean)
@@ -349,6 +366,57 @@ export class MessageDeliveryEngine {
349
366
  return { event: 'message.recalled', payload: recall };
350
367
  return { event: 'message.received', payload: message };
351
368
  }
369
+ messageRecallDedupKey(payload) {
370
+ const ids = payload.message_ids;
371
+ const idPart = Array.isArray(ids)
372
+ ? ids.map((i) => String(i ?? '').trim()).filter(Boolean).sort().join(',')
373
+ : String(ids ?? '').trim();
374
+ if (idPart)
375
+ return `p2p|id:${idPart}`;
376
+ for (const key of ['recalled_message_id', 'target_message_id', 'original_message_id']) {
377
+ const value = String(payload[key] ?? '').trim();
378
+ if (value)
379
+ return `p2p|id:${value}`;
380
+ }
381
+ const rawSeqs = payload.target_message_seqs;
382
+ const seqPart = Array.isArray(rawSeqs)
383
+ ? rawSeqs.map((seq) => String(seq ?? '').trim()).filter(Boolean).sort().join(',')
384
+ : String(rawSeqs ?? payload.target_seq ?? payload.original_seq ?? '').trim();
385
+ if (seqPart) {
386
+ const fromAid = String(payload.from ?? payload.from_aid ?? payload.sender_aid ?? '').trim();
387
+ const toAid = String(payload.to ?? payload.to_aid ?? '').trim();
388
+ return `p2p|from:${fromAid}|to:${toAid}|seq:${seqPart}`;
389
+ }
390
+ const tombstoneId = String(payload.tombstone_message_id ?? payload.message_id ?? '').trim();
391
+ if (tombstoneId)
392
+ return `p2p|tombstone:${tombstoneId}`;
393
+ return `p2p|unknown:${Date.now()}:${Math.random()}`;
394
+ }
395
+ async publishMessageRecallTombstone(seq, message) {
396
+ const client = this.runtime.client;
397
+ const eventPayload = this.recallEventFromMessage(message);
398
+ if (!eventPayload)
399
+ return false;
400
+ const dedupKey = this.messageRecallDedupKey(eventPayload);
401
+ let seen = client._messageRecallSeen;
402
+ if (!seen) {
403
+ seen = new Map();
404
+ client._messageRecallSeen = seen;
405
+ }
406
+ if (seen.has(dedupKey)) {
407
+ client._clientLog.debug(`message.recalled dedup suppressed: seq=${String(seq)} key=${dedupKey}`);
408
+ return false;
409
+ }
410
+ seen.set(dedupKey, Date.now());
411
+ if (seen.size > MESSAGE_RECALL_SEEN_LIMIT) {
412
+ const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - MESSAGE_RECALL_SEEN_LIMIT);
413
+ for (const [oldKey] of drop)
414
+ seen.delete(oldKey);
415
+ }
416
+ await client._publishAppEvent('message.recalled', eventPayload, 'message-recall');
417
+ client._clientLog.debug(`message.recalled published: seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
418
+ return true;
419
+ }
352
420
  recallEventFromGroupMessage(message) {
353
421
  if (!isJsonObject(message))
354
422
  return null;
@@ -365,6 +433,11 @@ export class MessageDeliveryEngine {
365
433
  event[key] = msg[key];
366
434
  }
367
435
  }
436
+ for (const key of RECALL_PAYLOAD_KEYS) {
437
+ if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
438
+ event[key] = msg[key];
439
+ }
440
+ }
368
441
  const rawIds = event.message_ids;
369
442
  let messageIds = Array.isArray(rawIds)
370
443
  ? rawIds.map((item) => String(item ?? '').trim()).filter(Boolean)
@@ -395,9 +468,9 @@ export class MessageDeliveryEngine {
395
468
  return event;
396
469
  }
397
470
  groupRecallDedupKey(groupId, payload) {
398
- // 群撤回去重键:group_id + 排序后的 message_ids。
471
+ // 群撤回去重键:group_id + 原始消息标识。
399
472
  // 一条消息只能被撤回一次(服务端 group_message_recalls uk_recall_msg_id 唯一约束),
400
- // (group_id, sorted message_ids) 已能唯一标识一次撤回。
473
+ // (group_id, sorted message_ids) 已能唯一标识一次撤回;缺 message_ids 时按原消息 id/seq 兜底。
401
474
  // 去重键不含 recalled_at:占位/通知 tombstone(pull,事务内时间)与在线 push(事务后重取时间)
402
475
  // 三条通道对同一次撤回可能携带不同来源的时间戳,纳入 recalled_at 会使去重失效、回调多次。
403
476
  // group_id 归一化一次,使去重键与来源无关(pull 用 normalizeGroupId,push 可能是原始值)。
@@ -405,15 +478,35 @@ export class MessageDeliveryEngine {
405
478
  const ids = payload.message_ids;
406
479
  const idPart = Array.isArray(ids)
407
480
  ? ids.map((i) => String(i ?? '').trim()).filter(Boolean).sort().join(',')
408
- : String(ids ?? '');
409
- return `${normalizedGroupId}|${idPart}`;
481
+ : String(ids ?? '').trim();
482
+ if (idPart)
483
+ return `${normalizedGroupId}|id:${idPart}`;
484
+ for (const key of ['recalled_message_id', 'target_message_id', 'original_message_id']) {
485
+ const value = String(payload[key] ?? '').trim();
486
+ if (value)
487
+ return `${normalizedGroupId}|id:${value}`;
488
+ }
489
+ const rawSeqs = payload.target_message_seqs;
490
+ const seqPart = Array.isArray(rawSeqs)
491
+ ? rawSeqs.map((seq) => String(seq ?? '').trim()).filter(Boolean).sort().join(',')
492
+ : String(rawSeqs ?? payload.original_seq ?? '').trim();
493
+ if (seqPart)
494
+ return `${normalizedGroupId}|seq:${seqPart}`;
495
+ const tombstoneId = String(payload.tombstone_message_id ?? payload.message_id ?? '').trim();
496
+ if (tombstoneId)
497
+ return `${normalizedGroupId}|tombstone:${tombstoneId}`;
498
+ return `${normalizedGroupId}|unknown:${Date.now()}:${Math.random()}`;
410
499
  }
411
500
  async publishGroupRecallTombstone(groupId, seq, message) {
412
501
  const client = this.runtime.client;
413
502
  const eventPayload = this.recallEventFromGroupMessage(message);
414
503
  if (!eventPayload)
415
504
  return false;
416
- const dedupKey = this.groupRecallDedupKey(groupId, eventPayload);
505
+ const rawGroupId = String(eventPayload.group_id ?? groupId ?? '').trim();
506
+ const dedupGroupId = normalizeGroupId(rawGroupId) || rawGroupId;
507
+ if (dedupGroupId)
508
+ eventPayload.group_id = dedupGroupId;
509
+ const dedupKey = this.groupRecallDedupKey(dedupGroupId, eventPayload);
417
510
  let seen = client._groupRecallSeen;
418
511
  if (!seen) {
419
512
  seen = new Map();
@@ -605,6 +698,29 @@ export class MessageDeliveryEngine {
605
698
  }
606
699
  return true;
607
700
  }
701
+ async runPushProcessSerialized(ns, operation) {
702
+ const client = this.runtime.client;
703
+ if (!ns) {
704
+ await operation();
705
+ return;
706
+ }
707
+ let queues = client._pushProcessQueues;
708
+ if (!queues) {
709
+ queues = new Map();
710
+ client._pushProcessQueues = queues;
711
+ }
712
+ const previous = queues.get(ns) ?? Promise.resolve();
713
+ const current = previous.catch(() => undefined).then(operation);
714
+ const stored = current.then(() => undefined, () => undefined);
715
+ queues.set(ns, stored);
716
+ try {
717
+ await current;
718
+ }
719
+ finally {
720
+ if (queues.get(ns) === stored)
721
+ queues.delete(ns);
722
+ }
723
+ }
608
724
  async onRawMessageReceived(data) {
609
725
  const client = this.runtime.client;
610
726
  const tStart = Date.now();
@@ -615,7 +731,10 @@ export class MessageDeliveryEngine {
615
731
  else {
616
732
  client._clientLog.debug('_onRawMessageReceived enter: non-object payload');
617
733
  }
618
- this.processAndPublishMessage(data).catch((exc) => {
734
+ const ns = isJsonObject(data) && client._aid && data.seq !== undefined
735
+ ? `p2p:${client._aid}`
736
+ : '';
737
+ this.runPushProcessSerialized(ns, () => this.processAndPublishMessage(data)).catch((exc) => {
619
738
  client._clientLog.warn(`P2P message decrypt failed: ${formatDeliveryError(exc)}`);
620
739
  if (isJsonObject(data)) {
621
740
  const src = data;
@@ -633,6 +752,39 @@ export class MessageDeliveryEngine {
633
752
  });
634
753
  client._clientLog.debug(`_onRawMessageReceived exit: elapsed=${Date.now() - tStart}ms (handler dispatched)`);
635
754
  }
755
+ async onRawMessageRecalled(data) {
756
+ const client = this.runtime.client;
757
+ if (!isJsonObject(data))
758
+ return;
759
+ const msg = { ...data };
760
+ if (!('type' in msg))
761
+ msg.type = 'message.recalled';
762
+ if (!this.messageTargetsCurrentInstance(msg))
763
+ return;
764
+ const seq = msg.seq;
765
+ if (seq !== undefined && seq !== null && client._aid) {
766
+ const ns = `p2p:${client._aid}`;
767
+ const seqNum = Number(seq);
768
+ if (Number.isFinite(seqNum) && seqNum > 0)
769
+ client._seqTracker.updateMaxSeen(ns, seqNum);
770
+ const contigBefore = client._seqTracker.getContiguousSeq(ns);
771
+ const published = await this.publishOrderedMessage('message.recalled', ns, seq, msg);
772
+ const contigAfter = client._seqTracker.getContiguousSeq(ns);
773
+ if (Number(seq) > contigAfter && !published) {
774
+ this.fillP2pGap().catch((exc) => client._clientLog.warn(`background recall gap fill failed: ${formatDeliveryError(exc)}`));
775
+ }
776
+ const contig = client._seqTracker.getContiguousSeq(ns);
777
+ if (contig > 0) {
778
+ const ackSeq = this.clampAckSeq('message.ack', 'seq', ns, contig);
779
+ client._withBackgroundRpc(() => client._ackV2(ackSeq))
780
+ .catch((e) => { client._clientLog.debug(`P2P recall auto-ack failed: ${formatDeliveryError(e)}`); });
781
+ }
782
+ if (contigAfter !== contigBefore)
783
+ this.persistSeq(ns);
784
+ return;
785
+ }
786
+ await this.publishMessageRecallTombstone(seq, msg);
787
+ }
636
788
  async processAndPublishMessage(data) {
637
789
  const client = this.runtime.client;
638
790
  if (!isJsonObject(data)) {
@@ -692,7 +844,10 @@ export class MessageDeliveryEngine {
692
844
  else {
693
845
  client._clientLog.debug('_onRawGroupMessageCreated enter: non-object payload');
694
846
  }
695
- this.processAndPublishGroupMessage(data).catch((exc) => {
847
+ const groupNs = isJsonObject(data)
848
+ ? String(data.group_id ?? '').trim()
849
+ : '';
850
+ this.runPushProcessSerialized(groupNs ? `group:${groupNs}` : '', () => this.processAndPublishGroupMessage(data)).catch((exc) => {
696
851
  client._clientLog.warn(`group message decrypt failed: ${formatDeliveryError(exc)}`);
697
852
  if (isJsonObject(data)) {
698
853
  const src = data;
@@ -1720,6 +1875,9 @@ export class MessageDeliveryEngine {
1720
1875
  const seqNum = Number(seq);
1721
1876
  if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0 || !ns) {
1722
1877
  client._clientLog.debug(`publish pulled direct(no-seq): event=${event}, ns=${ns || '<none>'}, seq=${String(seq)}`);
1878
+ if (event === 'message.recalled') {
1879
+ return this.publishMessageRecallTombstone(seq, payload);
1880
+ }
1723
1881
  const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, 'pull'));
1724
1882
  if (isPromiseLike(published))
1725
1883
  await published;
@@ -1733,16 +1891,25 @@ export class MessageDeliveryEngine {
1733
1891
  client._pendingOrderedMsgs.delete(ns);
1734
1892
  return false;
1735
1893
  }
1894
+ await this.drainOrderedMessages(ns, seqNum, true);
1736
1895
  queue?.delete(seqNum);
1737
1896
  if (queue && queue.size === 0)
1738
1897
  client._pendingOrderedMsgs.delete(ns);
1898
+ if (event === 'message.recalled') {
1899
+ const recallPublished = await this.publishMessageRecallTombstone(seqNum, payload);
1900
+ this.markPublishedSeq(ns, seqNum);
1901
+ client._markPulledSeqDelivered(ns, seqNum);
1902
+ client._clientLog.debug(`publish pulled delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
1903
+ await this.drainOrderedMessages(ns, undefined, true);
1904
+ return recallPublished;
1905
+ }
1739
1906
  const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, 'pull'));
1740
1907
  if (isPromiseLike(published))
1741
1908
  await published;
1742
1909
  this.markPublishedSeq(ns, seqNum);
1743
1910
  client._markPulledSeqDelivered(ns, seqNum);
1744
- await this.drainOrderedMessages(ns, undefined, true);
1745
1911
  client._clientLog.debug(`publish pulled delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
1912
+ await this.drainOrderedMessages(ns, undefined, true);
1746
1913
  return true;
1747
1914
  }
1748
1915
  }