@agentunion/fastaun-browser 0.5.4 → 0.5.8

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 (184) hide show
  1. package/CHANGELOG.md +177 -67
  2. package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/216/245/345/205/245/344/270/216/346/274/224/347/244/272/346/214/207/345/215/227.md +290 -0
  3. package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/234/215/345/212/241/346/236/266/346/236/204/344/270/216/350/257/246/347/273/206/345/256/236/347/216/260/350/256/241/345/210/222-codex.md +994 -0
  4. package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/234/215/345/212/241/350/277/220/347/273/264/344/270/216/345/217/221/345/270/203/346/214/207/345/215/227.md +144 -0
  5. package/_packed_docs/CHANGELOG.md +177 -67
  6. package/_packed_docs/INDEX.md +124 -65
  7. package/_packed_docs/KITE_DOCS_GUIDE.md +65 -30
  8. package/_packed_docs/agent.md/SCHEMA.md +83 -58
  9. package/_packed_docs/agent.md/examples/codeagent-claudecode.md +1 -1
  10. package/_packed_docs/agent.md/examples/openclaw-lobster.md +1 -1
  11. package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +1 -1
  12. package/_packed_docs/audit/AUN/346/234/215/345/212/241Go/345/214/226/351/207/215/346/236/204/347/262/276/347/273/206/345/214/226/346/272/220/347/240/201/345/256/241/346/237/245-20260718.md +366 -0
  13. package/_packed_docs/aun/345/205/254/347/275/221/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +234 -0
  14. package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1095 -0
  15. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1199 -0
  16. package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +6 -4
  17. package/_packed_docs/group-message-rpc-alignment-gaps.md +741 -0
  18. package/_packed_docs/message-online-push-alignment.md +572 -0
  19. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +58 -20
  20. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +219 -247
  21. package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +14 -14
  22. package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +3 -3
  23. 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 +165 -421
  24. package/_packed_docs/protocol/README.md +1 -0
  25. package/_packed_docs/protocol/aun-docs-guide.md +7 -4
  26. package/_packed_docs/protocol/index.md +25 -19
  27. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +63 -18
  28. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +22 -0
  29. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +93 -84
  30. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +6 -2
  31. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +73 -34
  32. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +3 -3
  33. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +21 -9
  34. package/_packed_docs/sdk/09-group-rpc-manual.md +319 -285
  35. package/_packed_docs/sdk/09-message-rpc-manual.md +144 -103
  36. package/_packed_docs/sdk/09-payload-reference.md +1 -1
  37. package/_packed_docs/sdk/09-storage-rpc-manual.md +14 -3
  38. package/_packed_docs/sdk/09-stream-rpc-manual.md +8 -8
  39. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +34 -23
  40. package/_packed_docs/sdk/CHANGELOG-0.5.6.md +73 -0
  41. package/_packed_docs/sdk/INDEX.md +45 -37
  42. package/_packed_docs/sdk/README.md +11 -9
  43. package/_packed_docs//345/217/221/345/270/203/346/212/245/345/221/212-0.5.6.md +260 -0
  44. package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/344/273/243/347/240/201/345/256/241/346/237/245/351/227/256/351/242/230/346/270/205/345/215/225.md +229 -0
  45. package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/346/224/271/351/200/240/346/226/271/346/241/210.md +750 -0
  46. package/dist/agent-md-schema.d.ts +14 -0
  47. package/dist/agent-md-schema.d.ts.map +1 -0
  48. package/dist/agent-md-schema.js +103 -0
  49. package/dist/agent-md-schema.js.map +1 -0
  50. package/dist/agent-md.d.ts +11 -5
  51. package/dist/agent-md.d.ts.map +1 -1
  52. package/dist/agent-md.js +295 -53
  53. package/dist/agent-md.js.map +1 -1
  54. package/dist/aid-store.d.ts +7 -7
  55. package/dist/aid-store.d.ts.map +1 -1
  56. package/dist/aid-store.js +46 -18
  57. package/dist/aid-store.js.map +1 -1
  58. package/dist/aid.d.ts +2 -0
  59. package/dist/aid.d.ts.map +1 -1
  60. package/dist/aid.js +29 -7
  61. package/dist/aid.js.map +1 -1
  62. package/dist/auth.d.ts.map +1 -1
  63. package/dist/auth.js +31 -11
  64. package/dist/auth.js.map +1 -1
  65. package/dist/bundle.js +29920 -19719
  66. package/dist/cert-utils.d.ts +1 -0
  67. package/dist/cert-utils.d.ts.map +1 -1
  68. package/dist/cert-utils.js +36 -16
  69. package/dist/cert-utils.js.map +1 -1
  70. package/dist/client/delivery.d.ts +106 -11
  71. package/dist/client/delivery.d.ts.map +1 -1
  72. package/dist/client/delivery.js +1925 -374
  73. package/dist/client/delivery.js.map +1 -1
  74. package/dist/client/group-state.d.ts.map +1 -1
  75. package/dist/client/group-state.js +27 -24
  76. package/dist/client/group-state.js.map +1 -1
  77. package/dist/client/lifecycle.d.ts +15 -1
  78. package/dist/client/lifecycle.d.ts.map +1 -1
  79. package/dist/client/lifecycle.js +477 -132
  80. package/dist/client/lifecycle.js.map +1 -1
  81. package/dist/client/mention-mode.d.ts +7 -0
  82. package/dist/client/mention-mode.d.ts.map +1 -0
  83. package/dist/client/mention-mode.js +184 -0
  84. package/dist/client/mention-mode.js.map +1 -0
  85. package/dist/client/peers.d.ts +1 -1
  86. package/dist/client/peers.d.ts.map +1 -1
  87. package/dist/client/peers.js +26 -3
  88. package/dist/client/peers.js.map +1 -1
  89. package/dist/client/rpc-pipeline.d.ts +34 -2
  90. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  91. package/dist/client/rpc-pipeline.js +503 -99
  92. package/dist/client/rpc-pipeline.js.map +1 -1
  93. package/dist/client/runtime.d.ts +1 -3
  94. package/dist/client/runtime.d.ts.map +1 -1
  95. package/dist/client/runtime.js +4 -7
  96. package/dist/client/runtime.js.map +1 -1
  97. package/dist/client/v2-e2ee.d.ts +43 -2
  98. package/dist/client/v2-e2ee.d.ts.map +1 -1
  99. package/dist/client/v2-e2ee.js +931 -138
  100. package/dist/client/v2-e2ee.js.map +1 -1
  101. package/dist/client.d.ts +46 -15
  102. package/dist/client.d.ts.map +1 -1
  103. package/dist/client.js +785 -304
  104. package/dist/client.js.map +1 -1
  105. package/dist/errors.d.ts.map +1 -1
  106. package/dist/errors.js +4 -1
  107. package/dist/errors.js.map +1 -1
  108. package/dist/facades.d.ts +26 -1
  109. package/dist/facades.d.ts.map +1 -1
  110. package/dist/facades.js +142 -60
  111. package/dist/facades.js.map +1 -1
  112. package/dist/group-id.d.ts.map +1 -1
  113. package/dist/group-id.js +2 -17
  114. package/dist/group-id.js.map +1 -1
  115. package/dist/group-index.d.ts +6 -1
  116. package/dist/group-index.d.ts.map +1 -1
  117. package/dist/group-index.js +44 -25
  118. package/dist/group-index.js.map +1 -1
  119. package/dist/index.d.ts +5 -4
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +3 -2
  122. package/dist/index.js.map +1 -1
  123. package/dist/keystore/index.d.ts +10 -5
  124. package/dist/keystore/index.d.ts.map +1 -1
  125. package/dist/keystore/indexeddb-identity-store.d.ts +0 -12
  126. package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
  127. package/dist/keystore/indexeddb-identity-store.js +0 -60
  128. package/dist/keystore/indexeddb-identity-store.js.map +1 -1
  129. package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
  130. package/dist/keystore/indexeddb-shared.js +9 -5
  131. package/dist/keystore/indexeddb-shared.js.map +1 -1
  132. package/dist/keystore/indexeddb-token-store.d.ts +3 -1
  133. package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
  134. package/dist/keystore/indexeddb-token-store.js +47 -2
  135. package/dist/keystore/indexeddb-token-store.js.map +1 -1
  136. package/dist/register-flow.d.ts.map +1 -1
  137. package/dist/register-flow.js +28 -3
  138. package/dist/register-flow.js.map +1 -1
  139. package/dist/seq-tracker.d.ts +28 -8
  140. package/dist/seq-tracker.d.ts.map +1 -1
  141. package/dist/seq-tracker.js +224 -61
  142. package/dist/seq-tracker.js.map +1 -1
  143. package/dist/storage/vfs.d.ts +1 -0
  144. package/dist/storage/vfs.d.ts.map +1 -1
  145. package/dist/storage/vfs.js +26 -2
  146. package/dist/storage/vfs.js.map +1 -1
  147. package/dist/tools/cross-sdk-agent.js +399 -11
  148. package/dist/tools/cross-sdk-agent.js.map +1 -1
  149. package/dist/transport.d.ts +12 -0
  150. package/dist/transport.d.ts.map +1 -1
  151. package/dist/transport.js +226 -102
  152. package/dist/transport.js.map +1 -1
  153. package/dist/v2/session/session.d.ts.map +1 -1
  154. package/dist/v2/session/session.js +3 -4
  155. package/dist/v2/session/session.js.map +1 -1
  156. package/dist/v2/state/commitment.d.ts.map +1 -1
  157. package/dist/v2/state/commitment.js +1 -2
  158. package/dist/v2/state/commitment.js.map +1 -1
  159. package/dist/validators.d.ts.map +1 -1
  160. package/dist/validators.js +8 -14
  161. package/dist/validators.js.map +1 -1
  162. package/dist/version.d.ts +1 -1
  163. package/dist/version.js +1 -1
  164. package/package.json +11 -9
  165. package/dist/group-resources.d.ts +0 -98
  166. package/dist/group-resources.d.ts.map +0 -1
  167. package/dist/group-resources.js +0 -635
  168. package/dist/group-resources.js.map +0 -1
  169. package/dist/keystore/indexeddb.d.ts +0 -179
  170. package/dist/keystore/indexeddb.d.ts.map +0 -1
  171. package/dist/keystore/indexeddb.js +0 -2031
  172. package/dist/keystore/indexeddb.js.map +0 -1
  173. package/dist/namespaces/auth.d.ts +0 -98
  174. package/dist/namespaces/auth.d.ts.map +0 -1
  175. package/dist/namespaces/auth.js +0 -992
  176. package/dist/namespaces/auth.js.map +0 -1
  177. package/dist/namespaces/custody.d.ts +0 -51
  178. package/dist/namespaces/custody.d.ts.map +0 -1
  179. package/dist/namespaces/custody.js +0 -302
  180. package/dist/namespaces/custody.js.map +0 -1
  181. package/dist/namespaces/meta.d.ts +0 -109
  182. package/dist/namespaces/meta.d.ts.map +0 -1
  183. package/dist/namespaces/meta.js +0 -549
  184. package/dist/namespaces/meta.js.map +0 -1
