@agentunion/fastaun 0.5.30 → 0.5.32

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/_packed_docs/CHANGELOG.md +60 -0
  3. package/_packed_docs/sdk/0.5.31-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +162 -0
  4. package/_packed_docs/sdk/0.5.31-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +167 -0
  5. package/_packed_docs/sdk/0.5.32-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +350 -0
  6. package/_packed_docs/sdk/0.5.32-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +319 -0
  7. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +12 -5
  8. package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +459 -0
  9. package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/274/200/345/217/221/347/247/273/346/244/215/346/214/207/345/215/227.md +554 -0
  10. package/_packed_docs/sdk/INDEX.md +27 -5
  11. package/_packed_docs/sdk//345/255/220AID/344/277/256/346/224/271agent.md/351/200/232/347/237/245Owner/345/234/272/346/231/257/346/214/207/345/215/227.md +234 -0
  12. package/dist/agent-md-schema.js +7 -6
  13. package/dist/agent-md-schema.js.map +1 -1
  14. package/dist/agent-md.d.ts +2 -0
  15. package/dist/agent-md.js +20 -6
  16. package/dist/agent-md.js.map +1 -1
  17. package/dist/aid-store.d.ts +1 -0
  18. package/dist/aid-store.js +1 -0
  19. package/dist/aid-store.js.map +1 -1
  20. package/dist/aid.js +1 -1
  21. package/dist/aid.js.map +1 -1
  22. package/dist/auth.js +1 -1
  23. package/dist/auth.js.map +1 -1
  24. package/dist/client/control-messages.d.ts +22 -7
  25. package/dist/client/control-messages.js +220 -185
  26. package/dist/client/control-messages.js.map +1 -1
  27. package/dist/client/delivery.d.ts +3 -0
  28. package/dist/client/delivery.js +67 -3
  29. package/dist/client/delivery.js.map +1 -1
  30. package/dist/client/group-state.d.ts +3 -0
  31. package/dist/client/group-state.js +48 -14
  32. package/dist/client/group-state.js.map +1 -1
  33. package/dist/client/lifecycle.js +12 -3
  34. package/dist/client/lifecycle.js.map +1 -1
  35. package/dist/client/rpc-pipeline.d.ts +9 -3
  36. package/dist/client/rpc-pipeline.js +61 -26
  37. package/dist/client/rpc-pipeline.js.map +1 -1
  38. package/dist/client/v2-e2ee.d.ts +13 -1
  39. package/dist/client/v2-e2ee.js +32 -19
  40. package/dist/client/v2-e2ee.js.map +1 -1
  41. package/dist/client.d.ts +18 -1
  42. package/dist/client.js +396 -140
  43. package/dist/client.js.map +1 -1
  44. package/dist/discovery.d.ts +1 -0
  45. package/dist/discovery.js +3 -1
  46. package/dist/discovery.js.map +1 -1
  47. package/dist/group-fs.js +1 -1
  48. package/dist/group-fs.js.map +1 -1
  49. package/dist/index.d.ts +2 -0
  50. package/dist/index.js +1 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/ownership-v21.d.ts +2 -2
  53. package/dist/ownership-v21.js +18 -21
  54. package/dist/ownership-v21.js.map +1 -1
  55. package/dist/ownership-workflow.js +6 -4
  56. package/dist/ownership-workflow.js.map +1 -1
  57. package/dist/pubsub/binding.d.ts +54 -0
  58. package/dist/pubsub/binding.js +343 -0
  59. package/dist/pubsub/binding.js.map +1 -0
  60. package/dist/pubsub/channel.d.ts +45 -0
  61. package/dist/pubsub/channel.js +137 -0
  62. package/dist/pubsub/channel.js.map +1 -0
  63. package/dist/pubsub/client.d.ts +93 -0
  64. package/dist/pubsub/client.js +318 -0
  65. package/dist/pubsub/client.js.map +1 -0
  66. package/dist/pubsub/credentials.d.ts +51 -0
  67. package/dist/pubsub/credentials.js +360 -0
  68. package/dist/pubsub/credentials.js.map +1 -0
  69. package/dist/pubsub/errors.d.ts +5 -0
  70. package/dist/pubsub/errors.js +6 -0
  71. package/dist/pubsub/errors.js.map +1 -0
  72. package/dist/pubsub/index.d.ts +48 -0
  73. package/dist/pubsub/index.js +152 -0
  74. package/dist/pubsub/index.js.map +1 -0
  75. package/dist/pubsub/scheduler.d.ts +58 -0
  76. package/dist/pubsub/scheduler.js +249 -0
  77. package/dist/pubsub/scheduler.js.map +1 -0
  78. package/dist/pubsub/service.d.ts +100 -0
  79. package/dist/pubsub/service.js +650 -0
  80. package/dist/pubsub/service.js.map +1 -0
  81. package/dist/pubsub/types.d.ts +119 -0
  82. package/dist/pubsub/types.js +2 -0
  83. package/dist/pubsub/types.js.map +1 -0
  84. package/dist/register-flow.js +1 -1
  85. package/dist/register-flow.js.map +1 -1
  86. package/dist/storage/lowlevel.js +49 -8
  87. package/dist/storage/lowlevel.js.map +1 -1
  88. package/dist/storage/read-cache.d.ts +7 -3
  89. package/dist/storage/read-cache.js +137 -18
  90. package/dist/storage/read-cache.js.map +1 -1
  91. package/dist/storage/vfs.d.ts +1 -0
  92. package/dist/storage/vfs.js +12 -4
  93. package/dist/storage/vfs.js.map +1 -1
  94. package/dist/tools/cross-sdk-agent.d.ts +1 -1
  95. package/dist/tools/cross-sdk-agent.js +4 -2
  96. package/dist/tools/cross-sdk-agent.js.map +1 -1
  97. package/dist/tools/pubsub-actor.d.ts +27 -0
  98. package/dist/tools/pubsub-actor.js +260 -0
  99. package/dist/tools/pubsub-actor.js.map +1 -0
  100. package/dist/transport.d.ts +26 -2
  101. package/dist/transport.js +182 -17
  102. package/dist/transport.js.map +1 -1
  103. package/dist/version.d.ts +1 -1
  104. package/dist/version.js +1 -1
  105. package/package.json +1 -1
