@agentunion/fastaun-browser 0.4.13 → 0.5.1

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 (223) hide show
  1. package/CHANGELOG.md +536 -386
  2. package/_packed_docs/CHANGELOG-validators.md +144 -0
  3. package/_packed_docs/CHANGELOG.md +536 -386
  4. package/_packed_docs/INDEX.md +159 -84
  5. package/_packed_docs/KITE_DOCS_GUIDE.md +48 -31
  6. package/_packed_docs/agent.md/SCHEMA.md +2 -0
  7. package/_packed_docs/agent.md/examples/codeagent-claudecode.md +2 -0
  8. package/_packed_docs/agent.md/examples/human-developer.md +2 -0
  9. package/_packed_docs/agent.md/examples/openclaw-lobster.md +2 -0
  10. package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +2 -0
  11. 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 +2 -0
  12. package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +266 -686
  13. package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +331 -0
  14. package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +2 -0
  15. package/_packed_docs/design/AUNClient/346/213/206/345/210/206/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +2 -0
  16. 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 +2 -0
  17. 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 +2 -0
  18. package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +2 -0
  19. package/_packed_docs/protocol/00A-/350/256/276/350/256/241/345/216/237/345/210/231-/344/270/272Agent/350/200/214/347/224/237.md +2 -0
  20. 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 -0
  21. package/_packed_docs/protocol/02-/350/257/201/344/271/246/344/270/216/344/277/241/344/273/273/344/275/223/347/263/273.md +2 -0
  22. package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +2 -0
  23. package/_packed_docs/protocol/04-Peer-/345/255/220/345/215/217/350/256/256.md +2 -0
  24. package/_packed_docs/protocol/05-Relay-/345/255/220/345/215/217/350/256/256.md +2 -0
  25. 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 +2 -0
  26. package/_packed_docs/protocol/08-AUN-E2EE-Group.md +296 -900
  27. package/_packed_docs/protocol/08-AUN-E2EE.md +2 -0
  28. package/_packed_docs/protocol/09-/345/256/211/345/205/250/350/200/203/350/231/221.md +2 -0
  29. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +66 -173
  30. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +55 -0
  31. package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +2 -0
  32. package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +2 -0
  33. 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 +2 -0
  34. 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 +2 -0
  35. package/_packed_docs/protocol/16-/347/263/273/347/273/237/347/233/256/345/275/225/344/277/235/346/212/244/346/226/271/346/241/210.md +177 -0
  36. package/_packed_docs/protocol/README.md +4 -1
  37. package/_packed_docs/protocol/agent.md/SCHEMA.md +2 -0
  38. package/_packed_docs/protocol/agent.md/examples/codeagent-claudecode.md +2 -0
  39. package/_packed_docs/protocol/agent.md/examples/human-developer.md +2 -0
  40. package/_packed_docs/protocol/agent.md/examples/openclaw-lobster.md +2 -0
  41. package/_packed_docs/protocol/aun-docs-guide.md +2 -0
  42. package/_packed_docs/protocol/index.md +10 -3
  43. package/_packed_docs/protocol//350/215/211/346/241/210-agent.md/347/255/276/345/220/215/345/215/217/350/256/256.md +2 -0
  44. package/_packed_docs/protocol//350/215/211/346/241/210-/346/213/222/347/273/235/344/277/241/345/217/267/345/215/217/350/256/256.md +2 -0
  45. package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +2 -0
  46. 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 +2 -0
  47. package/_packed_docs/protocol//351/231/204/345/275/225C-/347/247/201/351/222/245/347/256/241/347/220/206/344/270/216/350/272/253/344/273/275/346/201/242/345/244/215.md +2 -0
  48. package/_packed_docs/protocol//351/231/204/345/275/225D-Root_CA_/346/262/273/347/220/206/346/234/272/345/210/266.md +2 -0
  49. package/_packed_docs/protocol//351/231/204/345/275/225E-Root_CA_/345/207/206/345/205/245/346/265/201/347/250/213.md +2 -0
  50. package/_packed_docs/protocol//351/231/204/345/275/225F-Issuer_CA_/347/224/263/350/257/267/346/265/201/347/250/213.md +2 -0
  51. package/_packed_docs/protocol//351/231/204/345/275/225G-AID_/345/255/244/345/204/277/351/242/204/351/230/262/344/270/216/346/225/221/346/217/264/346/234/272/345/210/266.md +2 -0
  52. package/_packed_docs/protocol//351/231/204/345/275/225H-Identity/346/234/215/345/212/241/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  53. package/_packed_docs/protocol//351/231/204/345/275/225I-/350/267/250/345/237/237/346/266/210/346/201/257/350/267/257/347/224/261/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  54. 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 +2 -0
  55. package/_packed_docs/protocol//351/231/204/345/275/225K-Agent_Web/345/217/221/347/216/260/345/215/217/350/256/256.md +2 -0
  56. package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  57. 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 +2 -0
  58. 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 +2 -0
  59. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +2 -0
  60. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +2 -0
  61. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +2 -0
  62. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +2 -0
  63. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +6 -252
  64. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +219 -177
  65. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +4 -1
  66. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +2 -0
  67. package/_packed_docs/sdk/09-collab-rpc-manual.md +581 -0
  68. package/_packed_docs/sdk/09-custody-api-manual.md +2 -0
  69. package/_packed_docs/sdk/09-group-rpc-manual.md +301 -634
  70. package/_packed_docs/sdk/09-message-rpc-manual.md +2 -0
  71. package/_packed_docs/sdk/09-meta-rpc-manual.md +2 -0
  72. package/_packed_docs/sdk/09-payload-reference.md +2 -0
  73. package/_packed_docs/sdk/09-proxy-rpc-manual.md +2 -0
  74. package/_packed_docs/sdk/09-storage-rpc-manual.md +984 -274
  75. package/_packed_docs/sdk/09-stream-rpc-manual.md +2 -0
  76. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +36 -23
  77. 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 +2 -0
  78. package/_packed_docs/sdk/INDEX.md +47 -30
  79. package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +2 -0
  80. package/_packed_docs/sdk/README.md +17 -15
  81. package/dist/aid-store.d.ts +25 -0
  82. package/dist/aid-store.d.ts.map +1 -1
  83. package/dist/aid-store.js +89 -8
  84. package/dist/aid-store.js.map +1 -1
  85. package/dist/bundle.js +19615 -16870
  86. package/dist/client/delivery.d.ts +15 -2
  87. package/dist/client/delivery.d.ts.map +1 -1
  88. package/dist/client/delivery.js +303 -54
  89. package/dist/client/delivery.js.map +1 -1
  90. package/dist/client/group-state.d.ts.map +1 -1
  91. package/dist/client/group-state.js +36 -14
  92. package/dist/client/group-state.js.map +1 -1
  93. package/dist/client/lifecycle.js +2 -2
  94. package/dist/client/lifecycle.js.map +1 -1
  95. package/dist/client/rpc-pipeline.d.ts +2 -0
  96. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  97. package/dist/client/rpc-pipeline.js +235 -82
  98. package/dist/client/rpc-pipeline.js.map +1 -1
  99. package/dist/client/v2-e2ee.d.ts +14 -1
  100. package/dist/client/v2-e2ee.d.ts.map +1 -1
  101. package/dist/client/v2-e2ee.js +300 -121
  102. package/dist/client/v2-e2ee.js.map +1 -1
  103. package/dist/client.d.ts +36 -4
  104. package/dist/client.d.ts.map +1 -1
  105. package/dist/client.js +460 -57
  106. package/dist/client.js.map +1 -1
  107. package/dist/collab/client.d.ts +47 -0
  108. package/dist/collab/client.d.ts.map +1 -0
  109. package/dist/collab/client.js +136 -0
  110. package/dist/collab/client.js.map +1 -0
  111. package/dist/collab/errors.d.ts +16 -0
  112. package/dist/collab/errors.d.ts.map +1 -0
  113. package/dist/collab/errors.js +60 -0
  114. package/dist/collab/errors.js.map +1 -0
  115. package/dist/collab/index.d.ts +4 -0
  116. package/dist/collab/index.d.ts.map +1 -0
  117. package/dist/collab/index.js +4 -0
  118. package/dist/collab/index.js.map +1 -0
  119. package/dist/collab/types.d.ts +111 -0
  120. package/dist/collab/types.d.ts.map +1 -0
  121. package/dist/collab/types.js +2 -0
  122. package/dist/collab/types.js.map +1 -0
  123. package/dist/crypto.d.ts +6 -0
  124. package/dist/crypto.d.ts.map +1 -1
  125. package/dist/crypto.js +6 -0
  126. package/dist/crypto.js.map +1 -1
  127. package/dist/errors.d.ts +0 -20
  128. package/dist/errors.d.ts.map +1 -1
  129. package/dist/errors.js +9 -37
  130. package/dist/errors.js.map +1 -1
  131. package/dist/facades.d.ts +94 -0
  132. package/dist/facades.d.ts.map +1 -0
  133. package/dist/facades.js +389 -0
  134. package/dist/facades.js.map +1 -0
  135. package/dist/group-fs.d.ts +120 -0
  136. package/dist/group-fs.d.ts.map +1 -0
  137. package/dist/group-fs.js +539 -0
  138. package/dist/group-fs.js.map +1 -0
  139. package/dist/group-id.d.ts +9 -12
  140. package/dist/group-id.d.ts.map +1 -1
  141. package/dist/group-id.js +41 -63
  142. package/dist/group-id.js.map +1 -1
  143. package/dist/group-resources.d.ts +98 -0
  144. package/dist/group-resources.d.ts.map +1 -0
  145. package/dist/group-resources.js +635 -0
  146. package/dist/group-resources.js.map +1 -0
  147. package/dist/index.d.ts +8 -3
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +10 -1
  150. package/dist/index.js.map +1 -1
  151. package/dist/keystore/index.d.ts +2 -54
  152. package/dist/keystore/index.d.ts.map +1 -1
  153. package/dist/keystore/indexeddb-identity-store.d.ts +3 -0
  154. package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
  155. package/dist/keystore/indexeddb-identity-store.js +65 -0
  156. package/dist/keystore/indexeddb-identity-store.js.map +1 -1
  157. package/dist/keystore/indexeddb-shared.d.ts +3 -17
  158. package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
  159. package/dist/keystore/indexeddb-shared.js +4 -47
  160. package/dist/keystore/indexeddb-shared.js.map +1 -1
  161. package/dist/keystore/indexeddb-token-store.d.ts +1 -64
  162. package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
  163. package/dist/keystore/indexeddb-token-store.js +45 -774
  164. package/dist/keystore/indexeddb-token-store.js.map +1 -1
  165. package/dist/logger.d.ts +2 -0
  166. package/dist/logger.d.ts.map +1 -1
  167. package/dist/logger.js +4 -0
  168. package/dist/logger.js.map +1 -1
  169. package/dist/register-flow.d.ts +1 -0
  170. package/dist/register-flow.d.ts.map +1 -1
  171. package/dist/register-flow.js +3 -0
  172. package/dist/register-flow.js.map +1 -1
  173. package/dist/service-proxy.d.ts +1 -0
  174. package/dist/service-proxy.d.ts.map +1 -1
  175. package/dist/service-proxy.js +15 -3
  176. package/dist/service-proxy.js.map +1 -1
  177. package/dist/storage/errors.d.ts +28 -0
  178. package/dist/storage/errors.d.ts.map +1 -0
  179. package/dist/storage/errors.js +65 -0
  180. package/dist/storage/errors.js.map +1 -0
  181. package/dist/storage/index.d.ts +2 -0
  182. package/dist/storage/index.d.ts.map +1 -0
  183. package/dist/storage/index.js +2 -0
  184. package/dist/storage/index.js.map +1 -0
  185. package/dist/storage/lowlevel.d.ts +322 -0
  186. package/dist/storage/lowlevel.d.ts.map +1 -0
  187. package/dist/storage/lowlevel.js +471 -0
  188. package/dist/storage/lowlevel.js.map +1 -0
  189. package/dist/storage/types.d.ts +59 -0
  190. package/dist/storage/types.d.ts.map +1 -0
  191. package/dist/storage/types.js +113 -0
  192. package/dist/storage/types.js.map +1 -0
  193. package/dist/storage/vfs.d.ts +166 -0
  194. package/dist/storage/vfs.d.ts.map +1 -0
  195. package/dist/storage/vfs.js +457 -0
  196. package/dist/storage/vfs.js.map +1 -0
  197. package/dist/tools/cross-sdk-agent.d.ts +3 -0
  198. package/dist/tools/cross-sdk-agent.d.ts.map +1 -0
  199. package/dist/tools/cross-sdk-agent.js +1376 -0
  200. package/dist/tools/cross-sdk-agent.js.map +1 -0
  201. package/dist/transport.d.ts +5 -0
  202. package/dist/transport.d.ts.map +1 -1
  203. package/dist/transport.js +125 -12
  204. package/dist/transport.js.map +1 -1
  205. package/dist/types.d.ts +39 -56
  206. package/dist/types.d.ts.map +1 -1
  207. package/dist/v2/session/session.d.ts +2 -0
  208. package/dist/v2/session/session.d.ts.map +1 -1
  209. package/dist/v2/session/session.js +58 -22
  210. package/dist/v2/session/session.js.map +1 -1
  211. package/dist/v2/state/commitment.d.ts +1 -1
  212. package/dist/v2/state/commitment.d.ts.map +1 -1
  213. package/dist/v2/state/commitment.js +5 -3
  214. package/dist/v2/state/commitment.js.map +1 -1
  215. package/dist/validators.d.ts +35 -0
  216. package/dist/validators.d.ts.map +1 -0
  217. package/dist/validators.js +127 -0
  218. package/dist/validators.js.map +1 -0
  219. package/dist/version.d.ts +1 -1
  220. package/dist/version.d.ts.map +1 -1
  221. package/dist/version.js +1 -1
  222. package/dist/version.js.map +1 -1
  223. package/package.json +44 -44
