@agentunion/fastaun-browser 0.5.1 → 0.5.2

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 (50) hide show
  1. package/CHANGELOG.md +548 -518
  2. package/_packed_docs/CHANGELOG-validators.md +134 -131
  3. package/_packed_docs/CHANGELOG.md +548 -518
  4. package/_packed_docs/INDEX.md +51 -44
  5. package/_packed_docs/KITE_DOCS_GUIDE.md +19 -16
  6. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +169 -116
  7. package/_packed_docs/aun-perf-audit-critical-bugs.md +315 -0
  8. package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +263 -261
  9. package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +328 -328
  10. package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +2 -2
  11. 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 +1 -1
  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 +39 -16
  13. package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +8 -5
  14. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +18 -19
  15. 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 +1 -1
  16. package/_packed_docs/protocol/08-AUN-E2EE-Group.md +293 -294
  17. package/_packed_docs/protocol/08-AUN-E2EE.md +12 -10
  18. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +85 -89
  19. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +4 -4
  20. 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 +1 -1
  21. 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 -177
  22. package/_packed_docs/protocol/README.md +4 -4
  23. package/_packed_docs/protocol/aun-docs-guide.md +2 -2
  24. package/_packed_docs/protocol/index.md +4 -4
  25. 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 +1 -1
  26. package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +13 -13
  27. package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +9 -9
  28. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +15 -13
  29. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +26 -18
  30. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +33 -34
  31. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +22 -17
  32. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +11 -7
  33. package/_packed_docs/sdk/09-group-rpc-manual.md +135 -114
  34. package/_packed_docs/sdk/09-message-rpc-manual.md +50 -28
  35. package/_packed_docs/sdk/09-payload-reference.md +3 -3
  36. package/_packed_docs/sdk/09-storage-rpc-manual.md +1 -1
  37. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +11 -10
  38. 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 +3 -2
  39. package/_packed_docs/sdk/INDEX.md +15 -14
  40. package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +6 -2
  41. package/dist/agent-md.d.ts.map +1 -1
  42. package/dist/agent-md.js +18 -9
  43. package/dist/agent-md.js.map +1 -1
  44. package/dist/bundle.js +104 -24
  45. package/dist/client/v2-e2ee.d.ts.map +1 -1
  46. package/dist/client/v2-e2ee.js +78 -7
  47. package/dist/client/v2-e2ee.js.map +1 -1
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.js +1 -1
  50. package/package.json +1 -1
@@ -105,12 +105,15 @@ print(auth["access_token"], auth["gateway"])
105
105
  ### 连接
106
106
 
