@agentunion/fastaun-browser 0.5.20 → 0.5.21

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 (130) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/_packed_docs/CHANGELOG.md +47 -0
  3. package/_packed_docs/INDEX.md +453 -440
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +164 -161
  5. package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/344/270/216/346/215/242owner/347/274/226/346/216/222/346/226/271/346/241/210.md +530 -0
  6. package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/345/256/214/346/225/264/350/256/276/350/256/241/344/270/216/345/256/236/346/226/275/350/256/241/345/210/222.md +685 -0
  7. package/_packed_docs/agent.md/working/agent.md-schema/344/270/216/347/255/276/345/217/221/351/252/214/347/255/276/346/265/201/347/250/213/346/276/204/346/270/205.md +349 -0
  8. package/_packed_docs/agent.md/working//346/234/254/345/234/260Docker/346/265/213/350/257/225Bug/346/270/205/345/215/225.md +40 -0
  9. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1433 -1392
  10. package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
  11. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +2 -0
  12. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +666 -658
  13. package/_packed_docs/sdk/0.5.21-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +368 -0
  14. package/_packed_docs/sdk/0.5.21-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +350 -0
  15. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
  16. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
  17. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
  18. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +4 -1
  19. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +42 -39
  20. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +147 -14
  21. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +48 -47
  22. package/_packed_docs/sdk/09-group-rpc-manual.md +5 -2
  23. package/_packed_docs/sdk/09-message-rpc-manual.md +250 -160
  24. package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +71 -0
  25. package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +88 -0
  26. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +134 -130
  27. package/_packed_docs/sdk/INDEX.md +239 -221
  28. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +930 -922
  29. package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +106 -43
  30. package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +180 -9
  31. package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -708
  32. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +480 -476
  33. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +1 -1
  34. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +2 -2
  35. package/dist/agent-md-schema.d.ts +1 -0
  36. package/dist/agent-md-schema.d.ts.map +1 -1
  37. package/dist/agent-md-schema.js +5 -4
  38. package/dist/agent-md-schema.js.map +1 -1
  39. package/dist/agent-md.d.ts +25 -1
  40. package/dist/agent-md.d.ts.map +1 -1
  41. package/dist/agent-md.js +148 -71
  42. package/dist/agent-md.js.map +1 -1
  43. package/dist/aid-store.d.ts +13 -0
  44. package/dist/aid-store.d.ts.map +1 -1
  45. package/dist/aid-store.js +55 -8
  46. package/dist/aid-store.js.map +1 -1
  47. package/dist/aid.d.ts.map +1 -1
  48. package/dist/aid.js +2 -1
  49. package/dist/aid.js.map +1 -1
  50. package/dist/auth.d.ts.map +1 -1
  51. package/dist/auth.js +4 -0
  52. package/dist/auth.js.map +1 -1
  53. package/dist/bundle.js +4981 -1219
  54. package/dist/cert-utils.d.ts +1 -0
  55. package/dist/cert-utils.d.ts.map +1 -1
  56. package/dist/cert-utils.js +21 -0
  57. package/dist/cert-utils.js.map +1 -1
  58. package/dist/client/delivery.d.ts +101 -16
  59. package/dist/client/delivery.d.ts.map +1 -1
  60. package/dist/client/delivery.js +1172 -160
  61. package/dist/client/delivery.js.map +1 -1
  62. package/dist/client/rpc-pipeline.d.ts +4 -1
  63. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  64. package/dist/client/rpc-pipeline.js +122 -48
  65. package/dist/client/rpc-pipeline.js.map +1 -1
  66. package/dist/client/v2-e2ee.d.ts +22 -3
  67. package/dist/client/v2-e2ee.d.ts.map +1 -1
  68. package/dist/client/v2-e2ee.js +1258 -649
  69. package/dist/client/v2-e2ee.js.map +1 -1
  70. package/dist/client.d.ts +23 -2
  71. package/dist/client.d.ts.map +1 -1
  72. package/dist/client.js +391 -87
  73. package/dist/client.js.map +1 -1
  74. package/dist/config.d.ts +4 -0
  75. package/dist/config.d.ts.map +1 -1
  76. package/dist/config.js +1 -0
  77. package/dist/config.js.map +1 -1
  78. package/dist/events.d.ts +19 -1
  79. package/dist/events.d.ts.map +1 -1
  80. package/dist/events.js +146 -23
  81. package/dist/events.js.map +1 -1
  82. package/dist/facades.d.ts +42 -1
  83. package/dist/facades.d.ts.map +1 -1
  84. package/dist/facades.js +363 -7
  85. package/dist/facades.js.map +1 -1
  86. package/dist/index.d.ts +5 -3
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +1 -1
  89. package/dist/index.js.map +1 -1
  90. package/dist/keystore/index.d.ts +14 -1
  91. package/dist/keystore/index.d.ts.map +1 -1
  92. package/dist/keystore/indexeddb-identity-store.d.ts +4 -0
  93. package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
  94. package/dist/keystore/indexeddb-identity-store.js +30 -13
  95. package/dist/keystore/indexeddb-identity-store.js.map +1 -1
  96. package/dist/keystore/indexeddb-shared.d.ts +19 -2
  97. package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
  98. package/dist/keystore/indexeddb-shared.js +193 -6
  99. package/dist/keystore/indexeddb-shared.js.map +1 -1
  100. package/dist/keystore/indexeddb-token-store.d.ts +15 -2
  101. package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
  102. package/dist/keystore/indexeddb-token-store.js +308 -27
  103. package/dist/keystore/indexeddb-token-store.js.map +1 -1
  104. package/dist/org-protocol.d.ts +45 -0
  105. package/dist/org-protocol.d.ts.map +1 -1
  106. package/dist/org-protocol.js +757 -11
  107. package/dist/org-protocol.js.map +1 -1
  108. package/dist/ownership-v21.d.ts +21 -4
  109. package/dist/ownership-v21.d.ts.map +1 -1
  110. package/dist/ownership-v21.js +49 -16
  111. package/dist/ownership-v21.js.map +1 -1
  112. package/dist/ownership-workflow.d.ts +30 -0
  113. package/dist/ownership-workflow.d.ts.map +1 -1
  114. package/dist/ownership-workflow.js +412 -54
  115. package/dist/ownership-workflow.js.map +1 -1
  116. package/dist/seq-tracker.d.ts +2 -0
  117. package/dist/seq-tracker.d.ts.map +1 -1
  118. package/dist/seq-tracker.js +3 -2
  119. package/dist/seq-tracker.js.map +1 -1
  120. package/dist/tools/cross-sdk-agent.d.ts +60 -3
  121. package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
  122. package/dist/tools/cross-sdk-agent.js +433 -37
  123. package/dist/tools/cross-sdk-agent.js.map +1 -1
  124. package/dist/transport.d.ts +3 -0
  125. package/dist/transport.d.ts.map +1 -1
  126. package/dist/transport.js +62 -30
  127. package/dist/transport.js.map +1 -1
  128. package/dist/version.d.ts +1 -1
  129. package/dist/version.js +1 -1
  130. package/package.json +1 -1
