@agentunion/fastaun 0.5.18 → 0.5.20

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 (122) hide show
  1. package/CHANGELOG.md +172 -79
  2. package/_packed_docs/CHANGELOG.md +172 -79
  3. package/_packed_docs/INDEX.md +244 -116
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +98 -69
  5. package/_packed_docs/agent.md/SCHEMA.md +245 -205
  6. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +11 -11
  7. 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 +42 -15
  8. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +4 -4
  9. package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +45 -134
  10. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1072 -1072
  11. package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +5 -20
  12. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +687 -668
  13. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +19 -28
  14. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +443 -0
  15. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
  16. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +658 -0
  17. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
  18. package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
  19. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +1 -1
  20. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +68 -61
  21. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
  22. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +279 -285
  23. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
  24. package/_packed_docs/sdk/09-group-rpc-manual.md +271 -235
  25. package/_packed_docs/sdk/09-message-rpc-manual.md +47 -6
  26. package/_packed_docs/sdk/09-storage-rpc-manual.md +70 -68
  27. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +63 -34
  28. package/_packed_docs/sdk/INDEX.md +105 -34
  29. package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
  30. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
  31. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +922 -0
  32. package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
  33. package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +300 -0
  34. package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +382 -0
  35. package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -0
  36. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +476 -0
  37. package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
  38. package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
  39. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +173 -0
  40. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +211 -0
  41. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/345/256/236/347/216/260/347/247/273/346/244/215/346/214/207/345/215/227.md +331 -0
  42. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +207 -0
  43. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/344/273/243/347/240/201/345/256/241/346/237/245/346/212/245/345/221/212.md +252 -0
  44. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/345/256/236/346/226/275/350/256/241/345/210/222.md +156 -0
  45. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/346/226/271/346/241/210.md +273 -0
  46. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/350/257/246/347/273/206/350/256/276/350/256/241.md +211 -0
  47. package/dist/agent-md-schema.d.ts +15 -1
  48. package/dist/agent-md-schema.js +87 -24
  49. package/dist/agent-md-schema.js.map +1 -1
  50. package/dist/agent-md.d.ts +54 -2
  51. package/dist/agent-md.js +637 -53
  52. package/dist/agent-md.js.map +1 -1
  53. package/dist/aid-store.d.ts +38 -59
  54. package/dist/aid-store.js +300 -265
  55. package/dist/aid-store.js.map +1 -1
  56. package/dist/aid.js +30 -11
  57. package/dist/aid.js.map +1 -1
  58. package/dist/auth.d.ts +6 -1
  59. package/dist/auth.js +500 -15
  60. package/dist/auth.js.map +1 -1
  61. package/dist/cert-utils.d.ts +44 -2
  62. package/dist/cert-utils.js +149 -49
  63. package/dist/cert-utils.js.map +1 -1
  64. package/dist/client/delivery.d.ts +11 -1
  65. package/dist/client/delivery.js +321 -62
  66. package/dist/client/delivery.js.map +1 -1
  67. package/dist/client/rpc-pipeline.d.ts +1 -0
  68. package/dist/client/rpc-pipeline.js +142 -82
  69. package/dist/client/rpc-pipeline.js.map +1 -1
  70. package/dist/client/v2-e2ee.d.ts +1 -0
  71. package/dist/client/v2-e2ee.js +166 -96
  72. package/dist/client/v2-e2ee.js.map +1 -1
  73. package/dist/client.d.ts +84 -1
  74. package/dist/client.js +780 -30
  75. package/dist/client.js.map +1 -1
  76. package/dist/errors.js +4 -3
  77. package/dist/errors.js.map +1 -1
  78. package/dist/events.d.ts +9 -3
  79. package/dist/events.js +69 -11
  80. package/dist/events.js.map +1 -1
  81. package/dist/facades.d.ts +83 -3
  82. package/dist/facades.js +100 -6
  83. package/dist/facades.js.map +1 -1
  84. package/dist/index.d.ts +9 -4
  85. package/dist/index.js +3 -2
  86. package/dist/index.js.map +1 -1
  87. package/dist/keystore/aid-db.d.ts +5 -0
  88. package/dist/keystore/aid-db.js +57 -0
  89. package/dist/keystore/aid-db.js.map +1 -1
  90. package/dist/keystore/index.d.ts +8 -0
  91. package/dist/keystore/local-identity-store.js +10 -3
  92. package/dist/keystore/local-identity-store.js.map +1 -1
  93. package/dist/keystore/local-token-store.d.ts +6 -1
  94. package/dist/keystore/local-token-store.js +24 -1
  95. package/dist/keystore/local-token-store.js.map +1 -1
  96. package/dist/org-protocol.d.ts +98 -0
  97. package/dist/org-protocol.js +1753 -0
  98. package/dist/org-protocol.js.map +1 -0
  99. package/dist/ownership-proof.d.ts +82 -5
  100. package/dist/ownership-proof.js +327 -31
  101. package/dist/ownership-proof.js.map +1 -1
  102. package/dist/ownership-v21.d.ts +196 -0
  103. package/dist/ownership-v21.js +712 -0
  104. package/dist/ownership-v21.js.map +1 -0
  105. package/dist/ownership-workflow.d.ts +87 -0
  106. package/dist/ownership-workflow.js +1182 -0
  107. package/dist/ownership-workflow.js.map +1 -0
  108. package/dist/seq-tracker.d.ts +1 -0
  109. package/dist/seq-tracker.js +10 -1
  110. package/dist/seq-tracker.js.map +1 -1
  111. package/dist/tools/cross-sdk-agent.d.ts +90 -1
  112. package/dist/tools/cross-sdk-agent.js +441 -7
  113. package/dist/tools/cross-sdk-agent.js.map +1 -1
  114. package/dist/transport.d.ts +8 -0
  115. package/dist/transport.js +118 -4
  116. package/dist/transport.js.map +1 -1
  117. package/dist/v2/e2ee/decrypt.d.ts +1 -0
  118. package/dist/v2/e2ee/decrypt.js +9 -1
  119. package/dist/v2/e2ee/decrypt.js.map +1 -1
  120. package/dist/version.d.ts +1 -1
  121. package/dist/version.js +1 -1
  122. package/package.json +1 -1
