@agentunion/fastaun-browser 0.4.10 → 0.4.12

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 (41) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/_packed_docs/CHANGELOG.md +50 -0
  3. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +4 -2
  4. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +2 -2
  5. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +7 -4
  6. package/_packed_docs/sdk/09-group-rpc-manual.md +222 -38
  7. package/_packed_docs/sdk/09-message-rpc-manual.md +50 -26
  8. package/_packed_docs/sdk/09-payload-reference.md +1 -1
  9. package/_packed_docs/sdk/09-storage-rpc-manual.md +259 -37
  10. package/dist/bundle.js +320 -50
  11. package/dist/client/delivery.d.ts +10 -1
  12. package/dist/client/delivery.d.ts.map +1 -1
  13. package/dist/client/delivery.js +166 -20
  14. package/dist/client/delivery.js.map +1 -1
  15. package/dist/client/group-state.js +2 -2
  16. package/dist/client/group-state.js.map +1 -1
  17. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  18. package/dist/client/rpc-pipeline.js +31 -5
  19. package/dist/client/rpc-pipeline.js.map +1 -1
  20. package/dist/client/v2-e2ee.d.ts.map +1 -1
  21. package/dist/client/v2-e2ee.js +9 -2
  22. package/dist/client/v2-e2ee.js.map +1 -1
  23. package/dist/client.d.ts +1 -0
  24. package/dist/client.d.ts.map +1 -1
  25. package/dist/client.js +55 -14
  26. package/dist/client.js.map +1 -1
  27. package/dist/service-proxy.d.ts.map +1 -1
  28. package/dist/service-proxy.js +17 -2
  29. package/dist/service-proxy.js.map +1 -1
  30. package/dist/v2/e2ee/encrypt-p2p.js +1 -1
  31. package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
  32. package/dist/v2/session/keystore.d.ts.map +1 -1
  33. package/dist/v2/session/keystore.js +8 -9
  34. package/dist/v2/session/keystore.js.map +1 -1
  35. package/dist/v2/session/session.d.ts +4 -2
  36. package/dist/v2/session/session.d.ts.map +1 -1
  37. package/dist/v2/session/session.js +20 -4
  38. package/dist/v2/session/session.js.map +1 -1
  39. package/dist/version.d.ts +1 -1
  40. package/dist/version.js +1 -1
  41. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,56 @@
6
6
 
7
7
  ---
8
8
 
9
+ ## 0.4.12 — 2026-06-08
10
+
11
+ ### 新功能
12
+ - **应用层事件信封(envelope)**:`message.*` 与 `group.changed` 事件发布给应用层时注入 `envelope` 字段,聚合 `message_id`/`seq`/`from`/`to`/`group_id`/`action` 等元数据;顶层别名字段在兼容期保留,计划于 `0.5.*` 移除,请改用 `envelope.*` 访问(四语言对齐)
13
+ - **撤回事件携带 message_id 与自身信封**:`message.recalled` / `group.message_recalled` 通知补全 `message_id` 字段并继承原消息的信封键(四语言对齐)
14
+
15
+ ### 修复
16
+ - **入群首个事件被旧序号阻塞**:`isSelfJoinGroupChanged` 识别自己入群后,将本地 `group_event` seq 基线对齐到 `eventSeq-1`,避免被入群前不可见事件挡住(四语言对齐)
17
+ - **过期 token 重连死循环**:重连前同步 `_identity` 中的 token 状态到 `_sessionParams`,过期或缺失则清空以触发两阶段重新登录,避免反复用旧 token 触发 4001(四语言对齐)
18
+ - **Service Proxy 持久隧道重连**:新增指数退避(上限 60s,成功后重置);`AuthError` 触发 `_authenticateForAccessToken` 重新登录后再重连(四语言对齐)
19
+ - **protected_headers 参数兼容**:`mergeInstanceProtectedHeaders` 同时识别 `protected_headers` 与 `headers` 别名
20
+
21
+ ### 测试
22
+ - `自己入群首个 event_seq>1 不应被入群前不可见事件阻塞`
23
+ - `pull 缺失中间 event_seq 时视为永久空洞,不阻塞已拿到的群事件发布`
24
+ - `publishAppEvent 为群事件注入 envelope 并保留顶层兼容字段`
25
+ - `撤回事件发布给应用层时带撤回通知自身 envelope`
26
+
27
+ ---
28
+
29
+ ## 0.4.11 — 2026-06-08
30
+
31
+ ### 新功能
32
+ - **Storage 目录树与扩展操作**:`storage.*` 新增 15 个存储操作方法(含 `create_folder`/`rename_folder`/`move_object`/`batch_delete` 等),加入签名集合和非幂等集合(四语言+服务端对齐)
33
+ - **group.resources 树形资源系统**:新增 11 个资源管理方法,加入签名和非幂等集合;支持跨域访问票据(四语言+服务端对齐)
34
+ - **group.changed 事件保序去重**:新增 `handleGroupChangedEventSeq` 支持 event_seq 追踪和有序消息队列,空洞先到时缓存补洞;新增 `_isEventSignatureVerified()` 区分 pending 不标记已验签(四语言对齐)
35
+ - **V2 E2EE 注册并发保护**:SPK 注册改用 `_registeringPromise` 缓存并发请求,多并发调用时等待同一 Promise(四语言对齐)
36
+
37
+ ### 修复
38
+ - **IndexedDB 事务原子性**:`addSPK` 改用单事务处理原始记录和别名,避免独立事务导致一致性问题
39
+ - **SPK 销毁顺序**:`_doAutoDestroy` 中先删设备级密钥再删存储级密钥
40
+ - **sdk_version 拼写**:`sdk_vesion` → `sdk_version`
41
+ - **V2 并发冲突检测**:检查对端操作的 `inflightSet`,防止 SPK 注册与轮换并发冲突
42
+ - **storage/group.resources 签名和幂等性补全**:补全写操作方法进入签名集合与非幂等集合(四语言对齐)
43
+
44
+ ### 优化
45
+ - **群事件处理流程重构**:`group.changed` 事件分发逻辑从 `client.ts` 迁移到 `delivery.ts`,支持按 event_seq 保序发布;新增 `publishOrderedQueueItem()` / `publishOrderedGroupChanged()`
46
+ - **群组解散清理**:解散事件处理改为异步 `drainOrderedMessages`,保证 seq 追踪一致性
47
+ - **群事件自动 ack**:`handleGroupChangedEventSeq` 无需补洞时直接 ack event cursor
48
+ - **错误日志补充**:V2Session SPK 销毁异常捕获并输出日志
49
+
50
+ ### 测试
51
+ - 新增事件验签状态单元测试(pending 状态不标记已验签)
52
+ - 新增消息乱序补洞保序测试(高序号 push 先到时 SDK 内部消费和应用层发布的保序去重)
53
+ - 新增签名方法覆盖测试(storage 写操作和有副作用读操作进入非幂等集合)
54
+ - 新增 RPC 管道非幂等超时测试(storage/resources 方法按非幂等长超时 35s 发送)
55
+ - 修复 `handleGroupChangedEventSeq` 单元测试 async/await 标记和 ack cursor 验证
56
+
57
+ ---
58
+
9
59
  ## 0.4.10 — 2026-06-06
