@agentunion/fastaun-browser 0.5.16 → 0.5.17
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 +309 -274
- package/_packed_docs/CHANGELOG.md +309 -274
- package/_packed_docs/INDEX.md +8 -2
- package/_packed_docs/KITE_DOCS_GUIDE.md +2 -0
- package/_packed_docs/agent.md/SCHEMA.md +15 -15
- 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 +1178 -1178
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1213 -1213
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +58 -43
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +80 -17
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +27 -6
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +7 -4
- package/_packed_docs/sdk/09-group-rpc-manual.md +167 -35
- package/_packed_docs/sdk/09-message-rpc-manual.md +72 -42
- package/_packed_docs/sdk/09-storage-rpc-manual.md +23 -14
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +16 -12
- package/_packed_docs/sdk/INDEX.md +12 -12
- package/dist/agent-md.d.ts +2 -0
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +7 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/bundle.js +2274 -1789
- package/dist/client/delivery.d.ts +18 -0
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +147 -5
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts +4 -0
- package/dist/client/group-state.d.ts.map +1 -1
- package/dist/client/group-state.js +63 -0
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +37 -4
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +7 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +180 -25
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +60 -12
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +5 -0
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +15 -0
- package/dist/facades.js.map +1 -1
- package/dist/group-fs.d.ts.map +1 -1
- package/dist/group-fs.js +8 -5
- package/dist/group-fs.js.map +1 -1
- package/dist/storage/vfs.d.ts +1 -0
- package/dist/storage/vfs.d.ts.map +1 -1
- package/dist/storage/vfs.js +68 -5
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +34 -3
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.js +34 -34
- package/dist/types.d.ts +16 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -10
package/dist/client/v2-e2ee.js
CHANGED
|
@@ -948,6 +948,12 @@ export class V2E2EECoordinator {
|
|
|
948
948
|
await client._publishAppEvent(eventName, safeEvent, 'pending_retry');
|
|
949
949
|
continue;
|
|
950
950
|
}
|
|
951
|
+
if (!entry.groupId && String(entry.msg.storage ?? '') === 'volatile') {
|
|
952
|
+
await this.publishPendingV2VolatileMessage(entry.msg, plaintext, 'pending_retry');
|
|
953
|
+
client._v2SenderIKPending.delete(key);
|
|
954
|
+
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
951
957
|
client._v2SenderIKPending.delete(key);
|
|
952
958
|
const seq = Number(entry.msg.seq ?? 0);
|
|
953
959
|
if (entry.groupId) {
|
|
@@ -967,10 +973,35 @@ export class V2E2EECoordinator {
|
|
|
967
973
|
client._v2SenderIKFetching.delete(fetchKey);
|
|
968
974
|
}
|
|
969
975
|
}
|
|
976
|
+
async publishPendingV2VolatileMessage(rawMessage, plaintext, source) {
|
|
977
|
+
const delivery = this.client._delivery;
|
|
978
|
+
const claim = delivery.beginVolatileDelivery(rawMessage);
|
|
979
|
+
if (claim === 'inflight')
|
|
980
|
+
return;
|
|
981
|
+
if (claim === 'seen') {
|
|
982
|
+
await delivery.ackVolatileMessages([rawMessage], true, false);
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
try {
|
|
986
|
+
const publicMessage = delivery.publicVolatileMessage(plaintext);
|
|
987
|
+
for (const key of ['storage', 'volatile_cursor', 'created_at', 'expires_at', 'persist']) {
|
|
988
|
+
if (key in rawMessage)
|
|
989
|
+
publicMessage[key] = rawMessage[key];
|
|
990
|
+
}
|
|
991
|
+
const appEvent = delivery.p2pAppEventForMessage(publicMessage);
|
|
992
|
+
await this.client._publishAppEvent(appEvent.event, appEvent.payload, source);
|
|
993
|
+
delivery.markVolatileSeen(rawMessage);
|
|
994
|
+
}
|
|
995
|
+
finally {
|
|
996
|
+
delivery.finishVolatileDelivery(rawMessage);
|
|
997
|
+
}
|
|
998
|
+
await delivery.ackVolatileMessages([rawMessage], true, false);
|
|
999
|
+
}
|
|
970
1000
|
async sendV2(to, payload, opts) {
|
|
971
1001
|
const client = this.client;
|
|
972
1002
|
await client._ensureV2SessionReady('message.send', 'V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)');
|
|
973
1003
|
const toAid = String(to ?? '').trim();
|
|
1004
|
+
const targetDeviceId = String(opts?.deviceId ?? '').trim();
|
|
974
1005
|
if (!toAid)
|
|
975
1006
|
throw new ValidationError("message.send requires 'to'");
|
|
976
1007
|
if (!isJsonObject(payload))
|
|
@@ -981,6 +1012,7 @@ export class V2E2EECoordinator {
|
|
|
981
1012
|
payload,
|
|
982
1013
|
messageId: opts?.messageId,
|
|
983
1014
|
timestamp: opts?.timestamp,
|
|
1015
|
+
deviceId: targetDeviceId || undefined,
|
|
984
1016
|
protectedHeaders: opts?.protectedHeaders,
|
|
985
1017
|
context: opts?.context,
|
|
986
1018
|
useCache,
|
|
@@ -989,6 +1021,8 @@ export class V2E2EECoordinator {
|
|
|
989
1021
|
to: toAid,
|
|
990
1022
|
payload: envelope,
|
|
991
1023
|
message_id: opts?.messageId,
|
|
1024
|
+
...(opts?.ttl !== undefined ? { ttl: opts.ttl } : {}),
|
|
1025
|
+
...(targetDeviceId ? { device_id: targetDeviceId } : {}),
|
|
992
1026
|
encrypt: false,
|
|
993
1027
|
_skip_send_result_envelope: true,
|
|
994
1028
|
});
|
|
@@ -1232,6 +1266,7 @@ export class V2E2EECoordinator {
|
|
|
1232
1266
|
const client = this.client;
|
|
1233
1267
|
await client._ensureV2SessionReady('message.pull');
|
|
1234
1268
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1269
|
+
let volatileCursor = String(opts?.afterVolatileCursor ?? client._delivery.volatilePullCursor() ?? '').trim();
|
|
1235
1270
|
if (ns)
|
|
1236
1271
|
client._delivery.onPullStarted?.(ns);
|
|
1237
1272
|
if (opts?.windowMode === 'tail') {
|
|
@@ -1249,7 +1284,7 @@ export class V2E2EECoordinator {
|
|
|
1249
1284
|
}
|
|
1250
1285
|
if (ns && !opts?.gateLocked) {
|
|
1251
1286
|
const key = pullGateKeyForClient(client, 'message.v2.pull', {
|
|
1252
|
-
after_seq: afterSeq, force: opts?.force === true, limit,
|
|
1287
|
+
after_seq: afterSeq, after_volatile_cursor: volatileCursor, force: opts?.force === true, limit,
|
|
1253
1288
|
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
1254
1289
|
return await client._runPullSerialized(key, () => this.pullV2Impl(afterSeq, limit, {
|
|
1255
1290
|
...(opts ?? {}),
|
|
@@ -1260,7 +1295,7 @@ export class V2E2EECoordinator {
|
|
|
1260
1295
|
let nextAfterSeq = opts?.force ? afterSeq : (afterSeq || (ns ? client._seqTracker.getContiguousSeq(ns) : 0));
|
|
1261
1296
|
let pullGateKey = ns
|
|
1262
1297
|
? pullGateKeyForClient(client, 'message.v2.pull', {
|
|
1263
|
-
after_seq: afterSeq, force: opts?.force === true, limit,
|
|
1298
|
+
after_seq: afterSeq, after_volatile_cursor: volatileCursor, force: opts?.force === true, limit,
|
|
1264
1299
|
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`)
|
|
1265
1300
|
: '';
|
|
1266
1301
|
const deferredServerCursor = ns ? this.pendingForwardCursor(ns, client._seqTracker.getContiguousSeq(ns)) : 0;
|
|
@@ -1271,16 +1306,30 @@ export class V2E2EECoordinator {
|
|
|
1271
1306
|
const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
|
|
1272
1307
|
while (pageCount < maxPages) {
|
|
1273
1308
|
pageCount += 1;
|
|
1309
|
+
const volatileCursorBeforePage = volatileCursor;
|
|
1274
1310
|
const ackUpToSeq = pendingAckSeq;
|
|
1275
1311
|
const pendingInlineGeneration = ackUpToSeq > 0
|
|
1276
1312
|
? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null
|
|
1277
1313
|
: null;
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1314
|
+
let result;
|
|
1315
|
+
try {
|
|
1316
|
+
result = await client._callRawV2Rpc('message.v2.pull', {
|
|
1317
|
+
after_seq: nextAfterSeq,
|
|
1318
|
+
limit,
|
|
1319
|
+
...(volatileCursor ? { after_volatile_cursor: volatileCursor } : {}),
|
|
1320
|
+
...(opts?.force ? { force: true } : {}),
|
|
1321
|
+
...(ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}),
|
|
1322
|
+
}, pullGateKey);
|
|
1323
|
+
}
|
|
1324
|
+
catch (exc) {
|
|
1325
|
+
const text = exc instanceof Error ? exc.message : String(exc);
|
|
1326
|
+
if (!volatileCursor || !text.toLowerCase().includes('volatile cursor epoch mismatch'))
|
|
1327
|
+
throw exc;
|
|
1328
|
+
client._delivery.resetVolatilePullCursor();
|
|
1329
|
+
volatileCursor = '';
|
|
1330
|
+
pageCount -= 1;
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1284
1333
|
if (ackUpToSeq > 0) {
|
|
1285
1334
|
const actualAckSeq = client._delivery?.resolveP2PPullAckSeq?.(result, ackUpToSeq) ?? 0;
|
|
1286
1335
|
if (actualAckSeq >= ackUpToSeq) {
|
|
@@ -1299,6 +1348,12 @@ export class V2E2EECoordinator {
|
|
|
1299
1348
|
}
|
|
1300
1349
|
}
|
|
1301
1350
|
const pageMessages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
1351
|
+
const volatileMessages = client._delivery.volatileMessages(pageMessages);
|
|
1352
|
+
if (volatileMessages.length > 0) {
|
|
1353
|
+
decrypted.push(...await this.publishV2VolatileMessages(volatileMessages, 'pull', true));
|
|
1354
|
+
volatileCursor = client._delivery.volatilePullCursor();
|
|
1355
|
+
}
|
|
1356
|
+
const volatileAdvanced = Boolean(volatileCursor) && volatileCursor !== volatileCursorBeforePage;
|
|
1302
1357
|
const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
|
|
1303
1358
|
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
1304
1359
|
const hasMoreField = Object.prototype.hasOwnProperty.call(result, 'has_more');
|
|
@@ -1477,7 +1532,7 @@ export class V2E2EECoordinator {
|
|
|
1477
1532
|
const rawCount = messages.length;
|
|
1478
1533
|
if (blockedSeq > 0)
|
|
1479
1534
|
break;
|
|
1480
|
-
|
|
1535
|
+
let shouldContinue = shouldContinueForwardPage({
|
|
1481
1536
|
rawCount,
|
|
1482
1537
|
nextAfterSeq,
|
|
1483
1538
|
nextAfter,
|
|
@@ -1489,11 +1544,16 @@ export class V2E2EECoordinator {
|
|
|
1489
1544
|
observedUpper: typeof client._seqTracker.getMaxSeenSeq === 'function'
|
|
1490
1545
|
? client._seqTracker.getMaxSeenSeq(ns) : nextAfter,
|
|
1491
1546
|
});
|
|
1547
|
+
const volatileHasMoreField = Object.prototype.hasOwnProperty.call(result, 'volatile_has_more');
|
|
1548
|
+
const volatileHasMore = result.volatile_has_more === true;
|
|
1549
|
+
if (!shouldContinue && volatileAdvanced && (volatileHasMore || hasMore || (!volatileHasMoreField && limit > 0 && pageMessages.length >= limit))) {
|
|
1550
|
+
shouldContinue = true;
|
|
1551
|
+
}
|
|
1492
1552
|
if (!shouldContinue)
|
|
1493
1553
|
break;
|
|
1494
1554
|
if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
1495
1555
|
const nextKey = pullGateKeyForClient(client, 'message.v2.pull', {
|
|
1496
|
-
after_seq: nextAfter, force: opts?.force === true, limit,
|
|
1556
|
+
after_seq: nextAfter, after_volatile_cursor: volatileCursor, force: opts?.force === true, limit,
|
|
1497
1557
|
}, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
1498
1558
|
await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
|
|
1499
1559
|
pullGateKey = nextKey;
|
|
@@ -1513,6 +1573,73 @@ export class V2E2EECoordinator {
|
|
|
1513
1573
|
client._delivery.onPullWorkSettled?.();
|
|
1514
1574
|
return deduplicatePulledResults(decrypted);
|
|
1515
1575
|
}
|
|
1576
|
+
async publishV2VolatileMessages(messages, source, advanceCursor) {
|
|
1577
|
+
const client = this.client;
|
|
1578
|
+
if (!client._v2Session)
|
|
1579
|
+
return [];
|
|
1580
|
+
const processed = [];
|
|
1581
|
+
const plaintextMessages = [];
|
|
1582
|
+
let complete = true;
|
|
1583
|
+
for (const rawMessage of messages) {
|
|
1584
|
+
const claim = client._delivery.beginVolatileDelivery(rawMessage);
|
|
1585
|
+
if (claim === 'seen') {
|
|
1586
|
+
processed.push(rawMessage);
|
|
1587
|
+
continue;
|
|
1588
|
+
}
|
|
1589
|
+
if (claim === 'inflight') {
|
|
1590
|
+
complete = false;
|
|
1591
|
+
continue;
|
|
1592
|
+
}
|
|
1593
|
+
let plaintext;
|
|
1594
|
+
try {
|
|
1595
|
+
if (client._isEncryptedPushMessage(rawMessage)) {
|
|
1596
|
+
plaintext = await client._decryptV2Message(rawMessage, true, true, true, true, undefined, false, source);
|
|
1597
|
+
}
|
|
1598
|
+
else {
|
|
1599
|
+
const legacy = isJsonObject(rawMessage.legacy_v1)
|
|
1600
|
+
? rawMessage.legacy_v1
|
|
1601
|
+
: {};
|
|
1602
|
+
plaintext = {
|
|
1603
|
+
message_id: String(rawMessage.message_id ?? ''),
|
|
1604
|
+
from: String(rawMessage.from_aid ?? rawMessage.from ?? ''),
|
|
1605
|
+
to: String(legacy.to ?? rawMessage.to ?? client._aid ?? ''),
|
|
1606
|
+
type: String(rawMessage.type ?? ''),
|
|
1607
|
+
timestamp: rawMessage.t_server ?? rawMessage.timestamp,
|
|
1608
|
+
payload: legacy.payload !== undefined ? legacy.payload : rawMessage.payload,
|
|
1609
|
+
encrypted: false,
|
|
1610
|
+
};
|
|
1611
|
+
for (const key of ['context', 'protected_headers', 'headers', 'client_signature']) {
|
|
1612
|
+
if (key in rawMessage)
|
|
1613
|
+
plaintext[key] = rawMessage[key];
|
|
1614
|
+
}
|
|
1615
|
+
if (rawMessage.sender_device_id !== undefined)
|
|
1616
|
+
plaintext.sender_device_id = rawMessage.sender_device_id;
|
|
1617
|
+
attachGatewayProximity(plaintext, rawMessage);
|
|
1618
|
+
}
|
|
1619
|
+
if (!plaintext) {
|
|
1620
|
+
complete = false;
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
const publicMessage = client._delivery.publicVolatileMessage(plaintext);
|
|
1624
|
+
for (const key of ['storage', 'volatile_cursor', 'created_at', 'expires_at', 'persist']) {
|
|
1625
|
+
if (key in rawMessage)
|
|
1626
|
+
publicMessage[key] = rawMessage[key];
|
|
1627
|
+
}
|
|
1628
|
+
const appEvent = client._delivery.p2pAppEventForMessage(publicMessage);
|
|
1629
|
+
await client._publishAppEvent(appEvent.event, appEvent.payload, source);
|
|
1630
|
+
client._delivery.markVolatileSeen(rawMessage);
|
|
1631
|
+
processed.push(rawMessage);
|
|
1632
|
+
plaintextMessages.push(publicMessage);
|
|
1633
|
+
}
|
|
1634
|
+
finally {
|
|
1635
|
+
client._delivery.finishVolatileDelivery(rawMessage);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
if (processed.length > 0) {
|
|
1639
|
+
await client._delivery.ackVolatileMessages(processed, true, advanceCursor && complete && processed.length === messages.length);
|
|
1640
|
+
}
|
|
1641
|
+
return plaintextMessages;
|
|
1642
|
+
}
|
|
1516
1643
|
async confirmForwardP2PAck(upToSeq, inlineGeneration) {
|
|
1517
1644
|
const ns = this.client._aid ? `p2p:${this.client._aid}` : '';
|
|
1518
1645
|
try {
|
|
@@ -1528,41 +1655,51 @@ export class V2E2EECoordinator {
|
|
|
1528
1655
|
throw exc;
|
|
1529
1656
|
}
|
|
1530
1657
|
}
|
|
1531
|
-
async ackV2(upToSeq, inlineGeneration) {
|
|
1532
|
-
return this.ackV2WithConfirmation(upToSeq, inlineGeneration, false);
|
|
1658
|
+
async ackV2(upToSeq, inlineGeneration, volatileCursors = [], volatileIds = []) {
|
|
1659
|
+
return this.ackV2WithConfirmation(upToSeq, inlineGeneration, false, volatileCursors, volatileIds);
|
|
1533
1660
|
}
|
|
1534
1661
|
async ackV2ForForward(upToSeq, inlineGeneration) {
|
|
1535
1662
|
return this.ackV2WithConfirmation(upToSeq, inlineGeneration, true);
|
|
1536
1663
|
}
|
|
1537
|
-
async ackV2WithConfirmation(upToSeq, inlineGeneration, requireServerWatermark) {
|
|
1664
|
+
async ackV2WithConfirmation(upToSeq, inlineGeneration, requireServerWatermark, volatileCursors = [], volatileIds = []) {
|
|
1538
1665
|
const client = this.client;
|
|
1539
1666
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1540
1667
|
let seq = upToSeq ?? (ns ? client._seqTracker.getContiguousSeq(ns) : 0);
|
|
1541
1668
|
if (typeof seq !== 'number' || !Number.isSafeInteger(seq) || seq <= 0)
|
|
1669
|
+
seq = 0;
|
|
1670
|
+
if (seq <= 0 && volatileCursors.length === 0 && volatileIds.length === 0)
|
|
1542
1671
|
return { acked: 0 };
|
|
1543
|
-
|
|
1544
|
-
|
|
1672
|
+
if (seq > 0)
|
|
1673
|
+
seq = client._clampAckSeq('message.v2.ack', 'up_to_seq', ns, seq);
|
|
1674
|
+
if ((!Number.isSafeInteger(seq) || seq <= 0) && volatileCursors.length === 0 && volatileIds.length === 0) {
|
|
1545
1675
|
return { acked: 0 };
|
|
1676
|
+
}
|
|
1546
1677
|
// ACK 发出前绑定当时的 inline pending;迟到响应不得结算重置后新建的 pending。
|
|
1547
1678
|
const ackGeneration = inlineGeneration ?? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null;
|
|
1548
1679
|
const v2SessionAtStart = client._v2Session;
|
|
1549
1680
|
const v2RuntimeGenerationAtStart = Number(client._v2RuntimeGeneration ?? 0);
|
|
1550
|
-
const raw = await client._callRawV2Rpc('message.v2.ack', {
|
|
1681
|
+
const raw = await client._callRawV2Rpc('message.v2.ack', {
|
|
1682
|
+
...(seq > 0 ? { up_to_seq: seq } : {}),
|
|
1683
|
+
...(volatileCursors.length > 0 ? { volatile_cursors: volatileCursors } : {}),
|
|
1684
|
+
...(volatileIds.length > 0 ? { volatile_ids: volatileIds } : {}),
|
|
1685
|
+
});
|
|
1551
1686
|
const result = isJsonObject(raw)
|
|
1552
1687
|
? { ...raw }
|
|
1553
1688
|
: { result: raw };
|
|
1554
|
-
const actualAckSeq =
|
|
1555
|
-
? (
|
|
1556
|
-
|
|
1557
|
-
|
|
1689
|
+
const actualAckSeq = seq > 0
|
|
1690
|
+
? (requireServerWatermark
|
|
1691
|
+
? (client._delivery?.resolveForwardAckSeq?.(result, seq) ?? 0)
|
|
1692
|
+
: (client._delivery?.resolveActualAckSeq?.(result, seq) ?? seq))
|
|
1693
|
+
: 0;
|
|
1694
|
+
if (seq > 0 && requireServerWatermark && actualAckSeq < seq) {
|
|
1558
1695
|
throw new E2EEError(`message.v2.ack server ACK watermark ${actualAckSeq} is below requested ${seq}`);
|
|
1559
1696
|
}
|
|
1560
1697
|
result.ack_seq = actualAckSeq;
|
|
1561
1698
|
result.success = true;
|
|
1562
|
-
if (ackGeneration !== null) {
|
|
1699
|
+
if (seq > 0 && ackGeneration !== null) {
|
|
1563
1700
|
client._delivery?.completePendingP2PInlineAckUpTo?.(ns, actualAckSeq, ackGeneration);
|
|
1564
1701
|
}
|
|
1565
|
-
if (v2SessionAtStart
|
|
1702
|
+
if (seq > 0 && v2SessionAtStart
|
|
1566
1703
|
&& client._v2Session === v2SessionAtStart
|
|
1567
1704
|
&& Number(client._v2RuntimeGeneration ?? 0) === v2RuntimeGenerationAtStart) {
|
|
1568
1705
|
try {
|
|
@@ -2546,7 +2683,6 @@ export class V2E2EECoordinator {
|
|
|
2546
2683
|
seq: msg.seq ?? null,
|
|
2547
2684
|
timestamp: msg.timestamp ?? msg.t_server ?? null,
|
|
2548
2685
|
device_id: msg.device_id ?? null,
|
|
2549
|
-
slot_id: msg.slot_id ?? null,
|
|
2550
2686
|
_decrypt_error: 'encrypted push payload is not decryptable on raw push path',
|
|
2551
2687
|
_decrypt_stage: 'push_envelope',
|
|
2552
2688
|
};
|
|
@@ -2590,13 +2726,14 @@ export class V2E2EECoordinator {
|
|
|
2590
2726
|
encrypted: true,
|
|
2591
2727
|
e2ee: e2eeMeta,
|
|
2592
2728
|
};
|
|
2729
|
+
const senderDeviceId = String(aad.from_device ?? '').trim();
|
|
2730
|
+
if (senderDeviceId)
|
|
2731
|
+
result.sender_device_id = senderDeviceId;
|
|
2593
2732
|
result.direction = fromAid && fromAid === client._aid ? 'outbound_sync' : 'inbound';
|
|
2594
2733
|
if (msg.t_server !== undefined)
|
|
2595
2734
|
result.t_server = msg.t_server;
|
|
2596
2735
|
if (msg.device_id !== undefined)
|
|
2597
2736
|
result.device_id = msg.device_id;
|
|
2598
|
-
if (msg.slot_id !== undefined)
|
|
2599
|
-
result.slot_id = msg.slot_id;
|
|
2600
2737
|
attachGatewayProximity(result, msg);
|
|
2601
2738
|
if (group) {
|
|
2602
2739
|
result.group_id = msg.group_id ?? aad.group_id ?? envelope.group_id ?? null;
|
|
@@ -2674,6 +2811,10 @@ export class V2E2EECoordinator {
|
|
|
2674
2811
|
if (peerDevices.length === 0) {
|
|
2675
2812
|
throw new E2EEError(`V2 bootstrap: no devices found for ${to}`);
|
|
2676
2813
|
}
|
|
2814
|
+
const targetDeviceId = String(opts.deviceId ?? '').trim();
|
|
2815
|
+
if (targetDeviceId && !peerDevices.some((dev) => String(dev.device_id ?? '').trim() === targetDeviceId)) {
|
|
2816
|
+
throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
|
|
2817
|
+
}
|
|
2677
2818
|
let targetSet = useCache ? this.getTargetSetCache(to) : undefined;
|
|
2678
2819
|
if (targetSet) {
|
|
2679
2820
|
targetSet = this.cloneP2PTargetSet(targetSet);
|
|
@@ -2751,6 +2892,20 @@ export class V2E2EECoordinator {
|
|
|
2751
2892
|
});
|
|
2752
2893
|
targetSet = this.cloneP2PTargetSet(targetSet);
|
|
2753
2894
|
}
|
|
2895
|
+
if (targetDeviceId) {
|
|
2896
|
+
let peerTargetFound = false;
|
|
2897
|
+
targetSet.targets = targetSet.targets.filter((target) => {
|
|
2898
|
+
const isPeerTarget = target.aid === to
|
|
2899
|
+
&& (target.deviceId === targetDeviceId || (wrapPolicy?.scope === 'aid' && !target.deviceId))
|
|
2900
|
+
&& target.role !== 'self_sync';
|
|
2901
|
+
if (isPeerTarget)
|
|
2902
|
+
peerTargetFound = true;
|
|
2903
|
+
return isPeerTarget || target.role === 'self_sync';
|
|
2904
|
+
});
|
|
2905
|
+
if (!peerTargetFound) {
|
|
2906
|
+
throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2754
2909
|
const sender = await session.getSenderIdentity();
|
|
2755
2910
|
const envelope = await encryptP2PMessage(sender, targetSet, opts.payload, { messageId: opts.messageId, timestamp: opts.timestamp, protectedHeaders: opts.protectedHeaders, context: opts.context });
|
|
2756
2911
|
return envelope;
|