@agentunion/fastaun-browser 0.5.6 → 0.5.9
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 +104 -0
- package/_packed_docs/CHANGELOG.md +104 -0
- package/_packed_docs/INDEX.md +12 -11
- package/_packed_docs/KITE_DOCS_GUIDE.md +4 -3
- package/_packed_docs/agent.md/SCHEMA.md +83 -58
- package/_packed_docs/agent.md/examples/codeagent-claudecode.md +1 -1
- package/_packed_docs/agent.md/examples/openclaw-lobster.md +1 -1
- package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +1 -1
- package/_packed_docs/agent.md/examples//347/276/244/347/273/204-/345/274/200/345/217/221/345/233/242/351/230/237.md +21 -0
- package/_packed_docs/audit/AUN/346/234/215/345/212/241Go/345/214/226/351/207/215/346/236/204/347/262/276/347/273/206/345/214/226/346/272/220/347/240/201/345/256/241/346/237/245-20260718.md +3 -3
- package/_packed_docs/aun/345/205/254/347/275/221/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +7 -0
- 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 +422 -331
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +598 -398
- package/_packed_docs/group-message-rpc-alignment-gaps.md +741 -0
- package/_packed_docs/message-online-push-alignment.md +572 -0
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1 -1
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +10 -10
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +2 -2
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +30 -10
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +23 -20
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +3 -3
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +5 -6
- package/_packed_docs/sdk/09-group-rpc-manual.md +11 -7
- package/_packed_docs/sdk/09-message-rpc-manual.md +5 -3
- package/_packed_docs/sdk/09-storage-rpc-manual.md +14 -3
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +19 -17
- package/_packed_docs/sdk/CHANGELOG-0.5.6.md +73 -0
- package/_packed_docs/sdk/INDEX.md +22 -22
- package/_packed_docs/sdk/README.md +6 -5
- package/dist/agent-md-schema.d.ts +14 -0
- package/dist/agent-md-schema.d.ts.map +1 -0
- package/dist/agent-md-schema.js +103 -0
- package/dist/agent-md-schema.js.map +1 -0
- package/dist/agent-md.d.ts +7 -0
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +230 -10
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +5 -1
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +24 -4
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts +2 -0
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +29 -7
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +1 -0
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +30253 -21955
- package/dist/cert-utils.d.ts +1 -0
- package/dist/cert-utils.d.ts.map +1 -1
- package/dist/cert-utils.js +36 -16
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +40 -5
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +719 -221
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +2 -2
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.d.ts +15 -1
- package/dist/client/lifecycle.d.ts.map +1 -1
- package/dist/client/lifecycle.js +463 -151
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +25 -6
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +407 -123
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/runtime.d.ts +1 -3
- package/dist/client/runtime.d.ts.map +1 -1
- package/dist/client/runtime.js +4 -7
- package/dist/client/runtime.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +20 -2
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +434 -112
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +22 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +500 -116
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +23 -1
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +55 -7
- package/dist/facades.js.map +1 -1
- package/dist/group-id.d.ts.map +1 -1
- package/dist/group-id.js +2 -17
- package/dist/group-id.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +8 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +23 -1
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +8 -1
- package/dist/transport.js.map +1 -1
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +8 -14
- package/dist/validators.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizeSlotId, slotIsolationKey } from '../config.js';
|
|
2
|
-
import { ClientSignatureError, ConnectionError, PermissionError, ValidationError } from '../errors.js';
|
|
2
|
+
import { ClientSignatureError, ConnectionError, PermissionError, TimeoutError, ValidationError } from '../errors.js';
|
|
3
3
|
import { validateAIDFormat, validateGroupIDFormat } from '../validators.js';
|
|
4
4
|
import { p1363ToDer, pemToArrayBuffer, uint8ToBase64 } from '../crypto.js';
|
|
5
5
|
import { ConnectionState, isJsonObject, } from '../types.js';
|
|
@@ -71,6 +71,7 @@ const SIGNED_METHODS = new Set([
|
|
|
71
71
|
'group.dissolve', 'group.suspend', 'group.resume',
|
|
72
72
|
]);
|
|
73
73
|
const SIGNING_KEY_CACHE_MAX = 32;
|
|
74
|
+
const IDENTITY_ADMISSION_RETRY_DELAYS_MS = [0, 50, 100, 200];
|
|
74
75
|
const signingKeyCache = new Map();
|
|
75
76
|
const certFingerprintCache = new Map();
|
|
76
77
|
function cacheGet(cache, key) {
|
|
@@ -119,9 +120,17 @@ async function signingCertFingerprint(certPem) {
|
|
|
119
120
|
return await cached;
|
|
120
121
|
}
|
|
121
122
|
const PULL_GATE_STALE_MS = 30000;
|
|
123
|
+
const PULL_GATE_OPERATION_TIMEOUT_MS = 3000;
|
|
124
|
+
function sameIdentityAdmissionRejection(error, original) {
|
|
125
|
+
const errorCode = Number(error?.code);
|
|
126
|
+
const originalCode = Number(original?.code);
|
|
127
|
+
const errorMessage = String(error instanceof Error ? error.message : error?.message ?? '').trim().toLowerCase();
|
|
128
|
+
const originalMessage = String(original instanceof Error ? original.message : original?.message ?? '').trim().toLowerCase();
|
|
129
|
+
return errorCode === -32003 && originalCode === -32003 && errorMessage === originalMessage;
|
|
130
|
+
}
|
|
122
131
|
const NON_IDEMPOTENT_TIMEOUT = 35;
|
|
123
132
|
const NON_IDEMPOTENT_METHODS = new Set([
|
|
124
|
-
'message.send', 'group.send', 'group.create', 'group.invite',
|
|
133
|
+
'message.send', 'mail.send', 'group.send', 'group.create', 'group.invite',
|
|
125
134
|
'group.kick', 'group.remove_member', 'group.leave', 'group.dissolve',
|
|
126
135
|
'group.set_settings',
|
|
127
136
|
'group.update_announcement', 'group.update_rules',
|
|
@@ -168,7 +177,9 @@ function isTransientV2PullFallbackError(error) {
|
|
|
168
177
|
export class RpcPipeline {
|
|
169
178
|
runtime;
|
|
170
179
|
pullGateStates = new Map();
|
|
171
|
-
|
|
180
|
+
inlineRealtimeScopes = new Set();
|
|
181
|
+
pullGeneration = 0;
|
|
182
|
+
pullInvalidationWait = null;
|
|
172
183
|
constructor(runtime) {
|
|
173
184
|
this.runtime = runtime;
|
|
174
185
|
}
|
|
@@ -198,6 +209,8 @@ export class RpcPipeline {
|
|
|
198
209
|
};
|
|
199
210
|
const skipSendResultEnvelope = Boolean(p._skip_send_result_envelope);
|
|
200
211
|
delete p._skip_send_result_envelope;
|
|
212
|
+
const deferGroupReadyPostprocess = Boolean(p._defer_group_ready_postprocess);
|
|
213
|
+
delete p._defer_group_ready_postprocess;
|
|
201
214
|
if (method === 'message.send') {
|
|
202
215
|
const encrypt = p.encrypt !== undefined ? p.encrypt : true;
|
|
203
216
|
delete p.encrypt;
|
|
@@ -249,15 +262,11 @@ export class RpcPipeline {
|
|
|
249
262
|
}
|
|
250
263
|
const pullGateKey = this.pullGateKeyForCall(method, p);
|
|
251
264
|
if (pullGateKey) {
|
|
252
|
-
|
|
253
|
-
client._clientLog.debug(`pull skipped while processing pull response: method=${method} key=${pullGateKey}`);
|
|
254
|
-
return this.emptyPullResultForCall(method);
|
|
255
|
-
}
|
|
256
|
-
return await this.runPullSerialized(pullGateKey, async () => await this.callImplInner(method, p, skipSendResultEnvelope), this.pullGateBackgroundForCall(method, p));
|
|
265
|
+
return await this.runPullSerialized(pullGateKey, async () => await this.callImplInner(method, p, skipSendResultEnvelope, deferGroupReadyPostprocess), rpcBackground && this.pullGateBackgroundForCall(method, p));
|
|
257
266
|
}
|
|
258
|
-
return await runWithRpcPriority(() => this.callImplInner(method, p, skipSendResultEnvelope));
|
|
267
|
+
return await runWithRpcPriority(() => this.callImplInner(method, p, skipSendResultEnvelope, deferGroupReadyPostprocess));
|
|
259
268
|
}
|
|
260
|
-
async callImplInner(method, p, skipSendResultEnvelope = false) {
|
|
269
|
+
async callImplInner(method, p, skipSendResultEnvelope = false, deferGroupReadyPostprocess = false) {
|
|
261
270
|
const client = this.runtime.client;
|
|
262
271
|
if (method === 'message.v2.pull' || (method === 'message.pull' && hasMatchingV2Session(client))) {
|
|
263
272
|
try {
|
|
@@ -265,7 +274,10 @@ export class RpcPipeline {
|
|
|
265
274
|
client._clientLog.debug(`call route: ${method} -> V2 pull`);
|
|
266
275
|
if (p.window_mode === 'tail')
|
|
267
276
|
return await client._v2E2EE.pullV2TailInternal(p);
|
|
268
|
-
const messages = await client._pullV2(Number(p.after_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, {
|
|
277
|
+
const messages = await client._pullV2(Number(p.after_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, {
|
|
278
|
+
gateLocked: true,
|
|
279
|
+
force: p.force === true,
|
|
280
|
+
});
|
|
269
281
|
return { messages };
|
|
270
282
|
}
|
|
271
283
|
catch (exc) {
|
|
@@ -290,9 +302,11 @@ export class RpcPipeline {
|
|
|
290
302
|
if (p.window_mode === 'tail')
|
|
291
303
|
return await client._v2E2EE.pullGroupV2TailInternal(p);
|
|
292
304
|
const hasExplicitAfterSeq = 'after_seq' in p || 'after_message_seq' in p;
|
|
293
|
-
const cursorParams =
|
|
294
|
-
const ownsCursor = Object.keys(cursorParams).length === 0 ||
|
|
295
|
-
const pullOpts = {
|
|
305
|
+
const cursorParams = this.explicitGroupCursorParams(p);
|
|
306
|
+
const ownsCursor = Object.keys(cursorParams).length === 0 || this.groupCursorTargetsCurrentInstance(cursorParams);
|
|
307
|
+
const pullOpts = {
|
|
308
|
+
gateLocked: true,
|
|
309
|
+
};
|
|
296
310
|
if (hasExplicitAfterSeq)
|
|
297
311
|
pullOpts.explicitAfterSeq = true;
|
|
298
312
|
if (p.force === true)
|
|
@@ -317,8 +331,8 @@ export class RpcPipeline {
|
|
|
317
331
|
if ((method === 'group.v2.ack' || method === 'group.ack_messages') && p.group_id && (method === 'group.v2.ack' || hasMatchingV2Session(client))) {
|
|
318
332
|
await client._ensureV2SessionReady('group.ack_messages');
|
|
319
333
|
client._clientLog.debug(`call route: ${method} -> V2 ack`);
|
|
320
|
-
const cursorParams =
|
|
321
|
-
const ownsCursor = Object.keys(cursorParams).length === 0 ||
|
|
334
|
+
const cursorParams = this.explicitGroupCursorParams(p);
|
|
335
|
+
const ownsCursor = Object.keys(cursorParams).length === 0 || this.groupCursorTargetsCurrentInstance(cursorParams);
|
|
322
336
|
if (!ownsCursor) {
|
|
323
337
|
return await client._rawGroupAckMessages(p);
|
|
324
338
|
}
|
|
@@ -327,11 +341,15 @@ export class RpcPipeline {
|
|
|
327
341
|
await this.applyClientSignature(method, p);
|
|
328
342
|
const callTimeout = NON_IDEMPOTENT_METHODS.has(method) ? NON_IDEMPOTENT_TIMEOUT : undefined;
|
|
329
343
|
const rpcBackground = client._backgroundRpcDepth > 0;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
344
|
+
const invokeTransport = () => {
|
|
345
|
+
const request = callTimeout
|
|
346
|
+
? client._transport.call(method, p, callTimeout, undefined, rpcBackground)
|
|
347
|
+
: client._transport.call(method, p, undefined, undefined, rpcBackground);
|
|
348
|
+
return this.bindActivePullCancellation(method, p, request);
|
|
349
|
+
};
|
|
350
|
+
let result = await this.transportCallWithIdentityRecovery(invokeTransport, method, p);
|
|
333
351
|
const sendResultModeSource = result;
|
|
334
|
-
result = await this.postprocessResult(method, p, result);
|
|
352
|
+
result = await this.postprocessResult(method, p, result, { skipGroupState: deferGroupReadyPostprocess });
|
|
335
353
|
if (!skipSendResultEnvelope) {
|
|
336
354
|
result = client._delivery.attachSendResultEnvelope(method, p, result, Boolean(p.encrypted), sendResultModeSource);
|
|
337
355
|
}
|
|
@@ -531,8 +549,8 @@ export class RpcPipeline {
|
|
|
531
549
|
const mode = method === 'message.history' ? 'history' : params.window_mode === 'tail' ? 'tail' : 'forward';
|
|
532
550
|
const cursor = mode === 'history'
|
|
533
551
|
? `before=${String(params.before_seq)}`
|
|
534
|
-
: `after=${String(params.after_seq ?? 0)}
|
|
535
|
-
return `p2p:${client._aid}
|
|
552
|
+
: `after=${String(params.after_seq ?? 0)}`;
|
|
553
|
+
return `p2p:${client._aid}|mode=${mode}|cursor=${cursor}|force=${String(Boolean(params.force))}|limit=${String(params.limit)}`;
|
|
536
554
|
}
|
|
537
555
|
if (method === 'group.pull' || method === 'group.v2.pull' || method === 'group.history') {
|
|
538
556
|
const gid = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
@@ -541,13 +559,17 @@ export class RpcPipeline {
|
|
|
541
559
|
const mode = method === 'group.history' ? 'history' : params.window_mode === 'tail' ? 'tail' : 'forward';
|
|
542
560
|
const cursor = mode === 'history'
|
|
543
561
|
? `before=${String(params.before_seq)}`
|
|
544
|
-
: `after=${String(params.after_seq ?? params.after_message_seq ?? 0)}
|
|
545
|
-
|
|
562
|
+
: `after=${String(params.after_seq ?? params.after_message_seq ?? 0)}`;
|
|
563
|
+
const explicitCursor = this.explicitGroupCursorParams(params);
|
|
564
|
+
const cursorSuffix = Object.keys(explicitCursor).length > 0
|
|
565
|
+
? `|cursor_params=${stableStringify(explicitCursor)}`
|
|
566
|
+
: '';
|
|
567
|
+
return `group:${gid}|mode=${mode}|cursor=${cursor}${cursorSuffix}|force=${String(Boolean(params.force))}|limit=${String(params.limit)}`;
|
|
546
568
|
}
|
|
547
569
|
if (method === 'group.pull_events') {
|
|
548
570
|
const gid = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
549
571
|
return gid
|
|
550
|
-
? `group_event:${gid}|forward|after=${String(params.after_event_seq ?? 0)}|limit=${String(params.limit)}`
|
|
572
|
+
? `group_event:${gid}|mode=forward|cursor=after=${String(params.after_event_seq ?? 0)}|force=${String(Boolean(params.force))}|limit=${String(params.limit)}`
|
|
551
573
|
: '';
|
|
552
574
|
}
|
|
553
575
|
return '';
|
|
@@ -561,23 +583,60 @@ export class RpcPipeline {
|
|
|
561
583
|
return true;
|
|
562
584
|
}
|
|
563
585
|
pullGateName(key) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (key.startsWith('group:'))
|
|
567
|
-
return 'group_message';
|
|
568
|
-
if (key.startsWith('group_event:'))
|
|
569
|
-
return 'group_event';
|
|
570
|
-
return `other:${key}`;
|
|
586
|
+
void key;
|
|
587
|
+
return 'pull';
|
|
571
588
|
}
|
|
572
589
|
pullGateState(key) {
|
|
573
590
|
const name = this.pullGateName(key);
|
|
574
591
|
let state = this.pullGateStates.get(name);
|
|
575
592
|
if (!state) {
|
|
576
|
-
state = { active: null, foreground: [], background: [], byKey: new Map() };
|
|
593
|
+
state = { active: null, foreground: [], background: [], byKey: new Map(), lifecycle: new Map(), watchdog: null };
|
|
577
594
|
this.pullGateStates.set(name, state);
|
|
578
595
|
}
|
|
596
|
+
this.startPullGateWatchdog(state, name);
|
|
579
597
|
return state;
|
|
580
598
|
}
|
|
599
|
+
pullGateOperationTimeoutMs() {
|
|
600
|
+
const client = this.runtime.client;
|
|
601
|
+
const configured = Number(client._PULL_GATE_OPERATION_TIMEOUT_MS
|
|
602
|
+
?? client._PULL_GATE_TIMEOUT_MS
|
|
603
|
+
?? client._PULL_GATE_QUEUE_TIMEOUT_MS);
|
|
604
|
+
return Number.isFinite(configured) && configured > 0 ? configured : PULL_GATE_OPERATION_TIMEOUT_MS;
|
|
605
|
+
}
|
|
606
|
+
startPullGateWatchdog(state, name) {
|
|
607
|
+
if (state.watchdog)
|
|
608
|
+
return;
|
|
609
|
+
const interval = Math.min(50, Math.max(1, Math.floor(this.pullGateOperationTimeoutMs() / 4)));
|
|
610
|
+
const timer = setInterval(() => this.checkPullGate(state, name), interval);
|
|
611
|
+
state.watchdog = timer;
|
|
612
|
+
timer.unref?.();
|
|
613
|
+
}
|
|
614
|
+
stopPullGateWatchdogs() {
|
|
615
|
+
for (const state of this.pullGateStates.values()) {
|
|
616
|
+
if (state.watchdog === null)
|
|
617
|
+
continue;
|
|
618
|
+
clearInterval(state.watchdog);
|
|
619
|
+
state.watchdog = null;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
checkPullGate(state, name) {
|
|
623
|
+
const active = state.active;
|
|
624
|
+
if (!active) {
|
|
625
|
+
this.drainPullGate(state);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (!active.pullingStartedAt || Date.now() - active.pullingStartedAt < this.pullGateOperationTimeoutMs())
|
|
629
|
+
return;
|
|
630
|
+
if (active.timedOut)
|
|
631
|
+
return;
|
|
632
|
+
active.timedOut = true;
|
|
633
|
+
active.invalidated = true;
|
|
634
|
+
this.pullGeneration += 1;
|
|
635
|
+
active.cancel?.();
|
|
636
|
+
const err = new TimeoutError(`pull gate timeout: ${active.namespace}`, { retryable: true });
|
|
637
|
+
this.runtime.client._clientLog?.warn(`pull gate watchdog timeout: gate=${name} key=${active.key}`);
|
|
638
|
+
active.reject(err);
|
|
639
|
+
}
|
|
581
640
|
realtimePullNamespace(key) {
|
|
582
641
|
for (const marker of ['|tail|', '|forward|']) {
|
|
583
642
|
const index = key.indexOf(marker);
|
|
@@ -590,6 +649,22 @@ export class RpcPipeline {
|
|
|
590
649
|
const index = key.indexOf('|');
|
|
591
650
|
return index < 0 ? key : key.slice(0, index);
|
|
592
651
|
}
|
|
652
|
+
bindActivePullCancellation(method, params, request) {
|
|
653
|
+
const cancel = request.cancel;
|
|
654
|
+
const key = this.pullGateKeyForCall(method, params);
|
|
655
|
+
if (typeof cancel !== 'function' || !key)
|
|
656
|
+
return request;
|
|
657
|
+
const state = this.pullGateStates.get(this.pullGateName(key));
|
|
658
|
+
const active = state?.active;
|
|
659
|
+
if (!active || active.key !== key || active.timedOut || active.invalidated)
|
|
660
|
+
return request;
|
|
661
|
+
const boundCancel = () => cancel.call(request);
|
|
662
|
+
active.cancel = boundCancel;
|
|
663
|
+
return request.finally(() => {
|
|
664
|
+
if (active.cancel === boundCancel)
|
|
665
|
+
active.cancel = null;
|
|
666
|
+
});
|
|
667
|
+
}
|
|
593
668
|
isPullResponseProcessing(key) {
|
|
594
669
|
const responseKeys = this.runtime.client._pullResponseKeys;
|
|
595
670
|
return Boolean(key) && (responseKeys?.get(this.pullScopeKey(key)) ?? 0) > 0;
|
|
@@ -599,33 +674,12 @@ export class RpcPipeline {
|
|
|
599
674
|
return { events: [], count: 0 };
|
|
600
675
|
return { messages: [], count: 0 };
|
|
601
676
|
}
|
|
602
|
-
trackBypassedRealtimePull(key, delta) {
|
|
603
|
-
const ns = this.realtimePullNamespace(key);
|
|
604
|
-
if (!ns)
|
|
605
|
-
return;
|
|
606
|
-
const next = Math.max(0, (this.bypassedRealtimePulls.get(ns) ?? 0) + delta);
|
|
607
|
-
if (next === 0)
|
|
608
|
-
this.bypassedRealtimePulls.delete(ns);
|
|
609
|
-
else
|
|
610
|
-
this.bypassedRealtimePulls.set(ns, next);
|
|
611
|
-
}
|
|
612
|
-
async executeBypassedPull(key, operation, background) {
|
|
613
|
-
this.trackBypassedRealtimePull(key, 1);
|
|
614
|
-
try {
|
|
615
|
-
return await this.executePullOperation(operation, background);
|
|
616
|
-
}
|
|
617
|
-
finally {
|
|
618
|
-
this.trackBypassedRealtimePull(key, -1);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
677
|
hasPendingRealtimePull(ns) {
|
|
622
678
|
const normalized = String(ns ?? '').trim();
|
|
623
679
|
if (!normalized || (!normalized.startsWith('p2p:') && !normalized.startsWith('group:')))
|
|
624
680
|
return false;
|
|
625
681
|
if (this.isPullResponseProcessing(normalized))
|
|
626
682
|
return true;
|
|
627
|
-
if ((this.bypassedRealtimePulls.get(normalized) ?? 0) > 0)
|
|
628
|
-
return true;
|
|
629
683
|
const state = this.pullGateStates.get(this.pullGateName(normalized));
|
|
630
684
|
if (!state)
|
|
631
685
|
return false;
|
|
@@ -638,6 +692,36 @@ export class RpcPipeline {
|
|
|
638
692
|
|| state.foreground.some(matches)
|
|
639
693
|
|| state.background.some(matches);
|
|
640
694
|
}
|
|
695
|
+
hasPullActivity(namespace, includePending = true) {
|
|
696
|
+
const ns = String(namespace ?? '').trim();
|
|
697
|
+
if ((!ns.startsWith('p2p:') && !ns.startsWith('group:'))
|
|
698
|
+
|| this.inlineRealtimeScopes.has(ns))
|
|
699
|
+
return true;
|
|
700
|
+
const client = this.runtime.client;
|
|
701
|
+
if ((client._pullResponseKeys?.get?.(ns) ?? 0) > 0)
|
|
702
|
+
return true;
|
|
703
|
+
const pendingP2p = client._pendingP2pPullUpper;
|
|
704
|
+
const pendingGroup = client._pendingGroupPullUpper;
|
|
705
|
+
if (includePending && ((pendingP2p?.get(ns) ?? 0) > 0 || (pendingGroup?.get(ns) ?? 0) > 0
|
|
706
|
+
|| client._delivery?.hasPendingPull?.(ns)))
|
|
707
|
+
return true;
|
|
708
|
+
for (const marker of client._gapFillDone ?? []) {
|
|
709
|
+
const value = String(marker ?? '').trim();
|
|
710
|
+
if (ns.startsWith('p2p:') && (value.startsWith('p2p_pull:') || value.startsWith('p2p_pull_retry:')))
|
|
711
|
+
return true;
|
|
712
|
+
if (ns.startsWith('group:') && value.startsWith(`group_pull:${ns}`))
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
for (const [key, gate] of client._pullGates?.entries?.() ?? []) {
|
|
716
|
+
if (gate?.inflight && this.pullScopeKey(key) === ns)
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
const state = this.pullGateStates.get(this.pullGateName(ns));
|
|
720
|
+
if (!state)
|
|
721
|
+
return false;
|
|
722
|
+
const matches = (job) => job !== null && job.namespace === ns;
|
|
723
|
+
return matches(state.active) || state.foreground.some(matches) || state.background.some(matches);
|
|
724
|
+
}
|
|
641
725
|
async tryRunInlineRealtime(ns, seq, operation) {
|
|
642
726
|
const normalized = String(ns ?? '').trim();
|
|
643
727
|
if ((!normalized.startsWith('p2p:') && !normalized.startsWith('group:'))
|
|
@@ -645,8 +729,10 @@ export class RpcPipeline {
|
|
|
645
729
|
return { acquired: false };
|
|
646
730
|
}
|
|
647
731
|
const client = this.runtime.client;
|
|
648
|
-
if (this.
|
|
649
|
-
|
|
732
|
+
if (this.hasPullActivity(normalized)) {
|
|
733
|
+
return { acquired: false };
|
|
734
|
+
}
|
|
735
|
+
if (this.isPullResponseProcessing(normalized)) {
|
|
650
736
|
return { acquired: false };
|
|
651
737
|
}
|
|
652
738
|
for (const [key, gate] of client._pullGates?.entries?.() ?? []) {
|
|
@@ -660,28 +746,43 @@ export class RpcPipeline {
|
|
|
660
746
|
}
|
|
661
747
|
const guard = {
|
|
662
748
|
key,
|
|
749
|
+
namespace: normalized,
|
|
663
750
|
background: false,
|
|
751
|
+
running: true,
|
|
752
|
+
suspended: false,
|
|
664
753
|
inline: true,
|
|
665
754
|
operation,
|
|
666
755
|
promise: Promise.resolve(undefined),
|
|
667
756
|
resolve: () => { },
|
|
668
757
|
reject: () => { },
|
|
669
758
|
timer: null,
|
|
759
|
+
resume: null,
|
|
760
|
+
resumeResolve: null,
|
|
761
|
+
pullingStartedAt: 0,
|
|
762
|
+
timedOut: false,
|
|
763
|
+
invalidated: false,
|
|
764
|
+
cancel: null,
|
|
765
|
+
settled: Promise.resolve(),
|
|
766
|
+
settledResolve: null,
|
|
670
767
|
};
|
|
671
|
-
|
|
768
|
+
this.inlineRealtimeScopes.add(normalized);
|
|
672
769
|
try {
|
|
673
770
|
return { acquired: true, value: await operation() };
|
|
674
771
|
}
|
|
675
772
|
finally {
|
|
773
|
+
this.inlineRealtimeScopes.delete(normalized);
|
|
676
774
|
if (state.active === guard)
|
|
677
775
|
state.active = null;
|
|
678
776
|
this.drainPullGate(state);
|
|
777
|
+
this.runtime.client._delivery?.onPullGateIdle?.(normalized);
|
|
679
778
|
this.armQueuedPullTimers(state);
|
|
680
779
|
}
|
|
681
780
|
}
|
|
682
781
|
tryAcquirePullGate(key) {
|
|
683
782
|
if (!key)
|
|
684
783
|
return 0;
|
|
784
|
+
if (this.inlineRealtimeScopes.has(this.pullScopeKey(key)))
|
|
785
|
+
return null;
|
|
685
786
|
const serialized = this.pullGateStates.get(this.pullGateName(key));
|
|
686
787
|
if (serialized?.active?.inline === true)
|
|
687
788
|
return null;
|
|
@@ -709,97 +810,209 @@ export class RpcPipeline {
|
|
|
709
810
|
gate.inflight = false;
|
|
710
811
|
gate.startedAt = 0;
|
|
711
812
|
}
|
|
813
|
+
isPullGenerationCurrent(generation) {
|
|
814
|
+
return generation === this.pullGeneration;
|
|
815
|
+
}
|
|
816
|
+
invalidatePulls() {
|
|
817
|
+
if (this.pullInvalidationWait)
|
|
818
|
+
return this.pullInvalidationWait;
|
|
819
|
+
this.pullGeneration += 1;
|
|
820
|
+
const error = new Error('pull invalidated');
|
|
821
|
+
const waits = [];
|
|
822
|
+
for (const state of this.pullGateStates.values()) {
|
|
823
|
+
const jobs = new Set();
|
|
824
|
+
if (state.active)
|
|
825
|
+
jobs.add(state.active);
|
|
826
|
+
for (const job of state.foreground)
|
|
827
|
+
jobs.add(job);
|
|
828
|
+
for (const job of state.background)
|
|
829
|
+
jobs.add(job);
|
|
830
|
+
for (const job of jobs) {
|
|
831
|
+
job.invalidated = true;
|
|
832
|
+
if (!job.timedOut)
|
|
833
|
+
job.reject(error);
|
|
834
|
+
job.cancel?.();
|
|
835
|
+
job.resumeResolve?.();
|
|
836
|
+
job.resumeResolve = null;
|
|
837
|
+
if (job.running) {
|
|
838
|
+
waits.push(job.settled);
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
job.settledResolve?.();
|
|
842
|
+
job.settledResolve = null;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
state.foreground = state.foreground.filter((job) => !jobs.has(job));
|
|
846
|
+
state.background = state.background.filter((job) => !jobs.has(job));
|
|
847
|
+
for (const [key, job] of state.byKey.entries()) {
|
|
848
|
+
if (jobs.has(job) && !job.running)
|
|
849
|
+
state.byKey.delete(key);
|
|
850
|
+
}
|
|
851
|
+
this.drainPullGate(state);
|
|
852
|
+
}
|
|
853
|
+
const wait = Promise.all(waits).then(() => undefined);
|
|
854
|
+
let tracked;
|
|
855
|
+
tracked = wait.finally(() => {
|
|
856
|
+
if (this.pullInvalidationWait === tracked)
|
|
857
|
+
this.pullInvalidationWait = null;
|
|
858
|
+
});
|
|
859
|
+
this.pullInvalidationWait = tracked;
|
|
860
|
+
return tracked;
|
|
861
|
+
}
|
|
862
|
+
pullInvalidationInProgress() {
|
|
863
|
+
return this.pullInvalidationWait !== null;
|
|
864
|
+
}
|
|
712
865
|
async runPullSerialized(key, operation, background = false) {
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
return [];
|
|
866
|
+
if (this.pullInvalidationInProgress()) {
|
|
867
|
+
throw new Error('pull invalidated');
|
|
716
868
|
}
|
|
717
869
|
if (!key)
|
|
718
870
|
return await this.executePullOperation(operation, background);
|
|
719
871
|
const state = this.pullGateState(key);
|
|
720
|
-
const
|
|
721
|
-
const
|
|
872
|
+
const namespace = this.pullScopeKey(key);
|
|
873
|
+
const candidate = state.byKey.get(key);
|
|
874
|
+
const existing = candidate && !candidate.timedOut && !candidate.invalidated ? candidate : null;
|
|
722
875
|
if (existing) {
|
|
723
|
-
if (state.active
|
|
724
|
-
|
|
725
|
-
|
|
876
|
+
if (!background && existing.background && existing !== state.active) {
|
|
877
|
+
const index = state.background.indexOf(existing);
|
|
878
|
+
if (index >= 0)
|
|
879
|
+
state.background.splice(index, 1);
|
|
880
|
+
existing.background = false;
|
|
881
|
+
state.foreground.push(existing);
|
|
882
|
+
}
|
|
883
|
+
return await existing.promise;
|
|
726
884
|
}
|
|
727
885
|
let resolve;
|
|
728
886
|
let reject;
|
|
887
|
+
let settledResolve;
|
|
729
888
|
const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
|
|
730
|
-
const
|
|
889
|
+
const settled = new Promise((resolveSettled) => { settledResolve = resolveSettled; });
|
|
890
|
+
void promise.catch(() => { });
|
|
891
|
+
const job = {
|
|
892
|
+
key, namespace, background, running: false, suspended: false, operation, promise, resolve, reject, timer: null,
|
|
893
|
+
resume: null, resumeResolve: null,
|
|
894
|
+
pullingStartedAt: 0,
|
|
895
|
+
timedOut: false,
|
|
896
|
+
invalidated: false,
|
|
897
|
+
cancel: null,
|
|
898
|
+
settled,
|
|
899
|
+
settledResolve,
|
|
900
|
+
};
|
|
731
901
|
(background ? state.background : state.foreground).push(job);
|
|
732
902
|
state.byKey.set(key, job);
|
|
733
903
|
this.drainPullGate(state);
|
|
734
|
-
|
|
735
|
-
job.timer = setTimeout(() => this.bypassQueuedPull(state, job), timeoutMs);
|
|
736
|
-
}
|
|
904
|
+
this.checkPullGate(state, this.pullGateName(key));
|
|
737
905
|
return await promise;
|
|
738
906
|
}
|
|
739
907
|
async executePullOperation(operation, background) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
});
|
|
908
|
+
const client = this.runtime.client;
|
|
909
|
+
const hadPrevious = Object.prototype.hasOwnProperty.call(client, '_pullOperationGeneration');
|
|
910
|
+
const previous = client._pullOperationGeneration;
|
|
911
|
+
client._pullOperationGeneration = this.pullGeneration;
|
|
912
|
+
try {
|
|
913
|
+
if (background)
|
|
914
|
+
return await client._withBackgroundRpc(operation);
|
|
915
|
+
return await operation();
|
|
916
|
+
}
|
|
917
|
+
finally {
|
|
918
|
+
if (hadPrevious)
|
|
919
|
+
client._pullOperationGeneration = previous;
|
|
920
|
+
else
|
|
921
|
+
delete client._pullOperationGeneration;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
pullOperationIsCurrent() {
|
|
925
|
+
const generation = this.runtime.client._pullOperationGeneration;
|
|
926
|
+
return generation === undefined || this.isPullGenerationCurrent(generation);
|
|
927
|
+
}
|
|
928
|
+
throwIfPullInvalidated() {
|
|
929
|
+
if (!this.pullOperationIsCurrent())
|
|
930
|
+
throw new Error('pull invalidated');
|
|
764
931
|
}
|
|
765
|
-
|
|
766
|
-
if (
|
|
932
|
+
async yieldPullGate(key, nextKey, background) {
|
|
933
|
+
if (!key)
|
|
767
934
|
return;
|
|
768
|
-
const
|
|
769
|
-
const
|
|
770
|
-
|
|
935
|
+
const state = this.pullGateState(key);
|
|
936
|
+
const namespace = this.pullScopeKey(key);
|
|
937
|
+
const job = state.active?.key === key ? state.active : null;
|
|
938
|
+
if (!job || state.active !== job)
|
|
771
939
|
return;
|
|
772
|
-
|
|
940
|
+
const replacementKey = String(nextKey || key);
|
|
773
941
|
if (state.byKey.get(job.key) === job)
|
|
774
942
|
state.byKey.delete(job.key);
|
|
775
|
-
job.
|
|
776
|
-
|
|
777
|
-
|
|
943
|
+
job.key = replacementKey;
|
|
944
|
+
job.namespace = this.pullScopeKey(replacementKey);
|
|
945
|
+
if (background !== undefined)
|
|
946
|
+
job.background = background;
|
|
947
|
+
state.byKey.set(job.key, job);
|
|
948
|
+
state.lifecycle.set(namespace, 'pulled');
|
|
949
|
+
job.suspended = true;
|
|
950
|
+
job.resume = new Promise((resolve) => { job.resumeResolve = resolve; });
|
|
951
|
+
state.active = null;
|
|
952
|
+
(job.background ? state.background : state.foreground).push(job);
|
|
953
|
+
this.drainPullGate(state);
|
|
954
|
+
if (job.resume) {
|
|
955
|
+
await job.resume;
|
|
956
|
+
if (job.invalidated)
|
|
957
|
+
throw new Error('pull invalidated');
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
getPullLifecycle(namespace) {
|
|
961
|
+
const ns = this.pullScopeKey(namespace);
|
|
962
|
+
if (!ns)
|
|
963
|
+
return 'idle';
|
|
964
|
+
return this.pullGateStates.get(this.pullGateName(ns))?.lifecycle.get(ns) ?? 'idle';
|
|
965
|
+
}
|
|
966
|
+
setPullLifecycle(namespace, lifecycle) {
|
|
967
|
+
const ns = this.pullScopeKey(namespace);
|
|
968
|
+
if (!ns)
|
|
969
|
+
return;
|
|
970
|
+
const state = this.pullGateStates.get(this.pullGateName(ns));
|
|
971
|
+
if (!state || state.active?.namespace !== ns)
|
|
972
|
+
return;
|
|
973
|
+
state.lifecycle.set(ns, lifecycle);
|
|
778
974
|
}
|
|
779
975
|
drainPullGate(state) {
|
|
780
976
|
if (state.active)
|
|
781
977
|
return;
|
|
782
|
-
const
|
|
978
|
+
const takeRunnable = (queue) => {
|
|
979
|
+
const index = queue.findIndex((candidate) => !this.inlineRealtimeScopes.has(candidate.namespace));
|
|
980
|
+
return index >= 0 ? queue.splice(index, 1)[0] : undefined;
|
|
981
|
+
};
|
|
982
|
+
const job = takeRunnable(state.foreground) ?? takeRunnable(state.background);
|
|
783
983
|
if (!job)
|
|
784
984
|
return;
|
|
785
985
|
state.active = job;
|
|
786
986
|
if (job.timer)
|
|
787
987
|
clearTimeout(job.timer);
|
|
788
|
-
|
|
988
|
+
job.pullingStartedAt = Date.now();
|
|
989
|
+
job.timedOut = false;
|
|
990
|
+
state.lifecycle.set(job.namespace, 'pulling');
|
|
991
|
+
const resumeResolve = job.resumeResolve;
|
|
992
|
+
job.resume = null;
|
|
993
|
+
job.resumeResolve = null;
|
|
994
|
+
if (job.suspended) {
|
|
995
|
+
job.suspended = false;
|
|
996
|
+
resumeResolve?.();
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
job.running = true;
|
|
1000
|
+
void this.executePullOperation(job.operation, job.background).then((value) => { if (!job.timedOut && !job.invalidated)
|
|
1001
|
+
job.resolve(value); }, (error) => { if (!job.timedOut && !job.invalidated)
|
|
1002
|
+
job.reject(error); }).finally(() => {
|
|
1003
|
+
state.lifecycle.set(job.namespace, 'idle');
|
|
789
1004
|
if (state.active === job)
|
|
790
1005
|
state.active = null;
|
|
791
1006
|
if (state.byKey.get(job.key) === job)
|
|
792
1007
|
state.byKey.delete(job.key);
|
|
793
1008
|
this.drainPullGate(state);
|
|
1009
|
+
job.settledResolve?.();
|
|
1010
|
+
job.settledResolve = null;
|
|
1011
|
+
if (!job.invalidated)
|
|
1012
|
+
this.runtime.client._delivery?.onPullGateIdle?.(job.namespace);
|
|
794
1013
|
});
|
|
795
1014
|
}
|
|
796
|
-
armQueuedPullTimers(
|
|
797
|
-
const timeoutMs = Math.max(0, Number(this.runtime.client._PULL_GATE_QUEUE_TIMEOUT_MS ?? 3_000) || 0);
|
|
798
|
-
for (const job of [...state.foreground, ...state.background]) {
|
|
799
|
-
if (!job.timer)
|
|
800
|
-
job.timer = setTimeout(() => this.bypassQueuedPull(state, job), timeoutMs);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
1015
|
+
armQueuedPullTimers(_state) { }
|
|
803
1016
|
async rawCall(method, params, options) {
|
|
804
1017
|
const client = this.runtime.client;
|
|
805
1018
|
const payload = { ...(params ?? {}) };
|
|
@@ -810,19 +1023,49 @@ export class RpcPipeline {
|
|
|
810
1023
|
await this.applyClientSignature(method, payload);
|
|
811
1024
|
}
|
|
812
1025
|
const timeout = options?.timeout;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1026
|
+
const invokeTransport = () => {
|
|
1027
|
+
let request;
|
|
1028
|
+
if (rpcBackground)
|
|
1029
|
+
request = client._transport.call(method, payload, timeout, options?.trace, true);
|
|
1030
|
+
else if (options?.trace !== undefined)
|
|
1031
|
+
request = client._transport.call(method, payload, timeout, options.trace);
|
|
1032
|
+
else if (timeout !== undefined)
|
|
1033
|
+
request = client._transport.call(method, payload, timeout);
|
|
1034
|
+
else
|
|
1035
|
+
request = client._transport.call(method, payload);
|
|
1036
|
+
return this.bindActivePullCancellation(method, payload, request);
|
|
1037
|
+
};
|
|
1038
|
+
const result = await this.transportCallWithIdentityRecovery(invokeTransport, method, payload);
|
|
1039
|
+
this.throwIfPullInvalidated();
|
|
1040
|
+
return result;
|
|
1041
|
+
}
|
|
1042
|
+
async transportCallWithIdentityRecovery(operation, method, params) {
|
|
1043
|
+
try {
|
|
1044
|
+
return await operation();
|
|
818
1045
|
}
|
|
819
|
-
|
|
820
|
-
|
|
1046
|
+
catch (err) {
|
|
1047
|
+
const recover = this.runtime.client._recoverIdentityAdmission;
|
|
1048
|
+
if (typeof recover !== 'function' || !await recover.call(this.runtime.client, err, method, params))
|
|
1049
|
+
throw err;
|
|
1050
|
+
let lastError = err;
|
|
1051
|
+
for (const delay of IDENTITY_ADMISSION_RETRY_DELAYS_MS) {
|
|
1052
|
+
if (delay > 0)
|
|
1053
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1054
|
+
try {
|
|
1055
|
+
return await operation();
|
|
1056
|
+
}
|
|
1057
|
+
catch (retryError) {
|
|
1058
|
+
if (!sameIdentityAdmissionRejection(retryError, err))
|
|
1059
|
+
throw retryError;
|
|
1060
|
+
lastError = retryError;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
throw lastError;
|
|
821
1064
|
}
|
|
822
|
-
return await client._transport.call(method, payload);
|
|
823
1065
|
}
|
|
824
|
-
async postprocessResult(method, params, result) {
|
|
1066
|
+
async postprocessResult(method, params, result, options = {}) {
|
|
825
1067
|
const client = this.runtime.client;
|
|
1068
|
+
this.throwIfPullInvalidated();
|
|
826
1069
|
let next = result;
|
|
827
1070
|
if ((method === 'group.send' || method === 'group.v2.send') && isJsonObject(next)) {
|
|
828
1071
|
next = normalizeGroupMentionMode(next);
|
|
@@ -839,12 +1082,40 @@ export class RpcPipeline {
|
|
|
839
1082
|
next = await client._decryptMessageThoughts(next);
|
|
840
1083
|
}
|
|
841
1084
|
if (method === 'message.pull' && isJsonObject(next)) {
|
|
842
|
-
|
|
1085
|
+
const isTail = String(next.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
|
|
1086
|
+
if (!isTail && client._aid && client._seqTracker) {
|
|
1087
|
+
const ns = `p2p:${client._aid}`;
|
|
1088
|
+
const afterSeq = Math.max(0, Number(params.after_seq ?? 0) || 0);
|
|
1089
|
+
await client._delivery.commitPlainForwardPage(ns, afterSeq, 'message.pull', next);
|
|
1090
|
+
}
|
|
1091
|
+
else {
|
|
1092
|
+
this.postprocessMessagePull(params, next);
|
|
1093
|
+
}
|
|
843
1094
|
}
|
|
844
1095
|
if (method === 'group.pull' && isJsonObject(next)) {
|
|
845
|
-
|
|
1096
|
+
if (Array.isArray(next.messages)) {
|
|
1097
|
+
next.messages = next.messages.map((message) => normalizeGroupMentionMode(message));
|
|
1098
|
+
}
|
|
1099
|
+
const gid = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
1100
|
+
const explicitCursorParams = this.explicitGroupCursorParams(params);
|
|
1101
|
+
const ownsCursor = Object.keys(explicitCursorParams).length === 0
|
|
1102
|
+
|| this.groupCursorTargetsCurrentInstance(explicitCursorParams);
|
|
1103
|
+
const isTail = String(next.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
|
|
1104
|
+
if (!ownsCursor) {
|
|
1105
|
+
client._clientLog.debug(`group.pull external cursor skips local Forward commit: group=${gid}`);
|
|
1106
|
+
}
|
|
1107
|
+
else if (!isTail && gid && client._seqTracker) {
|
|
1108
|
+
const ns = `group:${gid}`;
|
|
1109
|
+
const afterSeq = Math.max(0, Number(params.after_message_seq ?? params.after_seq ?? 0) || 0);
|
|
1110
|
+
await client._delivery.commitPlainForwardPage(ns, afterSeq, 'group.pull', next, gid);
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
this.postprocessGroupPull(params, next);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
if (!options.skipGroupState) {
|
|
1117
|
+
next = await client._groupState.postprocessResult(method, params, next);
|
|
846
1118
|
}
|
|
847
|
-
next = await client._groupState.postprocessResult(method, params, next);
|
|
848
1119
|
return next;
|
|
849
1120
|
}
|
|
850
1121
|
_sleep(ms) {
|
|
@@ -960,6 +1231,19 @@ export class RpcPipeline {
|
|
|
960
1231
|
}
|
|
961
1232
|
return cursorParams;
|
|
962
1233
|
}
|
|
1234
|
+
explicitGroupCursorParams(params) {
|
|
1235
|
+
const value = params._group_cursor_params;
|
|
1236
|
+
if (!isJsonObject(value))
|
|
1237
|
+
return {};
|
|
1238
|
+
return { ...value };
|
|
1239
|
+
}
|
|
1240
|
+
groupCursorTargetsCurrentInstance(params) {
|
|
1241
|
+
const client = this.runtime.client;
|
|
1242
|
+
const deviceId = String(params.device_id ?? '').trim();
|
|
1243
|
+
const slotId = String(params.slot_id ?? '').trim();
|
|
1244
|
+
return (!deviceId || deviceId === (client._deviceId ?? ''))
|
|
1245
|
+
&& (!slotId || slotId === (client._slotId ?? ''));
|
|
1246
|
+
}
|
|
963
1247
|
validateMessageRecipient(toAid) {
|
|
964
1248
|
if (isGroupServiceAid(toAid)) {
|
|
965
1249
|
throw new ValidationError('message.send receiver cannot be group.{issuer}; use group.send instead');
|