@agentunion/fastaun 0.4.3 → 0.4.5

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 (71) hide show
  1. package/CHANGELOG.md +213 -185
  2. package/_packed_docs/CHANGELOG.md +213 -185
  3. package/_packed_docs/INDEX.md +17 -17
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +11 -11
  5. package/_packed_docs/agent.md/SCHEMA.md +49 -49
  6. package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +22 -22
  7. 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 +327 -327
  8. package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +686 -686
  9. package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +542 -542
  10. package/_packed_docs/design/E2EE_V2/347/256/200/345/214/226/344/270/2721DH/345/212/240Per-AID_Wrap/346/226/271/346/241/210.md +124 -124
  11. package/_packed_docs/design//350/267/250/350/257/255/350/250/200/345/256/271/345/231/250E2E/346/265/213/350/257/225/346/226/271/346/241/210.md +665 -665
  12. 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 +2 -2
  13. package/_packed_docs/protocol/14-/344/272/244/344/272/222/346/234/272/345/210/266-/345/223/215/345/272/224/346/250/241/345/274/217/344/270/216/350/207/252/344/270/273/346/250/241/345/274/217.md +170 -170
  14. 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 +419 -419
  15. package/_packed_docs/protocol/README.md +1 -1
  16. package/_packed_docs/protocol/aun-docs-guide.md +1 -1
  17. package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +15 -15
  18. package/_packed_docs/protocol//351/231/204/345/275/225B-/346/211/251/345/261/225/346/200/247/346/214/207/345/215/227.md +4 -4
  19. package/_packed_docs/protocol//351/231/204/345/275/225J-/345/256/242/346/210/267/347/253/257/346/216/245/345/205/245/347/244/272/344/276/213.md +98 -98
  20. package/_packed_docs/protocol//351/231/204/345/275/225M-JWT/350/256/244/350/257/201/345/256/236/347/216/260/346/214/207/345/215/227.md +46 -46
  21. package/_packed_docs/protocol//351/231/204/345/275/225N-/345/210/206/345/270/203/345/274/217Trace/345/215/217/350/256/256.md +257 -257
  22. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
  23. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
  24. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +1 -0
  25. package/_packed_docs/sdk/09-payload-reference.md +13 -13
  26. package/_packed_docs/sdk/E2EE_V2/346/266/210/346/201/257/351/200/232/344/277/241/346/227/266/345/272/217/345/233/276.md +171 -171
  27. package/dist/aid-store.d.ts +1 -0
  28. package/dist/aid-store.js +26 -3
  29. package/dist/aid-store.js.map +1 -1
  30. package/dist/aid.d.ts +2 -1
  31. package/dist/aid.js +7 -6
  32. package/dist/aid.js.map +1 -1
  33. package/dist/auth.d.ts +17 -32
  34. package/dist/auth.js +42 -291
  35. package/dist/auth.js.map +1 -1
  36. package/dist/client.d.ts +10 -3
  37. package/dist/client.js +275 -138
  38. package/dist/client.js.map +1 -1
  39. package/dist/index.d.ts +1 -0
  40. package/dist/index.js +1 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/keystore/aid-db.d.ts +0 -4
  43. package/dist/keystore/aid-db.js +4 -95
  44. package/dist/keystore/aid-db.js.map +1 -1
  45. package/dist/keystore/file.d.ts +8 -5
  46. package/dist/keystore/file.js +109 -68
  47. package/dist/keystore/file.js.map +1 -1
  48. package/dist/keystore/index.d.ts +39 -36
  49. package/dist/keystore/index.js +3 -2
  50. package/dist/keystore/index.js.map +1 -1
  51. package/dist/register-flow.d.ts +49 -0
  52. package/dist/register-flow.js +366 -0
  53. package/dist/register-flow.js.map +1 -0
  54. package/dist/secret-store/file-store.js +6 -1
  55. package/dist/secret-store/file-store.js.map +1 -1
  56. package/dist/tools/cross-sdk-agent.js +0 -9
  57. package/dist/tools/cross-sdk-agent.js.map +1 -1
  58. package/dist/transport.d.ts +1 -0
  59. package/dist/transport.js +7 -1
  60. package/dist/transport.js.map +1 -1
  61. package/dist/v2/session/keystore.d.ts +5 -0
  62. package/dist/v2/session/keystore.js +21 -3
  63. package/dist/v2/session/keystore.js.map +1 -1
  64. package/dist/version.d.ts +1 -1
  65. package/dist/version.js +1 -1
  66. package/package.json +1 -1
  67. package/_packed_docs/0.4.0_/345/267/256/345/274/202/346/240/270/345/256/236/345/206/263/347/255/226/350/256/260/345/275/225.md +0 -302
  68. package/_packed_docs/AUN_SDK_0.4.0_/350/256/276/350/256/241/345/257/271/346/257/224/345/210/206/346/236/220.md +0 -194
  69. package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/345/256/236/346/226/275/350/256/241/345/210/222.md +0 -596
  70. package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/350/256/276/350/256/241/346/226/271/346/241/210_v3.md +0 -1697
  71. package/_packed_docs/python-sdk-v2-only-changelog.md +0 -189
package/dist/client.js CHANGED
@@ -37,6 +37,14 @@ import { AID } from './aid.js';
37
37
  function isPromiseLike(value) {
38
38
  return Boolean(value && typeof value.then === 'function');
39
39
  }
40
+ function isAIDObject(value) {
41
+ const candidate = value;
42
+ return Boolean(candidate
43
+ && typeof candidate === 'object'
44
+ && typeof candidate.aid === 'string'
45
+ && typeof candidate.aunPath === 'string'
46
+ && typeof candidate.isPrivateKeyValid === 'function');
47
+ }
40
48
  /**
41
49
  * 递归排序键的 JSON 序列化(Canonical JSON for AUN)
42
50
  * 等价于 Python json.dumps(sort_keys=True, separators=(",",":"), ensure_ascii=False)
@@ -128,6 +136,20 @@ const DEFAULT_SESSION_OPTIONS = {
128
136
  http: 30.0,
129
137
  },
130
138
  };
139
+ const PUBLIC_CONNECTION_OPTION_KEYS = new Set([
140
+ 'auto_reconnect',
141
+ 'connect_timeout',
142
+ 'retry_initial_delay',
143
+ 'retry_max_delay',
144
+ 'retry_max_attempts',
145
+ 'heartbeat_interval',
146
+ 'call_timeout',
147
+ 'connection_kind',
148
+ 'short_ttl_ms',
149
+ 'delivery_mode',
150
+ 'extra_info',
151
+ 'background_sync',
152
+ ]);
131
153
  const PROTECTED_HEADERS_METHODS = new Set([
132
154
  'message.send',
133
155
  'group.send',
@@ -444,7 +466,7 @@ export class AUNClient {
444
466
  /** 认证流程 */