@@ -22,11 +22,12 @@ P2P `message.*` 的最终投递语义由连接阶段声明的 `delivery_mode`
22
22
  | [message.send](#messagesend) | 发送消息 |
23
23
  | [message.thought.put](#messagethoughtput) | 写入某个 P2P 上下文的思考内容 |
24
24
  | [message.thought.get](#messagethoughtget) | 读取某个 P2P 上下文的思考内容 |
25
+ | [message.peers](#messagepeers) | 查询待拉消息对端、水位及可选整体积压估计 |
25
26
  | [message.pull](#messagepull) | 增量拉取消息 |
26
27
  | [message.history](#messagehistory) | 向前只读翻页历史消息 |
27
- | [message.ack](#messageack) | 确认送达 |
28
- | [message.read](#messageread) | 上报当前设备的话题业务已读位置 |
29
- | [message.read_state](#messageread_state) | 查询当前 AID 的跨设备已读汇总 |
28
+ | [message.ack](#messageack) | 确认送达 |
29
+ | [message.read](#messageread) | 上报当前设备的话题业务已读位置 |
30
+ | [message.read_state](#messageread_state) | 查询当前 AID 的跨设备已读汇总 |
30
31
  | [message.recall](#messagerecall) | 撤回消息 |
31
32
  | [message.query_online](#messagequery_online) | 批量查询在线状态 |
32
33
 
@@ -44,51 +45,55 @@ P2P `message.*` 的最终投递语义由连接阶段声明的 `delivery_mode`
44
45
 
45
46
  ## 事件索引
46
47
 
47
- | 事件 | 说明 |
48
- |------|------|
49
- | [event/message.received](#eventmessagereceived) | 收到新消息 |
50
- | [event/message.recalled](#eventmessagerecalled) | 消息被撤回 |
51
- | [message.read_changed](#messageread_changed) | 同一 AID 的共享已读位置前进 |
52
- | `read_state_sync` | SDK 本地已读补偿完成,交付本轮 states/errors |
53
- | [delivery.changed](#deliverychanged) | SDK 本地批量交付完成通知,用于合并 UI 刷新 |
48
+ | 事件 | 说明 |
49
+ |------|------|
50
+ | [event/message.received](#eventmessagereceived) | 收到新消息 |
51
+ | [message.batch](#sdk-批量消息事件) | SDK 本地 P2P 消息/撤回批次 |
52
+ | [group.message_batch](#sdk-批量消息事件) | SDK 本地群消息/撤回批次 |
53
+ | [group.event_batch](#sdk-批量消息事件) | SDK 本地群事件批次 |
54
+ | [message.peers_snapshot](#messagepeers_snapshot) | SDK 本地对端列表、水位及可选整体积压估计快照 |
55
+ | [event/message.recalled](#eventmessagerecalled) | 消息被撤回 |
56
+ | [message.read_changed](#messageread_changed) | 同一 AID 的共享已读位置前进 |
57
+ | `read_state_sync` | SDK 本地已读补偿完成,交付本轮 states/errors |
58
+ | [delivery.changed](#deliverychanged) | SDK 本地批量交付完成通知,用于合并 UI 刷新 |
54
59
 
55
60
  ---
56
61
 
57
- ## message.read
58
-
59
- 请求为 `{"states":[{"scope":"p2p","topic_key":"topic-1","last_read_message_id":"m-100"}]}`。`states` 至少一项;省略时也兼容顶层单条,显式空数组、null 或非数组值拒绝;只更新出现的键。AID 与 device ID 从认证连接取得,不能指定其它设备。`scope` 缺省或为空时按 `p2p` 处理,非空值只能为 `p2p`;显式非字符串值拒绝。topic 原样保存,首尾空格、大小写和 Unicode 不折叠;topic、消息 ID、认证 AID 与 device ID 各最多 128 字符。
60
-
61
- 响应为 `{"updated":1,"ignored":0,"states":[{"scope":"p2p","topic_key":"topic-1","last_read_message_id":"m-100","changed":true}]}`。这些是逐项设备写入回执,不能当作共享汇总;`changed=true` 不保证共享位置前进。重复或更旧位置返回 `changed=false`,不回退状态。批量参数校验失败不提交其中任何一项。每个 RPC 只等待本次依赖的写入提交;跨多个数据库批次时,后续失败不回滚先前提交,原样幂等重试可收敛。
62
-
63
- 定向到 `device_id` / `to_device_id` 的消息形成独立设备事实,不进入 AID 共享汇总,也不广播共享已读变更。该业务 API 不推进投递 ACK。
64
-
65
- P2P 已读状态只由当前身份的 home 域管理。Gateway 从认证连接绑定主体,禁止调用方伪造 `_auth.aid`;`message.read` 与 `message.read_state` 的 federation 入站和出站均拒绝。该边界不改变受来源域约束的已读事件转发。
66
-
67
- ## message.read_state
68
-
69
- 查询字段均可选:`scope`(缺省或为空时按 `p2p`,非空值只能为 `p2p`)、`topic_key`、`aid`(非空值只能等于认证 AID)。显式非字符串或 `null` 拒绝。省略过滤条件查询当前 AID 的全部共享状态;无状态返回 `states=[]`。响应始终有 `states`;只有一项时顶层镜像该项字段。
70
-
71
- | 状态字段 | 含义 |
72
- | --- | --- |
73
- | `scope` / `topic_key` / `last_read_message_id` | 当前话题在全部设备中位置最新的共享业务已读消息 |
74
- | `last_read_order` | 公共消息顺序提示;用于 SDK 乱序保护,不是设备 seq |
75
- | `current_device_seq` | 该消息在当前认证设备 inbox 的本地 seq,无法映射为 `null` |
76
- | `mapping_status` | `resolved`:映射成功;`not_received`:公共位置已知,当前设备无投递映射;`unresolved`:公共消息位置无法解析 |
77
- | `updated_at` | 服务端更新时间,毫秒 |
78
-
79
- 未知消息首次上报保存待解析状态、查询为 `unresolved`,不广播汇总前进;晚到公共位置仅对仍匹配该消息且 order=0 的事实条件持久回填,不能只更新缓存或覆盖并发前进的事实;未知位置不能覆盖已知位置。历史清理后使用持久 `last_read_order` 防止比较回退。接口只返回共享汇总,不返回其它设备 ID、seq 或定向设备事实。当前设备投递索引缺失时回查公共索引,GC 后热/冷进程均返回一致映射状态。
80
-
81
- 网络 JSON 的公共顺序和 seq 是整数,Gateway 本域服务及 federation 回程必须无损保留;TS/JS 的 `last_read_order`、非空 `current_device_seq` 在安全整数范围内是 number,超出时是精确十进制字符串。`null` 映射保持不变。
82
-
83
- ## message.read_changed
84
-
62
+ ## message.read
63
+
64
+ 请求为 `{"states":[{"scope":"p2p","topic_key":"topic-1","last_read_message_id":"m-100"}]}`。`states` 至少一项;省略时也兼容顶层单条,显式空数组、null 或非数组值拒绝;只更新出现的键。AID 与 device ID 从认证连接取得,不能指定其它设备。`scope` 缺省或为空时按 `p2p` 处理,非空值只能为 `p2p`;显式非字符串值拒绝。topic 原样保存,首尾空格、大小写和 Unicode 不折叠;topic、消息 ID、认证 AID 与 device ID 各最多 128 字符。
65
+
66
+ 响应为 `{"updated":1,"ignored":0,"states":[{"scope":"p2p","topic_key":"topic-1","last_read_message_id":"m-100","changed":true}]}`。这些是逐项设备写入回执,不能当作共享汇总;`changed=true` 不保证共享位置前进。重复或更旧位置返回 `changed=false`,不回退状态。批量参数校验失败不提交其中任何一项。每个 RPC 只等待本次依赖的写入提交;跨多个数据库批次时,后续失败不回滚先前提交,原样幂等重试可收敛。
67
+
68
+ 定向到 `device_id` / `to_device_id` 的消息形成独立设备事实,不进入 AID 共享汇总,也不广播共享已读变更。该业务 API 不推进投递 ACK。
69
+
70
+ P2P 已读状态只由当前身份的 home 域管理。Gateway 从认证连接绑定主体,禁止调用方伪造 `_auth.aid`;`message.read` 与 `message.read_state` 的 federation 入站和出站均拒绝。该边界不改变受来源域约束的已读事件转发。
71
+
72
+ ## message.read_state
73
+
74
+ 查询字段均可选:`scope`(缺省或为空时按 `p2p`,非空值只能为 `p2p`)、`topic_key`、`aid`(非空值只能等于认证 AID)。显式非字符串或 `null` 拒绝。省略过滤条件查询当前 AID 的全部共享状态;无状态返回 `states=[]`。响应始终有 `states`;只有一项时顶层镜像该项字段。
75
+
76
+ | 状态字段 | 含义 |
77
+ | --- | --- |
78
+ | `scope` / `topic_key` / `last_read_message_id` | 当前话题在全部设备中位置最新的共享业务已读消息 |
79
+ | `last_read_order` | 公共消息顺序提示;用于 SDK 乱序保护,不是设备 seq |
80
+ | `current_device_seq` | 该消息在当前认证设备 inbox 的本地 seq,无法映射为 `null` |
81
+ | `mapping_status` | `resolved`:映射成功;`not_received`:公共位置已知,当前设备无投递映射;`unresolved`:公共消息位置无法解析 |
82
+ | `updated_at` | 服务端更新时间,毫秒 |
83
+
84
+ 未知消息首次上报保存待解析状态、查询为 `unresolved`,不广播汇总前进;晚到公共位置仅对仍匹配该消息且 order=0 的事实条件持久回填,不能只更新缓存或覆盖并发前进的事实;未知位置不能覆盖已知位置。历史清理后使用持久 `last_read_order` 防止比较回退。接口只返回共享汇总,不返回其它设备 ID、seq 或定向设备事实。当前设备投递索引缺失时回查公共索引,GC 后热/冷进程均返回一致映射状态。
85
+
86
+ 网络 JSON 的公共顺序和 seq 是整数,Gateway 本域服务及 federation 回程必须无损保留;TS/JS 的 `last_read_order`、非空 `current_device_seq` 在安全整数范围内是 number,超出时是精确十进制字符串。`null` 映射保持不变。
87
+
88
+ ## message.read_changed
89
+
85
90
  SDK 订阅名为 `message.read_changed`。事件包含 `owner_aid`、`scope=p2p`、`topic_key`、`last_read_message_id`、`last_read_order` 和 `updated_at`,只在数据库提交成功且共享位置前进后发出。`owner_aid` 必须非空。Gateway 即使收到显式 route 也只向该 AID 分发,并校验 Kernel 转发的可信服务来源;federation 将 `owner_aid` 绑定来源域。SDK 要求 `scope=p2p` 匹配原事件,忽略缺失 owner 或其它 AID 的状态;事件 ID 对 JSON 数组 `[owner_aid, topic_key, last_read_message_id]` 计算 SHA-256,不包含 `last_read_order`,失败日志不泄露 topic。事件没有统一设备 seq,不进入 Pull 队列,丢失通过重连后 `read_state` 查询补偿。
86
-
87
- 四端 facade 分别为 Python `message.read/read_state`、TS/JS `message.read/readState`、Go `Message().Read/ReadState`。四个已读门面保留显式 `None/null/nil` 字段供服务端拒绝,不改变其他门面的空值处理。仅查询和有效事件更新共享缓存;旧事件不交付应用,同位置事件保留设备映射并避免重复交付,新位置不继承旧映射。未知顺序不按时间或 ID 猜测前进;已知同序 ID 按 Unicode 码点/BINARY 顺序比较。
88
-
89
- SDK 登记成功查询的原过滤范围,空查询也登记;连接成功后补偿并在本地发布 `read_state_sync`,含 `owner_aid`、去重 `states` 和 `{method, params, error}` 错误项。消息 `background_sync=false` 不关闭补偿,AID/device 切换清理旧缓存及过滤项;旧身份在途结果按身份代际隔离,A→B→A 也不写当前缓存或补偿完成事件,原 RPC 调用仍可返回结果。应用在 connect 前订阅,详见 [跨设备已读未读 SDK 集成指南](../跨设备已读未读SDK集成指南.md)。
90
-
91
- ## message.send
91
+
92
+ 四端 facade 分别为 Python `message.read/read_state`、TS/JS `message.read/readState`、Go `Message().Read/ReadState`。四个已读门面保留显式 `None/null/nil` 字段供服务端拒绝,不改变其他门面的空值处理。仅查询和有效事件更新共享缓存;旧事件不交付应用,同位置事件保留设备映射并避免重复交付,新位置不继承旧映射。未知顺序不按时间或 ID 猜测前进;已知同序 ID 按 Unicode 码点/BINARY 顺序比较。
93
+
94
+ SDK 登记成功查询的原过滤范围,空查询也登记;连接成功后补偿并在本地发布 `read_state_sync`,含 `owner_aid`、去重 `states` 和 `{method, params, error}` 错误项。消息 `background_sync=false` 不关闭补偿,AID/device 切换清理旧缓存及过滤项;旧身份在途结果按身份代际隔离,A→B→A 也不写当前缓存或补偿完成事件,原 RPC 调用仍可返回结果。应用在 connect 前订阅,详见 [跨设备已读未读 SDK 集成指南](../跨设备已读未读SDK集成指南.md)。
95
+
96
+ ## message.send
92
97
 
93
98
  发送一条 P2P 消息。
94
99
 
@@ -99,10 +104,10 @@ SDK 登记成功查询的原过滤范围,空查询也登记;连接成功后
99
104
  "jsonrpc": "2.0",
100
105
  "method": "message.send",
101
106
  "params": {
102
- "to": "bob.agentid.pub",
103
- "device_id": "bob-phone",
104
- "ttl": 300,
105
- "payload": {"type": "text", "text": "Hello!"},
107
+ "to": "bob.agentid.pub",
108
+ "device_id": "bob-phone",
109
+ "ttl": 300,
110
+ "payload": {"type": "text", "text": "Hello!"},
106
111
  "encrypted": false,
107
112
  "message_id": "550e8400-e29b-41d4-a716-446655440000",
108
113
  "timestamp": 1234567890000
@@ -115,23 +120,26 @@ SDK 登记成功查询的原过滤范围,空查询也登记;连接成功后
115
120
 
116
121
  | 参数 | 类型 | 必填 | 默认值 | 说明 |
117
122
  |------|------|------|--------|------|
118
- | `to` | string | 是 | — | 接收方 AID |
119
- | `device_id` | string | 否 | — | 目标接收设备。非空时仅投递该设备;未指定时按接收方 AID 投递全部设备 |
120
- | `to_device_id` | string | 否 | — | `device_id` 的兼容别名;两者均非空时以 `device_id` 为准 |
121
- | `ttl` | integer | 否 | `0` | 秒。`ttl <= 0` 持久化;`ttl > 0` 钳制到 1–3600 秒并只保存在消息服务内存中 |
122
- | `payload` | object | 是 | — | 消息内容(任意 JSON 对象) |
123
+ | `to` | string | 是 | — | 接收方 AID |
124
+ | `device_id` | string | 否 | — | 目标接收设备。非空时仅投递该设备;未指定时按接收方 AID 投递全部设备 |
125
+ | `to_device_id` | string | 否 | — | `device_id` 的兼容别名;两者均非空时以 `device_id` 为准 |
126
+ | `ttl` | integer | 否 | `0` | 秒。`ttl <= 0` 持久化;`ttl > 0` 钳制到 1–3600 秒并只保存在消息服务内存中 |
127
+ | `payload` | object | 是 | — | 消息内容(任意 JSON 对象) |
123
128
  | `type` | string | 否 | — | 信封/封装类型,普通业务消息无需填写;SDK V2 加密发送时自动使用 `e2ee.p2p_encrypted` |
124
129
  | `encrypted` | boolean | 否 | `false` | 底层 RPC 的 E2EE 标记。Python SDK 便捷层通常使用 `encrypt` 入参并由 SDK 自动填充此字段 |
125
- | `message_id` | string | 否 | — | 幂等键(客户端提供或服务端生成 UUID) |
130
+ | `message_id` | string | 否 | — | 持久 P2P 的逻辑消息幂等键;新消息使用新的唯一 ID,重试复用原 ID,sender 隔离不扩大复用范围;客户端提供或服务端生成 UUID。volatile 保留原 sender+ID 隔离 |
131
+ | `need_render` | boolean | 否 | `true` | 是否需要应用渲染;不改变消息拉取顺序或 ACK 规则 |
126
132
  | `timestamp` | integer | 否 | — | 客户端时间戳(毫秒)。**服务端忽略此字段,始终使用服务端时间** |
127
133
  | `protected_headers` / `headers` | object | 否 | — | SDK 加密前读取的 E2EE 信封元数据,类似 HTTP headers;推荐使用 `protected_headers`,`headers` 仅作为兼容别名;服务端不解释,接收端验 `_auth` 后在 `e2ee.protected_headers` 暴露 |
128
134
 
129
- > 连接级 `delivery_mode` 在 `auth.connect` 阶段声明,结构见 `02-WebSocket协议.md`。Python SDK 的 P2P 消息发送会沿用当前连接的 `delivery_mode`,应用层发送时无需重复指定。
130
- > `protected_headers` 只在 SDK 加密路径生效;裸 RPC 发送明文或已加密信封时,调用方需自行遵守 [05-E2EE加密通信](05-E2EE加密通信.md#protectedheaders-与可验证上下文) 的格式和校验规则。
131
-
135
+ > 连接级 `delivery_mode` 在 `auth.connect` 阶段声明,结构见 `02-WebSocket协议.md`。Python SDK 的 P2P 消息发送会沿用当前连接的 `delivery_mode`,应用层发送时无需重复指定。
136
+ > `protected_headers` 只在 SDK 加密路径生效;裸 RPC 发送明文或已加密信封时,调用方需自行遵守 [05-E2EE加密通信](05-E2EE加密通信.md#protectedheaders-与可验证上下文) 的格式和校验规则。
137
+
132
138
  `message.send.device_id` 表示接收目标,不是发送方设备,也不同于 `message.pull` / `message.ack` 中表示当前消费实例的 `device_id`。指定目标设备时,SDK 的 V2 加密信封只选择该设备对应的 peer wrap(AID scope 使用对应 AID wrap),同时保留发送方 self-sync 和监管 audit wrap。缓存 bootstrap 中缺少目标设备时,四个 SDK 会清除该缓存,获取新鲜 bootstrap 并仅重试一次;目标设备仍不存在或不可用时发送失败,不会降级为 AID 全设备广播。
133
-
134
- `ttl` 与连接级 `delivery_mode` 相互独立。正 TTL 消息不写数据库或 WAL、不分配持久 `seq`;跨域只转发剩余 TTL,目标域不会重新执行 1 秒下限钳制。
139
+
140
+ `ttl` 与连接级 `delivery_mode` 相互独立。正 TTL 消息不写数据库或 WAL、不分配持久 `seq`;跨域只转发剩余 TTL,目标域不会重新执行 1 秒下限钳制。
141
+
142
+ `need_render` 由 SDK 提取到外层信封,接收事件保留该标记;缺字段及历史 NULL 按 `true`。它不进入 E2EE AAD、`protected_headers`、消息保护签名或元数据 HMAC,RPC `client_signature` 仍按既有完整业务参数规则覆盖它。
135
143
 
136
144
  ### Payload 参考约定
137
145
 
@@ -164,24 +172,24 @@ SDK 登记成功查询的原过滤范围,空查询也登记;连接成功后
164
172
 
165
173
  | 字段 | 类型 | 说明 |
166
174
  |------|------|------|
167
- | `message_id` | string | 消息 ID |
168
- | `storage` | string | `persistent` 或 `volatile`;旧服务端未返回时按 `persistent` 处理 |
169
- | `seq` | integer | 持久消息的接收方收件箱序号;volatile 消息不返回 |
170
- | `volatile_cursor` | string | volatile 消息的 opaque 游标;不得解析 |
171
- | `volatile_cursors` | string[] | 未指定目标设备且投递多个设备时的 volatile 游标列表 |
172
- | `created_at` | integer | volatile 消息的服务端创建时间(毫秒) |
173
- | `expires_at` | integer | volatile 消息的服务端过期时间(毫秒) |
175
+ | `message_id` | string | 消息 ID |
176
+ | `storage` | string | `persistent` 或 `volatile`;旧服务端未返回时按 `persistent` 处理 |
177
+ | `seq` | integer | 持久消息的接收方收件箱序号;volatile 消息不返回 |
178
+ | `volatile_cursor` | string | volatile 消息的 opaque 游标;不得解析 |
179
+ | `volatile_cursors` | string[] | 未指定目标设备且投递多个设备时的 volatile 游标列表 |
180
+ | `created_at` | integer | volatile 消息的服务端创建时间(毫秒) |
181
+ | `expires_at` | integer | volatile 消息的服务端过期时间(毫秒) |
174
182
  | `timestamp` | integer | 服务端时间戳(毫秒) |
175
- | `status` | string | `"sent"` / `"delivered"` / `"accepted"` / `"duplicate"` |
183
+ | `status` | string | `"sent"` / `"delivered"` / `"accepted"` / `"duplicate"` |
176
184
  | `delivery_mode` | string | 最终生效的连接级投递语义:`fanout` 或 `queue` |
177
185
  | `envelope` | object | SDK 回填的发送结果信封,包含发送方、接收方、业务类型、时间戳、加密标志、protected headers 等可转发元数据 |
178
186
  | `payload` | object | SDK 回填的应用层业务 payload;裸 RPC 或内部 `_skip_send_result_envelope` 路径可能没有该字段 |
179
187
  | `cross_domain` | boolean | 仅跨域投递时出现,当前值为 `true` |
180
188
  | `target_issuer` | string | 仅跨域投递时出现,表示目标 issuer |
181
189
 
182
- > **duplicate 响应**:当 `message_id` 重复时,若服务端仍缓存首次结果,返回完整首次响应并附加 `"status": "duplicate"`;若缓存已过期,仅返回 `message_id`、`timestamp`、`status`,**不含** `seq` 和 `delivery_mode`。客户端收到 `"duplicate"` 状态时应视为幂等成功,无需重试。
183
-
184
- volatile 成功响应以 `storage="volatile"`、`volatile_cursor`(多设备时还可含 `volatile_cursors`)、`created_at`、`expires_at` 和 `status="accepted"` 表示,不含 `seq`。到期或服务重启后消息允许丢失。
190
+ > **duplicate 响应**:当 `message_id` 重复时,若服务端仍缓存首次结果,返回完整首次响应并附加 `"status": "duplicate"`;若缓存已过期,仅返回 `message_id`、`timestamp`、`status`,**不含** `seq` 和 `delivery_mode`。客户端收到 `"duplicate"` 状态时应视为幂等成功,无需重试。
191
+
192
+ volatile 成功响应以 `storage="volatile"`、`volatile_cursor`(多设备时还可含 `volatile_cursors`)、`created_at`、`expires_at` 和 `status="accepted"` 表示,不含 `seq`。到期或服务重启后消息允许丢失。
185
193
 
186
194
  ### 错误
187
195
 
@@ -190,18 +198,18 @@ volatile 成功响应以 `storage="volatile"`、`volatile_cursor`(多设备时
190
198
  | -32002 | 服务暂不可用(如数据库未连接、服务证书未加载) |
191
199
  | -32603 | 参数缺失(to 或 payload) |
192
200
  | -32603 | payload 超过大小限制(默认 1 MB) |
193
- | -32603 | 目标 AID 不存在 |
194
- | -32603 | 指定的目标设备不存在、未注册或 V2 bootstrap 中不可用 |
195
- | -32603 | 频率限制超限 |
201
+ | -32603 | 目标 AID 不存在 |
202
+ | -32603 | 指定的目标设备不存在、未注册或 V2 bootstrap 中不可用 |
203
+ | -32603 | 频率限制超限 |
196
204
 
197
205
  ### 示例
198
206
 
199
207
  ```python
200
- result = await client.call("message.send", {
201
- "to": "bob.agentid.pub",
202
- "device_id": "bob-phone",
203
- "payload": {"type": "text", "text": "Hello!"},
204
- })
208
+ result = await client.call("message.send", {
209
+ "to": "bob.agentid.pub",
210
+ "device_id": "bob-phone",
211
+ "payload": {"type": "text", "text": "Hello!"},
212
+ })
205
213
  # result: {"message_id": "...", "seq": 42, "status": "delivered", ...}
206
214
  ```
207
215
 
@@ -332,9 +340,38 @@ result = await client.call("message.thought.get", {
332
340
 
333
341
  ---
334
342
 
343
+ ## message.peers
344
+
345
+ 新 SDK 自动声明连接能力 `p2p_peer_pull_v1: true`,只使用 peer 消费路径。`seq` 仍由接收设备的原收件箱共享;按完整 `sender_aid` 隔离 A/T/H、ACK 和 volatile 游标,不分配新序号,不逐条回填历史。旧 SDK 继续使用 global Pull/ACK。当前 peer 模式限定单 Message 服务实例;多实例支持留待 AUN 分布式 2.0,单实例可使用多个 writer shard。
346
+
347
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
348
+ |------|------|------|--------|------|
349
+ | `filter` | string | 否 | `pending` | `pending`:有持久待拉记录或可用 volatile 消息的对端;`all`:全部仍有消息状态的对端,不是联系人目录 |
350
+ | `limit` | integer | 否 | 100 | 仅 `all`,范围 1–200 |
351
+ | `page_token` | string | 否 | — | 仅 `all`,复用 `next_page_token` |
352
+
353
+ `pending` 一次完整返回,不分页、不截断到 200 个 peer;传 `limit` / `page_token` 会拒绝。响应包含 `cursor_scope="peer"`、`receiver_aid`、`device_id`、`bootstrap_floor_seq`、`catalog_ready`、`catalog_revision`、`peers`;`all` 额外返回 `has_more` / `next_page_token`。`catalog_ready=false` 不是已同步完成的空结果,SDK 保留重试。
354
+
355
+ | 每 peer 字段 | 说明 |
356
+ |-------------|------|
357
+ | `sender_aid` | 完整对端 AID |
358
+ | `head_seq` / `server_ack_seq` | 可靠扫描 Head / 当前 peer 服务端确认水位 |
359
+ | `initial_after_seq` / `initialization_source` | 首次本地初始化水位及其来源 `bootstrap` / `peer` |
360
+ | `next_pending_seq` | 可选排序近似,正常 pending 持久候选直接取该 peer 的 `server_ack_seq`(A),包含 0;all/volatile-only 允许省略,不代表真实首条未确认投递 |
361
+ | `volatile_has_more` | 有可拉 volatile 消息;volatile-only peer 仍列入 pending,不依赖持久数量 |
362
+ | `retention_floor_seq` / `earliest_available_seq` | 已永久不可取得的前缀及当前可读下界 |
363
+
364
+ `pending` 直接从同一游标表的 peer A/H 筛选内部 `peer_H>A` 且 `peer_H>R` 的持久候选,并合并可用 volatile sender;不扫描消息积压、不逐 peer seek,也不执行 COUNT/SUM。目录 `next_pending_seq=A` 不增加查询;保守候选仍需由 Forward 覆盖证明及 ACK 确认完成,公共 S 不用于抬高某 peer 的扫描起点。每 peer 与整体的 `pending_count`、`need_render_pending_count`、`non_render_pending_count` 三项精确数量不再返回。顶层可选 `pending_count_estimate=max(0,H-max(R,S_used))`,其中 H 是设备可靠 Head、R 是永久保留边界、S_used 是当前 slot 的有效公共安全前缀;S 缺失或 S>H 时按 0 使用,H 未知则省略。它是整体持久 seq 跨度上界,可含空号及 S 后已 ACK 消息,不含 volatile;不是准确条数、未读数或 all 单页数量,不用于调度、Tail 策略、完成判断或推进游标。
365
+
366
+ 服务端首次进入 peer 模式时原子冻结同设备/消费 slot 的旧服务端 ACK 为 `bootstrap_floor_seq`,无记录为 0;已有 peer ACK 优先。之后新 peer 始终用该固定基线,不能取后来变化的 global ACK。peer ACK 与 slot 公共 S 分行持久于同一表,bootstrap 保持独立;peer ACK 不推进旧 global ACK,同一设备切回旧 SDK 允许重拉。
367
+
368
+ 四 SDK 仅在内部调度副本中,将连续 peer 段按调度值稳定升序:有效非负整数 `next_pending_seq`(含 0)原值优先,缺失或非法才回退 `max(已确认覆盖水位,本地A)`,不取 `max(A,hint)`,缺提示 peer 仍参与排序。RPC 返回及 `message.peers_snapshot` 原列表顺序不变。后台 gate 在已就绪连续 peer Forward 段内优先最小调度值,前台优先,群/global 及原有调度边界不被跨越。每 N 次实际后台 peer Forward 派发一次 FIFO,默认 N=4;无提示 peer Forward 也计数,群/旧 global 不计,未派发或折叠 waiter 不计,防止高 seq peer 饥饿。
369
+
370
+ 正常 peer Forward 有后续持久消息时可携带精确 `next_pending_seq`,SDK 在本页消息可靠 commit 后即可用该页提示替换旧提示,不等待 ACK;只收响应或 commit 失败不能采用,ACK 失败独立保留待确认责任,迟到页/目录不能使提示回退。非法或缺失的可选提示只忽略,不阻断合法消息页,不由 `covered_through_seq+1` 补造提示,也不改变 A/H/覆盖或完成判断。持久末页清除已覆盖提示,volatile 或在途新 Push 的待拉责任仍保留。Tail/force/History 不更新正常增量调度提示;无每页额外 peers/ACK,保留网络与本地处理 pipeline。producer 可保持多页续拉责任,但每页独立申请和释放共享网络名额;实际派发只提供尽力偏好,不承诺严格 S 最优。
371
+
335
372
  ## message.pull
336
373
 
337
- 按两个独立游标增量拉取持久消息和 volatile 消息。volatile 消息不进入持久 `seq` 空间。
374
+ 新 SDK 指定 `sender_aid` 时拉取该对端;省略 sender 时先请求完整 pending `message.peers`,再通过共享 N=4 Gate 并发拉取,返回规范 `cursor_scope="peer"`、逐 peer 的 `peers` 结果及合并的 `messages`,`partial=true` 表示有 peer 未完成。Go 另保留旧 `peer_results` 嵌套结果及 `has_more=partial`、`complete=!partial` 兼容字段;新代码使用 `peers` / `partial`。聚合调用不可携带显式 `after_seq`(包括 0)、`after_volatile_cursor`、`window_mode` 或 `force=true`;这些参数必须指定 sender。持久和 volatile 游标分别维护,后者不进入持久 `seq` 空间。
338
375
 
339
376
  ### 请求
340
377
 
@@ -343,10 +380,10 @@ result = await client.call("message.thought.get", {
343
380
  "jsonrpc": "2.0",
344
381
  "method": "message.pull",
345
382
  "params": {
346
- "after_seq": 100,
347
- "after_volatile_cursor": "v1:i1:7:20",
348
- "limit": 50,
349
- "device_id": "device-001"
383
+ "sender_aid": "alice.agentid.pub",
384
+ "after_volatile_cursor": "v1:i1:7:20",
385
+ "limit": 200,
386
+ "device_id": "device-001"
350
387
  },
351
388
  "id": 2
352
389
  }
@@ -356,17 +393,23 @@ result = await client.call("message.thought.get", {
356
393
 
357
394
  | 参数 | 类型 | 必填 | 默认值 | 说明 |
358
395
  |------|------|------|--------|------|
359
- | `after_seq` | integer | 否 | 0 | 拉取 seq > after_seq 的消息 |
360
- | `after_volatile_cursor` | string | 否 | SDK 当前游标 | 拉取该 opaque volatile 游标之后的消息;应用通常无需传入 |
361
- | `limit` | integer | 否 | 50 | SDK 单页上限,最大 50 |
362
- | `device_id` | string | 否 | 当前连接实例 | 多实例消费上下文中的设备标识 |
363
-
364
- > 四个 SDK 自动管理 `after_volatile_cursor`,并在传输层注入当前消费实例的内部 slot。slot 不属于应用参数,也不会出现在消息、响应或事件中。
396
+ | `sender_aid` | string | 否 | — | 完整发送方 AID;省略表示高层聚合拉取全部 pending peer |
397
+ | `cursor_scope` | string | 否 | `peer` | 新 SDK 固定使用 `peer`;底层服务旧请求缺省 `global` |
398
+ | `after_seq` | integer | 否 | 当前 peer 本地 A | 显式指定时必须有 sender;读取 seq > after_seq 的该对端消息 |
399
+ | `after_volatile_cursor` | string | 否 | SDK 当前游标 | 拉取该 opaque volatile 游标之后的消息;应用通常无需传入 |
400
+ | `limit` | integer | 否 | 200 | peer 单页上限,最大 200,可能因响应字节限制更早截页 |
401
+ | `device_id` | string | 否 | 当前连接实例 | 多实例消费上下文中的设备标识 |
402
+ | `max_pages` | integer | 否 | 100 | 本次公开调用的页预算;全部 peer 共用预算,耗尽不表示后台同步完成 |
403
+ | `window_mode` | string | 否 | — | 指定 sender 后可显式使用 `tail`;不与 force 或 Piggy ACK 同用 |
365
404
 
366
- 当前四个 SDK 的 V2 路由仍将单页限制为 50。直接调用底层 `message.v2.pull` 时,服务端默认 50、最大 200;旧 `message.pull` 的持久消息及 `message.history` 仍为最大 50。
405
+ > 四个 SDK 自动管理 `after_volatile_cursor`,并在传输层注入当前消费实例的内部 slot。slot 不属于应用参数,也不会出现在消息、响应或事件中。
406
+
407
+ 新 SDK 的 peer Pull/Tail/History 单页默认及最大 200;旧 SDK 的 global 请求保留原默认值和限制。连接上线提示及重连恢复触发 pending 目录查询。后台同步的 `max_pages` 是工作片预算,达到 100 页仍有剩余时必须继续编排,不能将其当作同步终点。
367
408
 
368
409
  ### 响应
369
410
 
411
+ 以下基础消息字段沿用原格式;下表扫描证明与页内计数描述服务端单次 RPC 的原始页。SDK 自动续页后可能合并 `messages`,即使保留某一页的 proof/count,也只能解释该原始页,不能用于验证或计数整个合并列表。多 peer 聚合外层返回 `peers` / `messages` / `partial`,保留逐 peer 进度,不提供全体 peer 共用的 proof、可提交游标或精确总计数。
412
+
370
413
  ```json
371
414
  {
372
415
  "jsonrpc": "2.0",
@@ -394,34 +437,47 @@ result = await client.call("message.thought.get", {
394
437
 
395
438
  | 字段 | 类型 | 说明 |
396
439
  |------|------|------|
397
- | `messages` | array | 持久与 volatile 消息按服务端时间合并;持久消息子序列仍按 `seq` 升序。消息对象含 `encrypted` 字段(仅 `encrypted=true` 时出现) |
440
+ | `messages` | array | 持久与 volatile 消息按服务端时间合并;持久消息子序列仍按 `seq` 升序。消息对象含 `encrypted` 字段(仅 `encrypted=true` 时出现) |
398
441
  | `count` | integer | 本次返回的消息数 |
399
- | `latest_seq` | integer | 返回的最大 seq |
442
+ | `latest_seq` | integer | 返回的最大 seq |
400
443
  | `has_more` | boolean | 服务端 Forward 响应中仍有可见持久消息未返回;通过 `limit + 1` 探测及响应截页确定,不由 `unread_count` 或恰好满页推断 |
401
- | `latest_volatile_cursor` | string\|null | 本页最后一个 volatile 游标;不影响 `latest_seq` |
402
- | `volatile_has_more` | boolean | 是否仍有未返回的 volatile 消息;SDK 在 volatile cursor 前进时据此继续分页 |
403
- | `volatile_earliest_cursor` | string\|null | 当前 boot epoch 内仍可读取的最早 volatile 游标 |
404
- | `volatile_dropped_count` | integer | 当前 boot epoch 内因过期或容量淘汰而不可读的累计数 |
405
- | `server_ack_seq` | integer | 服务端已确认的 ack_seq(仅设备视图路径返回)。客户端用此值跳过 retention window 之外的空洞 |
444
+ | `latest_volatile_cursor` | string\|null | 本页最后一个 volatile 游标;不影响 `latest_seq` |
445
+ | `volatile_has_more` | boolean | 是否仍有未返回的 volatile 消息;SDK 在 volatile cursor 前进时据此继续分页 |
446
+ | `volatile_earliest_cursor` | string\|null | 当前 boot epoch 内仍可读取的最早 volatile 游标 |
447
+ | `volatile_dropped_count` | integer | 当前 boot epoch 内因过期或容量淘汰而不可读的累计数 |
448
+ | `server_ack_seq` | integer | 当前 peer 服务端 ACK;只用于首次本地初始化,运行中不得用它跳过本地未交付区间 |
406
449
  | `retention_floor_seq` | integer | 持久化保留窗口的下界 seq;seq 小于等于此值的消息已过期不可再拉取 |
407
450
  | `earliest_available_seq` | integer\|null | 当前可拉取的最小 seq(`retention_floor_seq + 1`);`retention_floor_seq=0` 时为 `null` |
408
451
  | `ephemeral_earliest_available_seq` | integer\|null | 临时缓冲中可用的最小 seq |
409
452
  | `ephemeral_dropped_count` | integer | 已被淘汰的临时消息数 |
410
453
 
411
- `message.pull` 是公开的 Forward Pull。实时 A/T/H 最新页同步由 SDK 内部自动完成,不提供需要应用传入的模式参数。Forward 页通过结构校验后,本地 A 按原始页最大 seq 推进。页内缺号视为服务端永久空号;单条解密失败通过 `message.undecryptable` 报告,不阻塞后续水位。Forward 仍支持 Piggy ACK:有后续页时携带待提交 ACK;没有续页证明时以独立 ACK 收尾,不为 ACK 额外拉取空页。
412
-
413
- 四个 SDK 只让含正 `seq` 的持久消息进入 seq tracker。volatile push 与 pull 在进程内按发送方和 `message_id` 去重,成功交付后自动 ACK;epoch 不匹配时清空本地 volatile 游标并重试一次。`message.history` 永远不返回 volatile 消息。
414
-
415
- `message.pull` 与 `message.history` 进入当前 `AUNClient` 的客户端级 Pull Gate。该 Gate 与 Group Message、Group Event Pull 共享,同一 namespace 内的 Tail、History、Forward 始终单飞,不同 namespace 最多 4 个并行。namespace 分别为 `p2p:{当前 AID}`、`group:{群标识}`、`group_event:{群标识}`,同群消息与群事件使用不同 namespace。相同完整请求 key 折叠并共享结果;可运行的不同 key 按前台 FIFO(Tail/History)优先于后台 FIFO(Forward/Gap Fill/Group Event)调度,跳过已被占用的 namespace。不同 `AUNClient` 实例之间不共享 Gate,应用层无需自行实现并发控制。
416
-
417
- 解密当前消息时若缺少 sender IK,SDK 会把同发送端请求 single-flight 合并,最多同步等待 3 秒执行 bootstrap;获取成功立即重试当前消息,失败或超时才进入 pending。该补救不改变 Forward 按原始页推进 A 的规则。
454
+ | peer Forward 字段 | 说明 |
455
+ |-------------------|------|
456
+ | `cursor_scope` / `sender_aid` | 固定 `peer` 和本次 sender,SDK 必须校验响应绑定 |
457
+ | `scan_after_seq` / `covered_through_seq` | 固定可靠 Head 下已经扫描的 `(after, through]` 区间 |
458
+ | `next_after_seq` / `head_seq` | 下一页扫描位置(等于 covered)/ 本页固定可靠 Head |
459
+ | `persistent_has_more` / `volatile_has_more` / `has_more` | 严格布尔值;最后一个等于前两个的 OR,不由满页或计数推断 |
460
+ | `next_pending_seq` | 可选,本页最终边界后首条未返回持久投递原 seq,`covered_through_seq < seq <= head_seq`;`persistent_has_more=false` 时省略,只用于调度 |
461
+ | `page_need_render_count` / `page_non_render_count` | 最终返回页消息的两类数量,包含本页 volatile;在构造页时计算,不改变拉取顺序 |
418
462
 
419
- SDK 只保证单个实时拉取响应页内按 seq 升序、同 seq 最多发布一次。A/T/H 是扫描水位,不记录应用是否已经收到回调;跨 Push、SDK 自动最新页同步和 Forward 页允许重复和乱序。全局去重与排序由应用业务仓库负责,不是 SDK 交付契约;应用必须按 `(namespace, message_id)` 幂等并按 `seq` 排序。SDK 进程内折叠仅作 best-effort 优化。History 只返回结果,不参与实时发布。
463
+ `next_pending_seq` 复用原 `limit+1` 查询及合并结果:200 条页通常取第 201 条;字节限制截断时取第一条未返回持久消息,volatile 混排挤占持久消息时按最终边界修正,无新增 SQL。only-volatile 不返回此字段,但仍按 `volatile_has_more` 续拉;peer force Forward 可返回,SDK 忽略其调度更新;Tail/History/旧 global 不增加该字段。
464
+
465
+ peer 的原 seq 是稀疏的,不能套用全局连续数字校验;只有通过验证的扫描覆盖区间可推进窗口。解密未完成或应用交付失败时,peer A/ACK 不越过该消息;运行中的服务端 ACK 也不能代替本地交付。合法 retention floor 可以解释已删除前缀。Forward 有后续页时可 Piggy ACK,末页独立 ACK,不为 ACK 额外拉空页。旧 global 与 Group 的终态规则保持原行为。
466
+
467
+ `window_mode="tail"` 返回 sender 最新一页、固定 `head_seq` 及 `window_start_seq` / `covered_through_seq`;seq 跨度可以超过 limit,不要求 Forward 的三个 `has_more` 字段。SDK 只有在已验证自身 `agent.md.type=human` 且需要先展示最新消息时自动 Tail→Forward;非 human、缺失或未知 type 直接 Forward,不能从对端名片决定自身策略。
468
+
469
+ 四个 SDK 只让含正 `seq` 的持久消息进入 seq tracker。volatile push 与 pull 在进程内按发送方和 `message_id` 去重,成功交付后自动 ACK;epoch 不匹配时清空本地 volatile 游标并重试一次。`message.history` 永远不返回 volatile 消息。
470
+
471
+ `message.pull` 与 `message.history` 的网络阶段进入当前 `AUNClient` 的共享 Pull Gate,与 Group Message、Group Event 共用 N=4。每个 peer 的物理 namespace 是 `p2p.peer.v1/` 加完整 sender UTF-8 的无 padding base64url;群沿用 `group:{群标识}` / `group_event:{群标识}`。同 namespace 串行与共享 N 名额分开维护,不同 namespace 最多 4 个网络请求并行;相同 key 折叠,可运行前台 FIFO 优先于后台 FIFO。每页响应或错误返回即释放共享名额,producer 等待、本地处理及应用回调不跨页占槽;网络预取可与本地处理重叠。各 peer 和群的应用消息事件共享队列,排队容量按内部 `page_size × N` 动态计算,默认 800 条;请求的 `limit` 不重新配置该容量。
472
+
473
+ 解密当前消息时若缺少 sender IK,SDK 会把同发送端请求 single-flight 合并,最多同步等待 3 秒执行 bootstrap;获取成功立即重试,失败或超时才进入 pending。peer 路径等待交付完成后才能推进对应 A/ACK。
474
+
475
+ SDK 仅在当前运行实例内协调重复交付:发布序号窗口按 namespace 保留最高 50,000 个 seq,P2P 成功交付绑定最多 50,000 条;消息热路径不逐条查询或保存耐久 receipt,也不要求自定义 store 提供 receipt 扩展。P2P 投递身份仍为接收 AID、设备、slot、事件类别及原正 delivery seq,并校验 sender/message_id;namespace、Push/Pull/Tail 来源不另造身份。全部所需应用 handler 成功后才提交内存成功标记;持久消息 Forward 页本页新增累计 seq ACK 在有效 proof 与页 A/T/H 提交后发送,具备持久化能力的 store 负责页状态耐久。volatile 与群事件 ACK 沿用各自原路径;失败页可重试,内存去重不替代耐久游标。淘汰、重建 client、进程崩溃或版本往返后允许再次回调,应用须将去重和业务副作用放在同一持久事务内;持久 P2P 使用 message_id 唯一键,volatile 保留原 sender+message_id 隔离。多个 client/进程共用 store 不提供跨实例首次回调原子 claim,不承诺跨重启 exactly-once;跨 peer 不保证全局回调顺序,显式 History/force/Tail 列表不因内存去重删行。
420
476
 
421
477
  ### 示例
422
478
 
423
479
  ```python
424
- result = await client.call("message.pull", {"after_seq": 0, "limit": 20})
480
+ result = await client.call("message.pull", {"sender_aid": "alice.agentid.pub", "limit": 200})
425
481
  for msg in result["messages"]:
426
482
  print(f"{msg['from']}: {msg['payload']}")
427
483
  ```
@@ -437,7 +493,8 @@ for msg in result["messages"]:
437
493
  | 参数 | 类型 | 必填 | 默认值 | 说明 |
438
494
  |------|------|------|--------|------|
439
495
  | `before_seq` | integer | 是 | — | 排他上界,只返回 `seq < before_seq` 的消息 |
440
- | `limit` | integer | 否 | 50 | 单页上限,最大 50 |
496
+ | `sender_aid` | string | 是 | — | 完整发送方 AID,隔离读取该对端历史 |
497
+ | `limit` | integer | 否 | 200 | peer 单页上限,最大 200 |
441
498
 
442
499
  ### 响应
443
500
 
@@ -451,16 +508,16 @@ for msg in result["messages"]:
451
508
  | `earliest_available_seq` | integer\|null | 当前最早可拉取边界 |
452
509
 
453
510
  ```python
454
- page = await client.message.history(before_seq=101, limit=50)
511
+ page = await client.message.history(sender_aid="alice.agentid.pub", before_seq=101, limit=200)
455
512
  while page.get("has_older"):
456
- page = await client.message.history(before_seq=page["next_before_seq"], limit=50)
513
+ page = await client.message.history(sender_aid="alice.agentid.pub", before_seq=page["next_before_seq"], limit=200)
457
514
  ```
458
515
 
459
516
  ---
460
517
 
461
518
  ## message.ack
462
519
 
463
- 确认已收到消息。持久 ACK 与 volatile ACK 可以单独提交,也可以在同一请求中提交。
520
+ 新 SDK 要求 `sender_aid`,只确认该 peer 的已交付范围。持久 ACK 与 volatile ACK 可以单独提交,也可以在同一请求中提交。下面 JSON 保留旧 global 字段示例,peer 请求还须带 `cursor_scope="peer"` 与 sender;SDK 自动注入 scope。
464
521
 
465
522
  ### 请求
466
523
 
@@ -469,9 +526,9 @@ while page.get("has_older"):
469
526
  "jsonrpc": "2.0",
470
527
  "method": "message.ack",
471
528
  "params": {
472
- "seq": 150,
473
- "volatile_cursors": ["v1:i1:7:21"],
474
- "device_id": "device-001"
529
+ "seq": 150,
530
+ "volatile_cursors": ["v1:i1:7:21"],
531
+ "device_id": "device-001"
475
532
  },
476
533
  "id": 3
477
534
  }
@@ -481,13 +538,15 @@ while page.get("has_older"):
481
538
 
482
539
  | 参数 | 类型 | 必填 | 默认值 | 说明 |
483
540
  |------|------|------|--------|------|
484
- | `seq` | integer | 否 | — | 确认 seq ≤ 此值的所有持久消息;未提交 volatile ACK 时必填 |
485
- | `volatile_cursor` | string | 否 | — | 单个 volatile 游标的兼容形式 |
486
- | `volatile_cursors` | string[] | 否 | — | 确认指定 volatile 游标;可独立于 `seq` 使用 |
487
- | `volatile_ids` | string[] | 否 | — | 按 `message_id` 确认 volatile 消息 |
488
- | `device_id` | string | 否 | 当前连接实例 | 多实例消费上下文中的设备标识 |
489
-
490
- > 四个 SDK 在传输层注入当前消费实例的内部 slot;应用不传入或读取 slot。
541
+ | `seq` | integer | 否 | — | 确认 seq ≤ 此值的所有持久消息;未提交 volatile ACK 时必填 |
542
+ | `sender_aid` | string | 是 | — | 完整发送方 AID;只确认该对端 |
543
+ | `up_to_seq` | integer | 否 | — | peer 持久确认水位;SDK 接受 `seq` 兼容形式并限制到本地已交付 A |
544
+ | `volatile_cursor` | string | 否 | — | 单个 volatile 游标的兼容形式 |
545
+ | `volatile_cursors` | string[] | 否 | — | 确认指定 volatile 游标;可独立于 `seq` 使用 |
546
+ | `volatile_ids` | string[] | 否 | — | 按 `message_id` 确认 volatile 消息 |
547
+ | `device_id` | string | 否 | 当前连接实例 | 多实例消费上下文中的设备标识 |
548
+
549
+ > 四个 SDK 在传输层注入当前消费实例的内部 slot;应用不传入或读取 slot。
491
550
 
492
551
  ### 响应
493
552
 
@@ -504,19 +563,21 @@ while page.get("has_older"):
504
563
 
505
564
  | 字段 | 类型 | 说明 |
506
565
  |------|------|------|
507
- | `success` | boolean | 操作是否成功 |
508
- | `ack_seq` | integer | 本次推进到的 ack_seq |
509
- | `volatile_ack` | object | volatile ACK 的 `accepted`、`expired` 等幂等结果;只提交持久 ACK 时可省略 |
566
+ | `success` | boolean | 操作是否成功 |
567
+ | `peer_ack_seq` / `effective_ack_seq` | integer | peer 响应的确认水位;不得拿旧 `ack_seq` 代替 |
568
+ | `legacy_safe_ack_seq` / `previous_legacy_safe_ack_seq` | integer | 可选的当前 slot 公共安全前缀 S 及其原值;真实 sender 的 Ap 与内部 `*` 行的 S 共表、同事务持久,不推进旧 global ACK/冻结 G,不新增设备聚合行 |
569
+ | `device_safe_ack_seq` | integer | 可选设备级安全证明;本期缺完整持久 slot 名册,允许省略,SDK 不据单 peer/slot 水位销毁旧 SPK |
570
+ | `volatile_ack` | object | volatile ACK 的 `accepted`、`expired` 等幂等结果;只提交持久 ACK 时可省略 |
510
571
 
511
572
  ### 副作用
512
573
 
513
- 确认成功后仅推进接收方消费游标。服务端不会向发送方或其他客户端推送 `message.ack` 事件,也不会把对端已读状态作为协议通知暴露。
574
+ peer ACK 不更新旧 global cursor,也不更新其他 peer。peer 能力连接的 ACK 通知使用 `cursor_scope="peer"` / `sender_aid` / `peer_ack_seq`,SDK 不能用远端 ACK 事件越过本地交付缺口。投递 ACK 与 `message.read` 的应用业务已读独立。
514
575
 
515
576
  ### 示例
516
577
 
517
578
  ```python
518
- result = await client.call("message.ack", {"seq": 150})
519
- # result: {"success": true, "ack_seq": 150}
579
+ result = await client.call("message.ack", {"sender_aid": "alice.agentid.pub", "seq": 150})
580
+ # peer 响应通过 peer_ack_seq / effective_ack_seq 表示确认水位。
520
581
  ```
521
582
 
522
583
  ---
@@ -642,8 +703,8 @@ result = await client.call("message.ack", {"seq": 150})
642
703
  "encrypted": false
643
704
  },
644
705
  "message_id": "uuid-1",
645
- "from": "alice.agentid.pub",
646
- "sender_device_id": "alice-phone",
706
+ "from": "alice.agentid.pub",
707
+ "sender_device_id": "alice-phone",
647
708
  "to": "bob.agentid.pub",
648
709
  "seq": 42,
649
710
  "timestamp": 1234567890000,
@@ -663,42 +724,71 @@ result = await client.call("message.ack", {"seq": 150})
663
724
  }
664
725
  ```
665
726
 
666
- SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;信封字段统一放在 `envelope`。`sender_device_id` 是发送方设备标识,四个 SDK 均会保留给应用层,可用于把回复定向到原发送设备。`envelope` 只保留可转发的归一化元数据,`from` 由 `from_aid` / `sender_aid` 归一化而来,`timestamp` 由 `created_at` / `t_server` 归一化而来。0.5.x 当前仍保留顶层 `message_id` / `from` / `to` / `seq` / `timestamp` 等兼容别名;新代码应优先通过 `msg["envelope"]["from"]`、`msg["envelope"]["timestamp"]` 等路径访问。`envelope.agent_md.sender` / `envelope.agent_md.group` 是 agent.md 云端版本提示,SDK 会自动观察并写入本地 `remote_etag`。Gateway 可能附加 `proximity` 及 `same_device` / `same_egress_ip` / `same_network`,表示由 Gateway 基于连接上下文判断的近端关系提示,不参与 E2EE AAD 或业务鉴权。
667
-
668
- volatile 事件额外包含 `storage="volatile"`、`volatile_cursor`、`created_at` 和 `expires_at`,不含持久 `seq`。四个 SDK 保留这些字段、自动去重和 ACK,并确保应用可见对象不含内部 slot。
727
+ SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;信封字段统一放在 `envelope`。`sender_device_id` 是发送方设备标识,四个 SDK 均会保留给应用层,可用于把回复定向到原发送设备。`envelope` 只保留可转发的归一化元数据,`from` 由 `from_aid` / `sender_aid` 归一化而来,`timestamp` 由 `created_at` / `t_server` 归一化而来。0.5.x 当前仍保留顶层 `message_id` / `from` / `to` / `seq` / `timestamp` 等兼容别名;新代码应优先通过 `msg["envelope"]["from"]`、`msg["envelope"]["timestamp"]` 等路径访问。`envelope.agent_md.sender` / `envelope.agent_md.group` 是 agent.md 云端版本提示,SDK 会自动观察并写入本地 `remote_etag`。Gateway 可能附加 `proximity` 及 `same_device` / `same_egress_ip` / `same_network`,表示由 Gateway 基于连接上下文判断的近端关系提示,不参与 E2EE AAD 或业务鉴权。
728
+
729
+ volatile 事件额外包含 `storage="volatile"`、`volatile_cursor`、`created_at` 和 `expires_at`,不含持久 `seq`。四个 SDK 保留这些字段、自动去重和 ACK,并确保应用可见对象不含内部 slot。
730
+
731
+ 持久 peer 推送可附 `prev_peer_seq`,表示同接收设备、同 sender 的前一条原 seq;未知时省略,可证明首条才为 0。A=103 时收到105,前驱103表示相接,前驱大于103表示需要补拉,缺前驱则保守通过 peer Forward 覆盖证明判断,不能直接用数字跳号判 Gap。上线提示不一定有具体 sender,SDK 会查询 pending peers 后调度。
669
732
 
670
733
  ### 订阅
671
734
 
672
735
  ```python
673
- client.on("message.received", lambda msg: print(msg["payload"]))
674
- ```
675
-
676
- ---
677
-
678
- ## delivery.changed
679
-
680
- `delivery.changed` 是四个 SDK 的本地事件,不是 Message 服务或 Gateway 发送的 `event/...`。应用仍通过 `message.received` 逐条接收和保存 P2P 消息,再使用该事件合并刷新 UI。
681
-
682
- ```json
683
- {
684
- "trigger": "pull_drained",
685
- "changes": [
686
- {"namespace": "p2p", "delivered_count": 50}
687
- ]
688
- }
689
- ```
690
-
691
- - 多页自动 Pull 不按页通知;当前 `AUNClient` 的所有 Pull 工作排空后,最多发布一次 `pull_drained`。
692
- - 有效 Push / Inline Push 在原消息事件处理完成后分别以 `push` / `inline_push` 通知;同一次有序队列 drain 内的同类消息会折叠。
693
- - 实时通知可以吸收尚未通知的 Pull 变更,后续不会再为同一批变更发布 `pull_drained`。
694
- - 空页、重复或过滤消息、非法 payload、解密失败及没有交付消息的 Head 通知不产生该事件。
695
- - `delivered_count` 是合并窗口内已经发布的原消息事件数,不表示当前未读总数。
696
-
697
- Pull 全局排空、群 namespace 和完整字段定义见 [06-API手册:delivery.changed](06-API手册.md#deliverychanged)。
698
-
699
- ---
700
-
701
- ## event/message.recalled
736
+ client.on("message.received", lambda msg: print(msg["payload"]))
737
+ ```
738
+
739
+ ---
740
+
741
+ ## SDK 批量消息事件
742
+
743
+ | 事件 | items 中保留的原事件 |
744
+ |------|----------------------|
745
+ | `message.batch` | `message.received`、`message.recalled` |
746
+ | `group.message_batch` | `group.message_created`、`group.message_recalled` |
747
+ | `group.event_batch` | `group.changed` |
748
+
749
+ 三者 payload 均为 `{namespace,items:[{event,payload}],source}`;保留原 event、payload、页内顺序和来源,不按 need_render 过滤。每个实际页固定 batch 模式及 batch handlers;该类 batch 有订阅时只发 batch,否则完整沿用原 single 发布链,single listeners 仍动态查找,无 single 订阅也不跳过处理,不双发。batch 订阅变化从下一页生效。
750
+
751
+ 批次不跨实际页或 namespace,空 items 不发布;Push/inline 单条使用单元素批次。全部所需 handler 成功后才完成内存标记并进入各类型原页提交/ACK 路径;失败不转 single 重发,保留原重试责任,部分 handler 已提交副作用时允许整批重放,由应用持久幂等处理。Go 提供 `OnBatch(event, func(any) error)`,error 阻止成功确认,原 `On` 兼容;群成员、密钥及解散的内部处理顺序、Group RPC/游标/ACK 模型不变。`message.peers_snapshot` 独立发布。
752
+
753
+ ## message.peers_snapshot
754
+
755
+ 这是 SDK 本地事件,不是服务端 Push;应用显式调用 `message.peers` 成功时,空列表和非空列表都发布。后台调用仅在列表非空时发布,包括整体持久估计为 0 或缺省的 volatile-only peer。查询失败或 `catalog_ready=false` 不发布成功快照;发布入应用事件队列,不等待其 handler 才继续 Pull。
756
+
757
+ 事件保留目录的 `filter`、`catalog_revision`、`bootstrap_floor_seq`、`catalog_ready` 和完整 `peers`,包括可选 `next_pending_seq`;列表保持服务端原顺序,不随内部调度副本重排,并附以下字段:
758
+
759
+ | 字段 | 说明 |
760
+ |------|------|
761
+ | `trigger` | `application` 或 `background`;合并请求中有应用调用则使用前者 |
762
+ | `peer_count` | 本次返回 peer 数量 |
763
+ | `pending_count_estimate` | 服务端提供时原样透传的整体持久积压估计,缺省时省略,不求和或填 0;`all` 分页时也不是本页消息数 |
764
+ | `aid` / `device_id` | 当前接收身份与设备;身份/设备切换后旧代际快照不会交付 |
765
+
766
+ 应用可用该快照展示待拉对端与大致积压;估计可明显偏大,不能当作精确进度或业务已读统计。`peers` 保留原水位与 volatile 标记,无逐 peer/顶层三项精确数量;Pull 最终返回页的 `count/page_need_render_count/page_non_render_count` 仍保留并在构造页时计算。
767
+
768
+ ## delivery.changed
769
+
770
+ `delivery.changed` 是四个 SDK 的本地事件,不是 Message 服务或 Gateway 发送的 `event/...`。应用通过已订阅的 `message.batch` 或原 `message.received` 接收并持久幂等保存 P2P 消息,再使用该事件合并刷新 UI。
771
+
772
+ ```json
773
+ {
774
+ "trigger": "pull_drained",
775
+ "changes": [
776
+ {"namespace": "p2p", "delivered_count": 50}
777
+ ]
778
+ }
779
+ ```
780
+
781
+ - 多页自动 Pull 不按页通知;当前 `AUNClient` 的所有 Pull 工作排空后,最多发布一次 `pull_drained`。
782
+ - 有效 Push / Inline Push 在原消息事件处理完成后分别以 `push` / `inline_push` 通知;同一次有序队列 drain 内的同类消息会折叠。
783
+ - 实时通知可以吸收尚未通知的 Pull 变更,后续不会再为同一批变更发布 `pull_drained`。
784
+ - 空页、重复或过滤消息、非法 payload、解密失败及没有交付消息的 Head 通知不产生该事件。
785
+ - `delivered_count` 是合并窗口内已经发布的原消息事件数,不表示当前未读总数。
786
+
787
+ Pull 全局排空、群 namespace 和完整字段定义见 [06-API手册:delivery.changed](06-API手册.md#deliverychanged)。
788
+
789
+ ---
790
+
791
+ ## event/message.recalled
702
792
 
703
793
  消息被发送方撤回时推送给**接收方**。
704
794