107
107
  ```python
108
- await client.connect({
109
- "slot_id": "main",
110
- "connection_kind": "long",
111
- "auto_reconnect": True,
112
- "heartbeat_interval": 30.0,
113
- "token_refresh_before": 60.0,
108
+ await client.connect({
109
+ "slot_id": "main",
110
+ "connection_kind": "long",
111
+ "delivery_mode": {"mode": "fanout"},
112
+ "background_sync": True,
113
+ "extra_info": {"app": "demo"},
114
+ "auto_reconnect": True,
115
+ "heartbeat_interval": 30.0,
116
+ "token_refresh_before": 60.0,
114
117
  "retry": {
115
118
  "initial_delay": 1.0,
116
119
  "max_delay": 64.0,
@@ -126,12 +129,14 @@ await client.connect({
126
129
  | 选项 | 说明 |
127
130
  |------|------|
128
131
  | `slot_id` | 同一设备内的实例槽位;允许 `/`、`:`、空格作为共享隔离键分隔符 |
129
- | `connection_kind` | `"long"` 或 `"short"` |
130
- | `short_ttl_ms` | 短连接服务端兜底超时 |
131
- | `delivery_mode` | 连接级投递语义 |
132
- | `auto_reconnect` | 断线后是否自动重连 |
133
- | `heartbeat_interval` | 心跳间隔,秒 |
134
- | `token_refresh_before` | token 过期前刷新提前量,秒 |
132
+ | `connection_kind` | `"long"` 或 `"short"` |
133
+ | `short_ttl_ms` | 短连接服务端兜底超时 |
134
+ | `delivery_mode` | 连接级投递语义 |
135
+ | `extra_info` | 应用层自定义连接信息;下划线开头的键不会透传给 Gateway |
136
+ | `background_sync` | 连接成功后是否执行 SDK 后台补洞 / 未读同步,默认开启 |
137
+ | `auto_reconnect` | 断线后是否自动重连 |
138
+ | `heartbeat_interval` | 心跳间隔,秒 |
139
+ | `token_refresh_before` | token 过期前刷新提前量,秒 |
135
140
  | `retry.initial_delay` / `retry.max_delay` | 退避重连参数 |
136
141
  | `timeouts.connect/call/http` | 连接、RPC、HTTP 超时 |
137
142
 
@@ -188,10 +193,11 @@ print(client.can_connect, client.can_send, client.is_online)
188
193
 
189
194
  | 事件 | 说明 |
190
195
  |------|------|
191
- | `state_change` | 状态变化,payload 中的 `state` 是九态公开值 |
192
- | `connection.error` | 连接、认证、重连错误 |
193
- | `token.refreshed` | token 自动刷新完成 |
194
- | `message.received` | 收到消息 |
196
+ | `state_change` | 状态变化,payload 中的 `state` 是九态公开值 |
197
+ | `connection.error` | 连接、认证、重连错误 |
198
+ | `token.refreshed` | token 自动刷新完成 |
199
+ | `token.refresh_exhausted` | refresh_token 缺失、过期或刷新链耗尽,SDK 已清理本地 token,下一次重连会重新走完整登录 |
200
+ | `message.received` | 收到消息 |
195
201
  | `group.changed` | 群组事件 |
196
202
  | `message.undecryptable` / `group.message_undecryptable` | E2EE 解密失败 |
197
203
 
@@ -238,8 +244,10 @@ state = await store.check_agent_md("bob.agentid.pub", ttl_days=1)
238
244
  | `AUNClient` | 连接、事件和 RPC 调用;不再暴露 agent.md 上传入口 |
239
245
 
240
246
  本地落盘位置由 SDK 管理。Python / TypeScript / Go 写入 `{aun_path}/AIDs/{aid}/agent.md` 和同目录 `agentmd.json`;浏览器 JavaScript 写入 IndexedDB 的等价 logical key,存储不可用时退化为内存缓存。agent.md 不写入 SQLite,也不再使用旧 `{aun_path}/AgentMDs` 目录。
241
-
242
- ---
247
+
248
+ 连接后的 RPC 响应、事件推送和消息信封会被 SDK 自动观察:Gateway `_meta.agent_md_etags` 中的 `requester`、`peer`、`group` 以及兼容别名 `receiver`、`target`、`to`、`sender`、`from` 会更新对应 AID 的 `remote_etag` / `last_modified`;V2 信封中的 `agent_md.sender` 和 `agent_md.group` 也会写入同一份本地记录。`group` 表示群自身 `group_aid` / `group_id` 的 agent.md,缺少 `aid` 时 SDK 会从信封顶层或 AAD 的 `group_aid` / `group_id` 兜底。
249
+
250
+ ---
243
251
 
244
252
  ## RPC 调用
245
253
 
@@ -41,14 +41,14 @@ await client.call("message.send", {
41
41
  })
42
42
  ```
43
43
 