445
467
  _auth;
446
468
  /** 密钥存储 */
447
- _keystore;
469
+ _tokenStore;
448
470
  /** 会话参数(重连用) */
449
471
  _sessionParams = null;
450
472
  /** 会话选项 */
@@ -498,6 +520,7 @@ export class AUNClient {
498
520
  // ── V2 E2EE 状态 ──────────────────────────────────────────────
499
521
  _v2Session;
500
522
  _v2KeyStore;
523
+ _v2SessionInitInFlight = null;
501
524
  /** V2 bootstrap 缓存:aid/group:id → 设备列表 + 时间戳 */
502
525
  _v2BootstrapCache = new Map();
503
526
  _connectCapabilities = null;
@@ -527,10 +550,10 @@ export class AUNClient {
527
550
  _logger;
528
551
  _clientLog;
529
552
  constructor(aid) {
530
- if (typeof aid === 'string') {
531
- throw new ValidationError('AUNClient aid must be an AID object, not a string');
553
+ if (aid !== null && aid !== undefined && !isAIDObject(aid)) {
554
+ throw new ValidationError('AUNClient only accepts an AID object or no argument');
532
555
  }
533
- const inputAid = (aid !== null && aid !== undefined && typeof aid.aunPath === 'string' && typeof aid.isPrivateKeyValid === 'function') ? aid : null;
556
+ const inputAid = aid ?? null;
534
557
  const rawConfig = {};
535
558
  if (inputAid) {
536
559
  rawConfig.aun_path = inputAid.aunPath;
@@ -565,30 +588,16 @@ export class AUNClient {
565
588
  logger: this._clientLog,
566
589
  });
567
590
  this._discovery = new GatewayDiscovery({ verifySsl: this._configModel.verifySsl, logger: this._clientLog, net: dnsNet });
568
- const keystore = new FileKeyStore(this._configModel.aunPath, {
569
- encryptionSeed: this._configModel.seedPassword ?? undefined,
591
+ const tokenStore = new FileKeyStore(this._configModel.aunPath, {
570
592
  logger: this._logger.for('aun_core.keystore'),
571
593
  secretStoreLogger: this._logger.for('aun_core.secret-store'),
572
594
  });
573
- this._keystore = keystore;
574
- // 启动时被动清理 registerAid 留下的孤儿临时目录(>10 分钟)
575
- try {
576
- const cleanup = keystore.cleanupPendingDirs;
577
- if (typeof cleanup === 'function') {
578
- const removed = cleanup.call(keystore, 600_000);
579
- if (removed > 0) {
580
- this._clientLog.info(`_pending cleanup removed=${removed}`);
581
- }
582
- }
583
- }
584
- catch (err) {
585
- this._clientLog.warn(`_pending cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
586
- }
595
+ this._tokenStore = tokenStore;
587
596
  this._slotId = inputAid?.slotId || 'default';
588
597
  this._connectDeliveryMode = normalizeDeliveryModeConfig({ mode: 'fanout' });
589
598
  this._defaultConnectDeliveryMode = { ...this._connectDeliveryMode };
590
599
  this._auth = new AuthFlow({
591
- keystore,
600
+ tokenStore,
592
601
  crypto: new CryptoProvider(),
593
602
  aid: initAid,
594
603
  deviceId: this._deviceId,
@@ -614,10 +623,11 @@ export class AUNClient {
614
623
  this._currentAid = inputAid;
615
624
  this._identity = {
616
625
  aid: inputAid.aid,
617
- private_key_pem: inputAid._privateKeyPem ?? '',
626
+ private_key_pem: inputAid.privateKeyPem,
618
627
  public_key_der_b64: inputAid.publicKey,
619
628
  cert: inputAid.certPem,
620
629
  };
630
+ this._auth.setIdentity(this._identity);
621
631
  this._state = 'standby';
622
632
  }
623
633
  }
@@ -697,6 +707,71 @@ export class AUNClient {
697
707
  get lastErrorCode() {
698
708
  return this._lastErrorCode;
699
709
  }
710
+ _applyAidRuntimeContext(aid) {
711
+ const rawConfig = {
712
+ aun_path: aid.aunPath,
713
+ verify_ssl: aid.verifySsl,
714
+ debug: aid.debug,
715
+ };
716
+ if (aid.rootCaPath)
717
+ rawConfig.root_ca_path = aid.rootCaPath;
718
+ const nextConfig = configFromMap(rawConfig);
719
+ try {
720
+ const close = this._tokenStore.close;
721
+ if (typeof close === 'function')
722
+ close.call(this._tokenStore);
723
+ }
724
+ catch {
725
+ // best-effort cleanup before switching tokenStore roots
726
+ }
727
+ this._configModel = nextConfig;
728
+ this.config.aun_path = nextConfig.aunPath;
729
+ this.config.root_ca_path = nextConfig.rootCaPath;
730
+ this.config.seed_password = nextConfig.seedPassword;
731
+ this._agentMdPath = path.join(nextConfig.aunPath, 'AIDs');
732
+ this._agentMdCache.clear();
733
+ this._agentMdFetchInflight.clear();
734
+ this._agentMdDownloadInflight.clear();
735
+ this._peerCache.clear();
736
+ this._certCache.clear();
737
+ this._gatewayUrl = null;
738
+ this._deviceId = aid.deviceId || getDeviceId(nextConfig.aunPath);
739
+ this._slotId = aid.slotId || 'default';
740
+ const debugFlag = nextConfig.debug;
741
+ this._logger = new AUNLogger({ debug: debugFlag, aunPath: nextConfig.aunPath });
742
+ this._logger.bindDeviceId(this._deviceId);
743
+ this._clientLog = this._logger.for('aun_core.client');
744
+ const dnsNet = new DnsResilientNet({
745
+ verifySsl: nextConfig.verifySsl,
746
+ logger: this._clientLog,
747
+ });
748
+ this._discovery = new GatewayDiscovery({ verifySsl: nextConfig.verifySsl, logger: this._clientLog, net: dnsNet });
749
+ const tokenStore = new FileKeyStore(nextConfig.aunPath, {
750
+ logger: this._logger.for('aun_core.keystore'),
751
+ secretStoreLogger: this._logger.for('aun_core.secret-store'),
752
+ });
753
+ this._tokenStore = tokenStore;
754
+ this._auth = new AuthFlow({
755
+ tokenStore,
756
+ crypto: new CryptoProvider(),
757
+ aid: aid.aid,
758
+ deviceId: this._deviceId,
759
+ slotId: this._slotId,
760
+ rootCaPath: nextConfig.rootCaPath ?? undefined,
761
+ verifySsl: nextConfig.verifySsl,
762
+ logger: this._logger.for('aun_core.auth'),
763
+ net: dnsNet,
764
+ });
765
+ this._transport = new RPCTransport({
766
+ eventDispatcher: this._dispatcher,
767
+ timeout: 10_000,
768
+ onDisconnect: (err, closeCode) => this._handleTransportDisconnect(err, closeCode),
769
+ verifySsl: nextConfig.verifySsl,
770
+ logger: this._logger.for('aun_core.transport'),
771
+ dnsNet,
772
+ });
773
+ this._transport.setMetaObserver((meta) => this._observeRpcMeta(meta));
774
+ }
700
775
  loadIdentity(aid) {
701
776
  if (!aid?.isPrivateKeyValid()) {
702
777
  throw new StateError('loadIdentity requires an AID with a valid private key');
@@ -705,16 +780,17 @@ export class AUNClient {
705
780
  if (publicState !== ConnectionState.NO_IDENTITY && publicState !== ConnectionState.CLOSED) {
706
781
  throw new StateError(`loadIdentity not allowed in state ${publicState}`);
707
782
  }
783
+ this._applyAidRuntimeContext(aid);
708
784
  this._currentAid = aid;
709
785
  this._aid = aid.aid;
710
786
  this._identity = {
711
787
  aid: aid.aid,
712
- private_key_pem: aid._privateKeyPem ?? '',
788
+ private_key_pem: aid.privateKeyPem,
713
789
  public_key_der_b64: aid.publicKey,
714
790
  cert: aid.certPem,
715
791
  };
716
- this._auth._aid = aid.aid;
717
- this._slotId = aid.slotId || 'default';
792
+ // 注入内存私钥到 AuthFlow,禁止 AuthFlow 内部再走 keystore 解密
793
+ this._auth.setIdentity(this._identity);
718
794
  this._state = 'standby';
719
795
  this._closing = false;
720
796
  this._lastError = null;
@@ -788,10 +864,7 @@ export class AUNClient {
788
864
  return `${agentMdHttpScheme(gatewayUrl)}://${agentMdAuthority(target, this._configModel.discoveryPort)}/agent.md`;
789
865
  }
790
866
  async _ensureAgentMdUploadToken(aid, gatewayUrl) {
791
- let identity = this._auth.loadIdentityOrNone(aid);
792
- if (!identity && this._identity && String(this._identity.aid ?? '') === aid) {
793
- identity = this._identity;
794
- }
867
+ let identity = this._identity && String(this._identity.aid ?? '') === aid ? this._identity : null;
795
868
  if (!identity) {
796
869
  throw new StateError('no local identity found, register or load an AID first');
797
870
  }
@@ -975,7 +1048,7 @@ export class AUNClient {
975
1048
  let resolvedCert = String(certPem ?? '').trim();
976
1049
  if (!resolvedCert) {
977
1050
  try {
978
- resolvedCert = String(this._keystore.loadCert(target) ?? '').trim();
1051
+ resolvedCert = String(this._tokenStore.loadCert(target) ?? '').trim();
979
1052
  }
980
1053
  catch {
981
1054
  resolvedCert = '';
@@ -1589,10 +1662,11 @@ export class AUNClient {
1589
1662
  async connect(opts) {
1590
1663
  const tStart = Date.now();
1591
1664
  // 先校验非法参数(ValidationError),再检查身份(StateError)
1592
- if (opts !== undefined && typeof opts === 'object') {
1665
+ if (opts !== undefined && opts !== null && typeof opts === 'object') {
1593
1666
  const raw = opts;
1594
- if ('access_token' in raw || 'aid' in raw || 'token' in raw) {
1595
- throw new ValidationError('connect options must not include access_token/aid; these are managed internally');
1667
+ const invalid = Object.keys(raw).filter((key) => !PUBLIC_CONNECTION_OPTION_KEYS.has(key)).sort();
1668
+ if (invalid.length > 0) {
1669
+ throw new ValidationError(`connect options contain unsupported field(s): ${invalid.join(', ')}`);
1596
1670
  }
1597
1671
  }
1598
1672
  const target = this._currentAid?.aid ?? this._aid ?? '';
@@ -1694,8 +1768,8 @@ export class AUNClient {
1694
1768
  this._stopBackgroundTasks();
1695
1769
  this._stopReconnect();
1696
1770
  if (this.state === ConnectionState.NO_IDENTITY || this.state === ConnectionState.CLOSED) {
1697
- const closableKeyStore = this._keystore;
1698
- closableKeyStore.close?.();
1771
+ const closableStore = this._tokenStore;
1772
+ closableStore.close?.();
1699
1773
  this._state = 'closed';
1700
1774
  this._logger.close();
1701
1775
  this._resetSeqTrackingState();
@@ -1703,8 +1777,8 @@ export class AUNClient {
1703
1777
  return;
1704
1778
  }
1705
1779
  await this._transport.close();
1706
- const closableKeyStore = this._keystore;
1707
- closableKeyStore.close?.();
1780
+ const closableStore = this._tokenStore;
1781
+ closableStore.close?.();
1708
1782
  this._state = 'closed';
1709
1783
  this._logger.close();
1710
1784
  await this._dispatcher.publish('state_change', { state: this._publicState(this._state) });
@@ -1788,6 +1862,14 @@ export class AUNClient {
1788
1862
  }
1789
1863
  this._validateOutboundCall(method, p);
1790
1864
  this._injectMessageCursorContext(method, p);
1865
+ if (method.startsWith('group.')
1866
+ && !('_group_cursor_params' in p)
1867
+ && !Boolean(p._pull_gate_locked)) {
1868
+ const explicitCursorParams = this._groupCursorParams(p);
1869
+ if (Object.keys(explicitCursorParams).length > 0) {
1870
+ p._group_cursor_params = explicitCursorParams;
1871
+ }
1872
+ }
1791
1873
  // group.* 方法的 group_id 归一化为 canonical 格式(兼容老/污染数据)
1792
1874
  if (method.startsWith('group.') && p.group_id !== undefined && p.group_id !== null) {
1793
1875
  const rawGroupId = String(p.group_id);
@@ -1889,7 +1971,17 @@ export class AUNClient {
1889
1971
  throw new ValidationError('group.pull requires group_id');
1890
1972
  }
1891
1973
  await this._ensureV2SessionReady('group.pull');
1892
- const messages = await runWithRpcPriority(() => this._pullGroupV2(String(p.group_id), Number(p.after_seq ?? p.after_message_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, { gateLocked: true }));
1974
+ const hasExplicitAfterSeq = 'after_seq' in p || 'after_message_seq' in p;
1975
+ const cursorParams = this._explicitGroupCursorParams(p);
1976
+ const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
1977
+ const pullOpts = { gateLocked: true };
1978
+ if (hasExplicitAfterSeq)
1979
+ pullOpts.explicitAfterSeq = true;
1980
+ if (Object.keys(cursorParams).length > 0)
1981
+ pullOpts.cursorParams = cursorParams;
1982
+ if (!ownsCursor)
1983
+ pullOpts.ownsCursor = false;
1984
+ const messages = await runWithRpcPriority(() => this._pullGroupV2(String(p.group_id), Number(p.after_seq ?? p.after_message_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, pullOpts));
1893
1985
  return { messages };
1894
1986
  }
1895
1987
  if (method === 'group.ack_messages' || method === 'group.v2.ack') {
@@ -1897,12 +1989,18 @@ export class AUNClient {
1897
1989
  throw new ValidationError('group.ack_messages requires group_id');
1898
1990
  }
1899
1991
  await this._ensureV2SessionReady('group.ack_messages');
1992
+ const cursorParams = this._explicitGroupCursorParams(p);
1993
+ const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
1994
+ if (method === 'group.ack_messages' && !ownsCursor) {
1995
+ return await runWithRpcPriority(() => this._rawGroupAckMessages(p));
1996
+ }
1900
1997
  return await runWithRpcPriority(() => this._ackGroupV2(String(p.group_id), Number(p.seq ?? p.msg_seq ?? p.up_to_seq ?? 0) || undefined));
1901
1998
  }
1902
1999
  if (method === 'message.pull') {
1903
2000
  delete p._skip_auto_ack;
1904
2001
  delete p.skip_auto_ack;
1905
2002
  }
2003
+ delete p._group_cursor_params;
1906
2004
  // 关键操作自动附加客户端签名
1907
2005
  if (SIGNED_METHODS.has(method)) {
1908
2006
  if (this._shouldSkipClientSignature(method, p)) {
@@ -1973,6 +2071,7 @@ export class AUNClient {
1973
2071
  delete p._pull_gate_locked;
1974
2072
  delete p._skip_auto_ack;
1975
2073
  delete p.skip_auto_ack;
2074
+ delete p._group_cursor_params;
1976
2075
  if (method.startsWith('group.') && p.group_id !== undefined && p.group_id !== null) {
1977
2076
  p.group_id = normalizeGroupId(String(p.group_id)) || String(p.group_id);
1978
2077
  }
@@ -2021,13 +2120,12 @@ export class AUNClient {
2021
2120
  * 签名覆盖所有非 _ 前缀且非 client_signature 的业务字段。
2022
2121
  */
2023
2122
  _signClientOperation(method, params) {
2024
- const identity = this._identity;
2025
- if (!identity || !identity.private_key_pem)
2123
+ const currentAid = this._currentAid;
2124
+ if (!currentAid?.privateKeyPem)
2026
2125
  return;
2027
2126
  try {
2028
- const aid = String(identity.aid ?? '');
2127
+ const aid = currentAid.aid;
2029
2128
  const ts = String(Math.floor(Date.now() / 1000));
2030
- // 计算 params hash — 必须递归排序所有键(与 Python json.dumps(sort_keys=True, separators=(",",":")) 一致)
2031
2129
  const paramsForHash = {};
2032
2130
  for (const [k, v] of Object.entries(params)) {
2033
2131
  if (k !== 'client_signature' && !k.startsWith('_')) {
@@ -2037,11 +2135,11 @@ export class AUNClient {
2037
2135
  const paramsJson = stableStringify(paramsForHash);
2038
2136
  const paramsHash = crypto.createHash('sha256').update(paramsJson, 'utf-8').digest('hex');
2039
2137
  const signData = Buffer.from(`${method}|${aid}|${ts}|${paramsHash}`, 'utf-8');
2040
- const privateKey = crypto.createPrivateKey(String(identity.private_key_pem));
2138
+ const privateKey = crypto.createPrivateKey(currentAid.privateKeyPem);
2041
2139
  const signature = crypto.sign('SHA256', signData, privateKey);
2042
2140
  // 证书指纹
2043
2141
  let certFingerprint = '';
2044
- const certPem = String(identity.cert ?? '');
2142
+ const certPem = currentAid.certPem;
2045
2143
  if (certPem) {
2046
2144
  const certObj = new crypto.X509Certificate(certPem);
2047
2145
  certFingerprint = 'sha256:' + certObj.fingerprint256.replace(/:/g, '').toLowerCase();
@@ -2747,6 +2845,27 @@ export class AUNClient {
2747
2845
  }
2748
2846
  return Math.max(0, ...values.filter((value) => Number.isFinite(value)));
2749
2847
  }
2848
+ _groupCursorParams(params) {
2849
+ const cursorParams = {};
2850
+ for (const key of ['device_id', 'slot_id', 'device_name', 'device_type']) {
2851
+ const value = params[key];
2852
+ if (value !== undefined && value !== null)
2853
+ cursorParams[key] = value;
2854
+ }
2855
+ return cursorParams;
2856
+ }
2857
+ _explicitGroupCursorParams(params) {
2858
+ const value = params._group_cursor_params;
2859
+ if (!isJsonObject(value))
2860
+ return {};
2861
+ return { ...value };
2862
+ }
2863
+ _groupCursorTargetsCurrentInstance(params) {
2864
+ const deviceId = String(params.device_id ?? '').trim();
2865
+ const slotId = String(params.slot_id ?? '').trim();
2866
+ return (!deviceId || deviceId === (this._deviceId ?? ''))
2867
+ && (!slotId || slotId === (this._slotId ?? ''));
2868
+ }
2750
2869
  _schedulePullFollowup(method, params, result) {
2751
2870
  if (method === 'message.pull')
2752
2871
  method = 'message.v2.pull';
@@ -3237,8 +3356,8 @@ export class AUNClient {
3237
3356
  const membershipSnapshot = String(d.membership_snapshot ?? '').trim();
3238
3357
  const policySnapshot = String(d.policy_snapshot ?? '').trim();
3239
3358
  // 1. 验证 prev_state_hash 连续性
3240
- const loadFn = this._keystore.loadGroupState;
3241
- const localState = loadFn ? loadFn.call(this._keystore, groupId) : null;
3359
+ const loadFn = this._tokenStore.loadGroupState;
3360
+ const localState = loadFn ? loadFn.call(this._tokenStore, groupId) : null;
3242
3361
  if (localState && localState.state_hash && localState.state_hash !== prevStateHash) {
3243
3362
  this._clientLog.warn(`state_hash chain discontinuous group=${groupId} local_sv=${localState.state_version} event_sv=${stateVersion}`);
3244
3363
  // 回源同步
@@ -3264,9 +3383,9 @@ export class AUNClient {
3264
3383
  return;
3265
3384
  }
3266
3385
  }
3267
- const saveFn = this._keystore.saveGroupState;
3386
+ const saveFn = this._tokenStore.saveGroupState;
3268
3387
  if (saveFn) {
3269
- saveFn.call(this._keystore, groupId, sv, sHash, sEpoch, sMembersJson || membershipSnapshot, sPolicyJson || policySnapshot);
3388
+ saveFn.call(this._tokenStore, groupId, sv, sHash, sEpoch, sMembersJson || membershipSnapshot, sPolicyJson || policySnapshot);
3270
3389
  }
3271
3390
  }
3272
3391
  }
@@ -3287,9 +3406,9 @@ export class AUNClient {
3287
3406
  return;
3288
3407
  }
3289
3408
  // 3. 更新本地存储
3290
- const saveFn = this._keystore.saveGroupState;
3409
+ const saveFn = this._tokenStore.saveGroupState;
3291
3410
  if (saveFn) {
3292
- saveFn.call(this._keystore, groupId, stateVersion, stateHash, keyEpoch, membershipSnapshot, policySnapshot);
3411
+ saveFn.call(this._tokenStore, groupId, stateVersion, stateHash, keyEpoch, membershipSnapshot, policySnapshot);
3293
3412
  }
3294
3413
  this._clientLog.debug(`_onGroupStateCommitted exit: elapsed=${Date.now() - tStart}ms group=${groupId}`);
3295
3414
  }
@@ -3449,7 +3568,7 @@ export class AUNClient {
3449
3568
  }
3450
3569
  try {
3451
3570
  // peer 证书只存版本目录,不覆盖 cert.pem
3452
- this._keystore.saveCert(aid, certPem, certFingerprint, { makeActive: false });
3571
+ this._tokenStore.saveCert(aid, certPem, certFingerprint, { makeActive: false });
3453
3572
  }
3454
3573
  catch (exc) {
3455
3574
  this._clientLog.error(`failed to write cert to keystore (aid=${aid}, fp=${certFingerprint ?? ''}): ${formatCaughtError(exc)}`, exc instanceof Error ? exc : undefined);
@@ -3720,9 +3839,9 @@ export class AUNClient {
3720
3839
  return;
3721
3840
  try {
3722
3841
  // 优先从 seq_tracker 表按行读取
3723
- const loadAll = this._keystore.loadAllSeqs;
3842
+ const loadAll = this._tokenStore.loadAllSeqs;
3724
3843
  if (typeof loadAll === 'function') {
3725
- let state = loadAll.call(this._keystore, this._aid, this._deviceId, this._slotId);
3844
+ let state = loadAll.call(this._tokenStore, this._aid, this._deviceId, this._slotId);
3726
3845
  if (state && Object.keys(state).length > 0) {
3727
3846
  state = this._migrateSeqStateGroupIds(state);
3728
3847
  this._seqTracker.restoreState(state);
@@ -3730,9 +3849,9 @@ export class AUNClient {
3730
3849
  }
3731
3850
  }
3732
3851
  // fallback: 从旧 instance_state JSON blob 恢复
3733
- const loader = this._keystore.loadInstanceState;
3852
+ const loader = this._tokenStore.loadInstanceState;
3734
3853
  if (typeof loader === 'function') {
3735
- const instanceState = loader.call(this._keystore, this._aid, this._deviceId, this._slotId);
3854
+ const instanceState = loader.call(this._tokenStore, this._aid, this._deviceId, this._slotId);
3736
3855
  if (instanceState && typeof instanceState.seq_tracker_state === 'object') {
3737
3856
  let state = instanceState.seq_tracker_state;
3738
3857
  state = this._migrateSeqStateGroupIds(state);
@@ -3783,20 +3902,20 @@ export class AUNClient {
3783
3902
  }
3784
3903
  this._clientLog.info(`SeqTracker group_id migration: ${Object.keys(renameMap).length} namespaces rewritten`);
3785
3904
  // 落盘
3786
- const saver = this._keystore.saveSeq;
3787
- const deleter = this._keystore.deleteSeq;
3905
+ const saver = this._tokenStore.saveSeq;
3906
+ const deleter = this._tokenStore.deleteSeq;
3788
3907
  if (typeof saver === 'function' && this._aid) {
3789
3908
  for (const [oldNs, newNs] of Object.entries(renameMap)) {
3790
3909
  if (typeof deleter === 'function') {
3791
3910
  try {
3792
- deleter.call(this._keystore, this._aid, this._deviceId, this._slotId, oldNs);
3911
+ deleter.call(this._tokenStore, this._aid, this._deviceId, this._slotId, oldNs);
3793
3912
  }
3794
3913
  catch (e) {
3795
3914
  this._clientLog.debug(`delete old seq ns failed: ns=${oldNs} err=${formatCaughtError(e)}`);
3796
3915
  }
3797
3916
  }
3798
3917
  try {
3799
- saver.call(this._keystore, this._aid, this._deviceId, this._slotId, newNs, newState[newNs]);
3918
+ saver.call(this._tokenStore, this._aid, this._deviceId, this._slotId, newNs, newState[newNs]);
3800
3919
  }
3801
3920
  catch (e) {
3802
3921
  this._clientLog.debug(`write new seq ns failed: ns=${newNs} err=${formatCaughtError(e)}`);
@@ -3844,17 +3963,17 @@ export class AUNClient {
3844
3963
  return;
3845
3964
  try {
3846
3965
  // 优先按行写入 seq_tracker 表
3847
- const saveFn = this._keystore.saveSeq;
3966
+ const saveFn = this._tokenStore.saveSeq;
3848
3967
  if (typeof saveFn === 'function') {
3849
3968
  for (const [ns, seq] of Object.entries(state)) {
3850
- saveFn.call(this._keystore, this._aid, this._deviceId, this._slotId, ns, seq);
3969
+ saveFn.call(this._tokenStore, this._aid, this._deviceId, this._slotId, ns, seq);
3851
3970
  }
3852
3971
  return;
3853
3972
  }
3854
3973
  // fallback: 旧版 updateInstanceState JSON blob
3855
- const updater = this._keystore.updateInstanceState;
3974
+ const updater = this._tokenStore.updateInstanceState;
3856
3975
  if (typeof updater === 'function') {
3857
- updater.call(this._keystore, this._aid, this._deviceId, this._slotId, (metadata) => {
3976
+ updater.call(this._tokenStore, this._aid, this._deviceId, this._slotId, (metadata) => {
3858
3977
  metadata.seq_tracker_state = state;
3859
3978
  return metadata;
3860
3979
  });
@@ -3877,13 +3996,13 @@ export class AUNClient {
3877
3996
  return;
3878
3997
  const seq = this._seqTracker.getContiguousSeq(ns);
3879
3998
  try {
3880
- if (seq > 0 && typeof this._keystore.saveSeq === 'function') {
3881
- this._keystore.saveSeq(this._aid, this._deviceId, this._slotId, ns, seq);
3999
+ if (seq > 0 && typeof this._tokenStore.saveSeq === 'function') {
4000
+ this._tokenStore.saveSeq(this._aid, this._deviceId, this._slotId, ns, seq);
3882
4001
  return;
3883
4002
  }
3884
- const deleteSeq = this._keystore.deleteSeq;
4003
+ const deleteSeq = this._tokenStore.deleteSeq;
3885
4004
  if (seq <= 0 && typeof deleteSeq === 'function') {
3886
- deleteSeq.call(this._keystore, this._aid, this._deviceId, this._slotId, ns);
4005
+ deleteSeq.call(this._tokenStore, this._aid, this._deviceId, this._slotId, ns);
3887
4006
  return;
3888
4007
  }
3889
4008
  if (seq > 0) {
@@ -4026,16 +4145,26 @@ export class AUNClient {
4026
4145
  this._restoreSeqTrackerState();
4027
4146
  }
4028
4147
  this._startBackgroundTasks();
4029
- // V2 E2EE:初始化 session 并注册本设备 SPK。
4030
- try {
4031
- await this._initV2Session();
4148
+ const connectionKind = String(params.connection_kind ?? 'long');
4149
+ const isShortConnection = connectionKind === 'short';
4150
+ if (!isShortConnection) {
4151
+ // V2 E2EE:长连接上线时初始化 session 并注册本设备 SPK。
4152
+ try {
4153
+ await this._initV2Session();
4154
+ }
4155
+ catch (exc) {
4156
+ this._clientLog.warn(`V2 session init failed (non-fatal): ${formatCaughtError(exc)}`);
4157
+ }
4032
4158
  }
4033
- catch (exc) {
4034
- this._clientLog.warn(`V2 session init failed (non-fatal): ${formatCaughtError(exc)}`);
4159
+ else {
4160
+ this._clientLog.debug('V2 session init deferred for short connection');
4035
4161
  }
4036
4162
  // connect/reconnect 成功后自动触发一次 P2P message.v2.pull,补齐离线期间积压
4037
4163
  // 群消息按惰性触发,不在此处主动 pull
4038
- if (this._sessionOptions.background_sync !== false) {
4164
+ const hasExplicitBackgroundSync = Object.prototype.hasOwnProperty.call(params, 'background_sync');
4165
+ const backgroundSyncEnabled = this._sessionOptions.background_sync !== false
4166
+ && (!isShortConnection || hasExplicitBackgroundSync);
4167
+ if (backgroundSyncEnabled) {
4039
4168
  void this._fillP2pGap().catch((exc) => {
4040
4169
  this._clientLog.warn(`schedule post-connect P2P gap fill failed: ${formatCaughtError(exc)}`);
4041
4170
  });
@@ -4074,6 +4203,15 @@ export class AUNClient {
4074
4203
  }
4075
4204
  /** V2-only:所有加密入口都必须有 V2 session。 */
4076
4205
  async _ensureV2SessionReady(method, errorMessage) {
4206
+ if (!this._v2Session) {
4207
+ if (!this._v2SessionInitInFlight) {
4208
+ this._v2SessionInitInFlight = this._initV2Session()
4209
+ .finally(() => {
4210
+ this._v2SessionInitInFlight = null;
4211
+ });
4212
+ }
4213
+ await this._v2SessionInitInFlight;
4214
+ }
4077
4215
  if (!this._v2Session) {
4078
4216
  throw new StateError(errorMessage ?? `V2 session not initialized; encrypted ${method} requires E2EE V2`);
4079
4217
  }
@@ -4096,42 +4234,13 @@ export class AUNClient {
4096
4234
  this._v2BootstrapCache.clear();
4097
4235
  }
4098
4236
  let identity = this._identity;
4099
- if (!identity) {
4100
- try {
4101
- identity = this._keystore.loadIdentity(this._aid);
4102
- if (identity)
4103
- this._identity = identity;
4104
- }
4105
- catch {
4106
- identity = null;
4107
- }
4108
- }
4109
- if (!identity?.private_key_pem) {
4110
- // fallback:缓存的 identity 可能被 instanceState 污染,重新从 keystore 加载
4111
- try {
4112
- identity = this._keystore.loadIdentity(this._aid);
4113
- if (identity?.private_key_pem) {
4114
- this._identity = identity;
4115
- this._clientLog.warn('V2 session init: identity cache was stale, reloaded from keystore');
4116
- // 重新持久化 instance_state,清理脏数据
4117
- const persistIdentity = this._auth._persistIdentity;
4118
- if (typeof persistIdentity === 'function') {
4119
- try {
4120
- persistIdentity.call(this._auth, identity);
4121
- }
4122
- catch { /* best-effort */ }
4123
- }
4124
- }
4125
- }
4126
- catch {
4127
- identity = null;
4128
- }
4129
- }
4130
- if (!identity?.private_key_pem) {
4237
+ // 私钥由 AIDStore 管理,直接从 _currentAid 读取明文私钥
4238
+ const currentAid = this._currentAid;
4239
+ if (!currentAid?.privateKeyPem) {
4131
4240
  this._clientLog.warn('V2 session init skipped: no AID private key');
4132
4241
  return;
4133
4242
  }
4134
- const privateKey = crypto.createPrivateKey(String(identity.private_key_pem));
4243
+ const privateKey = crypto.createPrivateKey(currentAid.privateKeyPem);
4135
4244
  const jwk = privateKey.export({ format: 'jwk' });
4136
4245
  if (jwk.kty !== 'EC' || jwk.crv !== 'P-256' || !jwk.d) {
4137
4246
  throw new StateError('AID private key must be EC P-256');
@@ -4139,8 +4248,8 @@ export class AUNClient {
4139
4248
  const aidPriv = _v2LeftPad32(_v2B64uToBytes(jwk.d));
4140
4249
  const pubDer = crypto.createPublicKey(privateKey).export({ format: 'der', type: 'spki' });
4141
4250
  const aidPubDer = new Uint8Array(pubDer);
4142
- const storeProvider = this._keystore;
4143
- const v2Store = storeProvider.getV2KeyStore?.call(this._keystore, this._aid);
4251
+ const storeProvider = this._tokenStore;
4252
+ const v2Store = storeProvider.getV2KeyStore?.call(this._tokenStore, this._aid);
4144
4253
  if (!v2Store) {
4145
4254
  throw new StateError('V2 key store is unavailable for current keystore');
4146
4255
  }
@@ -4150,6 +4259,28 @@ export class AUNClient {
4150
4259
  this._clientLog.debug(`V2 session initialized aid=${this._aid} device=${this._deviceId}`);
4151
4260
  // 群 state proposal 由服务端在 client.online 时定向通知。
4152
4261
  }
4262
+ _currentV2KeyStore() {
4263
+ if (this._v2KeyStore)
4264
+ return this._v2KeyStore;
4265
+ if (!this._aid)
4266
+ throw new StateError('V2 key store requires a loaded AID');
4267
+ const storeProvider = this._tokenStore;
4268
+ const v2Store = storeProvider.getV2KeyStore?.call(this._tokenStore, this._aid);
4269
+ if (!v2Store) {
4270
+ throw new StateError('V2 key store is unavailable for current identity');
4271
+ }
4272
+ this._v2KeyStore = v2Store;
4273
+ return v2Store;
4274
+ }
4275
+ _saveGroupIdentityToV2(groupAid, identity) {
4276
+ const privateKeyPem = String(identity.private_key_pem ?? '').trim();
4277
+ const publicKeyDerB64 = String(identity.public_key_der_b64 ?? '').trim();
4278
+ if (!groupAid || !privateKeyPem || !publicKeyDerB64) {
4279
+ throw new StateError('group identity is incomplete');
4280
+ }
4281
+ const pubDer = new Uint8Array(Buffer.from(publicKeyDerB64, 'base64'));
4282
+ this._currentV2KeyStore().saveGroupIdentity(this._deviceId, groupAid, privateKeyPem, pubDer);
4283
+ }
4153
4284
  async _v2TrustedIKPubDer(aid) {
4154
4285
  const normalizedAid = String(aid ?? '').trim();
4155
4286
  if (!normalizedAid)
@@ -4677,6 +4808,7 @@ export class AUNClient {
4677
4808
  decrypted.push(plaintext);
4678
4809
  this._logMessageDebug('decrypt-ok', 'message.v2.pull', 'message.received', plaintext);
4679
4810
  }
4811
+ const hasServerAckSeq = Object.prototype.hasOwnProperty.call(result, 'server_ack_seq');
4680
4812
  const serverAckSeq = Number(result.server_ack_seq ?? 0);
4681
4813
  if (ns && Number.isFinite(serverAckSeq) && serverAckSeq > 0) {
4682
4814
  const contig = this._seqTracker.getContiguousSeq(ns);
@@ -4692,7 +4824,11 @@ export class AUNClient {
4692
4824
  await this._drainOrderedMessages(ns, undefined, true);
4693
4825
  this._saveSeqTrackerState();
4694
4826
  }
4695
- if (messages.length > 0 && contigAdvanced && ackSeq > 0 && !opts?.skipAutoAck) {
4827
+ const ackNeeded = messages.length > 0
4828
+ && ackSeq > 0
4829
+ && !opts?.skipAutoAck
4830
+ && (contigAdvanced || (hasServerAckSeq && ackSeq > serverAckSeq));
4831
+ if (ackNeeded) {
4696
4832
  this._clientLog.debug(`message.v2.pull scheduling auto-ack: ns=${ns}, ack_seq=${ackSeq}, raw_count=${messages.length}`);
4697
4833
  this._safeAsync(this._ackV2(ackSeq).then(() => undefined));
4698
4834
  }
@@ -4955,7 +5091,9 @@ export class AUNClient {
4955
5091
  }
4956
5092
  const decrypted = [];
4957
5093
  let totalRawCount = 0;
4958
- let nextAfterSeq = afterSeq || this._seqTracker.getContiguousSeq(ns);
5094
+ const cursorParams = opts?.cursorParams ?? {};
5095
+ const ownsCursor = opts?.ownsCursor !== false;
5096
+ let nextAfterSeq = opts?.explicitAfterSeq ? afterSeq : (afterSeq || this._seqTracker.getContiguousSeq(ns));
4959
5097
  let pageCount = 0;
4960
5098
  const maxPages = 100;
4961
5099
  while (pageCount < maxPages) {
@@ -4965,6 +5103,7 @@ export class AUNClient {
4965
5103
  group_id: gid,
4966
5104
  after_seq: nextAfterSeq,
4967
5105
  limit,
5106
+ ...cursorParams,
4968
5107
  });
4969
5108
  const messages = (Array.isArray(result.messages) ? result.messages : []);
4970
5109
  totalRawCount += messages.length;
@@ -5043,7 +5182,9 @@ export class AUNClient {
5043
5182
  decrypted.push(plaintext);
5044
5183
  this._logMessageDebug('decrypt-ok', 'group.v2.pull', 'group.message_created', plaintext);
5045
5184
  }
5046
- const retentionFloor = this._pullRetentionFloor(result, 'retention_floor_message_seq', 'retention_floor_message_seq');
5185
+ const cursorCurrentSeq = Number(cursor?.current_seq ?? 0);
5186
+ const hasServerCursor = cursor !== null && Object.prototype.hasOwnProperty.call(cursor, 'current_seq');
5187
+ const retentionFloor = Math.max(this._pullRetentionFloor(result, 'retention_floor_message_seq', 'retention_floor_message_seq'), Number.isFinite(cursorCurrentSeq) ? cursorCurrentSeq : 0);
5047
5188
  if (retentionFloor > 0) {
5048
5189
  const contig = this._seqTracker.getContiguousSeq(ns);
5049
5190
  if (contig < retentionFloor) {
@@ -5057,11 +5198,17 @@ export class AUNClient {
5057
5198
  await this._drainOrderedMessages(ns, undefined, true);
5058
5199
  this._saveSeqTrackerState();
5059
5200
  }
5060
- if (messages.length > 0 && contigAdvanced && ackSeq > 0) {
5201
+ const ackNeeded = messages.length > 0
5202
+ && ackSeq > 0
5203
+ && ownsCursor
5204
+ && (contigAdvanced || (hasServerCursor && ackSeq > cursorCurrentSeq));
5205
+ if (ackNeeded) {
5061
5206
  this._clientLog.debug(`group.v2.pull scheduling auto-ack: group=${gid}, ns=${ns}, ack_seq=${ackSeq}, raw_count=${messages.length}`);
5062
5207
  this._safeAsync(this._ackGroupV2(gid, ackSeq).then(() => undefined));
5063
5208
  }
5064
5209
  const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
5210
+ if (!ownsCursor)
5211
+ break;
5065
5212
  if (messages.length === 0 || nextAfter <= nextAfterSeq || result.has_more === false)
5066
5213
  break;
5067
5214
  nextAfterSeq = nextAfter;
@@ -5072,6 +5219,10 @@ export class AUNClient {
5072
5219
  this._clientLog.debug(`group.v2.pull done: group=${gid}, requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${decrypted.length}, ns=${ns}`);
5073
5220
  return decrypted;
5074
5221
  }
5222
+ async _rawGroupAckMessages(params) {
5223
+ const p = { ...params };
5224
+ return await this._callRawV2Rpc('group.ack_messages', p);
5225
+ }
5075
5226
  /** V2 Group ack。 */
5076
5227
  async _ackGroupV2(groupId, upToSeq) {
5077
5228
  const gid = normalizeGroupId(groupId) || String(groupId ?? '').trim();
@@ -6071,7 +6222,7 @@ export class AUNClient {
6071
6222
  return;
6072
6223
  }
6073
6224
  let signature = '';
6074
- const privateKeyPem = String(this._identity?.private_key_pem ?? '');
6225
+ const privateKeyPem = this._currentAid?.privateKeyPem ?? '';
6075
6226
  if (privateKeyPem) {
6076
6227
  try {
6077
6228
  const signPayload = stableStringify({
@@ -6403,9 +6554,9 @@ export class AUNClient {
6403
6554
  if (this._gatewayUrl)
6404
6555
  return this._gatewayUrl;
6405
6556
  try {
6406
- const loadMetadata = this._keystore.loadMetadata;
6557
+ const loadMetadata = this._tokenStore.loadMetadata;
6407
6558
  const cachedGateway = typeof loadMetadata === 'function'
6408
- ? String(loadMetadata.call(this._keystore, resolvedAid)?.gateway_url ?? '').trim()
6559
+ ? String(loadMetadata.call(this._tokenStore, resolvedAid)?.gateway_url ?? '').trim()
6409
6560
  : '';
6410
6561
  if (cachedGateway) {
6411
6562
  this._gatewayUrl = cachedGateway;
@@ -6427,9 +6578,9 @@ export class AUNClient {
6427
6578
  const gateway = await this._discovery.discover(url);
6428
6579
  this._gatewayUrl = gateway;
6429
6580
  try {
6430
- const saveMetadata = this._keystore.saveMetadata;
6581
+ const saveMetadata = this._tokenStore.saveMetadata;
6431
6582
  if (typeof saveMetadata === 'function') {
6432
- saveMetadata.call(this._keystore, resolvedAid, { gateway_url: gateway, gateway_cached_at: Date.now() });
6583
+ saveMetadata.call(this._tokenStore, resolvedAid, { gateway_url: gateway, gateway_cached_at: Date.now() });
6433
6584
  }
6434
6585
  }
6435
6586
  catch {
@@ -6475,9 +6626,8 @@ export class AUNClient {
6475
6626
  }
6476
6627
  /** 连接后同步身份信息 */
6477
6628
  _syncIdentityAfterConnect(accessToken) {
6478
- const identity = this._auth.loadIdentityOrNone(this._aid ?? undefined);
6629
+ const identity = this._identity;
6479
6630
  if (identity === null) {
6480
- this._identity = null;
6481
6631
  return;
6482
6632
  }
6483
6633
  identity.access_token = accessToken;
@@ -6488,9 +6638,7 @@ export class AUNClient {
6488
6638
  const persistIdentity = this._auth._persistIdentity;
6489
6639
  if (typeof persistIdentity === 'function') {
6490
6640
  persistIdentity.call(this._auth, identity);
6491
- return;
6492
6641
  }
6493
- this._keystore.saveIdentity(String(identity.aid), identity);
6494
6642
  }
6495
6643
  // ── 内部:参数处理 ────────────────────────────────────────
6496
6644
  /** 规范化连接参数 */
@@ -6674,7 +6822,7 @@ export class AUNClient {
6674
6822
  scheduleNext();
6675
6823
  return;
6676
6824
  }
6677
- let identity = this._identity ?? this._auth.loadIdentityOrNone() ?? null;
6825
+ let identity = this._identity;
6678
6826
  if (identity === null) {
6679
6827
  scheduleNext();
6680
6828
  return;
@@ -7023,8 +7171,7 @@ export class AUNClient {
7023
7171
  }
7024
7172
  // ── Named Group(命名群)高层 API ────────────────────────────
7025
7173
  /**
7026
- * 创建命名群:本地生成 P-256 keypair,调用 group.create 传入 public_key,
7027
- * 服务端签发群 AID 证书,返回后将证书和私钥存入 keystore。
7174
+ * 创建命名群:群/P2P 私钥由 V2 数据库存储,不写入 AID 身份私钥存储。
7028
7175
  */
7029
7176
  async createNamedGroup(groupName, opts = {}) {
7030
7177
  const tStart = Date.now();
@@ -7044,15 +7191,10 @@ export class AUNClient {
7044
7191
  const aidCert = result?.aid_cert;
7045
7192
  const groupAid = String(groupInfo?.group_aid ?? '');
7046
7193
  if (groupAid && aidCert) {
7047
- this._keystore.saveIdentity(groupAid, {
7048
- private_key_pem: identity.private_key_pem,
7049
- public_key: identity.public_key_der_b64,
7050
- curve: 'P-256',
7051
- type: 'group_identity',
7052
- });
7194
+ this._saveGroupIdentityToV2(groupAid, identity);
7053
7195
  const certPem = String(aidCert.cert ?? '');
7054
7196
  if (certPem) {
7055
- this._keystore.saveCert(groupAid, certPem);
7197
+ this._tokenStore.saveCert(groupAid, certPem);
7056
7198
  }
7057
7199
  }
7058
7200
  this._clientLog.debug(`createNamedGroup exit: elapsed=${Date.now() - tStart}ms groupAid=${groupAid}`);
@@ -7083,15 +7225,10 @@ export class AUNClient {
7083
7225
  const aidCert = result?.aid_cert;
7084
7226
  const groupAid = String(groupInfo?.group_aid ?? '');
7085
7227
  if (groupAid && aidCert) {
7086
- this._keystore.saveIdentity(groupAid, {
7087
- private_key_pem: identity.private_key_pem,
7088
- public_key: identity.public_key_der_b64,
7089
- curve: 'P-256',
7090
- type: 'group_identity',
7091
- });
7228
+ this._saveGroupIdentityToV2(groupAid, identity);
7092
7229
  const certPem = String(aidCert.cert ?? '');
7093
7230
  if (certPem) {
7094
- this._keystore.saveCert(groupAid, certPem);
7231
+ this._tokenStore.saveCert(groupAid, certPem);
7095
7232
  }
7096
7233
  }
7097
7234
  this._clientLog.debug(`bindGroupAid exit: elapsed=${Date.now() - tStart}ms groupAid=${groupAid}`);