10
60
 
11
61
  ### 新功能
@@ -6,6 +6,56 @@
6
6
 
7
7
  ---
8
8
 
9
+ ## 0.4.12 — 2026-06-08
10
+
11
+ ### 新功能
12
+ - **应用层事件信封(envelope)**:`message.*` 与 `group.changed` 事件发布给应用层时注入 `envelope` 字段,聚合 `message_id`/`seq`/`from`/`to`/`group_id`/`action` 等元数据;顶层别名字段在兼容期保留,计划于 `0.5.*` 移除,请改用 `envelope.*` 访问(四语言对齐)
13
+ - **撤回事件携带 message_id 与自身信封**:`message.recalled` / `group.message_recalled` 通知补全 `message_id` 字段并继承原消息的信封键(四语言对齐)
14
+
15
+ ### 修复
16
+ - **入群首个事件被旧序号阻塞**:`isSelfJoinGroupChanged` 识别自己入群后,将本地 `group_event` seq 基线对齐到 `eventSeq-1`,避免被入群前不可见事件挡住(四语言对齐)
17
+ - **过期 token 重连死循环**:重连前同步 `_identity` 中的 token 状态到 `_sessionParams`,过期或缺失则清空以触发两阶段重新登录,避免反复用旧 token 触发 4001(四语言对齐)
18
+ - **Service Proxy 持久隧道重连**:新增指数退避(上限 60s,成功后重置);`AuthError` 触发 `_authenticateForAccessToken` 重新登录后再重连(四语言对齐)
19
+ - **protected_headers 参数兼容**:`mergeInstanceProtectedHeaders` 同时识别 `protected_headers` 与 `headers` 别名
20
+
21
+ ### 测试
22
+ - `自己入群首个 event_seq>1 不应被入群前不可见事件阻塞`
23
+ - `pull 缺失中间 event_seq 时视为永久空洞,不阻塞已拿到的群事件发布`
24
+ - `publishAppEvent 为群事件注入 envelope 并保留顶层兼容字段`
25
+ - `撤回事件发布给应用层时带撤回通知自身 envelope`
26
+
27
+ ---
28
+
29
+ ## 0.4.11 — 2026-06-08
30
+
31
+ ### 新功能
32
+ - **Storage 目录树与扩展操作**:`storage.*` 新增 15 个存储操作方法(含 `create_folder`/`rename_folder`/`move_object`/`batch_delete` 等),加入签名集合和非幂等集合(四语言+服务端对齐)
33
+ - **group.resources 树形资源系统**:新增 11 个资源管理方法,加入签名和非幂等集合;支持跨域访问票据(四语言+服务端对齐)
34
+ - **group.changed 事件保序去重**:新增 `handleGroupChangedEventSeq` 支持 event_seq 追踪和有序消息队列,空洞先到时缓存补洞;新增 `_isEventSignatureVerified()` 区分 pending 不标记已验签(四语言对齐)
35
+ - **V2 E2EE 注册并发保护**:SPK 注册改用 `_registeringPromise` 缓存并发请求,多并发调用时等待同一 Promise(四语言对齐)
36
+
37
+ ### 修复
38
+ - **IndexedDB 事务原子性**:`addSPK` 改用单事务处理原始记录和别名,避免独立事务导致一致性问题
39
+ - **SPK 销毁顺序**:`_doAutoDestroy` 中先删设备级密钥再删存储级密钥
40
+ - **sdk_version 拼写**:`sdk_vesion` → `sdk_version`
41
+ - **V2 并发冲突检测**:检查对端操作的 `inflightSet`,防止 SPK 注册与轮换并发冲突
42
+ - **storage/group.resources 签名和幂等性补全**:补全写操作方法进入签名集合与非幂等集合(四语言对齐)
43
+
44
+ ### 优化
45
+ - **群事件处理流程重构**:`group.changed` 事件分发逻辑从 `client.ts` 迁移到 `delivery.ts`,支持按 event_seq 保序发布;新增 `publishOrderedQueueItem()` / `publishOrderedGroupChanged()`
46
+ - **群组解散清理**:解散事件处理改为异步 `drainOrderedMessages`,保证 seq 追踪一致性
47
+ - **群事件自动 ack**:`handleGroupChangedEventSeq` 无需补洞时直接 ack event cursor
48
+ - **错误日志补充**:V2Session SPK 销毁异常捕获并输出日志
49
+
50
+ ### 测试
51
+ - 新增事件验签状态单元测试(pending 状态不标记已验签)
52
+ - 新增消息乱序补洞保序测试(高序号 push 先到时 SDK 内部消费和应用层发布的保序去重)
53
+ - 新增签名方法覆盖测试(storage 写操作和有副作用读操作进入非幂等集合)
54
+ - 新增 RPC 管道非幂等超时测试(storage/resources 方法按非幂等长超时 35s 发送)
55
+ - 修复 `handleGroupChangedEventSeq` 单元测试 async/await 标记和 ack cursor 验证
56
+
57
+ ---
58
+
9
59
  ## 0.4.10 — 2026-06-06