44
- SDK 优先使用 prekey_ecdh_v2,并默认要求前向保密:
45
-
46
- 1. **prekey_ecdh_v2** 对方有预上传的 prekey,四路 ECDH(ephemeral×prekey + ephemeral×identity + sender×prekey + sender×identity),前向安全,附带发送方签名
47
- 2. **long_term_key** 对方无 prekey,双路 ECDH(ephemeral×recipient_identity + sender×recipient_identity)+ HKDF 派生密钥(降级模式),附带发送方签名
48
-
49
- > Python SDK 默认 `require_forward_secrecy=true`,当加密结果不满足前向保密(无论是因为无 prekey 还是 prekey 加密失败降级到 long_term_key)时拒绝发送并抛出错误。需显式配置 `require_forward_secrecy=false` 才允许降级。
50
-
51
- 每条消息独立生成临时 ECDH 密钥对,实现一消息一密钥。
44
+ 当前 SDK 使用 E2EE V2 多设备 wrap 主路径:
45
+
46
+ 1. 发送前通过 `message.v2.bootstrap` `group.v2.bootstrap` 获取接收方当前活跃设备、设备 prekey、self-sync 设备和 audit recipients。
47
+ 2. SDK 为每条消息生成独立 `master_key`、消息 nonce 和发送方临时 session key,只加密一次正文。
48
+ 3. SDK 为每个接收设备生成一条 recipient wrap。设备有可用 SPK 时使用 `3DH` wrap;缺少 SPK 的兼容场景才使用 `1DH` wrap。
49
+ 4. E2EE 信封包含 `sender_signature`、AAD、`recipients_digest` / Merkle proof,接收端必须验签、验 AAD、验 recipient proof 后再解密。
50
+
51
+ 每条消息独立密钥;V2 当前主路径不再使用旧 `prekey_ecdh_v2` / `long_term_key` 信封作为默认发送格式。旧术语只用于历史兼容文档或迁移排查。
52
52
 
53
53
  ## ProtectedHeaders 与可验证上下文
54
54
 
@@ -65,9 +65,11 @@ SDK 优先使用 prekey_ecdh_v2,并默认要求前向保密:
65
65
 
66
66
  `payload_type` 不需要应用层传入。SDK 会读取加密前 `payload.type`,自动写入 `protected_headers.payload_type`,接收端解密后会校验它与明文 `payload.type` 一致。
67
67
 
68
- `protected_headers` / `headers` 是 send/thought 参数的顶层字段,不放入单独的 `envelope` 入参对象,也不属于业务 `payload`。裸 WebSocket 客户端若自行发送已加密信封,需要把 protected headers 放在自构造的 E2EE 信封内并自行完成 `_auth`,服务端不会替裸 RPC 调用生成或校验明文侧的 protected headers。
69
-
70
- 示例:
68
+ `protected_headers` / `headers` 是 send/thought 参数的顶层字段,不放入单独的 `envelope` 入参对象,也不属于业务 `payload`。裸 WebSocket 客户端若自行发送已加密信封,需要把 protected headers 放在自构造的 E2EE 信封内并自行完成 `_auth`,服务端不会替裸 RPC 调用生成或校验明文侧的 protected headers。
69
+
70
+ `agent_md` 是独立于 E2EE 的版本提示元数据,不属于 `protected_headers`,也不参与 AAD 或业务鉴权。当前 Message Service V2 P2P 信封可携带 `agent_md.sender`;SDK 也识别 `agent_md.group`。Gateway 在 RPC response / event push 的 `_meta.agent_md_etags` 中注入 `requester`、`peer`、`group`,四端 SDK 会自动写入对应 AID 的 `remote_etag` / `last_modified`,后续仍以下载后的 agent.md 签名验证作为可信依据。
71
+
72
+ 示例:
71
73
 
72
74
  ```python
73
75
  from aun_core import ProtectedHeaders
@@ -103,7 +105,8 @@ await client.call("group.send", {
103
105
 
104
106
  ```json
