@agentunion/fastaun 0.5.18 → 0.5.20
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 +172 -79
- package/_packed_docs/CHANGELOG.md +172 -79
- package/_packed_docs/INDEX.md +244 -116
- package/_packed_docs/KITE_DOCS_GUIDE.md +98 -69
- package/_packed_docs/agent.md/SCHEMA.md +245 -205
- package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +11 -11
- package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +42 -15
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +4 -4
- package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +45 -134
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1072 -1072
- package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +5 -20
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +687 -668
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +19 -28
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +443 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +658 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
- package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +1 -1
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +68 -61
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +279 -285
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
- package/_packed_docs/sdk/09-group-rpc-manual.md +271 -235
- package/_packed_docs/sdk/09-message-rpc-manual.md +47 -6
- package/_packed_docs/sdk/09-storage-rpc-manual.md +70 -68
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +63 -34
- package/_packed_docs/sdk/INDEX.md +105 -34
- package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +922 -0
- package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
- package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +300 -0
- package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +382 -0
- package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -0
- package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +476 -0
- package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
- package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
- package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +173 -0
- package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +211 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/345/256/236/347/216/260/347/247/273/346/244/215/346/214/207/345/215/227.md +331 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +207 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/344/273/243/347/240/201/345/256/241/346/237/245/346/212/245/345/221/212.md +252 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/345/256/236/346/226/275/350/256/241/345/210/222.md +156 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/346/226/271/346/241/210.md +273 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/350/257/246/347/273/206/350/256/276/350/256/241.md +211 -0
- package/dist/agent-md-schema.d.ts +15 -1
- package/dist/agent-md-schema.js +87 -24
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +54 -2
- package/dist/agent-md.js +637 -53
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +38 -59
- package/dist/aid-store.js +300 -265
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.js +30 -11
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts +6 -1
- package/dist/auth.js +500 -15
- package/dist/auth.js.map +1 -1
- package/dist/cert-utils.d.ts +44 -2
- package/dist/cert-utils.js +149 -49
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +11 -1
- package/dist/client/delivery.js +321 -62
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +1 -0
- package/dist/client/rpc-pipeline.js +142 -82
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -0
- package/dist/client/v2-e2ee.js +166 -96
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +84 -1
- package/dist/client.js +780 -30
- package/dist/client.js.map +1 -1
- package/dist/errors.js +4 -3
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +9 -3
- package/dist/events.js +69 -11
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +83 -3
- package/dist/facades.js +100 -6
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +5 -0
- package/dist/keystore/aid-db.js +57 -0
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +8 -0
- package/dist/keystore/local-identity-store.js +10 -3
- package/dist/keystore/local-identity-store.js.map +1 -1
- package/dist/keystore/local-token-store.d.ts +6 -1
- package/dist/keystore/local-token-store.js +24 -1
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +98 -0
- package/dist/org-protocol.js +1753 -0
- package/dist/org-protocol.js.map +1 -0
- package/dist/ownership-proof.d.ts +82 -5
- package/dist/ownership-proof.js +327 -31
- package/dist/ownership-proof.js.map +1 -1
- package/dist/ownership-v21.d.ts +196 -0
- package/dist/ownership-v21.js +712 -0
- package/dist/ownership-v21.js.map +1 -0
- package/dist/ownership-workflow.d.ts +87 -0
- package/dist/ownership-workflow.js +1182 -0
- package/dist/ownership-workflow.js.map +1 -0
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.js +10 -1
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +90 -1
- package/dist/tools/cross-sdk-agent.js +441 -7
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +8 -0
- package/dist/transport.js +118 -4
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/decrypt.d.ts +1 -0
- package/dist/v2/e2ee/decrypt.js +9 -1
- package/dist/v2/e2ee/decrypt.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/v2-e2ee.js
CHANGED
|
@@ -113,13 +113,13 @@ function deduplicateForwardPageMessages(value, afterSeq) {
|
|
|
113
113
|
function contiguousPullSeq(afterSeq, seqs, proof) {
|
|
114
114
|
const pulledMax = seqs.length > 0 ? Math.max(...seqs) : 0;
|
|
115
115
|
const serverProof = safeNonNegativeSeq(proof);
|
|
116
|
-
return Math.max(afterSeq, pulledMax, serverProof ?? 0);
|
|
116
|
+
return Math.max(afterSeq, pulledMax, pulledMax > 0 ? 0 : serverProof ?? 0);
|
|
117
117
|
}
|
|
118
118
|
function shouldContinueForwardPage({ rawCount, nextAfterSeq, nextAfter, limit, hasMoreField, hasMore, serverHead, observedUpper, pendingAckSeq, }) {
|
|
119
119
|
// 无新 seq 或游标未前进时必须停止,避免错误元数据造成循环。
|
|
120
120
|
if (rawCount <= 0 || nextAfter <= nextAfterSeq)
|
|
121
121
|
return false;
|
|
122
|
-
if (hasMore || serverHead > nextAfter || observedUpper > nextAfter
|
|
122
|
+
if (hasMore || serverHead > nextAfter || observedUpper > nextAfter)
|
|
123
123
|
return true;
|
|
124
124
|
// 仅旧服务缺失 has_more 时,以满页保留兼容续拉。
|
|
125
125
|
return !hasMoreField && limit > 0 && rawCount >= limit;
|
|
@@ -825,11 +825,12 @@ export class V2E2EECoordinator {
|
|
|
825
825
|
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
826
826
|
continue;
|
|
827
827
|
}
|
|
828
|
-
pending.delete(key);
|
|
829
828
|
const seq = Number(entry.msg.seq ?? 0);
|
|
830
829
|
if (entry.groupId) {
|
|
831
|
-
if (client._delivery.isSelfSentGroupMessage?.(entry.msg))
|
|
830
|
+
if (client._delivery.isSelfSentGroupMessage?.(entry.msg)) {
|
|
831
|
+
pending.delete(key);
|
|
832
832
|
continue;
|
|
833
|
+
}
|
|
833
834
|
plaintext = normalizeGroupMentionMode(plaintext, entry.msg, entry.msg.envelope_json);
|
|
834
835
|
plaintext.group_id = entry.groupId;
|
|
835
836
|
await client._publishPulledMessage('group.message_created', `group:${entry.groupId}`, seq, plaintext, false, 'pending_retry');
|
|
@@ -837,6 +838,8 @@ export class V2E2EECoordinator {
|
|
|
837
838
|
else {
|
|
838
839
|
await client._publishPulledMessage('message.received', `p2p:${client._aid ?? ''}`, seq, plaintext, false, 'pending_retry');
|
|
839
840
|
}
|
|
841
|
+
if (generationIsCurrent())
|
|
842
|
+
pending.delete(key);
|
|
840
843
|
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
841
844
|
}
|
|
842
845
|
}
|
|
@@ -914,10 +917,14 @@ export class V2E2EECoordinator {
|
|
|
914
917
|
});
|
|
915
918
|
}
|
|
916
919
|
}
|
|
920
|
+
const targetDeviceId = String(opts.deviceId ?? '').trim();
|
|
921
|
+
if (useCache && targetDeviceId && !peerDevices.some((dev) => String(dev.device_id ?? '').trim() === targetDeviceId)) {
|
|
922
|
+
this.deleteBootstrapCacheEntry(to);
|
|
923
|
+
return this.buildV2P2PEnvelope({ ...opts, useCache: false });
|
|
924
|
+
}
|
|
917
925
|
if (peerDevices.length === 0) {
|
|
918
926
|
throw new E2EEError(`V2 bootstrap: no devices found for ${to}`);
|
|
919
927
|
}
|
|
920
|
-
const targetDeviceId = String(opts.deviceId ?? '').trim();
|
|
921
928
|
if (targetDeviceId && !peerDevices.some((dev) => String(dev.device_id ?? '').trim() === targetDeviceId)) {
|
|
922
929
|
throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
|
|
923
930
|
}
|
|
@@ -1004,6 +1011,10 @@ export class V2E2EECoordinator {
|
|
|
1004
1011
|
targetSet = this.cloneP2PTargetSet(targetSet);
|
|
1005
1012
|
}
|
|
1006
1013
|
if (targetSet.targets.length === 0) {
|
|
1014
|
+
if (useCache && targetDeviceId) {
|
|
1015
|
+
this.deleteBootstrapCacheEntry(to);
|
|
1016
|
+
return this.buildV2P2PEnvelope({ ...opts, useCache: false });
|
|
1017
|
+
}
|
|
1007
1018
|
throw new E2EEError(`V2 bootstrap: no usable devices found for ${to}`);
|
|
1008
1019
|
}
|
|
1009
1020
|
if (targetDeviceId) {
|
|
@@ -1017,6 +1028,10 @@ export class V2E2EECoordinator {
|
|
|
1017
1028
|
return isPeerTarget || target.role === 'self_sync';
|
|
1018
1029
|
});
|
|
1019
1030
|
if (!peerTargetFound) {
|
|
1031
|
+
if (useCache) {
|
|
1032
|
+
this.deleteBootstrapCacheEntry(to);
|
|
1033
|
+
return this.buildV2P2PEnvelope({ ...opts, useCache: false });
|
|
1034
|
+
}
|
|
1020
1035
|
throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
|
|
1021
1036
|
}
|
|
1022
1037
|
}
|
|
@@ -1122,6 +1137,11 @@ export class V2E2EECoordinator {
|
|
|
1122
1137
|
history_error: { code: 'decrypt_failed', message: reason },
|
|
1123
1138
|
};
|
|
1124
1139
|
}
|
|
1140
|
+
ensureDeliveryCurrent(ns, generation) {
|
|
1141
|
+
if (Number(this.client._v2RuntimeGeneration ?? 0) !== generation
|
|
1142
|
+
|| this.client._rpcPipeline?.isPullNamespaceCurrent?.(ns) === false)
|
|
1143
|
+
throw new StateError('pull invalidated');
|
|
1144
|
+
}
|
|
1125
1145
|
async decodeP2PWindowMessage(msg, publish, history, source, orderedPublish = false) {
|
|
1126
1146
|
const client = this.client;
|
|
1127
1147
|
const seq = strictWindowSeq(msg.seq, 'messages[].seq', true);
|
|
@@ -1250,6 +1270,7 @@ export class V2E2EECoordinator {
|
|
|
1250
1270
|
}
|
|
1251
1271
|
async pullV2TailInternal(params, deferPublish = false) {
|
|
1252
1272
|
const client = this.client;
|
|
1273
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
1253
1274
|
if (params.window_mode !== 'tail')
|
|
1254
1275
|
throw new ValidationError('window_mode must equal tail');
|
|
1255
1276
|
if ('ack_up_to_seq' in params || params.force === true)
|
|
@@ -1264,6 +1285,14 @@ export class V2E2EECoordinator {
|
|
|
1264
1285
|
}, `${ns}|tail|after=${afterSeq}|limit=${limit}`);
|
|
1265
1286
|
const result = await client._callRawV2Rpc('message.v2.pull', { window_mode: 'tail', after_seq: afterSeq, limit, _rpc_foreground: true }, pullGateKey);
|
|
1266
1287
|
const page = validateTailPage(result, afterSeq, limit);
|
|
1288
|
+
const decoded = [];
|
|
1289
|
+
for (const msg of page.messages) {
|
|
1290
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1291
|
+
const plaintext = await this.decodeP2PWindowMessage(msg, true, false, 'tail', deferPublish);
|
|
1292
|
+
if (plaintext)
|
|
1293
|
+
decoded.push(plaintext);
|
|
1294
|
+
}
|
|
1295
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1267
1296
|
if (ns) {
|
|
1268
1297
|
client._seqTracker.commitTailWindow(ns, {
|
|
1269
1298
|
windowStartSeq: page.windowStart,
|
|
@@ -1273,12 +1302,7 @@ export class V2E2EECoordinator {
|
|
|
1273
1302
|
});
|
|
1274
1303
|
client._persistSeq(ns);
|
|
1275
1304
|
await client._saveSeqTrackerState?.();
|
|
1276
|
-
|
|
1277
|
-
const decoded = [];
|
|
1278
|
-
for (const msg of page.messages) {
|
|
1279
|
-
const plaintext = await this.decodeP2PWindowMessage(msg, true, false, 'tail', deferPublish);
|
|
1280
|
-
if (plaintext)
|
|
1281
|
-
decoded.push(plaintext);
|
|
1305
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1282
1306
|
}
|
|
1283
1307
|
const ack = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
1284
1308
|
if (ns)
|
|
@@ -1308,6 +1332,7 @@ export class V2E2EECoordinator {
|
|
|
1308
1332
|
}
|
|
1309
1333
|
async pullV2Impl(afterSeq = 0, limit = 50, opts) {
|
|
1310
1334
|
const client = this.client;
|
|
1335
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
1311
1336
|
await client._ensureV2SessionReady('message.pull');
|
|
1312
1337
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1313
1338
|
if (ns)
|
|
@@ -1348,7 +1373,7 @@ export class V2E2EECoordinator {
|
|
|
1348
1373
|
const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
|
|
1349
1374
|
let pipelineRunning = false;
|
|
1350
1375
|
let pipelineError = null;
|
|
1351
|
-
|
|
1376
|
+
let pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
|
|
1352
1377
|
let pipelineProducer = null;
|
|
1353
1378
|
let pipelineStopped = false;
|
|
1354
1379
|
const startPipeline = (startAfterSeq, startVolatileCursor) => {
|
|
@@ -1361,21 +1386,26 @@ export class V2E2EECoordinator {
|
|
|
1361
1386
|
for (let produced = pageCount; produced < maxPages; produced += 1) {
|
|
1362
1387
|
if (pipelineStopped)
|
|
1363
1388
|
break;
|
|
1389
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1364
1390
|
const requestedVolatileCursor = producerVolatileCursor;
|
|
1391
|
+
const ackUpToSeq = pendingAckSeq;
|
|
1392
|
+
const inlineAckGeneration = ackUpToSeq > 0 ? client._delivery.captureP2PInlineAckGeneration() : null;
|
|
1365
1393
|
const result = await client._callRawV2Rpc('message.v2.pull', {
|
|
1366
1394
|
after_seq: producerAfterSeq,
|
|
1367
1395
|
limit,
|
|
1396
|
+
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
1368
1397
|
...(producerVolatileCursor ? { after_volatile_cursor: producerVolatileCursor } : {}),
|
|
1369
1398
|
...(opts?.force ? { force: true } : {}),
|
|
1370
1399
|
}, pullGateKey);
|
|
1371
1400
|
if (pipelineStopped)
|
|
1372
1401
|
break;
|
|
1402
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1373
1403
|
const rawPageMessages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
1374
1404
|
const enqueued = await pipelineQueue.put({
|
|
1375
1405
|
result,
|
|
1376
1406
|
afterSeq: producerAfterSeq,
|
|
1377
|
-
ackUpToSeq
|
|
1378
|
-
inlineAckGeneration
|
|
1407
|
+
ackUpToSeq,
|
|
1408
|
+
inlineAckGeneration,
|
|
1379
1409
|
requestedVolatileCursor,
|
|
1380
1410
|
weight: rawPageMessages.length,
|
|
1381
1411
|
}, rawPageMessages.length);
|
|
@@ -1388,7 +1418,7 @@ export class V2E2EECoordinator {
|
|
|
1388
1418
|
const seqs = messages
|
|
1389
1419
|
.map((msg) => Number(msg.seq ?? 0))
|
|
1390
1420
|
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
1391
|
-
const nextAfter = Math.max(
|
|
1421
|
+
const nextAfter = Math.max(producerAfterSeq, ...seqs);
|
|
1392
1422
|
const cursor = isJsonObject(result.cursor)
|
|
1393
1423
|
? result.cursor : null;
|
|
1394
1424
|
const shouldContinue = shouldContinueForwardPage({
|
|
@@ -1438,7 +1468,7 @@ export class V2E2EECoordinator {
|
|
|
1438
1468
|
pageCount += 1;
|
|
1439
1469
|
const requestedVolatileCursor = pipelinePage?.requestedVolatileCursor ?? volatileCursor;
|
|
1440
1470
|
const ackUpToSeq = pipelinePage?.ackUpToSeq ?? pendingAckSeq;
|
|
1441
|
-
const inlineAckGeneration = pipelinePage
|
|
1471
|
+
const inlineAckGeneration = pipelinePage ? pipelinePage.inlineAckGeneration : (ackUpToSeq > 0
|
|
1442
1472
|
? client._delivery.captureP2PInlineAckGeneration()
|
|
1443
1473
|
: null);
|
|
1444
1474
|
client._clientLog.debug(`message.v2.pull page request: page=${pageCount}, after_seq=${nextAfterSeq}, limit=${limit}, ack_up_to_seq=${ackUpToSeq}, ns=${ns || '<none>'}`);
|
|
@@ -1467,25 +1497,31 @@ export class V2E2EECoordinator {
|
|
|
1467
1497
|
continue;
|
|
1468
1498
|
}
|
|
1469
1499
|
}
|
|
1500
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1470
1501
|
if (ackUpToSeq > 0) {
|
|
1471
1502
|
const actualAckSeq = client._delivery.resolveP2PPullAckSeq(result, ackUpToSeq);
|
|
1472
1503
|
if (actualAckSeq >= ackUpToSeq) {
|
|
1473
1504
|
if (inlineAckGeneration !== null) {
|
|
1474
1505
|
client._delivery.onP2PV2PullAckResult(result, ackUpToSeq, inlineAckGeneration);
|
|
1475
1506
|
}
|
|
1476
|
-
pendingAckSeq
|
|
1507
|
+
if (pendingAckSeq <= ackUpToSeq)
|
|
1508
|
+
pendingAckSeq = 0;
|
|
1477
1509
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
1478
1510
|
this.clearForwardAck(ns, ackUpToSeq);
|
|
1479
1511
|
}
|
|
1480
1512
|
else {
|
|
1481
1513
|
client._clientLog.warn(`message.v2.pull piggyback ACK below target: requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
|
|
1482
|
-
await this.confirmForwardP2PAck(ackUpToSeq);
|
|
1483
|
-
pendingAckSeq = 0;
|
|
1484
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
1485
1514
|
}
|
|
1486
1515
|
}
|
|
1487
1516
|
const pageMessages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
1488
1517
|
const volatileMessages = client._delivery.volatileMessages?.(pageMessages) ?? [];
|
|
1518
|
+
if (pipelinePage && volatileMessages.length > 0) {
|
|
1519
|
+
if (pipelineProducer)
|
|
1520
|
+
await pipelineProducer;
|
|
1521
|
+
pipelineProducer = null;
|
|
1522
|
+
pipelineRunning = false;
|
|
1523
|
+
pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
|
|
1524
|
+
}
|
|
1489
1525
|
if (volatileMessages.length > 0) {
|
|
1490
1526
|
decrypted.push(...await this.publishV2VolatileMessages(volatileMessages, 'pull', true));
|
|
1491
1527
|
volatileCursor = String(client._delivery.volatilePullCursor?.() ?? '').trim();
|
|
@@ -1504,14 +1540,20 @@ export class V2E2EECoordinator {
|
|
|
1504
1540
|
.map((msg) => Number(msg.seq ?? 0))
|
|
1505
1541
|
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
1506
1542
|
const pageContigBefore = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
: null;
|
|
1543
|
+
let pageTrackerSnapshot = null;
|
|
1544
|
+
let trackerMutated = false;
|
|
1510
1545
|
let pageMaxSeq = nextAfterSeq;
|
|
1511
1546
|
if (seqs.length > 0) {
|
|
1512
1547
|
pageMaxSeq = Math.max(...seqs);
|
|
1513
1548
|
}
|
|
1514
1549
|
const pageContiguousSeq = contiguousPullSeq(pageContigBefore, seqs, result.contiguous_seq);
|
|
1550
|
+
if (!pipelineRunning && volatileMessages.length === 0 && shouldContinueForwardPage({
|
|
1551
|
+
rawCount: messages.length, nextAfterSeq, nextAfter: pageMaxSeq, limit,
|
|
1552
|
+
hasMoreField, hasMore, serverHead,
|
|
1553
|
+
observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._pendingP2pPullUpper?.get?.(ns) ?? 0)),
|
|
1554
|
+
pendingAckSeq: 0,
|
|
1555
|
+
}))
|
|
1556
|
+
startPipeline(pageMaxSeq, volatileCursor);
|
|
1515
1557
|
const deferredKeyFetches = new Map();
|
|
1516
1558
|
let blockedSeq = 0;
|
|
1517
1559
|
const pageDecryptedBefore = decrypted.length;
|
|
@@ -1624,7 +1666,17 @@ export class V2E2EECoordinator {
|
|
|
1624
1666
|
let ackSeq = pageContigBefore;
|
|
1625
1667
|
let contigAdvanced = false;
|
|
1626
1668
|
try {
|
|
1669
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1670
|
+
const window = client._seqTracker.getSyncState?.(ns);
|
|
1671
|
+
const deliveryTarget = window && commitTarget >= window.tail - 1
|
|
1672
|
+
? Math.max(commitTarget, window.head) : commitTarget;
|
|
1673
|
+
if (deliveryTarget > pageContigBefore) {
|
|
1674
|
+
await client._drainOrderedMessages(ns, undefined, false, false, 'pull', deliveryTarget);
|
|
1675
|
+
}
|
|
1676
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1677
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns) ?? null;
|
|
1627
1678
|
if (commitTarget > pageContigBefore) {
|
|
1679
|
+
trackerMutated = true;
|
|
1628
1680
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
1629
1681
|
if (retentionFloor > pageContigBefore) {
|
|
1630
1682
|
client._clientLog.info(`message.v2.pull retention-floor advance: ns=${ns} contiguous=${pageContigBefore} -> retention_floor_seq=${retentionFloor}`);
|
|
@@ -1640,19 +1692,21 @@ export class V2E2EECoordinator {
|
|
|
1640
1692
|
contigAdvanced = ackSeq !== pageContigBefore;
|
|
1641
1693
|
if (contigAdvanced) {
|
|
1642
1694
|
await client._commitSeqTrackerState(ns);
|
|
1695
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1643
1696
|
if (pageCount === 1 && deferredServerCursor > 0)
|
|
1644
1697
|
this.clearForwardCursor(ns, ackSeq);
|
|
1645
1698
|
}
|
|
1646
1699
|
}
|
|
1647
1700
|
catch (exc) {
|
|
1648
|
-
if (
|
|
1649
|
-
client.
|
|
1701
|
+
if (trackerMutated && Number(client._v2RuntimeGeneration ?? 0) === generation
|
|
1702
|
+
&& client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false
|
|
1703
|
+
&& typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
1704
|
+
(client._seqTracker._restoreNamespaceSnapshotPreservingPush ?? client._seqTracker.restoreNamespaceSnapshot)
|
|
1705
|
+
.call(client._seqTracker, ns, pageTrackerSnapshot);
|
|
1650
1706
|
}
|
|
1651
1707
|
client._clientLog.error?.(`message.v2.pull Forward commit rolled back: ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
1652
1708
|
throw exc;
|
|
1653
1709
|
}
|
|
1654
|
-
if (contigAdvanced)
|
|
1655
|
-
await client._drainOrderedMessages(ns, undefined, false, false, 'pull');
|
|
1656
1710
|
for (const fetch of deferredKeyFetches.values()) {
|
|
1657
1711
|
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
1658
1712
|
}
|
|
@@ -1670,18 +1724,8 @@ export class V2E2EECoordinator {
|
|
|
1670
1724
|
|| (contigAdvanced && ackSeq > knownServerAckSeq));
|
|
1671
1725
|
if (ackNeeded) {
|
|
1672
1726
|
this.recordForwardAck(ns, ackSeq);
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
if (canContinuePage && !pipelineRunning) {
|
|
1676
|
-
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
1677
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
1678
|
-
client._clientLog.debug(`message.v2.pull queued piggyback auto-ack: ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
|
|
1679
|
-
}
|
|
1680
|
-
else {
|
|
1681
|
-
client._clientLog.debug(`message.v2.pull sending terminal auto-ack: ns=${ns}, ack_seq=${ackSeq}, raw_count=${messages.length}`);
|
|
1682
|
-
await this.confirmForwardP2PAck(ackSeq);
|
|
1683
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
1684
|
-
}
|
|
1727
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
1728
|
+
client._clientLog.debug(`message.v2.pull queued piggyback auto-ack: ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
|
|
1685
1729
|
}
|
|
1686
1730
|
}
|
|
1687
1731
|
if (!ns) {
|
|
@@ -1698,7 +1742,7 @@ export class V2E2EECoordinator {
|
|
|
1698
1742
|
currentSeq: client._seqTracker.getContiguousSeq(ns),
|
|
1699
1743
|
targetSeq: serverHead,
|
|
1700
1744
|
});
|
|
1701
|
-
const nextAfter = Math.max(
|
|
1745
|
+
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
1702
1746
|
const rawCount = messages.length;
|
|
1703
1747
|
if (blockedSeq > 0)
|
|
1704
1748
|
break;
|
|
@@ -1730,11 +1774,7 @@ export class V2E2EECoordinator {
|
|
|
1730
1774
|
&& volatileCursor !== requestedVolatileCursor;
|
|
1731
1775
|
if (!shouldContinuePersistent && !shouldContinueVolatile)
|
|
1732
1776
|
break;
|
|
1733
|
-
if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
1734
|
-
if (pendingAckSeq > 0) {
|
|
1735
|
-
await this.confirmForwardP2PAck(pendingAckSeq);
|
|
1736
|
-
pendingAckSeq = 0;
|
|
1737
|
-
}
|
|
1777
|
+
if (shouldContinueForPipeline && volatileMessages.length === 0 && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
1738
1778
|
startPipeline(nextAfter, volatileCursor);
|
|
1739
1779
|
}
|
|
1740
1780
|
if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
@@ -1744,7 +1784,7 @@ export class V2E2EECoordinator {
|
|
|
1744
1784
|
await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
|
|
1745
1785
|
pullGateKey = nextKey;
|
|
1746
1786
|
}
|
|
1747
|
-
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
1787
|
+
const tailDelayMs = pipelineRunning ? 0 : v2PullTailDelayMs(rawCount, limit);
|
|
1748
1788
|
if (tailDelayMs > 0)
|
|
1749
1789
|
await v2Sleep(tailDelayMs);
|
|
1750
1790
|
nextAfterSeq = nextAfter;
|
|
@@ -1755,11 +1795,14 @@ export class V2E2EECoordinator {
|
|
|
1755
1795
|
}
|
|
1756
1796
|
if (pageCount >= maxPages) {
|
|
1757
1797
|
client._clientLog.warn(`message.v2.pull reached max_pages=${maxPages} after_seq=${nextAfterSeq}`);
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1798
|
+
}
|
|
1799
|
+
if (pendingAckSeq > 0) {
|
|
1800
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1801
|
+
client._clientLog.debug(`message.v2.pull flushing pending auto-ack: ns=${ns}, ack_seq=${pendingAckSeq}`);
|
|
1802
|
+
const ackToFlush = pendingAckSeq;
|
|
1803
|
+
await this.confirmForwardP2PAck(ackToFlush);
|
|
1804
|
+
if (pendingAckSeq <= ackToFlush)
|
|
1761
1805
|
pendingAckSeq = 0;
|
|
1762
|
-
}
|
|
1763
1806
|
}
|
|
1764
1807
|
client._clientLog.debug(`message.v2.pull done: requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${decrypted.length}, ns=${ns || '<none>'}`);
|
|
1765
1808
|
return decrypted;
|
|
@@ -1846,8 +1889,10 @@ export class V2E2EECoordinator {
|
|
|
1846
1889
|
}
|
|
1847
1890
|
async confirmForwardP2PAck(upToSeq) {
|
|
1848
1891
|
const ns = this.client._aid ? `p2p:${this.client._aid}` : '';
|
|
1892
|
+
const generation = Number(this.client._v2RuntimeGeneration ?? 0);
|
|
1849
1893
|
try {
|
|
1850
1894
|
const result = await this.ackV2ForForward(upToSeq);
|
|
1895
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
1851
1896
|
const actualAckSeq = this.client._delivery.resolveForwardAckSeq(result, upToSeq);
|
|
1852
1897
|
if (actualAckSeq < upToSeq) {
|
|
1853
1898
|
throw new E2EEError(`message.v2.ack server ACK watermark ${actualAckSeq} is below requested ${upToSeq}`);
|
|
@@ -1855,7 +1900,9 @@ export class V2E2EECoordinator {
|
|
|
1855
1900
|
this.clearForwardAck(ns, upToSeq);
|
|
1856
1901
|
}
|
|
1857
1902
|
catch (exc) {
|
|
1858
|
-
this.
|
|
1903
|
+
if (Number(this.client._v2RuntimeGeneration ?? 0) === generation
|
|
1904
|
+
&& this.client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false)
|
|
1905
|
+
this.recordForwardAck(ns, upToSeq);
|
|
1859
1906
|
throw exc;
|
|
1860
1907
|
}
|
|
1861
1908
|
}
|
|
@@ -2128,6 +2175,7 @@ export class V2E2EECoordinator {
|
|
|
2128
2175
|
}
|
|
2129
2176
|
async pullGroupV2TailInternal(params, deferPublish = false) {
|
|
2130
2177
|
const client = this.client;
|
|
2178
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
2131
2179
|
if (params.window_mode !== 'tail')
|
|
2132
2180
|
throw new ValidationError('window_mode must equal tail');
|
|
2133
2181
|
if ('ack_up_to_seq' in params || params.force === true)
|
|
@@ -2167,6 +2215,15 @@ export class V2E2EECoordinator {
|
|
|
2167
2215
|
if (resultAid)
|
|
2168
2216
|
groupAid = resultAid;
|
|
2169
2217
|
const page = validateTailPage(result, afterSeq, limit);
|
|
2218
|
+
const decoded = [];
|
|
2219
|
+
for (const msg of page.messages) {
|
|
2220
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2221
|
+
client._logMessageDebug('tail-raw', 'group.v2.pull', 'group.message_created', msg);
|
|
2222
|
+
const plaintext = await this.decodeGroupWindowMessage(msg, groupId, groupAid, true, false, 'tail', deferPublish);
|
|
2223
|
+
if (plaintext)
|
|
2224
|
+
decoded.push(plaintext);
|
|
2225
|
+
}
|
|
2226
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2170
2227
|
client._seqTracker.commitTailWindow(ns, {
|
|
2171
2228
|
windowStartSeq: page.windowStart,
|
|
2172
2229
|
headSeq: page.head,
|
|
@@ -2175,13 +2232,7 @@ export class V2E2EECoordinator {
|
|
|
2175
2232
|
});
|
|
2176
2233
|
client._persistSeq(ns);
|
|
2177
2234
|
await client._saveSeqTrackerState?.();
|
|
2178
|
-
|
|
2179
|
-
for (const msg of page.messages) {
|
|
2180
|
-
client._logMessageDebug('tail-raw', 'group.v2.pull', 'group.message_created', msg);
|
|
2181
|
-
const plaintext = await this.decodeGroupWindowMessage(msg, groupId, groupAid, true, false, 'tail', deferPublish);
|
|
2182
|
-
if (plaintext)
|
|
2183
|
-
decoded.push(plaintext);
|
|
2184
|
-
}
|
|
2235
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2185
2236
|
const ack = client._seqTracker.getContiguousSeq(ns);
|
|
2186
2237
|
if (ownsCursor)
|
|
2187
2238
|
client._delivery.syncProgress(ns, {
|
|
@@ -2229,6 +2280,7 @@ export class V2E2EECoordinator {
|
|
|
2229
2280
|
}
|
|
2230
2281
|
async pullGroupV2Impl(groupId, afterSeq = 0, limit = 50, opts) {
|
|
2231
2282
|
const client = this.client;
|
|
2283
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
2232
2284
|
await client._ensureV2SessionReady('group.pull');
|
|
2233
2285
|
const gid = String(groupId ?? '').trim();
|
|
2234
2286
|
if (!gid)
|
|
@@ -2303,21 +2355,26 @@ export class V2E2EECoordinator {
|
|
|
2303
2355
|
for (let produced = pageCount; produced < maxPages; produced += 1) {
|
|
2304
2356
|
if (pipelineStopped)
|
|
2305
2357
|
break;
|
|
2358
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2359
|
+
const ackUpToSeq = pendingAckSeq;
|
|
2360
|
+
const inlineAckGeneration = ackUpToSeq > 0 ? client._delivery.captureGroupInlineAckGeneration() : null;
|
|
2306
2361
|
const result = await client._callRawV2Rpc('group.v2.pull', withExplicitGroupAid({
|
|
2307
2362
|
group_id: wireGroupId,
|
|
2308
2363
|
after_seq: producerAfterSeq,
|
|
2309
2364
|
limit,
|
|
2310
2365
|
...cursorParams,
|
|
2311
2366
|
...(opts?.force ? { force: true } : {}),
|
|
2367
|
+
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
2312
2368
|
}, groupAid), pullGateKey);
|
|
2313
2369
|
if (pipelineStopped)
|
|
2314
2370
|
break;
|
|
2371
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2315
2372
|
const pageMessages = (Array.isArray(result.messages) ? result.messages : []);
|
|
2316
2373
|
const enqueued = await pipelineQueue.put({
|
|
2317
2374
|
result,
|
|
2318
2375
|
afterSeq: producerAfterSeq,
|
|
2319
|
-
ackUpToSeq
|
|
2320
|
-
inlineAckGeneration
|
|
2376
|
+
ackUpToSeq,
|
|
2377
|
+
inlineAckGeneration,
|
|
2321
2378
|
requestedVolatileCursor: '',
|
|
2322
2379
|
weight: pageMessages.length,
|
|
2323
2380
|
}, pageMessages.length);
|
|
@@ -2327,7 +2384,7 @@ export class V2E2EECoordinator {
|
|
|
2327
2384
|
const seqs = messages
|
|
2328
2385
|
.map((msg) => Number(msg.seq ?? 0))
|
|
2329
2386
|
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
2330
|
-
const nextAfter = Math.max(
|
|
2387
|
+
const nextAfter = Math.max(producerAfterSeq, ...seqs);
|
|
2331
2388
|
const cursor = isJsonObject(result.cursor)
|
|
2332
2389
|
? result.cursor : null;
|
|
2333
2390
|
const shouldContinue = shouldContinueForwardPage({
|
|
@@ -2375,7 +2432,7 @@ export class V2E2EECoordinator {
|
|
|
2375
2432
|
}
|
|
2376
2433
|
pageCount += 1;
|
|
2377
2434
|
const ackUpToSeq = ownsCursor ? (pipelinePage?.ackUpToSeq ?? pendingAckSeq) : 0;
|
|
2378
|
-
const inlineAckGeneration = pipelinePage
|
|
2435
|
+
const inlineAckGeneration = pipelinePage ? pipelinePage.inlineAckGeneration : (ackUpToSeq > 0
|
|
2379
2436
|
? client._delivery.captureGroupInlineAckGeneration()
|
|
2380
2437
|
: null);
|
|
2381
2438
|
client._clientLog.debug(`group.v2.pull page request: group=${gid}, page=${pageCount}, after_seq=${nextAfterSeq}, limit=${limit}, ack_up_to_seq=${ackUpToSeq}, ns=${ns}`);
|
|
@@ -2394,21 +2451,21 @@ export class V2E2EECoordinator {
|
|
|
2394
2451
|
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
2395
2452
|
}, groupAid), pullGateKey);
|
|
2396
2453
|
}
|
|
2454
|
+
if (ownsCursor)
|
|
2455
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2397
2456
|
if (ackUpToSeq > 0) {
|
|
2398
2457
|
const actualAckSeq = client._delivery.resolveGroupPullAckSeq(result, ackUpToSeq);
|
|
2399
2458
|
if (actualAckSeq >= ackUpToSeq) {
|
|
2400
2459
|
if (inlineAckGeneration !== null) {
|
|
2401
2460
|
client._delivery.onGroupV2PullAckResult(gid, result, ackUpToSeq, inlineAckGeneration);
|
|
2402
2461
|
}
|
|
2403
|
-
pendingAckSeq
|
|
2462
|
+
if (pendingAckSeq <= ackUpToSeq)
|
|
2463
|
+
pendingAckSeq = 0;
|
|
2404
2464
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
2405
2465
|
this.clearForwardAck(ns, ackUpToSeq);
|
|
2406
2466
|
}
|
|
2407
2467
|
else {
|
|
2408
2468
|
client._clientLog.warn(`group.v2.pull piggyback ACK below target: group=${gid}, requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
|
|
2409
|
-
await this.confirmForwardGroupAck(gid, ackUpToSeq, groupAid);
|
|
2410
|
-
pendingAckSeq = 0;
|
|
2411
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
2412
2469
|
}
|
|
2413
2470
|
}
|
|
2414
2471
|
const resultGroupAid = String(result.group_aid ?? result.groupAid ?? '').trim();
|
|
@@ -2429,14 +2486,20 @@ export class V2E2EECoordinator {
|
|
|
2429
2486
|
.map((msg) => Number(msg.seq ?? 0))
|
|
2430
2487
|
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
2431
2488
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
: null;
|
|
2489
|
+
let pageTrackerSnapshot = null;
|
|
2490
|
+
let trackerMutated = false;
|
|
2435
2491
|
let pageMaxSeq = nextAfterSeq;
|
|
2436
2492
|
if (seqs.length > 0) {
|
|
2437
2493
|
pageMaxSeq = Math.max(...seqs);
|
|
2438
2494
|
}
|
|
2439
2495
|
const pageContiguousSeq = contiguousPullSeq(pageContigBefore, seqs, result.contiguous_seq);
|
|
2496
|
+
if (!pipelineRunning && ownsCursor && shouldContinueForwardPage({
|
|
2497
|
+
rawCount: messages.length, nextAfterSeq, nextAfter: pageMaxSeq, limit,
|
|
2498
|
+
hasMoreField, hasMore, serverHead,
|
|
2499
|
+
observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._delivery.pendingPullUpper?.(ns) ?? 0)),
|
|
2500
|
+
pendingAckSeq: 0,
|
|
2501
|
+
}))
|
|
2502
|
+
startPipeline(pageMaxSeq);
|
|
2440
2503
|
const deferredKeyFetches = new Map();
|
|
2441
2504
|
let blockedSeq = 0;
|
|
2442
2505
|
const pageDecryptedBefore = decrypted.length;
|
|
@@ -2582,7 +2645,17 @@ export class V2E2EECoordinator {
|
|
|
2582
2645
|
let contigAdvanced = false;
|
|
2583
2646
|
if (ownsCursor) {
|
|
2584
2647
|
try {
|
|
2648
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2649
|
+
const window = client._seqTracker.getSyncState?.(ns);
|
|
2650
|
+
const deliveryTarget = window && commitTarget >= window.tail - 1
|
|
2651
|
+
? Math.max(commitTarget, window.head) : commitTarget;
|
|
2652
|
+
if (deliveryTarget > pageContigBefore) {
|
|
2653
|
+
await client._drainOrderedMessages(ns, undefined, false, false, 'pull', deliveryTarget);
|
|
2654
|
+
}
|
|
2655
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2656
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns) ?? null;
|
|
2585
2657
|
if (commitTarget > pageContigBefore) {
|
|
2658
|
+
trackerMutated = true;
|
|
2586
2659
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
2587
2660
|
if (effectiveFloor > pageContigBefore) {
|
|
2588
2661
|
client._clientLog.info(`group.v2.pull floor advance: ns=${ns} contiguous=${pageContigBefore} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} visibility_floor=${visibilityFloor}`);
|
|
@@ -2598,19 +2671,21 @@ export class V2E2EECoordinator {
|
|
|
2598
2671
|
contigAdvanced = ackSeq !== pageContigBefore;
|
|
2599
2672
|
if (contigAdvanced) {
|
|
2600
2673
|
await client._commitSeqTrackerState(ns);
|
|
2674
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2601
2675
|
if (pageCount === 1 && deferredServerCursor > 0)
|
|
2602
2676
|
this.clearForwardCursor(ns, ackSeq);
|
|
2603
2677
|
}
|
|
2604
2678
|
}
|
|
2605
2679
|
catch (exc) {
|
|
2606
|
-
if (
|
|
2607
|
-
client.
|
|
2680
|
+
if (trackerMutated && Number(client._v2RuntimeGeneration ?? 0) === generation
|
|
2681
|
+
&& client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false
|
|
2682
|
+
&& typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
2683
|
+
(client._seqTracker._restoreNamespaceSnapshotPreservingPush ?? client._seqTracker.restoreNamespaceSnapshot)
|
|
2684
|
+
.call(client._seqTracker, ns, pageTrackerSnapshot);
|
|
2608
2685
|
}
|
|
2609
2686
|
client._clientLog.error?.(`group.v2.pull Forward commit rolled back: group=${gid} ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
2610
2687
|
throw exc;
|
|
2611
2688
|
}
|
|
2612
|
-
if (contigAdvanced)
|
|
2613
|
-
await client._drainOrderedMessages(ns, undefined, false, false, 'pull');
|
|
2614
2689
|
}
|
|
2615
2690
|
for (const fetch of deferredKeyFetches.values()) {
|
|
2616
2691
|
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
@@ -2630,18 +2705,8 @@ export class V2E2EECoordinator {
|
|
|
2630
2705
|
|| (contigAdvanced && ackSeq > knownServerCursorSeq));
|
|
2631
2706
|
if (ackNeeded) {
|
|
2632
2707
|
this.recordForwardAck(ns, ackSeq);
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
if (canContinuePage && !pipelineRunning) {
|
|
2636
|
-
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
2637
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
2638
|
-
client._clientLog.debug(`group.v2.pull queued piggyback auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
|
|
2639
|
-
}
|
|
2640
|
-
else {
|
|
2641
|
-
client._clientLog.debug(`group.v2.pull sending terminal auto-ack: group=${gid}, ns=${ns}, ack_seq=${ackSeq}, raw_count=${messages.length}`);
|
|
2642
|
-
await this.confirmForwardGroupAck(gid, ackSeq, groupAid);
|
|
2643
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
2644
|
-
}
|
|
2708
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
2709
|
+
client._clientLog.debug(`group.v2.pull queued piggyback auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
|
|
2645
2710
|
}
|
|
2646
2711
|
client._delivery.syncProgress(ns, {
|
|
2647
2712
|
rawCount: messages.length,
|
|
@@ -2651,7 +2716,7 @@ export class V2E2EECoordinator {
|
|
|
2651
2716
|
currentSeq: client._seqTracker.getContiguousSeq(ns),
|
|
2652
2717
|
targetSeq: serverHead,
|
|
2653
2718
|
});
|
|
2654
|
-
const nextAfter = Math.max(
|
|
2719
|
+
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
2655
2720
|
if (!ownsCursor)
|
|
2656
2721
|
break;
|
|
2657
2722
|
const rawCount = messages.length;
|
|
@@ -2682,10 +2747,6 @@ export class V2E2EECoordinator {
|
|
|
2682
2747
|
if (!shouldContinue)
|
|
2683
2748
|
break;
|
|
2684
2749
|
if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
2685
|
-
if (pendingAckSeq > 0) {
|
|
2686
|
-
await this.confirmForwardGroupAck(gid, pendingAckSeq, groupAid);
|
|
2687
|
-
pendingAckSeq = 0;
|
|
2688
|
-
}
|
|
2689
2750
|
startPipeline(nextAfter);
|
|
2690
2751
|
}
|
|
2691
2752
|
if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
@@ -2696,7 +2757,7 @@ export class V2E2EECoordinator {
|
|
|
2696
2757
|
await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
|
|
2697
2758
|
pullGateKey = nextKey;
|
|
2698
2759
|
}
|
|
2699
|
-
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
2760
|
+
const tailDelayMs = pipelineRunning ? 0 : v2PullTailDelayMs(rawCount, limit);
|
|
2700
2761
|
if (tailDelayMs > 0)
|
|
2701
2762
|
await v2Sleep(tailDelayMs);
|
|
2702
2763
|
nextAfterSeq = nextAfter;
|
|
@@ -2707,11 +2768,14 @@ export class V2E2EECoordinator {
|
|
|
2707
2768
|
}
|
|
2708
2769
|
if (pageCount >= maxPages) {
|
|
2709
2770
|
client._clientLog.warn(`group.v2.pull reached max_pages=${maxPages} group=${gid} after_seq=${nextAfterSeq}`);
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2771
|
+
}
|
|
2772
|
+
if (pendingAckSeq > 0) {
|
|
2773
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2774
|
+
client._clientLog.debug(`group.v2.pull flushing pending auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}`);
|
|
2775
|
+
const ackToFlush = pendingAckSeq;
|
|
2776
|
+
await this.confirmForwardGroupAck(gid, ackToFlush, groupAid);
|
|
2777
|
+
if (pendingAckSeq <= ackToFlush)
|
|
2713
2778
|
pendingAckSeq = 0;
|
|
2714
|
-
}
|
|
2715
2779
|
}
|
|
2716
2780
|
client._clientLog.debug(`group.v2.pull done: group=${gid}, requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${decrypted.length}, ns=${ns}`);
|
|
2717
2781
|
return decrypted;
|
|
@@ -2732,8 +2796,10 @@ export class V2E2EECoordinator {
|
|
|
2732
2796
|
}
|
|
2733
2797
|
async confirmForwardGroupAck(groupId, upToSeq, groupAid) {
|
|
2734
2798
|
const ns = `group:${String(groupId ?? '').trim()}`;
|
|
2799
|
+
const generation = Number(this.client._v2RuntimeGeneration ?? 0);
|
|
2735
2800
|
try {
|
|
2736
2801
|
const result = await this.ackGroupV2ForForward(groupId, upToSeq, groupAid);
|
|
2802
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
2737
2803
|
const actualAckSeq = this.client._delivery.resolveForwardAckSeq(result, upToSeq);
|
|
2738
2804
|
if (actualAckSeq < upToSeq) {
|
|
2739
2805
|
throw new E2EEError(`group.v2.ack server ACK watermark ${actualAckSeq} is below requested ${upToSeq}`);
|
|
@@ -2741,7 +2807,9 @@ export class V2E2EECoordinator {
|
|
|
2741
2807
|
this.clearForwardAck(ns, upToSeq);
|
|
2742
2808
|
}
|
|
2743
2809
|
catch (exc) {
|
|
2744
|
-
this.
|
|
2810
|
+
if (Number(this.client._v2RuntimeGeneration ?? 0) === generation
|
|
2811
|
+
&& this.client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false)
|
|
2812
|
+
this.recordForwardAck(ns, upToSeq);
|
|
2745
2813
|
throw exc;
|
|
2746
2814
|
}
|
|
2747
2815
|
}
|
|
@@ -3241,6 +3309,8 @@ export class V2E2EECoordinator {
|
|
|
3241
3309
|
encrypted: true,
|
|
3242
3310
|
e2ee,
|
|
3243
3311
|
};
|
|
3312
|
+
if (typeof plaintext.type === 'string' && plaintext.type.startsWith('aun.ownership.'))
|
|
3313
|
+
result.from_aid = fromAid;
|
|
3244
3314
|
const senderDeviceId = String(aad.from_device ?? '').trim();
|
|
3245
3315
|
if (senderDeviceId)
|
|
3246
3316
|
result.sender_device_id = senderDeviceId;
|