@agentunion/fastaun 0.5.3 → 0.5.6
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 +126 -52
- 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 +126 -52
- package/_packed_docs/INDEX.md +127 -69
- package/_packed_docs/KITE_DOCS_GUIDE.md +63 -29
- 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 +227 -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 +1004 -0
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +999 -0
- package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +6 -4
- 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 +62 -17
- 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 +67 -78
- 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 +87 -41
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +18 -5
- package/_packed_docs/sdk/09-group-rpc-manual.md +307 -267
- package/_packed_docs/sdk/09-message-rpc-manual.md +142 -103
- package/_packed_docs/sdk/09-payload-reference.md +1 -1
- package/_packed_docs/sdk/09-stream-rpc-manual.md +8 -8
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +26 -17
- package/_packed_docs/sdk/INDEX.md +42 -34
- package/_packed_docs/sdk/README.md +7 -6
- 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.js +8 -2
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +2 -0
- package/dist/aid-store.js +43 -7
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.d.ts +0 -2
- package/dist/auth.js +97 -50
- package/dist/auth.js.map +1 -1
- package/dist/client/delivery.d.ts +70 -7
- package/dist/client/delivery.js +1334 -236
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +25 -22
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +46 -3
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/mention-mode.d.ts +6 -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.js +26 -3
- package/dist/client/peers.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +20 -1
- package/dist/client/rpc-pipeline.js +332 -73
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +20 -1
- package/dist/client/v2-e2ee.js +625 -105
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +17 -5
- package/dist/client.js +332 -167
- package/dist/client.js.map +1 -1
- package/dist/errors.js +4 -1
- package/dist/errors.js.map +1 -1
- package/dist/facades.d.ts +6 -0
- package/dist/facades.js +196 -80
- package/dist/facades.js.map +1 -1
- package/dist/group-index.d.ts +5 -0
- package/dist/group-index.js +65 -30
- package/dist/group-index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +12 -0
- package/dist/keystore/aid-db.js +68 -4
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +12 -0
- package/dist/keystore/local-identity-store.d.ts +10 -0
- package/dist/keystore/local-identity-store.js +8 -2
- package/dist/keystore/local-identity-store.js.map +1 -1
- package/dist/keystore/local-token-store.d.ts +10 -0
- package/dist/keystore/local-token-store.js +8 -2
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/register-flow.d.ts +0 -1
- package/dist/register-flow.js +96 -38
- package/dist/register-flow.js.map +1 -1
- package/dist/secret-store/file-store.d.ts +3 -14
- package/dist/secret-store/file-store.js +7 -98
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/secret-store/index.d.ts +1 -4
- package/dist/secret-store/index.js +2 -2
- package/dist/secret-store/index.js.map +1 -1
- package/dist/seq-tracker.d.ts +29 -9
- package/dist/seq-tracker.js +233 -64
- package/dist/seq-tracker.js.map +1 -1
- package/dist/storage/vfs.d.ts +1 -0
- package/dist/storage/vfs.js +28 -4
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +360 -8
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +19 -0
- package/dist/transport.js +197 -14
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/decrypt.js +26 -23
- package/dist/v2/e2ee/decrypt.js.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.js +1 -2
- package/dist/v2/state/commitment.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -4
- package/dist/e2ee-group.d.ts +0 -249
- package/dist/e2ee-group.js +0 -1567
- package/dist/e2ee-group.js.map +0 -1
- package/dist/group-resources.d.ts +0 -31
- package/dist/group-resources.js +0 -84
- package/dist/group-resources.js.map +0 -1
- package/dist/keystore/file.d.ts +0 -109
- package/dist/keystore/file.js +0 -772
- package/dist/keystore/file.js.map +0 -1
- package/dist/keystore/sqlite-backup.d.ts +0 -44
- package/dist/keystore/sqlite-backup.js +0 -382
- package/dist/keystore/sqlite-backup.js.map +0 -1
- package/dist/namespaces/auth.d.ts +0 -118
- package/dist/namespaces/auth.js +0 -968
- package/dist/namespaces/auth.js.map +0 -1
- package/dist/namespaces/custody.d.ts +0 -48
- package/dist/namespaces/custody.js +0 -313
- package/dist/namespaces/custody.js.map +0 -1
- package/dist/namespaces/meta.d.ts +0 -76
- package/dist/namespaces/meta.js +0 -490
- package/dist/namespaces/meta.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as crypto from 'node:crypto';
|
|
2
2
|
import { normalizeSlotId, slotIsolationKey } from '../config.js';
|
|
3
|
-
import { ClientSignatureError, ConnectionError, PermissionError,
|
|
4
|
-
import { normalizeGroupId } from '../group-id.js';
|
|
3
|
+
import { ClientSignatureError, ConnectionError, PermissionError, ValidationError } from '../errors.js';
|
|
5
4
|
import { ConnectionState, isJsonObject, } from '../types.js';
|
|
6
5
|
import { validateAIDFormat, validateGroupIDFormat } from '../validators.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',
|
|
@@ -106,7 +106,7 @@ function getCertFingerprint(certPem) {
|
|
|
106
106
|
const certObj = new crypto.X509Certificate(certPem);
|
|
107
107
|
return cachePut(certFingerprintCache, key, 'sha256:' + certObj.fingerprint256.replace(/:/g, '').toLowerCase());
|
|
108
108
|
}
|
|
109
|
-
/**
|
|
109
|
+
/** 旧式 tryAcquirePullGate 的防并发窗口。 */
|
|
110
110
|
const PULL_GATE_STALE_MS = 30000;
|
|
111
111
|
const NON_IDEMPOTENT_TIMEOUT_MS = 35_000;
|
|
112
112
|
const NON_IDEMPOTENT_METHODS = new Set([
|
|
@@ -143,6 +143,9 @@ const NON_IDEMPOTENT_METHODS = new Set([
|
|
|
143
143
|
]);
|
|
144
144
|
export class RpcPipeline {
|
|
145
145
|
runtime;
|
|
146
|
+
pullGateStates = new Map();
|
|
147
|
+
inlineRealtimeScopes = new Set();
|
|
148
|
+
bypassedPullScopes = new Map();
|
|
146
149
|
constructor(runtime) {
|
|
147
150
|
this.runtime = runtime;
|
|
148
151
|
}
|
|
@@ -164,17 +167,25 @@ export class RpcPipeline {
|
|
|
164
167
|
const skipSendResultEnvelope = Boolean(p._skip_send_result_envelope);
|
|
165
168
|
delete p._skip_send_result_envelope;
|
|
166
169
|
const pullGateKey = this.pullGateKeyForCall(method, p);
|
|
170
|
+
const runWithPullResponseScope = async (operation) => {
|
|
171
|
+
const scope = pullGateKey ? this.pullScopeKey(pullGateKey) : '';
|
|
172
|
+
const wrapper = client._withPullResponseProcessing;
|
|
173
|
+
if (!scope || typeof wrapper !== 'function')
|
|
174
|
+
return await operation();
|
|
175
|
+
return await wrapper.call(client, scope, operation);
|
|
176
|
+
};
|
|
167
177
|
if (pullGateKey && this.isPullResponseProcessing(pullGateKey)) {
|
|
168
178
|
client._clientLog.debug(`pull skipped while processing pull response: method=${method} key=${pullGateKey}`);
|
|
169
179
|
return client._emptyPullResultForCall(method);
|
|
170
180
|
}
|
|
171
181
|
if (pullGateKey && !pullGateLocked) {
|
|
182
|
+
const pullBackground = this.pullGateBackgroundForCall(method, p);
|
|
172
183
|
const lockedParams = { ...p, _pull_gate_locked: true };
|
|
173
|
-
if (
|
|
184
|
+
if (pullBackground)
|
|
174
185
|
lockedParams._rpc_background = true;
|
|
175
186
|
if (skipSendResultEnvelope)
|
|
176
187
|
lockedParams._skip_send_result_envelope = true;
|
|
177
|
-
return await this.runPullSerialized(pullGateKey, async () => this.call(method, lockedParams));
|
|
188
|
+
return await this.runPullSerialized(pullGateKey, async () => this.call(method, lockedParams), pullBackground);
|
|
178
189
|
}
|
|
179
190
|
if (method === 'message.send') {
|
|
180
191
|
const encrypt = p.encrypt ?? true;
|
|
@@ -193,7 +204,8 @@ export class RpcPipeline {
|
|
|
193
204
|
const encrypt = p.encrypt ?? true;
|
|
194
205
|
delete p.encrypt;
|
|
195
206
|
if (encrypt) {
|
|
196
|
-
return await runWithRpcPriority(() => client._sendGroupV2(String(p.
|
|
207
|
+
return await runWithRpcPriority(() => client._sendGroupV2(String(p.group_id ?? p.group_aid ?? ''), p.payload, {
|
|
208
|
+
groupAid: String(p.group_aid ?? '').trim() || undefined,
|
|
197
209
|
messageId: String(p.message_id ?? '') || undefined,
|
|
198
210
|
timestamp: p.timestamp,
|
|
199
211
|
protectedHeaders: client._protectedHeadersFromParams(p),
|
|
@@ -223,15 +235,22 @@ export class RpcPipeline {
|
|
|
223
235
|
}
|
|
224
236
|
if (method === 'message.pull' || method === 'message.v2.pull') {
|
|
225
237
|
await client._ensureV2SessionReady('message.pull');
|
|
238
|
+
if (p.window_mode === 'tail') {
|
|
239
|
+
return await runWithPullResponseScope(() => client._v2E2EE.pullV2TailInternal(p));
|
|
240
|
+
}
|
|
226
241
|
const skipAutoAck = p._skip_auto_ack === true || p.skip_auto_ack === true;
|
|
227
242
|
const force = p.force === true;
|
|
228
243
|
const afterSeq = Number(p.after_seq ?? 0) || 0;
|
|
229
244
|
const limit = Number(p.limit ?? 50) || 50;
|
|
230
|
-
const messages = skipAutoAck
|
|
231
|
-
?
|
|
232
|
-
:
|
|
245
|
+
const messages = await runWithPullResponseScope(() => (skipAutoAck
|
|
246
|
+
? runWithRpcPriority(() => client._pullV2(afterSeq, limit, { skipAutoAck: true, gateLocked: true, force }))
|
|
247
|
+
: runWithRpcPriority(() => client._pullV2(afterSeq, limit, { gateLocked: true, force }))));
|
|
233
248
|
return { messages };
|
|
234
249
|
}
|
|
250
|
+
if (method === 'message.history') {
|
|
251
|
+
await client._ensureV2SessionReady('message.history');
|
|
252
|
+
return await client._v2E2EE.historyV2Internal(p);
|
|
253
|
+
}
|
|
235
254
|
if (method === 'message.ack' || method === 'message.v2.ack') {
|
|
236
255
|
await client._ensureV2SessionReady('message.ack');
|
|
237
256
|
return await runWithRpcPriority(() => client._ackV2(Number(p.seq ?? p.up_to_seq ?? 0) || undefined));
|
|
@@ -241,21 +260,35 @@ export class RpcPipeline {
|
|
|
241
260
|
throw new ValidationError('group.pull requires group_id');
|
|
242
261
|
}
|
|
243
262
|
await client._ensureV2SessionReady('group.pull');
|
|
263
|
+
if (p.window_mode === 'tail') {
|
|
264
|
+
return await runWithPullResponseScope(() => client._v2E2EE.pullGroupV2TailInternal(p));
|
|
265
|
+
}
|
|
244
266
|
const hasExplicitAfterSeq = 'after_seq' in p || 'after_message_seq' in p;
|
|
245
267
|
const cursorParams = client._explicitGroupCursorParams(p);
|
|
246
268
|
const ownsCursor = Object.keys(cursorParams).length === 0 || client._groupCursorTargetsCurrentInstance(cursorParams);
|
|
247
269
|
const pullOpts = { gateLocked: true };
|
|
248
270
|
if (hasExplicitAfterSeq)
|
|
249
271
|
pullOpts.explicitAfterSeq = true;
|
|
272
|
+
if (p.force === true)
|
|
273
|
+
pullOpts.force = true;
|
|
250
274
|
if (Object.keys(cursorParams).length > 0)
|
|
251
275
|
pullOpts.cursorParams = cursorParams;
|
|
252
276
|
if (!ownsCursor)
|
|
253
277
|
pullOpts.ownsCursor = false;
|
|
254
278
|
if (String(p.group_id ?? '').trim())
|
|
255
279
|
pullOpts.wireGroupId = String(p.group_id).trim();
|
|
256
|
-
|
|
280
|
+
if (String(p.group_aid ?? '').trim())
|
|
281
|
+
pullOpts.groupAid = String(p.group_aid).trim();
|
|
282
|
+
const messages = await runWithPullResponseScope(() => runWithRpcPriority(() => client._pullGroupV2(String(p.group_id ?? p.group_aid), Number(p.after_seq ?? p.after_message_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, pullOpts)));
|
|
257
283
|
return { messages };
|
|
258
284
|
}
|
|
285
|
+
if (method === 'group.history') {
|
|
286
|
+
if (!String(p.group_id ?? p.group_aid ?? '').trim()) {
|
|
287
|
+
throw new ValidationError('group.history requires group_id');
|
|
288
|
+
}
|
|
289
|
+
await client._ensureV2SessionReady('group.history');
|
|
290
|
+
return await client._v2E2EE.historyGroupV2Internal(p);
|
|
291
|
+
}
|
|
259
292
|
if (method === 'group.ack_messages' || method === 'group.v2.ack') {
|
|
260
293
|
if (!String(p.group_id ?? '').trim()) {
|
|
261
294
|
throw new ValidationError('group.ack_messages requires group_id');
|
|
@@ -266,7 +299,7 @@ export class RpcPipeline {
|
|
|
266
299
|
if (method === 'group.ack_messages' && !ownsCursor) {
|
|
267
300
|
return await runWithRpcPriority(() => client._rawGroupAckMessages(p));
|
|
268
301
|
}
|
|
269
|
-
return await runWithRpcPriority(() => client._ackGroupV2(String(p.
|
|
302
|
+
return await runWithRpcPriority(() => client._ackGroupV2(String(p.group_id ?? p.group_aid), Number(p.seq ?? p.msg_seq ?? p.up_to_seq ?? 0) || undefined, String(p.group_aid ?? '').trim() || undefined));
|
|
270
303
|
}
|
|
271
304
|
if (method === 'message.pull') {
|
|
272
305
|
delete p._skip_auto_ack;
|
|
@@ -285,9 +318,10 @@ export class RpcPipeline {
|
|
|
285
318
|
: (rpcBackground
|
|
286
319
|
? await client._transport.call(method, p, undefined, undefined, true)
|
|
287
320
|
: await client._transport.call(method, p));
|
|
321
|
+
const sendResultModeSource = result;
|
|
288
322
|
result = await this.postprocessResult(method, p, result);
|
|
289
323
|
if (!skipSendResultEnvelope) {
|
|
290
|
-
result = client._delivery.attachSendResultEnvelope(method, p, result, Boolean(p.encrypted));
|
|
324
|
+
result = client._delivery.attachSendResultEnvelope(method, p, result, Boolean(p.encrypted), sendResultModeSource);
|
|
291
325
|
}
|
|
292
326
|
client._clientLog.debug(`call exit: method=${method} elapsed=${Date.now() - tStart}ms`);
|
|
293
327
|
return result;
|
|
@@ -482,25 +516,159 @@ export class RpcPipeline {
|
|
|
482
516
|
// ── pull-gate ──────────────────────────────────────────────────────────────
|
|
483
517
|
pullGateKeyForCall(method, params) {
|
|
484
518
|
const client = this.runtime.client;
|
|
485
|
-
if (method === 'message.pull' || method === 'message.v2.pull') {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
519
|
+
if (method === 'message.pull' || method === 'message.v2.pull' || method === 'message.history') {
|
|
520
|
+
if (!client._aid)
|
|
521
|
+
return '';
|
|
522
|
+
let mode;
|
|
523
|
+
let cursor;
|
|
524
|
+
if (method === 'message.history') {
|
|
525
|
+
mode = 'history';
|
|
526
|
+
cursor = `before=${String(params.before_seq)}`;
|
|
527
|
+
}
|
|
528
|
+
else if (params.window_mode === 'tail') {
|
|
529
|
+
mode = 'tail';
|
|
530
|
+
cursor = `after=${String(params.after_seq ?? 0)}`;
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
mode = 'forward';
|
|
534
|
+
cursor = `after=${String(params.after_seq ?? 0)}|force=${String(Boolean(params.force))}`;
|
|
535
|
+
}
|
|
536
|
+
return `p2p:${client._aid}|${mode}|${cursor}|limit=${String(params.limit)}`;
|
|
537
|
+
}
|
|
538
|
+
if (method === 'group.pull' || method === 'group.v2.pull' || method === 'group.history') {
|
|
539
|
+
const groupId = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
540
|
+
if (!groupId)
|
|
541
|
+
return '';
|
|
542
|
+
let mode;
|
|
543
|
+
let cursor;
|
|
544
|
+
if (method === 'group.history') {
|
|
545
|
+
mode = 'history';
|
|
546
|
+
cursor = `before=${String(params.before_seq)}`;
|
|
547
|
+
}
|
|
548
|
+
else if (params.window_mode === 'tail') {
|
|
549
|
+
mode = 'tail';
|
|
550
|
+
cursor = `after=${String(params.after_seq ?? params.after_message_seq ?? 0)}`;
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
mode = 'forward';
|
|
554
|
+
cursor = `after=${String(params.after_seq ?? params.after_message_seq ?? 0)}|force=${String(Boolean(params.force))}`;
|
|
555
|
+
}
|
|
556
|
+
return `group:${groupId}|${mode}|${cursor}|limit=${String(params.limit)}`;
|
|
490
557
|
}
|
|
491
|
-
if (method === 'group.pull_events' && String(params.
|
|
492
|
-
|
|
558
|
+
if (method === 'group.pull_events' && String(params.group_id ?? params.group_aid ?? '').trim()) {
|
|
559
|
+
const groupId = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
560
|
+
return `group_event:${groupId}|forward|after=${String(params.after_event_seq ?? 0)}|limit=${String(params.limit)}`;
|
|
493
561
|
}
|
|
494
562
|
return '';
|
|
495
563
|
}
|
|
564
|
+
pullGateBackgroundForCall(method, params) {
|
|
565
|
+
if (method === 'message.history' || method === 'group.history')
|
|
566
|
+
return false;
|
|
567
|
+
if (method === 'message.pull' || method === 'message.v2.pull' || method === 'group.pull' || method === 'group.v2.pull') {
|
|
568
|
+
return params.window_mode !== 'tail';
|
|
569
|
+
}
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
pullGateName(key) {
|
|
573
|
+
if (key.startsWith('p2p:'))
|
|
574
|
+
return 'p2p_message';
|
|
575
|
+
if (key.startsWith('group:'))
|
|
576
|
+
return 'group_message';
|
|
577
|
+
if (key.startsWith('group_event:'))
|
|
578
|
+
return 'group_event';
|
|
579
|
+
return `other:${key}`;
|
|
580
|
+
}
|
|
581
|
+
pullGateState(key) {
|
|
582
|
+
const name = this.pullGateName(key);
|
|
583
|
+
let state = this.pullGateStates.get(name);
|
|
584
|
+
if (!state) {
|
|
585
|
+
state = { active: null, foreground: [], background: [], byKey: new Map() };
|
|
586
|
+
this.pullGateStates.set(name, state);
|
|
587
|
+
}
|
|
588
|
+
return state;
|
|
589
|
+
}
|
|
590
|
+
pullScopeKey(key) {
|
|
591
|
+
return key.split('|', 1)[0] ?? key;
|
|
592
|
+
}
|
|
593
|
+
hasInlineRealtimeGuard(namespace) {
|
|
594
|
+
const ns = String(namespace ?? '').trim();
|
|
595
|
+
return Boolean(ns) && this.inlineRealtimeScopes.has(ns);
|
|
596
|
+
}
|
|
597
|
+
async executeBypassedPull(key, operation, background) {
|
|
598
|
+
const scope = this.pullScopeKey(key);
|
|
599
|
+
this.bypassedPullScopes.set(scope, (this.bypassedPullScopes.get(scope) ?? 0) + 1);
|
|
600
|
+
try {
|
|
601
|
+
return await this.executePullOperation(operation, background);
|
|
602
|
+
}
|
|
603
|
+
finally {
|
|
604
|
+
const remaining = (this.bypassedPullScopes.get(scope) ?? 1) - 1;
|
|
605
|
+
if (remaining <= 0) {
|
|
606
|
+
this.bypassedPullScopes.delete(scope);
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
this.bypassedPullScopes.set(scope, remaining);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
496
613
|
isPullResponseProcessing(key) {
|
|
497
614
|
if (!key)
|
|
498
615
|
return false;
|
|
499
|
-
return (this.runtime.client._pullResponseKeys.get(key) ?? 0) > 0;
|
|
616
|
+
return (this.runtime.client._pullResponseKeys.get(this.pullScopeKey(key)) ?? 0) > 0;
|
|
617
|
+
}
|
|
618
|
+
hasPullActivity(namespace) {
|
|
619
|
+
const ns = String(namespace ?? '').trim();
|
|
620
|
+
if ((!ns.startsWith('p2p:') && !ns.startsWith('group:')) || this.hasInlineRealtimeGuard(ns))
|
|
621
|
+
return true;
|
|
622
|
+
const client = this.runtime.client;
|
|
623
|
+
if ((client._pullResponseKeys.get(ns) ?? 0) > 0)
|
|
624
|
+
return true;
|
|
625
|
+
if (Number(client._pendingP2pPullUpper?.get?.(ns) ?? 0) > 0)
|
|
626
|
+
return true;
|
|
627
|
+
if ((this.bypassedPullScopes.get(ns) ?? 0) > 0)
|
|
628
|
+
return true;
|
|
629
|
+
for (const key of client._gapFillDone?.keys?.() ?? []) {
|
|
630
|
+
const marker = String(key ?? '').trim();
|
|
631
|
+
if (ns.startsWith('p2p:') && marker.startsWith('p2p:'))
|
|
632
|
+
return true;
|
|
633
|
+
if (ns.startsWith('group:') && marker.startsWith(`group_msg:${ns.slice('group:'.length)}:`))
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
for (const [key, gate] of client._pullGates?.entries?.() ?? []) {
|
|
637
|
+
if (gate?.inflight && this.pullScopeKey(key) === ns)
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
for (const state of this.pullGateStates.values()) {
|
|
641
|
+
const jobs = [state.active, ...state.foreground, ...state.background];
|
|
642
|
+
if (jobs.some((job) => job !== null && this.pullScopeKey(job.key) === ns))
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
async tryRunInlineRealtime(namespace, seq, operation) {
|
|
648
|
+
const ns = String(namespace ?? '').trim();
|
|
649
|
+
if ((!ns.startsWith('p2p:') && !ns.startsWith('group:'))
|
|
650
|
+
|| !Number.isSafeInteger(seq) || seq <= 0 || typeof operation !== 'function'
|
|
651
|
+
|| this.hasPullActivity(ns)) {
|
|
652
|
+
return { acquired: false };
|
|
653
|
+
}
|
|
654
|
+
this.inlineRealtimeScopes.add(ns);
|
|
655
|
+
try {
|
|
656
|
+
return { acquired: true, value: await operation() };
|
|
657
|
+
}
|
|
658
|
+
finally {
|
|
659
|
+
this.inlineRealtimeScopes.delete(ns);
|
|
660
|
+
const state = this.pullGateStates.get(this.pullGateName(ns));
|
|
661
|
+
if (state) {
|
|
662
|
+
this.drainPullGate(state);
|
|
663
|
+
this.armQueuedPullTimers(state);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
500
666
|
}
|
|
501
667
|
tryAcquirePullGate(key) {
|
|
502
668
|
if (!key)
|
|
503
669
|
return 0;
|
|
670
|
+
if (this.hasInlineRealtimeGuard(this.pullScopeKey(key)))
|
|
671
|
+
return null;
|
|
504
672
|
const client = this.runtime.client;
|
|
505
673
|
const now = Date.now();
|
|
506
674
|
const gate = client._pullGates.get(key) ?? { inflight: false, startedAt: 0, token: 0 };
|
|
@@ -529,27 +697,123 @@ export class RpcPipeline {
|
|
|
529
697
|
client._schedulePendingP2pPullIfNeeded(key, 'pull-gate-release');
|
|
530
698
|
}
|
|
531
699
|
}
|
|
532
|
-
async runPullSerialized(key, operation) {
|
|
700
|
+
async runPullSerialized(key, operation, background = false) {
|
|
533
701
|
if (key && this.isPullResponseProcessing(key)) {
|
|
534
702
|
this.runtime.client._clientLog.debug(`pull skipped while processing pull response: key=${key}`);
|
|
535
703
|
return [];
|
|
536
704
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
if (
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
|
|
705
|
+
if (!key)
|
|
706
|
+
return await this.executePullOperation(operation, background);
|
|
707
|
+
const state = this.pullGateState(key);
|
|
708
|
+
const timeoutMs = Math.max(0, Number(this.runtime.client._PULL_GATE_QUEUE_TIMEOUT_MS ?? 3_000) || 0);
|
|
709
|
+
const existing = state.byKey.get(key);
|
|
710
|
+
if (existing) {
|
|
711
|
+
this.runtime.client._clientLog.debug(`pull gate coalesced: gate=${this.pullGateName(key)} key=${key} background=${String(background)}`);
|
|
712
|
+
if (this.hasInlineRealtimeGuard(this.pullScopeKey(key)))
|
|
713
|
+
return await existing.promise;
|
|
714
|
+
return await this.waitForCoalesced(existing.promise, timeoutMs, operation, background, key);
|
|
715
|
+
}
|
|
716
|
+
let resolve;
|
|
717
|
+
let reject;
|
|
718
|
+
const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
|
|
719
|
+
const job = {
|
|
720
|
+
key,
|
|
721
|
+
background,
|
|
722
|
+
enqueuedAt: Date.now(),
|
|
723
|
+
operation,
|
|
724
|
+
promise,
|
|
725
|
+
resolve,
|
|
726
|
+
reject,
|
|
727
|
+
timer: null,
|
|
728
|
+
};
|
|
729
|
+
const queue = background ? state.background : state.foreground;
|
|
730
|
+
queue.push(job);
|
|
731
|
+
state.byKey.set(key, job);
|
|
732
|
+
this.drainPullGate(state);
|
|
733
|
+
if (state.active !== job && !this.hasInlineRealtimeGuard(this.pullScopeKey(job.key))) {
|
|
734
|
+
job.timer = setTimeout(() => this.bypassQueuedPull(state, job), timeoutMs);
|
|
735
|
+
}
|
|
736
|
+
return await promise;
|
|
737
|
+
}
|
|
738
|
+
async executePullOperation(operation, background) {
|
|
739
|
+
if (background)
|
|
549
740
|
return await this.runtime.client._withBackgroundRpc(operation);
|
|
741
|
+
return await operation();
|
|
742
|
+
}
|
|
743
|
+
waitForCoalesced(shared, timeoutMs, operation, background, key) {
|
|
744
|
+
return new Promise((resolve, reject) => {
|
|
745
|
+
let settled = false;
|
|
746
|
+
const timer = setTimeout(() => {
|
|
747
|
+
if (settled)
|
|
748
|
+
return;
|
|
749
|
+
settled = true;
|
|
750
|
+
this.runtime.client._clientLog.warn(`pull gate coalesced wait timeout, bypass: gate=${this.pullGateName(key)} key=${key} timeout=${timeoutMs}ms`);
|
|
751
|
+
void this.executeBypassedPull(key, operation, background).then(resolve, reject);
|
|
752
|
+
}, timeoutMs);
|
|
753
|
+
shared.then((value) => {
|
|
754
|
+
if (settled)
|
|
755
|
+
return;
|
|
756
|
+
settled = true;
|
|
757
|
+
clearTimeout(timer);
|
|
758
|
+
resolve(value);
|
|
759
|
+
}, (error) => {
|
|
760
|
+
if (settled)
|
|
761
|
+
return;
|
|
762
|
+
settled = true;
|
|
763
|
+
clearTimeout(timer);
|
|
764
|
+
reject(error);
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
bypassQueuedPull(state, job) {
|
|
769
|
+
if (state.active === job)
|
|
770
|
+
return;
|
|
771
|
+
if (this.hasInlineRealtimeGuard(this.pullScopeKey(job.key))) {
|
|
772
|
+
job.timer = null;
|
|
773
|
+
return;
|
|
550
774
|
}
|
|
551
|
-
|
|
552
|
-
|
|
775
|
+
const queue = job.background ? state.background : state.foreground;
|
|
776
|
+
const index = queue.indexOf(job);
|
|
777
|
+
if (index < 0)
|
|
778
|
+
return;
|
|
779
|
+
queue.splice(index, 1);
|
|
780
|
+
if (state.byKey.get(job.key) === job)
|
|
781
|
+
state.byKey.delete(job.key);
|
|
782
|
+
job.timer = null;
|
|
783
|
+
this.runtime.client._clientLog.warn(`pull gate queue timeout, bypass: gate=${this.pullGateName(job.key)} key=${job.key} background=${String(job.background)}`);
|
|
784
|
+
void this.executeBypassedPull(job.key, job.operation, job.background).then(job.resolve, job.reject);
|
|
785
|
+
}
|
|
786
|
+
drainPullGate(state) {
|
|
787
|
+
if (state.active)
|
|
788
|
+
return;
|
|
789
|
+
const takeRunnable = (queue) => {
|
|
790
|
+
const index = queue.findIndex((candidate) => !this.hasInlineRealtimeGuard(this.pullScopeKey(candidate.key)));
|
|
791
|
+
return index >= 0 ? queue.splice(index, 1)[0] : undefined;
|
|
792
|
+
};
|
|
793
|
+
const job = takeRunnable(state.foreground) ?? takeRunnable(state.background);
|
|
794
|
+
if (!job)
|
|
795
|
+
return;
|
|
796
|
+
state.active = job;
|
|
797
|
+
if (job.timer) {
|
|
798
|
+
clearTimeout(job.timer);
|
|
799
|
+
job.timer = null;
|
|
800
|
+
}
|
|
801
|
+
void this.executePullOperation(job.operation, job.background)
|
|
802
|
+
.then(job.resolve, job.reject)
|
|
803
|
+
.finally(() => {
|
|
804
|
+
if (state.active === job)
|
|
805
|
+
state.active = null;
|
|
806
|
+
if (state.byKey.get(job.key) === job)
|
|
807
|
+
state.byKey.delete(job.key);
|
|
808
|
+
this.drainPullGate(state);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
armQueuedPullTimers(state) {
|
|
812
|
+
const timeoutMs = Math.max(0, Number(this.runtime.client._PULL_GATE_QUEUE_TIMEOUT_MS ?? 3_000) || 0);
|
|
813
|
+
for (const job of [...state.foreground, ...state.background]) {
|
|
814
|
+
if (!job.timer && !this.hasInlineRealtimeGuard(this.pullScopeKey(job.key))) {
|
|
815
|
+
job.timer = setTimeout(() => this.bypassQueuedPull(state, job), timeoutMs);
|
|
816
|
+
}
|
|
553
817
|
}
|
|
554
818
|
}
|
|
555
819
|
// ── raw-call ───────────────────────────────────────────────────────────────
|
|
@@ -575,6 +839,12 @@ export class RpcPipeline {
|
|
|
575
839
|
async postprocessResult(method, params, result) {
|
|
576
840
|
const client = this.runtime.client;
|
|
577
841
|
let next = result;
|
|
842
|
+
if ((method === 'group.send' || method === 'group.v2.send') && isUnknownJsonObject(next)) {
|
|
843
|
+
next = normalizeGroupMentionMode(next);
|
|
844
|
+
}
|
|
845
|
+
if (method === 'group.get_settings' || method === 'group.set_settings') {
|
|
846
|
+
next = normalizeGroupSettingsResponse(next);
|
|
847
|
+
}
|
|
578
848
|
if (method === 'group.thought.get' && isUnknownJsonObject(next)) {
|
|
579
849
|
client._clientLog?.debug?.(`group.thought.get transport result: found=${String(next.found ?? '')}, raw_count=${Array.isArray(next.thoughts) ? next.thoughts.length : 0}`);
|
|
580
850
|
next = await client._decryptGroupThoughts(next);
|
|
@@ -605,12 +875,12 @@ export class RpcPipeline {
|
|
|
605
875
|
if (rawMessages.length) {
|
|
606
876
|
client._seqTracker.onPullResult(ns, rawMessages, Number(params.after_seq ?? 0) || 0);
|
|
607
877
|
}
|
|
608
|
-
const
|
|
609
|
-
if (
|
|
878
|
+
const retentionFloor = Math.max(0, Number(result.retention_floor_seq ?? 0));
|
|
879
|
+
if (retentionFloor > 0) {
|
|
610
880
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
611
|
-
if (contig <
|
|
612
|
-
client._clientLog?.info?.(`message.pull retention-floor advance: ns=${ns} contiguous=${contig} ->
|
|
613
|
-
client._seqTracker.forceContiguousSeq(ns,
|
|
881
|
+
if (contig < retentionFloor) {
|
|
882
|
+
client._clientLog?.info?.(`message.pull retention-floor advance: ns=${ns} contiguous=${contig} -> retention_floor_seq=${retentionFloor}`);
|
|
883
|
+
client._seqTracker.forceContiguousSeq(ns, retentionFloor);
|
|
614
884
|
}
|
|
615
885
|
}
|
|
616
886
|
if (client._seqTracker.getContiguousSeq(ns) !== contigBefore) {
|
|
@@ -620,7 +890,10 @@ export class RpcPipeline {
|
|
|
620
890
|
}
|
|
621
891
|
postprocessGroupPull(params, result) {
|
|
622
892
|
const client = this.runtime.client;
|
|
623
|
-
|
|
893
|
+
if (Array.isArray(result.messages)) {
|
|
894
|
+
result.messages = result.messages.map((message) => normalizeGroupMentionMode(message));
|
|
895
|
+
}
|
|
896
|
+
const gid = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
624
897
|
if (!gid || !client._seqTracker) {
|
|
625
898
|
return;
|
|
626
899
|
}
|
|
@@ -632,12 +905,15 @@ export class RpcPipeline {
|
|
|
632
905
|
client._seqTracker.onPullResult(ns, rawMessages, Number(params.after_message_seq ?? params.after_seq ?? 0) || 0);
|
|
633
906
|
}
|
|
634
907
|
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
635
|
-
const
|
|
636
|
-
|
|
908
|
+
const retentionFloor = Math.max(0, Number(result.retention_floor_message_seq ?? result.retention_floor_seq ?? 0), Number(cursor?.retention_floor_seq ?? 0));
|
|
909
|
+
const isTail = String(result.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
|
|
910
|
+
const visibilityFloor = isTail ? 0 : Math.max(0, Math.max(0, Number(result.earliest_available_message_seq ?? 1) - 1), Number(cursor?.join_seq ?? 0));
|
|
911
|
+
const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
|
|
912
|
+
if (effectiveFloor > 0) {
|
|
637
913
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
638
|
-
if (contig <
|
|
639
|
-
client._clientLog?.info?.(`group.pull
|
|
640
|
-
client._seqTracker.forceContiguousSeq(ns,
|
|
914
|
+
if (contig < effectiveFloor) {
|
|
915
|
+
client._clientLog?.info?.(`group.pull floor advance: ns=${ns} contiguous=${contig} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} visibility_floor=${visibilityFloor} mode=${isTail ? 'tail' : 'forward'}`);
|
|
916
|
+
client._seqTracker.forceContiguousSeq(ns, effectiveFloor);
|
|
641
917
|
}
|
|
642
918
|
}
|
|
643
919
|
if (client._seqTracker.getContiguousSeq(ns) !== contigBefore) {
|
|
@@ -674,33 +950,16 @@ export class RpcPipeline {
|
|
|
674
950
|
return;
|
|
675
951
|
}
|
|
676
952
|
const record = params;
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const normalizedGroupId = normalizeGroupId(rawGroupId);
|
|
688
|
-
if (normalizedGroupId) {
|
|
689
|
-
if (normalizedGroupId !== rawGroupId) {
|
|
690
|
-
this.runtime.client._clientLog?.debug?.(`call group identifier normalized: ${key}=${rawGroupId} -> ${normalizedGroupId} method=${method}`);
|
|
691
|
-
}
|
|
692
|
-
const existingGroupId = String(record.group_id ?? record.groupId ?? '').trim();
|
|
693
|
-
if (key === 'group_id' || key === 'groupId') {
|
|
694
|
-
params.group_id = rawGroupId;
|
|
695
|
-
}
|
|
696
|
-
else if (existingGroupId) {
|
|
697
|
-
params.group_id = existingGroupId;
|
|
698
|
-
}
|
|
699
|
-
else {
|
|
700
|
-
params.group_id = normalizedGroupId;
|
|
701
|
-
}
|
|
702
|
-
params.group_aid = normalizedGroupId;
|
|
703
|
-
}
|
|
953
|
+
const rawGroupId = String(record.group_id ?? record.groupId ?? '').trim();
|
|
954
|
+
const rawGroupAid = String(record.group_aid ?? record.groupAid ?? '').trim();
|
|
955
|
+
if (rawGroupId) {
|
|
956
|
+
params.group_id = rawGroupId;
|
|
957
|
+
}
|
|
958
|
+
else if (rawGroupAid) {
|
|
959
|
+
params.group_id = rawGroupAid;
|
|
960
|
+
}
|
|
961
|
+
if (rawGroupAid) {
|
|
962
|
+
params.group_aid = rawGroupAid;
|
|
704
963
|
}
|
|
705
964
|
delete record.groupAid;
|
|
706
965
|
delete record.groupId;
|