package/dist/client.js CHANGED
@@ -7,7 +7,9 @@
7
7
  import { createConfig, getDeviceId, normalizeSlotId } from './config.js';
8
8
  import { EventDispatcher } from './events.js';
9
9
  import { normalizeGroupId } from './group-id.js';
10
+ import { validateAIDFormat, validateGroupIDFormat } from './validators.js';
10
11
  import { GatewayDiscovery } from './discovery.js';
12
+ import { AIDStore } from './aid-store.js';
11
13
  import { RPCTransport } from './transport.js';
12
14
  import { AuthFlow } from './auth.js';
13
15
  import { SeqTracker } from './seq-tracker.js';
@@ -17,6 +19,9 @@ import { IdentityRuntimeManager } from './client/identity.js';
17
19
  import { LifecycleController } from './client/lifecycle.js';
18
20
  import { PeerDirectory } from './client/peers.js';
19
21
  import { RpcPipeline } from './client/rpc-pipeline.js';
22
+ import { GroupFacade, MessageFacade, StreamFacade } from './facades.js';
23
+ import { StorageVFS } from './storage/vfs.js';
24
+ import { CollabClient } from './collab/client.js';
20
25
  import { V2E2EECoordinator } from './client/v2-e2ee.js';
21
26
  import { GroupStateCoordinator } from './client/group-state.js';