@@ -0,0 +1,750 @@
1
+ # 消息同步与历史拉取改造方案
2
+
3
+ > 状态:实现已完成,正在按本文门禁进行分布式验收和文档收口。
4
+ >
5
+ > 范围:P2P Message、Group Message、Message/Group Python 与 Go 服务端、Gateway、Python/TypeScript/Go/JavaScript SDK。Group Event 的 RPC、Forward Cursor 与 ACK 语义不变,其 Pull 新增独立第三 Gate。
6
+
7
+ ## 1. 结论
8
+
9
+ 本次改造不建设新的完整性同步体系,而是在原 Forward Cursor 上叠加一个可靠实时扫描窗口;每次 Tail Pull 最多读取一页,窗口可通过相邻页面接续扩展,但始终只有一个区间:
10
+
11
+ ```text
12
+ (0, A] (A, T) [T, H]
13
+ Forward已扫描 唯一未扫描Gap Tail已扫描窗口
14
+ ```
15
+
16
+ - `A = acked_local = contiguous_seq`:原 Forward 游标提交到的最大 seq,也是唯一允许 ACK 的水位。
17
+ - `T = tail_local`:服务端已证明完成 Tail 扫描的实时窗口左边界。
18
+ - `H = head_local`:服务端已证明完成 Tail 扫描的实时窗口右边界。
19
+ - Push 只提供服务端最新上界提示,不能直接把该值写入 `H`。
20
+ - Sync 复用原 Pull,通过 `window_mode="tail"` 拉取最新不超过一页。
21
+ - History 使用独立只读 RPC,完全不参与同步游标和 ACK。
22
+ - Group Event 继续使用原 `pull_events -> ack_events` Forward Cursor,不增加 T/H。
23
+
24
+ 这不是三套协议游标。协议完整性状态仍只有 A;T/H 只是 SDK 为“先看到最新消息”保存的本地窗口证明。
25
+
26
+ ## 2. 目标与非目标
27
+
28
+ ### 2.1 目标
29
+
30
+ 1. 大 Gap 场景下,新消息不必等待全部历史补齐即可交付应用。
31
+ 2. 后台仍从 A 向右执行原 Forward Pull,最终补齐唯一 Gap。
32
+ 3. ACK 永远不跨过未补齐且仍可恢复的消息。
33
+ 4. 新服务端兼容旧 SDK;新 SDK 只面向支持 Tail/History 的新服务端。
34
+ 5. 服务端 retention floor 只反映已经实际删除的数据。
35
+ 6. SDK 改动收敛在原 SeqTracker、原 Pull 和原 Push 调度附近。
36
+
37
+ ### 2.2 非目标
38
+
39
+ - 不承诺 Push、Sync、Forward 三个通道之间全局唯一或全局有序。
40
+ - 不在 SDK 内建设消息正文仓库、历史索引或全局去重数据库。
41
+ - 不用 History 反向翻页推进完整性 ACK。
42
+ - 不让 SDK 持久化或推算服务端 retention floor。
43
+ - 不改造 Group Event 的 RPC、Forward Cursor 与 ACK 语义,也不改造 Group State 或其他同步协议;Group Event Pull 仅接入独立第三 Gate。
44
+ - 不恢复 `stash@{0}` 中已经撤回的复杂 Sync V2、游标表和状态链方案。
45
+
46
+ ## 3. 状态模型
47
+
48
+ ### 3.1 状态定义
49
+
50
+ | 状态 | 是否持久化 | 含义 |
51
+ | --- | --- | --- |
52
+ | `A / acked_local` | 是 | Forward 原始响应页已扫描到的最大 seq,或被权威 floor 证明可跳过的最大 seq;ACK 只能提交 A |
53
+ | `T / tail_local` | 是 | 当前 Tail 已扫描窗口的包含式左边界 |
54
+ | `H / head_local` | 是 | 当前 Tail 已扫描窗口的包含式右边界 |
55
+ | `M / max_seen_seq` | 否,沿用现有运行态 | Push 观察到的最高服务端 seq 提示,不是可靠窗口证明 |
56
+ | `S / head_server` | 否 | 本次 Push 或 Sync 观察到的服务端 head |
57
+ | `F / retention_floor_seq` | 否 | 服务端返回的实际不可恢复下界 |
58
+ | `P / page_size` | 配置 | Forward 与 Tail 的单页容量,用于调度阈值 |
59
+
60
+ ### 3.2 不变量
61
+
62
+ 折叠态:
63
+
64
+ ```text
65
+ A = T = H
66
+ ```
67
+
68
+ 有 Gap 时:
69
+
70
+ ```text
71
+ A < T - 1
72
+ T <= H
73
+ Gap = [A + 1, T - 1]
74
+ Window = [T, H]
75
+ ```
76
+
77
+ 必须始终满足:
78
+
79
+ 1. 同一有效会话和存储代际内,A、T、H 单调推进,不允许迟到响应回退任一水位;启动时丢弃损坏持久化值属于安全重建,不视为运行态回退。
80
+ 2. ACK 目标恒等于 A,不能取 H、M、Push seq 或首条返回消息 seq。
81
+ 3. `[T,H]` 在服务端原始 Tail 页及边界证明通过校验后成立;单条解密或应用回调失败只进入诊断,不撤销扫描证明。
82
+ 4. Sync 页面不能进入原 `onPullResult/onMessageSeq` 的 Forward 推进逻辑。
83
+ 5. 如果 `A >= T - 1`,必须立即合并窗口并折叠为 `A=T=H=max(A,H)`。
84
+ 6. T/H 丢失或损坏时折叠为 `T=H=A`;允许重复拉取,不允许猜测窗口。
85
+
86
+ ### 3.3 两个连续区间与一个 Gap
87
+
88
+ - `(0,A]`:原 Forward Cursor 已扫描,或已由服务端权威 floor 合法截断的区间。
89
+ - `[T,H]`:某次或多次可接续 Tail Pull 已扫描的最新窗口。
90
+ - `[A+1,T-1]`:唯一尚未扫描、需要 Forward Fill 的 Gap。
91
+
92
+ Forward/Tail 原始页内部缺失的 seq 视为服务端形成的永久空号,不再生成 Gap Fill 任务。SDK 的 Gap 只表示 A 与 T 之间尚未扫描的区间,不表示其中每个整数 seq 都必须存在消息。
93
+
94
+ 当新 Tail 与旧窗口无法接壤时,T 右移到新窗口起点。旧窗口中的消息可以继续留在应用仓库,但不再属于 SDK 的完整性证明;Forward 追到时允许再次发布,由应用幂等合并。
95
+
96
+ ## 4. 调度状态机
97
+
98
+ ### 4.1 收到 Push
99
+
100
+ 设 Push 通知观察到 `S=head_server`:
101
+
102
+ | 条件 | 动作 |
103
+ | --- | --- |
104
+ | `S <= H` 且 `A >= T-1` | 重复或已覆盖通知;不再 Tail,必要时重试 ACK |
105
+ | `S <= H` 且 `A < T-1` | 不再 Tail;允许该重复通知触发一页后台 Forward Fill |
106
+ | `S > H` | 无论 Gap 大小,先执行 Tail;Tail 提交后重新读取 A/T/H,仍有 Gap 才执行一页后台 Forward |
107
+
108
+ Push 只提供上界,不携带可提交的扫描证明。只要 `head_local < head_server` 就必须先 Tail,不能按页大小绕过 Tail,也不能用 Push 正文直接推进 A/T/H。
109
+
110
+ ### 4.2 Sync 后维护 T/H
111
+
112
+ Sync 固定服务端快照 `Hs=head_seq`,返回可靠窗口起点 `Ws=window_start_seq`。请求发出时的状态只用于诊断;真正提交时必须在 namespace 锁内读取当前 A/T/H 重新计算:
113
+
114
+ | 条件 | 状态变化 |
115
+ | --- | --- |
116
+ | `Hs <= currentH` | 迟到或重复响应;不得回退 T/H |
117
+ | `currentH + P >= Hs` 且 `Ws <= currentH + 1` | 新页和当前窗口接壤;保留 T,推进 `H=Hs` |
118
+ | `currentH + P < Hs` | 一页无法接壤;协议要求 `Ws>currentH+1`,右移 `T=Ws`并推进 H |
119
+ | `Ws > currentH + 1` | 即使算术上预计接壤,服务端边界证明不接壤;仍右移 `T=Ws` |
120
+ | 原始响应或边界证明校验失败 | 不提交本页 T/H;独立 floor clamp 仍按原语义处理,若因此推进 A 则提交后单独 ACK |
121
+ | 单条解密或应用回调失败 | 保留本页 T/H 扫描证明;记录失败 seq 和阶段,后续消息不被阻塞 |
122
+
123
+ Tail 响应必须满足 `Hs-Ws+1<=P`。因此 `currentH+P<Hs` 却返回 `Ws<=currentH+1` 属于协议矛盾,SDK 必须拒绝提交,而不是猜测优先级。其他情况下,最终是否接壤以服务端边界和当前 H 共同判断,以覆盖 retention clamp 和响应字节截断。
124
+
125
+ Tail 页面与 floor clamp 是两个独立来源:Tail 的 W/Hs 永远不能跨 Gap 直接推进 A;同一响应若携带更高的已提交权威 F,SDK 仍可按原 Pull 语义 clamp A,再基于当前 A 决定窗口是否合并。Tail 提交必须在 namespace 锁内基于最新状态统一归一化,并用一个事务保存完整 A/T/H。若在途 Forward、floor clamp 或窗口接壤使 A 实际增长,事务提交后必须通过原 ACK RPC 单独 ACK 新的 A。原始证明失败时不提交本页 T/H;解密或回调失败不影响证明提交。
126
+
127
+ ### 4.3 后台 Forward Fill
128
+
129
+ Sync 完成后立即恢复原 Forward 流程:
130
+
131
+ ```text
132
+ Pull(after_seq=A) -> 校验原始页 -> A推进到页内最大seq
133
+ ```
134
+
135
+ - 当 `A < T-1`:继续逐页 Forward。
136
+ - 当 `A >= T-1`:`[T,H]` 已与左侧连续前缀接壤,合并为 `A=T=H=max(A,H)`。
137
+ - 合并后按原 ACK RPC 提交 A。
138
+ - Forward RPC、响应校验或状态持久化失败时,不修改未提交页面对应的 A,并按原重试策略继续。
139
+ - 原始页校验通过后,页内缺号、单条解密失败或应用回调失败都不阻塞 A;失败项单独记录诊断事件。
140
+ - Tail 右移期间已经在途的旧 Forward 响应只能单调推进 A,不能覆盖较新的 T/H。
141
+ - Tail 后的 Forward 调度只运行一页;每页完成后释放 Pull Gate,让等待中的前台 Tail/History 优先于下一次后台 Forward。
142
+
143
+ ### 4.4 服务端下界 clamp
144
+
145
+ 普通 Forward 收到服务端下界证明后:
146
+
147
+ 1. P2P 只有显式 `retention_floor_seq=F` 可以截断不可恢复前缀。
148
+ 2. Group Message/Event 另有成员可见性下界 `V=max(cursor.join_seq, earliest_available_*-1)`;普通 Forward 将 A 单调推进到 `max(A,F,V)`。`V` 是当前成员永久不可读前缀,不是物理 GC,不得写入或命名为 retention floor。
149
+ 3. `cursor.current_seq` 仅用于同设备已提交服务端 ACK 的恢复,不得与 F/V 混称。
150
+ 4. Group Tail 不应用 V,只允许独立的 F clamp;History 不读取或修改 A/T/H。
151
+ 5. SDK 不单独持久化 F/V,而是原子持久化归一化后的 A/T/H;推进后执行与普通 Forward 相同的窗口合并判断。
152
+ 6. 若有效下界已超过 H,则丢弃旧窗口证明并折叠为 `A=T=H=effective_floor`。
153
+
154
+ ## 5. 协议设计
155
+
156
+ ### 5.1 Forward Pull 保持原语义
157
+
158
+ 以下接口的无 `window_mode` 请求保持原请求、响应、游标和 ACK 语义:
159
+
160
+ - `message.pull` / `message.v2.pull`
161
+ - `group.pull` / `group.v2.pull`
162
+ - `group.pull_events`
163
+
164
+ 新服务端不得向旧请求强制增加新响应形状,不得要求旧 SDK 理解 T/H。
165
+
166
+ ### 5.2 Tail Sync:原 Pull 的可选模式
167
+
168
+ 请求在原 Pull 参数上增加:
169
+
170
+ | 字段 | 语义 |
171
+ | --- | --- |
172
+ | `window_mode` | 固定为 `tail` |
173
+ | `after_seq` | SDK 当前 A,用于兼容、clamp 和诊断,不作为 Tail 查询左边界 |
174
+ | `limit` | 最大页容量 P |
175
+ | `group_id` | 仅 Group Message 必填 |
176
+
177
+ 成功进入 Tail 模式时,服务端必须显式回显:
178
+
179
+ | 字段 | 语义 |
180
+ | --- | --- |
181
+ | `window_mode` | `tail`;没有该回显时 SDK 不得按 Sync 处理 |
182
+ | `window_start_seq` | 服务端证明已完整覆盖的包含式窗口左边界 |
183
+ | `head_seq` | 本次请求固定的安全 head 快照 |
184
+ | `covered_through_seq` | 本页覆盖证明;Tail 正常等于 `head_seq` |
185
+ | `messages` | 窗口内当前可见消息,按 seq 升序 |
186
+ | 原 floor/ACK 字段 | 继续用于服务端 clamp,不得被 Tail 窗口反推 |
187
+
188
+ 服务端处理约束:
189
+
190
+ 1. 请求开始时固定安全 `head_seq`。
191
+ 2. 计算以 head 结尾、跨度不超过一页的 seq band。
192
+ 3. 合并现有 inbox、recent cache、legacy fallback 和 WAL/pending 可见数据。
193
+ 4. 只返回固定快照内的数据,页内按 seq 升序。
194
+ 5. 如果响应字节上限要求裁剪,只能从左侧丢弃较旧消息,并同步右移 `window_start_seq`。
195
+ 6. Tail 不得推进 ACK、visibility floor、retention floor、Pull activity 或 catch-up 状态。
196
+ 7. Tail 不能用第一条返回消息推算 `window_start_seq`;窗口边界由服务端固定快照和 seq band 决定。
197
+ 8. 页内未返回的 seq 属于服务端永久空号或不可见项,客户端不得据此创建 Gap 或猜测更窄边界。
198
+ 9. Tail 请求不得携带 piggyback ACK;`force=true`与 `window_mode=tail`组合必须返回参数错误,避免误走旧推进路径。
199
+
200
+ SDK 只有在普通 Forward 请求中才接受完全不含 `window_mode` 的普通响应。Tail 请求的响应必须回显 `window_mode=tail`,并在提交前至少校验:`0<=Ws<=Hs`、`covered_through_seq==Hs`、`Hs-Ws+1<=P`、所有消息 seq 都落在 `[Ws,Hs]`、页内无重复 seq 冲突且可排序为 ASC。Tail 响应未回显模式,或 W/Hs/covered 证明字段缺失、非法时均属于协议错误,不得降级为 Forward。
201
+
202
+ Message 与 Group 的 V1/V2 Pull 都必须实现相同 Tail 契约。尤其 Group V2 Tail 必须绕过现有“根据第一条可见消息推进 visibility floor/ACK”的路径;Tail 响应中的 W/Hs不允许写入`last_ack_msg_seq`。
203
+
204
+ 兼容边界:新服务端必须保持无 `window_mode` 的旧 Pull 请求和响应语义,确保旧 SDK 正常工作。新 SDK 不兼容旧服务端:Tail 缺少 `window_mode=tail` 回显或证明字段时直接返回明确协议错误,不降级为 Forward;History 不存在时返回明确 unsupported/method-not-found,且不修改 A/T/H。
205
+
206
+ ### 5.3 History RPC
207
+
208
+ 新增:
209
+
210
+ - `message.history`
211
+ - `group.history`
212
+
213
+ 请求:
214
+
215
+ | 字段 | 语义 |
216
+ | --- | --- |
217
+ | `before_seq` | 必填正整数,排他上界;需要最新历史页时使用已知 `head_seq+1`或本地最早消息 seq |
218
+ | `limit` | 最大返回条数 |
219
+ | `group_id` | 仅 Group History 必填 |
220
+
221
+ 响应:
222
+
223
+ | 字段 | 语义 |
224
+ | --- | --- |
225
+ | `messages` | 页内按 seq 升序 |
226
+ | `window_start_seq` | 本页实际最小边界,也是下一页的排他上界;空页时为空 |
227
+ | `next_before_seq` | 下一页直接使用的排他上界;没有更早消息时为空 |
228
+ | `has_older` | 服务端是否仍有更早的可见消息 |
229
+ | `retention_floor_seq` | 当前实际不可恢复下界 |
230
+ | `earliest_available_seq` | 当前最早可拉取边界;无数据时可空 |
231
+
232
+ History 硬约束:
233
+
234
+ - 不读取或修改 SDK SeqTracker。
235
+ - 不推进服务端 ACK、cursor、floor 或 activity。
236
+ - 不触发 `message.received`、`group.message_created` 等实时事件。
237
+ - SDK只完成校验和解密并把结果返回调用方;是否写入业务仓库由应用决定。
238
+ - 相邻页面只依赖响应的 `next_before_seq`,不得由客户端对首条 seq 自行加减猜测。
239
+ - Group History 必须合并当前设备V2 inbox、明文broadcast、WAL/pending和迁移期旧表;不得直接暴露只查询明文的现有内部history helper。
240
+
241
+ ### 5.4 Retention floor 的唯一语义
242
+
243
+ `retention_floor_seq=F`只表示:对该接收方而言,所有 `seq <= F` 中原本可恢复的数据已经由 GC 实际删除,因此不再返回。
244
+
245
+ 禁止把以下值当作 F:
246
+
247
+ - TTL 时间计算出的候选 seq。
248
+ - 本次 Pull 第一条返回消息之前的 seq。
249
+ - visibility 过滤形成的空洞。
250
+ - 当前 ACK、Tail 的 T/H 或 `next_seq-1`。
251
+ - 尚未提交或只删除了部分批次的 GC 候选上界。
252
+
253
+ GC 必须在同一事务或等价原子清单中完成“删除实际记录 + 推进对应 floor”。事务失败时两者都不生效。Group 入群点、epoch 和权限属于可见性规则,不得伪装成“已经删除”的 retention floor;普通 Group Forward 可以通过独立的 `join_seq` 与 `earliest_available_*-1` 证明该成员的永久不可读前缀并推进 A。
254
+
255
+ ## 6. 发布、去重与排序语义
256
+
257
+ 原 Forward 单通道可以提供连续有序处理;加入 Push、Tail Sync 和 History 后,协议改为至少一次、多通道可能乱序:
258
+
259
+ - 每个 Pull/Sync/History 响应页内部按 seq 升序。
260
+ - Push、Sync、Forward 之间不承诺全局回调顺序。
261
+ - 同一消息可能从 Push、Sync、Forward 重复到达。
262
+ - History 不发布实时事件,但应用把结果写入同一仓库时仍会和实时结果重叠。
263
+
264
+ 应用层最低要求:
265
+
266
+ 1. 按 `(namespace, message_id)` 幂等。
267
+ 2. 按 `(namespace, seq)` 保证同一序号不会保存冲突消息。
268
+ 3. 查询和展示时按 seq 排序,而不是依赖 SDK 回调到达顺序。
269
+ 4. 同一 seq 对应不同 message_id,或同一 message_id 内容冲突时,记录并拒绝静默覆盖。
270
+
271
+ 现有 SDK `pushed_seqs`、有序队列和内存去重只能作为 best-effort 性能优化,不能继续写成协议级 unique ordered publish 保证。全局去重与排序是应用业务仓库职责,不再作为 SDK 级业务逻辑要求。
272
+
273
+ ## 7. 并发、异常与崩溃语义
274
+
275
+ ### 7.1 SDK 并发边界
276
+
277
+ - Pull Gate 按游标状态域拆成三个:P2P Message、Group Message、Group Event 各共用一个;每个 Gate 同时最多一个受控 RPC 在途。
278
+ - Gate 内同 key 请求折叠共享结果,不同 key 排队;前台队列和后台队列各自保持 FIFO,调度顺序固定为前台优先。
279
+ - Tail/History 是前台 Pull,`_rpc_background=false`;Forward/Gap Fill 是后台 Pull,`_rpc_background=true`。
280
+ - 排队或等待同 key 折叠结果超过 3 秒时,请求从 Gate 摘出并旁路执行,避免慢请求造成全局饥饿;旁路不取消原请求。
281
+ - 每个 namespace 仍串行提交 A/T/H;多个 Push 合并为最高 M,Tail 完成后重新检查 M。
282
+ - Group Event 使用独立的第三个 Gate 和 Forward Cursor,不进入消息 A/T/H 协调器,也不与 Group Message 竞争同一 Gate。
283
+ - 状态提交必须校验当前 AID、device、slot 和本地存储代际,旧任务不得写入新会话。
284
+
285
+ ### 7.2 提交顺序
286
+
287
+ Tail:
288
+
289
+ ```text
290
+ 校验响应与权威floor -> 锁内按最新状态重算 -> 原子保存完整A/T/H -> 解密/发布并逐项诊断 -> A增长时ACK A
291
+ ```
292
+
293
+ Forward:
294
+
295
+ ```text
296
+ 校验服务端原始页 -> best-effort解密/发布并逐项诊断 -> 以原始页最大seq计算新A/T/H -> 原子保存三元组 -> ACK A
297
+ ```
298
+
299
+ History:
300
+
301
+ ```text
302
+ 校验响应 -> 解密 -> 返回调用方
303
+ ```
304
+
305
+ 水位由服务端原始页证明决定,不以应用是否成功解密或处理为条件。原始页通过校验但单条解密/回调失败时仍提交水位并记录失败项,避免永久坏密文卡住 A/T/H;持久化失败则回滚内存水位。水位落盘后 ACK 响应丢失时按原 ACK 幂等重试。Tail 原始证明失败但权威 floor 独立验证成功时,可在锁内只应用 floor、归一化并原子保存 A/T/H;若 A 增长,仍需单独 ACK。
306
+
307
+ 冷启动缺 sender IK 是可恢复的临时解密条件,不应直接把当前消息丢给后台。四端 SDK 先查 session/PKI,本地未命中时按发送端 single-flight 合并,最多同步等待 3 秒执行 `message.v2.bootstrap`;群消息仍未命中时再执行 `group.v2.bootstrap`。获取成功立即重试当前消息解密;失败或超时才进入实时 pending,History 只返回结构化失败项。等待者取消不得取消共享 bootstrap,共享任务完成后必须清理 single-flight 槽位。该补救只增加首条冷消息的有界延迟,不改变永久坏密文按原始页证明推进水位的规则。
308
+
309
+ Forward Piggy ACK 保持原行为:有待提交 ACK 时将其带入下一页 Pull。即使当前页少于 limit,也继续再拉一页;下一页为空时完成 piggy ACK 并停止。无待提交 ACK 的非满页可直接停止,且 `max_pages` 仍是最终保护上限。
310
+
311
+ ### 7.3 失败分类
312
+
313
+ | 失败点 | 状态结果 |
314
+ | --- | --- |
315
+ | Tail RPC 超时/断线 | A/T/H 不变,保留最高 M,退避重试 |
316
+ | Tail 响应完全没有window_mode字段 | 协议错误;不得降级为 Forward,不修改本页 T/H |
317
+ | 已回显window_mode=tail但证明字段缺失或非法 | 协议错误;不得按Forward推进,不提交本页T/H;独立验证的权威floor可单独生效 |
318
+ | Tail 单条解密/发布失败 | 提交已验证的 T/H;记录失败 seq 与阶段,继续处理后续消息 |
319
+ | Forward 单条解密/发布失败 | 原始页有效时 A 仍推进到页内最大 seq;失败项单独诊断 |
320
+ | 缺 sender IK | 最多 3 秒 single-flight 同步 bootstrap;成功立即重试当前消息,失败或超时才 pending,水位仍按原始页证明提交 |
321
+ | Forward RPC/响应校验/持久化失败 | 该页不推进 A,保留原状态等待重试 |
322
+ | History 失败 | 只向调用方返回错误,不修改同步状态 |
323
+ | floor/GC 读取失败 | 服务端返回可重试错误,禁止默认成 0 或推算值 |
324
+ | 迟到 Sync/Forward 响应 | 只能单调合并,不能回退或创建第二个 Gap |
325
+
326
+ ### 7.4 诊断日志计划
327
+
328
+ 实现阶段在现有 AUNLogger 中增加结构化诊断,不使用临时 `print`:
329
+
330
+ - SDK:namespace、触发原因、A/T/H/M 旧值和新值、RPC 模式、请求 head、响应 head、窗口起点、是否合并、耗时和错误分类。
331
+ - 服务端:请求模式、固定 head、实际 floor、查询 band、返回条数、字节裁剪、窗口起点、ACK/floor 是否保持不变。
332
+ - GC:候选范围、实际删除范围、事务结果、floor 旧值和新值。
333
+ - 日志不得输出消息明文、密钥、完整密文或身份私钥材料。
334
+
335
+ ## 8. 持久化与恢复
336
+
337
+ ### 8.1 内置存储
338
+
339
+ Python、TypeScript 和 Go 的现有 `seq_tracker` 行增加:
340
+
341
+ - `tail_seq NOT NULL DEFAULT 0`
342
+ - `head_seq NOT NULL DEFAULT 0`
343
+
344
+ JavaScript IndexedDB 的原记录增加同名可选字段。A/T/H 应由内置存储以一次原子写保存。
345
+
346
+ 不修改现有 TokenStore `saveSeq/loadSeq/loadAllSeqs` 必选接口。新增可选窗口扩展接口;旧自定义 Store 没有扩展接口时只保存 A,重启后折叠窗口。
347
+
348
+ ### 8.2 恢复规则
349
+
350
+ 恢复判断顺序固定为:先处理旧字段缺失,再校验A/T/H取值是否合法,最后判断保留Gap还是合并窗口。后续规则不得先于非法状态校验执行。
351
+
352
+ | 持久化值 | 恢复结果 |
353
+ | --- | --- |
354
+ | 旧行缺少 T/H | `T=H=A` |
355
+ | A为负数或越界 | 不采信任何水位,恢复为`A=T=H=0`并记录ERROR |
356
+ | A合法,但T/H为负数、越界或`H<T` | 丢弃窗口,`T=H=A`并记录WARN |
357
+ | `0<=A<T-1`且`T<=H` | 保留窗口 |
358
+ | `0<=T<=H`且`A>=T-1` | 合并为`A=T=H=max(A,H)` |
359
+ | 旧 SDK 覆盖了 JS 记录中的 T/H | 新 SDK按缺失窗口恢复,重新 Sync |
360
+ | floor clamp 后 `F>H` | 折叠为 `A=T=H=F` |
361
+
362
+ 该策略优先保证不遗漏。窗口丢失最多导致重复 Sync/Forward,不允许利用残缺状态跳过消息。
363
+
364
+ ## 9. 新老版本兼容矩阵
365
+
366
+ | SDK | 服务端 | 行为 |
367
+ | --- | --- | --- |
368
+ | 旧 SDK | 旧服务端 | 当前 Forward/ACK 行为 |
369
+ | 旧 SDK | 新服务端 | 无 `window_mode`,请求与响应保持旧形状 |
370
+ | 新 SDK | 旧服务端 | 不保证兼容;Tail/History 返回明确协议错误或 unsupported,不回退为 Forward |
371
+ | 新 SDK | 新服务端 | 新 Head 一律先 Tail,仍有 Gap 时后台 Forward 补齐 |
372
+ | 新 SDK + 旧自定义 Store | 任意服务端 | 运行期支持窗口;重启后折叠到 A并重新同步 |
373
+ | 新 SDK 降级到旧 SDK后再升级 | 新服务端 | 校验持久化状态;无有效窗口时折叠,不采信陈旧 T/H |
374
+
375
+ 旧 SDK 的 Forward Cursor、ACK 和服务端 GC clamp 必须继续工作。新字段只能是可选增量,不能成为旧请求的必填项。
376
+
377
+ ## 10. 最小实施范围
378
+
379
+ ### 10.1 Message 服务端
380
+
381
+ Python 重点位置:
382
+
383
+ - `../extensions/services/message/entry.py`
384
+ - `../extensions/services/message/db.py`
385
+
386
+ Go 重点位置:
387
+
388
+ - `../extensions/services/message/go/internal/p2p/pull.go`
389
+ - `../extensions/services/message/go/internal/legacy/service.go`
390
+ - `../extensions/services/message/go/internal/store/message_store.go`
391
+ - `../extensions/services/message/go/cmd/message-go/main.go`
392
+
393
+ 实施内容:原 Pull 增加 Tail 分支、History 只读查询、删除 Pull 侧 floor 推断、GC 删除与 floor 原子推进。
394
+
395
+ ### 10.2 Group 服务端
396
+
397
+ Python 重点位置:
398
+
399
+ - `../extensions/services/group/service.py`
400
+ - `../extensions/services/group/message_store.py`
401
+ - `../extensions/services/group/entry.py`
402
+
403
+ Go 重点位置:
404
+
405
+ - `../extensions/services/group/go/internal/service/pull_messages.go`
406
+ - `../extensions/services/group/go/internal/service/v2_pull.go`
407
+ - `../extensions/services/group/go/internal/repository/`
408
+ - `../extensions/services/group/go/internal/rpc/`
409
+
410
+ 实施内容:`group.pull/group.v2.pull`同时支持Message Tail,新增完整覆盖plain/V2/WAL/旧表的History,Tail禁止visibility-floor/ACK推进,收口真实GC floor;`group.pull_events/group.ack_events/last_ack_event_seq/group.changed`保持不动,也不增加Event History。
411
+
412
+ ### 10.3 Gateway
413
+
414
+ Tail 继续路由现有 Pull,不增加 Sync method allowlist。仅为以下新方法增加透明路由和能力声明:
415
+
416
+ - `message.history`
417
+ - `group.history`
418
+
419
+ Python 与 Go Gateway 不生成、推算、裁剪或改写窗口和 floor 字段。History还必须补齐session-auth只读方法集合、服务attach广告/快照、消息诊断分类和RPC response proximity装饰;不得列入V2-only或写方法签名集合。Group History按现有group_id跨域规则路由,Python实现必须复用当前Group Pull的federation转发边界。
420
+
421
+ ### 10.4 四 SDK
422
+
423
+ 共同改造点:
424
+
425
+ - SeqTracker 增加 T/H 和窗口合并原语。
426
+ - 内置存储增加兼容字段和原子窗口保存。
427
+ - P2P/Group Push 接入同一个实时窗口协调器。
428
+ - Tail/History 复用解密逻辑,但禁用 Forward 游标推进和自动 ACK。
429
+ - Group Event 保持原 RPC、Forward Cursor 与 ACK 实现,仅将 Pull 调度接入独立第三 Gate。
430
+ - Message/Group facade 增加 History 薄封装;Sync 作为内部调度能力,可按现有 API 风格决定是否公开显式入口。
431
+
432
+ 实施基准顺序:Python -> TypeScript -> Go -> JavaScript。非 Python 端以 Python 状态向量和协议结果为基准,不复制语言特有实现。
433
+
434
+ ## 11. TDD 测试用例
435
+
436
+ ### 11.1 共同状态机向量
437
+
438
+ | ID | 初态与动作 | 核心断言 |
439
+ | --- | --- | --- |
440
+ | SM-01 | `A=100,T=H=100,P=50,S=150` | 只调用 Tail;Tail 后再按新 A/T/H 决定是否 Forward |
441
+ | SM-02 | `A=100,T=H=100,P=50,S=151` | Tail 必须先于 Forward |
442
+ | SM-03 | `A=T=H=100`,V2 Push通知`S=101` | Push正文不旁路提交;先调用Tail |
443
+ | SM-03A | legacy inline Push正文落在Gap内 | 可提前发布,但T/H不变,继续Forward |
444
+ | SM-03B | `A=100,T=H=100,S=99` | 不改状态;最多重试ACK |
445
+ | SM-03C | legacy inline Push正文`S=101`处理成功 | A推进到101,T/H保持900/1000;未接壤前不得折叠窗口 |
446
+ | SM-04 | `A=100,T=900,H=1000,S=950` | 不再 Tail;窗口不变 |
447
+ | SM-05 | `oldT=900,oldH=1000,P=50,Hs=1050,Ws=1001` | 保留 T=900,H=1050 |
448
+ | SM-06 | `oldT=900,oldH=1000,P=50,Hs=1051,Ws=1002` | T 右移为1002,H=1051 |
449
+ | SM-07 | 算术预计接壤,但字节裁剪后 `Ws>oldH+1` | 以 Ws 为准右移 T |
450
+ | SM-08 | `A=T-3`,Forward成功推进一位到`T-2` | 不合并窗口 |
451
+ | SM-09 | `A`推进到`T-1` | 一次合并到 H并折叠 A=T=H |
452
+ | SM-10 | floor 将 A推进到`T-1` | 与 Forward 相同地合并窗口 |
453
+ | SM-11 | floor `F>H` | 折叠为 A=T=H=F |
454
+ | SM-12 | H 已为1100,迟到 Sync 返回 Hs=1050 | T/H 不回退 |
455
+ | SM-13 | floor不变,Tail页第N条解密或发布失败 | 提交已验证的T/H,记录失败项,后续消息继续处理 |
456
+ | SM-13A | Tail页单条解密失败且响应携带更高的已提交F | 同时提交floor clamp与已验证T/H,统一归一化;A增长则单独ACK |
457
+ | SM-14 | Sync 在途期间收到多个 Push | M取最大值;同一时刻最多一个 Sync |
458
+ | SM-15 | Sync 返回 Hs 仍小于 M | 提交可靠页后继续调度,不宣称已追平 |
459
+ | SM-16 | floor不变,Tail返回`covered_through_seq!=Hs`、窗口跨度超过P或消息越界 | 协议错误;A/T/H不提交;不得降级为Forward |
460
+ | SM-17 | 多次相邻Tail使`H-T+1>P` | 允许累计窗口扩展,但仍只有单一[T,H]和单一Gap |
461
+
462
+ 上述向量在 Python、TypeScript、Go、JavaScript 四端使用相同 JSON 测试数据,防止边界判断漂移。
463
+
464
+ ### 11.2 Message 服务端 Tail
465
+
466
+ 计划文件:
467
+
468
+ - Python:`../extensions/services/message/tests/test_msg_tail_window.py`
469
+ - Go:`../extensions/services/message/go/internal/p2p/pull_test.go`及 legacy Pull 测试
470
+
471
+ | ID | 场景 | 核心断言 |
472
+ | --- | --- | --- |
473
+ | MT-01 | A远落后于head,Tail取最新一页 | 返回固定 head、可靠 window_start、页内ASC |
474
+ | MT-02 | Tail返回首条seq远大于A | server ACK/floor 不因首条消息推进 |
475
+ | MT-03 | DB、recent cache、legacy、WAL/pending重叠 | 按 seq/message_id 去重,无冲突静默覆盖 |
476
+ | MT-04 | 固定快照后并发写入更高seq | 本页不混入新写入,head保持请求快照 |
477
+ | MT-05 | seq band内部没有对应消息行 | 保持固定band边界;该缺号按服务端永久空号处理,不生成客户端Gap |
478
+ | MT-06 | 512KiB等字节上限触发裁剪 | 只丢左侧旧消息,window_start同步右移,head仍保留 |
479
+ | MT-07 | Tail请求执行前后检查ACK、floor、pull activity | 所有副作用计数为0 |
480
+ | MT-08 | 不带window_mode的旧Pull | 响应形状和游标行为与基线完全一致 |
481
+ | MT-09 | 非法window_mode/limit/after_seq | 固定错误码,无状态写入 |
482
+ | MT-10 | floor读取失败或查询超时 | 返回可重试错误,不以0或首条seq降级 |
483
+ | MT-11 | Tail请求携带piggyback ACK或force=true | 参数错误;ACK、floor、activity写入均为0 |
484
+
485
+ ### 11.3 Message History
486
+
487
+ 计划文件:
488
+
489
+ - Python:`../extensions/services/message/tests/test_msg_history.py`
490
+ - Go:Message handler/store 对应测试
491
+
492
+ | ID | 场景 | 核心断言 |
493
+ | --- | --- | --- |
494
+ | MH-00 | before缺失、非正整数或limit越界 | 参数错误,无任何游标副作用 |
495
+ | MH-01 | `before_seq=head+1` | 取得head之前最新一页,页内ASC |
496
+ | MH-02 | `before_seq=100` | 只返回`seq<100` |
497
+ | MH-03 | 连续翻两页 | 使用next_before_seq后无遗漏、无重复边界 |
498
+ | MH-04 | before已到floor | 空页且has_older=false |
499
+ | MH-05 | DB/cache/WAL重叠 | 结果去重且排序稳定 |
500
+ | MH-06 | History前后读取ACK、floor、activity | 全部不变 |
501
+ | MH-07 | History查询异常/超时 | 返回错误,不影响后续Forward |
502
+ | MH-08 | before范围内没有更早可见消息 | messages为空、window_start/next_before为空、has_older=false |
503
+
504
+ ### 11.4 Group Message Tail 与 History
505
+
506
+ 计划文件:
507
+
508
+ - Python:现有 pull metadata 测试加 Tail 用例,并新增 Group History 测试
509
+ - Go:`pull_messages_test.go`、`v2_pull_test.go`和新 History 测试
510
+
511
+ | ID | 场景 | 核心断言 |
512
+ | --- | --- | --- |
513
+ | GT-01 | `ack=100,head=10000`执行Tail | cursor.current_seq仍为100 |
514
+ | GT-02 | V2 Tail首条消息远高于ACK | 禁止 `_inbox_visibility_floor` 或等价逻辑推进ACK |
515
+ | GT-03 | plain与V2 inbox混合 | 同一窗口统一去重、ASC返回 |
516
+ | GT-04 | 响应字节裁剪 | 保留最新后缀并右移window_start |
517
+ | GT-05 | 固定head后并发群消息写入 | 新写入不污染当前页 |
518
+ | GT-06 | History before排他连续翻页 | 无边界遗漏,页内ASC |
519
+ | GT-07 | 入群点/epoch/权限过滤 | 只影响可见性,不伪造retention floor |
520
+ | GT-08 | Tail/History前后检查消息ACK | `last_ack_msg_seq`不变 |
521
+ | GT-09 | 执行所有Group Event回归 | pull_events/ack_events请求、响应和调用次数不变 |
522
+ | GT-10 | 分别调用group.pull与group.v2.pull Tail | 两条路径返回同一边界语义,均不跨洞ACK |
523
+
524
+ ### 11.5 GC 与 floor 根因测试
525
+
526
+ Message、Group 的 Python/Go 都必须覆盖:
527
+
528
+ | ID | 场景 | 核心断言 |
529
+ | --- | --- | --- |
530
+ | GC-01 | TTL到期但GC尚未执行 | floor不推进,Pull仍可返回消息 |
531
+ | GC-02 | GC事务删除连续前缀并提交 | 删除结果与floor同时可见 |
532
+ | GC-03 | 删除成功前事务回滚 | 消息与floor都保持原值 |
533
+ | GC-04 | 批次只删除候选区间一部分 | floor最多推进到实际连续删除边界 |
534
+ | GC-05 | 较低seq仍存活但更高seq过期 | 单一floor不能跨过存活seq |
535
+ | GC-06 | Pull返回首条消息形成数值gap | Pull不得据此推进floor |
536
+ | GC-07 | Python/Go相同数据库初态 | 实际删除集合与最终floor完全一致 |
537
+ | GC-08 | GC与Tail/Forward并发 | 响应使用事务前或事务后的一致快照,不出现半删除边界 |
538
+
539
+ ### 11.6 SDK Tail/Forward 协调
540
+
541
+ 四端共同覆盖:
542
+
543
+ | ID | 场景 | 核心断言 |
544
+ | --- | --- | --- |
545
+ | SDK-01 | 任意新Head收到Push | 首个RPC必须为Tail |
546
+ | SDK-02 | Tail后仍有Gap | 再执行一页后台Forward;已接壤则不执行Forward |
547
+ | SDK-03 | Tail成功,提交时A仍未接壤且floor不变 | 页内ASC处理;A不变;完整A/T/H原子提交 |
548
+ | SDK-03A | Tail成功且响应F提高 | clamp A后与可靠T/H统一归一化并原子提交;W/Hs不直接跨Gap推进A;A增长则ACK |
549
+ | SDK-03B | Tail在途期间Forward已推进到T-1 | 提交时读取最新状态,合并到新H并原子保存A/T/H,再ACK新的A |
550
+ | SDK-04 | Tail响应完全没有window_mode字段 | 协议错误,不按Forward处理,不能建立窗口 |
551
+ | SDK-04A | 已回显tail但W/Hs/covered缺失或非法 | 协议错误,不按Forward推进,不提交本页T/H |
552
+ | SDK-05 | 旧服务拒绝window_mode | 返回明确错误,不重试Forward,不修改A/T/H |
553
+ | SDK-06 | Tail页重复包含已Push消息 | 允许重复发布;水位正确 |
554
+ | SDK-07 | Forward多页追到T-1 | 每页按原策略ACK已提交A;接壤页合并到H后ACK新的A,绝不提前ACK H |
555
+ | SDK-08 | Forward中途超时后重试 | 已提交页面不回退,最终追平 |
556
+ | SDK-09 | Forward在Tail返回前追到旧T | Tail提交基于当前状态重算,不产生第二Gap |
557
+ | SDK-10 | Tail返回后立即再来更高Push | 复用最高M继续调度,T/H单调 |
558
+ | SDK-11 | Tail窗口证明已验证,但原子保存A/T/H前崩溃 | 重连后允许重复Tail,不遗漏;保存成功后才处理逐条交付 |
559
+ | SDK-12 | 保存T/H后、Forward前崩溃 | 重启恢复Gap并继续Forward |
560
+ | SDK-13 | Forward发布后、保存A前崩溃 | 重启允许重复Forward,不跨洞 |
561
+ | SDK-14 | 保存A后、ACK响应前崩溃 | 按原ACK幂等恢复 |
562
+ | SDK-15 | namespace/AID/slot代际切换 | 旧任务不能写入新状态 |
563
+ | SDK-16 | A/T/H原子保存注入中断 | 恢复只能看到旧三元组或新三元组,不出现撕裂状态 |
564
+ | SDK-17 | Forward原始页页内缺号 | 以原始页最大seq推进A,不再为页内缺号发起Gap Fill |
565
+ | SDK-18 | Forward/Tail单条解密或回调失败 | 水位按原始页证明推进,失败项有诊断且不阻塞后续消息 |
566
+ | SDK-18A | 并发冷启动消息缺同一 sender IK | 只发起一次有界同步 bootstrap;成功后当前消息直接解密,不进入 pending |
567
+ | SDK-18B | sender IK bootstrap 超时或等待者取消 | 超时后进入 pending;取消不终止共享任务,任务完成后 single-flight 槽位清空 |
568
+ | SDK-19 | 同一Gate不同key并发Pull | 前台队列优先、各队列FIFO、同key折叠、Gate内最多一个受控RPC在途 |
569
+ | SDK-20 | Pull排队或折叠等待超过3秒 | 请求旁路Gate独立执行,原请求不被取消 |
570
+ | SDK-21 | Forward非满页且有待piggy ACK | 再拉一页并携带ACK,空页后停止 |
571
+
572
+ ### 11.7 SDK 持久化兼容
573
+
574
+ | ID | 场景 | 核心断言 |
575
+ | --- | --- | --- |
576
+ | PS-01 | 从只有contiguous_seq的旧行升级 | T=H=A |
577
+ | PS-02 | 恢复合法A/T/H | 保留唯一Gap |
578
+ | PS-03 | 恢复时A已到T-1 | 自动合并到H |
579
+ | PS-04 | A合法,但H<T、T/H负数/越界或字段损坏 | 保留A,折叠T=H=A并记录WARN |
580
+ | PS-04A | A为负数或越界 | 不采信任何水位,恢复A=T=H=0并记录ERROR |
581
+ | PS-05 | 新SDK写入后由旧SDK推进A,再升级 | 校验并安全合并或折叠,不回退A |
582
+ | PS-06 | JS旧SDK覆盖IndexedDB记录导致T/H丢失 | 新SDK折叠到A并重新Tail |
583
+ | PS-07 | 自定义旧TokenStore无窗口接口 | 运行期正确,重启只损失优化不遗漏消息 |
584
+ | PS-08 | 原子窗口保存失败 | A/T/H内存状态不得伪装成已持久化状态 |
585
+
586
+ ### 11.8 History SDK 无副作用
587
+
588
+ | ID | 场景 | 核心断言 |
589
+ | --- | --- | --- |
590
+ | HS-01 | 调用P2P History | 返回解密结果;SeqTracker/persist/ACK/实时事件均0次 |
591
+ | HS-02 | 调用Group History | 同上,并保持Group Event状态不变 |
592
+ | HS-03 | History与实时Sync返回同一消息 | 应用幂等仓库最终只有一条 |
593
+ | HS-04 | History密文无法解密 | 返回逐项错误或结构化失败,不改变实时pending状态 |
594
+ | HS-05 | 连续before翻页 | next_before_seq可直接复用,无漏页 |
595
+
596
+ ### 11.9 发布语义测试
597
+
598
+ | ID | 场景 | 核心断言 |
599
+ | --- | --- | --- |
600
+ | PUB-01 | Push先到、Sync后到、Forward最终到 | SDK允许三次交付;应用幂等后唯一 |
601
+ | PUB-02 | 不同通道故意打乱完成顺序 | SDK不宣称全局顺序;应用查询按seq有序 |
602
+ | PUB-03 | 同seq不同message_id | 应用仓库检测冲突,不静默覆盖 |
603
+ | PUB-04 | History先写入、实时消息后到 | 幂等合并结果一致 |
604
+
605
+ ### 11.10 兼容矩阵测试
606
+
607
+ | ID | 组合 | 核心断言 |
608
+ | --- | --- | --- |
609
+ | CP-01 | 旧SDK + 新Python服务 | 旧Pull/ACK响应和游标不变 |
610
+ | CP-02 | 旧SDK + 新Go服务 | 同CP-01 |
611
+ | CP-03 | 新SDK + 旧服务 | 明确不兼容;Tail失败不回退Forward,A/T/H不损坏 |
612
+ | CP-03A | 新SDK调用旧Gateway或旧服务的History | 明确unsupported,A/T/H、ACK和实时事件均不变 |
613
+ | CP-04 | 新SDK + 新服务 | 大Gap先Tail,最终Forward合并 |
614
+ | CP-05 | Python服务与Go服务同一向量 | 边界字段、消息集合、floor和错误码一致 |
615
+ | CP-06 | 四SDK同一状态向量 | RPC顺序与最终A/T/H一致 |
616
+ | CP-07 | Group Event旧请求全集 | 字节级或结构级基线不变 |
617
+
618
+ ### 11.11 Gateway 路由与透明性
619
+
620
+ | ID | 场景 | 核心断言 |
621
+ | --- | --- | --- |
622
+ | GW-01 | Message/Group Tail经过Gateway | window_mode、after_seq、limit和边界字段原样透传 |
623
+ | GW-02 | 调用message.history/group.history | session-auth按只读消息方法校验,服务端收到短方法history |
624
+ | GW-03 | 服务实例未声明history | 返回明确method_not_declared/Method not found,不误路由旧实例 |
625
+ | GW-04 | 新旧服务实例混部 | History只选声明能力的新实例;普通Pull仍可选兼容实例 |
626
+ | GW-05 | 服务超时、断连或draining | 沿用既有Gateway错误;Gateway不改写状态或伪造窗口 |
627
+ | GW-06 | 超时后的迟到响应 | 响应被丢弃;只读服务端无ACK副作用 |
628
+ | GW-07 | proximity/诊断装饰响应 | 只修改消息元数据,不改window_start/head/next_before/floor |
629
+ | GW-08 | Group History跨域 | 按group_id使用现有federation路由,边界字段完整返回 |
630
+ | GW-09 | 旧Gateway或旧服务不认识History | 返回明确unsupported;不得fallback为Forward或改动A/T/H |
631
+
632
+ ### 11.12 诊断日志与敏感信息
633
+
634
+ | ID | 场景 | 核心断言 |
635
+ | --- | --- | --- |
636
+ | LOG-01 | SDK完成Tail、Forward、合并和失败重试 | 日志包含namespace、原因、A/T/H/M旧新值、模式、耗时和错误分类 |
637
+ | LOG-02 | 服务端处理Tail/History | 日志包含请求边界、stored floor、固定head、window start、来源计数、bytes和截断结果 |
638
+ | LOG-03 | GC提交与回滚 | 日志包含候选范围、实际删除范围、floor旧新值和commit/rollback |
639
+ | LOG-04A | 扫描器输入已知泄漏样本 | 必须准确检出消息明文、私钥、seed、完整密文和认证令牌 |
640
+ | LOG-04B | 扫描GC提交与回滚日志 | 不包含任何敏感字段,且保留定位删除范围所需元数据 |
641
+ | LOG-04C | 扫描Tail/History/Gateway日志 | 不包含任何敏感字段,且保留边界、来源计数和错误分类 |
642
+ | LOG-04D | 扫描四SDK协调器日志 | 不包含任何敏感字段,且保留namespace、水位变化和失败阶段 |
643
+ | LOG-04E | 扫描单域/双域E2E收集的全链路日志 | 所有服务端与SDK日志均通过同一规则集 |
644
+
645
+ ### 11.13 集成与 E2E 场景
646
+
647
+ | ID | 场景 | 验收结果 |
648
+ | --- | --- | --- |
649
+ | E2E-01 | 离线积压超过三页后收到新Push | 最新页先可见,随后后台最终补齐 |
650
+ | E2E-02 | Sync后、Gap未补齐时重启 | 恢复A/T/H并继续Forward |
651
+ | E2E-03 | 重启后再收到跨一页的新Push | T按新可靠窗口右移,仍只有一个Gap |
652
+ | E2E-04 | Gap Fill期间发生真实GC | SDK按服务端floor clamp并正确合并或续补齐 |
653
+ | E2E-05 | History与实时同步并发 | 最终业务仓库集合完整、幂等、按seq可排序 |
654
+ | E2E-06 | P2P、多个Group Message与Group Event同时拉取 | P2P Message、Group Message、Group Event三个Gate互不阻塞;各Gate内同key折叠、不同key按优先级和FIFO排队,游标不串线 |
655
+ | E2E-07 | 跨域P2P/Group History | Gateway透明路由,边界字段不丢失 |
656
+ | E2E-08 | Python/TS/Go/JS依次连接同一服务端 | 四端最终状态和消息集合一致 |
657
+
658
+ ## 12. 分阶段实施计划
659
+
660
+ ### Phase 0:冻结基线
661
+
662
+ 1. 保存旧 Pull/ACK、Group Event 和新老兼容的契约快照。
663
+ 2. 先运行现有针对性单测,确认 HEAD 基线问题与预期一致。
664
+ 3. 将 CP-01、CP-02、CP-07 建成基线绿测,防止后续误改旧语义。
665
+ 4. 先写LOG-04A红测和已知泄漏样本,再实现敏感字段扫描门禁直到准确检出样本并转绿;同时建立共享契约向量和故障注入夹具。
666
+ 5. 本阶段只冻结基线和测试基础设施,不提前编写Phase 1/2的行为红测,避免同一用例重复定义首次Red时机。
667
+
668
+ 验收:旧语义冻结测试保持绿色;共享向量、故障注入和敏感字段扫描门禁可复用,后续每类行为用例仍未被实现代码提前满足。
669
+
670
+ ### Phase 1:收口真实 GC floor
671
+
672
+ 1. 先写 GC-01 至 GC-08、LOG-03和LOG-04B红测。
673
+ 2. 删除 Message/Group Pull 侧 TTL、首条消息和 visibility gap 推断。
674
+ 3. 将实际删除与 floor 推进收进同一事务。
675
+ 4. 对齐 Python/Go 实现、错误语义和GC提交/回滚诊断日志。
676
+
677
+ 验收:只有实际删除会改变 floor;Pull只读持久化 floor。
678
+
679
+ ### Phase 2:服务端 Tail 与 History
680
+
681
+ 1. 先写 MT、MH、GT、GW-01 至 GW-09、CP-05、LOG-02和LOG-04C红测。
682
+ 2. 在原 Pull 增加无副作用 Tail 分支。
683
+ 3. 新增 Message/Group History 只读 RPC。
684
+ 4. Gateway Python/Go补齐History透明路由、session-auth、服务广告、诊断分类和跨域转发,不新增Tail方法。
685
+
686
+ 验收:Tail能返回可靠窗口,W/Hs不推进任何ACK;只有已提交权威floor可沿用原clamp语义。History无副作用;旧Pull不变。
687
+
688
+ ### Phase 3:SeqTracker 与持久化原语
689
+
690
+ 1. 先用共同状态向量实现 SM、PS 红测。
691
+ 2. Python实现A/T/H状态与内置存储迁移。
692
+ 3. 依次对齐TypeScript、Go、JavaScript。
693
+ 4. 自定义旧Store走明确降级路径。
694
+
695
+ 验收:四端对相同输入得到相同A/T/H,旧数据可无损升级。
696
+
697
+ ### Phase 4:SDK实时窗口协调器
698
+
699
+ 1. 先写 SDK-01 至 SDK-16及全部子编号、HS-01至HS-05、PUB-01至PUB-04、CP-03/CP-03A/CP-04/CP-06、LOG-01和LOG-04D红测。
700
+ 2. 抽取Tail/History无游标副作用的解密处理路径。
701
+ 3. 接入P2P和Group Message Push调度。
702
+ 4. Group Event保持独立Forward Cursor,不进入消息A/T/H协调器;其Pull进入独立的第三个Gate并作为后台请求排队。
703
+ 5. 加入结构化诊断日志。
704
+
705
+ 验收:任意新Head最新页优先;后台Forward最终合并;永久页内Gap与坏密文不阻塞水位;异常和迟到响应不回退状态。
706
+
707
+ ### Phase 5:应用语义与手册
708
+
709
+ 1. 复验Phase 4已经转绿的PUB、HS用例,以测试结果冻结应用与SDK文档契约;本阶段不新增同步行为实现。
710
+ 2. 更新 SDK 核心概念、最佳实践、Message/Group RPC Manual。
711
+ 3. 明确至少一次、跨通道乱序、应用幂等与按seq排序责任。
712
+ 4. 同步生成目录到 SDK skill,避免直接手改生成副本。
713
+
714
+ 验收:文档不再承诺全局 unique ordered publish,示例采用幂等仓库语义。
715
+
716
+ ### Phase 6:集成、E2E 与跨语言验收
717
+
718
+ 1. 服务端代码改动后 rebuild 镜像并 `--force-recreate`。
719
+ 2. 先跑服务端 Python/Go针对性单测和契约向量。
720
+ 3. 四 SDK 按 Python -> TypeScript -> Go -> JavaScript 串行验证。
721
+ 4. 再完整复验CP-01至CP-07(含CP-03A),并执行E2E-01至E2E-08的Green-only组合验收;若失败,先在归属组件补出可稳定复现的红测,再修复根因并重跑E2E。
722
+ 5. 执行LOG-04E全链路Green-only敏感字段扫描,并汇总确认LOG-01/LOG-02/LOG-03及LOG-04A至LOG-04E全部通过;若失败,同样先下沉为对应日志路径的红测。
723
+ 6. 每轮测试结束后重新完整读取 `docs/aun测试运行指南.md`。
724
+
725
+ 验收:E2E-01 至 E2E-08通过,新老兼容矩阵通过,固定身份和持久化测试数据未被修改或删除。
726
+
727
+ ## 13. 实施纪律与停止条件
728
+
729
+ - 每个包含行为实现或修复的阶段必须先出现能够证明缺陷的红测,再写实现;纯文档阶段只复验已转绿契约。
730
+ - 每个行为测试ID只允许有一个首次Red归属阶段;后续阶段只能作为回归复验,不能重新定义其契约。E2E-01至E2E-08与LOG-04E是组合层Green-only验收门禁,不对应新行为实现;一旦失败,必须先在归属组件新增或定位下层红测,再修复。
731
+ - Python/Go服务端使用同一契约向量;非Python SDK以Python结果为基准。
732
+ - 同时修复一个SDK问题时,必须检查其他三端是否有同类路径。
733
+ - 服务端异常、超时、数据库回滚、并发GC和迟到响应必须有诊断日志和明确断言。
734
+ - 禁止通过扩大ACK、强制跳过Gap或客户端持久化floor来让测试“通过”。
735
+ - 禁止删除测试AID、密钥、证书、种子或SQLCipher数据库。
736
+ - 禁止运行`python main.py`;需要Kite整体测试时由用户手动启动。
737
+ - 任一阶段发现必须改变已确认的A/T/H模型、Tail协议或旧SDK兼容语义时,停止实现并重新确认方案。
738
+
739
+ ## 14. 最终验收口径
740
+
741
+ 1. 原 Forward Cursor 是唯一完整性算法和ACK来源。
742
+ 2. 大Gap时最新一页优先交付,Gap最终由Forward补齐。
743
+ 3. 任意时刻SDK最多维护一个Gap和一个可靠实时窗口。
744
+ 4. Tail、History和Pull都不能根据首条返回消息推算不可恢复下界。
745
+ 5. retention floor只由服务端实际GC删除结果推进。
746
+ 6. SDK不保存floor;floor只通过原Pull clamp A。
747
+ 7. Group Event行为、游标和ACK完全保持原语义。
748
+ 8. 新老SDK与新老服务端四种组合的旧Forward/ACK及实时同步路径均可工作;History只在新Gateway与声明能力的新服务上可用,旧Gateway或旧服务必须明确返回unsupported。
749
+ 9. Push/Sync/Forward允许重复和跨通道乱序,应用幂等并按seq排序后结果完整。
750
+ 10. Python/TypeScript/Go/JavaScript状态机和Python/Go服务端协议结果一致。