10
60
 
11
61
  ### 新功能
@@ -56,14 +56,16 @@ SDK 优先使用 prekey_ecdh_v2,并默认要求前向保密:
56
56
 
57
57
  `protected_headers` 会随 E2EE 信封发送,接收端可以读取,因此它提供完整性保护,不提供机密性保护。不要把访问令牌、私钥、隐私正文或其他只允许端到端可见的内容放入 `protected_headers`;这类内容应放进加密的 `payload`。
58
58
 
59
- 发送方可以在以下 SDK 调用中传入 `protected_headers`,各 SDK 也兼容别名 `headers`:
59
+ 推荐通过 SDK 实例级 setter 设置稳定元数据,例如 `client.set_protected_headers(...)` / `client.setProtectedHeaders(...)` / `client.SetProtectedHeaders(...)`。发送方也可以在以下 SDK 调用中传入顶层 `protected_headers` 作为单次发送的高级覆盖;`headers` 仅作为兼容旧调用的别名,不推荐新代码使用:
60
60
 
61
61
  - `message.send`
62
62
  - `message.thought.put`
63
63
  - `group.send`
64
64
  - `group.thought.put`
65
65
 
66
- `payload_type` 不需要应用层传入。SDK 会读取加密前 `payload.type`,自动写入 `protected_headers.payload_type`,接收端解密后会校验它与明文 `payload.type` 一致。
66
+ `payload_type` 不需要应用层传入。SDK 会读取加密前 `payload.type`,自动写入 `protected_headers.payload_type`,接收端解密后会校验它与明文 `payload.type` 一致。
67
+
68
+ `protected_headers` / `headers` 是 send/thought 参数的顶层字段,不放入单独的 `envelope` 入参对象,也不属于业务 `payload`。裸 WebSocket 客户端若自行发送已加密信封,需要把 protected headers 放在自构造的 E2EE 信封内并自行完成 `_auth`,服务端不会替裸 RPC 调用生成或校验明文侧的 protected headers。
67
69
 
68
70
  示例:
69
71
 
@@ -409,8 +409,8 @@ sub.unsubscribe()
409
409
  | 领域 | 手册 | 关键方法 |
410
410
  |------|------|----------|
411
411
  | 消息 | [09-message-rpc-manual.md](09-message-rpc-manual.md) | `message.send` / `message.pull` / `message.ack` / `message.thought.*` |
412
- | 群组 | [09-group-rpc-manual.md](09-group-rpc-manual.md) | `group.create` / `group.invite` / `group.send` / `group.v2.*` |
413
- | 存储 | [09-storage-rpc-manual.md](09-storage-rpc-manual.md) | `storage.upload` / `storage.download` / `storage.share` |
412
+ | 群组 | [09-group-rpc-manual.md](09-group-rpc-manual.md) | `group.create` / `group.send` / `group.v2.*` / `group.resources.*` |
413
+ | 存储 | [09-storage-rpc-manual.md](09-storage-rpc-manual.md) | `storage.put_object` / `storage.create_upload_session` / `storage.create_folder` / `storage.create_share_link` |
414
414
  | 元信息 | [09-meta-rpc-manual.md](09-meta-rpc-manual.md) | `meta.ping` / `meta.status` / `meta.trust_roots` |
415
415
  | Stream | [09-stream-rpc-manual.md](09-stream-rpc-manual.md) | `stream.create` / `stream.close` / `stream.list_active` |
416
416
  | Service Proxy | [09-proxy-rpc-manual.md](09-proxy-rpc-manual.md) | `proxy.register_services` / `proxy.unregister_services` / `proxy.list_services` |
@@ -13,13 +13,13 @@ AUNError
13
13
  │ └── IdentityConflictError
14
14
  ├── PermissionError
15
15
  ├── ValidationError
16
+ │ └── ClientSignatureError
16
17
  ├── NotFoundError
17
18
  ├── RateLimitError
18
19
  ├── VersionConflictError
19
20
  ├── StateError
20
21
  ├── SerializationError
21
22
  ├── SessionError
22
- ├── ClientSignatureError
23
23
  ├── GroupError
24
24
  └── E2EEError