105
107
  {
106
- "type": "e2ee.encrypted",
108
+ "type": "e2ee.p2p_encrypted",
109
+ "version": "v2",
107
110
  "ciphertext": "...",
108
111
  "protected_headers": {
109
112
  "device_id": "dev-123",
@@ -127,8 +130,8 @@ await client.call("group.send", {
127
130
 
128
131
  计算规则:
129
132
 
130
- 1. 解密流程会派生出本条消息的 `message_key`。
131
- 2. `metadata_key = HMAC-SHA256(message_key, "aun-envelope-metadata-key-v1")`。
133
+ 1. 解密流程会得到本条消息的 `master_key`。
134
+ 2. `metadata_key = HMAC-SHA256(master_key, "aun-envelope-metadata-key-v1")`。
132
135
  3. 对字典去掉 `_auth` 后做 canonical JSON:UTF-8、key 排序、紧凑分隔符。
133
136
  4. `tag = HMAC-SHA256(metadata_key, domain + "\0" + canonical_json(body))`。
134
137
  5. `domain` 对 `protected_headers` 为 `aun-protected-headers-v1`,对 `context` 为 `aun-protected-context-v1`。
@@ -294,24 +297,20 @@ for msg in result["messages"]:
294
297
 
295
298
  ---
296
299
 
297
- ## Prekey 管理
298
-
299
- 连接时 SDK 自动上传 prekey,并定时轮换(默认每小时)。一般无需手动管理。
300
-
301
- 手动上传 prekey:
302
-
303
- ```python
304
- # 通过 AUNClient 上传(生成 + RPC)
305
- await client._upload_prekey()
306
- ```
307
-
308
- 底层 API(E2EEManager 只生成材料,不做 RPC):
309
-
310
- ```python
311
- prekey_material = client.e2ee.generate_prekey()
312
- # 返回 {"prekey_id": "...", "public_key": "...", "signature": "...", "created_at": ...}
313
- # 需要自行上传:await transport.call("message.e2ee.put_prekey", prekey_material)
314
- ```
300
+ ## V2 设备公钥与 SPK 管理
301
+
302
+ 连接成功后,SDK 会初始化本设备 V2 session,生成或加载 IK / SPK,并通过 `message.v2.put_peer_pk` 幂等注册当前 P2P 设备 SPK。群组路径会按群生成独立 group SPK,并通过 `group.v2.put_group_pk` 注册。应用层一般无需手动管理这些密钥。
303
+
304
+ 当前主路径的要点:
305
+
306
+ - P2P 设备 SPK 的 `key_source` 为 `peer_device_prekey`,由 AID 私钥签名背书。
307
+ - 群内独立 group SPK `key_source` 为 `group_device_prekey`,按规范化后的 `group_aid` 隔离。
308
+ - SDK 发送前通过 `message.v2.bootstrap` / `group.v2.bootstrap` 获取目标设备集合和当前 SPK。
309
+ - 旧 SPK 会在本地保留一段安全窗口,用于解密引用旧 SPK 的历史消息;满足已消费和保留窗口条件后才销毁。
310
+
311
+ WebSocket 客户端如果绕过 SDK,需要自行完成同等的 SPK 生成、AID 私钥签名、注册和 bootstrap 逻辑。旧 `message.e2ee.put_prekey/get_prekey` 只用于 legacy `prekey_ecdh_v2` 信封兼容,不是当前 SDK 的默认路径。
312
+
313
+ SPK 签名里的 `spk_timestamp` 使用 Unix 秒;消息、群事件和服务端 `timestamp` / `created_at` 等主路径时间字段仍使用 Unix 毫秒。
315
314
 
316
315
  ---
317
316
 
@@ -361,7 +360,7 @@ client = AUNClient(aid)
361
360
  | P2P 消息默认要求发送方签名 | 无 `sender_signature` 的消息被拒绝 |
362
361
  | 群组消息默认要求发送方签名 | `require_signature=True`,无签名或无发送方证书的消息被拒绝 |
363
362
  | 群组 E2EE 为固定启用能力 | `group_e2ee=true`,不可关闭 |
364
- | 默认要求前向保密 | `require_forward_secrecy=true`,无 prekey 时拒绝 long_term_key 降级 |
365
- | 客户端操作签名 | `group.send`/`group.kick`/`group.add_member`/`group.leave` 等操作自动附加 `client_signature`,服务端强制验签 |
363
+ | 默认要求前向保密 | V2 优先使用设备 SPK 的 `3DH` wrap;缺少 SPK `1DH` 路径仅作为兼容降级 |
364
+ | 客户端操作签名 | SDK 会为关键操作附加 `client_signature`;Gateway 对 `send/pull/ack` 等常规 RPC 优先使用连接级身份认证,只有身份声明与连接不一致、敏感操作、能力身份或主动携签场景才执行 ECDSA 验签 |
366
365
 
367
366
 
@@ -116,9 +116,11 @@ Python、TS/Node 与 Go 的 `AIDStore` 本地方法返回 Result 包装;浏览
116
116
  | `cached` | bool | 是否命中 TTL 窗口内的本地检查记录 |
117
117
  | `verify_status` / `verify_error` | str | 最近一次下载验签状态 |
118
118
 
119
- agent.md 本地记录不写入 SQLite。Python / TypeScript / Go 使用 `{aun_path}/AIDs/{aid}/agent.md` 与 `agentmd.json`;浏览器 JavaScript 使用 IndexedDB 等价 key,存储不可用时退化为内存缓存。
120
-
121
- > **v0.4.2 变更**:`discoveryPort` 配置项已移除,Gateway 地址完全由 SDK 根据 AID issuer 自动发现,无需手动指定端口。
119
+ agent.md 本地记录不写入 SQLite。Python / TypeScript / Go 使用 `{aun_path}/AIDs/{aid}/agent.md` 与 `agentmd.json`;浏览器 JavaScript 使用 IndexedDB 等价 key,存储不可用时退化为内存缓存。
120
+
121
+ `remote_etag` / `last_modified` 除了来自 `check_agent_md()` 的 HEAD,也会由连接后的内部观察器更新:SDK 会读取 RPC response / event push `_meta.agent_md_etags` 的 `requester`、`peer`、`group` 及兼容别名,并读取 V2 envelope 的 `agent_md.sender` / `agent_md.group`。`group` 记录使用群自身 `group_aid` / `group_id` 作为 AID key。
122
+
123
+ > **v0.4.2 变更**:`discoveryPort` 配置项已移除,Gateway 地址完全由 SDK 根据 AID issuer 自动发现,无需手动指定端口。
122
124
 
123
125
  ---
124
126
 
@@ -260,7 +262,7 @@ await client.call("meta.trust_roots", {})
260
262
  ```python
261
263
  await client.notify("notification/client.activity", {"state": "idle"})
262
264
  await client.notify("event/app.typing", {"thread_id": "t1"}, to="bob.agentid.pub", ttl_ms=5000)
263
- await client.notify("event/app.presence", {"state": "active"}, group_id="group.agentid.pub/123")
265
+ await client.notify("event/app.presence", {"state": "active"}, group_id="g-abc123.agentid.pub")
264
266
  ```
265
267
 
266
268
  路由选项:
@@ -268,7 +270,7 @@ await client.notify("event/app.presence", {"state": "active"}, group_id="group.a
268
270
  | 选项 | 说明 |
269
271
  |------|------|
270
272
  | `to` / `To` | 目标 AID;可同域或跨域 |
271
- | `group_id` / `groupId` / `GroupID` | 目标群;与 `to` 互斥 |
273
+ | `group_id` / `groupId` / `GroupID` | 目标群;兼容参数名,值使用目标态 `group_aid`,与 `to` 互斥 |
272
274
  | `device_id` / `deviceId` / `DeviceID` | 限定目标 AID 的在线设备;必须配合 `to` |
273
275
  | `slot_id` / `slotId` / `SlotID` | 限定目标设备的在线 slot;必须配合 `device_id` |
274
276
  | `ttl_ms` / `ttlMs` / `TTLMS` | `0..60000`,只控制在线投递过期,不表示离线缓存 |
@@ -307,10 +309,11 @@ headers = client.get_protected_headers()
307
309
 
308
310
  说明:
309
311
 
310
- - agent.md 上传、下载和检查入口都在 `AIDStore`;`AUNClient` 不再暴露上传入口。
311
- - 上传要求目标 AID 已在本地加载且私钥有效;SDK 会对正文签名,并通过 `AuthFlow` 获取或复用该 AID 的 access_token。
312
- - SDK 发起 GET 时只发送 `Accept: text/markdown`,不主动发送 `If-None-Match` / `If-Modified-Since`。如果服务端异常返回 304,本地有内容则复用;无内容时再发一次无条件 GET。
313
- - `Accept: text/markdown` agent.md YAML frontmatter + Markdown 格式兼容;agent.md 仍是 Markdown 媒体类型上的结构化约定。
312
+ - agent.md 上传、下载和检查入口都在 `AIDStore`;`AUNClient` 不再暴露上传入口。
313
+ - 上传要求目标 AID 已在本地加载且私钥有效;SDK 会对正文签名,并通过 `AuthFlow` 获取或复用该 AID 的 access_token。
314
+ - SDK 发起 GET 时只发送 `Accept: text/markdown`,不主动发送 `If-None-Match` / `If-Modified-Since`。如果服务端异常返回 304,本地有内容则复用;无内容时再发一次无条件 GET。
315
+ - SDK 会自动从 Gateway `_meta.agent_md_etags` 和信封 `agent_md` 观察远端版本;`requester`、`peer`、`group` 是标准角色键,`receiver`、`target`、`to`、`sender`、`from` 是兼容别名。
316
+ - `Accept: text/markdown` 与 agent.md 的 YAML frontmatter + Markdown 格式兼容;agent.md 仍是 Markdown 媒体类型上的结构化约定。
314
317
 
315
318
  ---
316
319
 
@@ -415,14 +418,16 @@ sub.unsubscribe()
415
418
 
416
419
  | 事件 | 说明 |
417
420
  |------|------|
418
- | `state_change` | 状态变化,`state` 为九态公开值 |
419
- | `connection.error` | 连接或重连错误 |
420
- | `token.refreshed` | token 刷新完成 |
421
- | `message.received` | 收到 P2P 消息 |
422
- | `message.ack` | 消息 ack |
423
- | `message.undecryptable` | P2P E2EE 解密失败 |
424
- | `group.changed` | 群组事件 |
425
- | `group.message_undecryptable` | E2EE 解密失败 |
421
+ | `state_change` | 状态变化,`state` 为九态公开值 |
422
+ | `connection.error` | 连接或重连错误 |
423
+ | `token.refreshed` | token 刷新完成 |
424
+ | `token.refresh_exhausted` | refresh_token 缺失、过期或刷新链耗尽,SDK 已清理本地 token 并等待重新登录 |
425
+ | `message.received` | 收到 P2P 消息 |
426
+ | `message.ack` | 消息 ack |
427
+ | `message.undecryptable` | P2P E2EE 解密失败 |
428
+ | `group.changed` | 群组事件 |
429
+ | `group.message_undecryptable` | 群 E2EE 解密失败 |
430
+ | `storage.object_changed` | Storage 对象变更事件透传 |
426
431
 
427
432
  ---
428
433
 
@@ -143,12 +143,16 @@ await client.call("message.send", params) # 非 ready 状态会抛 ConnectionEr
143
143
  发送前检查:
144
144
 
145
145
  ```python
146
- if not client.can_send:
147
- await client.connect({"auto_reconnect": True})
148
- ```
149
-
150
- ### E2EE 解密失败
151
-
152
- P2P 或群消息解密失败通常由 prekey 不匹配、AAD 篡改、密文损坏或群 epoch 不一致引起。SDK 会发布 `message.undecryptable` / `group.message_undecryptable` 事件,应用可记录并继续处理其他消息。
146
+ if not client.can_send:
147
+ await client.connect({"auto_reconnect": True})
148
+ ```
149
+
150
+ ### refresh_token 失效
151
+
152
+ 当自动刷新命中 `missing refresh_token`、`invalid_or_expired_refresh_token`、刷新链耗尽或服务端返回 `relogin_required=true` 时,SDK 会清理本地 `access_token` / `refresh_token` / `kite_token`,发布 `token.refresh_exhausted` 事件,并在后续重连时重新走完整登录链路。应用层可监听该事件提示用户重新授权;不要继续复用旧 token。
153
+
154
+ ### E2EE 解密失败
155
+
156
+ P2P 或群消息解密失败通常由 prekey 不匹配、AAD 篡改、密文损坏或群 epoch 不一致引起。SDK 会发布 `message.undecryptable` / `group.message_undecryptable` 事件,应用可记录并继续处理其他消息。
153
157
 
154
158