@agentunion/fastaun-browser 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.d.ts.map +1 -1
- package/dist/agent-md.js +23 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +16 -2
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +1374 -267
- package/dist/client/delivery.d.ts +34 -11
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +390 -100
- 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.d.ts.map +1 -1
- package/dist/client/lifecycle.js +34 -38
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -0
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +70 -4
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +4 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +169 -38
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +83 -33
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +31 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +167 -7
- package/dist/events.js.map +1 -1
- package/dist/register-flow.d.ts.map +1 -1
- package/dist/register-flow.js +16 -2
- package/dist/register-flow.js.map +1 -1
- package/dist/transport.d.ts +10 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +427 -29
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +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
|
@@ -847,11 +847,17 @@ export class V2E2EECoordinator {
|
|
|
847
847
|
if (!fromAid || client._v2SenderIKFetching.has(fetchKey))
|
|
848
848
|
return;
|
|
849
849
|
client._v2SenderIKFetching.add(fetchKey);
|
|
850
|
-
client.
|
|
850
|
+
const releasePullWork = client._rpcPipeline?.reservePullWork?.() ?? (() => { });
|
|
851
|
+
const generation = client._delivery.captureInlineGeneration?.();
|
|
852
|
+
client._safeAsync(this.resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey, generation).finally(releasePullWork));
|
|
851
853
|
}
|
|
852
|
-
async resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey) {
|
|
854
|
+
async resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey, generation) {
|
|
853
855
|
const client = this.client;
|
|
856
|
+
const generationCurrent = () => generation === undefined
|
|
857
|
+
|| client._delivery.isInlineGenerationCurrent?.(generation) !== false;
|
|
854
858
|
try {
|
|
859
|
+
if (!generationCurrent())
|
|
860
|
+
return;
|
|
855
861
|
const session = client._v2Session;
|
|
856
862
|
if (session && fromAid) {
|
|
857
863
|
try {
|
|
@@ -887,18 +893,26 @@ export class V2E2EECoordinator {
|
|
|
887
893
|
await this.getV2SenderPubDer(fromAid, senderDeviceId);
|
|
888
894
|
}
|
|
889
895
|
}
|
|
896
|
+
if (!generationCurrent())
|
|
897
|
+
return;
|
|
890
898
|
const pendingItems = [...client._v2SenderIKPending.entries()].filter(([, entry]) => entry.fromAid === fromAid && entry.senderDeviceId === senderDeviceId && entry.groupId === groupId);
|
|
891
899
|
for (const [key, entry] of pendingItems) {
|
|
900
|
+
if (!generationCurrent())
|
|
901
|
+
return;
|
|
892
902
|
let plaintext = null;
|
|
893
903
|
const retryStatus = {};
|
|
894
904
|
try {
|
|
895
|
-
plaintext = await client._decryptV2Message(entry.msg, false, true, true, true, retryStatus);
|
|
905
|
+
plaintext = await client._decryptV2Message(entry.msg, false, true, true, true, retryStatus, false, 'pending_retry');
|
|
896
906
|
}
|
|
897
907
|
catch (exc) {
|
|
908
|
+
if (!generationCurrent())
|
|
909
|
+
return;
|
|
898
910
|
client._clientLog.warn(`V2 sender IK pending retry raised: key=${key} err=${String(formatE2EEError(exc))}`);
|
|
899
911
|
client._v2SenderIKPending.delete(key);
|
|
900
912
|
continue;
|
|
901
913
|
}
|
|
914
|
+
if (!generationCurrent())
|
|
915
|
+
return;
|
|
902
916
|
if (plaintext === null) {
|
|
903
917
|
if (retryStatus.deferred) {
|
|
904
918
|
client._clientLog.warn(`V2 pending retry still missing key material: key=${key}`);
|
|
@@ -918,10 +932,10 @@ export class V2E2EECoordinator {
|
|
|
918
932
|
if (entry.groupId) {
|
|
919
933
|
plaintext = normalizeGroupMentionMode(plaintext, entry.msg, entry.msg.envelope_json);
|
|
920
934
|
plaintext.group_id = entry.groupId;
|
|
921
|
-
await client._publishPulledMessage('group.message_created', `group:${entry.groupId}`, seq, plaintext);
|
|
935
|
+
await client._publishPulledMessage('group.message_created', `group:${entry.groupId}`, seq, plaintext, false, 'pending_retry');
|
|
922
936
|
}
|
|
923
937
|
else {
|
|
924
|
-
await client._publishPulledMessage('message.received', `p2p:${client._aid ?? ''}`, seq, plaintext);
|
|
938
|
+
await client._publishPulledMessage('message.received', `p2p:${client._aid ?? ''}`, seq, plaintext, false, 'pending_retry');
|
|
925
939
|
}
|
|
926
940
|
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
927
941
|
}
|
|
@@ -951,6 +965,7 @@ export class V2E2EECoordinator {
|
|
|
951
965
|
return client.call('message.send', {
|
|
952
966
|
to: toAid,
|
|
953
967
|
payload: envelope,
|
|
968
|
+
message_id: opts?.messageId,
|
|
954
969
|
encrypt: false,
|
|
955
970
|
_skip_send_result_envelope: true,
|
|
956
971
|
});
|
|
@@ -999,10 +1014,10 @@ export class V2E2EECoordinator {
|
|
|
999
1014
|
if (String(msg.version ?? '') === 'v1') {
|
|
1000
1015
|
const legacy = isJsonObject(msg.legacy_v1)
|
|
1001
1016
|
? msg.legacy_v1 : {};
|
|
1002
|
-
const payload = legacy.payload;
|
|
1017
|
+
const payload = legacy.payload !== undefined ? legacy.payload : msg.payload;
|
|
1003
1018
|
const payloadType = isJsonObject(payload)
|
|
1004
1019
|
? String(payload.type ?? '').trim() : '';
|
|
1005
|
-
if (payload !== undefined && payload !== null && !['e2ee.encrypted', 'e2ee.group_encrypted'].includes(payloadType)) {
|
|
1020
|
+
if (payload !== undefined && payload !== null && !['e2ee.encrypted', 'e2ee.group_encrypted', 'e2ee.p2p_encrypted'].includes(payloadType)) {
|
|
1006
1021
|
plaintext = {
|
|
1007
1022
|
message_id: String(msg.message_id ?? ''), from: String(msg.from_aid ?? ''),
|
|
1008
1023
|
to: String(legacy.to ?? client._aid ?? ''), seq, type: String(msg.type ?? ''),
|
|
@@ -1010,6 +1025,11 @@ export class V2E2EECoordinator {
|
|
|
1010
1025
|
};
|
|
1011
1026
|
attachGatewayProximity(plaintext, msg);
|
|
1012
1027
|
}
|
|
1028
|
+
else if (isJsonObject(payload)
|
|
1029
|
+
&& ['e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
1030
|
+
msg = { ...msg, envelope_json: JSON.stringify(payload) };
|
|
1031
|
+
plaintext = await client._decryptV2Message(msg, false, !history, true, true, undefined, false, source);
|
|
1032
|
+
}
|
|
1013
1033
|
}
|
|
1014
1034
|
else {
|
|
1015
1035
|
plaintext = source === 'inline_push'
|
|
@@ -1025,7 +1045,7 @@ export class V2E2EECoordinator {
|
|
|
1025
1045
|
if (publish) {
|
|
1026
1046
|
const event = client._delivery.p2pAppEventForMessage(plaintext);
|
|
1027
1047
|
if (orderedPublish) {
|
|
1028
|
-
await client._delivery.publishOrderedMessage(event.event, client._aid ? `p2p:${client._aid}` : '', seq, event.payload);
|
|
1048
|
+
await client._delivery.publishOrderedMessage(event.event, client._aid ? `p2p:${client._aid}` : '', seq, event.payload, source);
|
|
1029
1049
|
}
|
|
1030
1050
|
else {
|
|
1031
1051
|
await client._publishAppEvent(event.event, event.payload, source);
|
|
@@ -1039,26 +1059,48 @@ export class V2E2EECoordinator {
|
|
|
1039
1059
|
let plaintext = null;
|
|
1040
1060
|
if (String(msg.version ?? '') === 'v1') {
|
|
1041
1061
|
const payload = msg.payload;
|
|
1062
|
+
const recall = typeof client._delivery?.recallEventFromGroupMessage === 'function'
|
|
1063
|
+
? client._delivery.recallEventFromGroupMessage(msg)
|
|
1064
|
+
: null;
|
|
1065
|
+
if (recall) {
|
|
1066
|
+
const recallPayload = {
|
|
1067
|
+
...recall,
|
|
1068
|
+
group_id: groupId,
|
|
1069
|
+
group_aid: groupAid,
|
|
1070
|
+
seq,
|
|
1071
|
+
};
|
|
1072
|
+
if (publish) {
|
|
1073
|
+
if (orderedPublish) {
|
|
1074
|
+
await client._delivery.publishOrderedMessage('group.message_recalled', `group:${groupId}`, seq, recallPayload, source);
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
await client._publishAppEvent('group.message_recalled', recallPayload, source);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return recallPayload;
|
|
1081
|
+
}
|
|
1042
1082
|
if (payload === undefined || payload === null) {
|
|
1043
1083
|
if (history)
|
|
1044
1084
|
return this.historyDecryptFailure(msg, 'legacy payload is missing');
|
|
1045
1085
|
client._clientLog.warn(`Group Tail 缺少 payload 的 V1 行已跳过,连续性证明仍保留: group=${groupId}, seq=${seq}`);
|
|
1046
1086
|
return null;
|
|
1047
1087
|
}
|
|
1088
|
+
const payloadType = isJsonObject(payload)
|
|
1089
|
+
? String(payload.type ?? '').trim() : '';
|
|
1048
1090
|
if (isJsonObject(payload)
|
|
1049
|
-
&& ['e2ee.
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1091
|
+
&& ['e2ee.group_encrypted', 'e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
1092
|
+
msg = { ...msg, envelope_json: JSON.stringify(payload) };
|
|
1093
|
+
plaintext = await client._decryptV2Message(msg, false, !history, true, true, undefined, false, source);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
plaintext = {
|
|
1097
|
+
message_id: String(msg.message_id ?? ''), from: String(msg.from_aid ?? ''),
|
|
1098
|
+
group_id: groupId, group_aid: groupAid, seq, type: String(msg.type ?? ''),
|
|
1099
|
+
timestamp: msg.t_server, payload: payload, encrypted: false,
|
|
1100
|
+
};
|
|
1101
|
+
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
1102
|
+
attachGatewayProximity(plaintext, msg);
|
|
1054
1103
|
}
|
|
1055
|
-
plaintext = {
|
|
1056
|
-
message_id: String(msg.message_id ?? ''), from: String(msg.from_aid ?? ''),
|
|
1057
|
-
group_id: groupId, group_aid: groupAid, seq, type: String(msg.type ?? ''),
|
|
1058
|
-
timestamp: msg.t_server, payload: payload, encrypted: false,
|
|
1059
|
-
};
|
|
1060
|
-
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
1061
|
-
attachGatewayProximity(plaintext, msg);
|
|
1062
1104
|
}
|
|
1063
1105
|
else {
|
|
1064
1106
|
plaintext = source === 'inline_push'
|
|
@@ -1083,7 +1125,7 @@ export class V2E2EECoordinator {
|
|
|
1083
1125
|
}
|
|
1084
1126
|
if (publish) {
|
|
1085
1127
|
if (orderedPublish) {
|
|
1086
|
-
await client._delivery.publishOrderedMessage('group.message_created', `group:${groupId}`, seq, plaintext);
|
|
1128
|
+
await client._delivery.publishOrderedMessage('group.message_created', `group:${groupId}`, seq, plaintext, source);
|
|
1087
1129
|
}
|
|
1088
1130
|
else {
|
|
1089
1131
|
await client._publishAppEvent('group.message_created', plaintext, source);
|
|
@@ -1114,6 +1156,8 @@ export class V2E2EECoordinator {
|
|
|
1114
1156
|
const afterSeq = strictWindowSeq(params.after_seq ?? 0, 'after_seq');
|
|
1115
1157
|
const limit = strictWindowLimit(params.limit ?? V2_PULL_DEFAULT_PAGE_LIMIT);
|
|
1116
1158
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1159
|
+
if (ns)
|
|
1160
|
+
client._delivery.onPullStarted?.(ns);
|
|
1117
1161
|
const result = await client._callRawV2Rpc('message.v2.pull', { window_mode: 'tail', after_seq: afterSeq, limit, _rpc_foreground: true });
|
|
1118
1162
|
const floor = Number(result.retention_floor_seq ?? 0);
|
|
1119
1163
|
const previous = ns ? client._seqTracker.getSyncState(ns) : null;
|
|
@@ -1135,6 +1179,9 @@ export class V2E2EECoordinator {
|
|
|
1135
1179
|
const ack = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
1136
1180
|
if (previous && ack > previous.ack)
|
|
1137
1181
|
await this.ackV2(ack);
|
|
1182
|
+
if (ns)
|
|
1183
|
+
client._delivery.onPullPage?.(ns, decoded.length, result.has_more === true, page.head, typeof result.remaining === 'number' && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null, page.messages.length, ack, page.head);
|
|
1184
|
+
client._delivery.onPullWorkSettled?.();
|
|
1138
1185
|
return { ...result, messages: decoded, raw_count: page.messages.length };
|
|
1139
1186
|
}
|
|
1140
1187
|
async historyV2Internal(params) {
|
|
@@ -1150,16 +1197,33 @@ export class V2E2EECoordinator {
|
|
|
1150
1197
|
}
|
|
1151
1198
|
return { ...result, messages: decoded };
|
|
1152
1199
|
}
|
|
1200
|
+
async runPullWithLifecycle(ns, operation) {
|
|
1201
|
+
if (ns)
|
|
1202
|
+
this.client._delivery.onPullStarted?.(ns);
|
|
1203
|
+
try {
|
|
1204
|
+
return await operation();
|
|
1205
|
+
}
|
|
1206
|
+
catch (error) {
|
|
1207
|
+
this.client._delivery.onPullAborted?.();
|
|
1208
|
+
throw error;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1153
1211
|
async pullV2(afterSeq = 0, limit = 50, opts) {
|
|
1212
|
+
const ns = this.client._aid ? `p2p:${this.client._aid}` : '';
|
|
1213
|
+
return await this.runPullWithLifecycle(ns, () => this.pullV2Impl(afterSeq, limit, opts));
|
|
1214
|
+
}
|
|
1215
|
+
async pullV2Impl(afterSeq = 0, limit = 50, opts) {
|
|
1154
1216
|
const client = this.client;
|
|
1155
1217
|
await client._ensureV2SessionReady('message.pull');
|
|
1156
1218
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1219
|
+
if (ns)
|
|
1220
|
+
client._delivery.onPullStarted?.(ns);
|
|
1157
1221
|
if (opts?.windowMode === 'tail') {
|
|
1158
1222
|
if (!opts.gateLocked) {
|
|
1159
1223
|
const key = pullGateKeyForClient(client, 'message.v2.pull', {
|
|
1160
1224
|
window_mode: 'tail', after_seq: afterSeq, limit,
|
|
1161
1225
|
}, `${ns}|tail|after=${afterSeq}|limit=${limit}`);
|
|
1162
|
-
return await client._runPullSerialized(key, () => this.
|
|
1226
|
+
return await client._runPullSerialized(key, () => this.pullV2Impl(afterSeq, limit, {
|
|
1163
1227
|
...(opts ?? {}),
|
|
1164
1228
|
gateLocked: true,
|
|
1165
1229
|
}), false);
|
|
@@ -1171,7 +1235,7 @@ export class V2E2EECoordinator {
|
|
|
1171
1235
|
const key = pullGateKeyForClient(client, 'message.v2.pull', {
|
|
1172
1236
|
after_seq: afterSeq, force: opts?.force === true, limit,
|
|
1173
1237
|
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
1174
|
-
return await client._runPullSerialized(key, () => this.
|
|
1238
|
+
return await client._runPullSerialized(key, () => this.pullV2Impl(afterSeq, limit, {
|
|
1175
1239
|
...(opts ?? {}),
|
|
1176
1240
|
gateLocked: true,
|
|
1177
1241
|
}), true);
|
|
@@ -1235,6 +1299,7 @@ export class V2E2EECoordinator {
|
|
|
1235
1299
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
1236
1300
|
const deferredKeyFetches = new Map();
|
|
1237
1301
|
let blockedSeq = 0;
|
|
1302
|
+
const pageDecryptedBefore = decrypted.length;
|
|
1238
1303
|
for (const msg of messages) {
|
|
1239
1304
|
const seq = Number(msg.seq ?? 0);
|
|
1240
1305
|
if (!Number.isFinite(seq) || seq <= 0)
|
|
@@ -1242,12 +1307,12 @@ export class V2E2EECoordinator {
|
|
|
1242
1307
|
const version = String(msg.version ?? 'v2');
|
|
1243
1308
|
if (version === 'v1') {
|
|
1244
1309
|
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
1245
|
-
const legacyPayload = legacy.payload;
|
|
1310
|
+
const legacyPayload = legacy.payload !== undefined ? legacy.payload : msg.payload;
|
|
1246
1311
|
const payloadType = isJsonObject(legacyPayload)
|
|
1247
1312
|
? String(legacyPayload.type ?? '').trim()
|
|
1248
1313
|
: '';
|
|
1249
1314
|
if (legacyPayload !== undefined && legacyPayload !== null
|
|
1250
|
-
&&
|
|
1315
|
+
&& !['e2ee.encrypted', 'e2ee.group_encrypted', 'e2ee.p2p_encrypted'].includes(payloadType)) {
|
|
1251
1316
|
const v1Msg = {
|
|
1252
1317
|
message_id: String(msg.message_id ?? ''),
|
|
1253
1318
|
from: String(msg.from_aid ?? ''),
|
|
@@ -1263,9 +1328,28 @@ export class V2E2EECoordinator {
|
|
|
1263
1328
|
if (ns)
|
|
1264
1329
|
await client._publishPulledMessage(appEvent.event, ns, seq, appEvent.payload, false);
|
|
1265
1330
|
else
|
|
1266
|
-
await client._publishAppEvent(appEvent.event, appEvent.payload);
|
|
1331
|
+
await client._publishAppEvent(appEvent.event, appEvent.payload, 'pull');
|
|
1267
1332
|
decrypted.push(v1Msg);
|
|
1268
1333
|
}
|
|
1334
|
+
else if (isJsonObject(legacyPayload)
|
|
1335
|
+
&& ['e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
1336
|
+
const deferStatus = {};
|
|
1337
|
+
const plaintext = await client._decryptV2Message({ ...msg, envelope_json: JSON.stringify(legacyPayload) }, true, true, true, true, deferStatus, true, 'pull');
|
|
1338
|
+
if (deferStatus.deferred) {
|
|
1339
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
1340
|
+
if (deferStatus.fromAid) {
|
|
1341
|
+
const key = `${deferStatus.fromAid}\u0000${deferStatus.senderDeviceId ?? ''}\u0000${deferStatus.groupId ?? ''}`;
|
|
1342
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus.fromAid, senderDeviceId: deferStatus.senderDeviceId ?? '', groupId: deferStatus.groupId ?? '' });
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (plaintext) {
|
|
1346
|
+
if (ns)
|
|
1347
|
+
await client._publishPulledMessage('message.received', ns, seq, plaintext, false);
|
|
1348
|
+
else
|
|
1349
|
+
await client._publishAppEvent('message.received', plaintext, 'pull');
|
|
1350
|
+
decrypted.push(plaintext);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1269
1353
|
else {
|
|
1270
1354
|
client._clientLog.debug(`message.v2.pull skipping V1 envelope seq=${seq} payload_type=${payloadType || '<none>'} (V1 E2EE removed)`);
|
|
1271
1355
|
}
|
|
@@ -1280,7 +1364,7 @@ export class V2E2EECoordinator {
|
|
|
1280
1364
|
client._v2Session.trackOldSPKMaxSeq(msgSpkId, seq);
|
|
1281
1365
|
}
|
|
1282
1366
|
const deferStatus = {};
|
|
1283
|
-
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true);
|
|
1367
|
+
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, 'pull');
|
|
1284
1368
|
if (deferStatus.deferred) {
|
|
1285
1369
|
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
1286
1370
|
}
|
|
@@ -1299,7 +1383,7 @@ export class V2E2EECoordinator {
|
|
|
1299
1383
|
decrypted.push(plaintext);
|
|
1300
1384
|
}
|
|
1301
1385
|
else {
|
|
1302
|
-
await client._publishAppEvent('message.received', plaintext);
|
|
1386
|
+
await client._publishAppEvent('message.received', plaintext, 'pull');
|
|
1303
1387
|
decrypted.push(plaintext);
|
|
1304
1388
|
}
|
|
1305
1389
|
}
|
|
@@ -1360,7 +1444,7 @@ export class V2E2EECoordinator {
|
|
|
1360
1444
|
if (ackNeeded) {
|
|
1361
1445
|
this.recordForwardAck(ns, ackSeq);
|
|
1362
1446
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
1363
|
-
const canContinuePage = messages.length > 0 && nextAfter > nextAfterSeq;
|
|
1447
|
+
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter > nextAfterSeq;
|
|
1364
1448
|
if (canContinuePage) {
|
|
1365
1449
|
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
1366
1450
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
@@ -1371,6 +1455,8 @@ export class V2E2EECoordinator {
|
|
|
1371
1455
|
}
|
|
1372
1456
|
}
|
|
1373
1457
|
}
|
|
1458
|
+
if (ns)
|
|
1459
|
+
client._delivery.onPullPage?.(ns, decrypted.length - pageDecryptedBefore, hasMore, pageMaxSeq, typeof result.remaining === 'number' && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null, messages.length, client._seqTracker.getContiguousSeq(ns), pageMaxSeq);
|
|
1374
1460
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
1375
1461
|
const rawCount = messages.length;
|
|
1376
1462
|
if (blockedSeq > 0)
|
|
@@ -1406,6 +1492,7 @@ export class V2E2EECoordinator {
|
|
|
1406
1492
|
pendingAckSeq = 0;
|
|
1407
1493
|
}
|
|
1408
1494
|
}
|
|
1495
|
+
client._delivery.onPullWorkSettled?.();
|
|
1409
1496
|
return decrypted;
|
|
1410
1497
|
}
|
|
1411
1498
|
async confirmForwardP2PAck(upToSeq, inlineGeneration) {
|
|
@@ -1496,6 +1583,7 @@ export class V2E2EECoordinator {
|
|
|
1496
1583
|
return client.call('group.v2.send', withExplicitGroupAid({
|
|
1497
1584
|
group_id: gid,
|
|
1498
1585
|
envelope,
|
|
1586
|
+
message_id: opts?.messageId,
|
|
1499
1587
|
}, groupAid));
|
|
1500
1588
|
};
|
|
1501
1589
|
try {
|
|
@@ -1558,6 +1646,14 @@ export class V2E2EECoordinator {
|
|
|
1558
1646
|
const afterSeq = strictWindowSeq(params.after_seq ?? 0, 'after_seq');
|
|
1559
1647
|
const limit = strictWindowLimit(params.limit ?? V2_PULL_DEFAULT_PAGE_LIMIT);
|
|
1560
1648
|
const ns = `group:${groupId}`;
|
|
1649
|
+
const cursorParams = isJsonObject(params._group_cursor_params)
|
|
1650
|
+
? params._group_cursor_params : params;
|
|
1651
|
+
const requestDeviceId = String(cursorParams.device_id ?? '').trim();
|
|
1652
|
+
const requestSlotId = String(cursorParams.slot_id ?? '').trim();
|
|
1653
|
+
const ownsCursor = (!requestDeviceId || requestDeviceId === String(client._deviceId ?? ''))
|
|
1654
|
+
&& (!requestSlotId || requestSlotId === String(client._slotId ?? ''));
|
|
1655
|
+
if (ownsCursor)
|
|
1656
|
+
client._delivery.onPullStarted?.(ns);
|
|
1561
1657
|
const result = await client._callRawV2Rpc('group.v2.pull', withExplicitGroupAid({
|
|
1562
1658
|
group_id: groupId, window_mode: 'tail', after_seq: afterSeq, limit, _rpc_foreground: true,
|
|
1563
1659
|
}, groupAid));
|
|
@@ -1583,6 +1679,9 @@ export class V2E2EECoordinator {
|
|
|
1583
1679
|
const ack = client._seqTracker.getContiguousSeq(ns);
|
|
1584
1680
|
if (ack > previous.ack)
|
|
1585
1681
|
await this.ackGroupV2(groupId, ack, groupAid);
|
|
1682
|
+
if (ownsCursor)
|
|
1683
|
+
client._delivery.onPullPage?.(ns, decoded.length, result.has_more === true, page.head, typeof result.remaining === 'number' && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null, page.messages.length, ack, page.head);
|
|
1684
|
+
client._delivery.onPullWorkSettled?.();
|
|
1586
1685
|
return { ...result, messages: decoded, raw_count: page.messages.length };
|
|
1587
1686
|
}
|
|
1588
1687
|
async historyGroupV2Internal(params) {
|
|
@@ -1615,6 +1714,13 @@ export class V2E2EECoordinator {
|
|
|
1615
1714
|
});
|
|
1616
1715
|
}
|
|
1617
1716
|
async pullGroupV2(groupId, afterSeq = 0, limit = 50, opts) {
|
|
1717
|
+
const lifecycleNs = `group:${String(groupId ?? '').trim()}`;
|
|
1718
|
+
if (opts?.ownsCursor === false) {
|
|
1719
|
+
return await this.pullGroupV2Impl(groupId, afterSeq, limit, opts);
|
|
1720
|
+
}
|
|
1721
|
+
return await this.runPullWithLifecycle(lifecycleNs, () => this.pullGroupV2Impl(groupId, afterSeq, limit, opts));
|
|
1722
|
+
}
|
|
1723
|
+
async pullGroupV2Impl(groupId, afterSeq = 0, limit = 50, opts) {
|
|
1618
1724
|
const client = this.client;
|
|
1619
1725
|
await client._ensureV2SessionReady('group.pull');
|
|
1620
1726
|
const gid = String(groupId ?? '').trim();
|
|
@@ -1628,12 +1734,13 @@ export class V2E2EECoordinator {
|
|
|
1628
1734
|
group_id: gid, window_mode: 'tail', after_seq: afterSeq, limit,
|
|
1629
1735
|
_group_cursor_params: opts?.cursorParams,
|
|
1630
1736
|
}, `${ns}|tail|after=${afterSeq}|limit=${limit}`);
|
|
1631
|
-
return await client._runPullSerialized(key, () => this.
|
|
1737
|
+
return await client._runPullSerialized(key, () => this.pullGroupV2Impl(gid, afterSeq, limit, {
|
|
1632
1738
|
...(opts ?? {}),
|
|
1633
1739
|
gateLocked: true,
|
|
1634
1740
|
}), false);
|
|
1635
1741
|
}
|
|
1636
1742
|
const result = await this.pullGroupV2TailInternal({
|
|
1743
|
+
...(opts?.cursorParams ?? {}),
|
|
1637
1744
|
group_id: String(opts.wireGroupId ?? gid), group_aid: groupAid || undefined,
|
|
1638
1745
|
window_mode: 'tail', after_seq: afterSeq, limit,
|
|
1639
1746
|
});
|
|
@@ -1644,7 +1751,7 @@ export class V2E2EECoordinator {
|
|
|
1644
1751
|
group_id: gid, after_seq: afterSeq, force: opts?.force === true, limit,
|
|
1645
1752
|
_group_cursor_params: opts?.cursorParams,
|
|
1646
1753
|
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
1647
|
-
return await client._runPullSerialized(key, () => this.
|
|
1754
|
+
return await client._runPullSerialized(key, () => this.pullGroupV2Impl(gid, afterSeq, limit, {
|
|
1648
1755
|
...(opts ?? {}),
|
|
1649
1756
|
gateLocked: true,
|
|
1650
1757
|
}), true);
|
|
@@ -1653,6 +1760,8 @@ export class V2E2EECoordinator {
|
|
|
1653
1760
|
const wireGroupId = String(opts?.wireGroupId ?? groupId ?? '').trim() || gid;
|
|
1654
1761
|
const cursorParams = opts?.cursorParams ?? {};
|
|
1655
1762
|
const ownsCursor = opts?.ownsCursor !== false;
|
|
1763
|
+
if (ownsCursor)
|
|
1764
|
+
client._delivery.onPullStarted?.(ns);
|
|
1656
1765
|
let pullGateKey = ownsCursor
|
|
1657
1766
|
? pullGateKeyForClient(client, 'group.v2.pull', {
|
|
1658
1767
|
group_id: gid, after_seq: afterSeq, force: opts?.force === true, limit,
|
|
@@ -1721,28 +1830,31 @@ export class V2E2EECoordinator {
|
|
|
1721
1830
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
1722
1831
|
const deferredKeyFetches = new Map();
|
|
1723
1832
|
let blockedSeq = 0;
|
|
1833
|
+
const pageDecryptedBefore = decrypted.length;
|
|
1724
1834
|
for (const msg of messages) {
|
|
1725
1835
|
const seq = Number(msg.seq ?? 0);
|
|
1726
1836
|
if (!Number.isFinite(seq) || seq <= 0)
|
|
1727
1837
|
continue;
|
|
1728
1838
|
const version = String(msg.version ?? 'v2');
|
|
1729
1839
|
if (version === 'v1') {
|
|
1730
|
-
const
|
|
1840
|
+
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
1841
|
+
const payload = msg.payload !== undefined ? msg.payload : legacy.payload;
|
|
1731
1842
|
const payloadObj = isJsonObject(payload) ? payload : null;
|
|
1732
1843
|
// 群撤回 tombstone(占位 / 通知):归一化为 group.message_recalled 事件,仍占 seq。
|
|
1733
|
-
if (client._delivery
|
|
1844
|
+
if (typeof client._delivery?.recallEventFromGroupMessage === 'function'
|
|
1845
|
+
&& client._delivery.recallEventFromGroupMessage(msg)) {
|
|
1734
1846
|
await client._delivery.publishGroupRecallTombstone(gid, seq, {
|
|
1735
1847
|
...msg,
|
|
1736
1848
|
group_id: gid,
|
|
1737
1849
|
group_aid: eventGroupAid,
|
|
1738
|
-
});
|
|
1850
|
+
}, 'pull');
|
|
1739
1851
|
client._markPublishedSeq(ns, seq);
|
|
1740
1852
|
client._clientLog.debug(`group.v2.pull recall tombstone delivered: group=${gid}, seq=${seq}`);
|
|
1741
1853
|
continue;
|
|
1742
1854
|
}
|
|
1743
1855
|
if (payloadObj) {
|
|
1744
1856
|
const payloadType = String(payloadObj.type ?? '').trim();
|
|
1745
|
-
if (payloadType !== 'e2ee.encrypted' && payloadType !== 'e2ee.group_encrypted') {
|
|
1857
|
+
if (payloadType !== 'e2ee.encrypted' && payloadType !== 'e2ee.group_encrypted' && payloadType !== 'e2ee.p2p_encrypted') {
|
|
1746
1858
|
let v1Msg = {
|
|
1747
1859
|
message_id: String(msg.message_id ?? ''),
|
|
1748
1860
|
from: String(msg.from_aid ?? ''),
|
|
@@ -1779,6 +1891,23 @@ export class V2E2EECoordinator {
|
|
|
1779
1891
|
decrypted.push(v1Msg);
|
|
1780
1892
|
continue;
|
|
1781
1893
|
}
|
|
1894
|
+
if (payloadObj && ['e2ee.group_encrypted', 'e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(String(payloadObj.type ?? '').trim())) {
|
|
1895
|
+
const deferStatus = {};
|
|
1896
|
+
const plaintext = await client._decryptV2Message({ ...msg, envelope_json: JSON.stringify(payloadObj) }, true, true, true, true, deferStatus, true, 'pull');
|
|
1897
|
+
if (deferStatus.deferred) {
|
|
1898
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
1899
|
+
if (deferStatus.fromAid) {
|
|
1900
|
+
const key = `${deferStatus.fromAid}\u0000${deferStatus.senderDeviceId ?? ''}\u0000${deferStatus.groupId ?? ''}`;
|
|
1901
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus.fromAid, senderDeviceId: deferStatus.senderDeviceId ?? '', groupId: deferStatus.groupId ?? '' });
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
if (plaintext) {
|
|
1905
|
+
plaintext.group_id = gid;
|
|
1906
|
+
plaintext.group_aid = eventGroupAid;
|
|
1907
|
+
await client._publishPulledMessage('group.message_created', ns, seq, plaintext, false);
|
|
1908
|
+
decrypted.push(plaintext);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1782
1911
|
client._clientLog.debug(`group.v2.pull skipping V1 envelope group=${gid} seq=${seq} payload_type=${payloadObj ? String(payloadObj.type ?? '') : '<none>'} (V1 E2EE removed)`);
|
|
1783
1912
|
continue;
|
|
1784
1913
|
}
|
|
@@ -1787,7 +1916,7 @@ export class V2E2EECoordinator {
|
|
|
1787
1916
|
continue;
|
|
1788
1917
|
}
|
|
1789
1918
|
const deferStatus = {};
|
|
1790
|
-
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true);
|
|
1919
|
+
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, 'pull');
|
|
1791
1920
|
if (deferStatus.deferred) {
|
|
1792
1921
|
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
1793
1922
|
}
|
|
@@ -1851,6 +1980,7 @@ export class V2E2EECoordinator {
|
|
|
1851
1980
|
for (const fetch of deferredKeyFetches.values()) {
|
|
1852
1981
|
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
1853
1982
|
}
|
|
1983
|
+
client._delivery.onPullPage?.(ns, decrypted.length - pageDecryptedBefore, hasMore, pageMaxSeq, typeof result.remaining === 'number' && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null, messages.length, client._seqTracker.getContiguousSeq(ns), pageMaxSeq);
|
|
1854
1984
|
if (ownsCursor && parsedCursorCurrentSeq !== null && cursorCurrentSeq > ackSeq) {
|
|
1855
1985
|
this.recordForwardCursor(ns, cursorCurrentSeq, ackSeq);
|
|
1856
1986
|
}
|
|
@@ -1868,7 +1998,7 @@ export class V2E2EECoordinator {
|
|
|
1868
1998
|
if (ackNeeded) {
|
|
1869
1999
|
this.recordForwardAck(ns, ackSeq);
|
|
1870
2000
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
1871
|
-
const canContinuePage = messages.length > 0 && nextAfter > nextAfterSeq && ownsCursor;
|
|
2001
|
+
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter > nextAfterSeq && ownsCursor;
|
|
1872
2002
|
if (canContinuePage) {
|
|
1873
2003
|
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
1874
2004
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
@@ -1916,6 +2046,7 @@ export class V2E2EECoordinator {
|
|
|
1916
2046
|
pendingAckSeq = 0;
|
|
1917
2047
|
}
|
|
1918
2048
|
}
|
|
2049
|
+
client._delivery.onPullWorkSettled?.();
|
|
1919
2050
|
return decrypted;
|
|
1920
2051
|
}
|
|
1921
2052
|
async confirmForwardGroupAck(groupId, upToSeq, groupAid) {
|
|
@@ -2470,7 +2601,7 @@ export class V2E2EECoordinator {
|
|
|
2470
2601
|
async decryptV2PushMessage(data) {
|
|
2471
2602
|
if (!isJsonObject(data))
|
|
2472
2603
|
return null;
|
|
2473
|
-
return await this.client._decryptV2Message(data);
|
|
2604
|
+
return await this.client._decryptV2Message(data, false, false, false, false, undefined, false, 'inline_push');
|
|
2474
2605
|
}
|
|
2475
2606
|
async buildV2P2PEnvelope(opts) {
|
|
2476
2607
|
const client = this.client;
|