25
25
  ```
@@ -55,17 +55,20 @@ except AUNError as e:
55
55
  | 4040 / 404 | 资源不存在 | `NotFoundError` |
56
56
  | 4290 / 429 | 请求限流 | `RateLimitError` |
57
57
  | -32001 / -32003 | 认证失败 | `AuthError` |
58
- | -32004 | 权限不足 | `PermissionError` |
58
+ | -32004 | RPC handler 超时(消息以 `"rpc handler timeout"` 开头) | `TimeoutError` |
59
+ | -32004 | 权限不足(其他情况) | `PermissionError` |
59
60
  | -32008 | 资源不存在 | `NotFoundError` |
60
61
  | -32009 | 版本冲突 | `VersionConflictError` |
61
62
  | -32010 / -32011 / -32013 | 会话错误 | `SessionError` |
63
+ | -32051 | 客户端签名验证失败 | `ClientSignatureError`(继承自 `ValidationError`) |
62
64
  | -32029 | 请求限流 | `RateLimitError` |
63
65
  | -32600 / -32601 / -32602 | JSON-RPC 参数错误 | `ValidationError` |
64
66
  | -32040 ~ -32044 | E2EE 群组错误 | `E2EEError` 子类 |
65
67
  | 4090 | 身份冲突 | `IdentityConflictError` |
66
68
  | -32050 | 证书已吊销 | `CertificateRevokedError` |
67
- | -32051 | 客户端签名验证失败 | `ClientSignatureError` |
68
- | -33001 ~ -33009 | 群组错误 | `GroupError` 子类 |
69
+ | -33001 | 群组不存在 | `GroupNotFoundError` |
70
+ | -33002 / -33003 | 群组状态错误 | `GroupStateError` |
71
+ | -33004 ~ -33009 | 其他群组错误 | `GroupError` 子类 |
69
72
 
70
73
  ---
71
74
 
@@ -121,6 +121,16 @@
121
121
  | 方法 | 说明 |
122
122
  |------|------|
123
123
  | [group.resources.put](#groupresourcesput) | 分享资源 |
124
+ | [group.resources.create_folder](#groupresourcescreate_folder) | 创建资源目录 |
125
+ | [group.resources.list_children](#groupresourceslist_children) | 列出目录子节点 |
126
+ | [group.resources.rename](#groupresourcesrename) | 重命名资源节点 |
127
+ | [group.resources.move](#groupresourcesmove) | 移动资源节点 |
128
+ | [group.resources.mount_object](#groupresourcesmount_object) | 挂载 storage 对象为资源 |
129
+ | [group.resources.request_mount_object](#groupresourcesrequest_mount_object) | 申请挂载 storage 对象 |
130
+ | [group.resources.unmount](#groupresourcesunmount) | 取消挂载资源 |
131
+ | [group.resources.resolve_path](#groupresourcesresolve_path) | 按路径解析资源 |
132
+ | [group.resources.list_refs_by_storage](#groupresourceslist_refs_by_storage) | 按 storage 引用反查资源 |
133
+ | [group.resources.cleanup_by_storage_ref](#groupresourcescleanup_by_storage_ref) | 清理失效 storage 引用 |
124
134
  | [group.resources.get](#groupresourcesget) | 查看资源 |
125
135
  | [group.resources.list](#groupresourceslist) | 列出资源 |
126
136
  | [group.resources.update](#groupresourcesupdate) | 更新资源元数据 |
@@ -979,7 +989,7 @@ await client.call("group.set_settings", {
979
989
  | `payload` | object | 否 | 消息内容 |
980
990
  | `type` | string | 否 | 信封/封装类型,普通业务消息无需填写;SDK 加密群消息时自动使用 `e2ee.group_encrypted` |
981
991
  | `attachments` | array | 否 | 兼容旧接口的顶层附件元数据;推荐把业务附件放入 `payload.attachments` |
982
- | `protected_headers` / `headers` | object | 否 | SDK 加密前读取的 E2EE 信封元数据,类似 HTTP headers;服务端不解释,接收端验 `_auth` 后在 `e2ee.protected_headers` 暴露 |
992
+ | `protected_headers` / `headers` | object | 否 | SDK 加密前读取的 E2EE 信封元数据,类似 HTTP headers;推荐使用 `protected_headers`,`headers` 仅作为兼容别名;服务端不解释,接收端验 `_auth` 后在 `e2ee.protected_headers` 暴露 |
983
993
 
984
994
  ### Payload 参考约定
985
995
 
@@ -1039,7 +1049,7 @@ SDK 调用时必须走群组 E2EE。应用层传入明文 `payload`,SDK 会加
1039
1049
  | `encrypt` | boolean | 否 | SDK 侧固定按 `true` 处理;`false` 会被拒绝 |
1040
1050
  | `thought_id` | string | 否 | thought item ID;不传时 SDK 生成 `gt-*` |
1041
1051
  | `timestamp` | integer | 否 | 客户端时间戳;不传时 SDK 生成 |
1042
- | `protected_headers` / `headers` | object | 否 | SDK 加密前读取的 E2EE 信封元数据;`context` 会被 SDK 复制进信封并单独验 `_auth` |
1052
+ | `protected_headers` / `headers` | object | 否 | SDK 加密前读取的 E2EE 信封元数据;推荐使用 `protected_headers`,`headers` 仅作为兼容别名;`context` 会被 SDK 复制进信封并单独验 `_auth` |
1043
1053
 
1044
1054
  **SDK 调用示例**:
1045
1055
 
@@ -1358,6 +1368,122 @@ result = await client.call("group.thought.get", {
1358
1368
 
1359
1369
  > `created` 为 `true` 表示新建,`false` 表示更新已有资源。
1360
1370
 
1371
+ ### group.resources.create_folder
1372
+
1373
+ 创建群资源目录。需要 **member 及以上**权限。
1374
+
1375
+ **参数**:
1376
+
1377
+ | 参数 | 类型 | 必填 | 说明 |
1378
+ |------|------|------|------|
1379
+ | `group_id` | string | 是 | 群组 ID |
1380
+ | `path` / `resource_path` | string | 否 | 完整目录路径 |
1381
+ | `name` | string | 否 | 目录名;未提供完整路径时使用 |
1382
+ | `parent_resource_id` / `parent_path` | string | 否 | 父目录 |
1383
+ | `title` | string | 否 | 显示标题,默认目录名 |
1384
+ | `metadata` | object | 否 | 自定义元数据 |
1385
+ | `visibility` | string | 否 | `"members_only"` / `"public"` |
1386
+ | `tags` | array | 否 | 标签 |
1387
+ | `mkdirs` | boolean | 否 | 是否递归创建父目录 |
1388
+ | `sort_order` | integer | 否 | 排序值 |
1389
+
1390
+ **响应**:`{ "group_id": "...", "resource": { ... }, "created": true }`。
1391
+
1392
+ ### group.resources.list_children
1393
+
1394
+ 列出某个资源目录下的直接子节点。
1395
+
1396
+ **参数**:
1397
+
1398
+ | 参数 | 类型 | 必填 | 说明 |
1399
+ |------|------|------|------|
1400
+ | `group_id` | string | 是 | 群组 ID |
1401
+ | `resource_id` / `path` / `resource_path` | string | 否 | 父目录;不传表示根目录 |
1402
+ | `type` / `resource_type` | string | 否 | `"folder"` / `"file"` / `"link"` |
1403
+ | `include_status` | boolean | 否 | 是否附带 storage 状态 |
1404
+ | `page` / `offset` | integer | 否 | 分页位置 |
1405
+ | `size` / `limit` | integer | 否 | 每页数量 |
1406
+ | `sort_by` | string | 否 | 排序字段,默认 `sort_order` |
1407
+ | `order` | string | 否 | `"asc"` / `"desc"` |
1408
+
1409
+ **响应**:`group_id`、`resource_id`、`path`、`items`、`total`、`count`、`page`、`size`、`offset`。
1410
+
1411
+ ### group.resources.rename
1412
+
1413
+ 重命名资源节点。需要资源创建者、storage owner、owner 或 admin 权限。
1414
+
1415
+ **参数**:`group_id`,资源选择器(`resource_id` / `resource_path` / `path`),`new_name`;可选 `title`、`expected_version`。
1416
+
1417
+ **响应**:更新后的 `resource`。
1418
+
1419
+ ### group.resources.move
1420
+
1421
+ 移动资源节点。目录不能移动到自身或自身子目录。
1422
+
1423
+ **参数**:`group_id`,资源选择器,目标父目录(`dst_parent_resource_id` / `dst_parent_path`),可选 `new_name` / `dst_name`、`expected_version`。
1424
+
1425
+ **响应**:更新后的 `resource`。
1426
+
1427
+ ### group.resources.mount_object
1428
+
1429
+ 将 `storage.*` 对象挂载为群资源。需要 **owner/admin** 权限。
1430
+
1431
+ **参数**:
1432
+
1433
+ | 参数 | 类型 | 必填 | 说明 |
1434
+ |------|------|------|------|
1435
+ | `group_id` | string | 是 | 群组 ID |
1436
+ | `storage_ref` | object | 是 | storage 引用,通常包含 `owner_aid`、`bucket`、`object_id` 或 `object_key` |
1437
+ | `path` / `resource_path` | string | 否 | 资源路径;不传时用 storage 文件名 |
1438
+ | `title` | string | 否 | 显示标题 |
1439
+ | `metadata` | object | 否 | 自定义元数据 |
1440
+ | `visibility` | string | 否 | `"members_only"` / `"public"` |
1441
+ | `tags` | array | 否 | 标签 |
1442
+ | `mkdirs` | boolean | 否 | 是否递归创建父目录,默认 `true` |
1443
+ | `conflict_policy` | string | 否 | `"reject"` / `"replace"` / `"keep_both"` |
1444
+
1445
+ **响应**:`{ "group_id": "...", "resource": { ... }, "created": true }`。
1446
+
1447
+ ### group.resources.request_mount_object
1448
+
1449
+ 成员申请挂载自己的 storage 对象,进入待审批队列。语义等同 `group.resources.request_add`,但输入按挂载对象组织。
1450
+
1451
+ **参数**:同 `group.resources.mount_object`,但不需要 owner/admin 权限。
1452
+
1453
+ **响应**:`{ "group_id": "...", "request": { ... } }`。
1454
+
1455
+ ### group.resources.unmount
1456
+
1457
+ 取消挂载资源,等价于非递归 `group.resources.delete`。
1458
+
1459
+ **参数**:`group_id`,资源选择器(`resource_id` / `resource_path` / `path`)。
1460
+
1461
+ **响应**:删除结果。
1462
+
1463
+ ### group.resources.resolve_path
1464
+
1465
+ 按路径解析资源节点。
1466
+
1467
+ **参数**:`group_id`、`path` / `resource_path`;可选 `expected_type`。
1468
+
1469
+ **响应**:`resource_id`、`resource_type`、`resource_path`、`path`、`status`、`resource`。
1470
+
1471
+ ### group.resources.list_refs_by_storage
1472
+
1473
+ 按 storage 引用反查群资源。传 `group_id` 时要求调用者是该群成员;不传 `group_id` 时要求调用者是 `owner_aid`。
1474
+
1475
+ **参数**:`owner_aid` 必填,`object_id` 或 `object_key` 至少一个;可选 `bucket`、`group_id`、`include_missing`、`offset`、`limit` / `size`。
1476
+
1477
+ **响应**:`items`、`total`、`count`、`limit`、`offset`,并回显 storage 选择器字段。
1478
+
1479
+ ### group.resources.cleanup_by_storage_ref
1480
+
1481
+ 清理指向已删除或失效 storage 对象的资源引用。传 `group_id` 时要求 owner/admin 或 storage owner;不传 `group_id` 时要求 storage owner。
1482
+
1483
+ **参数**:`owner_aid` 必填,`object_id` 或 `object_key` 至少一个;可选 `bucket`、`group_id`、`mode`。
1484
+
1485
+ **响应**:`affected_count` 和被影响的资源 `items`。
1486
+
1361
1487
  ### group.resources.get
1362
1488
 
1363
1489
  查看资源详情。
@@ -1485,7 +1611,7 @@ result = await client.call("group.thought.get", {
1485
1611
 
1486
1612
  ### group.resources.direct_add
1487
1613
 
1488
- Owner 直接添加资源(无需审批)。需要 **owner** 权限。
1614
+ Owner/Admin 直接添加资源(无需审批)。需要 **owner/admin** 权限。
1489
1615
 
1490
1616
  **参数**:同 `group.resources.put`(`resource_type` 不能是 `"folder"`)。
1491
1617
 
@@ -1831,22 +1957,44 @@ CAS 轮换群组 E2EE Epoch。需要 **admin 及以上**权限。
1831
1957
 
1832
1958
  **Payload**:
1833
1959
 
1834
- ```json
1835
- {
1836
- "module_id": "group",
1837
- "action": "member_added",
1838
- "group_id": "g-abc123.agentid.pub"
1839
- }
1840
- ```
1841
-
1842
- | 字段 | 类型 | 说明 |
1843
- |------|------|------|
1844
- | `module_id` | string | 固定 `"group"` |
1845
- | `action` | string | 变更类型(见下表) |
1846
- | `group_id` | string | 群组 ID |
1960
+ ```json
1961
+ {
1962
+ "envelope": {
1963
+ "module_id": "group",
1964
+ "action": "member_added",
1965
+ "group_id": "g-abc123.agentid.pub",
1966
+ "event_seq": 42
1967
+ },
1968
+ "module_id": "group",
1969
+ "action": "member_added",
1970
+ "group_id": "g-abc123.agentid.pub",
1971
+ "event_seq": 42
1972
+ }
1973
+ ```
1974
+
1975
+ SDK 交付给应用层的群事件信封字段统一放在 `envelope`。0.4.x 兼容期仍保留顶层 `module_id` / `action` / `group_id` / `event_seq` 等别名,下一个大版本 0.5.* 将移除这些顶层别名,请通过 `ev["envelope"]["action"]` 等路径访问。
1976
+
1977
+ | 字段 | 类型 | 说明 |
1978
+ |------|------|------|
1979
+ | `envelope` | object | 群事件信封,包含 `module_id`、`action`、`group_id`、`event_seq`、`event_type`、`actor_aid`、`created_at`、`device_id`、`slot_id` 等存在的字段 |
1980
+ | `module_id` | string | 固定 `"group"` |
1981
+ | `action` | string | 变更类型(见下表) |
1982
+ | `group_id` | string | 群组 ID |
1983
+ | `event_seq` | integer | 可选,服务端分配的单调递增序号,用于 SDK 内部保序去重 |
1847
1984
  | `request_id` | string | 可选,仅资源审批相关 action |
1848
1985
  | `resource_path` | string | 可选,仅资源相关 action |
1849
1986
 
1987
+ **保序去重(SDK 内部行为)**:
1988
+
1989
+ 服务端为每条 `group.changed` 事件分配 `event_seq`(按群 `group_event:{group_id}` 命名空间单调递增)。SDK 收到事件后:
1990
+
1991
+ 1. **去重**:`event_seq` ≤ 已连续消费序号,或已处理过该序号,则丢弃
1992
+ 2. **保序**:事件入有序队列,按序号连续后才发布给应用层
1993
+ 3. **补洞**:检测到序号空洞时,自动调用 `group.pull_events` 拉取缺失事件补齐
1994
+ 4. **ack**:连续段推进后自动发送 `group.ack_events`(namespace `group_event:{group_id}`)
1995
+
1996
+ 不携带 `event_seq` 的旧格式事件直接发布,不参与保序(兼容旧服务端)。
1997
+
1850
1998
  **action 取值**:
1851
1999
 
1852
2000
  | action | 说明 |
@@ -1926,37 +2074,73 @@ SDK 收到后自动解密 `payload`,解密后的明文消息直接交付用户
1926
2074
  }
1927
2075
  ```
