@agentunion/fastaun 0.5.16 → 0.5.18
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 +336 -260
- package/_packed_docs/CHANGELOG.md +336 -260
- package/_packed_docs/INDEX.md +36 -5
- package/_packed_docs/KITE_DOCS_GUIDE.md +13 -5
- package/_packed_docs/agent.md/SCHEMA.md +52 -33
- 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 +1170 -1164
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1215 -1213
- 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 +95 -6
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +58 -43
- 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 +19 -4
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +80 -17
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +25 -2
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +69 -10
- 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 +48 -18
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +16 -12
- package/_packed_docs/sdk/INDEX.md +14 -13
- package/dist/agent-md-schema.js +2 -0
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +3 -1
- package/dist/agent-md.js +10 -2
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +58 -0
- package/dist/aid-store.js +234 -3
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts +17 -1
- package/dist/aid.js +48 -2
- package/dist/aid.js.map +1 -1
- package/dist/client/delivery.d.ts +19 -0
- package/dist/client/delivery.js +264 -31
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts +4 -0
- package/dist/client/group-state.js +63 -0
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.js +29 -14
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +17 -2
- package/dist/client/v2-e2ee.js +971 -520
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +3 -0
- package/dist/client.js +120 -19
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +7 -0
- package/dist/facades.js +11 -0
- package/dist/facades.js.map +1 -1
- package/dist/group-fs.js +7 -5
- package/dist/group-fs.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ownership-proof.d.ts +40 -0
- package/dist/ownership-proof.js +140 -0
- package/dist/ownership-proof.js.map +1 -0
- package/dist/storage/vfs.d.ts +1 -0
- package/dist/storage/vfs.js +61 -6
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +31 -1
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/types.d.ts +16 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -14
package/dist/client/delivery.js
CHANGED
|
@@ -7,6 +7,7 @@ const PUSHED_SEQS_LIMIT = 50_000;
|
|
|
7
7
|
const PENDING_ORDERED_LIMIT = 50_000;
|
|
8
8
|
const MESSAGE_RECALL_SEEN_LIMIT = 10_000;
|
|
9
9
|
const GROUP_RECALL_SEEN_LIMIT = 10_000;
|
|
10
|
+
const VOLATILE_SEEN_LIMIT = 10_000;
|
|
10
11
|
const SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS = 200;
|
|
11
12
|
const DELIVERY_CHANGED_MESSAGE_EVENTS = new Set([
|
|
12
13
|
'message.received',
|
|
@@ -35,7 +36,7 @@ const APP_SEND_ENVELOPE_METHODS = new Set([
|
|
|
35
36
|
const APP_GROUP_EVENT_ENVELOPE_KEYS = [
|
|
36
37
|
'module_id', 'event_id', 'event_seq', 'seq', 'event_type', 'action', 'group_id',
|
|
37
38
|
'actor_aid', 'sender_aid', 'member_aid', 'target_aid', 'operator_aid',
|
|
38
|
-
'created_at', 'timestamp', 't_server', 'status', 'device_id',
|
|
39
|
+
'created_at', 'timestamp', 't_server', 'status', 'device_id',
|
|
39
40
|
];
|
|
40
41
|
function isPromiseLike(value) {
|
|
41
42
|
return Boolean(value && typeof value.then === 'function');
|
|
@@ -87,6 +88,10 @@ export class MessageDeliveryEngine {
|
|
|
87
88
|
deliveryChangedGeneration = 0;
|
|
88
89
|
syncRun = 0;
|
|
89
90
|
syncSessions = null;
|
|
91
|
+
volatileContext = '';
|
|
92
|
+
volatileCursor = '';
|
|
93
|
+
volatileSeenMessages = new Map();
|
|
94
|
+
volatileInFlightMessages = new Set();
|
|
90
95
|
// 同一实例的 tracker 写入必须串行,避免旧的延迟 flush 覆盖新的 Forward 提交。
|
|
91
96
|
seqTrackerWriteChain = Promise.resolve();
|
|
92
97
|
constructor(runtime) {
|
|
@@ -157,9 +162,13 @@ export class MessageDeliveryEngine {
|
|
|
157
162
|
throw new Error(`${method} response must be an object`);
|
|
158
163
|
}
|
|
159
164
|
const client = this.runtime.client;
|
|
165
|
+
const response = result;
|
|
166
|
+
const volatileMessages = method === 'message.pull' ? this.volatileMessages(response.messages) : [];
|
|
167
|
+
if (volatileMessages.length > 0) {
|
|
168
|
+
await this.publishPlainVolatileMessages(volatileMessages, 'pull', true);
|
|
169
|
+
}
|
|
160
170
|
this.ensurePullOperationCurrent(ns);
|
|
161
171
|
this.syncStarted(ns);
|
|
162
|
-
const response = result;
|
|
163
172
|
const messages = deduplicatePlainForwardPageMessages(response.messages, afterSeq);
|
|
164
173
|
const coordinator = this.forwardCoordinator();
|
|
165
174
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
@@ -279,8 +288,121 @@ export class MessageDeliveryEngine {
|
|
|
279
288
|
hasMore: typeof response.has_more === 'boolean' ? response.has_more : undefined,
|
|
280
289
|
currentSeq: client._seqTracker.getContiguousSeq(ns),
|
|
281
290
|
});
|
|
291
|
+
response.messages = (Array.isArray(response.messages) ? response.messages : [])
|
|
292
|
+
.map((message) => this.stripInternalSenderDeviceFields(message));
|
|
282
293
|
return { rawCount: messages.length, publishedCount };
|
|
283
294
|
}
|
|
295
|
+
isVolatileMessage(message) {
|
|
296
|
+
return isJsonObject(message)
|
|
297
|
+
&& String(message.storage ?? '') === 'volatile';
|
|
298
|
+
}
|
|
299
|
+
volatileMessages(value) {
|
|
300
|
+
if (!Array.isArray(value))
|
|
301
|
+
return [];
|
|
302
|
+
return value.filter((item) => this.isVolatileMessage(item)).map((item) => ({ ...item }));
|
|
303
|
+
}
|
|
304
|
+
ensureVolatileContext() {
|
|
305
|
+
const client = this.runtime.client;
|
|
306
|
+
const context = JSON.stringify([client._aid ?? '', client._deviceId ?? '', client._slotId ?? '']);
|
|
307
|
+
if (context === this.volatileContext)
|
|
308
|
+
return;
|
|
309
|
+
this.volatileContext = context;
|
|
310
|
+
this.volatileCursor = '';
|
|
311
|
+
this.volatileSeenMessages.clear();
|
|
312
|
+
this.volatileInFlightMessages.clear();
|
|
313
|
+
}
|
|
314
|
+
volatilePullCursor() {
|
|
315
|
+
this.ensureVolatileContext();
|
|
316
|
+
return this.volatileCursor;
|
|
317
|
+
}
|
|
318
|
+
resetVolatilePullCursor() {
|
|
319
|
+
this.ensureVolatileContext();
|
|
320
|
+
this.volatileCursor = '';
|
|
321
|
+
}
|
|
322
|
+
volatileSeenKey(message) {
|
|
323
|
+
const messageId = String(message.message_id ?? '').trim();
|
|
324
|
+
const sender = String(message.from_aid ?? message.from ?? '').trim();
|
|
325
|
+
const cursor = String(message.volatile_cursor ?? '').trim();
|
|
326
|
+
return `${sender}|${messageId || cursor}`;
|
|
327
|
+
}
|
|
328
|
+
isVolatileSeen(message) {
|
|
329
|
+
this.ensureVolatileContext();
|
|
330
|
+
const now = Date.now();
|
|
331
|
+
for (const [key, expiresAt] of this.volatileSeenMessages) {
|
|
332
|
+
if (expiresAt <= now)
|
|
333
|
+
this.volatileSeenMessages.delete(key);
|
|
334
|
+
}
|
|
335
|
+
return this.volatileSeenMessages.has(this.volatileSeenKey(message));
|
|
336
|
+
}
|
|
337
|
+
markVolatileSeen(message) {
|
|
338
|
+
this.ensureVolatileContext();
|
|
339
|
+
const now = Date.now();
|
|
340
|
+
const expiresAt = Math.max(now + 60_000, Number(message.expires_at ?? now + 1_800_000) || now + 1_800_000);
|
|
341
|
+
this.volatileSeenMessages.set(this.volatileSeenKey(message), expiresAt);
|
|
342
|
+
while (this.volatileSeenMessages.size > VOLATILE_SEEN_LIMIT) {
|
|
343
|
+
const first = this.volatileSeenMessages.keys().next().value;
|
|
344
|
+
if (first === undefined)
|
|
345
|
+
break;
|
|
346
|
+
this.volatileSeenMessages.delete(first);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
beginVolatileDelivery(message) {
|
|
350
|
+
if (this.isVolatileSeen(message))
|
|
351
|
+
return 'seen';
|
|
352
|
+
const key = this.volatileSeenKey(message);
|
|
353
|
+
if (this.volatileInFlightMessages.has(key))
|
|
354
|
+
return 'inflight';
|
|
355
|
+
this.volatileInFlightMessages.add(key);
|
|
356
|
+
return 'claimed';
|
|
357
|
+
}
|
|
358
|
+
finishVolatileDelivery(message) {
|
|
359
|
+
this.ensureVolatileContext();
|
|
360
|
+
this.volatileInFlightMessages.delete(this.volatileSeenKey(message));
|
|
361
|
+
}
|
|
362
|
+
publicVolatileMessage(message) {
|
|
363
|
+
const result = this.stripInternalSenderDeviceFields(message);
|
|
364
|
+
delete result.seq;
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
async ackVolatileMessages(messages, v2, advanceCursor) {
|
|
368
|
+
const cursors = [...new Set(messages
|
|
369
|
+
.map((message) => String(message.volatile_cursor ?? '').trim())
|
|
370
|
+
.filter(Boolean))];
|
|
371
|
+
if (cursors.length === 0)
|
|
372
|
+
return undefined;
|
|
373
|
+
const result = await this.runtime.client._callRawV2Rpc(v2 ? 'message.v2.ack' : 'message.ack', { volatile_cursors: cursors, _rpc_background: true });
|
|
374
|
+
if (advanceCursor) {
|
|
375
|
+
this.ensureVolatileContext();
|
|
376
|
+
this.volatileCursor = cursors[cursors.length - 1] ?? '';
|
|
377
|
+
}
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
async publishPlainVolatileMessages(messages, source, advanceCursor) {
|
|
381
|
+
const processed = [];
|
|
382
|
+
for (const rawMessage of messages) {
|
|
383
|
+
const message = this.publicVolatileMessage(rawMessage);
|
|
384
|
+
const claim = this.beginVolatileDelivery(message);
|
|
385
|
+
if (claim === 'seen') {
|
|
386
|
+
processed.push(rawMessage);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (claim === 'inflight')
|
|
390
|
+
continue;
|
|
391
|
+
try {
|
|
392
|
+
const appEvent = this.p2pAppEventForMessage(message);
|
|
393
|
+
await this.runtime.client._publishAppEvent(appEvent.event, appEvent.payload, source);
|
|
394
|
+
this.markVolatileSeen(message);
|
|
395
|
+
}
|
|
396
|
+
finally {
|
|
397
|
+
this.finishVolatileDelivery(message);
|
|
398
|
+
}
|
|
399
|
+
processed.push(rawMessage);
|
|
400
|
+
}
|
|
401
|
+
if (processed.length > 0) {
|
|
402
|
+
await this.ackVolatileMessages(processed, false, advanceCursor);
|
|
403
|
+
}
|
|
404
|
+
return processed.map((message) => this.publicVolatileMessage(message));
|
|
405
|
+
}
|
|
284
406
|
resetInlineAckState() {
|
|
285
407
|
this.syncStopped('aborted');
|
|
286
408
|
this.deliveryChangedGeneration += 1;
|
|
@@ -714,9 +836,6 @@ export class MessageDeliveryEngine {
|
|
|
714
836
|
if (!('device_id' in result)) {
|
|
715
837
|
result.device_id = client._deviceId;
|
|
716
838
|
}
|
|
717
|
-
if (!('slot_id' in result)) {
|
|
718
|
-
result.slot_id = client._slotId;
|
|
719
|
-
}
|
|
720
839
|
return result;
|
|
721
840
|
}
|
|
722
841
|
normalizePublishedMessagePayload(event, payload, source = 'direct') {
|
|
@@ -728,7 +847,7 @@ export class MessageDeliveryEngine {
|
|
|
728
847
|
normalized = this.attachAppMessageEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
|
|
729
848
|
}
|
|
730
849
|
else if (this.isGroupScopedEvent(event)) {
|
|
731
|
-
normalized = this.attachAppGroupEventEnvelope(this.attachCurrentInstanceContext(payload));
|
|
850
|
+
normalized = this.attachAppGroupEventEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
|
|
732
851
|
}
|
|
733
852
|
else {
|
|
734
853
|
normalized = payload;
|
|
@@ -901,7 +1020,7 @@ export class MessageDeliveryEngine {
|
|
|
901
1020
|
const authoritativeSource = hasGroupMentionModeField(serverSource) ? serverSource : out;
|
|
902
1021
|
out = normalizeGroupMentionMode(out, authoritativeSource);
|
|
903
1022
|
}
|
|
904
|
-
return out;
|
|
1023
|
+
return this.stripInternalSenderDeviceFields(out);
|
|
905
1024
|
}
|
|
906
1025
|
attachAppGroupEventEnvelope(payload) {
|
|
907
1026
|
if (!isJsonObject(payload))
|
|
@@ -915,10 +1034,13 @@ export class MessageDeliveryEngine {
|
|
|
915
1034
|
if (!isJsonObject(payload))
|
|
916
1035
|
return payload;
|
|
917
1036
|
const result = { ...payload };
|
|
918
|
-
delete result.sender_device_id;
|
|
919
1037
|
delete result._sender_device_id;
|
|
920
1038
|
delete result.from_device_id;
|
|
921
1039
|
delete result.from_device;
|
|
1040
|
+
delete result.slot_id;
|
|
1041
|
+
delete result.sender_slot_id;
|
|
1042
|
+
delete result.target_slot_id;
|
|
1043
|
+
delete result.owner_slot_id;
|
|
922
1044
|
return result;
|
|
923
1045
|
}
|
|
924
1046
|
recallEventFromMessage(message) {
|
|
@@ -973,9 +1095,7 @@ export class MessageDeliveryEngine {
|
|
|
973
1095
|
}
|
|
974
1096
|
if ('device_id' in msg && !('device_id' in event))
|
|
975
1097
|
event.device_id = msg.device_id;
|
|
976
|
-
|
|
977
|
-
event.slot_id = msg.slot_id;
|
|
978
|
-
return event;
|
|
1098
|
+
return this.stripInternalSenderDeviceFields(event);
|
|
979
1099
|
}
|
|
980
1100
|
p2pAppEventForMessage(message) {
|
|
981
1101
|
const recall = this.recallEventFromMessage(message);
|
|
@@ -1082,7 +1202,7 @@ export class MessageDeliveryEngine {
|
|
|
1082
1202
|
if (!('tombstone_message_id' in event))
|
|
1083
1203
|
event.tombstone_message_id = msg.message_id;
|
|
1084
1204
|
}
|
|
1085
|
-
return event;
|
|
1205
|
+
return this.stripInternalSenderDeviceFields(event);
|
|
1086
1206
|
}
|
|
1087
1207
|
groupRecallDedupKey(groupId, payload) {
|
|
1088
1208
|
// 群撤回去重键:group_id + 原始消息标识。
|
|
@@ -1340,6 +1460,24 @@ export class MessageDeliveryEngine {
|
|
|
1340
1460
|
return false;
|
|
1341
1461
|
return senderDevice.trim() === String(client._deviceId).trim();
|
|
1342
1462
|
}
|
|
1463
|
+
isSelfSentP2PMessage(message) {
|
|
1464
|
+
if (!isJsonObject(message))
|
|
1465
|
+
return false;
|
|
1466
|
+
const client = this.runtime.client;
|
|
1467
|
+
if (String(message.direction ?? '').trim() !== 'outbound_sync')
|
|
1468
|
+
return false;
|
|
1469
|
+
const selfAid = String(client._aid ?? '').trim().toLowerCase();
|
|
1470
|
+
const senderAid = String(message.sender_aid ?? message.from_aid ?? message.from ?? '').trim().toLowerCase();
|
|
1471
|
+
if (!selfAid || senderAid !== selfAid)
|
|
1472
|
+
return false;
|
|
1473
|
+
const senderDevice = Object.prototype.hasOwnProperty.call(message, 'sender_device_id')
|
|
1474
|
+
? message.sender_device_id
|
|
1475
|
+
: message.from_device_id;
|
|
1476
|
+
return typeof senderDevice === 'string'
|
|
1477
|
+
&& Boolean(senderDevice.trim())
|
|
1478
|
+
&& Boolean(String(client._deviceId ?? '').trim())
|
|
1479
|
+
&& senderDevice.trim() === String(client._deviceId).trim();
|
|
1480
|
+
}
|
|
1343
1481
|
async runPushProcessSerialized(ns, operation, inlineGeneration) {
|
|
1344
1482
|
const client = this.runtime.client;
|
|
1345
1483
|
if (inlineGeneration === undefined) {
|
|
@@ -1460,12 +1598,16 @@ export class MessageDeliveryEngine {
|
|
|
1460
1598
|
client._clientLog.debug(`P2P push filtered by instance: message_id=${String(msg.message_id ?? '')}, seq=${String(msg.seq ?? '')}, target_device=${String(msg.device_id ?? '')}, target_slot=${String(msg.slot_id ?? '')}, local_device=${client._deviceId}, local_slot=${client._slotId}`);
|
|
1461
1599
|
return;
|
|
1462
1600
|
}
|
|
1601
|
+
if (this.isVolatileMessage(msg)) {
|
|
1602
|
+
await this.publishPlainVolatileMessages([msg], 'push', false);
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1463
1605
|
// 新 SDK 的所有带合法 seq P2P Push 都只作为 Head 通知处理。
|
|
1464
1606
|
// 新服务端为兼容旧 SDK 仍可能发送 message.received;V2 会话下必须
|
|
1465
1607
|
// 在旧 seq tracking / ordered publish 之前转入前台 Tail 协调器。
|
|
1466
1608
|
const inlinePresent = hasNonEmptyInlineMessage(msg.inline_message);
|
|
1467
1609
|
const legacyPushSeq = this.realtimeHeadSeq(msg.seq, inlinePresent);
|
|
1468
|
-
if (client._v2Session && client._aid && (msg.seq !== undefined && msg.seq !== null || inlinePresent)) {
|
|
1610
|
+
if ((client._v2Session || inlinePresent) && client._aid && (msg.seq !== undefined && msg.seq !== null || inlinePresent)) {
|
|
1469
1611
|
if (legacyPushSeq === null) {
|
|
1470
1612
|
if (inlinePresent)
|
|
1471
1613
|
await this.recoverInvalidP2PRealtimeHead(`p2p:${client._aid}`, msg.seq);
|
|
@@ -1482,10 +1624,18 @@ export class MessageDeliveryEngine {
|
|
|
1482
1624
|
const ns = `p2p:${client._aid}`;
|
|
1483
1625
|
if (seq > 0)
|
|
1484
1626
|
client._seqTracker.updateMaxSeen(ns, seq);
|
|
1627
|
+
if (encryptedPush) {
|
|
1628
|
+
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1629
|
+
client._clientLog.debug(`P2P push triggering Forward: ns=${ns}, seq=${seq}, contiguous=${client._seqTracker.getContiguousSeq(ns)}`);
|
|
1630
|
+
this.fillP2pGap().catch((exc) => client._clientLog.warn(`background gap fill trigger failed: ${formatDeliveryError(exc)}`));
|
|
1631
|
+
}
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1485
1634
|
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1486
1635
|
client._clientLog.debug(`P2P push triggering Forward: ns=${ns}, seq=${seq}, contiguous=${client._seqTracker.getContiguousSeq(ns)}`);
|
|
1487
1636
|
this.fillP2pGap().catch((exc) => client._clientLog.warn(`background gap fill trigger failed: ${formatDeliveryError(exc)}`));
|
|
1488
1637
|
}
|
|
1638
|
+
await this.publishOrderedMessage('message.received', ns, seq, msg, 'push');
|
|
1489
1639
|
return;
|
|
1490
1640
|
}
|
|
1491
1641
|
else {
|
|
@@ -1493,6 +1643,8 @@ export class MessageDeliveryEngine {
|
|
|
1493
1643
|
await client._publishEncryptedPushMessage('message.received', 'message.undecryptable', '', seq ?? 0, msg, false);
|
|
1494
1644
|
return;
|
|
1495
1645
|
}
|
|
1646
|
+
if (this.isSelfSentP2PMessage(msg))
|
|
1647
|
+
return;
|
|
1496
1648
|
await client._publishAppEvent('message.received', msg, 'push');
|
|
1497
1649
|
}
|
|
1498
1650
|
}
|
|
@@ -1547,7 +1699,7 @@ export class MessageDeliveryEngine {
|
|
|
1547
1699
|
// 不能让 payload 路径直接推进旧 ordered publish 状态。
|
|
1548
1700
|
const inlinePresent = hasNonEmptyInlineMessage(msg.inline_message);
|
|
1549
1701
|
const legacyPushSeq = this.realtimeHeadSeq(seq, inlinePresent);
|
|
1550
|
-
if (client._v2Session && groupId && (seq !== undefined && seq !== null || inlinePresent)) {
|
|
1702
|
+
if ((client._v2Session || inlinePresent) && groupId && (seq !== undefined && seq !== null || inlinePresent)) {
|
|
1551
1703
|
if (legacyPushSeq === null) {
|
|
1552
1704
|
if (inlinePresent)
|
|
1553
1705
|
await this.recoverInvalidGroupRealtimeHead(groupId, `group:${groupId}`, seq);
|
|
@@ -1570,10 +1722,20 @@ export class MessageDeliveryEngine {
|
|
|
1570
1722
|
const ns = `group:${groupId}`;
|
|
1571
1723
|
if (seq > 0)
|
|
1572
1724
|
client._seqTracker.updateMaxSeen(ns, seq);
|
|
1725
|
+
if (encryptedPush) {
|
|
1726
|
+
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1727
|
+
client._clientLog.debug(`group push triggering Forward: group=${groupId}, seq=${seq}, contiguous=${client._seqTracker.getContiguousSeq(ns)}`);
|
|
1728
|
+
this.fillGroupGap(groupId).catch((exc) => client._clientLog.warn(`background gap fill trigger failed: ${formatDeliveryError(exc)}`));
|
|
1729
|
+
}
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1573
1732
|
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1574
1733
|
client._clientLog.debug(`group push triggering Forward: group=${groupId}, seq=${seq}, contiguous=${client._seqTracker.getContiguousSeq(ns)}`);
|
|
1575
1734
|
this.fillGroupGap(groupId).catch((exc) => client._clientLog.warn(`background gap fill trigger failed: ${formatDeliveryError(exc)}`));
|
|
1576
1735
|
}
|
|
1736
|
+
if (!this.isSelfSentGroupMessage(msg)) {
|
|
1737
|
+
await this.publishOrderedMessage('group.message_created', ns, seq, msg, 'group-push');
|
|
1738
|
+
}
|
|
1577
1739
|
return;
|
|
1578
1740
|
}
|
|
1579
1741
|
else {
|
|
@@ -2250,7 +2412,7 @@ export class MessageDeliveryEngine {
|
|
|
2250
2412
|
after_seq: state.ack,
|
|
2251
2413
|
limit: pageLimit,
|
|
2252
2414
|
...(headForward ? { max_pages: Math.max(2, requiredPages) } : order[0] === 'tail' ? { max_pages: requiredPages } : {}),
|
|
2253
|
-
},
|
|
2415
|
+
}, true, headForward || tailMissedDuringPull);
|
|
2254
2416
|
}
|
|
2255
2417
|
if (tailHead !== null && this.syncState(ns).ack < this.syncState(ns).tail - 1) {
|
|
2256
2418
|
this.scheduleRealtimeWindowForward(ns);
|
|
@@ -2327,7 +2489,7 @@ export class MessageDeliveryEngine {
|
|
|
2327
2489
|
after_seq: state.ack,
|
|
2328
2490
|
limit: pageLimit,
|
|
2329
2491
|
...(headForward ? { max_pages: Math.max(2, requiredPages) } : order[0] === 'tail' ? { max_pages: requiredPages } : {}),
|
|
2330
|
-
},
|
|
2492
|
+
}, true, headForward || tailMissedDuringPull);
|
|
2331
2493
|
}
|
|
2332
2494
|
if (tailHead !== null && this.syncState(ns).ack < this.syncState(ns).tail - 1) {
|
|
2333
2495
|
this.scheduleRealtimeWindowForward(ns, groupId);
|
|
@@ -2598,10 +2760,10 @@ export class MessageDeliveryEngine {
|
|
|
2598
2760
|
const pending = client._pendingOrderedMsgs.get(ns);
|
|
2599
2761
|
return !pending || pending.size === 0;
|
|
2600
2762
|
}
|
|
2601
|
-
async decodeInlineP2P(row) {
|
|
2763
|
+
async decodeInlineP2P(row, deferStatus) {
|
|
2602
2764
|
const client = this.runtime.client;
|
|
2603
2765
|
if (this.inlineRowKind(row) === 'v2') {
|
|
2604
|
-
const decrypted = await client._decryptV2PushMessage(row);
|
|
2766
|
+
const decrypted = await client._decryptV2PushMessage(row, deferStatus);
|
|
2605
2767
|
return isJsonObject(decrypted) ? decrypted : null;
|
|
2606
2768
|
}
|
|
2607
2769
|
const legacy = isJsonObject(row.legacy_v1) ? row.legacy_v1 : null;
|
|
@@ -2626,16 +2788,20 @@ export class MessageDeliveryEngine {
|
|
|
2626
2788
|
timestamp: row.t_server,
|
|
2627
2789
|
payload: payload,
|
|
2628
2790
|
encrypted: false,
|
|
2629
|
-
direction: String(row.direction ?? '').trim() || (fromAid && fromAid === client._aid ? 'outbound_sync' : 'inbound'),
|
|
2630
2791
|
};
|
|
2792
|
+
const direction = String(row.direction ?? '').trim();
|
|
2793
|
+
if (direction)
|
|
2794
|
+
result.direction = direction;
|
|
2795
|
+
else if (fromAid && fromAid !== client._aid)
|
|
2796
|
+
result.direction = 'inbound';
|
|
2631
2797
|
if (row.sender_device_id !== undefined)
|
|
2632
2798
|
result.sender_device_id = row.sender_device_id;
|
|
2633
2799
|
return result;
|
|
2634
2800
|
}
|
|
2635
|
-
async decodeInlineGroup(row, groupId, groupAid) {
|
|
2801
|
+
async decodeInlineGroup(row, groupId, groupAid, deferStatus) {
|
|
2636
2802
|
const client = this.runtime.client;
|
|
2637
2803
|
if (this.inlineRowKind(row) === 'v2') {
|
|
2638
|
-
const decrypted = await client._decryptV2PushMessage(row);
|
|
2804
|
+
const decrypted = await client._decryptV2PushMessage(row, deferStatus);
|
|
2639
2805
|
if (!isJsonObject(decrypted))
|
|
2640
2806
|
return null;
|
|
2641
2807
|
const decoded = decrypted;
|
|
@@ -2701,9 +2867,13 @@ export class MessageDeliveryEngine {
|
|
|
2701
2867
|
const snapshot = typeof tracker.snapshotNamespace === 'function' ? tracker.snapshotNamespace(ns) : null;
|
|
2702
2868
|
let delivered = false;
|
|
2703
2869
|
try {
|
|
2704
|
-
const
|
|
2705
|
-
|
|
2706
|
-
|
|
2870
|
+
const selfSync = (event === 'message.received' && this.isSelfSentP2PMessage(payload))
|
|
2871
|
+
|| (event === 'group.message_created' && this.isSelfSentGroupMessage(payload));
|
|
2872
|
+
if (!selfSync) {
|
|
2873
|
+
const published = client._publishAppEvent(event, payload, 'inline-push');
|
|
2874
|
+
if (isPromiseLike(published))
|
|
2875
|
+
await published;
|
|
2876
|
+
}
|
|
2707
2877
|
if (!this.inlineGenerationIsCurrent(generation))
|
|
2708
2878
|
return false;
|
|
2709
2879
|
delivered = true;
|
|
@@ -2740,6 +2910,34 @@ export class MessageDeliveryEngine {
|
|
|
2740
2910
|
return false;
|
|
2741
2911
|
}
|
|
2742
2912
|
}
|
|
2913
|
+
async commitInlineTerminalDecryptSeq(ns, seq, generation) {
|
|
2914
|
+
const client = this.runtime.client;
|
|
2915
|
+
const tracker = client._seqTracker;
|
|
2916
|
+
if (!this.inlineGenerationIsCurrent(generation) || !this.inlineStateIsCaughtUp(ns, seq))
|
|
2917
|
+
return false;
|
|
2918
|
+
const snapshot = typeof tracker.snapshotNamespace === 'function' ? tracker.snapshotNamespace(ns) : null;
|
|
2919
|
+
try {
|
|
2920
|
+
const needsPull = Boolean(tracker.onMessageSeq(ns, seq));
|
|
2921
|
+
tracker.commitTailWindow(ns, {
|
|
2922
|
+
windowStartSeq: seq,
|
|
2923
|
+
headSeq: seq,
|
|
2924
|
+
coveredThroughSeq: seq,
|
|
2925
|
+
pageLimit: 1,
|
|
2926
|
+
});
|
|
2927
|
+
const committed = this.syncState(ns);
|
|
2928
|
+
if (needsPull || committed.ack !== seq || committed.tail !== seq || committed.head !== seq) {
|
|
2929
|
+
throw new Error('inline realtime terminal cursor did not collapse after consume');
|
|
2930
|
+
}
|
|
2931
|
+
await client._commitSeqTrackerState(ns);
|
|
2932
|
+
return true;
|
|
2933
|
+
}
|
|
2934
|
+
catch (exc) {
|
|
2935
|
+
if (typeof tracker.restoreNamespaceSnapshot === 'function')
|
|
2936
|
+
tracker.restoreNamespaceSnapshot(ns, snapshot);
|
|
2937
|
+
client._clientLog.debug(`inline realtime terminal consume rolled back: ns=${ns} seq=${seq} err=${formatDeliveryError(exc)}`);
|
|
2938
|
+
return false;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2743
2941
|
async tryConsumeInlineP2P(head, ns, pushSeq, expectedVersion, inlineGeneration) {
|
|
2744
2942
|
const client = this.runtime.client;
|
|
2745
2943
|
if (!isJsonObject(head.inline_message))
|
|
@@ -2759,7 +2957,14 @@ export class MessageDeliveryEngine {
|
|
|
2759
2957
|
return false;
|
|
2760
2958
|
let committed = false;
|
|
2761
2959
|
try {
|
|
2762
|
-
const
|
|
2960
|
+
const decryptStatus = {};
|
|
2961
|
+
const decoded = await this.decodeInlineP2P(row, decryptStatus);
|
|
2962
|
+
if (!decoded && decryptStatus.terminal) {
|
|
2963
|
+
if (!await this.commitInlineTerminalDecryptSeq(ns, pushSeq, generation))
|
|
2964
|
+
return false;
|
|
2965
|
+
committed = this.markPendingP2PInlineAckCommitted(ns, pushSeq);
|
|
2966
|
+
return committed;
|
|
2967
|
+
}
|
|
2763
2968
|
if (!this.inlineGenerationIsCurrent(generation) || !decoded || !this.inlineStateIsCaughtUp(ns, pushSeq)
|
|
2764
2969
|
|| !this.decodedInlineMatches(head, decoded, pushSeq))
|
|
2765
2970
|
return false;
|
|
@@ -2797,7 +3002,14 @@ export class MessageDeliveryEngine {
|
|
|
2797
3002
|
return false;
|
|
2798
3003
|
let committed = false;
|
|
2799
3004
|
try {
|
|
2800
|
-
const
|
|
3005
|
+
const decryptStatus = {};
|
|
3006
|
+
const decoded = await this.decodeInlineGroup(row, groupId, groupAid || groupId, decryptStatus);
|
|
3007
|
+
if (!decoded && decryptStatus.terminal) {
|
|
3008
|
+
if (!await this.commitInlineTerminalDecryptSeq(ns, pushSeq, generation))
|
|
3009
|
+
return false;
|
|
3010
|
+
committed = this.markPendingGroupInlineAckCommitted(ns, pushSeq);
|
|
3011
|
+
return committed;
|
|
3012
|
+
}
|
|
2801
3013
|
if (!this.inlineGenerationIsCurrent(generation) || !decoded || !this.inlineStateIsCaughtUp(ns, pushSeq)
|
|
2802
3014
|
|| !this.decodedInlineMatches(head, decoded, pushSeq, groupId))
|
|
2803
3015
|
return false;
|
|
@@ -2916,20 +3128,24 @@ export class MessageDeliveryEngine {
|
|
|
2916
3128
|
const client = this.runtime.client;
|
|
2917
3129
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
2918
3130
|
return;
|
|
2919
|
-
if (!client._v2Session)
|
|
2920
|
-
return;
|
|
2921
3131
|
const pushData = isJsonObject(data) ? data : null;
|
|
2922
3132
|
const rawPushSeq = pushData?.seq;
|
|
2923
3133
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
2924
3134
|
if (!pushData || !ns)
|
|
2925
3135
|
return;
|
|
3136
|
+
const inlinePresent = hasNonEmptyInlineMessage(pushData.inline_message);
|
|
3137
|
+
if (!client._v2Session && !(expectedVersion === 'v1' && inlinePresent))
|
|
3138
|
+
return;
|
|
3139
|
+
if (this.isVolatileMessage(pushData)) {
|
|
3140
|
+
await client._v2E2EE.publishV2VolatileMessages([pushData], 'push', false);
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
2926
3143
|
if (String(pushData.kind ?? '').trim() === 'message.online_unread_hint') {
|
|
2927
3144
|
if (client._sessionOptions?.background_sync === false)
|
|
2928
3145
|
return;
|
|
2929
3146
|
this.enqueueOnlineUnreadP2pHint(pushData);
|
|
2930
3147
|
return;
|
|
2931
3148
|
}
|
|
2932
|
-
const inlinePresent = hasNonEmptyInlineMessage(pushData.inline_message);
|
|
2933
3149
|
const pushSeq = this.realtimeHeadSeq(rawPushSeq, inlinePresent);
|
|
2934
3150
|
if (pushSeq === null) {
|
|
2935
3151
|
if (inlinePresent) {
|
|
@@ -3026,8 +3242,8 @@ export class MessageDeliveryEngine {
|
|
|
3026
3242
|
const client = this.runtime.client;
|
|
3027
3243
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
3028
3244
|
return;
|
|
3029
|
-
if (!isJsonObject(data)
|
|
3030
|
-
client._clientLog.debug(
|
|
3245
|
+
if (!isJsonObject(data)) {
|
|
3246
|
+
client._clientLog.debug('_onRawGroupV2MessageCreated skipped: is_object=false');
|
|
3031
3247
|
return;
|
|
3032
3248
|
}
|
|
3033
3249
|
const d = data;
|
|
@@ -3035,6 +3251,8 @@ export class MessageDeliveryEngine {
|
|
|
3035
3251
|
const groupId = typeof d.group_id === 'string' ? d.group_id.trim() : '';
|
|
3036
3252
|
const rawSeq = d.seq;
|
|
3037
3253
|
const inlinePresent = hasNonEmptyInlineMessage(d.inline_message);
|
|
3254
|
+
if (!client._v2Session && !(expectedVersion === 'v1' && inlinePresent))
|
|
3255
|
+
return;
|
|
3038
3256
|
const seq = this.realtimeHeadSeq(rawSeq, inlinePresent);
|
|
3039
3257
|
if (!groupId) {
|
|
3040
3258
|
client._clientLog.debug(`_onRawGroupV2MessageCreated skipped: group=${groupId || '<empty>'}, seq=${String(d.seq ?? '')}`);
|
|
@@ -3611,6 +3829,11 @@ export class MessageDeliveryEngine {
|
|
|
3611
3829
|
client._pendingOrderedMsgs.delete(ns);
|
|
3612
3830
|
return false;
|
|
3613
3831
|
}
|
|
3832
|
+
if (event === 'message.received' && this.isSelfSentP2PMessage(payload)) {
|
|
3833
|
+
this.markPublishedSeq(ns, seqNum);
|
|
3834
|
+
client._clientLog.debug(`publish ordered skipped current-device self-sync: ns=${ns}, seq=${seqNum}`);
|
|
3835
|
+
return false;
|
|
3836
|
+
}
|
|
3614
3837
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
3615
3838
|
if (seqNum > contig && seqNum !== contig + 1) {
|
|
3616
3839
|
client._clientLog.debug(`publish ordered enqueue(gap): event=${event}, ns=${ns}, seq=${seqNum}, contiguous=${contig}`);
|
|
@@ -3624,6 +3847,11 @@ export class MessageDeliveryEngine {
|
|
|
3624
3847
|
client._clientLog.debug(`publish ordered skipped after-drain duplicate: event=${event}, ns=${ns}, seq=${seqNum}`);
|
|
3625
3848
|
return false;
|
|
3626
3849
|
}
|
|
3850
|
+
if (event === 'message.received' && this.isSelfSentP2PMessage(payload)) {
|
|
3851
|
+
this.markPublishedSeq(ns, seqNum);
|
|
3852
|
+
client._clientLog.debug(`publish pulled skipped current-device self-sync: ns=${ns}, seq=${seqNum}`);
|
|
3853
|
+
return false;
|
|
3854
|
+
}
|
|
3627
3855
|
const queue = client._pendingOrderedMsgs.get(ns);
|
|
3628
3856
|
queue?.delete(seqNum);
|
|
3629
3857
|
if (queue && queue.size === 0)
|
|
@@ -3671,6 +3899,11 @@ export class MessageDeliveryEngine {
|
|
|
3671
3899
|
client._pendingOrderedMsgs.delete(ns);
|
|
3672
3900
|
return false;
|
|
3673
3901
|
}
|
|
3902
|
+
if (event === 'message.received' && this.isSelfSentP2PMessage(payload)) {
|
|
3903
|
+
this.markPublishedSeq(ns, seqNum);
|
|
3904
|
+
client._clientLog.debug('publish pulled skipped current-device self-sync');
|
|
3905
|
+
return source === 'inline_push';
|
|
3906
|
+
}
|
|
3674
3907
|
await this.drainOrderedMessages(ns, seqNum, true, persist, source);
|
|
3675
3908
|
if (pullScoped)
|
|
3676
3909
|
this.ensurePullOperationCurrent(ns);
|