@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.
- package/CHANGELOG.md +70 -0
- package/_packed_docs/CHANGELOG.md +70 -0
- package/_packed_docs/INDEX.md +16 -10
- package/_packed_docs/KITE_DOCS_GUIDE.md +4 -2
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +202 -75
- package/_packed_docs/protocol/README.md +3 -2
- package/_packed_docs/protocol/aun-docs-guide.md +3 -2
- package/_packed_docs/protocol/index.md +6 -5
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +20 -1
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +303 -28
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +30 -6
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +28 -12
- package/_packed_docs/sdk/09-group-rpc-manual.md +126 -42
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +6 -4
- package/_packed_docs/sdk/INDEX.md +17 -17
- package/dist/client/delivery.d.ts +4 -0
- package/dist/client/delivery.js +175 -8
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/v2-e2ee.js +2 -2
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +23 -1
- package/dist/client.js +37 -2
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +9 -0
- package/dist/facades.js +287 -33
- package/dist/facades.js.map +1 -1
- package/dist/group-index.d.ts +109 -0
- package/dist/group-index.js +308 -0
- package/dist/group-index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +3 -1
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/client/delivery.js
CHANGED
|
@@ -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 +
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|