1928
2076
 
1929
- SDK 收到后自动调用 `group.pull` 拉取最新消息并逐条解密后交付用户回调。
1930
-
1931
- ### event/group.message_recalled
2077
+ SDK 收到后自动调用 `group.pull` 拉取最新消息并逐条解密后交付用户回调。
2078
+
2079
+ **SDK 应用层回调形态**:
2080
+
2081
+ ```json
2082
+ {
2083
+ "envelope": {
2084
+ "group_id": "g-abc123.agentid.pub",
2085
+ "seq": 42,
2086
+ "message_id": "uuid",
2087
+ "sender_aid": "alice.agentid.pub",
2088
+ "message_type": "group.message",
2089
+ "dispatch_mode": "broadcast"
2090
+ },
2091
+ "group_id": "g-abc123.agentid.pub",
2092
+ "seq": 42,
2093
+ "message_id": "uuid",
2094
+ "sender_aid": "alice.agentid.pub",
2095
+ "message_type": "group.message",
2096
+ "dispatch_mode": "broadcast",
2097
+ "payload": {"type": "text", "text": "Hello"}
2098
+ }
2099
+ ```
2100
+
2101
+ SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;群消息信封字段统一放在 `envelope`。0.4.x 兼容期仍保留顶层 `group_id` / `seq` / `message_id` / `sender_aid` 等别名,下一个大版本 0.5.* 将移除这些顶层别名,请通过 `msg["envelope"]["message_id"]` 等路径访问。
2102
+
2103
+ ### event/group.message_recalled
1932
2104
 
