@agentunion/fastaun-browser 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 +17 -3
- package/dist/agent-md-schema.d.ts.map +1 -1
- package/dist/agent-md-schema.js +94 -26
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +54 -3
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +711 -65
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +58 -46
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +254 -248
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts +4 -1
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +27 -33
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts +4 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +426 -39
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +38179 -32054
- package/dist/cert-utils.d.ts +50 -2
- package/dist/cert-utils.d.ts.map +1 -1
- package/dist/cert-utils.js +105 -47
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +22 -1
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +389 -78
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +103 -77
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -0
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +182 -89
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +100 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1220 -25
- package/dist/client.js.map +1 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +8 -6
- package/dist/crypto.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -2
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +9 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +72 -13
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +82 -2
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +100 -6
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +17 -0
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +10 -1
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +111 -0
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +7 -1
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +30 -1
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +98 -0
- package/dist/org-protocol.d.ts.map +1 -0
- package/dist/org-protocol.js +1802 -0
- package/dist/org-protocol.js.map +1 -0
- package/dist/ownership-proof.d.ts +93 -5
- package/dist/ownership-proof.d.ts.map +1 -1
- package/dist/ownership-proof.js +239 -53
- package/dist/ownership-proof.js.map +1 -1
- package/dist/ownership-v21.d.ts +194 -0
- package/dist/ownership-v21.d.ts.map +1 -0
- package/dist/ownership-v21.js +678 -0
- package/dist/ownership-v21.js.map +1 -0
- package/dist/ownership-workflow.d.ts +151 -0
- package/dist/ownership-workflow.d.ts.map +1 -0
- package/dist/ownership-workflow.js +1229 -0
- package/dist/ownership-workflow.js.map +1 -0
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +10 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +83 -0
- package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
- package/dist/tools/cross-sdk-agent.js +415 -7
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +7 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +90 -2
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/decrypt.d.ts +1 -0
- package/dist/v2/e2ee/decrypt.d.ts.map +1 -1
- 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,12 +113,10 @@ function deduplicateForwardPageMessages(value, afterSeq) {
|
|
|
113
113
|
}
|
|
114
114
|
return [...bySeq.entries()].sort(([a], [b]) => a - b).map(([, row]) => row);
|
|
115
115
|
}
|
|
116
|
-
function contiguousPullSeq(afterSeq, seqs,
|
|
116
|
+
function contiguousPullSeq(afterSeq, seqs, _proof) {
|
|
117
117
|
// Forward 原始页中的缺号视为服务端永久空号;页完成后按页内最大
|
|
118
118
|
// seq 推进 A,避免单页空洞阻塞后续 pull/ACK。
|
|
119
|
-
|
|
120
|
-
const serverProof = safeNonNegativeSeq(proof);
|
|
121
|
-
return serverProof !== null ? Math.max(contiguous, serverProof) : contiguous;
|
|
119
|
+
return seqs.length > 0 ? Math.max(afterSeq, ...seqs) : afterSeq;
|
|
122
120
|
}
|
|
123
121
|
function deduplicatePulledResults(messages) {
|
|
124
122
|
const seen = new Set();
|
|
@@ -138,7 +136,7 @@ function shouldContinueForwardPage({ rawCount, nextAfterSeq, nextAfter, limit, h
|
|
|
138
136
|
// 无新 seq 或游标未前进时必须停止,避免错误元数据造成循环。
|
|
139
137
|
if (rawCount <= 0 || nextAfter <= nextAfterSeq)
|
|
140
138
|
return false;
|
|
141
|
-
if (hasMore || serverHead > nextAfter ||
|
|
139
|
+
if (hasMore || serverHead > nextAfter || observedUpper > nextAfter)
|
|
142
140
|
return true;
|
|
143
141
|
// 仅旧服务缺失 has_more 时,以满页保留兼容续拉。
|
|
144
142
|
return !hasMoreField && limit > 0 && rawCount >= limit;
|
|
@@ -1000,11 +998,12 @@ export class V2E2EECoordinator {
|
|
|
1000
998
|
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
1001
999
|
continue;
|
|
1002
1000
|
}
|
|
1003
|
-
client._v2SenderIKPending.delete(key);
|
|
1004
1001
|
const seq = Number(entry.msg.seq ?? 0);
|
|
1005
1002
|
if (entry.groupId) {
|
|
1006
|
-
if (client._delivery.isSelfSentGroupMessage?.(entry.msg))
|
|
1003
|
+
if (client._delivery.isSelfSentGroupMessage?.(entry.msg)) {
|
|
1004
|
+
client._v2SenderIKPending.delete(key);
|
|
1007
1005
|
continue;
|
|
1006
|
+
}
|
|
1008
1007
|
plaintext = normalizeGroupMentionMode(plaintext, entry.msg, entry.msg.envelope_json);
|
|
1009
1008
|
plaintext.group_id = entry.groupId;
|
|
1010
1009
|
await client._publishPulledMessage('group.message_created', `group:${entry.groupId}`, seq, plaintext, false, 'pending_retry');
|
|
@@ -1012,6 +1011,7 @@ export class V2E2EECoordinator {
|
|
|
1012
1011
|
else {
|
|
1013
1012
|
await client._publishPulledMessage('message.received', `p2p:${client._aid ?? ''}`, seq, plaintext, false, 'pending_retry');
|
|
1014
1013
|
}
|
|
1014
|
+
client._v2SenderIKPending.delete(key);
|
|
1015
1015
|
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
1016
1016
|
}
|
|
1017
1017
|
}
|
|
@@ -1110,6 +1110,12 @@ export class V2E2EECoordinator {
|
|
|
1110
1110
|
history_error: { code: 'decrypt_failed', message: reason },
|
|
1111
1111
|
};
|
|
1112
1112
|
}
|
|
1113
|
+
ensureDeliveryCurrent(ns, generation, inlineGeneration) {
|
|
1114
|
+
if (Number(this.client._v2RuntimeGeneration ?? 0) !== generation
|
|
1115
|
+
|| (inlineGeneration !== undefined && !this.client._delivery.isInlineGenerationCurrent(inlineGeneration))
|
|
1116
|
+
|| this.client._rpcPipeline?.isPullNamespaceCurrent?.(ns) === false)
|
|
1117
|
+
throw new StateError('pull invalidated');
|
|
1118
|
+
}
|
|
1113
1119
|
async decodeP2PWindowMessage(msg, publish, history, source, orderedPublish = false) {
|
|
1114
1120
|
const client = this.client;
|
|
1115
1121
|
const seq = strictWindowSeq(msg.seq, 'messages[].seq', true);
|
|
@@ -1257,6 +1263,8 @@ export class V2E2EECoordinator {
|
|
|
1257
1263
|
}
|
|
1258
1264
|
async pullV2TailInternal(params, deferPublish = false) {
|
|
1259
1265
|
const client = this.client;
|
|
1266
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
1267
|
+
const inlineGeneration = client._delivery.captureInlineGeneration?.();
|
|
1260
1268
|
if (params.window_mode !== 'tail')
|
|
1261
1269
|
throw new ValidationError('window_mode must equal tail');
|
|
1262
1270
|
if ('ack_up_to_seq' in params || params.force === true)
|
|
@@ -1271,19 +1279,27 @@ export class V2E2EECoordinator {
|
|
|
1271
1279
|
}, `${ns}|tail|after=${afterSeq}|limit=${limit}`);
|
|
1272
1280
|
const result = await client._callRawV2Rpc('message.v2.pull', { window_mode: 'tail', after_seq: afterSeq, limit, _rpc_foreground: true }, pullGateKey);
|
|
1273
1281
|
const page = validateTailPage(result, afterSeq, limit);
|
|
1274
|
-
if (ns) {
|
|
1275
|
-
client._seqTracker.commitTailWindow(ns, {
|
|
1276
|
-
windowStartSeq: page.windowStart, headSeq: page.head, coveredThroughSeq: page.head, pageLimit: limit,
|
|
1277
|
-
});
|
|
1278
|
-
client._persistSeq(ns);
|
|
1279
|
-
await client._saveSeqTrackerState?.();
|
|
1280
|
-
}
|
|
1281
1282
|
const decoded = [];
|
|
1282
1283
|
for (const msg of page.messages) {
|
|
1284
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1283
1285
|
const plaintext = await this.decodeP2PWindowMessage(msg, true, false, 'tail', deferPublish);
|
|
1284
1286
|
if (plaintext)
|
|
1285
1287
|
decoded.push(plaintext);
|
|
1286
1288
|
}
|
|
1289
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1290
|
+
if (ns) {
|
|
1291
|
+
const proof = {
|
|
1292
|
+
windowStartSeq: page.windowStart, headSeq: page.head, coveredThroughSeq: page.head, pageLimit: limit,
|
|
1293
|
+
};
|
|
1294
|
+
if (deferPublish)
|
|
1295
|
+
client._delivery.deferTailWindow(ns, proof);
|
|
1296
|
+
else {
|
|
1297
|
+
client._seqTracker.commitTailWindow(ns, proof);
|
|
1298
|
+
client._persistSeq(ns);
|
|
1299
|
+
await client._saveSeqTrackerState?.();
|
|
1300
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1287
1303
|
const ack = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
1288
1304
|
if (ns)
|
|
1289
1305
|
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);
|
|
@@ -1320,6 +1336,8 @@ export class V2E2EECoordinator {
|
|
|
1320
1336
|
}
|
|
1321
1337
|
async pullV2Impl(afterSeq = 0, limit = 50, opts) {
|
|
1322
1338
|
const client = this.client;
|
|
1339
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
1340
|
+
const inlineGeneration = client._delivery.captureInlineGeneration?.();
|
|
1323
1341
|
await client._ensureV2SessionReady('message.pull');
|
|
1324
1342
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1325
1343
|
let volatileCursor = String(opts?.afterVolatileCursor ?? client._delivery.volatilePullCursor() ?? '').trim();
|
|
@@ -1359,10 +1377,11 @@ export class V2E2EECoordinator {
|
|
|
1359
1377
|
let pageCount = 0;
|
|
1360
1378
|
let lastAutoAckSeq = 0;
|
|
1361
1379
|
let pendingAckSeq = deferredForwardAck;
|
|
1380
|
+
let committedAckSeq = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
1362
1381
|
const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
|
|
1363
1382
|
let pipelineRunning = false;
|
|
1364
1383
|
let pipelineError = null;
|
|
1365
|
-
|
|
1384
|
+
let pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
|
|
1366
1385
|
let pipelineProducer = null;
|
|
1367
1386
|
let pipelineStopped = false;
|
|
1368
1387
|
const startPipeline = (startAfterSeq, startVolatileCursor) => {
|
|
@@ -1375,21 +1394,27 @@ export class V2E2EECoordinator {
|
|
|
1375
1394
|
for (let produced = pageCount; produced < maxPages; produced += 1) {
|
|
1376
1395
|
if (pipelineStopped)
|
|
1377
1396
|
break;
|
|
1397
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1378
1398
|
const requestedVolatileCursor = producerVolatileCursor;
|
|
1399
|
+
const ackUpToSeq = Math.min(pendingAckSeq, committedAckSeq);
|
|
1400
|
+
const inlineAckGeneration = ackUpToSeq > 0
|
|
1401
|
+
? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null : null;
|
|
1379
1402
|
const result = await client._callRawV2Rpc('message.v2.pull', {
|
|
1380
1403
|
after_seq: producerAfterSeq,
|
|
1381
1404
|
limit,
|
|
1382
1405
|
...(producerVolatileCursor ? { after_volatile_cursor: producerVolatileCursor } : {}),
|
|
1383
1406
|
...(opts?.force ? { force: true } : {}),
|
|
1407
|
+
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
1384
1408
|
}, pullGateKey);
|
|
1385
1409
|
if (pipelineStopped)
|
|
1386
1410
|
break;
|
|
1411
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1387
1412
|
const rawPageMessages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
1388
1413
|
const enqueued = await pipelineQueue.put({
|
|
1389
1414
|
result,
|
|
1390
1415
|
afterSeq: producerAfterSeq,
|
|
1391
|
-
ackUpToSeq
|
|
1392
|
-
inlineAckGeneration
|
|
1416
|
+
ackUpToSeq,
|
|
1417
|
+
inlineAckGeneration,
|
|
1393
1418
|
requestedVolatileCursor,
|
|
1394
1419
|
weight: rawPageMessages.length,
|
|
1395
1420
|
}, rawPageMessages.length);
|
|
@@ -1480,21 +1505,24 @@ export class V2E2EECoordinator {
|
|
|
1480
1505
|
continue;
|
|
1481
1506
|
}
|
|
1482
1507
|
}
|
|
1508
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1509
|
+
if (!opts?.force)
|
|
1510
|
+
client._delivery.observeBackgroundPullResponse?.(ns, nextAfterSeq, result, limit);
|
|
1483
1511
|
if (ackUpToSeq > 0) {
|
|
1484
1512
|
const actualAckSeq = client._delivery?.resolveP2PPullAckSeq?.(result, ackUpToSeq) ?? 0;
|
|
1485
1513
|
if (actualAckSeq >= ackUpToSeq) {
|
|
1486
1514
|
if (pendingInlineGeneration !== null) {
|
|
1487
1515
|
client._delivery?.completePendingP2PInlineAckUpTo?.(ns, actualAckSeq, pendingInlineGeneration);
|
|
1488
1516
|
}
|
|
1489
|
-
pendingAckSeq
|
|
1517
|
+
if (pendingAckSeq <= ackUpToSeq)
|
|
1518
|
+
pendingAckSeq = 0;
|
|
1490
1519
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
1491
1520
|
this.clearForwardAck(ns, ackUpToSeq);
|
|
1492
1521
|
}
|
|
1493
1522
|
else {
|
|
1494
1523
|
client._clientLog.warn(`message.v2.pull piggyback ACK below target: requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
1524
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackUpToSeq);
|
|
1525
|
+
this.recordForwardAck(ns, ackUpToSeq);
|
|
1498
1526
|
}
|
|
1499
1527
|
}
|
|
1500
1528
|
const pageMessages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
@@ -1514,11 +1542,16 @@ export class V2E2EECoordinator {
|
|
|
1514
1542
|
.map((msg) => Number(msg.seq ?? 0))
|
|
1515
1543
|
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
1516
1544
|
const pageContigBefore = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
1517
|
-
|
|
1518
|
-
? client._seqTracker.snapshotNamespace(ns)
|
|
1519
|
-
: null;
|
|
1545
|
+
let pageTrackerSnapshot;
|
|
1520
1546
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
1521
1547
|
const pageContiguousSeq = contiguousPullSeq(pageContigBefore, seqs, result.contiguous_seq);
|
|
1548
|
+
if (!pipelineRunning && volatileMessages.length === 0 && shouldContinueForwardPage({
|
|
1549
|
+
rawCount: messages.length, nextAfterSeq, nextAfter: pageMaxSeq, limit,
|
|
1550
|
+
hasMoreField, hasMore, serverHead, pendingAckSeq: 0,
|
|
1551
|
+
observedUpper: typeof client._seqTracker.getMaxSeenSeq === 'function'
|
|
1552
|
+
? client._seqTracker.getMaxSeenSeq(ns) : pageMaxSeq,
|
|
1553
|
+
}))
|
|
1554
|
+
startPipeline(pageMaxSeq, volatileCursor);
|
|
1522
1555
|
const deferredKeyFetches = new Map();
|
|
1523
1556
|
let blockedSeq = 0;
|
|
1524
1557
|
const pageDecryptedBefore = decrypted.length;
|
|
@@ -1622,10 +1655,18 @@ export class V2E2EECoordinator {
|
|
|
1622
1655
|
const serverAckSeq = parsedServerAckSeq ?? 0;
|
|
1623
1656
|
const retentionFloor = Math.max(0, Number(result.retention_floor_seq ?? 0));
|
|
1624
1657
|
if (ns) {
|
|
1625
|
-
|
|
1658
|
+
let commitTarget = Math.max(pageContigBefore, Number.isFinite(retentionFloor) ? retentionFloor : 0, pageCount === 1 ? deferredServerCursor : 0, serverAckSeq, blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq);
|
|
1659
|
+
commitTarget = client._delivery.pendingTailDeliveryThrough?.(ns, commitTarget) ?? commitTarget;
|
|
1626
1660
|
let ackSeq = pageContigBefore;
|
|
1627
1661
|
let contigAdvanced = false;
|
|
1628
1662
|
try {
|
|
1663
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1664
|
+
const state = client._seqTracker.getSyncState?.(ns);
|
|
1665
|
+
const deliveryThrough = client._delivery.pendingTailDeliveryThrough?.(ns, state && commitTarget >= state.tail - 1 ? Math.max(commitTarget, state.head) : commitTarget) ?? (state && commitTarget >= state.tail - 1 ? Math.max(commitTarget, state.head) : commitTarget);
|
|
1666
|
+
await client._drainOrderedMessages(ns, undefined, false, false, deliveryThrough);
|
|
1667
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1668
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns);
|
|
1669
|
+
client._delivery.commitPendingTailWindow?.(ns, commitTarget);
|
|
1629
1670
|
if (commitTarget > pageContigBefore) {
|
|
1630
1671
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
1631
1672
|
if (retentionFloor > pageContigBefore) {
|
|
@@ -1642,19 +1683,24 @@ export class V2E2EECoordinator {
|
|
|
1642
1683
|
contigAdvanced = ackSeq !== pageContigBefore;
|
|
1643
1684
|
if (contigAdvanced) {
|
|
1644
1685
|
await client._commitSeqTrackerState(ns);
|
|
1686
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1645
1687
|
if (pageCount === 1 && deferredServerCursor > 0)
|
|
1646
1688
|
this.clearForwardCursor(ns, ackSeq);
|
|
1647
1689
|
}
|
|
1690
|
+
client._delivery.completePendingTailWindow?.(ns);
|
|
1691
|
+
committedAckSeq = ackSeq;
|
|
1648
1692
|
}
|
|
1649
1693
|
catch (exc) {
|
|
1650
|
-
if (
|
|
1651
|
-
client.
|
|
1694
|
+
if (Number(client._v2RuntimeGeneration ?? 0) === generation
|
|
1695
|
+
&& (inlineGeneration === undefined || client._delivery.isInlineGenerationCurrent(inlineGeneration))
|
|
1696
|
+
&& client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false
|
|
1697
|
+
&& pageTrackerSnapshot !== undefined
|
|
1698
|
+
&& typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
1699
|
+
client._seqTracker._restoreNamespaceSnapshotPreservingPush(ns, pageTrackerSnapshot);
|
|
1652
1700
|
}
|
|
1653
1701
|
client._clientLog.error?.(`message.v2.pull Forward commit rolled back: ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
1654
1702
|
throw exc;
|
|
1655
1703
|
}
|
|
1656
|
-
if (contigAdvanced)
|
|
1657
|
-
await client._drainOrderedMessages(ns, undefined, false, false);
|
|
1658
1704
|
for (const fetch of deferredKeyFetches.values()) {
|
|
1659
1705
|
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
1660
1706
|
}
|
|
@@ -1672,16 +1718,7 @@ export class V2E2EECoordinator {
|
|
|
1672
1718
|
|| (contigAdvanced && ackSeq > knownServerAckSeq));
|
|
1673
1719
|
if (ackNeeded) {
|
|
1674
1720
|
this.recordForwardAck(ns, ackSeq);
|
|
1675
|
-
|
|
1676
|
-
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter > nextAfterSeq;
|
|
1677
|
-
if (canContinuePage && !pipelineRunning) {
|
|
1678
|
-
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
1679
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
1680
|
-
}
|
|
1681
|
-
else {
|
|
1682
|
-
await this.confirmForwardP2PAck(ackSeq);
|
|
1683
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
1684
|
-
}
|
|
1721
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
1685
1722
|
}
|
|
1686
1723
|
}
|
|
1687
1724
|
if (ns)
|
|
@@ -1721,11 +1758,14 @@ export class V2E2EECoordinator {
|
|
|
1721
1758
|
});
|
|
1722
1759
|
if (!shouldContinue)
|
|
1723
1760
|
break;
|
|
1761
|
+
if (pipelinePage && volatileMessages.length > 0) {
|
|
1762
|
+
await stopPipeline();
|
|
1763
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1764
|
+
pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
|
|
1765
|
+
pipelineStopped = false;
|
|
1766
|
+
pipelineError = null;
|
|
1767
|
+
}
|
|
1724
1768
|
if (shouldContinueForPipeline && !volatileAdvanced && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
1725
|
-
if (pendingAckSeq > 0) {
|
|
1726
|
-
await this.confirmForwardP2PAck(pendingAckSeq);
|
|
1727
|
-
pendingAckSeq = 0;
|
|
1728
|
-
}
|
|
1729
1769
|
startPipeline(nextAfter, volatileCursor);
|
|
1730
1770
|
}
|
|
1731
1771
|
if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
@@ -1736,7 +1776,7 @@ export class V2E2EECoordinator {
|
|
|
1736
1776
|
pullGateKey = nextKey;
|
|
1737
1777
|
}
|
|
1738
1778
|
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
1739
|
-
if (tailDelayMs > 0)
|
|
1779
|
+
if (!pipelineRunning && tailDelayMs > 0)
|
|
1740
1780
|
await v2Sleep(tailDelayMs);
|
|
1741
1781
|
nextAfterSeq = nextAfter;
|
|
1742
1782
|
}
|
|
@@ -1746,10 +1786,11 @@ export class V2E2EECoordinator {
|
|
|
1746
1786
|
}
|
|
1747
1787
|
if (pageCount >= maxPages) {
|
|
1748
1788
|
client._clientLog.warn(`message.v2.pull reached max_pages=${maxPages} after_seq=${nextAfterSeq}`);
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1789
|
+
}
|
|
1790
|
+
if (pendingAckSeq > 0) {
|
|
1791
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1792
|
+
await this.confirmForwardP2PAck(pendingAckSeq);
|
|
1793
|
+
pendingAckSeq = 0;
|
|
1753
1794
|
}
|
|
1754
1795
|
client._delivery.onPullWorkSettled?.();
|
|
1755
1796
|
return deduplicatePulledResults(decrypted);
|
|
@@ -1823,8 +1864,11 @@ export class V2E2EECoordinator {
|
|
|
1823
1864
|
}
|
|
1824
1865
|
async confirmForwardP2PAck(upToSeq, inlineGeneration) {
|
|
1825
1866
|
const ns = this.client._aid ? `p2p:${this.client._aid}` : '';
|
|
1867
|
+
const generation = Number(this.client._v2RuntimeGeneration ?? 0);
|
|
1868
|
+
const deliveryGeneration = this.client._delivery.captureInlineGeneration?.();
|
|
1826
1869
|
try {
|
|
1827
1870
|
const result = await this.ackV2ForForward(upToSeq, inlineGeneration);
|
|
1871
|
+
this.ensureDeliveryCurrent(ns, generation, deliveryGeneration);
|
|
1828
1872
|
const actualAckSeq = this.client._delivery?.resolveForwardAckSeq?.(result, upToSeq) ?? 0;
|
|
1829
1873
|
if (actualAckSeq < upToSeq) {
|
|
1830
1874
|
throw new E2EEError(`message.v2.ack server ACK watermark ${actualAckSeq} is below requested ${upToSeq}`);
|
|
@@ -1832,7 +1876,10 @@ export class V2E2EECoordinator {
|
|
|
1832
1876
|
this.clearForwardAck(ns, upToSeq);
|
|
1833
1877
|
}
|
|
1834
1878
|
catch (exc) {
|
|
1835
|
-
this.
|
|
1879
|
+
if (Number(this.client._v2RuntimeGeneration ?? 0) === generation
|
|
1880
|
+
&& (deliveryGeneration === undefined || this.client._delivery.isInlineGenerationCurrent(deliveryGeneration))
|
|
1881
|
+
&& this.client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false)
|
|
1882
|
+
this.recordForwardAck(ns, upToSeq);
|
|
1836
1883
|
throw exc;
|
|
1837
1884
|
}
|
|
1838
1885
|
}
|
|
@@ -1965,6 +2012,8 @@ export class V2E2EECoordinator {
|
|
|
1965
2012
|
}
|
|
1966
2013
|
async pullGroupV2TailInternal(params, deferPublish = false) {
|
|
1967
2014
|
const client = this.client;
|
|
2015
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
2016
|
+
const inlineGeneration = client._delivery.captureInlineGeneration?.();
|
|
1968
2017
|
if (params.window_mode !== 'tail')
|
|
1969
2018
|
throw new ValidationError('window_mode must equal tail');
|
|
1970
2019
|
if ('ack_up_to_seq' in params || params.force === true)
|
|
@@ -2000,18 +2049,26 @@ export class V2E2EECoordinator {
|
|
|
2000
2049
|
if (resultAid)
|
|
2001
2050
|
groupAid = resultAid;
|
|
2002
2051
|
const page = validateTailPage(result, afterSeq, limit);
|
|
2003
|
-
client._seqTracker.commitTailWindow(ns, {
|
|
2004
|
-
windowStartSeq: page.windowStart, headSeq: page.head, coveredThroughSeq: page.head, pageLimit: limit,
|
|
2005
|
-
});
|
|
2006
|
-
client._persistSeq(ns);
|
|
2007
|
-
await client._saveSeqTrackerState?.();
|
|
2008
2052
|
const decoded = [];
|
|
2009
2053
|
for (const msg of page.messages) {
|
|
2054
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2010
2055
|
client._logMessageDebug?.('tail-raw', 'group.v2.pull', 'group.message_created', msg);
|
|
2011
2056
|
const plaintext = await this.decodeGroupWindowMessage(msg, groupId, groupAid, true, false, 'tail', deferPublish);
|
|
2012
2057
|
if (plaintext)
|
|
2013
2058
|
decoded.push(plaintext);
|
|
2014
2059
|
}
|
|
2060
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2061
|
+
const proof = {
|
|
2062
|
+
windowStartSeq: page.windowStart, headSeq: page.head, coveredThroughSeq: page.head, pageLimit: limit,
|
|
2063
|
+
};
|
|
2064
|
+
if (deferPublish)
|
|
2065
|
+
client._delivery.deferTailWindow(ns, proof);
|
|
2066
|
+
else {
|
|
2067
|
+
client._seqTracker.commitTailWindow(ns, proof);
|
|
2068
|
+
client._persistSeq(ns);
|
|
2069
|
+
await client._saveSeqTrackerState?.();
|
|
2070
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2071
|
+
}
|
|
2015
2072
|
const ack = client._seqTracker.getContiguousSeq(ns);
|
|
2016
2073
|
if (ownsCursor)
|
|
2017
2074
|
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);
|
|
@@ -2056,6 +2113,8 @@ export class V2E2EECoordinator {
|
|
|
2056
2113
|
}
|
|
2057
2114
|
async pullGroupV2Impl(groupId, afterSeq = 0, limit = 50, opts) {
|
|
2058
2115
|
const client = this.client;
|
|
2116
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
2117
|
+
const inlineGeneration = client._delivery.captureInlineGeneration?.();
|
|
2059
2118
|
await client._ensureV2SessionReady('group.pull');
|
|
2060
2119
|
const gid = String(groupId ?? '').trim();
|
|
2061
2120
|
if (!gid)
|
|
@@ -2111,6 +2170,7 @@ export class V2E2EECoordinator {
|
|
|
2111
2170
|
let pageCount = 0;
|
|
2112
2171
|
let lastAutoAckSeq = 0;
|
|
2113
2172
|
let pendingAckSeq = deferredForwardAck;
|
|
2173
|
+
let committedAckSeq = ownsCursor ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
2114
2174
|
const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
|
|
2115
2175
|
let pipelineRunning = false;
|
|
2116
2176
|
let pipelineError = null;
|
|
@@ -2126,21 +2186,27 @@ export class V2E2EECoordinator {
|
|
|
2126
2186
|
for (let produced = pageCount; produced < maxPages; produced += 1) {
|
|
2127
2187
|
if (pipelineStopped)
|
|
2128
2188
|
break;
|
|
2189
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2190
|
+
const ackUpToSeq = Math.min(pendingAckSeq, committedAckSeq);
|
|
2191
|
+
const inlineAckGeneration = ackUpToSeq > 0
|
|
2192
|
+
? client._delivery?.pendingGroupInlineAckGeneration?.(ns) ?? null : null;
|
|
2129
2193
|
const result = await client._callRawV2Rpc('group.v2.pull', withExplicitGroupAid({
|
|
2130
2194
|
group_id: wireGroupId,
|
|
2131
2195
|
after_seq: producerAfterSeq,
|
|
2132
2196
|
limit,
|
|
2133
2197
|
...cursorParams,
|
|
2134
2198
|
...(opts?.force ? { force: true } : {}),
|
|
2199
|
+
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
2135
2200
|
}, groupAid), pullGateKey);
|
|
2136
2201
|
if (pipelineStopped)
|
|
2137
2202
|
break;
|
|
2203
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2138
2204
|
const pageMessages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
2139
2205
|
const enqueued = await pipelineQueue.put({
|
|
2140
2206
|
result,
|
|
2141
2207
|
afterSeq: producerAfterSeq,
|
|
2142
|
-
ackUpToSeq
|
|
2143
|
-
inlineAckGeneration
|
|
2208
|
+
ackUpToSeq,
|
|
2209
|
+
inlineAckGeneration,
|
|
2144
2210
|
requestedVolatileCursor: '',
|
|
2145
2211
|
weight: pageMessages.length,
|
|
2146
2212
|
}, pageMessages.length);
|
|
@@ -2214,21 +2280,24 @@ export class V2E2EECoordinator {
|
|
|
2214
2280
|
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
2215
2281
|
}, groupAid), pullGateKey);
|
|
2216
2282
|
}
|
|
2283
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2284
|
+
if (ownsCursor && !opts?.force)
|
|
2285
|
+
client._delivery.observeBackgroundPullResponse?.(ns, nextAfterSeq, result, limit);
|
|
2217
2286
|
if (ackUpToSeq > 0) {
|
|
2218
2287
|
const actualAckSeq = client._delivery?.resolveGroupPullAckSeq?.(result, ackUpToSeq) ?? 0;
|
|
2219
2288
|
if (actualAckSeq >= ackUpToSeq) {
|
|
2220
2289
|
if (pendingInlineGeneration !== null) {
|
|
2221
2290
|
client._delivery?.completePendingGroupInlineAckUpTo?.(ns, actualAckSeq, pendingInlineGeneration);
|
|
2222
2291
|
}
|
|
2223
|
-
pendingAckSeq
|
|
2292
|
+
if (pendingAckSeq <= ackUpToSeq)
|
|
2293
|
+
pendingAckSeq = 0;
|
|
2224
2294
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
2225
2295
|
this.clearForwardAck(ns, ackUpToSeq);
|
|
2226
2296
|
}
|
|
2227
2297
|
else {
|
|
2228
2298
|
client._clientLog.warn(`group.v2.pull piggyback ACK below target: group=${gid}, requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
2299
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackUpToSeq);
|
|
2300
|
+
this.recordForwardAck(ns, ackUpToSeq);
|
|
2232
2301
|
}
|
|
2233
2302
|
}
|
|
2234
2303
|
const resultGroupAid = String(result.group_aid ?? result.groupAid ?? '').trim();
|
|
@@ -2245,11 +2314,16 @@ export class V2E2EECoordinator {
|
|
|
2245
2314
|
.map((msg) => Number(msg.seq ?? 0))
|
|
2246
2315
|
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
2247
2316
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
2248
|
-
|
|
2249
|
-
? client._seqTracker.snapshotNamespace(ns)
|
|
2250
|
-
: null;
|
|
2317
|
+
let pageTrackerSnapshot;
|
|
2251
2318
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
2252
2319
|
const pageContiguousSeq = contiguousPullSeq(pageContigBefore, seqs, result.contiguous_seq);
|
|
2320
|
+
if (!pipelineRunning && shouldContinueForwardPage({
|
|
2321
|
+
rawCount: messages.length, nextAfterSeq, nextAfter: pageMaxSeq, limit,
|
|
2322
|
+
hasMoreField, hasMore, serverHead, pendingAckSeq: 0,
|
|
2323
|
+
observedUpper: typeof client._seqTracker.getMaxSeenSeq === 'function'
|
|
2324
|
+
? client._seqTracker.getMaxSeenSeq(ns) : pageMaxSeq,
|
|
2325
|
+
}))
|
|
2326
|
+
startPipeline(pageMaxSeq);
|
|
2253
2327
|
const deferredKeyFetches = new Map();
|
|
2254
2328
|
let blockedSeq = 0;
|
|
2255
2329
|
const pageDecryptedBefore = decrypted.length;
|
|
@@ -2383,11 +2457,19 @@ export class V2E2EECoordinator {
|
|
|
2383
2457
|
const retentionFloor = Math.max(Number(result.retention_floor_message_seq ?? result.retention_floor_seq ?? 0), Number(cursor?.retention_floor_seq ?? 0));
|
|
2384
2458
|
const visibilityFloor = Math.max(0, Math.max(0, Number(result.earliest_available_message_seq ?? 1) - 1), Number(cursor?.join_seq ?? 0));
|
|
2385
2459
|
const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
|
|
2386
|
-
|
|
2460
|
+
let commitTarget = Math.max(pageContigBefore, Number.isFinite(effectiveFloor) ? effectiveFloor : 0, ownsCursor && pageCount === 1 ? deferredServerCursor : 0, cursorCurrentSeq, blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq);
|
|
2461
|
+
commitTarget = client._delivery.pendingTailDeliveryThrough?.(ns, commitTarget) ?? commitTarget;
|
|
2387
2462
|
let ackSeq = pageContigBefore;
|
|
2388
2463
|
let contigAdvanced = false;
|
|
2389
2464
|
if (ownsCursor) {
|
|
2390
2465
|
try {
|
|
2466
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2467
|
+
const state = client._seqTracker.getSyncState?.(ns);
|
|
2468
|
+
const deliveryThrough = client._delivery.pendingTailDeliveryThrough?.(ns, state && commitTarget >= state.tail - 1 ? Math.max(commitTarget, state.head) : commitTarget) ?? (state && commitTarget >= state.tail - 1 ? Math.max(commitTarget, state.head) : commitTarget);
|
|
2469
|
+
await client._drainOrderedMessages(ns, undefined, false, false, deliveryThrough);
|
|
2470
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2471
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns);
|
|
2472
|
+
client._delivery.commitPendingTailWindow?.(ns, commitTarget);
|
|
2391
2473
|
if (commitTarget > pageContigBefore) {
|
|
2392
2474
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
2393
2475
|
if (effectiveFloor > pageContigBefore) {
|
|
@@ -2404,19 +2486,24 @@ export class V2E2EECoordinator {
|
|
|
2404
2486
|
contigAdvanced = ackSeq !== pageContigBefore;
|
|
2405
2487
|
if (contigAdvanced) {
|
|
2406
2488
|
await client._commitSeqTrackerState(ns);
|
|
2489
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2407
2490
|
if (pageCount === 1 && deferredServerCursor > 0)
|
|
2408
2491
|
this.clearForwardCursor(ns, ackSeq);
|
|
2409
2492
|
}
|
|
2493
|
+
client._delivery.completePendingTailWindow?.(ns);
|
|
2494
|
+
committedAckSeq = ackSeq;
|
|
2410
2495
|
}
|
|
2411
2496
|
catch (exc) {
|
|
2412
|
-
if (
|
|
2413
|
-
client.
|
|
2497
|
+
if (Number(client._v2RuntimeGeneration ?? 0) === generation
|
|
2498
|
+
&& (inlineGeneration === undefined || client._delivery.isInlineGenerationCurrent(inlineGeneration))
|
|
2499
|
+
&& client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false
|
|
2500
|
+
&& pageTrackerSnapshot !== undefined
|
|
2501
|
+
&& typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
2502
|
+
client._seqTracker._restoreNamespaceSnapshotPreservingPush(ns, pageTrackerSnapshot);
|
|
2414
2503
|
}
|
|
2415
2504
|
client._clientLog.error?.(`group.v2.pull Forward commit rolled back: group=${gid} ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
2416
2505
|
throw exc;
|
|
2417
2506
|
}
|
|
2418
|
-
if (contigAdvanced)
|
|
2419
|
-
await client._drainOrderedMessages(ns, undefined, false, false);
|
|
2420
2507
|
}
|
|
2421
2508
|
for (const fetch of deferredKeyFetches.values()) {
|
|
2422
2509
|
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
@@ -2437,16 +2524,7 @@ export class V2E2EECoordinator {
|
|
|
2437
2524
|
|| (contigAdvanced && ackSeq > knownServerCursorSeq));
|
|
2438
2525
|
if (ackNeeded) {
|
|
2439
2526
|
this.recordForwardAck(ns, ackSeq);
|
|
2440
|
-
|
|
2441
|
-
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter > nextAfterSeq && ownsCursor;
|
|
2442
|
-
if (canContinuePage && !pipelineRunning) {
|
|
2443
|
-
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
2444
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
2445
|
-
}
|
|
2446
|
-
else {
|
|
2447
|
-
await this.confirmForwardGroupAck(gid, ackSeq, groupAid);
|
|
2448
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
2449
|
-
}
|
|
2527
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
2450
2528
|
}
|
|
2451
2529
|
const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
2452
2530
|
if (!ownsCursor)
|
|
@@ -2481,10 +2559,6 @@ export class V2E2EECoordinator {
|
|
|
2481
2559
|
if (!shouldContinue)
|
|
2482
2560
|
break;
|
|
2483
2561
|
if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
2484
|
-
if (pendingAckSeq > 0) {
|
|
2485
|
-
await this.confirmForwardGroupAck(gid, pendingAckSeq, groupAid);
|
|
2486
|
-
pendingAckSeq = 0;
|
|
2487
|
-
}
|
|
2488
2562
|
startPipeline(nextAfter);
|
|
2489
2563
|
}
|
|
2490
2564
|
if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
@@ -2496,7 +2570,7 @@ export class V2E2EECoordinator {
|
|
|
2496
2570
|
pullGateKey = nextKey;
|
|
2497
2571
|
}
|
|
2498
2572
|
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
2499
|
-
if (tailDelayMs > 0)
|
|
2573
|
+
if (!pipelineRunning && tailDelayMs > 0)
|
|
2500
2574
|
await v2Sleep(tailDelayMs);
|
|
2501
2575
|
nextAfterSeq = nextAfter;
|
|
2502
2576
|
}
|
|
@@ -2506,18 +2580,22 @@ export class V2E2EECoordinator {
|
|
|
2506
2580
|
}
|
|
2507
2581
|
if (pageCount >= maxPages) {
|
|
2508
2582
|
client._clientLog.warn(`group.v2.pull reached max_pages=${maxPages} group=${gid} after_seq=${nextAfterSeq}`);
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2583
|
+
}
|
|
2584
|
+
if (pendingAckSeq > 0) {
|
|
2585
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
2586
|
+
await this.confirmForwardGroupAck(gid, pendingAckSeq, groupAid);
|
|
2587
|
+
pendingAckSeq = 0;
|
|
2513
2588
|
}
|
|
2514
2589
|
client._delivery.onPullWorkSettled?.();
|
|
2515
2590
|
return deduplicatePulledResults(decrypted);
|
|
2516
2591
|
}
|
|
2517
2592
|
async confirmForwardGroupAck(groupId, upToSeq, groupAid) {
|
|
2518
2593
|
const ns = `group:${String(groupId ?? '').trim()}`;
|
|
2594
|
+
const generation = Number(this.client._v2RuntimeGeneration ?? 0);
|
|
2595
|
+
const deliveryGeneration = this.client._delivery.captureInlineGeneration?.();
|
|
2519
2596
|
try {
|
|
2520
2597
|
const result = await this.ackGroupV2ForForward(groupId, upToSeq, groupAid);
|
|
2598
|
+
this.ensureDeliveryCurrent(ns, generation, deliveryGeneration);
|
|
2521
2599
|
const actualAckSeq = this.client._delivery?.resolveForwardAckSeq?.(result, upToSeq) ?? 0;
|
|
2522
2600
|
if (actualAckSeq < upToSeq) {
|
|
2523
2601
|
throw new E2EEError(`group.v2.ack server ACK watermark ${actualAckSeq} is below requested ${upToSeq}`);
|
|
@@ -2525,7 +2603,10 @@ export class V2E2EECoordinator {
|
|
|
2525
2603
|
this.clearForwardAck(ns, upToSeq);
|
|
2526
2604
|
}
|
|
2527
2605
|
catch (exc) {
|
|
2528
|
-
this.
|
|
2606
|
+
if (Number(this.client._v2RuntimeGeneration ?? 0) === generation
|
|
2607
|
+
&& (deliveryGeneration === undefined || this.client._delivery.isInlineGenerationCurrent(deliveryGeneration))
|
|
2608
|
+
&& this.client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false)
|
|
2609
|
+
this.recordForwardAck(ns, upToSeq);
|
|
2529
2610
|
throw exc;
|
|
2530
2611
|
}
|
|
2531
2612
|
}
|
|
@@ -3108,11 +3189,19 @@ export class V2E2EECoordinator {
|
|
|
3108
3189
|
});
|
|
3109
3190
|
}
|
|
3110
3191
|
}
|
|
3192
|
+
const targetDeviceId = String(opts.deviceId ?? '').trim();
|
|
3111
3193
|
if (peerDevices.length === 0) {
|
|
3194
|
+
if (targetDeviceId && useCache) {
|
|
3195
|
+
this.deleteBootstrapCacheEntry(to);
|
|
3196
|
+
return this.buildV2P2PEnvelope({ ...opts, useCache: false });
|
|
3197
|
+
}
|
|
3112
3198
|
throw new E2EEError(`V2 bootstrap: no devices found for ${to}`);
|
|
3113
3199
|
}
|
|
3114
|
-
const targetDeviceId = String(opts.deviceId ?? '').trim();
|
|
3115
3200
|
if (targetDeviceId && !peerDevices.some((dev) => String(dev.device_id ?? '').trim() === targetDeviceId)) {
|
|
3201
|
+
if (useCache) {
|
|
3202
|
+
this.deleteBootstrapCacheEntry(to);
|
|
3203
|
+
return this.buildV2P2PEnvelope({ ...opts, useCache: false });
|
|
3204
|
+
}
|
|
3116
3205
|
throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
|
|
3117
3206
|
}
|
|
3118
3207
|
let targetSet = useCache ? this.getTargetSetCache(to) : undefined;
|
|
@@ -3203,6 +3292,10 @@ export class V2E2EECoordinator {
|
|
|
3203
3292
|
return isPeerTarget || target.role === 'self_sync';
|
|
3204
3293
|
});
|
|
3205
3294
|
if (!peerTargetFound) {
|
|
3295
|
+
if (useCache) {
|
|
3296
|
+
this.deleteBootstrapCacheEntry(to);
|
|
3297
|
+
return this.buildV2P2PEnvelope({ ...opts, useCache: false });
|
|
3298
|
+
}
|
|
3206
3299
|
throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
|
|
3207
3300
|
}
|
|
3208
3301
|
}
|