package/dist/transport.js CHANGED
@@ -19,8 +19,15 @@ import { messageClassOf } from './client/control-messages.js';
19
19
  const MAX_WS_PAYLOAD_SIZE = 1_000_000;
20
20
  const MAX_RPC_INFLIGHT = 16;
21
21
  const MAX_BACKGROUND_RPC_INFLIGHT = 8;
22
+ const MAX_EVENT_RPC_INFLIGHT = 4;
23
+ const MAX_PULL_RPC_INFLIGHT = 4;
22
24
  const WORKER_START_TIMEOUT_MS = 2_000;
23
25
  const SHORT_RPC_WORKER_START_TIMEOUT_MS = 2_000;
26
+ function normalizeRpcBackground(value) {
27
+ if (value === 'event' || value === 'pull')
28
+ return value;
29
+ return value === true ? true : false;
30
+ }
24
31
  const _noopLogger = {
25
32
  error: () => { },
26
33
  warn: () => { },
@@ -351,7 +358,7 @@ function parseReadStateJSON(raw) {
351
358
  }
352
359
  return JSON.parse(chunks.length ? chunks.join('') + raw.slice(copied) : raw);
353
360
  }
354
- function executeShortRpcEnvelope(url, method, params, signal) {
361
+ function executeShortRpcEnvelope(url, method, params, signal, verifySsl = false) {
355
362
  return new Promise((resolve, reject) => {
356
363
  if (signal.aborted) {
357
364
  reject(new AuthError('short RPC cancelled'));
@@ -359,7 +366,7 @@ function executeShortRpcEnvelope(url, method, params, signal) {
359
366
  }
360
367
  const requestId = `pre-${method}`;
361
368
  const payload = JSON.stringify({ jsonrpc: '2.0', id: requestId, method, params });
362
- const ws = new WebSocket(url, { handshakeTimeout: 5_000, rejectUnauthorized: false });
369
+ const ws = new WebSocket(url, { handshakeTimeout: 5_000, rejectUnauthorized: verifySsl });
363
370
  let opened = false;
364
371
  let challengeReceived = false;
365
372
  let settled = false;
@@ -437,10 +444,10 @@ function executeShortRpcEnvelope(url, method, params, signal) {
437
444
  onAbort();
438
445
  });
439
446
  }
440
- export function shortRpcViaNetworkActor(url, method, params) {
447
+ export function shortRpcViaNetworkActor(url, method, params, verifySsl = false) {
441
448
  if (!isMainThread || process.env.VITEST || process.env.AUN_TRANSPORT_LOCAL === '1') {
442
449
  const controller = new AbortController();
443
- return Object.assign(executeShortRpcEnvelope(url, method, params, controller.signal), {
450
+ return Object.assign(executeShortRpcEnvelope(url, method, params, controller.signal, verifySsl), {
444
451
  cancel: () => controller.abort(),
445
452
  });
446
453
  }
@@ -487,12 +494,12 @@ export function shortRpcViaNetworkActor(url, method, params) {
487
494
  return;
488
495
  cleanup();
489
496
  localController = new AbortController();
490
- void executeShortRpcEnvelope(url, method, params, localController.signal).then(resolveOnce, rejectOnce);
497
+ void executeShortRpcEnvelope(url, method, params, localController.signal, verifySsl).then(resolveOnce, rejectOnce);
491
498
  };
492
499
  try {
493
500
  worker = new Worker(new URL(import.meta.url), {
494
501
  execArgv: process.execArgv.filter((arg) => !arg.startsWith('--input-type')),
495
- workerData: { aunShortRpcWorker: true, id, url, method, params },
502
+ workerData: { aunShortRpcWorker: true, id, url, method, params, verifySsl },
496
503
  });
497
504
  workerReadyTimer = setTimeout(() => {
498
505
  if (!workerReady && !settled)
@@ -569,9 +576,14 @@ export class RPCTransport {
569
576
  _pending = new Map();
570
577
  _pendingMeta = new Map();
571
578
  _pendingBackground = new Set();
579
+ _pendingEvent = new Set();
580
+ _pendingPull = new Set();
572
581
  _rpcQueue = [];
573
582
  _backgroundRpcQueue = [];
574
583
  _drainingRpcQueue = false;
584
+ _rateLimitUntil = 0;
585
+ _rateLimitTimer = null;
586
+ _rateLimitWaiters = new Set();
575
587
  _onForegroundDrained;
576
588
  _onRpcResponse;
577
589
  _sendQueue = [];
@@ -605,6 +617,19 @@ export class RPCTransport {
605
617
  _workerConnectAttempt = 0;
606
618
  _workerBufferedEvents = [];
607
619
  _workerOptions;
620
+ get inflightCounts() {
621
+ return {
622
+ total: this._pending.size,
623
+ background: this._pendingBackground.size,
624
+ event: this._pendingEvent.size,
625
+ pull: this._pendingPull.size,
626
+ };
627
+ }
628
+ get foregroundWaiting() {
629
+ return this._rpcQueue.length;
630
+ }
631
+ _eventBackpressure(timeout = 30_000) { if (this._worker)
632
+ return this._workerCommand({ type: 'event_backpressure', timeout }); return Promise.resolve(this.foregroundWaiting > 0 || Number(this._ws?.bufferedAmount ?? 0) >= 65536); }
608
633
  constructor(opts) {
609
634
  this._logger = opts.logger ?? _noopLogger;
610
635
  this._dispatcher = opts.eventDispatcher;
@@ -782,6 +807,8 @@ export class RPCTransport {
782
807
  }
783
808
  this._workerDisabled = true;
784
809
  this._workerConnected = false;
810
+ for (const wake of this._rateLimitWaiters)
811
+ wake();
785
812
  this._workerConnecting = false;
786
813
  this._workerGeneration = null;
787
814
  this._workerBufferedEvents = [];
@@ -837,6 +864,12 @@ export class RPCTransport {
837
864
  }
838
865
  if (this._workerGeneration === null || message.generation !== this._workerGeneration)
839
866
  return;
867
+ if (message.event === '_transport.rate_limit') {
868
+ const until = Number(message.payload?.until_ms);
869
+ if (Number.isFinite(until))
870
+ this._rateLimitUntil = Math.max(this._rateLimitUntil, until);
871
+ return;
872
+ }
840
873
  if (message.event === '_transport.risk_control') {
841
874
  this._dispatchRiskControl(message.payload, () => message.generation === this._workerGeneration);
842
875
  return;
@@ -856,6 +889,8 @@ export class RPCTransport {
856
889
  }
857
890
  if (message.event === '_transport.disconnect') {
858
891
  this._workerConnected = false;
892
+ for (const wake of this._rateLimitWaiters)
893
+ wake();
859
894
  this._workerGeneration = null;
860
895
  const payload = (message.payload && typeof message.payload === 'object')
861
896
  ? { ...message.payload }
@@ -911,7 +946,7 @@ export class RPCTransport {
911
946
  const id = `worker-${++this._workerSeq}`;
912
947
  const timeoutMs = command.type === 'call'
913
948
  ? null
914
- : command.type === 'connect' ? this._connectTimeout + 1_000 : 30_000;
949
+ : command.type === 'connect' ? this._connectTimeout + 1_000 : command.type === 'event_backpressure' ? command.timeout ?? 30_000 : 30_000;
915
950
  let posted = false;
916
951
  const promise = new Promise((resolve, reject) => {
917
952
  const timer = timeoutMs === null ? null : setTimeout(() => {
@@ -1495,6 +1530,9 @@ export class RPCTransport {
1495
1530
  async close() {
1496
1531
  if (this._worker) {
1497
1532
  const worker = this._worker;
1533
+ this._workerConnected = false;
1534
+ for (const wake of this._rateLimitWaiters)
1535
+ wake();
1498
1536
  try {
1499
1537
  await this._workerCommand({ type: 'close' });
1500
1538
  }
@@ -1531,6 +1569,11 @@ export class RPCTransport {
1531
1569
  this._handshakeAbort = null;
1532
1570
  abortHandshake?.();
1533
1571
  this._closed = true;
1572
+ for (const wake of this._rateLimitWaiters)
1573
+ wake();
1574
+ if (this._rateLimitTimer !== null)
1575
+ clearTimeout(this._rateLimitTimer);
1576
+ this._rateLimitTimer = null;
1534
1577
  this._riskDeliveries = 0;
1535
1578
  this._startSendQueue();
1536
1579
  this._connectionGeneration += 1;
@@ -1543,6 +1586,8 @@ export class RPCTransport {
1543
1586
  this._pending.clear();
1544
1587
  this._pendingMeta.clear();
1545
1588
  this._pendingBackground.clear();
1589
+ this._pendingEvent.clear();
1590
+ this._pendingPull.clear();
1546
1591
  for (const queued of this._rpcQueue) {
1547
1592
  clearTimeout(queued.pending.timer);
1548
1593
  queued.pending.reject(new ConnectionError('transport closed'));
@@ -1647,7 +1692,7 @@ export class RPCTransport {
1647
1692
  const requestLog = withLogContext(this._logger, requestContext);
1648
1693
  const responseLog = withLogContext(requestLog, { direction: 'inbound', peerAid: requestContext.peerAid });
1649
1694
  requestLog.debug(`RPC request queued: method=${method}`);
1650
- const request = this._workerCommand({ type: 'call', method, params, timeout, trace, background, ...(onSent ? { trackSent: true } : {}) }, undefined, onSent, sentCheckpoint);
1695
+ const request = this._workerCommand({ type: 'call', method, params, timeout, trace, background, rateLimitUntil: this._rateLimitUntil, ...(onSent ? { trackSent: true } : {}) }, undefined, onSent, sentCheckpoint);
1651
1696
  const result = request.then((result) => {
1652
1697
  responseLog.debug(`RPC response ok: method=${method}`);
1653
1698
  return result;
@@ -1667,15 +1712,20 @@ export class RPCTransport {
1667
1712
  });
1668
1713
  }
1669
1714
  const rpcId = `rpc-${crypto.randomBytes(8).toString('hex')}`;
1670
- const effectiveTimeout = timeout ?? this._timeout;
1715
+ const deadline = typeof params?._rpc_deadline === 'number' ? params._rpc_deadline : undefined;
1716
+ const effectiveTimeout = deadline === undefined ? timeout ?? this._timeout : Math.min(timeout ?? this._timeout, deadline - Date.now());
1717
+ if (effectiveTimeout <= 0)
1718
+ throw new TimeoutError(`rpc timeout before send: ${method}`, { retryable: true });
1671
1719
  const tStart = Date.now();
1672
1720
  // 注入 _trace(会话级或调用级 mode 非 off 时)
1673
1721
  const effectiveTraceMode = (trace === 'off' || trace === 'log' || trace === 'diag') ? trace : this._traceMode;
1674
1722
  let traceId = '';
1675
1723
  const sendParams = { ...(params ?? {}) };
1676
1724
  const localParams = sendParams;
1677
- const backgroundRpc = background || localParams._rpc_background === true;
1725
+ const selectedBackground = normalizeRpcBackground(background !== false ? background : localParams._rpc_background);
1726
+ const backgroundRpc = selectedBackground === true && ['message.peers', 'message.pull', 'message.v2.pull', 'message.ack', 'message.v2.ack', 'group.pull', 'group.v2.pull', 'group.pull_events', 'group.ack_messages', 'group.v2.ack'].includes(method) ? 'pull' : selectedBackground;
1678
1727
  delete localParams._rpc_background;
1728
+ delete localParams._rpc_deadline;
1679
1729
  const requestContext = trafficLogContext('outbound', sendParams);
1680
1730
  const requestLog = withLogContext(this._logger, requestContext);
1681
1731
  const responseLog = withLogContext(requestLog, {
@@ -1706,7 +1756,9 @@ export class RPCTransport {
1706
1756
  const timer = setTimeout(() => {
1707
1757
  this._removeRpc(rpcId, pending);
1708
1758
  requestLog.warn(`RPC timeout: method=${method}, id=${rpcId}, elapsed=${Date.now() - tStart}ms, timeout=${effectiveTimeout}ms`);
1709
- reject(new TimeoutError(`rpc timeout: ${method}`, { retryable: true }));
1759
+ reject(backgroundRpc && !pending.sent && this._rateLimitRemainingMs() > 0
1760
+ ? this._rateLimitError()
1761
+ : new TimeoutError(`rpc timeout: ${method}`, { retryable: true }));
1710
1762
  this._drainRpcQueue();
1711
1763
  }, effectiveTimeout);
1712
1764
  const pending = {
@@ -1787,7 +1839,7 @@ export class RPCTransport {
1787
1839
  pending,
1788
1840
  tStart,
1789
1841
  timeoutMs: effectiveTimeout,
1790
- background: true,
1842
+ background: backgroundRpc,
1791
1843
  priority: this._messagePriority(localParams.message_class),
1792
1844
  logger: requestLog,
1793
1845
  onSent,
@@ -1843,6 +1895,64 @@ export class RPCTransport {
1843
1895
  }
1844
1896
  _messagePriority(messageClass) { return messageClass === 'sdk_control' ? 2 : messageClass === 'control' ? 1 : 0; }
1845
1897
  _isRiskControlMethod(method) { return ['gateway.risk_ack', 'meta.ping', 'system.ping', 'ping', 'auth.disconnect'].includes(method); }
1898
+ _rateLimitRemainingMs() { return Math.max(0, this._rateLimitUntil - Date.now()); }
1899
+ _rateLimitError() {
1900
+ const remaining = this._rateLimitRemainingMs();
1901
+ return new RateLimitError('RPC rate limited: local backoff', {
1902
+ code: -32429, retryable: true, data: { retry_after_seconds: Math.ceil(remaining / 1000), retry_after_ms: Math.ceil(remaining) },
1903
+ });
1904
+ }
1905
+ async _waitRateLimit(timeout = this._timeout, signal) {
1906
+ const started = Date.now();
1907
+ while (this._rateLimitRemainingMs() > 0) {
1908
+ if (signal?.aborted)
1909
+ throw new TimeoutError('rpc cancelled', { retryable: true });
1910
+ if (!this.isConnected())
1911
+ throw new ConnectionError('transport closed');
1912
+ const remaining = this._rateLimitRemainingMs();
1913
+ const budget = timeout - (Date.now() - started);
1914
+ if (budget <= 0)
1915
+ throw this._rateLimitError();
1916
+ await new Promise(resolve => {
1917
+ const wake = () => {
1918
+ clearTimeout(timer);
1919
+ this._rateLimitWaiters.delete(wake);
1920
+ signal?.removeEventListener('abort', wake);
1921
+ resolve();
1922
+ };
1923
+ const timer = setTimeout(wake, Math.min(remaining, budget, 2_147_483_647));
1924
+ this._rateLimitWaiters.add(wake);
1925
+ signal?.addEventListener('abort', wake, { once: true });
1926
+ });
1927
+ }
1928
+ }
1929
+ _observeRateLimit(error) {
1930
+ if (![4290, 429, -32429, -32029].includes(Number(error.code)))
1931
+ return;
1932
+ const data = error.data;
1933
+ const seconds = Number(data?.retry_after_seconds);
1934
+ const milliseconds = Number(data?.retry_after_ms);
1935
+ this._rateLimitUntil = Math.max(this._rateLimitUntil, Date.now() + Math.max(60_000, Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : 0, Number.isFinite(milliseconds) && milliseconds > 0 ? milliseconds : 0));
1936
+ this._worker?.postMessage({ type: 'rate_limit', rateLimitUntil: this._rateLimitUntil });
1937
+ if (!isMainThread && Boolean(workerData?.aunTransportWorker) && parentPort) {
1938
+ parentPort.postMessage({ type: 'event', generation: this._connectionGeneration, event: '_transport.rate_limit', payload: { until_ms: this._rateLimitUntil } });
1939
+ }
1940
+ }
1941
+ _canDispatch(entry) {
1942
+ if (this._pending.size >= MAX_RPC_INFLIGHT && !this._isRiskControlMethod(entry.method))
1943
+ return false;
1944
+ if (!entry.background)
1945
+ return true;
1946
+ if (this._rateLimitRemainingMs() > 0)
1947
+ return false;
1948
+ if (this._pendingBackground.size >= MAX_BACKGROUND_RPC_INFLIGHT)
1949
+ return false;
1950
+ if (entry.background === 'event')
1951
+ return this._pendingEvent.size < MAX_EVENT_RPC_INFLIGHT;
1952
+ if (entry.background === 'pull')
1953
+ return this._pendingPull.size < MAX_PULL_RPC_INFLIGHT;
1954
+ return true;
1955
+ }
1846
1956
  _enqueueSend(task, priority = 0) {
1847
1957
  const result = new Promise((resolve, reject) => { this._sendQueue.push({ task, priority, resolve, reject }); });
1848
1958
  this._startSendQueue();
@@ -1956,6 +2066,8 @@ export class RPCTransport {
1956
2066
  this._pending.delete(rpcId);
1957
2067
  this._pendingMeta.delete(rpcId);
1958
2068
  this._pendingBackground.delete(rpcId);
2069
+ this._pendingEvent.delete(rpcId);
2070
+ this._pendingPull.delete(rpcId);
1959
2071
  }
1960
2072
  if (this._rpcQueue.length > 0) {
1961
2073
  this._rpcQueue = this._rpcQueue.filter((entry) => entry.rpcId !== rpcId || (pending !== undefined && entry.pending !== pending));
@@ -1979,10 +2091,12 @@ export class RPCTransport {
1979
2091
  let entry;
1980
2092
  let selectedQueue;
1981
2093
  let selectedIndex = -1;
1982
- for (const queue of [this._rpcQueue, ...(this._pendingBackground.size < MAX_BACKGROUND_RPC_INFLIGHT ? [this._backgroundRpcQueue] : [])]) {
2094
+ for (const queue of [this._rpcQueue, this._backgroundRpcQueue]) {
1983
2095
  for (let index = 0; index < queue.length; index++) {
1984
2096
  if (controlOnly && !this._isRiskControlMethod(queue[index].method))
1985
2097
  continue;
2098
+ if (!this._canDispatch(queue[index]))
2099
+ continue;
1986
2100
  if (!entry || queue[index].priority > entry.priority) {
1987
2101
  entry = queue[index];
1988
2102
  selectedQueue = queue;
@@ -1999,7 +2113,9 @@ export class RPCTransport {
1999
2113
  if (elapsed >= entry.timeoutMs) {
2000
2114
  clearTimeout(entry.pending.timer);
2001
2115
  entry.logger.warn(`RPC queue timeout: method=${entry.method}, id=${entry.rpcId}, elapsed=${elapsed}ms, timeout=${entry.timeoutMs}ms`);
2002
- entry.pending.reject(new TimeoutError(`rpc timeout before send: ${entry.method}`, { retryable: true }));
2116
+ entry.pending.reject(entry.background && this._rateLimitRemainingMs() > 0
2117
+ ? this._rateLimitError()
2118
+ : new TimeoutError(`rpc timeout before send: ${entry.method}`, { retryable: true }));
2003
2119
  continue;
2004
2120
  }
2005
2121
  this._pending.set(entry.rpcId, entry.pending);
@@ -2012,16 +2128,36 @@ export class RPCTransport {
2012
2128
  });
2013
2129
  if (entry.background) {
2014
2130
  this._pendingBackground.add(entry.rpcId);
2131
+ if (entry.background === 'event')
2132
+ this._pendingEvent.add(entry.rpcId);
2133
+ if (entry.background === 'pull')
2134
+ this._pendingPull.add(entry.rpcId);
2015
2135
  }
2136
+ let deferredForRateLimit = false;
2016
2137
  this._sendText(entry.payload, `rpc ${entry.method}`, () => {
2017
2138
  if (this._pending.get(entry.rpcId) !== entry.pending)
2018
2139
  return false;
2140
+ if (Date.now() - entry.tStart >= entry.timeoutMs) {
2141
+ this._removeRpc(entry.rpcId, entry.pending);
2142
+ entry.pending.reject(new TimeoutError(`rpc timeout before send: ${entry.method}`, { retryable: true }));
2143
+ return false;
2144
+ }
2145
+ if (entry.background && this._rateLimitRemainingMs() > 0) {
2146
+ deferredForRateLimit = true;
2147
+ this._removeRpc(entry.rpcId, entry.pending);
2148
+ this._backgroundRpcQueue.push(entry);
2149
+ this._drainRpcQueue();
2150
+ return false;
2151
+ }
2019
2152
  this._requestCheck?.(`rpc:${entry.method}`);
2153
+ entry.pending.sent = true;
2020
2154
  return true;
2021
2155
  }, this._isRiskControlMethod(entry.method) ? 3 : entry.priority).then(() => {
2022
2156
  entry.logger.debug(`RPC request sent: method=${entry.method}, id=${entry.rpcId}, background=${entry.background}`);
2023
2157
  return entry.onSent?.();
2024
2158
  }).catch((err) => {
2159
+ if (deferredForRateLimit)
2160
+ return;
2025
2161
  if (this._pending.get(entry.rpcId) !== entry.pending)
2026
2162
  return;
2027
2163
  this._removeRpc(entry.rpcId, entry.pending);
@@ -2035,6 +2171,13 @@ export class RPCTransport {
2035
2171
  }
2036
2172
  finally {
2037
2173
  this._drainingRpcQueue = false;
2174
+ if (this._rateLimitTimer !== null)
2175
+ clearTimeout(this._rateLimitTimer);
2176
+ this._rateLimitTimer = null;
2177
+ const remaining = this._rateLimitRemainingMs();
2178
+ if (!this._closed && this._backgroundRpcQueue.length && remaining > 0) {
2179
+ this._rateLimitTimer = setTimeout(() => { this._rateLimitTimer = null; this._drainRpcQueue(); }, Math.min(remaining, 2_147_483_647));
2180
+ }
2038
2181
  if (foregroundDrained)
2039
2182
  this._onForegroundDrained?.();
2040
2183
  }
@@ -2086,6 +2229,11 @@ export class RPCTransport {
2086
2229
  this._stopWebSocketHeartbeat(ws);
2087
2230
  const wasClosed = this._closed;
2088
2231
  this._closed = true;
2232
+ for (const wake of this._rateLimitWaiters)
2233
+ wake();
2234
+ if (this._rateLimitTimer !== null)
2235
+ clearTimeout(this._rateLimitTimer);
2236
+ this._rateLimitTimer = null;
2089
2237
  this._ws = null;
2090
2238
  this._lastCloseCode = Number.isFinite(code) ? code : null;
2091
2239
  this._logger.debug(`WebSocket closed: code=${code}, intentional close=${wasClosed}`);
@@ -2097,6 +2245,8 @@ export class RPCTransport {
2097
2245
  this._pending.clear();
2098
2246
  this._pendingMeta.clear();
2099
2247
  this._pendingBackground.clear();
2248
+ this._pendingEvent.clear();
2249
+ this._pendingPull.clear();
2100
2250
  for (const queued of this._rpcQueue) {
2101
2251
  clearTimeout(queued.pending.timer);
2102
2252
  queued.pending.reject(err);
@@ -2229,12 +2379,16 @@ export class RPCTransport {
2229
2379
  this._receiveRiskControl(state);
2230
2380
  delete error.data.risk_state;
2231
2381
  }
2382
+ if (isJsonObject(error))
2383
+ this._observeRateLimit(error);
2232
2384
  const rpcId = String(message.id);
2233
2385
  const pending = this._pending.get(rpcId);
2234
2386
  if (pending) {
2235
2387
  this._pending.delete(rpcId);
2236
2388
  this._pendingMeta.delete(rpcId);
2237
2389
  this._pendingBackground.delete(rpcId);
2390
+ this._pendingEvent.delete(rpcId);
2391
+ this._pendingPull.delete(rpcId);
2238
2392
  pending.resolve(message);
2239
2393
  this._drainRpcQueue();
2240
2394
  this._onRpcResponse?.();
@@ -2363,7 +2517,7 @@ if (!isMainThread && Boolean(workerData?.aunShortRpcWorker) && parentPort) {
2363
2517
  const command = workerData;
2364
2518
  const controller = new AbortController();
2365
2519
  parentPort.postMessage({ type: 'ready', id: command.id });
2366
- void executeShortRpcEnvelope(command.url, command.method, command.params, controller.signal)
2520
+ void executeShortRpcEnvelope(command.url, command.method, command.params, controller.signal, command.verifySsl)
2367
2521
  .then((result) => parentPort.postMessage({
2368
2522
  type: 'reply', id: command.id, ok: true, result,
2369
2523
  }))
@@ -2424,6 +2578,12 @@ if (!isMainThread && Boolean(workerData?.aunTransportWorker) && parentPort) {
2424
2578
  parentPort.on('message', async (command) => {
2425
2579
  const id = command.id;
2426
2580
  try {
2581
+ if (command.type === 'rate_limit') {
2582
+ if (Number.isFinite(command.rateLimitUntil))
2583
+ actor['_rateLimitUntil'] = Math.max(actor['_rateLimitUntil'], command.rateLimitUntil);
2584
+ actor['_drainRpcQueue']();
2585
+ return;
2586
+ }
2427
2587
  if (command.type === 'risk_release') {
2428
2588
  if (command.generation === bridgeGeneration())
2429
2589
  actor['_finishRiskDelivery']();
@@ -2457,12 +2617,14 @@ if (!isMainThread && Boolean(workerData?.aunTransportWorker) && parentPort) {
2457
2617
  break;
2458
2618
  }
2459
2619
  case 'call': {
2620
+ if (Number.isFinite(command.rateLimitUntil))
2621
+ actor['_rateLimitUntil'] = Math.max(actor['_rateLimitUntil'], command.rateLimitUntil);
2460
2622
  const request = (command.trackSent
2461
2623
  ? actor['_callWithSent'](String(command.method ?? ''), command.params ?? undefined, async () => {
2462
2624
  if (id)
2463
2625
  parentPort.postMessage({ type: 'sent', id, generation: bridgeGeneration() });
2464
- }, command.timeout, command.trace, Boolean(command.background))
2465
- : actor.call(String(command.method ?? ''), command.params ?? undefined, command.timeout, command.trace, Boolean(command.background)));
2626
+ }, command.timeout, command.trace, normalizeRpcBackground(command.background))
2627
+ : actor.call(String(command.method ?? ''), command.params ?? undefined, command.timeout, command.trace, normalizeRpcBackground(command.background)));
2466
2628
  if (id)
2467
2629
  active.set(id, request);
2468
2630
  result = await request;
@@ -2470,6 +2632,9 @@ if (!isMainThread && Boolean(workerData?.aunTransportWorker) && parentPort) {
2470
2632
  active.delete(id);
2471
2633
  break;
2472
2634
  }
2635
+ case 'event_backpressure':
2636
+ result = await actor._eventBackpressure();
2637
+ break;
2473
2638
  case 'notify':
2474
2639
  result = await actor.notify(String(command.method ?? ''), command.params);
2475
2640
  break;