@agentunion/fastaun 0.5.9 → 0.5.11
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 +64 -0
- package/_packed_docs/CHANGELOG.md +64 -0
- package/_packed_docs/INDEX.md +3 -3
- package/_packed_docs/KITE_DOCS_GUIDE.md +1 -1
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +6 -5
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +102 -8
- package/_packed_docs/sdk/09-group-rpc-manual.md +18 -3
- package/_packed_docs/sdk/09-message-rpc-manual.md +34 -10
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +3 -2
- package/_packed_docs/sdk/INDEX.md +2 -1
- package/dist/agent-md.js +5 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/auth.d.ts +2 -1
- package/dist/auth.js +30 -44
- package/dist/auth.js.map +1 -1
- package/dist/client/delivery.d.ts +39 -9
- package/dist/client/delivery.js +407 -73
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +6 -6
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +5 -14
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -0
- package/dist/client/rpc-pipeline.js +51 -5
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +3 -0
- package/dist/client/v2-e2ee.js +236 -59
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.js +84 -30
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +23 -8
- package/dist/events.js +120 -26
- package/dist/events.js.map +1 -1
- package/dist/register-flow.js +15 -111
- package/dist/register-flow.js.map +1 -1
- package/dist/transport.d.ts +36 -3
- package/dist/transport.js +855 -33
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/dist/client/v2-e2ee.js
CHANGED
|
@@ -298,6 +298,10 @@ export class V2E2EECoordinator {
|
|
|
298
298
|
this.pendingForwardCursors.clear();
|
|
299
299
|
this.pendingForwardAcks.clear();
|
|
300
300
|
}
|
|
301
|
+
hasPendingSenderIKWork() {
|
|
302
|
+
return this.senderIKSyncFetches.size > 0
|
|
303
|
+
|| Boolean(this.client._v2SenderIKFetching?.size);
|
|
304
|
+
}
|
|
301
305
|
pendingForwardCursor(ns, localAck) {
|
|
302
306
|
if (!ns)
|
|
303
307
|
return 0;
|
|
@@ -648,6 +652,7 @@ export class V2E2EECoordinator {
|
|
|
648
652
|
finally {
|
|
649
653
|
if (this.senderIKSyncFetches.get(fetchKey) === task)
|
|
650
654
|
this.senderIKSyncFetches.delete(fetchKey);
|
|
655
|
+
client._rpcPipeline?.requestPullDrainedCheck?.();
|
|
651
656
|
}
|
|
652
657
|
}
|
|
653
658
|
scheduleSenderIKPending(args) {
|
|
@@ -679,6 +684,10 @@ export class V2E2EECoordinator {
|
|
|
679
684
|
}
|
|
680
685
|
async resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey) {
|
|
681
686
|
const client = this.client;
|
|
687
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
688
|
+
const pending = client._v2SenderIKPending;
|
|
689
|
+
const fetching = client._v2SenderIKFetching;
|
|
690
|
+
const generationIsCurrent = () => Number(client._v2RuntimeGeneration ?? 0) === generation;
|
|
682
691
|
try {
|
|
683
692
|
const session = client._v2Session;
|
|
684
693
|
if (session && fromAid) {
|
|
@@ -718,18 +727,23 @@ export class V2E2EECoordinator {
|
|
|
718
727
|
await this.getV2SenderPubDer(fromAid, senderDeviceId);
|
|
719
728
|
}
|
|
720
729
|
}
|
|
721
|
-
|
|
730
|
+
if (!generationIsCurrent())
|
|
731
|
+
return;
|
|
732
|
+
const pendingItems = [...pending.entries()].filter(([, entry]) => entry.fromAid === fromAid && entry.senderDeviceId === senderDeviceId && entry.groupId === groupId);
|
|
722
733
|
for (const [key, entry] of pendingItems) {
|
|
723
734
|
let plaintext = null;
|
|
724
735
|
const retryStatus = {};
|
|
725
736
|
try {
|
|
726
|
-
plaintext = await client._decryptV2Message(entry.msg, false, true, true, true, retryStatus);
|
|
737
|
+
plaintext = await client._decryptV2Message(entry.msg, false, true, true, true, retryStatus, false, 'pending_retry');
|
|
727
738
|
}
|
|
728
739
|
catch (exc) {
|
|
729
740
|
client._clientLog.warn(`V2 sender IK pending retry raised: key=${key} err=${formatE2EEError(exc)}`);
|
|
730
|
-
|
|
741
|
+
if (generationIsCurrent())
|
|
742
|
+
pending.delete(key);
|
|
731
743
|
continue;
|
|
732
744
|
}
|
|
745
|
+
if (!generationIsCurrent())
|
|
746
|
+
return;
|
|
733
747
|
if (plaintext === null) {
|
|
734
748
|
if (retryStatus.deferred) {
|
|
735
749
|
client._clientLog.warn(`V2 pending retry still missing key material: key=${key}`);
|
|
@@ -741,24 +755,33 @@ export class V2E2EECoordinator {
|
|
|
741
755
|
const safeEvent = this.safeUndecryptablePushEvent(entry.msg, isGroup);
|
|
742
756
|
safeEvent._decrypt_error = 'sender IK pending retry failed';
|
|
743
757
|
safeEvent._decrypt_stage = 'sender_ik_pending_retry';
|
|
744
|
-
|
|
758
|
+
try {
|
|
759
|
+
await client._publishAppEvent(eventName, safeEvent, 'pending_retry');
|
|
760
|
+
}
|
|
761
|
+
finally {
|
|
762
|
+
if (generationIsCurrent())
|
|
763
|
+
pending.delete(key);
|
|
764
|
+
}
|
|
745
765
|
continue;
|
|
746
766
|
}
|
|
747
|
-
|
|
767
|
+
pending.delete(key);
|
|
748
768
|
const seq = Number(entry.msg.seq ?? 0);
|
|
749
769
|
if (entry.groupId) {
|
|
750
770
|
plaintext = normalizeGroupMentionMode(plaintext, entry.msg, entry.msg.envelope_json);
|
|
751
771
|
plaintext.group_id = entry.groupId;
|
|
752
|
-
await client._publishPulledMessage('group.message_created', `group:${entry.groupId}`, seq, plaintext);
|
|
772
|
+
await client._publishPulledMessage('group.message_created', `group:${entry.groupId}`, seq, plaintext, false, 'pending_retry');
|
|
753
773
|
}
|
|
754
774
|
else {
|
|
755
|
-
await client._publishPulledMessage('message.received', `p2p:${client._aid ?? ''}`, seq, plaintext);
|
|
775
|
+
await client._publishPulledMessage('message.received', `p2p:${client._aid ?? ''}`, seq, plaintext, false, 'pending_retry');
|
|
756
776
|
}
|
|
757
777
|
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
758
778
|
}
|
|
759
779
|
}
|
|
760
780
|
finally {
|
|
761
|
-
client._v2SenderIKFetching
|
|
781
|
+
if (generationIsCurrent() && client._v2SenderIKFetching === fetching) {
|
|
782
|
+
fetching.delete(fetchKey);
|
|
783
|
+
client._rpcPipeline?.requestPullDrainedCheck?.();
|
|
784
|
+
}
|
|
762
785
|
}
|
|
763
786
|
}
|
|
764
787
|
async buildV2P2PEnvelope(opts) {
|
|
@@ -924,9 +947,10 @@ export class V2E2EECoordinator {
|
|
|
924
947
|
throw new ValidationError("message.send requires 'to'");
|
|
925
948
|
if (!isJsonObject(payload))
|
|
926
949
|
throw new ValidationError('message.send payload must be a dict for V2 encryption');
|
|
950
|
+
const messageId = String(opts?.messageId ?? '').trim() || `m-${crypto.randomUUID().replace(/-/g, '')}`;
|
|
927
951
|
client._logMessageDebug('send-plaintext', 'message.send.v2', 'message.send', {
|
|
928
952
|
to: toAid,
|
|
929
|
-
message_id:
|
|
953
|
+
message_id: messageId,
|
|
930
954
|
payload,
|
|
931
955
|
}, { payloadOverride: payload });
|
|
932
956
|
const attempt = async (useCache) => {
|
|
@@ -934,7 +958,7 @@ export class V2E2EECoordinator {
|
|
|
934
958
|
const envelope = await client._buildV2P2PEnvelope({
|
|
935
959
|
to: toAid,
|
|
936
960
|
payload,
|
|
937
|
-
messageId
|
|
961
|
+
messageId,
|
|
938
962
|
timestamp: opts?.timestamp,
|
|
939
963
|
protectedHeaders: opts?.protectedHeaders,
|
|
940
964
|
context: opts?.context,
|
|
@@ -943,6 +967,7 @@ export class V2E2EECoordinator {
|
|
|
943
967
|
const result = await client.call('message.send', {
|
|
944
968
|
to: toAid,
|
|
945
969
|
payload: envelope,
|
|
970
|
+
message_id: messageId,
|
|
946
971
|
encrypt: false,
|
|
947
972
|
_skip_send_result_envelope: true,
|
|
948
973
|
});
|
|
@@ -954,7 +979,7 @@ export class V2E2EECoordinator {
|
|
|
954
979
|
return client._delivery.attachSendResultEnvelope('message.send', {
|
|
955
980
|
to: toAid,
|
|
956
981
|
payload,
|
|
957
|
-
message_id:
|
|
982
|
+
message_id: messageId,
|
|
958
983
|
timestamp: opts?.timestamp,
|
|
959
984
|
protected_headers: opts?.protectedHeaders,
|
|
960
985
|
context: opts?.context,
|
|
@@ -969,7 +994,7 @@ export class V2E2EECoordinator {
|
|
|
969
994
|
return client._delivery.attachSendResultEnvelope('message.send', {
|
|
970
995
|
to: toAid,
|
|
971
996
|
payload,
|
|
972
|
-
message_id:
|
|
997
|
+
message_id: messageId,
|
|
973
998
|
timestamp: opts?.timestamp,
|
|
974
999
|
protected_headers: opts?.protectedHeaders,
|
|
975
1000
|
context: opts?.context,
|
|
@@ -993,10 +1018,10 @@ export class V2E2EECoordinator {
|
|
|
993
1018
|
if (String(msg.version ?? '') === 'v1') {
|
|
994
1019
|
const legacy = isJsonObject(msg.legacy_v1)
|
|
995
1020
|
? msg.legacy_v1 : {};
|
|
996
|
-
const payload = legacy.payload;
|
|
1021
|
+
const payload = legacy.payload !== undefined ? legacy.payload : msg.payload;
|
|
997
1022
|
const payloadType = isJsonObject(payload)
|
|
998
1023
|
? String(payload.type ?? '').trim() : '';
|
|
999
|
-
if (payload !== undefined && payload !== null && !['e2ee.encrypted', 'e2ee.group_encrypted'].includes(payloadType)) {
|
|
1024
|
+
if (payload !== undefined && payload !== null && !['e2ee.encrypted', 'e2ee.group_encrypted', 'e2ee.p2p_encrypted'].includes(payloadType)) {
|
|
1000
1025
|
plaintext = {
|
|
1001
1026
|
message_id: String(msg.message_id ?? ''),
|
|
1002
1027
|
from: String(msg.from_aid ?? ''),
|
|
@@ -1009,9 +1034,14 @@ export class V2E2EECoordinator {
|
|
|
1009
1034
|
};
|
|
1010
1035
|
attachGatewayProximity(plaintext, msg);
|
|
1011
1036
|
}
|
|
1037
|
+
else if (isJsonObject(payload)
|
|
1038
|
+
&& ['e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
1039
|
+
msg = { ...msg, envelope_json: JSON.stringify(payload) };
|
|
1040
|
+
plaintext = await client._decryptV2Message(msg, false, !history, true, true, undefined, false, source);
|
|
1041
|
+
}
|
|
1012
1042
|
}
|
|
1013
1043
|
else {
|
|
1014
|
-
plaintext = await client._decryptV2Message(msg, false, !history);
|
|
1044
|
+
plaintext = await client._decryptV2Message(msg, false, !history, true, true, undefined, false, source);
|
|
1015
1045
|
}
|
|
1016
1046
|
if (!plaintext) {
|
|
1017
1047
|
if (history)
|
|
@@ -1022,7 +1052,7 @@ export class V2E2EECoordinator {
|
|
|
1022
1052
|
if (publish) {
|
|
1023
1053
|
const event = client._delivery.p2pAppEventForMessage(plaintext);
|
|
1024
1054
|
if (orderedPublish) {
|
|
1025
|
-
await client._delivery.publishOrderedMessage(event.event, client._aid ? `p2p:${client._aid}` : '', seq, event.payload);
|
|
1055
|
+
await client._delivery.publishOrderedMessage(event.event, client._aid ? `p2p:${client._aid}` : '', seq, event.payload, source);
|
|
1026
1056
|
}
|
|
1027
1057
|
else {
|
|
1028
1058
|
await client._publishAppEvent(event.event, event.payload, source);
|
|
@@ -1036,35 +1066,57 @@ export class V2E2EECoordinator {
|
|
|
1036
1066
|
let plaintext = null;
|
|
1037
1067
|
if (String(msg.version ?? '') === 'v1') {
|
|
1038
1068
|
const payload = msg.payload;
|
|
1069
|
+
const recall = typeof client._delivery?.recallEventFromGroupMessage === 'function'
|
|
1070
|
+
? client._delivery.recallEventFromGroupMessage(msg)
|
|
1071
|
+
: null;
|
|
1072
|
+
if (recall) {
|
|
1073
|
+
const recallPayload = {
|
|
1074
|
+
...recall,
|
|
1075
|
+
group_id: groupId,
|
|
1076
|
+
group_aid: groupAid,
|
|
1077
|
+
seq,
|
|
1078
|
+
};
|
|
1079
|
+
if (publish) {
|
|
1080
|
+
if (orderedPublish) {
|
|
1081
|
+
await client._delivery.publishOrderedMessage('group.message_recalled', `group:${groupId}`, seq, recallPayload, source);
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
await client._publishAppEvent('group.message_recalled', recallPayload, source);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return recallPayload;
|
|
1088
|
+
}
|
|
1039
1089
|
if (payload === undefined || payload === null) {
|
|
1040
1090
|
if (history)
|
|
1041
1091
|
return this.historyDecryptFailure(msg, 'legacy payload is missing');
|
|
1042
1092
|
client._clientLog.warn(`Group Tail 缺少 payload 的 V1 行已跳过,连续性证明仍保留: group=${groupId}, seq=${seq}`);
|
|
1043
1093
|
return null;
|
|
1044
1094
|
}
|
|
1095
|
+
const payloadType = isJsonObject(payload)
|
|
1096
|
+
? String(payload.type ?? '').trim() : '';
|
|
1045
1097
|
if (isJsonObject(payload)
|
|
1046
|
-
&& ['e2ee.
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1098
|
+
&& ['e2ee.group_encrypted', 'e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
1099
|
+
msg = { ...msg, envelope_json: JSON.stringify(payload) };
|
|
1100
|
+
plaintext = await client._decryptV2Message(msg, false, !history, true, true, undefined, false, source);
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
plaintext = {
|
|
1104
|
+
message_id: String(msg.message_id ?? ''),
|
|
1105
|
+
from: String(msg.from_aid ?? ''),
|
|
1106
|
+
group_id: groupId,
|
|
1107
|
+
group_aid: groupAid,
|
|
1108
|
+
seq,
|
|
1109
|
+
type: String(msg.type ?? ''),
|
|
1110
|
+
timestamp: msg.t_server,
|
|
1111
|
+
payload: payload,
|
|
1112
|
+
encrypted: false,
|
|
1113
|
+
};
|
|
1114
|
+
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
1115
|
+
attachGatewayProximity(plaintext, msg);
|
|
1051
1116
|
}
|
|
1052
|
-
plaintext = {
|
|
1053
|
-
message_id: String(msg.message_id ?? ''),
|
|
1054
|
-
from: String(msg.from_aid ?? ''),
|
|
1055
|
-
group_id: groupId,
|
|
1056
|
-
group_aid: groupAid,
|
|
1057
|
-
seq,
|
|
1058
|
-
type: String(msg.type ?? ''),
|
|
1059
|
-
timestamp: msg.t_server,
|
|
1060
|
-
payload: payload,
|
|
1061
|
-
encrypted: false,
|
|
1062
|
-
};
|
|
1063
|
-
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
1064
|
-
attachGatewayProximity(plaintext, msg);
|
|
1065
1117
|
}
|
|
1066
1118
|
else {
|
|
1067
|
-
plaintext = await client._decryptV2Message(msg, false, !history);
|
|
1119
|
+
plaintext = await client._decryptV2Message(msg, false, !history, true, true, undefined, false, source);
|
|
1068
1120
|
if (plaintext) {
|
|
1069
1121
|
plaintext.group_id = groupId;
|
|
1070
1122
|
plaintext.group_aid = groupAid;
|
|
@@ -1079,7 +1131,7 @@ export class V2E2EECoordinator {
|
|
|
1079
1131
|
}
|
|
1080
1132
|
if (publish) {
|
|
1081
1133
|
if (orderedPublish) {
|
|
1082
|
-
await client._delivery.publishOrderedMessage('group.message_created', `group:${groupId}`, seq, plaintext);
|
|
1134
|
+
await client._delivery.publishOrderedMessage('group.message_created', `group:${groupId}`, seq, plaintext, source);
|
|
1083
1135
|
}
|
|
1084
1136
|
else {
|
|
1085
1137
|
await client._publishAppEvent('group.message_created', plaintext, source);
|
|
@@ -1104,6 +1156,8 @@ export class V2E2EECoordinator {
|
|
|
1104
1156
|
const afterSeq = strictWindowSeq(params.after_seq ?? 0, 'after_seq');
|
|
1105
1157
|
const limit = strictWindowLimit(params.limit ?? V2_PULL_DEFAULT_PAGE_LIMIT);
|
|
1106
1158
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1159
|
+
if (ns)
|
|
1160
|
+
client._delivery.syncStarted(ns);
|
|
1107
1161
|
const result = await client._callRawV2Rpc('message.v2.pull', { window_mode: 'tail', after_seq: afterSeq, limit, _rpc_foreground: true });
|
|
1108
1162
|
const floor = Number(result.retention_floor_seq ?? 0);
|
|
1109
1163
|
const previous = ns ? client._seqTracker.getSyncState(ns) : null;
|
|
@@ -1128,6 +1182,15 @@ export class V2E2EECoordinator {
|
|
|
1128
1182
|
const ack = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
1129
1183
|
if (previous && ack > previous.ack)
|
|
1130
1184
|
await this.ackV2(ack);
|
|
1185
|
+
if (ns)
|
|
1186
|
+
client._delivery.syncProgress(ns, {
|
|
1187
|
+
rawCount: page.messages.length,
|
|
1188
|
+
pulledCount: decoded.length,
|
|
1189
|
+
remaining: result.remaining,
|
|
1190
|
+
hasMore: typeof result.has_more === 'boolean' ? result.has_more : undefined,
|
|
1191
|
+
currentSeq: ack,
|
|
1192
|
+
targetSeq: page.head,
|
|
1193
|
+
});
|
|
1131
1194
|
return { ...result, messages: decoded, raw_count: page.messages.length };
|
|
1132
1195
|
}
|
|
1133
1196
|
async historyV2Internal(params) {
|
|
@@ -1144,21 +1207,23 @@ export class V2E2EECoordinator {
|
|
|
1144
1207
|
}
|
|
1145
1208
|
return { ...result, messages: decoded };
|
|
1146
1209
|
}
|
|
1147
|
-
async
|
|
1210
|
+
async pullV2Impl(afterSeq = 0, limit = 50, opts) {
|
|
1148
1211
|
const client = this.client;
|
|
1149
1212
|
await client._ensureV2SessionReady('message.pull');
|
|
1150
1213
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1214
|
+
if (ns)
|
|
1215
|
+
client._delivery.syncStarted(ns);
|
|
1151
1216
|
if (opts?.windowMode === 'tail') {
|
|
1152
1217
|
if (!opts.gateLocked) {
|
|
1153
1218
|
const key = `${ns}|tail|after=${afterSeq}|limit=${limit}`;
|
|
1154
|
-
return (await client._runPullSerialized(key, () => this.
|
|
1219
|
+
return (await client._runPullSerialized(key, () => this.pullV2Impl(afterSeq, limit, { ...opts, gateLocked: true }), false));
|
|
1155
1220
|
}
|
|
1156
1221
|
const result = await this.pullV2TailInternal({ window_mode: 'tail', after_seq: afterSeq, limit });
|
|
1157
1222
|
return (Array.isArray(result.messages) ? result.messages : []);
|
|
1158
1223
|
}
|
|
1159
1224
|
if (ns && !opts?.gateLocked) {
|
|
1160
1225
|
const key = `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`;
|
|
1161
|
-
return await client._runPullSerialized(key, async () => this.
|
|
1226
|
+
return await client._runPullSerialized(key, async () => this.pullV2Impl(afterSeq, limit, {
|
|
1162
1227
|
...(opts ?? {}),
|
|
1163
1228
|
gateLocked: true,
|
|
1164
1229
|
scheduleFollowup: true,
|
|
@@ -1228,6 +1293,7 @@ export class V2E2EECoordinator {
|
|
|
1228
1293
|
}
|
|
1229
1294
|
const deferredKeyFetches = new Map();
|
|
1230
1295
|
let blockedSeq = 0;
|
|
1296
|
+
const pageDecryptedBefore = decrypted.length;
|
|
1231
1297
|
for (const msg of messages) {
|
|
1232
1298
|
const seq = Number(msg.seq ?? 0);
|
|
1233
1299
|
if (!Number.isFinite(seq) || seq <= 0)
|
|
@@ -1235,11 +1301,12 @@ export class V2E2EECoordinator {
|
|
|
1235
1301
|
const version = String(msg.version ?? 'v2');
|
|
1236
1302
|
if (version === 'v1') {
|
|
1237
1303
|
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
1238
|
-
const legacyPayload = legacy.payload;
|
|
1304
|
+
const legacyPayload = legacy.payload !== undefined ? legacy.payload : msg.payload;
|
|
1239
1305
|
const payloadType = isJsonObject(legacyPayload)
|
|
1240
1306
|
? String(legacyPayload.type ?? '').trim()
|
|
1241
1307
|
: '';
|
|
1242
|
-
if (legacyPayload !== undefined && legacyPayload !== null
|
|
1308
|
+
if (legacyPayload !== undefined && legacyPayload !== null
|
|
1309
|
+
&& !['e2ee.encrypted', 'e2ee.group_encrypted', 'e2ee.p2p_encrypted'].includes(payloadType)) {
|
|
1243
1310
|
const v1Msg = {
|
|
1244
1311
|
message_id: String(msg.message_id ?? ''),
|
|
1245
1312
|
from: String(msg.from_aid ?? ''),
|
|
@@ -1261,6 +1328,25 @@ export class V2E2EECoordinator {
|
|
|
1261
1328
|
decrypted.push(v1Msg);
|
|
1262
1329
|
client._clientLog.debug(`message.v2.pull plaintext V1 delivered: seq=${seq}, ns=${ns || '<none>'}`);
|
|
1263
1330
|
}
|
|
1331
|
+
else if (isJsonObject(legacyPayload)
|
|
1332
|
+
&& ['e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
1333
|
+
const deferStatus = {};
|
|
1334
|
+
const plaintext = await client._decryptV2Message({ ...msg, envelope_json: JSON.stringify(legacyPayload) }, true, true, true, true, deferStatus, true, 'pull');
|
|
1335
|
+
if (deferStatus.deferred) {
|
|
1336
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
1337
|
+
if (deferStatus.fromAid) {
|
|
1338
|
+
const key = `${deferStatus.fromAid}\u0000${deferStatus.senderDeviceId ?? ''}\u0000${deferStatus.groupId ?? ''}`;
|
|
1339
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus.fromAid, senderDeviceId: deferStatus.senderDeviceId ?? '', groupId: deferStatus.groupId ?? '' });
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (plaintext) {
|
|
1343
|
+
if (ns)
|
|
1344
|
+
await client._publishPulledMessage('message.received', ns, seq, plaintext, false);
|
|
1345
|
+
else
|
|
1346
|
+
await client._publishAppEvent('message.received', plaintext, 'pull');
|
|
1347
|
+
decrypted.push(plaintext);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1264
1350
|
else {
|
|
1265
1351
|
client._clientLog.debug(`message.v2.pull skipping V1 envelope seq=${seq} payload_type=${payloadType || '<none>'} (V1 E2EE removed)`);
|
|
1266
1352
|
}
|
|
@@ -1275,7 +1361,7 @@ export class V2E2EECoordinator {
|
|
|
1275
1361
|
client._v2Session.trackOldSPKMaxSeq(spkId, seq);
|
|
1276
1362
|
}
|
|
1277
1363
|
const deferStatus = {};
|
|
1278
|
-
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true);
|
|
1364
|
+
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, 'pull');
|
|
1279
1365
|
if (deferStatus.deferred) {
|
|
1280
1366
|
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
1281
1367
|
}
|
|
@@ -1337,7 +1423,7 @@ export class V2E2EECoordinator {
|
|
|
1337
1423
|
throw exc;
|
|
1338
1424
|
}
|
|
1339
1425
|
if (contigAdvanced)
|
|
1340
|
-
await client._drainOrderedMessages(ns, undefined, false, false);
|
|
1426
|
+
await client._drainOrderedMessages(ns, undefined, false, false, 'pull');
|
|
1341
1427
|
for (const fetch of deferredKeyFetches.values()) {
|
|
1342
1428
|
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
1343
1429
|
}
|
|
@@ -1374,8 +1460,19 @@ export class V2E2EECoordinator {
|
|
|
1374
1460
|
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
1375
1461
|
}
|
|
1376
1462
|
}
|
|
1463
|
+
if (ns)
|
|
1464
|
+
client._delivery.syncProgress(ns, {
|
|
1465
|
+
rawCount: messages.length,
|
|
1466
|
+
pulledCount: decrypted.length - pageDecryptedBefore,
|
|
1467
|
+
remaining: result.remaining,
|
|
1468
|
+
hasMore: hasMoreField ? hasMore : undefined,
|
|
1469
|
+
currentSeq: client._seqTracker.getContiguousSeq(ns),
|
|
1470
|
+
targetSeq: serverHead,
|
|
1471
|
+
});
|
|
1377
1472
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
1378
1473
|
const rawCount = messages.length;
|
|
1474
|
+
if (blockedSeq > 0)
|
|
1475
|
+
break;
|
|
1379
1476
|
const shouldContinue = shouldContinueForwardPage({
|
|
1380
1477
|
rawCount,
|
|
1381
1478
|
nextAfterSeq,
|
|
@@ -1409,6 +1506,19 @@ export class V2E2EECoordinator {
|
|
|
1409
1506
|
client._clientLog.debug(`message.v2.pull done: requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${decrypted.length}, ns=${ns || '<none>'}`);
|
|
1410
1507
|
return decrypted;
|
|
1411
1508
|
}
|
|
1509
|
+
async pullV2(afterSeq = 0, limit = 50, opts) {
|
|
1510
|
+
const ns = this.client._aid ? `p2p:${this.client._aid}` : '';
|
|
1511
|
+
if (!ns)
|
|
1512
|
+
return await this.pullV2Impl(afterSeq, limit, opts);
|
|
1513
|
+
this.client._delivery.syncStarted(ns);
|
|
1514
|
+
try {
|
|
1515
|
+
return await this.pullV2Impl(afterSeq, limit, opts);
|
|
1516
|
+
}
|
|
1517
|
+
catch (error) {
|
|
1518
|
+
this.client._delivery.onPullAborted?.();
|
|
1519
|
+
throw error;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1412
1522
|
async confirmForwardP2PAck(upToSeq) {
|
|
1413
1523
|
const ns = this.client._aid ? `p2p:${this.client._aid}` : '';
|
|
1414
1524
|
try {
|
|
@@ -1487,9 +1597,10 @@ export class V2E2EECoordinator {
|
|
|
1487
1597
|
const groupAid = String(opts?.groupAid ?? '').trim();
|
|
1488
1598
|
if (!isJsonObject(payload))
|
|
1489
1599
|
throw new ValidationError('group.send payload must be a dict for V2 encryption');
|
|
1600
|
+
const messageId = String(opts?.messageId ?? '').trim() || `m-${crypto.randomUUID().replace(/-/g, '')}`;
|
|
1490
1601
|
client._logMessageDebug('send-plaintext', 'group.send.v2', 'group.send', withExplicitGroupAid({
|
|
1491
1602
|
group_id: gid,
|
|
1492
|
-
message_id:
|
|
1603
|
+
message_id: messageId,
|
|
1493
1604
|
payload,
|
|
1494
1605
|
}, groupAid), { payloadOverride: payload });
|
|
1495
1606
|
const attempt = async (useCache) => {
|
|
@@ -1497,7 +1608,7 @@ export class V2E2EECoordinator {
|
|
|
1497
1608
|
const envelope = await client._buildV2GroupEnvelope({
|
|
1498
1609
|
groupId: gid,
|
|
1499
1610
|
payload,
|
|
1500
|
-
messageId
|
|
1611
|
+
messageId,
|
|
1501
1612
|
timestamp: opts?.timestamp,
|
|
1502
1613
|
protectedHeaders: opts?.protectedHeaders,
|
|
1503
1614
|
context: opts?.context,
|
|
@@ -1529,7 +1640,7 @@ export class V2E2EECoordinator {
|
|
|
1529
1640
|
return client._delivery.attachSendResultEnvelope('group.send', withExplicitGroupAid({
|
|
1530
1641
|
group_id: gid,
|
|
1531
1642
|
payload,
|
|
1532
|
-
message_id:
|
|
1643
|
+
message_id: messageId,
|
|
1533
1644
|
timestamp: opts?.timestamp,
|
|
1534
1645
|
protected_headers: opts?.protectedHeaders,
|
|
1535
1646
|
context: opts?.context,
|
|
@@ -1545,7 +1656,7 @@ export class V2E2EECoordinator {
|
|
|
1545
1656
|
return client._delivery.attachSendResultEnvelope('group.send', withExplicitGroupAid({
|
|
1546
1657
|
group_id: gid,
|
|
1547
1658
|
payload,
|
|
1548
|
-
message_id:
|
|
1659
|
+
message_id: messageId,
|
|
1549
1660
|
timestamp: opts?.timestamp,
|
|
1550
1661
|
protected_headers: opts?.protectedHeaders,
|
|
1551
1662
|
context: opts?.context,
|
|
@@ -1713,6 +1824,14 @@ export class V2E2EECoordinator {
|
|
|
1713
1824
|
const afterSeq = strictWindowSeq(params.after_seq ?? 0, 'after_seq');
|
|
1714
1825
|
const limit = strictWindowLimit(params.limit ?? V2_PULL_DEFAULT_PAGE_LIMIT);
|
|
1715
1826
|
const ns = `group:${groupId}`;
|
|
1827
|
+
const cursorParams = isJsonObject(params._group_cursor_params)
|
|
1828
|
+
? params._group_cursor_params : params;
|
|
1829
|
+
const requestDeviceId = String(cursorParams.device_id ?? '').trim();
|
|
1830
|
+
const requestSlotId = String(cursorParams.slot_id ?? '').trim();
|
|
1831
|
+
const ownsCursor = (!requestDeviceId || requestDeviceId === String(client._deviceId ?? ''))
|
|
1832
|
+
&& (!requestSlotId || requestSlotId === String(client._slotId ?? ''));
|
|
1833
|
+
if (ownsCursor)
|
|
1834
|
+
client._delivery.syncStarted(ns);
|
|
1716
1835
|
const result = await client._callRawV2Rpc('group.v2.pull', withExplicitGroupAid({
|
|
1717
1836
|
group_id: groupId,
|
|
1718
1837
|
window_mode: 'tail',
|
|
@@ -1745,6 +1864,15 @@ export class V2E2EECoordinator {
|
|
|
1745
1864
|
const ack = client._seqTracker.getContiguousSeq(ns);
|
|
1746
1865
|
if (ack > previous.ack)
|
|
1747
1866
|
await this.ackGroupV2(groupId, ack, groupAid);
|
|
1867
|
+
if (ownsCursor)
|
|
1868
|
+
client._delivery.syncProgress(ns, {
|
|
1869
|
+
rawCount: page.messages.length,
|
|
1870
|
+
pulledCount: decoded.length,
|
|
1871
|
+
remaining: result.remaining,
|
|
1872
|
+
hasMore: typeof result.has_more === 'boolean' ? result.has_more : undefined,
|
|
1873
|
+
currentSeq: ack,
|
|
1874
|
+
targetSeq: page.head,
|
|
1875
|
+
});
|
|
1748
1876
|
return { ...result, messages: decoded, raw_count: page.messages.length };
|
|
1749
1877
|
}
|
|
1750
1878
|
async historyGroupV2Internal(params) {
|
|
@@ -1780,7 +1908,7 @@ export class V2E2EECoordinator {
|
|
|
1780
1908
|
force: params.force === true,
|
|
1781
1909
|
});
|
|
1782
1910
|
}
|
|
1783
|
-
async
|
|
1911
|
+
async pullGroupV2Impl(groupId, afterSeq = 0, limit = 50, opts) {
|
|
1784
1912
|
const client = this.client;
|
|
1785
1913
|
await client._ensureV2SessionReady('group.pull');
|
|
1786
1914
|
const gid = String(groupId ?? '').trim();
|
|
@@ -1788,15 +1916,18 @@ export class V2E2EECoordinator {
|
|
|
1788
1916
|
throw new ValidationError('group.pull requires group_id');
|
|
1789
1917
|
let groupAid = String(opts?.groupAid ?? '').trim();
|
|
1790
1918
|
const ns = `group:${gid}`;
|
|
1919
|
+
if (opts?.ownsCursor !== false)
|
|
1920
|
+
client._delivery.syncStarted(ns);
|
|
1791
1921
|
if (opts?.windowMode === 'tail') {
|
|
1792
1922
|
if (!opts.gateLocked) {
|
|
1793
1923
|
const key = `${ns}|tail|after=${afterSeq}|limit=${limit}`;
|
|
1794
|
-
return (await client._runPullSerialized(key, () => this.
|
|
1924
|
+
return (await client._runPullSerialized(key, () => this.pullGroupV2Impl(gid, afterSeq, limit, {
|
|
1795
1925
|
...opts,
|
|
1796
1926
|
gateLocked: true,
|
|
1797
1927
|
}), false));
|
|
1798
1928
|
}
|
|
1799
1929
|
const result = await this.pullGroupV2TailInternal({
|
|
1930
|
+
...(opts?.cursorParams ?? {}),
|
|
1800
1931
|
group_id: String(opts.wireGroupId ?? gid),
|
|
1801
1932
|
group_aid: groupAid || undefined,
|
|
1802
1933
|
window_mode: 'tail',
|
|
@@ -1807,7 +1938,7 @@ export class V2E2EECoordinator {
|
|
|
1807
1938
|
}
|
|
1808
1939
|
if (!opts?.gateLocked) {
|
|
1809
1940
|
const key = `${ns}|forward|after=${afterSeq}|force=${opts?.force === true}|limit=${limit}`;
|
|
1810
|
-
return await client._runPullSerialized(key, async () => this.
|
|
1941
|
+
return await client._runPullSerialized(key, async () => this.pullGroupV2Impl(gid, afterSeq, limit, {
|
|
1811
1942
|
...(opts ?? {}),
|
|
1812
1943
|
gateLocked: true,
|
|
1813
1944
|
scheduleFollowup: true,
|
|
@@ -1890,28 +2021,31 @@ export class V2E2EECoordinator {
|
|
|
1890
2021
|
}
|
|
1891
2022
|
const deferredKeyFetches = new Map();
|
|
1892
2023
|
let blockedSeq = 0;
|
|
2024
|
+
const pageDecryptedBefore = decrypted.length;
|
|
1893
2025
|
for (const msg of messages) {
|
|
1894
2026
|
const seq = Number(msg.seq ?? 0);
|
|
1895
2027
|
if (!Number.isFinite(seq) || seq <= 0)
|
|
1896
2028
|
continue;
|
|
1897
2029
|
const version = String(msg.version ?? 'v2');
|
|
1898
2030
|
if (version === 'v1') {
|
|
1899
|
-
const
|
|
2031
|
+
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
2032
|
+
const payload = msg.payload !== undefined ? msg.payload : legacy.payload;
|
|
1900
2033
|
const payloadObj = isJsonObject(payload) ? payload : null;
|
|
2034
|
+
const payloadType = payloadObj ? String(payloadObj.type ?? '').trim() : '';
|
|
1901
2035
|
// 群撤回 tombstone(占位 / 通知):归一化为 group.message_recalled 事件,仍占 seq。
|
|
1902
|
-
if (client._delivery
|
|
2036
|
+
if (typeof client._delivery?.recallEventFromGroupMessage === 'function'
|
|
2037
|
+
&& client._delivery.recallEventFromGroupMessage(msg)) {
|
|
1903
2038
|
await client._delivery.publishGroupRecallTombstone(gid, seq, {
|
|
1904
2039
|
...msg,
|
|
1905
2040
|
group_id: gid,
|
|
1906
2041
|
group_aid: eventGroupAid,
|
|
1907
|
-
});
|
|
2042
|
+
}, 'pull');
|
|
1908
2043
|
client._markPublishedSeq(ns, seq);
|
|
1909
2044
|
client._clientLog.debug(`group.v2.pull recall tombstone delivered: group=${gid}, seq=${seq}`);
|
|
1910
2045
|
continue;
|
|
1911
2046
|
}
|
|
1912
2047
|
if (payloadObj) {
|
|
1913
|
-
|
|
1914
|
-
if (payloadType !== 'e2ee.encrypted' && payloadType !== 'e2ee.group_encrypted') {
|
|
2048
|
+
if (!['e2ee.encrypted', 'e2ee.group_encrypted', 'e2ee.p2p_encrypted'].includes(payloadType)) {
|
|
1915
2049
|
let v1Msg = {
|
|
1916
2050
|
message_id: String(msg.message_id ?? ''),
|
|
1917
2051
|
from: String(msg.from_aid ?? ''),
|
|
@@ -1950,7 +2084,26 @@ export class V2E2EECoordinator {
|
|
|
1950
2084
|
client._clientLog.debug(`group.v2.pull plaintext V1 delivered: group=${gid}, seq=${seq}`);
|
|
1951
2085
|
continue;
|
|
1952
2086
|
}
|
|
1953
|
-
|
|
2087
|
+
if (payloadObj && ['e2ee.group_encrypted', 'e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
2088
|
+
const deferStatus = {};
|
|
2089
|
+
const plaintext = await client._decryptV2Message({ ...msg, envelope_json: JSON.stringify(payloadObj) }, true, true, true, true, deferStatus, true, 'pull');
|
|
2090
|
+
if (deferStatus.deferred) {
|
|
2091
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
2092
|
+
if (deferStatus.fromAid) {
|
|
2093
|
+
const key = `${deferStatus.fromAid}\u0000${deferStatus.senderDeviceId ?? ''}\u0000${deferStatus.groupId ?? ''}`;
|
|
2094
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus.fromAid, senderDeviceId: deferStatus.senderDeviceId ?? '', groupId: deferStatus.groupId ?? '' });
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
if (plaintext) {
|
|
2098
|
+
plaintext.group_id = gid;
|
|
2099
|
+
plaintext.group_aid = eventGroupAid;
|
|
2100
|
+
await client._publishPulledMessage('group.message_created', ns, seq, plaintext, false);
|
|
2101
|
+
decrypted.push(plaintext);
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
else {
|
|
2105
|
+
client._clientLog.debug(`group.v2.pull skipping V1 envelope group=${gid} seq=${seq} payload_type=${payloadObj ? String(payloadObj.type ?? '') : '<none>'} (V1 E2EE removed)`);
|
|
2106
|
+
}
|
|
1954
2107
|
continue;
|
|
1955
2108
|
}
|
|
1956
2109
|
if (version !== 'v2') {
|
|
@@ -1958,7 +2111,7 @@ export class V2E2EECoordinator {
|
|
|
1958
2111
|
continue;
|
|
1959
2112
|
}
|
|
1960
2113
|
const deferStatus = {};
|
|
1961
|
-
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true);
|
|
2114
|
+
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, 'pull');
|
|
1962
2115
|
if (deferStatus.deferred) {
|
|
1963
2116
|
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
1964
2117
|
}
|
|
@@ -2020,7 +2173,7 @@ export class V2E2EECoordinator {
|
|
|
2020
2173
|
throw exc;
|
|
2021
2174
|
}
|
|
2022
2175
|
if (contigAdvanced)
|
|
2023
|
-
await client._drainOrderedMessages(ns, undefined, false, false);
|
|
2176
|
+
await client._drainOrderedMessages(ns, undefined, false, false, 'pull');
|
|
2024
2177
|
}
|
|
2025
2178
|
for (const fetch of deferredKeyFetches.values()) {
|
|
2026
2179
|
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
@@ -2053,10 +2206,20 @@ export class V2E2EECoordinator {
|
|
|
2053
2206
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
2054
2207
|
}
|
|
2055
2208
|
}
|
|
2209
|
+
client._delivery.syncProgress(ns, {
|
|
2210
|
+
rawCount: messages.length,
|
|
2211
|
+
pulledCount: decrypted.length - pageDecryptedBefore,
|
|
2212
|
+
remaining: result.remaining,
|
|
2213
|
+
hasMore: hasMoreField ? hasMore : undefined,
|
|
2214
|
+
currentSeq: client._seqTracker.getContiguousSeq(ns),
|
|
2215
|
+
targetSeq: serverHead,
|
|
2216
|
+
});
|
|
2056
2217
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
2057
2218
|
if (!ownsCursor)
|
|
2058
2219
|
break;
|
|
2059
2220
|
const rawCount = messages.length;
|
|
2221
|
+
if (blockedSeq > 0)
|
|
2222
|
+
break;
|
|
2060
2223
|
const shouldContinue = shouldContinueForwardPage({
|
|
2061
2224
|
rawCount,
|
|
2062
2225
|
nextAfterSeq,
|
|
@@ -2090,6 +2253,20 @@ export class V2E2EECoordinator {
|
|
|
2090
2253
|
client._clientLog.debug(`group.v2.pull done: group=${gid}, requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${decrypted.length}, ns=${ns}`);
|
|
2091
2254
|
return decrypted;
|
|
2092
2255
|
}
|
|
2256
|
+
async pullGroupV2(groupId, afterSeq = 0, limit = 50, opts) {
|
|
2257
|
+
if (opts?.ownsCursor === false) {
|
|
2258
|
+
return await this.pullGroupV2Impl(groupId, afterSeq, limit, opts);
|
|
2259
|
+
}
|
|
2260
|
+
const ns = `group:${String(groupId ?? '').trim()}`;
|
|
2261
|
+
this.client._delivery.syncStarted(ns);
|
|
2262
|
+
try {
|
|
2263
|
+
return await this.pullGroupV2Impl(groupId, afterSeq, limit, opts);
|
|
2264
|
+
}
|
|
2265
|
+
catch (error) {
|
|
2266
|
+
this.client._delivery.onPullAborted?.();
|
|
2267
|
+
throw error;
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2093
2270
|
async confirmForwardGroupAck(groupId, upToSeq, groupAid) {
|
|
2094
2271
|
const ns = `group:${String(groupId ?? '').trim()}`;
|
|
2095
2272
|
try {
|
|
@@ -2649,7 +2826,7 @@ export class V2E2EECoordinator {
|
|
|
2649
2826
|
async decryptV2PushMessage(data) {
|
|
2650
2827
|
if (!isJsonObject(data))
|
|
2651
2828
|
return null;
|
|
2652
|
-
return await this.client._decryptV2Message(data, false, false, false, false);
|
|
2829
|
+
return await this.client._decryptV2Message(data, false, false, false, false, undefined, false, 'inline_push');
|
|
2653
2830
|
}
|
|
2654
2831
|
handleGroupChangedSpk(data, groupId, action) {
|
|
2655
2832
|
if (!this.client._v2Session || !groupId || !MEMBERSHIP_ACTIONS.has(action))
|