1933
2105
  群消息撤回后推送给所有在线成员(与 pull 双 tombstone 兜底互补)。在线 push 是实时通道,双 tombstone 是离线 / 未读 / push 丢失时的可靠性兜底;两者最终一致,SDK 去重保证应用层只感知一次。
1934
2106
 
1935
2107
  **Payload**:
1936
2108
 
1937
- ```json
1938
- {
1939
- "module_id": "group",
1940
- "group_id": "g-abc123.agentid.pub",
1941
- "seq": 43,
1942
- "message_id": "grm-uuid",
1943
- "message_ids": ["gm-aaa"],
1944
- "target_message_seqs": [42],
1945
- "sender_aid": "alice.agentid.pub",
2109
+ ```json
2110
+ {
2111
+ "envelope": {
2112
+ "module_id": "group",
2113
+ "group_id": "g-abc123.agentid.pub",
2114
+ "seq": 43,
2115
+ "message_id": "grm-uuid",
2116
+ "sender_aid": "alice.agentid.pub"
2117
+ },
2118
+ "module_id": "group",
2119
+ "group_id": "g-abc123.agentid.pub",
2120
+ "seq": 43,
2121
+ "message_id": "grm-uuid",
2122
+ "tombstone_message_id": "grm-uuid",
2123
+ "message_ids": ["gm-aaa"],
2124
+ "target_message_seqs": [42],
2125
+ "sender_aid": "alice.agentid.pub",
1946
2126
  "recalled_by": "alice.agentid.pub",
1947
2127
  "recalled_at": 1234567890000,
1948
2128
  "reason": "",
1949
2129
  "member_aids": ["bob.agentid.pub"]
1950
- }
1951
- ```
1952
-
1953
- | 字段 | 类型 | 说明 |
1954
- |------|------|------|
1955
- | `module_id` | string | 固定 `"group"` |
1956
- | `group_id` | string | 群组 ID |
1957
- | `seq` | integer | 撤回通知 tombstone 的**新群消息 seq** |
1958
- | `message_id` | string | 撤回通知 tombstone 自己的 message_id |
1959
- | `message_ids` | string[] | 被撤回的**原消息 ID 列表** |
2130
+ }
2131
+ ```
2132
+
2133
+ SDK 交付给应用层的撤回事件同样带 `envelope`。`envelope` 表示当前交付的撤回 tombstone / 通知自身信封,不是被撤回原消息的信封;业务侧被撤回的原消息列表继续使用 `message_ids` / `target_message_seqs`。0.4.x 兼容期仍保留顶层 `group_id` / `seq` / `message_id` / `sender_aid` 等别名,下一个大版本 0.5.* 将移除这些顶层别名。
2134
+
2135
+ | 字段 | 类型 | 说明 |
2136
+ |------|------|------|
2137
+ | `envelope` | object | 撤回 tombstone / 通知自身信封,包含 `module_id`、`group_id`、`seq`、`message_id`、`sender_aid`、`device_id`、`slot_id` 等存在的字段 |
2138
+ | `module_id` | string | 固定 `"group"` |
2139
+ | `group_id` | string | 群组 ID |
2140
+ | `seq` | integer | 当前交付的撤回 tombstone / 通知 seq;在线 push 为 notice_seq,原 seq 占位 tombstone 为原消息 seq |
2141
+ | `message_id` | string | 当前交付的撤回 tombstone / 通知自己的 message_id |
2142
+ | `tombstone_message_id` | string | 兼容别名,等同于撤回 tombstone / 通知自身的 `message_id` |
2143
+ | `message_ids` | string[] | 被撤回的**原消息 ID 列表** |
1960
2144
  | `target_message_seqs` | integer[] | 被撤回的原消息 seq 列表 |
