@agentunion/fastaun-browser 0.5.4 → 0.5.8
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 +177 -67
- package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/216/245/345/205/245/344/270/216/346/274/224/347/244/272/346/214/207/345/215/227.md +290 -0
- package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/234/215/345/212/241/346/236/266/346/236/204/344/270/216/350/257/246/347/273/206/345/256/236/347/216/260/350/256/241/345/210/222-codex.md +994 -0
- package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/234/215/345/212/241/350/277/220/347/273/264/344/270/216/345/217/221/345/270/203/346/214/207/345/215/227.md +144 -0
- package/_packed_docs/CHANGELOG.md +177 -67
- package/_packed_docs/INDEX.md +124 -65
- package/_packed_docs/KITE_DOCS_GUIDE.md +65 -30
- 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/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 +366 -0
- 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 +234 -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 +1095 -0
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1199 -0
- package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +6 -4
- 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 +58 -20
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +219 -247
- package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +14 -14
- package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +3 -3
- package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +165 -421
- package/_packed_docs/protocol/README.md +1 -0
- package/_packed_docs/protocol/aun-docs-guide.md +7 -4
- package/_packed_docs/protocol/index.md +25 -19
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +63 -18
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +22 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +93 -84
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +6 -2
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +73 -34
- 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 +21 -9
- package/_packed_docs/sdk/09-group-rpc-manual.md +319 -285
- package/_packed_docs/sdk/09-message-rpc-manual.md +144 -103
- package/_packed_docs/sdk/09-payload-reference.md +1 -1
- package/_packed_docs/sdk/09-storage-rpc-manual.md +14 -3
- package/_packed_docs/sdk/09-stream-rpc-manual.md +8 -8
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +34 -23
- package/_packed_docs/sdk/CHANGELOG-0.5.6.md +73 -0
- package/_packed_docs/sdk/INDEX.md +45 -37
- package/_packed_docs/sdk/README.md +11 -9
- package/_packed_docs//345/217/221/345/270/203/346/212/245/345/221/212-0.5.6.md +260 -0
- package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/344/273/243/347/240/201/345/256/241/346/237/245/351/227/256/351/242/230/346/270/205/345/215/225.md +229 -0
- package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/346/224/271/351/200/240/346/226/271/346/241/210.md +750 -0
- 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 +11 -5
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +295 -53
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +7 -7
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +46 -18
- 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 +31 -11
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +29920 -19719
- 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 +106 -11
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +1925 -374
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts.map +1 -1
- package/dist/client/group-state.js +27 -24
- 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 +477 -132
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/mention-mode.d.ts +7 -0
- package/dist/client/mention-mode.d.ts.map +1 -0
- package/dist/client/mention-mode.js +184 -0
- package/dist/client/mention-mode.js.map +1 -0
- package/dist/client/peers.d.ts +1 -1
- package/dist/client/peers.d.ts.map +1 -1
- package/dist/client/peers.js +26 -3
- package/dist/client/peers.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +34 -2
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +503 -99
- 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 +43 -2
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +931 -138
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +46 -15
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +785 -304
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -1
- package/dist/errors.js.map +1 -1
- package/dist/facades.d.ts +26 -1
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +142 -60
- 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/group-index.d.ts +6 -1
- package/dist/group-index.d.ts.map +1 -1
- package/dist/group-index.js +44 -25
- package/dist/group-index.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +10 -5
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.d.ts +0 -12
- package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.js +0 -60
- package/dist/keystore/indexeddb-identity-store.js.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +9 -5
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +3 -1
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +47 -2
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/register-flow.d.ts.map +1 -1
- package/dist/register-flow.js +28 -3
- package/dist/register-flow.js.map +1 -1
- package/dist/seq-tracker.d.ts +28 -8
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +224 -61
- package/dist/seq-tracker.js.map +1 -1
- package/dist/storage/vfs.d.ts +1 -0
- package/dist/storage/vfs.d.ts.map +1 -1
- package/dist/storage/vfs.js +26 -2
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +399 -11
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +12 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +226 -102
- package/dist/transport.js.map +1 -1
- package/dist/v2/session/session.d.ts.map +1 -1
- package/dist/v2/session/session.js +3 -4
- package/dist/v2/session/session.js.map +1 -1
- package/dist/v2/state/commitment.d.ts.map +1 -1
- package/dist/v2/state/commitment.js +1 -2
- package/dist/v2/state/commitment.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 +11 -9
- package/dist/group-resources.d.ts +0 -98
- package/dist/group-resources.d.ts.map +0 -1
- package/dist/group-resources.js +0 -635
- package/dist/group-resources.js.map +0 -1
- package/dist/keystore/indexeddb.d.ts +0 -179
- package/dist/keystore/indexeddb.d.ts.map +0 -1
- package/dist/keystore/indexeddb.js +0 -2031
- package/dist/keystore/indexeddb.js.map +0 -1
- package/dist/namespaces/auth.d.ts +0 -98
- package/dist/namespaces/auth.d.ts.map +0 -1
- package/dist/namespaces/auth.js +0 -992
- package/dist/namespaces/auth.js.map +0 -1
- package/dist/namespaces/custody.d.ts +0 -51
- package/dist/namespaces/custody.d.ts.map +0 -1
- package/dist/namespaces/custody.js +0 -302
- package/dist/namespaces/custody.js.map +0 -1
- package/dist/namespaces/meta.d.ts +0 -109
- package/dist/namespaces/meta.d.ts.map +0 -1
- package/dist/namespaces/meta.js +0 -549
- package/dist/namespaces/meta.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { normalizeSlotId, slotIsolationKey } from '../config.js';
|
|
2
|
-
import { ClientSignatureError, ConnectionError, PermissionError,
|
|
3
|
-
import { normalizeGroupId } from '../group-id.js';
|
|
2
|
+
import { ClientSignatureError, ConnectionError, PermissionError, TimeoutError, ValidationError } from '../errors.js';
|
|
4
3
|
import { validateAIDFormat, validateGroupIDFormat } from '../validators.js';
|
|
5
4
|
import { p1363ToDer, pemToArrayBuffer, uint8ToBase64 } from '../crypto.js';
|
|
6
5
|
import { ConnectionState, isJsonObject, } from '../types.js';
|
|
6
|
+
import { normalizeGroupMentionMode, normalizeGroupSettingsResponse } from './mention-mode.js';
|
|
7
7
|
const INTERNAL_ONLY_METHODS = new Set([
|
|
8
8
|
'auth.login1',
|
|
9
9
|
'auth.aid_login1',
|
|
@@ -22,11 +22,11 @@ const PROTECTED_HEADERS_METHODS = new Set([
|
|
|
22
22
|
const SIGNED_METHODS = new Set([
|
|
23
23
|
'message.send',
|
|
24
24
|
'message.v2.put_peer_pk', 'message.v2.bootstrap',
|
|
25
|
-
'message.v2.group_bootstrap', 'message.v2.pull',
|
|
25
|
+
'message.v2.group_bootstrap', 'message.v2.pull', 'message.history',
|
|
26
26
|
'message.v2.ack',
|
|
27
27
|
'group.send',
|
|
28
28
|
'group.v2.put_group_pk', 'group.v2.bootstrap',
|
|
29
|
-
'group.v2.send', 'group.v2.pull', 'group.v2.ack',
|
|
29
|
+
'group.v2.send', 'group.v2.pull', 'group.history', 'group.v2.ack',
|
|
30
30
|
'group.v2.propose_state', 'group.v2.confirm_state',
|
|
31
31
|
'group.v2.get_proposal',
|
|
32
32
|
'group.kick', 'group.add_member',
|
|
@@ -119,9 +119,10 @@ async function signingCertFingerprint(certPem) {
|
|
|
119
119
|
return await cached;
|
|
120
120
|
}
|
|
121
121
|
const PULL_GATE_STALE_MS = 30000;
|
|
122
|
+
const PULL_GATE_OPERATION_TIMEOUT_MS = 3000;
|
|
122
123
|
const NON_IDEMPOTENT_TIMEOUT = 35;
|
|
123
124
|
const NON_IDEMPOTENT_METHODS = new Set([
|
|
124
|
-
'message.send', 'group.send', 'group.create', 'group.invite',
|
|
125
|
+
'message.send', 'mail.send', 'group.send', 'group.create', 'group.invite',
|
|
125
126
|
'group.kick', 'group.remove_member', 'group.leave', 'group.dissolve',
|
|
126
127
|
'group.set_settings',
|
|
127
128
|
'group.update_announcement', 'group.update_rules',
|
|
@@ -167,6 +168,8 @@ function isTransientV2PullFallbackError(error) {
|
|
|
167
168
|
}
|
|
168
169
|
export class RpcPipeline {
|
|
169
170
|
runtime;
|
|
171
|
+
pullGateStates = new Map();
|
|
172
|
+
inlineRealtimeScopes = new Set();
|
|
170
173
|
constructor(runtime) {
|
|
171
174
|
this.runtime = runtime;
|
|
172
175
|
}
|
|
@@ -196,6 +199,8 @@ export class RpcPipeline {
|
|
|
196
199
|
};
|
|
197
200
|
const skipSendResultEnvelope = Boolean(p._skip_send_result_envelope);
|
|
198
201
|
delete p._skip_send_result_envelope;
|
|
202
|
+
const deferGroupReadyPostprocess = Boolean(p._defer_group_ready_postprocess);
|
|
203
|
+
delete p._defer_group_ready_postprocess;
|
|
199
204
|
if (method === 'message.send') {
|
|
200
205
|
const encrypt = p.encrypt !== undefined ? p.encrypt : true;
|
|
201
206
|
delete p.encrypt;
|
|
@@ -217,7 +222,8 @@ export class RpcPipeline {
|
|
|
217
222
|
if (encrypt) {
|
|
218
223
|
await client._ensureV2SessionReady('group.send', 'V2 session not initialized; encrypted group.send requires V2 (V1 E2EE removed)');
|
|
219
224
|
client._clientLog.debug('call route: group.send -> V2 encrypted send');
|
|
220
|
-
return await client._sendGroupV2(String(p.
|
|
225
|
+
return await client._sendGroupV2(String(p.group_id ?? p.group_aid ?? ''), p.payload ?? {}, {
|
|
226
|
+
groupAid: String(p.group_aid ?? '').trim() || undefined,
|
|
221
227
|
messageId: String(p.message_id ?? '') || undefined,
|
|
222
228
|
timestamp: p.timestamp,
|
|
223
229
|
protectedHeaders: client._protectedHeadersFromParams(p),
|
|
@@ -246,19 +252,22 @@ export class RpcPipeline {
|
|
|
246
252
|
}
|
|
247
253
|
const pullGateKey = this.pullGateKeyForCall(method, p);
|
|
248
254
|
if (pullGateKey) {
|
|
249
|
-
return await this.runPullSerialized(pullGateKey, async () =>
|
|
250
|
-
return await runWithRpcPriority(() => this.callImplInner(method, p, skipSendResultEnvelope));
|
|
251
|
-
});
|
|
255
|
+
return await this.runPullSerialized(pullGateKey, async () => await this.callImplInner(method, p, skipSendResultEnvelope, deferGroupReadyPostprocess), rpcBackground && this.pullGateBackgroundForCall(method, p));
|
|
252
256
|
}
|
|
253
|
-
return await runWithRpcPriority(() => this.callImplInner(method, p, skipSendResultEnvelope));
|
|
257
|
+
return await runWithRpcPriority(() => this.callImplInner(method, p, skipSendResultEnvelope, deferGroupReadyPostprocess));
|
|
254
258
|
}
|
|
255
|
-
async callImplInner(method, p, skipSendResultEnvelope = false) {
|
|
259
|
+
async callImplInner(method, p, skipSendResultEnvelope = false, deferGroupReadyPostprocess = false) {
|
|
256
260
|
const client = this.runtime.client;
|
|
257
261
|
if (method === 'message.v2.pull' || (method === 'message.pull' && hasMatchingV2Session(client))) {
|
|
258
262
|
try {
|
|
259
263
|
await client._ensureV2SessionReady('message.pull');
|
|
260
264
|
client._clientLog.debug(`call route: ${method} -> V2 pull`);
|
|
261
|
-
|
|
265
|
+
if (p.window_mode === 'tail')
|
|
266
|
+
return await client._v2E2EE.pullV2TailInternal(p);
|
|
267
|
+
const messages = await client._pullV2(Number(p.after_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, {
|
|
268
|
+
gateLocked: true,
|
|
269
|
+
force: p.force === true,
|
|
270
|
+
});
|
|
262
271
|
return { messages };
|
|
263
272
|
}
|
|
264
273
|
catch (exc) {
|
|
@@ -268,48 +277,71 @@ export class RpcPipeline {
|
|
|
268
277
|
client._clientLog.warn(`message.pull V2 route unavailable, fallback to plain pull: ${exc instanceof Error ? exc.message : String(exc)}`);
|
|
269
278
|
}
|
|
270
279
|
}
|
|
280
|
+
if (method === 'message.history') {
|
|
281
|
+
await client._ensureV2SessionReady('message.history');
|
|
282
|
+
return await client._v2E2EE.historyV2Internal(p);
|
|
283
|
+
}
|
|
271
284
|
if (method === 'message.v2.ack' || (method === 'message.ack' && hasMatchingV2Session(client))) {
|
|
272
285
|
await client._ensureV2SessionReady('message.ack');
|
|
273
286
|
client._clientLog.debug(`call route: ${method} -> V2 ack`);
|
|
274
|
-
return await client._ackV2(
|
|
287
|
+
return await client._ackV2((p.seq ?? p.up_to_seq));
|
|
275
288
|
}
|
|
276
289
|
if ((method === 'group.v2.pull' || method === 'group.pull') && p.group_id && (method === 'group.v2.pull' || hasMatchingV2Session(client))) {
|
|
277
290
|
await client._ensureV2SessionReady('group.pull');
|
|
278
291
|
client._clientLog.debug(`call route: ${method} -> V2 pull`);
|
|
292
|
+
if (p.window_mode === 'tail')
|
|
293
|
+
return await client._v2E2EE.pullGroupV2TailInternal(p);
|
|
279
294
|
const hasExplicitAfterSeq = 'after_seq' in p || 'after_message_seq' in p;
|
|
280
|
-
const cursorParams =
|
|
281
|
-
const ownsCursor = Object.keys(cursorParams).length === 0 ||
|
|
282
|
-
const pullOpts = {
|
|
295
|
+
const cursorParams = this.explicitGroupCursorParams(p);
|
|
296
|
+
const ownsCursor = Object.keys(cursorParams).length === 0 || this.groupCursorTargetsCurrentInstance(cursorParams);
|
|
297
|
+
const pullOpts = {
|
|
298
|
+
gateLocked: true,
|
|
299
|
+
};
|
|
283
300
|
if (hasExplicitAfterSeq)
|
|
284
301
|
pullOpts.explicitAfterSeq = true;
|
|
302
|
+
if (p.force === true)
|
|
303
|
+
pullOpts.force = true;
|
|
285
304
|
if (Object.keys(cursorParams).length > 0)
|
|
286
305
|
pullOpts.cursorParams = cursorParams;
|
|
287
306
|
if (!ownsCursor)
|
|
288
307
|
pullOpts.ownsCursor = false;
|
|
289
308
|
if (String(p.group_id ?? '').trim())
|
|
290
309
|
pullOpts.wireGroupId = String(p.group_id).trim();
|
|
291
|
-
|
|
310
|
+
if (String(p.group_aid ?? '').trim())
|
|
311
|
+
pullOpts.groupAid = String(p.group_aid).trim();
|
|
312
|
+
const messages = await client._pullGroupV2(String(p.group_id ?? p.group_aid), Number(p.after_seq ?? p.after_message_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, Object.keys(pullOpts).length > 0 ? pullOpts : undefined);
|
|
292
313
|
return { messages };
|
|
293
314
|
}
|
|
315
|
+
if (method === 'group.history') {
|
|
316
|
+
if (!String(p.group_id ?? p.group_aid ?? '').trim())
|
|
317
|
+
throw new ValidationError('group.history requires group_id');
|
|
318
|
+
await client._ensureV2SessionReady('group.history');
|
|
319
|
+
return await client._v2E2EE.historyGroupV2Internal(p);
|
|
320
|
+
}
|
|
294
321
|
if ((method === 'group.v2.ack' || method === 'group.ack_messages') && p.group_id && (method === 'group.v2.ack' || hasMatchingV2Session(client))) {
|
|
295
322
|
await client._ensureV2SessionReady('group.ack_messages');
|
|
296
323
|
client._clientLog.debug(`call route: ${method} -> V2 ack`);
|
|
297
|
-
const cursorParams =
|
|
298
|
-
const ownsCursor = Object.keys(cursorParams).length === 0 ||
|
|
324
|
+
const cursorParams = this.explicitGroupCursorParams(p);
|
|
325
|
+
const ownsCursor = Object.keys(cursorParams).length === 0 || this.groupCursorTargetsCurrentInstance(cursorParams);
|
|
299
326
|
if (!ownsCursor) {
|
|
300
327
|
return await client._rawGroupAckMessages(p);
|
|
301
328
|
}
|
|
302
|
-
return await client._ackGroupV2(
|
|
329
|
+
return await client._ackGroupV2((p.group_id ?? p.group_aid), (p.seq ?? p.msg_seq ?? p.up_to_seq), p.group_aid);
|
|
303
330
|
}
|
|
304
331
|
await this.applyClientSignature(method, p);
|
|
305
332
|
const callTimeout = NON_IDEMPOTENT_METHODS.has(method) ? NON_IDEMPOTENT_TIMEOUT : undefined;
|
|
306
333
|
const rpcBackground = client._backgroundRpcDepth > 0;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
334
|
+
const invokeTransport = () => {
|
|
335
|
+
const request = callTimeout
|
|
336
|
+
? client._transport.call(method, p, callTimeout, undefined, rpcBackground)
|
|
337
|
+
: client._transport.call(method, p, undefined, undefined, rpcBackground);
|
|
338
|
+
return this.bindActivePullCancellation(method, p, request);
|
|
339
|
+
};
|
|
340
|
+
let result = await this.transportCallWithIdentityRecovery(invokeTransport, method, p);
|
|
341
|
+
const sendResultModeSource = result;
|
|
342
|
+
result = await this.postprocessResult(method, p, result, { skipGroupState: deferGroupReadyPostprocess });
|
|
311
343
|
if (!skipSendResultEnvelope) {
|
|
312
|
-
result = client._delivery.attachSendResultEnvelope(method, p, result, Boolean(p.encrypted));
|
|
344
|
+
result = client._delivery.attachSendResultEnvelope(method, p, result, Boolean(p.encrypted), sendResultModeSource);
|
|
313
345
|
}
|
|
314
346
|
return result;
|
|
315
347
|
}
|
|
@@ -501,22 +533,240 @@ export class RpcPipeline {
|
|
|
501
533
|
// ── Pull Gate(序列化同一 key 的并发 pull)──────────────────
|
|
502
534
|
pullGateKeyForCall(method, params) {
|
|
503
535
|
const client = this.runtime.client;
|
|
504
|
-
if (method === 'message.pull' || method === 'message.v2.pull') {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
536
|
+
if (method === 'message.pull' || method === 'message.v2.pull' || method === 'message.history') {
|
|
537
|
+
if (!client._aid)
|
|
538
|
+
return '';
|
|
539
|
+
const mode = method === 'message.history' ? 'history' : params.window_mode === 'tail' ? 'tail' : 'forward';
|
|
540
|
+
const cursor = mode === 'history'
|
|
541
|
+
? `before=${String(params.before_seq)}`
|
|
542
|
+
: `after=${String(params.after_seq ?? 0)}|force=${String(Boolean(params.force))}`;
|
|
543
|
+
return `p2p:${client._aid}|${mode}|${cursor}|limit=${String(params.limit)}`;
|
|
544
|
+
}
|
|
545
|
+
if (method === 'group.pull' || method === 'group.v2.pull' || method === 'group.history') {
|
|
546
|
+
const gid = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
547
|
+
if (!gid)
|
|
548
|
+
return '';
|
|
549
|
+
const mode = method === 'group.history' ? 'history' : params.window_mode === 'tail' ? 'tail' : 'forward';
|
|
550
|
+
const cursor = mode === 'history'
|
|
551
|
+
? `before=${String(params.before_seq)}`
|
|
552
|
+
: `after=${String(params.after_seq ?? params.after_message_seq ?? 0)}|force=${String(Boolean(params.force))}`;
|
|
553
|
+
return `group:${gid}|${mode}|${cursor}|limit=${String(params.limit)}`;
|
|
510
554
|
}
|
|
511
555
|
if (method === 'group.pull_events') {
|
|
512
|
-
const gid = String(params.
|
|
513
|
-
return gid
|
|
556
|
+
const gid = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
557
|
+
return gid
|
|
558
|
+
? `group_event:${gid}|forward|after=${String(params.after_event_seq ?? 0)}|limit=${String(params.limit)}`
|
|
559
|
+
: '';
|
|
560
|
+
}
|
|
561
|
+
return '';
|
|
562
|
+
}
|
|
563
|
+
pullGateBackgroundForCall(method, params) {
|
|
564
|
+
if (method === 'message.history' || method === 'group.history')
|
|
565
|
+
return false;
|
|
566
|
+
if (['message.pull', 'message.v2.pull', 'group.pull', 'group.v2.pull'].includes(method)) {
|
|
567
|
+
return params.window_mode !== 'tail';
|
|
568
|
+
}
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
pullGateName(key) {
|
|
572
|
+
void key;
|
|
573
|
+
return 'pull';
|
|
574
|
+
}
|
|
575
|
+
pullGateState(key) {
|
|
576
|
+
const name = this.pullGateName(key);
|
|
577
|
+
let state = this.pullGateStates.get(name);
|
|
578
|
+
if (!state) {
|
|
579
|
+
state = { active: null, foreground: [], background: [], byKey: new Map(), lifecycle: new Map(), watchdog: null };
|
|
580
|
+
this.pullGateStates.set(name, state);
|
|
581
|
+
this.startPullGateWatchdog(state, name);
|
|
582
|
+
}
|
|
583
|
+
return state;
|
|
584
|
+
}
|
|
585
|
+
pullGateOperationTimeoutMs() {
|
|
586
|
+
const client = this.runtime.client;
|
|
587
|
+
const configured = Number(client._PULL_GATE_OPERATION_TIMEOUT_MS
|
|
588
|
+
?? client._PULL_GATE_TIMEOUT_MS
|
|
589
|
+
?? client._PULL_GATE_QUEUE_TIMEOUT_MS);
|
|
590
|
+
return Number.isFinite(configured) && configured > 0 ? configured : PULL_GATE_OPERATION_TIMEOUT_MS;
|
|
591
|
+
}
|
|
592
|
+
startPullGateWatchdog(state, name) {
|
|
593
|
+
if (state.watchdog)
|
|
594
|
+
return;
|
|
595
|
+
const interval = Math.min(50, Math.max(1, Math.floor(this.pullGateOperationTimeoutMs() / 4)));
|
|
596
|
+
const timer = setInterval(() => this.checkPullGate(state, name), interval);
|
|
597
|
+
state.watchdog = timer;
|
|
598
|
+
timer.unref?.();
|
|
599
|
+
}
|
|
600
|
+
checkPullGate(state, name) {
|
|
601
|
+
const active = state.active;
|
|
602
|
+
if (!active) {
|
|
603
|
+
this.drainPullGate(state);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
if (!active.pullingStartedAt || Date.now() - active.pullingStartedAt < this.pullGateOperationTimeoutMs())
|
|
607
|
+
return;
|
|
608
|
+
if (active.timedOut)
|
|
609
|
+
return;
|
|
610
|
+
active.timedOut = true;
|
|
611
|
+
active.cancel?.();
|
|
612
|
+
const err = new TimeoutError(`pull gate timeout: ${active.namespace}`, { retryable: true });
|
|
613
|
+
this.runtime.client._clientLog?.warn(`pull gate watchdog timeout: gate=${name} key=${active.key}`);
|
|
614
|
+
state.lifecycle.set(active.namespace, 'idle');
|
|
615
|
+
if (state.active === active)
|
|
616
|
+
state.active = null;
|
|
617
|
+
if (state.byKey.get(active.key) === active)
|
|
618
|
+
state.byKey.delete(active.key);
|
|
619
|
+
active.resolve = () => { };
|
|
620
|
+
active.reject(err);
|
|
621
|
+
this.drainPullGate(state);
|
|
622
|
+
this.runtime.client._delivery?.onPullGateIdle?.(active.namespace);
|
|
623
|
+
}
|
|
624
|
+
realtimePullNamespace(key) {
|
|
625
|
+
for (const marker of ['|tail|', '|forward|']) {
|
|
626
|
+
const index = key.indexOf(marker);
|
|
627
|
+
if (index > 0)
|
|
628
|
+
return key.slice(0, index);
|
|
514
629
|
}
|
|
515
630
|
return '';
|
|
516
631
|
}
|
|
632
|
+
pullScopeKey(key) {
|
|
633
|
+
const index = key.indexOf('|');
|
|
634
|
+
return index < 0 ? key : key.slice(0, index);
|
|
635
|
+
}
|
|
636
|
+
bindActivePullCancellation(method, params, request) {
|
|
637
|
+
const cancel = request.cancel;
|
|
638
|
+
const key = this.pullGateKeyForCall(method, params);
|
|
639
|
+
if (typeof cancel !== 'function' || !key)
|
|
640
|
+
return request;
|
|
641
|
+
const state = this.pullGateStates.get(this.pullGateName(key));
|
|
642
|
+
const active = state?.active;
|
|
643
|
+
if (!active || active.namespace !== this.pullScopeKey(key) || active.timedOut)
|
|
644
|
+
return request;
|
|
645
|
+
const boundCancel = () => cancel.call(request);
|
|
646
|
+
active.cancel = boundCancel;
|
|
647
|
+
return request.finally(() => {
|
|
648
|
+
if (active.cancel === boundCancel)
|
|
649
|
+
active.cancel = null;
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
isPullResponseProcessing(key) {
|
|
653
|
+
const responseKeys = this.runtime.client._pullResponseKeys;
|
|
654
|
+
return Boolean(key) && (responseKeys?.get(this.pullScopeKey(key)) ?? 0) > 0;
|
|
655
|
+
}
|
|
656
|
+
emptyPullResultForCall(method) {
|
|
657
|
+
if (method === 'group.pull_events')
|
|
658
|
+
return { events: [], count: 0 };
|
|
659
|
+
return { messages: [], count: 0 };
|
|
660
|
+
}
|
|
661
|
+
hasPendingRealtimePull(ns) {
|
|
662
|
+
const normalized = String(ns ?? '').trim();
|
|
663
|
+
if (!normalized || (!normalized.startsWith('p2p:') && !normalized.startsWith('group:')))
|
|
664
|
+
return false;
|
|
665
|
+
if (this.isPullResponseProcessing(normalized))
|
|
666
|
+
return true;
|
|
667
|
+
const state = this.pullGateStates.get(this.pullGateName(normalized));
|
|
668
|
+
if (!state)
|
|
669
|
+
return false;
|
|
670
|
+
const matches = (job) => {
|
|
671
|
+
if (!job)
|
|
672
|
+
return false;
|
|
673
|
+
return this.realtimePullNamespace(job.key) === normalized;
|
|
674
|
+
};
|
|
675
|
+
return matches(state.active)
|
|
676
|
+
|| state.foreground.some(matches)
|
|
677
|
+
|| state.background.some(matches);
|
|
678
|
+
}
|
|
679
|
+
hasPullActivity(namespace, includePending = true) {
|
|
680
|
+
const ns = String(namespace ?? '').trim();
|
|
681
|
+
if ((!ns.startsWith('p2p:') && !ns.startsWith('group:'))
|
|
682
|
+
|| this.inlineRealtimeScopes.has(ns))
|
|
683
|
+
return true;
|
|
684
|
+
const client = this.runtime.client;
|
|
685
|
+
if ((client._pullResponseKeys?.get?.(ns) ?? 0) > 0)
|
|
686
|
+
return true;
|
|
687
|
+
const pendingP2p = client._pendingP2pPullUpper;
|
|
688
|
+
const pendingGroup = client._pendingGroupPullUpper;
|
|
689
|
+
if (includePending && ((pendingP2p?.get(ns) ?? 0) > 0 || (pendingGroup?.get(ns) ?? 0) > 0
|
|
690
|
+
|| client._delivery?.hasPendingPull?.(ns)))
|
|
691
|
+
return true;
|
|
692
|
+
for (const marker of client._gapFillDone ?? []) {
|
|
693
|
+
const value = String(marker ?? '').trim();
|
|
694
|
+
if (ns.startsWith('p2p:') && (value.startsWith('p2p_pull:') || value.startsWith('p2p_pull_retry:')))
|
|
695
|
+
return true;
|
|
696
|
+
if (ns.startsWith('group:') && value.startsWith(`group_pull:${ns}`))
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
for (const [key, gate] of client._pullGates?.entries?.() ?? []) {
|
|
700
|
+
if (gate?.inflight && this.pullScopeKey(key) === ns)
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
const state = this.pullGateStates.get(this.pullGateName(ns));
|
|
704
|
+
if (!state)
|
|
705
|
+
return false;
|
|
706
|
+
const matches = (job) => job !== null && job.namespace === ns;
|
|
707
|
+
return matches(state.active) || state.foreground.some(matches) || state.background.some(matches);
|
|
708
|
+
}
|
|
709
|
+
async tryRunInlineRealtime(ns, seq, operation) {
|
|
710
|
+
const normalized = String(ns ?? '').trim();
|
|
711
|
+
if ((!normalized.startsWith('p2p:') && !normalized.startsWith('group:'))
|
|
712
|
+
|| !Number.isSafeInteger(seq) || seq <= 0 || typeof operation !== 'function') {
|
|
713
|
+
return { acquired: false };
|
|
714
|
+
}
|
|
715
|
+
const client = this.runtime.client;
|
|
716
|
+
if (this.hasPullActivity(normalized)) {
|
|
717
|
+
return { acquired: false };
|
|
718
|
+
}
|
|
719
|
+
if (this.isPullResponseProcessing(normalized)) {
|
|
720
|
+
return { acquired: false };
|
|
721
|
+
}
|
|
722
|
+
for (const [key, gate] of client._pullGates?.entries?.() ?? []) {
|
|
723
|
+
if (gate?.inflight && this.pullScopeKey(key) === normalized)
|
|
724
|
+
return { acquired: false };
|
|
725
|
+
}
|
|
726
|
+
const key = `${normalized}|inline|seq=${seq}`;
|
|
727
|
+
const state = this.pullGateState(key);
|
|
728
|
+
if (state.active !== null || state.foreground.length > 0 || state.background.length > 0) {
|
|
729
|
+
return { acquired: false };
|
|
730
|
+
}
|
|
731
|
+
const guard = {
|
|
732
|
+
key,
|
|
733
|
+
namespace: normalized,
|
|
734
|
+
background: false,
|
|
735
|
+
running: true,
|
|
736
|
+
suspended: false,
|
|
737
|
+
inline: true,
|
|
738
|
+
operation,
|
|
739
|
+
promise: Promise.resolve(undefined),
|
|
740
|
+
resolve: () => { },
|
|
741
|
+
reject: () => { },
|
|
742
|
+
timer: null,
|
|
743
|
+
resume: null,
|
|
744
|
+
resumeResolve: null,
|
|
745
|
+
pullingStartedAt: 0,
|
|
746
|
+
timedOut: false,
|
|
747
|
+
cancel: null,
|
|
748
|
+
};
|
|
749
|
+
this.inlineRealtimeScopes.add(normalized);
|
|
750
|
+
try {
|
|
751
|
+
return { acquired: true, value: await operation() };
|
|
752
|
+
}
|
|
753
|
+
finally {
|
|
754
|
+
this.inlineRealtimeScopes.delete(normalized);
|
|
755
|
+
if (state.active === guard)
|
|
756
|
+
state.active = null;
|
|
757
|
+
this.drainPullGate(state);
|
|
758
|
+
this.runtime.client._delivery?.onPullGateIdle?.(normalized);
|
|
759
|
+
this.armQueuedPullTimers(state);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
517
762
|
tryAcquirePullGate(key) {
|
|
518
763
|
if (!key)
|
|
519
764
|
return 0;
|
|
765
|
+
if (this.inlineRealtimeScopes.has(this.pullScopeKey(key)))
|
|
766
|
+
return null;
|
|
767
|
+
const serialized = this.pullGateStates.get(this.pullGateName(key));
|
|
768
|
+
if (serialized?.active?.inline === true)
|
|
769
|
+
return null;
|
|
520
770
|
const client = this.runtime.client;
|
|
521
771
|
const now = Date.now();
|
|
522
772
|
const gate = client._pullGates.get(key) ?? { inflight: false, startedAt: 0, token: 0 };
|
|
@@ -541,25 +791,129 @@ export class RpcPipeline {
|
|
|
541
791
|
gate.inflight = false;
|
|
542
792
|
gate.startedAt = 0;
|
|
543
793
|
}
|
|
544
|
-
async runPullSerialized(key, operation) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
794
|
+
async runPullSerialized(key, operation, background = false) {
|
|
795
|
+
if (!key)
|
|
796
|
+
return await this.executePullOperation(operation, background);
|
|
797
|
+
const state = this.pullGateState(key);
|
|
798
|
+
const namespace = this.pullScopeKey(key);
|
|
799
|
+
const active = state.active?.namespace === namespace ? state.active : null;
|
|
800
|
+
const queuedForeground = state.foreground.find((job) => job.namespace === namespace);
|
|
801
|
+
const queuedBackground = state.background.find((job) => job.namespace === namespace);
|
|
802
|
+
const existing = background
|
|
803
|
+
? (active ?? queuedForeground ?? queuedBackground)
|
|
804
|
+
: (queuedForeground ?? (active && !active.background ? active : null) ?? queuedBackground);
|
|
805
|
+
if (existing) {
|
|
806
|
+
if (!background && existing.background && existing !== state.active) {
|
|
807
|
+
const index = state.background.indexOf(existing);
|
|
808
|
+
if (index >= 0)
|
|
809
|
+
state.background.splice(index, 1);
|
|
810
|
+
existing.background = false;
|
|
811
|
+
state.foreground.push(existing);
|
|
554
812
|
}
|
|
813
|
+
return await existing.promise;
|
|
814
|
+
}
|
|
815
|
+
let resolve;
|
|
816
|
+
let reject;
|
|
817
|
+
const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
|
|
818
|
+
void promise.catch(() => { });
|
|
819
|
+
const job = {
|
|
820
|
+
key, namespace, background, running: false, suspended: false, operation, promise, resolve, reject, timer: null,
|
|
821
|
+
resume: null, resumeResolve: null,
|
|
822
|
+
pullingStartedAt: 0,
|
|
823
|
+
timedOut: false,
|
|
824
|
+
cancel: null,
|
|
825
|
+
};
|
|
826
|
+
(background ? state.background : state.foreground).push(job);
|
|
827
|
+
state.byKey.set(key, job);
|
|
828
|
+
this.drainPullGate(state);
|
|
829
|
+
this.checkPullGate(state, this.pullGateName(key));
|
|
830
|
+
return await promise;
|
|
831
|
+
}
|
|
832
|
+
async executePullOperation(operation, background) {
|
|
833
|
+
if (background)
|
|
834
|
+
return await this.runtime.client._withBackgroundRpc(operation);
|
|
835
|
+
return await operation();
|
|
836
|
+
}
|
|
837
|
+
async yieldPullGate(key, nextKey, background) {
|
|
838
|
+
if (!key)
|
|
839
|
+
return;
|
|
840
|
+
const state = this.pullGateState(key);
|
|
841
|
+
const namespace = this.pullScopeKey(key);
|
|
842
|
+
const job = state.active?.namespace === namespace
|
|
843
|
+
? state.active
|
|
844
|
+
: [...state.foreground, ...state.background].find((candidate) => candidate.namespace === namespace);
|
|
845
|
+
if (!job || state.active !== job)
|
|
846
|
+
return;
|
|
847
|
+
const replacementKey = String(nextKey || key);
|
|
848
|
+
if (state.byKey.get(job.key) === job)
|
|
849
|
+
state.byKey.delete(job.key);
|
|
850
|
+
job.key = replacementKey;
|
|
851
|
+
job.namespace = this.pullScopeKey(replacementKey);
|
|
852
|
+
if (background !== undefined)
|
|
853
|
+
job.background = background;
|
|
854
|
+
state.byKey.set(job.key, job);
|
|
855
|
+
state.lifecycle.set(namespace, 'pulled');
|
|
856
|
+
job.suspended = true;
|
|
857
|
+
job.resume = new Promise((resolve) => { job.resumeResolve = resolve; });
|
|
858
|
+
state.active = null;
|
|
859
|
+
(job.background ? state.background : state.foreground).push(job);
|
|
860
|
+
this.drainPullGate(state);
|
|
861
|
+
if (job.resume)
|
|
862
|
+
await job.resume;
|
|
863
|
+
}
|
|
864
|
+
getPullLifecycle(namespace) {
|
|
865
|
+
const ns = this.pullScopeKey(namespace);
|
|
866
|
+
if (!ns)
|
|
867
|
+
return 'idle';
|
|
868
|
+
return this.pullGateStates.get(this.pullGateName(ns))?.lifecycle.get(ns) ?? 'idle';
|
|
869
|
+
}
|
|
870
|
+
setPullLifecycle(namespace, lifecycle) {
|
|
871
|
+
const ns = this.pullScopeKey(namespace);
|
|
872
|
+
if (!ns)
|
|
873
|
+
return;
|
|
874
|
+
const state = this.pullGateStates.get(this.pullGateName(ns));
|
|
875
|
+
if (!state || state.active?.namespace !== ns)
|
|
876
|
+
return;
|
|
877
|
+
state.lifecycle.set(ns, lifecycle);
|
|
878
|
+
}
|
|
879
|
+
drainPullGate(state) {
|
|
880
|
+
if (state.active)
|
|
881
|
+
return;
|
|
882
|
+
const takeRunnable = (queue) => {
|
|
883
|
+
const index = queue.findIndex((candidate) => !this.inlineRealtimeScopes.has(candidate.namespace));
|
|
884
|
+
return index >= 0 ? queue.splice(index, 1)[0] : undefined;
|
|
885
|
+
};
|
|
886
|
+
const job = takeRunnable(state.foreground) ?? takeRunnable(state.background);
|
|
887
|
+
if (!job)
|
|
888
|
+
return;
|
|
889
|
+
state.active = job;
|
|
890
|
+
if (job.timer)
|
|
891
|
+
clearTimeout(job.timer);
|
|
892
|
+
job.pullingStartedAt = Date.now();
|
|
893
|
+
job.timedOut = false;
|
|
894
|
+
state.lifecycle.set(job.namespace, 'pulling');
|
|
895
|
+
const resumeResolve = job.resumeResolve;
|
|
896
|
+
job.resume = null;
|
|
897
|
+
job.resumeResolve = null;
|
|
898
|
+
if (job.suspended) {
|
|
899
|
+
job.suspended = false;
|
|
900
|
+
resumeResolve?.();
|
|
901
|
+
return;
|
|
555
902
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
903
|
+
job.running = true;
|
|
904
|
+
void this.executePullOperation(job.operation, job.background).then(job.resolve, job.reject).finally(() => {
|
|
905
|
+
if (job.timedOut)
|
|
906
|
+
return;
|
|
907
|
+
state.lifecycle.set(job.namespace, 'idle');
|
|
908
|
+
if (state.active === job)
|
|
909
|
+
state.active = null;
|
|
910
|
+
if (state.byKey.get(job.key) === job)
|
|
911
|
+
state.byKey.delete(job.key);
|
|
912
|
+
this.drainPullGate(state);
|
|
913
|
+
this.runtime.client._delivery?.onPullGateIdle?.(job.namespace);
|
|
914
|
+
});
|
|
562
915
|
}
|
|
916
|
+
armQueuedPullTimers(_state) { }
|
|
563
917
|
async rawCall(method, params, options) {
|
|
564
918
|
const client = this.runtime.client;
|
|
565
919
|
const payload = { ...(params ?? {}) };
|
|
@@ -570,20 +924,40 @@ export class RpcPipeline {
|
|
|
570
924
|
await this.applyClientSignature(method, payload);
|
|
571
925
|
}
|
|
572
926
|
const timeout = options?.timeout;
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
927
|
+
const invokeTransport = () => {
|
|
928
|
+
let request;
|
|
929
|
+
if (rpcBackground)
|
|
930
|
+
request = client._transport.call(method, payload, timeout, options?.trace, true);
|
|
931
|
+
else if (options?.trace !== undefined)
|
|
932
|
+
request = client._transport.call(method, payload, timeout, options.trace);
|
|
933
|
+
else if (timeout !== undefined)
|
|
934
|
+
request = client._transport.call(method, payload, timeout);
|
|
935
|
+
else
|
|
936
|
+
request = client._transport.call(method, payload);
|
|
937
|
+
return this.bindActivePullCancellation(method, payload, request);
|
|
938
|
+
};
|
|
939
|
+
return await this.transportCallWithIdentityRecovery(invokeTransport, method, payload);
|
|
940
|
+
}
|
|
941
|
+
async transportCallWithIdentityRecovery(operation, method, params) {
|
|
942
|
+
try {
|
|
943
|
+
return await operation();
|
|
578
944
|
}
|
|
579
|
-
|
|
580
|
-
|
|
945
|
+
catch (err) {
|
|
946
|
+
const recover = this.runtime.client._recoverIdentityAdmission;
|
|
947
|
+
if (typeof recover !== 'function' || !await recover.call(this.runtime.client, err, method, params))
|
|
948
|
+
throw err;
|
|
949
|
+
return await operation();
|
|
581
950
|
}
|
|
582
|
-
return await client._transport.call(method, payload);
|
|
583
951
|
}
|
|
584
|
-
async postprocessResult(method, params, result) {
|
|
952
|
+
async postprocessResult(method, params, result, options = {}) {
|
|
585
953
|
const client = this.runtime.client;
|
|
586
954
|
let next = result;
|
|
955
|
+
if ((method === 'group.send' || method === 'group.v2.send') && isJsonObject(next)) {
|
|
956
|
+
next = normalizeGroupMentionMode(next);
|
|
957
|
+
}
|
|
958
|
+
if (method === 'group.get_settings' || method === 'group.set_settings') {
|
|
959
|
+
next = normalizeGroupSettingsResponse(next);
|
|
960
|
+
}
|
|
587
961
|
if (method === 'group.thought.get' && isJsonObject(next)) {
|
|
588
962
|
client._clientLog?.debug?.(`group.thought.get transport result: found=${String(next.found ?? '')}, raw_count=${Array.isArray(next.thoughts) ? next.thoughts.length : 0}`);
|
|
589
963
|
next = await client._decryptGroupThoughts(next);
|
|
@@ -593,12 +967,40 @@ export class RpcPipeline {
|
|
|
593
967
|
next = await client._decryptMessageThoughts(next);
|
|
594
968
|
}
|
|
595
969
|
if (method === 'message.pull' && isJsonObject(next)) {
|
|
596
|
-
|
|
970
|
+
const isTail = String(next.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
|
|
971
|
+
if (!isTail && client._aid && client._seqTracker) {
|
|
972
|
+
const ns = `p2p:${client._aid}`;
|
|
973
|
+
const afterSeq = Math.max(0, Number(params.after_seq ?? 0) || 0);
|
|
974
|
+
await client._delivery.commitPlainForwardPage(ns, afterSeq, 'message.pull', next);
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
this.postprocessMessagePull(params, next);
|
|
978
|
+
}
|
|
597
979
|
}
|
|
598
980
|
if (method === 'group.pull' && isJsonObject(next)) {
|
|
599
|
-
|
|
981
|
+
if (Array.isArray(next.messages)) {
|
|
982
|
+
next.messages = next.messages.map((message) => normalizeGroupMentionMode(message));
|
|
983
|
+
}
|
|
984
|
+
const gid = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
985
|
+
const explicitCursorParams = this.explicitGroupCursorParams(params);
|
|
986
|
+
const ownsCursor = Object.keys(explicitCursorParams).length === 0
|
|
987
|
+
|| this.groupCursorTargetsCurrentInstance(explicitCursorParams);
|
|
988
|
+
const isTail = String(next.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
|
|
989
|
+
if (!ownsCursor) {
|
|
990
|
+
client._clientLog.debug(`group.pull external cursor skips local Forward commit: group=${gid}`);
|
|
991
|
+
}
|
|
992
|
+
else if (!isTail && gid && client._seqTracker) {
|
|
993
|
+
const ns = `group:${gid}`;
|
|
994
|
+
const afterSeq = Math.max(0, Number(params.after_message_seq ?? params.after_seq ?? 0) || 0);
|
|
995
|
+
await client._delivery.commitPlainForwardPage(ns, afterSeq, 'group.pull', next, gid);
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
this.postprocessGroupPull(params, next);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (!options.skipGroupState) {
|
|
1002
|
+
next = await client._groupState.postprocessResult(method, params, next);
|
|
600
1003
|
}
|
|
601
|
-
next = await client._groupState.postprocessResult(method, params, next);
|
|
602
1004
|
return next;
|
|
603
1005
|
}
|
|
604
1006
|
_sleep(ms) {
|
|
@@ -616,12 +1018,12 @@ export class RpcPipeline {
|
|
|
616
1018
|
if (rawMessages.length) {
|
|
617
1019
|
client._seqTracker.onPullResult(ns, rawMessages, Number(params.after_seq ?? 0) || 0);
|
|
618
1020
|
}
|
|
619
|
-
const
|
|
620
|
-
if (
|
|
1021
|
+
const retentionFloor = Math.max(0, Number(result.retention_floor_seq ?? 0));
|
|
1022
|
+
if (retentionFloor > 0) {
|
|
621
1023
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
622
|
-
if (contig <
|
|
623
|
-
client._clientLog?.info?.(`message.pull retention-floor advance: ns=${ns} contiguous=${contig} ->
|
|
624
|
-
client._seqTracker.forceContiguousSeq(ns,
|
|
1024
|
+
if (contig < retentionFloor) {
|
|
1025
|
+
client._clientLog?.info?.(`message.pull retention-floor advance: ns=${ns} contiguous=${contig} -> retention_floor_seq=${retentionFloor}`);
|
|
1026
|
+
client._seqTracker.forceContiguousSeq(ns, retentionFloor);
|
|
625
1027
|
}
|
|
626
1028
|
}
|
|
627
1029
|
if (client._seqTracker.getContiguousSeq(ns) !== contigBefore) {
|
|
@@ -631,7 +1033,10 @@ export class RpcPipeline {
|
|
|
631
1033
|
}
|
|
632
1034
|
postprocessGroupPull(params, result) {
|
|
633
1035
|
const client = this.runtime.client;
|
|
634
|
-
|
|
1036
|
+
if (Array.isArray(result.messages)) {
|
|
1037
|
+
result.messages = result.messages.map((message) => normalizeGroupMentionMode(message));
|
|
1038
|
+
}
|
|
1039
|
+
const gid = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
635
1040
|
if (!gid || !client._seqTracker) {
|
|
636
1041
|
return;
|
|
637
1042
|
}
|
|
@@ -643,12 +1048,15 @@ export class RpcPipeline {
|
|
|
643
1048
|
client._seqTracker.onPullResult(ns, rawMessages, Number(params.after_message_seq ?? params.after_seq ?? 0) || 0);
|
|
644
1049
|
}
|
|
645
1050
|
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
646
|
-
const
|
|
647
|
-
|
|
1051
|
+
const retentionFloor = Math.max(0, Number(result.retention_floor_message_seq ?? result.retention_floor_seq ?? 0), Number(cursor?.retention_floor_seq ?? 0));
|
|
1052
|
+
const isTail = String(result.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
|
|
1053
|
+
const visibilityFloor = isTail ? 0 : Math.max(0, Math.max(0, Number(result.earliest_available_message_seq ?? 1) - 1), Number(cursor?.join_seq ?? 0));
|
|
1054
|
+
const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
|
|
1055
|
+
if (effectiveFloor > 0) {
|
|
648
1056
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
649
|
-
if (contig <
|
|
650
|
-
client._clientLog?.info?.(`group.pull
|
|
651
|
-
client._seqTracker.forceContiguousSeq(ns,
|
|
1057
|
+
if (contig < effectiveFloor) {
|
|
1058
|
+
client._clientLog?.info?.(`group.pull floor advance: ns=${ns} contiguous=${contig} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} visibility_floor=${visibilityFloor} mode=${isTail ? 'tail' : 'forward'}`);
|
|
1059
|
+
client._seqTracker.forceContiguousSeq(ns, effectiveFloor);
|
|
652
1060
|
}
|
|
653
1061
|
}
|
|
654
1062
|
if (client._seqTracker.getContiguousSeq(ns) !== contigBefore) {
|
|
@@ -685,33 +1093,16 @@ export class RpcPipeline {
|
|
|
685
1093
|
return;
|
|
686
1094
|
}
|
|
687
1095
|
const record = params;
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const normalizedGroupId = normalizeGroupId(rawGroupId);
|
|
699
|
-
if (normalizedGroupId) {
|
|
700
|
-
if (normalizedGroupId !== rawGroupId) {
|
|
701
|
-
this.runtime.client._clientLog?.debug?.(`call group identifier normalized: ${key}=${rawGroupId} -> ${normalizedGroupId} method=${method}`);
|
|
702
|
-
}
|
|
703
|
-
const existingGroupId = String(record.group_id ?? record.groupId ?? '').trim();
|
|
704
|
-
if (key === 'group_id' || key === 'groupId') {
|
|
705
|
-
params.group_id = rawGroupId;
|
|
706
|
-
}
|
|
707
|
-
else if (existingGroupId) {
|
|
708
|
-
params.group_id = existingGroupId;
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
params.group_id = normalizedGroupId;
|
|
712
|
-
}
|
|
713
|
-
params.group_aid = normalizedGroupId;
|
|
714
|
-
}
|
|
1096
|
+
const rawGroupId = String(record.group_id ?? record.groupId ?? '').trim();
|
|
1097
|
+
const rawGroupAid = String(record.group_aid ?? record.groupAid ?? '').trim();
|
|
1098
|
+
if (rawGroupId) {
|
|
1099
|
+
params.group_id = rawGroupId;
|
|
1100
|
+
}
|
|
1101
|
+
else if (rawGroupAid) {
|
|
1102
|
+
params.group_id = rawGroupAid;
|
|
1103
|
+
}
|
|
1104
|
+
if (rawGroupAid) {
|
|
1105
|
+
params.group_aid = rawGroupAid;
|
|
715
1106
|
}
|
|
716
1107
|
delete record.groupAid;
|
|
717
1108
|
delete record.groupId;
|
|
@@ -725,6 +1116,19 @@ export class RpcPipeline {
|
|
|
725
1116
|
}
|
|
726
1117
|
return cursorParams;
|
|
727
1118
|
}
|
|
1119
|
+
explicitGroupCursorParams(params) {
|
|
1120
|
+
const value = params._group_cursor_params;
|
|
1121
|
+
if (!isJsonObject(value))
|
|
1122
|
+
return {};
|
|
1123
|
+
return { ...value };
|
|
1124
|
+
}
|
|
1125
|
+
groupCursorTargetsCurrentInstance(params) {
|
|
1126
|
+
const client = this.runtime.client;
|
|
1127
|
+
const deviceId = String(params.device_id ?? '').trim();
|
|
1128
|
+
const slotId = String(params.slot_id ?? '').trim();
|
|
1129
|
+
return (!deviceId || deviceId === (client._deviceId ?? ''))
|
|
1130
|
+
&& (!slotId || slotId === (client._slotId ?? ''));
|
|
1131
|
+
}
|
|
728
1132
|
validateMessageRecipient(toAid) {
|
|
729
1133
|
if (isGroupServiceAid(toAid)) {
|
|
730
1134
|
throw new ValidationError('message.send receiver cannot be group.{issuer}; use group.send instead');
|