22
27
  import { CryptoProvider, base64ToUint8, pemToArrayBuffer, ecdsaVerifyDer, importCertPublicKeyEcdsa, } from './crypto.js';
@@ -26,7 +31,7 @@ import { ecdsaVerifyRaw } from './v2/crypto/ecdsa.js';
26
31
  import { AUNLogger } from './logger.js';
27
32
  import { AgentMdManager } from './agent-md.js';
28
33
  import { certMatchesFingerprint, normalizeFingerprintHex } from './cert-utils.js';
29
- import { AUNError, AuthError, ConnectionError, E2EEError, NotFoundError, PermissionError, StateError, ValidationError, } from './errors.js';
34
+ import { AUNError, AuthError, ConnectionError, E2EEError, NotFoundError, PermissionError, StateError, TimeoutError, ValidationError, } from './errors.js';
30
35
  import { isJsonObject, ConnectionState, STATE_TO_PUBLIC, } from './types.js';
31
36
  import { AID } from './aid.js';
32
37
  function getV2DeviceId(dev) {
@@ -101,8 +106,8 @@ const NON_IDEMPOTENT_TIMEOUT = 35;
101
106
  const NON_IDEMPOTENT_METHODS = new Set([
102
107
  'message.send', 'group.send', 'group.create', 'group.invite',
103
108
  'group.kick', 'group.remove_member', 'group.leave', 'group.dissolve',
104
- 'group.update_name', 'group.update_avatar', 'group.update_announcement',
105
- 'group.update_settings',
109
+ 'group.set_settings',
110
+ 'group.update_announcement', 'group.update_rules',
106
111
  'storage.put_object', 'storage.delete_object',
107
112
  'storage.create_share_link', 'storage.revoke_share_link',
108
113
  'storage.get_by_share',
@@ -110,18 +115,26 @@ const NON_IDEMPOTENT_METHODS = new Set([
110
115
  'storage.create_folder', 'storage.rename_folder', 'storage.move_folder',
111
116
  'storage.delete_folder', 'storage.move_object', 'storage.copy_object',
112
117
  'storage.batch_delete', 'storage.set_object_meta', 'storage.append_object',
118
+ 'storage.set_acl', 'storage.remove_acl', 'storage.set_visibility',
119
+ 'storage.issue_token', 'storage.revoke_token',
120
+ 'storage.create_symlink', 'storage.atomic_repoint',
121
+ 'storage.rename_symlink', 'storage.delete_symlink',
122
+ 'storage.fs.mkdir', 'storage.fs.remove', 'storage.fs.rename', 'storage.fs.copy',
123
+ 'storage.fs.mount', 'storage.fs.approve', 'storage.fs.reject', 'storage.fs.unmount',
124
+ 'storage.fs.invalidate_membership',
125
+ 'storage.volume.create', 'storage.volume.renew', 'storage.volume.expire_due',
113
126
  'auth.create_aid', 'auth.renew_cert', 'auth.rekey',
114
127
  'message.thought.put', 'group.thought.put',
115
- 'group.add_member',
116
- 'group.resources.put', 'group.resources.create_folder',
117
- 'group.resources.rename', 'group.resources.move',
118
- 'group.resources.mount_object', 'group.resources.update',
119
- 'group.resources.delete', 'group.resources.cleanup_by_storage_ref',
120
- 'group.resources.request_add', 'group.resources.request_mount_object',
121
- 'group.resources.direct_add', 'group.resources.approve_request',
122
- 'group.resources.reject_request', 'group.resources.unmount',
123
- 'group.resources.get_access',
124
- 'group.resources.resolve_access_ticket',
128
+ 'group.add_member', 'group.bind_group_aid', 'group.complete_transfer',
129
+ 'group.fs.mkdir', 'group.fs.rm', 'group.fs.cp', 'group.fs.mv',
130
+ 'group.fs.set_acl', 'group.fs.remove_acl',
131
+ 'group.fs.mount', 'group.fs.umount',
132
+ 'group.fs.check_upload', 'group.fs.create_upload_session',
133
+ 'group.fs.complete_upload', 'group.fs.create_download_ticket',
134
+ 'collab.create', 'collab.commit', 'collab.clone',
135
+ 'collab.prune', 'collab.unregister',
136
+ 'collab.tag.create', 'collab.tag.restore',
137
+ 'collab.tag.rm', 'collab.tag.prune',
125
138
  ]);
126
139
  /** 需要客户端签名的关键方法。运行时逻辑已迁入 RpcPipeline,此处保留给源码审计测试。 */
127
140
  const SIGNED_METHODS = new Set([
@@ -135,23 +148,23 @@ const SIGNED_METHODS = new Set([
135
148
  'group.v2.propose_state', 'group.v2.confirm_state',
136
149
  'group.v2.get_proposal',
137
150
  'group.kick', 'group.add_member',
138
- 'group.leave', 'group.remove_member', 'group.update_rules',
139
- 'group.update', 'group.update_announcement',
140
- 'group.update_join_requirements', 'group.set_role',
141
- 'group.transfer_owner', 'group.review_join_request',
151
+ 'group.leave', 'group.remove_member',
152
+ 'group.update',
153
+ 'group.set_role',
154
+ 'group.transfer_owner', 'group.bind_group_aid', 'group.complete_transfer',
155
+ 'group.review_join_request',
142
156
  'group.batch_review_join_request',
143
157
  'group.request_join', 'group.use_invite_code',
144
158
  'group.thought.put',
145
159
  'message.thought.put',
146
160
  'group.set_settings',
147
- 'group.resources.put', 'group.resources.create_folder',
148
- 'group.resources.rename', 'group.resources.move',
149
- 'group.resources.mount_object', 'group.resources.update',
150
- 'group.resources.delete', 'group.resources.cleanup_by_storage_ref',
151
- 'group.resources.request_add', 'group.resources.request_mount_object',
152
- 'group.resources.direct_add', 'group.resources.approve_request',
153
- 'group.resources.reject_request', 'group.resources.unmount',
154
- 'group.resources.get_access', 'group.resources.resolve_access_ticket',
161
+ 'group.update_announcement',
162
+ 'group.update_rules',
163
+ 'group.fs.mkdir', 'group.fs.rm', 'group.fs.cp', 'group.fs.mv',
164
+ 'group.fs.set_acl', 'group.fs.remove_acl',
165
+ 'group.fs.mount', 'group.fs.umount',
166
+ 'group.fs.check_upload', 'group.fs.create_upload_session',
167
+ 'group.fs.complete_upload', 'group.fs.create_download_ticket',
155
168
  'storage.put_object', 'storage.delete_object',
156
169
  'storage.create_share_link', 'storage.revoke_share_link',
157
170
  'storage.get_by_share',
@@ -159,6 +172,19 @@ const SIGNED_METHODS = new Set([
159
172
  'storage.create_folder', 'storage.rename_folder', 'storage.move_folder',
160
173
  'storage.delete_folder', 'storage.move_object', 'storage.copy_object',
161
174
  'storage.batch_delete', 'storage.set_object_meta', 'storage.append_object',
175
+ 'storage.set_acl', 'storage.remove_acl', 'storage.set_visibility',
176
+ 'storage.check_access',
177
+ 'storage.issue_token', 'storage.revoke_token',
178
+ 'storage.create_symlink', 'storage.atomic_repoint',
179
+ 'storage.rename_symlink', 'storage.delete_symlink',
180
+ 'storage.fs.mkdir', 'storage.fs.remove', 'storage.fs.rename', 'storage.fs.copy',
181
+ 'storage.fs.mount', 'storage.fs.approve', 'storage.fs.reject', 'storage.fs.unmount',
182
+ 'storage.fs.invalidate_membership',
183
+ 'storage.volume.create', 'storage.volume.renew', 'storage.volume.expire_due',
184
+ 'collab.create', 'collab.commit', 'collab.clone',
185
+ 'collab.prune', 'collab.unregister',
186
+ 'collab.tag.create', 'collab.tag.restore',
187
+ 'collab.tag.rm', 'collab.tag.prune',
162
188
  'group.commit_state',
163
189
  'group.ban', 'group.unban',
164
190
  'group.dissolve', 'group.suspend', 'group.resume',
@@ -560,6 +586,7 @@ export class AUNClient {
560
586
  _onlineUnreadHintIntervalMs = 50;
561
587
  /** gap fill 来源标记:true 表示当前正在补洞(pull 触发),false 表示非补洞 */
562
588
  _gapFillActive = false;
589
+ _backgroundRpcDepth = 0;
563
590
  // Pull Gate:序列化同一 key 的并发 pull 操作,防止重复拉取
564
591
  _pullGates = new Map();
565
592
  // 重连相关
@@ -595,6 +622,11 @@ export class AUNClient {
595
622
  _delivery;
596
623
  _v2E2EE;
597
624
  _groupState;
625
+ _storage;
626
+ _collab;
627
+ _messageFacade;
628
+ _groupFacade;
629
+ _streamFacade;
598
630
  constructor(aid) {
599
631
  if (aid !== null && aid !== undefined && !isAIDObject(aid)) {
600
632
  throw new ValidationError('AUNClient only accepts an AID object or no argument');
@@ -709,7 +741,7 @@ export class AUNClient {
709
741
  this._dispatcher.subscribe('_raw.group.message_recalled', (data) => {
710
742
  this._safeAsync(this._onRawGroupMessageRecalled(data));
711
743
  });
712
- // 群组变更事件:验签 + 透传 + gap 检测
744
+ // 群组变更事件:验签 + 透传 + gap 检测 + V2 state/SPK 维护
713
745
  this._dispatcher.subscribe('_raw.group.changed', (data) => {
714
746
  this._onRawGroupChanged(data);
715
747
  });
@@ -720,9 +752,6 @@ export class AUNClient {
720
752
  this._dispatcher.subscribe('_raw.group.v2.message_created', (data) => {
721
753
  this._safeAsync(this._onRawGroupV2MessageCreated(data));
722
754
  });
723
- this._dispatcher.subscribe('_raw.group.v2.epoch_rotated', (data) => {
724
- this._safeAsync(this._onV2EpochRotated(data));
725
- });
726
755
  this._dispatcher.subscribe('_raw.group.v2.state_proposed', (data) => {
727
756
  this._safeAsync(this._onV2StateProposed(data));
728
757
  });
@@ -751,6 +780,36 @@ export class AUNClient {
751
780
  get aid() {
752
781
  return this._aid;
753
782
  }
783
+ /** AUN Storage VFS 入口 */
784
+ get storage() {
785
+ if (!this._storage)
786
+ this._storage = new StorageVFS(this);
787
+ return this._storage;
788
+ }
789
+ /** AUN collab 协作层入口 */
790
+ get collab() {
791
+ if (!this._collab)
792
+ this._collab = new CollabClient(this);
793
+ return this._collab;
794
+ }
795
+ /** Message RPC facade 入口 */
796
+ get message() {
797
+ if (!this._messageFacade)
798
+ this._messageFacade = new MessageFacade(this);
799
+ return this._messageFacade;
800
+ }
801
+ /** Group RPC facade 入口 */
802
+ get group() {
803
+ if (!this._groupFacade)
804
+ this._groupFacade = new GroupFacade(this);
805
+ return this._groupFacade;
806
+ }
807
+ /** Stream RPC facade 入口 */
808
+ get stream() {
809
+ if (!this._streamFacade)
810
+ this._streamFacade = new StreamFacade(this);
811
+ return this._streamFacade;
812
+ }
754
813
  async _observeAgentMdFromEnvelope(envelope) {
755
814
  await this._agentMdManager.observeEnvelope(envelope);
756
815
  }
@@ -990,7 +1049,326 @@ export class AUNClient {
990
1049
  * 自动解密 message.pull/group.pull、Group E2EE 生命周期编排。
991
1050
  */
992
1051
  async call(method, params) {
993
- return await this._rpcPipeline.call(method, params);
1052
+ try {
1053
+ return await this._rpcPipeline.call(method, params);
1054
+ }
1055
+ catch (err) {
1056
+ await this._maybeHandleHalfOpenRpcFailure(err);
1057
+ throw err;
1058
+ }
1059
+ }
1060
+ async _maybeHandleHalfOpenRpcFailure(err) {
1061
+ if (this._closing || this.state !== ConnectionState.READY)
1062
+ return;
1063
+ if (!this._sessionOptions?.auto_reconnect || this._reconnectActive)
1064
+ return;
1065
+ if (!(err instanceof ConnectionError) && !(err instanceof TimeoutError))
1066
+ return;
1067
+ await this._handleTransportDisconnect(err);
1068
+ }
1069
+ async createGroup(params = {}) {
1070
+ if (!isJsonObject(params)) {
1071
+ throw new ValidationError('createGroup params must be an object');
1072
+ }
1073
+ const payload = { ...params };
1074
+ const groupName = String(payload.group_name ?? payload.groupName ?? '').trim();
1075
+ if (!groupName) {
1076
+ return await this.call('group.create', payload);
1077
+ }
1078
+ const keyPair = await new CryptoProvider().generateIdentity();
1079
+ payload.public_key = keyPair.public_key_der_b64;
1080
+ payload.curve = keyPair.curve;
1081
+ const result = await this.call('group.create', payload);
1082
+ const resultMap = isJsonObject(result) ? result : {};
1083
+ const group = isJsonObject(resultMap.group) ? resultMap.group : {};
1084
+ const aidCert = isJsonObject(resultMap.aid_cert) ? resultMap.aid_cert : {};
1085
+ const groupAid = String(group.group_aid ?? resultMap.group_aid ?? '').trim();
1086
+ const certPem = String(aidCert.cert ?? aidCert.cert_pem ?? resultMap.cert ?? resultMap.cert_pem ?? '').trim();
1087
+ if (!groupAid || !certPem) {
1088
+ throw new ValidationError('group.create named group response missing group.group_aid or aid_cert.cert');
1089
+ }
1090
+ const store = new AIDStore({
1091
+ aunPath: this.configModel.aunPath,
1092
+ encryptionSeed: this.configModel.seedPassword ?? '',
1093
+ slotId: this._slotId,
1094
+ rootCaPem: this.configModel.rootCaPem,
1095
+ verifySsl: this.configModel.verifySsl,
1096
+ });
1097
+ try {
1098
+ const imported = await store.importGroupIdentity(groupAid, {
1099
+ private_key_pem: keyPair.private_key_pem,
1100
+ public_key_der_b64: keyPair.public_key_der_b64,
1101
+ curve: keyPair.curve,
1102
+ cert_pem: certPem,
1103
+ });
1104
+ if (!imported.ok) {
1105
+ throw new ValidationError(imported.error.message);
1106
+ }
1107
+ }
1108
+ finally {
1109
+ store.close();
1110
+ }
1111
+ return result;
1112
+ }
1113
+ async bindGroupAid(params = {}, options = {}) {
1114
+ if (!isJsonObject(params)) {
1115
+ throw new ValidationError('bindGroupAid params must be an object');
1116
+ }
1117
+ const store = options.aidStore;
1118
+ if (!store) {
1119
+ throw new ValidationError('bindGroupAid requires aidStore');
1120
+ }
1121
+ const groupId = String(params.group_id ?? params.group_aid ?? '').trim();
1122
+ const keystore = store._keystore;
1123
+ let keyPair = null;
1124
+ // 优先复用 pending 槽位中已暂存的待绑定密钥(崩溃/重试幂等)
1125
+ if (groupId && keystore && typeof keystore.loadPendingGroupBind === 'function') {
1126
+ keyPair = await keystore.loadPendingGroupBind(groupId);
1127
+ }
1128
+ if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
1129
+ // 未命中 pending,生成新密钥
1130
+ const generated = await new CryptoProvider().generateIdentity();
1131
+ keyPair = {
1132
+ private_key_pem: generated.private_key_pem,
1133
+ public_key_der_b64: generated.public_key_der_b64,
1134
+ curve: generated.curve,
1135
+ };
1136
+ // 发 RPC 前先落盘暂存,确保崩溃后重试能复用同一密钥
1137
+ if (groupId && keystore && typeof keystore.savePendingGroupBind === 'function') {
1138
+ await keystore.savePendingGroupBind(groupId, keyPair);
1139
+ }
1140
+ }
1141
+ if (!keyPair) {
1142
+ throw new ValidationError('bindGroupAid: failed to generate or load key pair');
1143
+ }
1144
+ const payload = { ...params };
1145
+ if (!String(payload.group_id ?? '').trim() && groupId) {
1146
+ payload.group_id = groupId;
1147
+ }
1148
+ payload.public_key = keyPair.public_key_der_b64;
1149
+ payload.curve = keyPair.curve;
1150
+ const result = await this.call('group.bind_group_aid', payload);
1151
+ const resultMap = isJsonObject(result) ? result : {};
1152
+ const group = isJsonObject(resultMap.group) ? resultMap.group : {};
1153
+ const aidCert = isJsonObject(resultMap.aid_cert) ? resultMap.aid_cert : {};
1154
+ const groupAid = String(group.group_aid ?? resultMap.group_aid ?? '').trim();
1155
+ const certPem = String(aidCert.cert ?? aidCert.cert_pem ?? resultMap.cert ?? resultMap.cert_pem ?? '').trim();
1156
+ if (!groupAid || !certPem) {
1157
+ throw new ValidationError('group.bind_group_aid response missing group.group_aid or aid_cert.cert');
1158
+ }
1159
+ const imported = await store.importGroupIdentity(groupAid, {
1160
+ private_key_pem: keyPair.private_key_pem,
1161
+ public_key_der_b64: keyPair.public_key_der_b64,
1162
+ curve: keyPair.curve,
1163
+ cert_pem: certPem,
1164
+ });
1165
+ if (!imported.ok) {
1166
+ throw new ValidationError(imported.error.message);
1167
+ }
1168
+ // 落盘成功,清除 pending 槽位
1169
+ if (groupId && keystore && typeof keystore.clearPendingGroupBind === 'function') {
1170
+ await keystore.clearPendingGroupBind(groupId);
1171
+ }
1172
+ return result;
1173
+ }
1174
+ async renewGroupAid(params = {}, options = {}) {
1175
+ if (!isJsonObject(params)) {
1176
+ throw new ValidationError('renewGroupAid params must be an object');
1177
+ }
1178
+ const store = options.aidStore;
1179
+ if (!store) {
1180
+ throw new ValidationError('renewGroupAid requires aidStore');
1181
+ }
1182
+ const groupId = String(params.group_id ?? params.group_aid ?? '').trim();
1183
+ if (!groupId) {
1184
+ throw new ValidationError('renewGroupAid requires group_id or group_aid');
1185
+ }
1186
+ let groupAid = String(params.group_aid ?? '').trim();
1187
+ if (!groupAid) {
1188
+ const info = await this.call('group.get_info', { group_id: groupId, required: ['member'] });
1189
+ groupAid = String(info?.group_aid ?? '').trim();
1190
+ }
1191
+ if (!groupAid) {
1192
+ throw new ValidationError('renewGroupAid: unable to determine group_aid');
1193
+ }
1194
+ const loaded = await store.load(groupAid);
1195
+ if (!loaded.ok || !loaded.data) {
1196
+ throw new ValidationError(`renewGroupAid: group_aid identity not found: ${groupAid}`);
1197
+ }
1198
+ const oldAidObj = loaded.data.aid;
1199
+ if (!oldAidObj.isPrivateKeyValid() || !oldAidObj.privateKeyPem) {
1200
+ throw new ValidationError(`renewGroupAid: group_aid has no private key: ${groupAid}`);
1201
+ }
1202
+ const oldPublicKey = oldAidObj.publicKey;
1203
+ if (!oldPublicKey) {
1204
+ throw new ValidationError(`renewGroupAid: cannot determine old public key for ${groupAid}`);
1205
+ }
1206
+ const newKeyPair = await new CryptoProvider().generateIdentity();
1207
+ const newPublicKey = newKeyPair.public_key_der_b64;
1208
+ const newPrivateKey = newKeyPair.private_key_pem;
1209
+ const curve = newKeyPair.curve || 'P-256';
1210
+ if (!newPublicKey || !newPrivateKey) {
1211
+ throw new ValidationError('renewGroupAid: generated incomplete group identity');
1212
+ }
1213
+ const nonce = globalThis.crypto.randomUUID().replace(/-/g, '');
1214
+ const issuedMs = Date.now();
1215
+ const oldHash = bytesToHex(new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(oldPublicKey))));
1216
+ const newHash = bytesToHex(new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(newPublicKey))));
1217
+ const canonical = [
1218
+ 'aun-group-aid-renew-v1',
1219
+ groupId.toLowerCase(),
1220
+ groupAid.toLowerCase(),
1221
+ oldHash,
1222
+ newHash,
1223
+ nonce,
1224
+ String(issuedMs),
1225
+ ].join('|');
1226
+ const signed = await oldAidObj.sign(canonical);
1227
+ if (!signed.ok) {
1228
+ throw new ValidationError(`renewGroupAid: sign failed: ${signed.error.message}`);
1229
+ }
1230
+ const payload = { ...params };
1231
+ if (!String(payload.group_id ?? '').trim()) {
1232
+ payload.group_id = groupId;
1233
+ }
1234
+ payload.group_aid = groupAid;
1235
+ payload.old_public_key = oldPublicKey;
1236
+ payload.new_public_key = newPublicKey;
1237
+ payload.curve = curve;
1238
+ payload.renew_proof = { nonce, issued_ms: issuedMs, signature: signed.data.signature };
1239
+ const result = await this.call('group.renew_group_aid', payload);
1240
+ const resultMap = isJsonObject(result) ? result : {};
1241
+ const group = isJsonObject(resultMap.group) ? resultMap.group : {};
1242
+ const aidCert = isJsonObject(resultMap.aid_cert) ? resultMap.aid_cert : {};
1243
+ const returnedGroupAid = String(group.group_aid ?? resultMap.group_aid ?? groupAid).trim();
1244
+ const certPem = String(aidCert.cert ?? aidCert.cert_pem ?? resultMap.cert ?? resultMap.cert_pem ?? '').trim();
1245
+ if (!returnedGroupAid || !certPem) {
1246
+ throw new ValidationError('renewGroupAid response missing group.group_aid or aid_cert.cert');
1247
+ }
1248
+ const imported = await store.importGroupIdentity(returnedGroupAid, {
1249
+ private_key_pem: newPrivateKey,
1250
+ public_key_der_b64: newPublicKey,
1251
+ curve,
1252
+ cert_pem: certPem,
1253
+ });
1254
+ if (!imported.ok) {
1255
+ throw new ValidationError(`renewGroupAid failed to persist group identity: ${imported.error.message}`);
1256
+ }
1257
+ return result;
1258
+ }
1259
+ async startGroupTransfer(params = {}, options = {}) {
1260
+ if (!isJsonObject(params)) {
1261
+ throw new ValidationError('startGroupTransfer params must be an object');
1262
+ }
1263
+ const store = options.aidStore;
1264
+ if (!store) {
1265
+ throw new ValidationError('startGroupTransfer requires aidStore');
1266
+ }
1267
+ const groupId = String(params.group_id ?? '').trim();
1268
+ const newOwner = String(params.new_owner ?? '').trim();
1269
+ if (!groupId || !newOwner) {
1270
+ throw new ValidationError('startGroupTransfer requires group_id and new_owner');
1271
+ }
1272
+ let groupAid = String(params.group_aid ?? '').trim();
1273
+ if (!groupAid) {
1274
+ const info = await this.call('group.get_info', { group_id: groupId, required: ['member'] });
1275
+ groupAid = String(info?.group_aid ?? '').trim();
1276
+ }
1277
+ if (!groupAid) {
1278
+ throw new ValidationError('startGroupTransfer: unable to determine group_aid');
1279
+ }
1280
+ const loaded = await store.load(groupAid);
1281
+ if (!loaded.ok || !loaded.data) {
1282
+ throw new ValidationError(`startGroupTransfer: group_aid identity not found: ${groupAid}`);
1283
+ }
1284
+ const aidObj = loaded.data.aid;
1285
+ if (!aidObj.isPrivateKeyValid() || !aidObj.privateKeyPem) {
1286
+ throw new ValidationError(`startGroupTransfer: group_aid private key not found: ${groupAid}`);
1287
+ }
1288
+ const nonce = globalThis.crypto.randomUUID().replace(/-/g, '');
1289
+ const issuedMs = Date.now();
1290
+ const canonical = [
1291
+ 'aun-group-owner-transfer-v1',
1292
+ groupId.toLowerCase(),
1293
+ groupAid.toLowerCase(),
1294
+ newOwner.toLowerCase(),
1295
+ nonce,
1296
+ String(issuedMs),
1297
+ ].join('|');
1298
+ const signed = await aidObj.sign(canonical);
1299
+ if (!signed.ok) {
1300
+ throw new ValidationError(`startGroupTransfer: sign failed: ${signed.error.message}`);
1301
+ }
1302
+ const payload = { ...params, group_aid: groupAid };
1303
+ payload.transfer_auth = { nonce, issued_ms: issuedMs, signature: signed.data.signature };
1304
+ return await this.call('group.transfer_owner', payload);
1305
+ }
1306
+ async completeGroupTransfer(params = {}, options = {}) {
1307
+ if (!isJsonObject(params)) {
1308
+ throw new ValidationError('completeGroupTransfer params must be an object');
1309
+ }
1310
+ const store = options.aidStore;
1311
+ if (!store) {
1312
+ throw new ValidationError('completeGroupTransfer requires aidStore');
1313
+ }
1314
+ const keyPair = await new CryptoProvider().generateIdentity();
1315
+ const payload = { ...params };
1316
+ const groupId = String(params.group_id ?? '').trim();
1317
+ if (!groupId) {
1318
+ throw new ValidationError('completeGroupTransfer requires group_id');
1319
+ }
1320
+ let groupAid = String(params.group_aid ?? '').trim();
1321
+ if (!groupAid) {
1322
+ const info = await this.call('group.get_info', { group_id: groupId, required: ['member'] });
1323
+ groupAid = String(info?.group_aid ?? '').trim();
1324
+ }
1325
+ if (!groupAid) {
1326
+ throw new ValidationError('completeGroupTransfer: unable to determine group_aid');
1327
+ }
1328
+ const current = this.currentAid;
1329
+ const newOwner = String(current?.aid ?? '').trim();
1330
+ if (!current || !newOwner || !current.isPrivateKeyValid()) {
1331
+ throw new ValidationError('completeGroupTransfer requires current new-owner AID with private key');
1332
+ }
1333
+ const nonce = globalThis.crypto.randomUUID().replace(/-/g, '');
1334
+ const issuedMs = Date.now();
1335
+ const publicKeyHash = bytesToHex(new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(keyPair.public_key_der_b64))));
1336
+ const canonical = [
1337
+ 'aun-group-owner-transfer-accept-v1',
1338
+ groupId.toLowerCase(),
1339
+ groupAid.toLowerCase(),
1340
+ newOwner.toLowerCase(),
1341
+ publicKeyHash,
1342
+ nonce,
1343
+ String(issuedMs),
1344
+ ].join('|');
1345
+ const signed = await current.sign(canonical);
1346
+ if (!signed.ok) {
1347
+ throw new ValidationError(`completeGroupTransfer: accept sign failed: ${signed.error.message}`);
1348
+ }
1349
+ payload.group_aid = groupAid;
1350
+ payload.public_key = keyPair.public_key_der_b64;
1351
+ payload.curve = keyPair.curve;
1352
+ payload.transfer_accept = { nonce, issued_ms: issuedMs, signature: signed.data.signature };
1353
+ const result = await this.call('group.complete_transfer', payload);
1354
+ const resultMap = isJsonObject(result) ? result : {};
1355
+ const group = isJsonObject(resultMap.group) ? resultMap.group : {};
1356
+ const aidCert = isJsonObject(resultMap.aid_cert) ? resultMap.aid_cert : {};
1357
+ const returnedGroupAid = String(group.group_aid ?? resultMap.group_aid ?? '').trim();
1358
+ const certPem = String(aidCert.cert ?? aidCert.cert_pem ?? resultMap.cert ?? resultMap.cert_pem ?? '').trim();
1359
+ if (!returnedGroupAid || !certPem) {
1360
+ throw new ValidationError('group.complete_transfer response missing group.group_aid or aid_cert.cert');
1361
+ }
1362
+ const imported = await store.importGroupIdentity(returnedGroupAid, {
1363
+ private_key_pem: keyPair.private_key_pem,
1364
+ public_key_der_b64: keyPair.public_key_der_b64,
1365
+ curve: keyPair.curve,
1366
+ cert_pem: certPem,
1367
+ });
1368
+ if (!imported.ok) {
1369
+ throw new ValidationError(imported.error.message);
1370
+ }
1371
+ return result;
994
1372
  }
995
1373
  static _notifyParamsSizeOk(params) {
996
1374
  return new TextEncoder().encode(JSON.stringify(params)).length <= MAX_NOTIFY_PAYLOAD_SIZE;
@@ -1026,7 +1404,7 @@ export class AUNClient {
1026
1404
  throw new ValidationError('notify payload is too large');
1027
1405
  }
1028
1406
  const targetAid = String(options.to ?? '').trim();
1029
- const targetGroupId = String(options.group_id ?? options.groupId ?? '').trim();
1407
+ const targetGroupId = String(options.group_aid ?? options.groupAid ?? options.group_id ?? options.groupId ?? '').trim();
1030
1408
  const targetDeviceId = String(options.device_id ?? options.deviceId ?? '').trim();
1031
1409
  const targetSlotId = String(options.slot_id ?? options.slotId ?? '').trim();
1032
1410
  const ttl = AUNClient._normalizeNotifyTtl(options.ttl_ms ?? options.ttlMs);
@@ -1038,6 +1416,8 @@ export class AUNClient {
1038
1416
  }
1039
1417
  if (targetAid) {
1040
1418
  const eventMethod = AUNClient._validateNotifyEventMethod(method);
1419
+ // 校验目标 AID 格式(拒绝 __system__ 等非法格式)
1420
+ validateAIDFormat(targetAid, 'notify.to');
1041
1421
  const target = { type: 'aid', aid: targetAid };
1042
1422
  if (targetDeviceId)
1043
1423
  target.device_id = targetDeviceId;
@@ -1054,11 +1434,14 @@ export class AUNClient {
1054
1434
  }
1055
1435
  if (targetGroupId) {
1056
1436
  const eventMethod = AUNClient._validateNotifyEventMethod(method);
1437
+ // 校验目标 Group ID 格式
1438
+ validateGroupIDFormat(targetGroupId, 'notify.group_id');
1057
1439
  const normalizedGroupId = normalizeGroupId(targetGroupId);
1058
1440
  if (!normalizedGroupId) {
1059
1441
  throw new ValidationError('group_id is required for group notify');
1060
1442
  }
1061
1443
  const routeParams = {
1444
+ group_aid: normalizedGroupId,
1062
1445
  group_id: normalizedGroupId,
1063
1446
  deliver: { method: eventMethod, params: payload },
1064
1447
  };
@@ -1078,12 +1461,17 @@ export class AUNClient {
1078
1461
  }
1079
1462
  async _callRawV2Rpc(method, params) {
1080
1463
  const p = { ...(params ?? {}) };
1464
+ const rpcBackground = Boolean(p._rpc_background) || this._backgroundRpcDepth > 0;
1465
+ delete p._rpc_background;
1081
1466
  delete p._pull_gate_locked;
1082
1467
  delete p._skip_auto_ack;
1083
1468
  delete p.skip_auto_ack;
1084
1469
  delete p._group_cursor_params;
1085
- if (method.startsWith('group.') && p.group_id !== undefined && p.group_id !== null) {
1086
- p.group_id = normalizeGroupId(String(p.group_id)) || String(p.group_id);
1470
+ if (method.startsWith('group.') && p.group_aid !== undefined && p.group_aid !== null) {
1471
+ p.group_aid = normalizeGroupId(String(p.group_aid)) || String(p.group_aid);
1472
+ }
1473
+ else if (method.startsWith('group.') && p.group_id !== undefined && p.group_id !== null) {
1474
+ p.group_aid = normalizeGroupId(String(p.group_id)) || String(p.group_id);
1087
1475
  }
1088
1476
  if (method.startsWith('group.') && p.device_id === undefined) {
1089
1477
  p.device_id = this._deviceId;
@@ -1091,7 +1479,7 @@ export class AUNClient {
1091
1479
  if (method.startsWith('group.') && p.slot_id === undefined) {
1092
1480
  p.slot_id = this._slotId;
1093
1481
  }
1094
- return await this._rpcPipeline.rawCall(method, p);
1482
+ return await this._rpcPipeline.rawCall(method, p, { background: rpcBackground });
1095
1483
  }
1096
1484
  // ── 事件 ──────────────────────────────────────────
1097
1485
  /**
@@ -1195,19 +1583,19 @@ export class AUNClient {
1195
1583
  }
1196
1584
  _extractGroupIdFromResult(result) {
1197
1585
  const group = isJsonObject(result.group) ? result.group : null;
1198
- const gid = group ? String(group.group_id ?? '') : '';
1586
+ const gid = group ? String(group.group_aid ?? group.group_id ?? '') : '';
1199
1587
  if (gid)
1200
1588
  return gid;
1201
- const directGid = String(result.group_id ?? '');
1589
+ const directGid = String(result.group_aid ?? result.group_id ?? '');
1202
1590
  if (directGid)
1203
1591
  return directGid;
1204
1592
  const member = isJsonObject(result.member) ? result.member : null;
1205
- return member ? String(member.group_id ?? '') : '';
1593
+ return member ? String(member.group_aid ?? member.group_id ?? '') : '';
1206
1594
  }
1207
1595
  async _onRawGroupChanged(data) {
1208
1596
  const tStart = Date.now();
1209
1597
  const action = String(data?.action ?? '');
1210
- const groupIdInit = String(data?.group_id ?? '');
1598
+ const groupIdInit = String(data?.group_aid ?? data?.group_id ?? '');
1211
1599
  this._clientLog.debug(`_onRawGroupChanged enter: group_id=${groupIdInit} action=${action}`);
1212
1600
  try {
1213
1601
  if (isJsonObject(data)) {
@@ -1223,7 +1611,7 @@ export class AUNClient {
1223
1611
  d._verified = this._isEventSignatureVerified(verified);
1224
1612
  }
1225
1613
  }
1226
- const groupId = (d.group_id ?? '');
1614
+ const groupId = (d.group_aid ?? d.group_id ?? '');
1227
1615
  await this._delivery.handleGroupChangedEventSeq(d, groupId);
1228
1616
  }
1229
1617
  else {
@@ -1256,9 +1644,13 @@ export class AUNClient {
1256
1644
  this._v2E2EE.deleteBootstrapCacheEntry(`group:${groupId}`);
1257
1645
  this._v2GroupSecurityLevels.delete(groupId);
1258
1646
  this._v2StateChains.delete(groupId);
1259
- this._seqTracker.removeNamespace(`group:${groupId}`);
1260
- this._seqTracker.removeNamespace(`group_event:${groupId}`);
1261
- this._saveSeqTrackerState();
1647
+ const groupNs = `group:${groupId}`;
1648
+ const groupEventNs = `group_event:${groupId}`;
1649
+ this._seqTracker.removeNamespace(groupNs);
1650
+ this._seqTracker.removeNamespace(groupEventNs);
1651
+ this._persistRepairedSeq(groupNs);
1652
+ this._persistRepairedSeq(groupEventNs);
1653
+ void this._saveSeqTrackerState();
1262
1654
  // 2. 清理补洞去重缓存中的相关条目
1263
1655
  for (const key of this._gapFillDone.keys()) {
1264
1656
  if (key.includes(groupId)) {
@@ -1266,10 +1658,10 @@ export class AUNClient {
1266
1658
  }
1267
1659
  }
1268
1660
  // 3. 清理推送 seq 去重缓存
1269
- this._pushedSeqs.delete(`group:${groupId}`);
1270
- this._pushedSeqs.delete(`group_event:${groupId}`);
1271
- this._pendingOrderedMsgs.delete(`group:${groupId}`);
1272
- this._pendingOrderedMsgs.delete(`group_event:${groupId}`);
1661
+ this._pushedSeqs.delete(groupNs);
1662
+ this._pushedSeqs.delete(groupEventNs);
1663
+ this._pendingOrderedMsgs.delete(groupNs);
1664
+ this._pendingOrderedMsgs.delete(groupEventNs);
1273
1665
  this._clientLog.info(`cleanup dissolved group ${groupId} local state`);
1274
1666
  }
1275
1667
  async _verifyEventSignature(_event, cs) {
@@ -1766,7 +2158,7 @@ export class AUNClient {
1766
2158
  let consecutiveFailures = 0;
1767
2159
  const maxFailures = 2;
1768
2160
  this._heartbeatTimer = setInterval(async () => {
1769
- if (this._state !== 'connected' || this._closing)
2161
+ if (this.state !== ConnectionState.READY || this._closing)
1770
2162
  return;
1771
2163
  try {
1772
2164
  const pong = await this._transport.call('meta.ping', {});
@@ -1801,7 +2193,7 @@ export class AUNClient {
1801
2193
  clearInterval(this._heartbeatTimer);
1802
2194
  this._heartbeatTimer = null;
1803
2195
  }
1804
- if (newInterval > 0 && this._state === 'connected' && !this._closing) {
2196
+ if (newInterval > 0 && this.state === ConnectionState.READY && !this._closing) {
1805
2197
  this._startHeartbeat();
1806
2198
  }
1807
2199
  }
@@ -1820,7 +2212,7 @@ export class AUNClient {
1820
2212
  if (this._closing)
1821
2213
  return;
1822
2214
  this._tokenRefreshTimer = null;
1823
- if (this._state !== 'connected' || !this._gatewayUrl) {
2215
+ if (this.state !== ConnectionState.READY || !this._gatewayUrl) {
1824
2216
  scheduleRefresh();
1825
2217
  return;
1826
2218
  }
@@ -1838,14 +2230,14 @@ export class AUNClient {
1838
2230
  scheduleRefresh();
1839
2231
  return;
1840
2232
  }
1841
- if (this._state !== 'connected' || !this._gatewayUrl || this._closing) {
2233
+ if (this.state !== ConnectionState.READY || !this._gatewayUrl || this._closing) {
1842
2234
  scheduleRefresh();
1843
2235
  return;
1844
2236
  }
1845
2237
  try {
1846
2238
  identity = await this._auth.refreshCachedTokens(this._gatewayUrl, identity);
1847
2239
  // 刷新期间可能已断线,复检状态,避免写回 stale identity
1848
- if (this._state !== 'connected') {
2240
+ if (this.state !== ConnectionState.READY) {
1849
2241
  scheduleRefresh();
1850
2242
  return;
1851
2243
  }
@@ -1930,6 +2322,9 @@ export class AUNClient {
1930
2322
  this._state = 'standby';
1931
2323
  // 先停止后台任务,避免心跳/token刷新在重连期间继续触发
1932
2324
  this._stopBackgroundTasks();
2325
+ void this._transport.close().catch((exc) => {
2326
+ this._clientLog.debug(`transport cleanup skipped: ${formatCaughtError(exc)}`);
2327
+ });
1933
2328
  await this._dispatcher.publish('state_change', {
1934
2329
  state: this._publicState(this._state),
1935
2330
  error,
@@ -2108,6 +2503,7 @@ export class AUNClient {
2108
2503
  return JSON.stringify([this._aid, this._deviceId, this._slotId]);
2109
2504
  }
2110
2505
  _resetSeqTrackingState() {
2506
+ void this._saveSeqTrackerState();
2111
2507
  this._resetV2IdentityRuntime();
2112
2508
  this._seqTracker = new SeqTracker();
2113
2509
  this._seqTrackerContext = null;
@@ -2128,6 +2524,7 @@ export class AUNClient {
2128
2524
  const nextContext = this._currentSeqTrackerContext();
2129
2525
  if (nextContext === this._seqTrackerContext)
2130
2526
  return;
2527
+ void this._saveSeqTrackerState();
2131
2528
  this._seqTracker = new SeqTracker();
2132
2529
  this._gapFillDone.clear();
2133
2530
  this._pushedSeqs.clear();
@@ -2147,6 +2544,9 @@ export class AUNClient {
2147
2544
  _saveSeqTrackerState() {
2148
2545
  return this._delivery.saveSeqTrackerState();
2149
2546
  }
2547
+ _persistSeq(ns, forceSeq) {
2548
+ return this._delivery.persistSeq(ns, forceSeq);
2549
+ }
2150
2550
  _persistRepairedSeq(ns) {
2151
2551
  return this._delivery.persistRepairedSeq(ns);
2152
2552
  }
@@ -2663,12 +3063,6 @@ export class AUNClient {
2663
3063
  async _onV2PushNotification(data) {
2664
3064
  return this._delivery.onV2PushNotification(data);
2665
3065
  }
2666
- /**
2667
- * 处理 V2 epoch 轮换事件:清除 bootstrap 缓存 + 触发 SPK rotation。
2668
- */
2669
- async _onV2EpochRotated(data) {
2670
- return this._v2E2EE.handleV2EpochRotated(data);
2671
- }
2672
3066
  /** 安全执行异步操作(不阻塞调用方,错误打 warning 便于排障) */
2673
3067
  _safeAsync(promise) {
2674
3068
  promise.catch((exc) => {
@@ -2688,6 +3082,15 @@ export class AUNClient {
2688
3082
  && (!slotId || slotId === (this._slotId ?? ''));
2689
3083
  }
2690
3084
  // ── Pull Gate(序列化同一 key 的并发 pull)──────────────────
3085
+ async _withBackgroundRpc(operation) {
3086
+ this._backgroundRpcDepth += 1;
3087
+ try {
3088
+ return await operation();
3089
+ }
3090
+ finally {
3091
+ this._backgroundRpcDepth = Math.max(0, this._backgroundRpcDepth - 1);
3092
+ }
3093
+ }
2691
3094
  async _runPullSerialized(key, operation) {
2692
3095
  return await this._rpcPipeline.runPullSerialized(key, operation);
2693
3096
  }