1961
2145
  | `sender_aid` | string | 原消息发送方 |
1962
2146
  | `recalled_by` | string | 撤回操作者 |
@@ -78,7 +78,7 @@ P2P `message.*` 的最终投递语义由连接阶段声明的 `delivery_mode`
78
78
  | `encrypted` | boolean | 否 | `false` | 底层 RPC 的 E2EE 标记。Python SDK 便捷层通常使用 `encrypt` 入参并由 SDK 自动填充此字段 |
79
79
  | `message_id` | string | 否 | — | 幂等键(客户端提供或服务端生成 UUID) |
80
80
  | `timestamp` | integer | 否 | — | 客户端时间戳(毫秒)。**服务端忽略此字段,始终使用服务端时间** |
81
- | `protected_headers` / `headers` | object | 否 | — | SDK 加密前读取的 E2EE 信封元数据,类似 HTTP headers;服务端不解释,接收端验 `_auth` 后在 `e2ee.protected_headers` 暴露 |
81
+ | `protected_headers` / `headers` | object | 否 | — | SDK 加密前读取的 E2EE 信封元数据,类似 HTTP headers;推荐使用 `protected_headers`,`headers` 仅作为兼容别名;服务端不解释,接收端验 `_auth` 后在 `e2ee.protected_headers` 暴露 |
82
82
 
83
83
  > 连接级 `delivery_mode` 在 `auth.connect` 阶段声明,结构见 `02-WebSocket协议.md`。Python SDK 的 P2P 消息发送会沿用当前连接的 `delivery_mode`,应用层发送时无需重复指定。