@@ -61,6 +61,7 @@ export declare class RpcPipeline {
61
61
  background?: boolean;
62
62
  pullGateKey?: string;
63
63
  }): Promise<unknown>;
64
+ private observeBackgroundPullResponse;
64
65
  private transportCallWithIdentityRecovery;
65
66
  postprocessResult(method: string, params: RpcParams, result: unknown, options?: {
66
67
  skipGroupState?: boolean;
@@ -1,6 +1,8 @@
1
1
  import * as crypto from 'node:crypto';
2
2
  import { normalizeSlotId, slotIsolationKey } from '../config.js';
3
3
  import { certTimeError } from '../cert-utils.js';
4
+ import { orgRequestParams, validateOrgRequestJSON } from '../org-protocol.js';
5
+ import { canonicalJson as canonicalClientRequest } from '../v2/crypto/canonical.js';
4
6
  import { ClientSignatureError, ConnectionError, PermissionError, TimeoutError, ValidationError } from '../errors.js';
5
7
  import { ConnectionState, isJsonObject, } from '../types.js';
6
8
  import { validateAIDFormat, validateGroupIDFormat } from '../validators.js';
@@ -113,6 +115,7 @@ function getCertFingerprint(certPem) {
113
115
  /** 旧式 tryAcquirePullGate 的防并发窗口。 */
114
116
  const PULL_GATE_STALE_MS = 35_000;
115
117
  const PULL_GATE_OPERATION_TIMEOUT_MS = 35_000;
118
+ const PULL_GATE_MAX_ACTIVE = 4;
116
119
  function sameIdentityAdmissionRejection(error, original) {
117
120
  const errorCode = Number(error?.code);
118
121
  const originalCode = Number(original?.code);
@@ -165,6 +168,7 @@ export class RpcPipeline {
165
168
  }
166
169
  async call(method, params) {
167
170
  const client = this.runtime.client;
171
+ const generation = client._delivery?.captureInlineAckGeneration?.();
168
172
  const tStart = Date.now();
169
173
  const requestedAckSeq = method === 'message.ack' || method === 'message.v2.ack'
170
174
  ? Number(params?.seq
@@ -355,6 +359,9 @@ export class RpcPipeline {
355
359
  return this.bindActivePullCancellation(method, p, request);
356
360
  };
357
361
  let result = await this.transportCallWithIdentityRecovery(invokeTransport, method, p);
362
+ if (generation === client._delivery?.captureInlineAckGeneration?.()) {
363
+ this.observeBackgroundPullResponse(method, p, result, pullGateNamespace);
364
+ }
358
365
  const sendResultModeSource = result;
359
366
  result = await this.postprocessResult(method, p, result, {
360
367
  skipGroupState: deferGroupReadyPostprocess,
@@ -370,6 +377,10 @@ export class RpcPipeline {
370
377
  client._clientLog.debug(`call exit (error): method=${method} elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
371
378
  throw err;
372
379
  }
380
+ finally {
381
+ if (generation === client._delivery?.captureInlineAckGeneration?.())
382
+ client._delivery?.requestBackgroundPullRecovery?.();
383
+ }
373
384
  }
374
385
  preflight(method, params) {
375
386
  const client = this.runtime.client;
@@ -502,7 +513,10 @@ export class RpcPipeline {
502
513
  }
503
514
  applyClientSignature(method, params) {
504
515
  try {
505
- if (!SIGNED_METHODS.has(method)) {
516
+ if (method.startsWith('org.')) {
517
+ params.aad = orgRequestParams(method, params).aad;
518
+ }
519
+ else if (!SIGNED_METHODS.has(method)) {
506
520
  return;
507
521
  }
508
522
  if (this.shouldSkipClientSignature(method, params)) {
@@ -527,8 +541,11 @@ export class RpcPipeline {
527
541
  const currentAid = (internal && typeof internal === 'object')
528
542
  ? internal
529
543
  : this.runtime.client._currentAid;
530
- if (!currentAid?.privateKeyPem)
544
+ if (!currentAid?.privateKeyPem) {
545
+ if (method.startsWith('org.'))
546
+ throw new ClientSignatureError('组织请求签名身份不可用');
531
547
  return;
548
+ }
532
549
  try {
533
550
  const aid = currentAid.aid;
534
551
  const certPem = currentAid.certPem ?? '';
@@ -543,8 +560,16 @@ export class RpcPipeline {
543
560
  paramsForHash[k] = v;
544
561
  }
545
562
  }
546
- const paramsJson = stableStringify(paramsForHash);
563
+ if (method.startsWith('org.'))
564
+ validateOrgRequestJSON(paramsForHash);
565
+ const paramsJson = method.startsWith('org.') ? new TextDecoder().decode(canonicalClientRequest(paramsForHash)) : stableStringify(paramsForHash);
547
566
  const paramsHash = crypto.createHash('sha256').update(paramsJson, 'utf-8').digest('hex');
567
+ if (params.client_signature !== undefined) {
568
+ const existing = params.client_signature;
569
+ if (!isJsonObject(existing) || existing.aid !== aid || existing.cert_fingerprint !== getCertFingerprint(certPem) || existing.params_hash !== paramsHash || typeof existing.timestamp !== 'string' || !/^\d+$/.test(existing.timestamp) || typeof existing.signature !== 'string' || !crypto.verify('SHA256', Buffer.from(`${method}|${aid}|${existing.timestamp}|${paramsHash}`, 'utf-8'), crypto.createPublicKey(certPem), Buffer.from(existing.signature, 'base64')))
570
+ throw new Error('已有客户端签名与请求内容不一致');
571
+ return;
572
+ }
548
573
  const signData = Buffer.from(`${method}|${aid}|${ts}|${paramsHash}`, 'utf-8');
549
574
  const privateKey = getSigningPrivateKey(currentAid.privateKeyPem);
550
575
  const signature = crypto.sign('SHA256', signData, privateKey);
@@ -634,7 +659,7 @@ export class RpcPipeline {
634
659
  const name = this.pullGateName(key);
635
660
  let state = this.pullGateStates.get(name);
636
661
  if (!state) {
637
- state = { active: null, foreground: [], background: [], byKey: new Map(), lifecycle: new Map(), watchdog: null };
662
+ state = { activeByNamespace: new Map(), foreground: [], background: [], byKey: new Map(), lifecycle: new Map(), watchdog: null };
638
663
  this.pullGateStates.set(name, state);
639
664
  }
640
665
  this.startPullGateWatchdog(state, name);
@@ -665,23 +690,25 @@ export class RpcPipeline {
665
690
  }
666
691
  }
667
692
  checkPullGate(state, name) {
668
- const active = state.active;
669
- if (!active) {
693
+ const activeJobs = [...state.activeByNamespace.values()];
694
+ if (activeJobs.length === 0) {
670
695
  this.drainPullGate(state);
671
696
  return;
672
697
  }
673
- if (!active.pullingStartedAt || Date.now() - active.pullingStartedAt < this.pullGateOperationTimeoutMs())
674
- return;
675
- if (active.timedOut)
676
- return;
677
- active.timedOut = true;
678
- active.invalidated = true;
679
- active.cancel?.();
680
- const timeout = new TimeoutError(`pull gate timeout: ${active.namespace}`, { retryable: true });
681
- this.runtime.client._clientLog.warn(`pull gate watchdog timeout: gate=${name} key=${active.key} elapsed=${Date.now() - active.pullingStartedAt}ms`);
682
- // 仅拒绝当前调用方;取消不敏感的 operation 仍可能运行,必须等其
683
- // 真正 settle 后才释放 active,避免与后续 Pull 并发修改游标。
684
- active.reject(timeout);
698
+ for (const active of activeJobs) {
699
+ if (!active.pullingStartedAt || Date.now() - active.pullingStartedAt < this.pullGateOperationTimeoutMs())
700
+ continue;
701
+ if (active.timedOut)
702
+ continue;
703
+ active.timedOut = true;
704
+ active.invalidated = true;
705
+ active.cancel?.();
706
+ const timeout = new TimeoutError(`pull gate timeout: ${active.namespace}`, { retryable: true });
707
+ this.runtime.client._clientLog.warn(`pull gate watchdog timeout: gate=${name} key=${active.key} elapsed=${Date.now() - active.pullingStartedAt}ms`);
708
+ // 仅拒绝当前调用方;取消不敏感的 operation 仍可能运行,必须等其
709
+ // 真正 settle 后才释放 active,避免与后续 Pull 并发修改游标。
710
+ active.reject(timeout);
711
+ }
685
712
  }
686
713
  pullScopeKey(key) {
687
714
  return key.split('|', 1)[0] ?? key;
@@ -696,7 +723,7 @@ export class RpcPipeline {
696
723
  if (typeof cancel !== 'function' || !key)
697
724
  return request;
698
725
  const state = this.pullGateStates.get(this.pullGateName(key));
699
- const active = state?.active;
726
+ const active = [...(state?.activeByNamespace.values() ?? [])].find((candidate) => candidate.key === key);
700
727
  if (!active || active.key !== key || active.timedOut || active.invalidated)
701
728
  return request;
702
729
  const boundCancel = () => cancel.call(request);
@@ -734,8 +761,8 @@ export class RpcPipeline {
734
761
  return true;
735
762
  }
736
763
  for (const state of this.pullGateStates.values()) {
737
- const jobs = [state.active, ...state.foreground, ...state.background];
738
- if (jobs.some((job) => job !== null && this.pullScopeKey(job.key) === ns))
764
+ const jobs = [...state.activeByNamespace.values(), ...state.foreground, ...state.background];
765
+ if (jobs.some((job) => this.pullScopeKey(job.key) === ns))
739
766
  return true;
740
767
  }
741
768
  return false;
@@ -798,7 +825,7 @@ export class RpcPipeline {
798
825
  const ns = this.pullScopeKey(namespace);
799
826
  if (!ns)
800
827
  return true;
801
- const active = this.pullGateStates.get(this.pullGateName(ns))?.active;
828
+ const active = this.pullGateStates.get(this.pullGateName(ns))?.activeByNamespace.get(ns);
802
829
  return !active || active.namespace !== ns || (!active.timedOut && !active.invalidated);
803
830
  }
804
831
  invalidatePulls() {
@@ -809,8 +836,8 @@ export class RpcPipeline {
809
836
  const waits = [];
810
837
  for (const state of this.pullGateStates.values()) {
811
838
  const jobs = new Set();
812
- if (state.active)
813
- jobs.add(state.active);
839
+ for (const active of state.activeByNamespace.values())
840
+ jobs.add(active);
814
841
  for (const job of state.foreground)
815
842
  jobs.add(job);
816
843
  for (const job of state.background)
@@ -851,6 +878,9 @@ export class RpcPipeline {
851
878
  return this.pullInvalidationWait !== null;
852
879
  }
853
880
  async runPullSerialized(key, operation, background = false) {
881
+ if (this.runtime.client._dispatcher?._isApplicationHandler?.()) {
882
+ throw new Error('pull cannot wait inside an application handler');
883
+ }
854
884
  if (this.pullInvalidationInProgress()) {
855
885
  throw new Error('pull invalidated');
856
886
  }
@@ -865,7 +895,7 @@ export class RpcPipeline {
865
895
  }
866
896
  const state = this.pullGateState(key);
867
897
  const namespace = this.pullScopeKey(key);
868
- const active = state.active?.key === key && !state.active.timedOut && !state.active.invalidated ? state.active : null;
898
+ const active = [...state.activeByNamespace.values()].find((job) => job.key === key && !job.timedOut && !job.invalidated) ?? null;
869
899
  const queuedForeground = state.foreground.find((job) => job.key === key && !job.timedOut && !job.invalidated);
870
900
  const queuedBackground = state.background.find((job) => job.key === key && !job.timedOut && !job.invalidated);
871
901
  const existing = background
@@ -925,8 +955,8 @@ export class RpcPipeline {
925
955
  return;
926
956
  const state = this.pullGateState(key);
927
957
  const namespace = this.pullScopeKey(key);
928
- const job = state.active?.key === key ? state.active : null;
929
- if (!job || state.active !== job || job.timedOut || job.invalidated)
958
+ const job = [...state.activeByNamespace.values()].find((candidate) => candidate.key === key) ?? null;
959
+ if (!job || job.timedOut || job.invalidated)
930
960
  return;
931
961
  const replacementKey = String(nextKey || key);
932
962
  if (state.byKey.get(job.key) === job)
@@ -945,7 +975,7 @@ export class RpcPipeline {
945
975
  state.lifecycle.set(namespace, 'pulled');
946
976
  job.suspended = true;
947
977
  job.resume = new Promise((resolve) => { job.resumeResolve = resolve; });
948
- state.active = null;
978
+ state.activeByNamespace.delete(namespace);
949
979
  (job.background ? state.background : state.foreground).push(job);
950
980
  this.drainPullGate(state);
951
981
  if (job.resume) {
@@ -965,63 +995,68 @@ export class RpcPipeline {
965
995
  if (!ns)
966
996
  return;
967
997
  const state = this.pullGateStates.get(this.pullGateName(ns));
968
- if (!state || state.active?.namespace !== ns)
998
+ if (!state || !state.activeByNamespace.has(ns))
969
999
  return;
970
1000
  state.lifecycle.set(ns, lifecycle);
971
1001
  }
972
1002
  drainPullGate(state) {
973
- if (state.active)
974
- return;
975
1003
  const takeRunnable = (queue) => {
976
- const index = queue.findIndex((candidate) => !this.hasInlineRealtimeGuard(this.pullScopeKey(candidate.key)));
1004
+ const index = queue.findIndex((candidate) => (!this.hasInlineRealtimeGuard(this.pullScopeKey(candidate.key))
1005
+ && !state.activeByNamespace.has(candidate.namespace)));
977
1006
  return index >= 0 ? queue.splice(index, 1)[0] : undefined;
978
1007
  };
979
- const job = takeRunnable(state.foreground) ?? takeRunnable(state.background);
980
- if (!job)
981
- return;
982
- state.active = job;
983
- this.runtime.client._delivery?.syncStarted?.(job.namespace);
984
- if (job.timer) {
985
- clearTimeout(job.timer);
986
- job.timer = null;
987
- }
988
- job.pullingStartedAt = Date.now();
989
- job.timedOut = false;
990
- state.lifecycle.set(job.namespace, 'pulling');
991
- const resumeResolve = job.resumeResolve;
992
- job.resume = null;
993
- job.resumeResolve = null;
994
- if (job.suspended) {
995
- job.suspended = false;
996
- resumeResolve?.();
997
- return;
998
- }
999
- job.running = true;
1000
- const operationPromise = this.executePullOperation(job.operation, job.background);
1001
- let failed = false;
1002
- void operationPromise
1003
- .then((value) => { if (!job.timedOut && !job.invalidated)
1004
- job.resolve(value); }, (error) => {
1005
- failed = true;
1006
- if (!job.timedOut && !job.invalidated)
1007
- job.reject(error);
1008
- })
1009
- .finally(() => {
1010
- state.lifecycle.set(job.namespace, 'idle');
1011
- if (state.active === job)
1012
- state.active = null;
1013
- if (state.byKey.get(job.key) === job)
1014
- state.byKey.delete(job.key);
1015
- this.drainPullGate(state);
1016
- job.settledResolve?.();
1017
- job.settledResolve = null;
1018
- if (!job.invalidated) {
1019
- this.runtime.client._delivery?.onPullGateIdle?.(job.namespace);
1008
+ while (state.activeByNamespace.size < PULL_GATE_MAX_ACTIVE) {
1009
+ const foregroundJob = takeRunnable(state.foreground);
1010
+ const foregroundDrained = foregroundJob !== undefined && state.foreground.length === 0;
1011
+ const job = foregroundJob ?? takeRunnable(state.background);
1012
+ if (!job)
1013
+ break;
1014
+ state.activeByNamespace.set(job.namespace, job);
1015
+ this.runtime.client._delivery?.syncStarted?.(job.namespace);
1016
+ if (foregroundDrained)
1017
+ this.runtime.client._delivery?.onForegroundPullsDrained?.();
1018
+ if (job.timer) {
1019
+ clearTimeout(job.timer);
1020
+ job.timer = null;
1020
1021
  }
1021
- if (failed || job.invalidated)
1022
- this.runtime.client._delivery?.onPullAborted?.();
1023
- this.notifyPullDrainedIfIdle();
1024
- });
1022
+ job.pullingStartedAt = Date.now();
1023
+ job.timedOut = false;
1024
+ state.lifecycle.set(job.namespace, 'pulling');
1025
+ const resumeResolve = job.resumeResolve;
1026
+ job.resume = null;
1027
+ job.resumeResolve = null;
1028
+ if (job.suspended) {
1029
+ job.suspended = false;
1030
+ resumeResolve?.();
1031
+ continue;
1032
+ }
1033
+ job.running = true;
1034
+ const operationPromise = this.executePullOperation(job.operation, job.background);
1035
+ let failed = false;
1036
+ void operationPromise
1037
+ .then((value) => { if (!job.timedOut && !job.invalidated)
1038
+ job.resolve(value); }, (error) => {
1039
+ failed = true;
1040
+ if (!job.timedOut && !job.invalidated)
1041
+ job.reject(error);
1042
+ })
1043
+ .finally(() => {
1044
+ state.lifecycle.set(job.namespace, 'idle');
1045
+ if (state.activeByNamespace.get(job.namespace) === job)
1046
+ state.activeByNamespace.delete(job.namespace);
1047
+ if (state.byKey.get(job.key) === job)
1048
+ state.byKey.delete(job.key);
1049
+ this.drainPullGate(state);
1050
+ job.settledResolve?.();
1051
+ job.settledResolve = null;
1052
+ if (!job.invalidated) {
1053
+ this.runtime.client._delivery?.onPullGateIdle?.(job.namespace);
1054
+ }
1055
+ if (failed || job.invalidated)
1056
+ this.runtime.client._delivery?.onPullAborted?.();
1057
+ this.notifyPullDrainedIfIdle();
1058
+ });
1059
+ }
1025
1060
  }
1026
1061
  notifyPullDrainedIfIdle() {
1027
1062
  if (this.pullDrainedCheckQueued)
@@ -1037,7 +1072,7 @@ export class RpcPipeline {
1037
1072
  }
1038
1073
  notifyPullDrainedIfIdleStable() {
1039
1074
  for (const state of this.pullGateStates.values()) {
1040
- if (state.active || state.foreground.length > 0 || state.background.length > 0)
1075
+ if (state.activeByNamespace.size > 0 || state.foreground.length > 0 || state.background.length > 0)
1041
1076
  return;
1042
1077
  }
1043
1078
  const client = this.runtime.client;
@@ -1055,7 +1090,7 @@ export class RpcPipeline {
1055
1090
  const { timeout, trace, signed = true, background = false, pullGateKey = '' } = opts;
1056
1091
  const client = this.runtime.client;
1057
1092
  const payload = params ? { ...params } : {};
1058
- if (signed) {
1093
+ if (signed || method.startsWith('org.')) {
1059
1094
  this.applyClientSignature(method, payload);
1060
1095
  }
1061
1096
  const invokeTransport = () => {
@@ -1070,9 +1105,34 @@ export class RpcPipeline {
1070
1105
  request = client._transport.call(method, payload);
1071
1106
  return this.bindActivePullCancellation(method, payload, request, pullGateKey);
1072
1107
  };
1073
- const result = await this.transportCallWithIdentityRecovery(invokeTransport, method, payload);
1074
- this.throwIfPullInvalidated(this.pullScopeKey(pullGateKey || this.pullGateKeyForCall(method, payload)));
1075
- return result;
1108
+ const generation = client._delivery?.captureInlineAckGeneration?.();
1109
+ try {
1110
+ const result = await this.transportCallWithIdentityRecovery(invokeTransport, method, payload);
1111
+ this.throwIfPullInvalidated(this.pullScopeKey(pullGateKey || this.pullGateKeyForCall(method, payload)));
1112
+ if (generation === client._delivery?.captureInlineAckGeneration?.()) {
1113
+ this.observeBackgroundPullResponse(method, payload, result, pullGateKey);
1114
+ }
1115
+ return result;
1116
+ }
1117
+ finally {
1118
+ if (generation === client._delivery?.captureInlineAckGeneration?.())
1119
+ client._delivery?.requestBackgroundPullRecovery?.();
1120
+ }
1121
+ }
1122
+ observeBackgroundPullResponse(method, params, result, key = '') {
1123
+ if (!['message.pull', 'message.v2.pull', 'group.pull', 'group.v2.pull', 'group.pull_events'].includes(method)
1124
+ || params.force === true || params.window_mode === 'tail')
1125
+ return;
1126
+ const client = this.runtime.client;
1127
+ if (method.startsWith('group.') && !this.groupCursorTargetsCurrentInstance(params))
1128
+ return;
1129
+ const explicitCursor = this.explicitGroupCursorParams(params);
1130
+ if (method.startsWith('group.') && !this.groupCursorTargetsCurrentInstance(explicitCursor))
1131
+ return;
1132
+ const ns = this.pullScopeKey(key) || (method === 'group.pull_events'
1133
+ ? `group_event:${String(params.group_id ?? params.group_aid ?? '')}`
1134
+ : method.startsWith('group.') ? `group:${String(params.group_id ?? params.group_aid ?? '')}` : `p2p:${client._aid}`);
1135
+ client._delivery?.observeBackgroundPullResponse?.(ns, Number(params.after_seq ?? params.after_event_seq ?? 0), result, Number(params.limit ?? 50));
1076
1136
  }
1077
1137
  async transportCallWithIdentityRecovery(operation, method, params) {
1078
1138
  try {