@agentunion/fastaun 0.5.11 → 0.5.13
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 +46 -0
- package/_packed_docs/CHANGELOG.md +46 -0
- package/_packed_docs/INDEX.md +3 -3
- package/_packed_docs/KITE_DOCS_GUIDE.md +1 -1
- package/_packed_docs/agent.md/SCHEMA.md +84 -84
- 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 +8 -6
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +29 -15
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +21 -3
- package/_packed_docs/sdk/09-group-rpc-manual.md +29 -2
- package/dist/agent-md-schema.js +8 -4
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/client/delivery.d.ts +2 -3
- package/dist/client/delivery.js +69 -222
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +1 -0
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.js +18 -8
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +0 -1
- package/dist/client/v2-e2ee.js +43 -62
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +17 -0
- package/dist/client.js +269 -16
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +6 -0
- package/dist/facades.js +66 -0
- package/dist/facades.js.map +1 -1
- package/dist/seq-tracker.d.ts +2 -2
- package/dist/seq-tracker.js +11 -17
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +50 -20
- package/dist/tools/cross-sdk-agent.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.d.ts
CHANGED
|
@@ -118,6 +118,9 @@ export declare class AUNClient {
|
|
|
118
118
|
private _seqTrackerContext;
|
|
119
119
|
/** 惰性群同步:已同步过的 group_id 集合 */
|
|
120
120
|
private _groupSynced;
|
|
121
|
+
/** 群定向邀请按 group_id + invitee_aid 聚合去重。 */
|
|
122
|
+
private _groupInviteAggregates;
|
|
123
|
+
private _groupInviteAggregatesLoaded;
|
|
121
124
|
/** P2P 撤回去重:原始 message_id -> 时间戳,保证应用层只回调一次 */
|
|
122
125
|
_messageRecallSeen: Map<string, number>;
|
|
123
126
|
/** 群撤回去重:group_id|sorted(message_ids) -> 时间戳,保证应用层只回调一次 */
|
|
@@ -272,6 +275,7 @@ export declare class AUNClient {
|
|
|
272
275
|
private _checkIdentityAdmissionAfterConnect;
|
|
273
276
|
private _runGroupIdentityOperation;
|
|
274
277
|
createGroup(params?: RpcParams, options?: CreateGroupOptions): Promise<RpcResult>;
|
|
278
|
+
private _inviteInitialGroupMembers;
|
|
275
279
|
private _groupAgentMdContent;
|
|
276
280
|
private _uploadGroupAgentMd;
|
|
277
281
|
bindGroupAid(params?: RpcParams, options?: BindGroupAidOptions): Promise<RpcResult>;
|
|
@@ -367,6 +371,19 @@ export declare class AUNClient {
|
|
|
367
371
|
/** 将 SeqTracker 状态保存到 keystore */
|
|
368
372
|
private _saveSeqTrackerState;
|
|
369
373
|
private _persistSeq;
|
|
374
|
+
private static readonly GROUP_INVITE_METADATA_KEY;
|
|
375
|
+
private _groupInviteArray;
|
|
376
|
+
private _groupInvitePayload;
|
|
377
|
+
private _groupInviteKey;
|
|
378
|
+
private _loadGroupInviteAggregates;
|
|
379
|
+
private _persistGroupInviteAggregates;
|
|
380
|
+
private _onRawGroupInviteCreated;
|
|
381
|
+
private _onRawGroupInviteFinalized;
|
|
382
|
+
private _handleGroupInviteAggregate;
|
|
383
|
+
/** 连接建立/恢复后从事实列表补发尚未提示的群级邀请。 */
|
|
384
|
+
private _recoverGroupInvites;
|
|
385
|
+
/** 接受/拒绝/撤回成功后清理本地待处理聚合项。 */
|
|
386
|
+
private clearGroupInviteAggregate;
|
|
370
387
|
private _commitSeqTrackerState;
|
|
371
388
|
private _persistRepairedSeq;
|
|
372
389
|
private _clampAckSeq;
|
package/dist/client.js
CHANGED
|
@@ -124,7 +124,7 @@ const MAX_NOTIFY_PAYLOAD_SIZE = 64 * 1024;
|
|
|
124
124
|
// P1-23: 非幂等方法使用更长超时(35s),避免 SDK 10s 超时 < gateway 30s 处理时间
|
|
125
125
|
const NON_IDEMPOTENT_TIMEOUT_MS = 35_000;
|
|
126
126
|
const NON_IDEMPOTENT_METHODS = new Set([
|
|
127
|
-
'message.send', 'mail.send', 'group.send', 'group.create',
|
|
127
|
+
'message.send', 'mail.send', 'group.send', 'group.create',
|
|
128
128
|
'group.kick', 'group.remove_member', 'group.leave', 'group.dissolve',
|
|
129
129
|
'group.set_settings',
|
|
130
130
|
'group.update_announcement', 'group.update_rules',
|
|
@@ -150,6 +150,7 @@ const NON_IDEMPOTENT_METHODS = new Set([
|
|
|
150
150
|
'auth.create_aid', 'auth.renew_cert', 'auth.rekey',
|
|
151
151
|
'message.thought.put', 'group.thought.put',
|
|
152
152
|
'group.add_member', 'group.bind_group_aid', 'group.complete_transfer',
|
|
153
|
+
'group.accept_invite', 'group.reject_invite', 'group.revoke_invite',
|
|
153
154
|
'collab.create', 'collab.commit', 'collab.clone',
|
|
154
155
|
'collab.prune', 'collab.unregister',
|
|
155
156
|
'collab.tag.create', 'collab.tag.restore',
|
|
@@ -174,6 +175,8 @@ const SIGNED_METHODS = new Set([
|
|
|
174
175
|
'group.review_join_request',
|
|
175
176
|
'group.batch_review_join_request',
|
|
176
177
|
'group.request_join', 'group.use_invite_code',
|
|
178
|
+
'group.invite_member', 'group.list_my_invites', 'group.accept_invite',
|
|
179
|
+
'group.reject_invite', 'group.revoke_invite',
|
|
177
180
|
'group.thought.put',
|
|
178
181
|
'message.thought.put',
|
|
179
182
|
'group.set_settings',
|
|
@@ -468,6 +471,9 @@ export class AUNClient {
|
|
|
468
471
|
_seqTrackerContext = null;
|
|
469
472
|
/** 惰性群同步:已同步过的 group_id 集合 */
|
|
470
473
|
_groupSynced = new Set();
|
|
474
|
+
/** 群定向邀请按 group_id + invitee_aid 聚合去重。 */
|
|
475
|
+
_groupInviteAggregates = new Map();
|
|
476
|
+
_groupInviteAggregatesLoaded = false;
|
|
471
477
|
/** P2P 撤回去重:原始 message_id -> 时间戳,保证应用层只回调一次 */
|
|
472
478
|
_messageRecallSeen = new Map();
|
|
473
479
|
/** 群撤回去重:group_id|sorted(message_ids) -> 时间戳,保证应用层只回调一次 */
|
|
@@ -680,6 +686,9 @@ export class AUNClient {
|
|
|
680
686
|
this._dispatcher.subscribe('_raw.group.message_recalled', (data) => this._safeAsync(this._onRawGroupMessageRecalled(data)));
|
|
681
687
|
// 群组变更事件:透传并触发 V2 state/SPK 维护
|
|
682
688
|
this._dispatcher.subscribe('_raw.group.changed', (data) => this._onRawGroupChanged(data));
|
|
689
|
+
// 定向邀请底层事件:按群 + 目标 AID 聚合后向应用层只提示一次。
|
|
690
|
+
this._dispatcher.subscribe('_raw.group.invite_created', (data) => this._onRawGroupInviteCreated(data));
|
|
691
|
+
this._dispatcher.subscribe('_raw.group.invite_finalized', (data) => this._onRawGroupInviteFinalized(data));
|
|
683
692
|
// V2 state proposal 服务平面事件:owner/admin 负责确认或重新提案
|
|
684
693
|
this._dispatcher.subscribe('_raw.group.v2.state_proposed', (data) => this._safeAsync(this._onV2StateProposed(data)));
|
|
685
694
|
this._dispatcher.subscribe('_raw.group.v2.state_retry_needed', (data) => this._safeAsync(this._onV2StateRetryNeeded(data)));
|
|
@@ -1153,6 +1162,8 @@ export class AUNClient {
|
|
|
1153
1162
|
}
|
|
1154
1163
|
this._aidStore = store;
|
|
1155
1164
|
const payload = { ...params };
|
|
1165
|
+
const initialMembers = payload.members;
|
|
1166
|
+
delete payload.members;
|
|
1156
1167
|
if (payload.group_name === undefined && payload.groupName !== undefined) {
|
|
1157
1168
|
payload.group_name = payload.groupName;
|
|
1158
1169
|
delete payload.groupName;
|
|
@@ -1180,7 +1191,8 @@ export class AUNClient {
|
|
|
1180
1191
|
const result = { ...createdMap, ...boundMap, group: { ...createdGroup, ...boundGroup } };
|
|
1181
1192
|
const postprocessParams = { ...payload };
|
|
1182
1193
|
delete postprocessParams._defer_group_ready_postprocess;
|
|
1183
|
-
|
|
1194
|
+
const processed = await this._groupState.postprocessResult('group.create', postprocessParams, result);
|
|
1195
|
+
return await this._inviteInitialGroupMembers(processed, groupId, initialMembers);
|
|
1184
1196
|
}
|
|
1185
1197
|
const pendingKey = `create:${String(payload.group_name).trim().toLowerCase()}`;
|
|
1186
1198
|
const keystore = store._keystore;
|
|
@@ -1223,7 +1235,32 @@ export class AUNClient {
|
|
|
1223
1235
|
}
|
|
1224
1236
|
const postprocessParams = { ...payload };
|
|
1225
1237
|
delete postprocessParams._defer_group_ready_postprocess;
|
|
1226
|
-
|
|
1238
|
+
const processed = await this._groupState.postprocessResult('group.create', postprocessParams, result);
|
|
1239
|
+
return await this._inviteInitialGroupMembers(processed, String(group.group_id ?? groupAid).trim(), initialMembers);
|
|
1240
|
+
}
|
|
1241
|
+
async _inviteInitialGroupMembers(result, groupId, members) {
|
|
1242
|
+
const invites = [];
|
|
1243
|
+
const inviteErrors = [];
|
|
1244
|
+
const seen = new Set();
|
|
1245
|
+
if (Array.isArray(members)) {
|
|
1246
|
+
for (const item of members) {
|
|
1247
|
+
const aid = typeof item === 'string' ? item.trim() : (isJsonObject(item) ? String(item.aid ?? '').trim() : '');
|
|
1248
|
+
const memberType = isJsonObject(item) ? String(item.member_type ?? '').trim() : '';
|
|
1249
|
+
if (!aid || aid === String(this._aid ?? '').trim() || seen.has(aid))
|
|
1250
|
+
continue;
|
|
1251
|
+
seen.add(aid);
|
|
1252
|
+
const params = { group_id: groupId, invitee_aid: aid };
|
|
1253
|
+
if (memberType)
|
|
1254
|
+
params.member_type = memberType;
|
|
1255
|
+
try {
|
|
1256
|
+
invites.push(await this.group.inviteMember(params));
|
|
1257
|
+
}
|
|
1258
|
+
catch (err) {
|
|
1259
|
+
inviteErrors.push({ aid, error: err instanceof Error ? err.message : String(err) });
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
return { ...(isJsonObject(result) ? result : {}), invites, invite_errors: inviteErrors };
|
|
1227
1264
|
}
|
|
1228
1265
|
_groupAgentMdContent(groupAid, params, group) {
|
|
1229
1266
|
const explicit = params.group_agent_md ?? params.groupAgentMd ?? params.content;
|
|
@@ -2381,6 +2418,225 @@ export class AUNClient {
|
|
|
2381
2418
|
_persistSeq(ns, forceSeq) {
|
|
2382
2419
|
return this._delivery.persistSeq(ns, forceSeq);
|
|
2383
2420
|
}
|
|
2421
|
+
static GROUP_INVITE_METADATA_KEY = 'group_invites_pending_v1';
|
|
2422
|
+
_groupInviteArray(value) {
|
|
2423
|
+
if (Array.isArray(value))
|
|
2424
|
+
return value.map((item) => String(item ?? '').trim()).filter(Boolean);
|
|
2425
|
+
const item = String(value ?? '').trim();
|
|
2426
|
+
return item ? [item] : [];
|
|
2427
|
+
}
|
|
2428
|
+
_groupInvitePayload(data) {
|
|
2429
|
+
if (!isJsonObject(data))
|
|
2430
|
+
return null;
|
|
2431
|
+
const source = data;
|
|
2432
|
+
const groupId = normalizeGroupId(source.group_id ?? source.group_aid ?? source.groupId ?? source.groupAid);
|
|
2433
|
+
const inviteeAid = String(source.invitee_aid ?? source.inviteeAid ?? this._aid ?? '').trim();
|
|
2434
|
+
if (!groupId || !inviteeAid)
|
|
2435
|
+
return null;
|
|
2436
|
+
if (this._aid && inviteeAid.toLowerCase() !== this._aid.toLowerCase())
|
|
2437
|
+
return null;
|
|
2438
|
+
const inviteIds = this._groupInviteArray(source.invite_ids ?? source.invite_id ?? source.inviteIds);
|
|
2439
|
+
const inviterAids = this._groupInviteArray(source.inviter_aids ?? source.inviter_aid ?? source.inviterAids);
|
|
2440
|
+
return {
|
|
2441
|
+
...source,
|
|
2442
|
+
group_id: groupId,
|
|
2443
|
+
group_aid: String(source.group_aid ?? groupId).trim() || groupId,
|
|
2444
|
+
invite_ids: inviteIds,
|
|
2445
|
+
inviter_aids: inviterAids,
|
|
2446
|
+
invitee_aid: inviteeAid,
|
|
2447
|
+
role: String(source.role ?? 'member'),
|
|
2448
|
+
member_type: String(source.member_type ?? 'human'),
|
|
2449
|
+
status: String(source.status ?? 'pending').toLowerCase() || 'pending',
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
_groupInviteKey(payload) {
|
|
2453
|
+
return `${String(payload.group_id ?? '').trim().toLowerCase()}\u0000${String(payload.invitee_aid ?? '').trim().toLowerCase()}`;
|
|
2454
|
+
}
|
|
2455
|
+
_loadGroupInviteAggregates() {
|
|
2456
|
+
if (this._groupInviteAggregatesLoaded)
|
|
2457
|
+
return;
|
|
2458
|
+
this._groupInviteAggregatesLoaded = true;
|
|
2459
|
+
const aid = String(this._aid ?? '').trim();
|
|
2460
|
+
const store = this._tokenStore;
|
|
2461
|
+
if (!aid || !store || typeof store.loadMetadata !== 'function')
|
|
2462
|
+
return;
|
|
2463
|
+
try {
|
|
2464
|
+
const raw = store.loadMetadata(aid)?.[AUNClient.GROUP_INVITE_METADATA_KEY];
|
|
2465
|
+
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
2466
|
+
if (!isJsonObject(parsed))
|
|
2467
|
+
return;
|
|
2468
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
2469
|
+
if (isJsonObject(value) && String(value.status ?? 'pending').toLowerCase() === 'pending') {
|
|
2470
|
+
this._groupInviteAggregates.set(key, value);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
catch (exc) {
|
|
2475
|
+
this._clientLog.debug(`group invite metadata restore failed: ${formatCaughtError(exc)}`);
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
_persistGroupInviteAggregates() {
|
|
2479
|
+
const aid = String(this._aid ?? '').trim();
|
|
2480
|
+
const store = this._tokenStore;
|
|
2481
|
+
if (!aid || !store || typeof store.saveMetadata !== 'function')
|
|
2482
|
+
return;
|
|
2483
|
+
try {
|
|
2484
|
+
const values = {};
|
|
2485
|
+
for (const [key, value] of this._groupInviteAggregates)
|
|
2486
|
+
values[key] = value;
|
|
2487
|
+
store.saveMetadata(aid, { [AUNClient.GROUP_INVITE_METADATA_KEY]: JSON.stringify(values) });
|
|
2488
|
+
}
|
|
2489
|
+
catch (exc) {
|
|
2490
|
+
this._clientLog.debug(`group invite metadata persist failed: ${formatCaughtError(exc)}`);
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
async _onRawGroupInviteCreated(data) {
|
|
2494
|
+
await this._handleGroupInviteAggregate(data);
|
|
2495
|
+
}
|
|
2496
|
+
async _onRawGroupInviteFinalized(data) {
|
|
2497
|
+
const payload = this._groupInvitePayload(data);
|
|
2498
|
+
if (!payload)
|
|
2499
|
+
return;
|
|
2500
|
+
this._loadGroupInviteAggregates();
|
|
2501
|
+
const key = this._groupInviteKey(payload);
|
|
2502
|
+
const existing = this._groupInviteAggregates.get(key);
|
|
2503
|
+
if (!existing)
|
|
2504
|
+
return;
|
|
2505
|
+
const source = data;
|
|
2506
|
+
const status = String(payload.status ?? '').toLowerCase();
|
|
2507
|
+
const aggregateStatus = String(source.aggregate_status ?? source.aggregateStatus ?? '').toLowerCase();
|
|
2508
|
+
const remainingPending = source.remaining_pending === true
|
|
2509
|
+
|| source.remaining_pending === 1
|
|
2510
|
+
|| String(source.remaining_pending ?? '').toLowerCase() === 'true';
|
|
2511
|
+
const pending = source.pending === true
|
|
2512
|
+
|| source.pending === 1
|
|
2513
|
+
|| String(source.pending ?? '').toLowerCase() === 'true';
|
|
2514
|
+
const pendingIds = this._groupInviteArray(source.pending_invite_ids ?? source.pendingInviteIds);
|
|
2515
|
+
const pendingInviters = this._groupInviteArray(source.pending_inviter_aids ?? source.pendingInviterAids);
|
|
2516
|
+
const currentIds = this._groupInviteArray(existing.invite_ids);
|
|
2517
|
+
const inviteId = String(source.invite_id ?? '').trim();
|
|
2518
|
+
const inferredPartialRevoke = status === 'revoked' && inviteId
|
|
2519
|
+
&& currentIds.length > 1 && currentIds.includes(inviteId);
|
|
2520
|
+
const leavesPending = remainingPending || pending || status === 'pending'
|
|
2521
|
+
|| aggregateStatus === 'pending' || pendingIds.length > 0 || inferredPartialRevoke;
|
|
2522
|
+
if (!leavesPending) {
|
|
2523
|
+
this._groupInviteAggregates.delete(key);
|
|
2524
|
+
}
|
|
2525
|
+
else {
|
|
2526
|
+
const finalizedIds = new Set(remainingPending && inviteId
|
|
2527
|
+
? [inviteId]
|
|
2528
|
+
: this._groupInviteArray(payload.invite_ids));
|
|
2529
|
+
const nextIds = pendingIds.length > 0
|
|
2530
|
+
? pendingIds
|
|
2531
|
+
: this._groupInviteArray(existing.invite_ids).filter((id) => !finalizedIds.has(id));
|
|
2532
|
+
if (nextIds.length === 0) {
|
|
2533
|
+
this._groupInviteAggregates.delete(key);
|
|
2534
|
+
}
|
|
2535
|
+
else {
|
|
2536
|
+
existing.invite_ids = nextIds;
|
|
2537
|
+
existing.inviter_aids = pendingInviters.length > 0
|
|
2538
|
+
? pendingInviters
|
|
2539
|
+
: this._groupInviteArray(existing.inviter_aids);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
this._persistGroupInviteAggregates();
|
|
2543
|
+
}
|
|
2544
|
+
async _handleGroupInviteAggregate(data) {
|
|
2545
|
+
const payload = this._groupInvitePayload(data);
|
|
2546
|
+
if (!payload)
|
|
2547
|
+
return;
|
|
2548
|
+
this._loadGroupInviteAggregates();
|
|
2549
|
+
const status = String(payload.status ?? 'pending').toLowerCase();
|
|
2550
|
+
const key = this._groupInviteKey(payload);
|
|
2551
|
+
if (status !== 'pending') {
|
|
2552
|
+
if (this._groupInviteAggregates.delete(key))
|
|
2553
|
+
this._persistGroupInviteAggregates();
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
const existing = this._groupInviteAggregates.get(key);
|
|
2557
|
+
if (existing) {
|
|
2558
|
+
existing.invite_ids = [...new Set([
|
|
2559
|
+
...this._groupInviteArray(existing.invite_ids),
|
|
2560
|
+
...this._groupInviteArray(payload.invite_ids),
|
|
2561
|
+
])];
|
|
2562
|
+
existing.inviter_aids = [...new Set([
|
|
2563
|
+
...this._groupInviteArray(existing.inviter_aids),
|
|
2564
|
+
...this._groupInviteArray(payload.inviter_aids),
|
|
2565
|
+
])];
|
|
2566
|
+
this._persistGroupInviteAggregates();
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
const snapshot = {
|
|
2570
|
+
...payload,
|
|
2571
|
+
invite_ids: this._groupInviteArray(payload.invite_ids),
|
|
2572
|
+
inviter_aids: this._groupInviteArray(payload.inviter_aids),
|
|
2573
|
+
};
|
|
2574
|
+
this._groupInviteAggregates.set(key, snapshot);
|
|
2575
|
+
this._persistGroupInviteAggregates();
|
|
2576
|
+
this._dispatcher.enqueue('group.invite_received', { ...snapshot, invite_ids: [...this._groupInviteArray(snapshot.invite_ids)], inviter_aids: [...this._groupInviteArray(snapshot.inviter_aids)] });
|
|
2577
|
+
}
|
|
2578
|
+
/** 连接建立/恢复后从事实列表补发尚未提示的群级邀请。 */
|
|
2579
|
+
async _recoverGroupInvites(result) {
|
|
2580
|
+
let response = result;
|
|
2581
|
+
let cursor = '';
|
|
2582
|
+
const seen = new Set();
|
|
2583
|
+
do {
|
|
2584
|
+
if (response === undefined) {
|
|
2585
|
+
response = await this.call('group.list_my_invites', cursor ? { cursor } : {});
|
|
2586
|
+
}
|
|
2587
|
+
let items = [];
|
|
2588
|
+
let nextCursor = '';
|
|
2589
|
+
if (Array.isArray(response))
|
|
2590
|
+
items = response;
|
|
2591
|
+
else if (isJsonObject(response)) {
|
|
2592
|
+
const responseObject = response;
|
|
2593
|
+
if (Array.isArray(responseObject.items))
|
|
2594
|
+
items = responseObject.items;
|
|
2595
|
+
else if (Array.isArray(responseObject.invites))
|
|
2596
|
+
items = responseObject.invites;
|
|
2597
|
+
else if (isJsonObject(responseObject.data) && Array.isArray(responseObject.data.items))
|
|
2598
|
+
items = responseObject.data.items;
|
|
2599
|
+
nextCursor = String(responseObject.next_cursor ?? responseObject.nextCursor ?? responseObject.cursor ?? '');
|
|
2600
|
+
}
|
|
2601
|
+
for (const item of items) {
|
|
2602
|
+
const payload = this._groupInvitePayload(item);
|
|
2603
|
+
if (payload)
|
|
2604
|
+
seen.add(this._groupInviteKey(payload));
|
|
2605
|
+
await this._handleGroupInviteAggregate(item);
|
|
2606
|
+
}
|
|
2607
|
+
cursor = nextCursor;
|
|
2608
|
+
response = undefined;
|
|
2609
|
+
} while (cursor);
|
|
2610
|
+
this._loadGroupInviteAggregates();
|
|
2611
|
+
let stale = false;
|
|
2612
|
+
for (const key of this._groupInviteAggregates.keys()) {
|
|
2613
|
+
if (!seen.has(key)) {
|
|
2614
|
+
this._groupInviteAggregates.delete(key);
|
|
2615
|
+
stale = true;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
if (stale)
|
|
2619
|
+
this._persistGroupInviteAggregates();
|
|
2620
|
+
}
|
|
2621
|
+
/** 接受/拒绝/撤回成功后清理本地待处理聚合项。 */
|
|
2622
|
+
clearGroupInviteAggregate(params) {
|
|
2623
|
+
this._loadGroupInviteAggregates();
|
|
2624
|
+
const input = params ?? {};
|
|
2625
|
+
const inviteId = String(input.invite_id ?? input.inviteId ?? '').trim();
|
|
2626
|
+
const groupId = normalizeGroupId(input.group_id ?? input.group_aid ?? input.groupId ?? input.groupAid);
|
|
2627
|
+
const inviteeAid = String(input.invitee_aid ?? input.inviteeAid ?? this._aid ?? '').trim();
|
|
2628
|
+
let removed = false;
|
|
2629
|
+
for (const [key, value] of this._groupInviteAggregates) {
|
|
2630
|
+
const matchesKey = groupId && inviteeAid && key === `${groupId.toLowerCase()}\u0000${inviteeAid.toLowerCase()}`;
|
|
2631
|
+
const matchesInvite = inviteId && this._groupInviteArray(value.invite_ids).includes(inviteId);
|
|
2632
|
+
if (matchesKey || matchesInvite) {
|
|
2633
|
+
this._groupInviteAggregates.delete(key);
|
|
2634
|
+
removed = true;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
if (removed)
|
|
2638
|
+
this._persistGroupInviteAggregates();
|
|
2639
|
+
}
|
|
2384
2640
|
_commitSeqTrackerState(ns) {
|
|
2385
2641
|
return this._delivery.commitSeqTrackerState(ns);
|
|
2386
2642
|
}
|
|
@@ -2394,19 +2650,10 @@ export class AUNClient {
|
|
|
2394
2650
|
return this._delivery.clampAckParams(method, params);
|
|
2395
2651
|
}
|
|
2396
2652
|
_repairPushContiguousBound(ns, pushSeq, hasPayload, label) {
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
const shouldRepair = contig > pushSeq;
|
|
2402
|
-
if (!shouldRepair)
|
|
2403
|
-
return contig;
|
|
2404
|
-
const repairedTo = Math.max(0, pushSeq - 1);
|
|
2405
|
-
this._seqTracker.repairContiguousSeq(ns, repairedTo);
|
|
2406
|
-
const repaired = this._seqTracker.getContiguousSeq(ns);
|
|
2407
|
-
this._persistRepairedSeq(ns);
|
|
2408
|
-
this._clientLog.warn(`${label} push repaired contiguous_seq: ns=${ns} payload=${hasPayload} push_seq=${pushSeq} contiguous=${contig}->${repaired}`);
|
|
2409
|
-
return repaired;
|
|
2653
|
+
void pushSeq;
|
|
2654
|
+
void hasPayload;
|
|
2655
|
+
void label;
|
|
2656
|
+
return ns ? this._seqTracker.getContiguousSeq(ns) : 0;
|
|
2410
2657
|
}
|
|
2411
2658
|
// ── URL 辅助 ──────────────────────────────────────────────
|
|
2412
2659
|
/** 跨域时将 Gateway URL 替换为 peer 所在域的 Gateway URL */
|
|
@@ -2558,6 +2805,12 @@ export class AUNClient {
|
|
|
2558
2805
|
const hasExplicitBackgroundSync = Object.prototype.hasOwnProperty.call(params, 'background_sync');
|
|
2559
2806
|
const backgroundSyncEnabled = this._sessionOptions.background_sync !== false
|
|
2560
2807
|
&& (!isShortConnection || hasExplicitBackgroundSync);
|
|
2808
|
+
if (backgroundSyncEnabled) {
|
|
2809
|
+
// 连接建立/恢复后先从事实列表恢复定向邀请提示;失败不影响连接本身。
|
|
2810
|
+
this._safeAsync(this._recoverGroupInvites().catch((exc) => {
|
|
2811
|
+
this._clientLog.debug(`group invite recovery failed: ${formatCaughtError(exc)}`);
|
|
2812
|
+
}));
|
|
2813
|
+
}
|
|
2561
2814
|
if (!isShortConnection) {
|
|
2562
2815
|
await this._v2E2EE.onConnected({ backgroundSync: backgroundSyncEnabled });
|
|
2563
2816
|
this._assertReconnectOwner(reconnectOwner);
|