84
84
  > `protected_headers` 只在 SDK 加密路径生效;裸 RPC 发送明文或已加密信封时,调用方需自行遵守 [05-E2EE加密通信](05-E2EE加密通信.md#protectedheaders-与可验证上下文) 的格式和校验规则。
@@ -156,7 +156,7 @@ SDK 调用时必须走 P2P E2EE。应用层传入明文 `payload`,SDK 会加
156
156
  | `encrypt` | boolean | 否 | SDK 侧固定按 `true` 处理;`false` 会被拒绝 |
157
157
  | `thought_id` | string | 否 | thought item ID;不传时 SDK 生成 `mt-*` |
158
158
  | `timestamp` | integer | 否 | 客户端时间戳;不传时 SDK 生成 |
159
- | `protected_headers` / `headers` | object | 否 | SDK 加密前读取的 E2EE 信封元数据;`context` 会被 SDK 复制进信封并单独验 `_auth` |
159
+ | `protected_headers` / `headers` | object | 否 | SDK 加密前读取的 E2EE 信封元数据;推荐使用 `protected_headers`,`headers` 仅作为兼容别名;`context` 会被 SDK 复制进信封并单独验 `_auth` |
160
160
 
161
161
  ### SDK 调用示例
162
162
 
@@ -510,20 +510,30 @@ result = await client.call("message.ack", {"seq": 150})
510
510
 
511
511
  ### Payload
512
512
 
513
- ```json
514
- {
515
- "message_id": "uuid-1",
516
- "from": "alice.agentid.pub",
517
- "to": "bob.agentid.pub",
513
+ ```json
514
+ {
515
+ "envelope": {
516
+ "message_id": "uuid-1",
517
+ "from": "alice.agentid.pub",
518
+ "to": "bob.agentid.pub",
519
+ "seq": 42,
520
+ "timestamp": 1234567890000,
521
+ "encrypted": false
522
+ },
523
+ "message_id": "uuid-1",
524
+ "from": "alice.agentid.pub",
525
+ "to": "bob.agentid.pub",
518
526
  "seq": 42,
519
527
  "timestamp": 1234567890000,
520
528
  "payload": {"type": "text", "text": "Hello!"},
521
529
  "delivery_mode": "queue",
522
530
  "encrypted": false
523
- }
524
- ```
525
-
526
- ### 订阅
531
+ }
532
+ ```
533
+
534
+ SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;信封字段统一放在 `envelope`。0.4.x 兼容期仍保留顶层 `message_id` / `from` / `to` / `seq` / `timestamp` 等别名,下一个大版本 0.5.* 将移除这些顶层别名,请通过 `msg["envelope"]["seq"]` 等路径访问。
535
+
536
+ ### 订阅
527
537
 
528
538
  ```python
529
539
  client.on("message.received", lambda msg: print(msg["payload"]))
@@ -537,21 +547,35 @@ client.on("message.received", lambda msg: print(msg["payload"]))
537
547
 
538
548
  ### Payload
539
549
 
540
- ```json
541
- {
542
- "from": "alice.agentid.pub",
543
- "to": "bob.agentid.pub",
544
- "message_ids": ["uuid-1", "uuid-2"],
545
- "timestamp": 1234567890000
546
- }
547
- ```
548
-
549
- | 字段 | 类型 | 说明 |
550
- |------|------|------|
551
- | `from` | string | 发送方(撤回者)AID |
552
- | `to` | string | 接收方 AID |
553
- | `message_ids` | array | 被撤回的消息 ID 列表 |
554
- | `timestamp` | integer | 服务端时间戳(毫秒) |
550
+ ```json
551
+ {
552
+ "envelope": {
553
+ "message_id": "recall-uuid",
554
+ "from": "alice.agentid.pub",
555
+ "to": "bob.agentid.pub",
556
+ "seq": 43,
557
+ "timestamp": 1234567890000
558
+ },
559
+ "message_id": "recall-uuid",
560
+ "tombstone_message_id": "recall-uuid",
561
+ "from": "alice.agentid.pub",
562
+ "to": "bob.agentid.pub",
563
+ "message_ids": ["uuid-1", "uuid-2"],
564
+ "timestamp": 1234567890000
565
+ }
566
+ ```
567
+
568
+ SDK 交付给应用层的撤回事件同样带 `envelope`。`envelope` 表示撤回 tombstone / 通知自身的信封,不是被撤回原消息的信封;被撤回的原消息继续通过 `message_ids` 表达。0.4.x 兼容期仍保留顶层 `message_id` / `from` / `to` / `seq` / `timestamp` 等别名,下一个大版本 0.5.* 将移除这些顶层别名。
569
+
570
+ | 字段 | 类型 | 说明 |
571
+ |------|------|------|
572
+ | `envelope` | object | 撤回 tombstone / 通知自身信封,包含 `message_id`、`from`、`to`、`seq`、`timestamp`、`device_id`、`slot_id` 等存在的字段 |
573
+ | `message_id` | string | 撤回 tombstone / 通知自身的 message_id |
574
+ | `tombstone_message_id` | string | 兼容别名,等同于撤回 tombstone / 通知自身的 `message_id` |
575
+ | `from` | string | 发送方(撤回者)AID |
576
+ | `to` | string | 接收方 AID |
577
+ | `message_ids` | array | 被撤回的消息 ID 列表 |
578
+ | `timestamp` | integer | 服务端时间戳(毫秒) |
555
579
 
556
580
  ### 订阅
557
581
 
@@ -40,7 +40,7 @@
40
40
  | `to` | `message.send.params` | P2P 接收方 AID |
41
41
  | `group_id` | `group.send.params` 和群消息信封 | 群组 ID |
42
42
  | `context.type + context.id` | `message.thought.put/get.params` 和 `group.thought.put/get.params` | 思考内容 selector;必填,不要只放在 payload 内 |
43
- | `protected_headers` / `headers` | `message.send` / `message.thought.put` / `group.send` / `group.thought.put` 参数 | E2EE 信封元数据,类似 HTTP headersSDK 验 `_auth` 后在 `e2ee.protected_headers` 暴露 |
43
+ | `protected_headers` / `headers` | `message.send` / `message.thought.put` / `group.send` / `group.thought.put` 参数 | E2EE 信封元数据,类似 HTTP headers;推荐 `protected_headers`,`headers` 仅为兼容别名;SDK 验 `_auth` 后在 `e2ee.protected_headers` 暴露 |
44
44
  | `from` / `sender_aid` | 服务端生成的消息信封 | 发送方身份 |
45
45
  | `message_id` / `seq` / `timestamp` / `created_at` | 服务端生成或发送参数 | 当前消息 ID、序号和服务端时间 |
46
46
  | `encrypted` / `delivery_mode` | 发送参数或连接上下文 | 加密和 P2P 投递语义 |