@agentunion/fastaun-browser 0.5.19 → 0.5.20

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 (114) hide show
  1. package/CHANGELOG.md +129 -78
  2. package/_packed_docs/CHANGELOG.md +129 -78
  3. package/_packed_docs/INDEX.md +201 -195
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +93 -90
  5. package/_packed_docs/agent.md/SCHEMA.md +7 -7
  6. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +11 -11
  7. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1072 -1072
  8. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +676 -676
  9. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +8 -12
  10. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +443 -0
  11. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
  12. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +658 -0
  13. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
  14. package/_packed_docs/protocol/agent.md/SCHEMA.md +1 -1
  15. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +1 -1
  16. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +67 -63
  17. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
  18. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +35 -8
  19. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
  20. package/_packed_docs/sdk/09-group-rpc-manual.md +258 -254
  21. package/_packed_docs/sdk/09-message-rpc-manual.md +7 -4
  22. package/_packed_docs/sdk/09-storage-rpc-manual.md +18 -26
  23. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +61 -52
  24. package/_packed_docs/sdk/INDEX.md +92 -80
  25. package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
  26. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
  27. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +922 -0
  28. package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
  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 +300 -0
  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 +382 -0
  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 -0
  32. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +476 -0
  33. package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
  34. package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
  35. 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 +173 -0
  36. 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 +211 -0
  37. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/345/256/236/347/216/260/347/247/273/346/244/215/346/214/207/345/215/227.md +331 -0
  38. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +207 -0
  39. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/344/273/243/347/240/201/345/256/241/346/237/245/346/212/245/345/221/212.md +252 -0
  40. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/345/256/236/346/226/275/350/256/241/345/210/222.md +156 -0
  41. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/346/226/271/346/241/210.md +273 -0
  42. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/350/257/246/347/273/206/350/256/276/350/256/241.md +211 -0
  43. package/dist/agent-md-schema.d.ts +15 -4
  44. package/dist/agent-md-schema.d.ts.map +1 -1
  45. package/dist/agent-md-schema.js +64 -30
  46. package/dist/agent-md-schema.js.map +1 -1
  47. package/dist/agent-md.d.ts +10 -2
  48. package/dist/agent-md.d.ts.map +1 -1
  49. package/dist/agent-md.js +278 -121
  50. package/dist/agent-md.js.map +1 -1
  51. package/dist/aid-store.d.ts +46 -0
  52. package/dist/aid-store.d.ts.map +1 -1
  53. package/dist/aid-store.js +149 -10
  54. package/dist/aid-store.js.map +1 -1
  55. package/dist/aid.d.ts +4 -1
  56. package/dist/aid.d.ts.map +1 -1
  57. package/dist/aid.js +23 -42
  58. package/dist/aid.js.map +1 -1
  59. package/dist/auth.d.ts +1 -1
  60. package/dist/auth.d.ts.map +1 -1
  61. package/dist/auth.js +85 -18
  62. package/dist/auth.js.map +1 -1
  63. package/dist/bundle.js +1581 -781
  64. package/dist/cert-utils.d.ts +49 -1
  65. package/dist/cert-utils.d.ts.map +1 -1
  66. package/dist/cert-utils.js +102 -56
  67. package/dist/cert-utils.js.map +1 -1
  68. package/dist/client/delivery.d.ts +7 -0
  69. package/dist/client/delivery.d.ts.map +1 -1
  70. package/dist/client/delivery.js +243 -28
  71. package/dist/client/delivery.js.map +1 -1
  72. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  73. package/dist/client/rpc-pipeline.js +89 -77
  74. package/dist/client/rpc-pipeline.js.map +1 -1
  75. package/dist/client/v2-e2ee.d.ts.map +1 -1
  76. package/dist/client/v2-e2ee.js +99 -80
  77. package/dist/client/v2-e2ee.js.map +1 -1
  78. package/dist/client.d.ts.map +1 -1
  79. package/dist/client.js +31 -17
  80. package/dist/client.js.map +1 -1
  81. package/dist/crypto.d.ts.map +1 -1
  82. package/dist/crypto.js +8 -6
  83. package/dist/crypto.js.map +1 -1
  84. package/dist/events.d.ts +5 -2
  85. package/dist/events.d.ts.map +1 -1
  86. package/dist/events.js +33 -14
  87. package/dist/events.js.map +1 -1
  88. package/dist/facades.js +5 -5
  89. package/dist/facades.js.map +1 -1
  90. package/dist/index.d.ts +1 -1
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js.map +1 -1
  93. package/dist/org-protocol.d.ts.map +1 -1
  94. package/dist/org-protocol.js +72 -12
  95. package/dist/org-protocol.js.map +1 -1
  96. package/dist/ownership-v21.d.ts +25 -8
  97. package/dist/ownership-v21.d.ts.map +1 -1
  98. package/dist/ownership-v21.js +142 -73
  99. package/dist/ownership-v21.js.map +1 -1
  100. package/dist/ownership-workflow.d.ts +6 -1
  101. package/dist/ownership-workflow.d.ts.map +1 -1
  102. package/dist/ownership-workflow.js +221 -58
  103. package/dist/ownership-workflow.js.map +1 -1
  104. package/dist/tools/cross-sdk-agent.d.ts +2 -1
  105. package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
  106. package/dist/tools/cross-sdk-agent.js +91 -8
  107. package/dist/tools/cross-sdk-agent.js.map +1 -1
  108. package/dist/transport.d.ts +1 -0
  109. package/dist/transport.d.ts.map +1 -1
  110. package/dist/transport.js +2 -0
  111. package/dist/transport.js.map +1 -1
  112. package/dist/version.d.ts +1 -1
  113. package/dist/version.js +1 -1
  114. package/package.json +1 -1
@@ -0,0 +1,207 @@
1
+ # AUN 跨设备已读未读 SDK 集成指南
2
+
3
+ > 适用能力:AUN SDK Core 跨设备已读未读客户端完整实现版本,至少支持四个 RPC facade、`read_changed` 订阅、身份代际隔离和连接恢复后的 `read_state_sync` 补偿。
4
+ >
5
+ > 示例语言:TypeScript(Node.js)
6
+ >
7
+ > 相关契约:`docs/跨设备已读未读详细设计.md`、`docs/sdk/09-message-rpc-manual.md`、`docs/sdk/09-group-rpc-manual.md`
8
+ >
9
+ > 跨域验证只使用本地 Docker 的 `agentid.pub` 与 `agentid.cn`,不连接线上环境。
10
+
11
+ ## 1. 功能边界
12
+
13
+ 跨设备已读未读保存的是同一 AID 在不同设备上的业务已读位置。应用可以按话题上报最后已读消息,并查询所有设备合并后的最新位置,再在本地消息索引上计算未读数。
14
+
15
+ 本功能覆盖:
16
+
17
+ - P2P 收件箱和 Group 消息;
18
+ - 单条或批量上报;
19
+ - 查询当前认证 AID 的状态,可按话题、Group 过滤;`aid` 仅可作为当前身份的等值筛选器;
20
+ - 在线 `read_changed` 事件;
21
+ - 断线重连后的查询补偿。
22
+
23
+ 本功能不改变消息接收、`pull`、`history`、投递 `ack`、SeqTracker、E2EE 或对端已读回执。业务已读游标和投递确认游标必须分开维护。
24
+
25
+ ## 2. 接入前准备
26
+
27
+ 应用需要准备以下信息:
28
+
29
+ 1. 已注册且包含私钥的 AID;
30
+ 2. 当前设备使用的 AUN 本地目录和加密种子;
31
+ 3. 能够发现该 AID 所属域 Gateway 的网络配置;
32
+ 4. 应用自己的话题标识 `topic_key` 和消息索引。
33
+
34
+ TypeScript SDK 包名为 `@agentunion/fastaun`。`AIDStore` 负责从本地 keystore 加载身份,`AUNClient` 负责连接 Gateway、认证和 RPC。
35
+
36
+ ## 3. 最小连接示例
37
+
38
+ `AIDStore.load()` 是同步方法,返回 Result;成功结果中的身份位于 `loaded.data.aid`。身份加载失败时不要创建无身份客户端继续调用业务 RPC。
39
+
40
+ ```ts
41
+ import { AIDStore, AUNClient } from "@agentunion/fastaun";
42
+
43
+ const store = new AIDStore({
44
+ aunPath: process.env.AUN_PATH!, encryptionSeed: process.env.AUN_SEED ?? "",
45
+ });
46
+ const loaded = store.load("alice.agentid.pub");
47
+ if (!loaded.ok) throw new Error(loaded.error.message);
48
+ const client = new AUNClient(loaded.data.aid);
49
+ await client.connect({ auto_reconnect: true });
50
+ ```
51
+
52
+ `connect()` 的身份来自构造函数,也可以先创建无参客户端,再调用 `client.loadIdentity(aid)`。`auto_reconnect: true` 只负责连接恢复;应用仍应把事件当作实时提示,并以查询结果作为状态校正来源。
53
+
54
+ ## 4. 上报 P2P 和 Group 已读位置
55
+
56
+ ### 4.1 P2P
57
+
58
+ P2P 上报通过 `client.message.read()` 完成。`states` 至少包含一项,批量项可以属于不同 `topic_key`。省略 `states` 时也可直接传顶层单条;显式空数组、null 或非数组值拒绝。业务参数不传 `aid`、`device_id` 或 `slot_id`,这些值由认证连接确定。
59
+
60
+ ```ts
61
+ await client.message.read({
62
+ states: [{
63
+ scope: "p2p", topic_key: "inbox:primary",
64
+ last_read_message_id: "m-100",
65
+ }],
66
+ });
67
+ ```
68
+
69
+ ### 4.2 Group
70
+
71
+ Group 上报通过 `client.group.read()` 完成,每一项必须包含 `group_id`。Group 历史别名由服务端解析为 canonical ID,响应与事件使用 canonical `group_id`;成员权限按当前认证 AID 检查并跨实例同步。
72
+
73
+ ```ts
74
+ await client.group.read({
75
+ states: [{
76
+ group_id: "team.agentid.pub", topic_key: "chat:general",
77
+ last_read_message_id: "gm-200",
78
+ }],
79
+ });
80
+ ```
81
+
82
+ 两种调用都支持批量和部分话题更新。服务端按公共消息顺序单调更新;重复或更旧的位置返回 `changed: false`。返回的 `states` 是逐项设备写入回执,不是共享汇总;`changed: true` 也不保证共享位置前进。应用通过 `readState()` 或有效 `read_changed` 获取共享位置,网络超时后可以原样幂等重试。
83
+
84
+ ## 5. 查询合并后的状态
85
+
86
+ ### 5.1 P2P 查询
87
+
88
+ `client.message.readState()` 可按 `scope`、`topic_key` 和 `aid` 过滤。`scope` 缺省或为空时按 `p2p` 处理,非空值只能为 `p2p`;`aid` 省略或为空时不增加过滤,非空值必须等于当前认证 AID。只给 `topic_key` 查询一个 P2P 话题;省略过滤字段则查询当前认证 AID 的全部 P2P 状态。
89
+
90
+ ```ts
91
+ const result = await client.message.readState({
92
+ scope: "p2p", topic_key: "inbox:primary",
93
+ });
94
+ const states = (result as { states?: Array<Record<string, unknown>> }).states ?? [];
95
+ const state = states[0];
96
+ ```
97
+
98
+ ### 5.2 Group 查询
99
+
100
+ `client.group.readState()` 可按 `scope`、`group_id`、`topic_key` 和 `aid` 过滤。`scope` 缺省或为空时按 `group` 处理,非空值只能为 `group`;`aid` 省略或为空时不增加过滤,非空值必须等于当前认证 AID。`group_id` 与 `topic_key` 同时出现时取交集;历史 Group ID 别名会解析为 canonical ID,响应中的 `group_id` 使用 canonical ID。省略 group 过滤时查询当前 AID 在各所属域有权限的群,包括远域成员群,保留按 topic 的条件。
101
+
102
+ ```ts
103
+ const result = await client.group.readState({
104
+ group_id: "team.agentid.pub", topic_key: "chat:general",
105
+ });
106
+ const state = ((result as { states?: Array<Record<string, unknown>> }).states ?? [])[0];
107
+ ```
108
+
109
+ 响应始终包含 `states` 数组。单项结果可能同时在顶层镜像字段,但应用应以 `states` 为准;没有状态时是空数组,不应把缺失状态当作消息已读到某个默认位置。
110
+
111
+ ## 6. 状态字段解释
112
+
113
+ | 字段 | 含义 | 应用处理方式 |
114
+ | --- | --- | --- |
115
+ | `scope` | `p2p` 或 `group` | 与调用类型匹配;Group 状态还需要 `group_id` |
116
+ | `group_id` | Group 标识 | P2P 状态没有该字段;按现有 Group 标识语义传递 |
117
+ | `topic_key` | 应用话题键 | 原样保存和比较,不能 trim、改大小写、解析或 Unicode 规范化 |
118
+ | `last_read_message_id` | 合并后的最后已读消息 ID | 用于在本地消息索引上计算未读范围 |
119
+ | `last_read_order` | 服务端公共顺序提示 | 只用于防止乱序回退,不能当作设备 seq 或传给 ACK |
120
+ | `current_device_seq` | 该消息在当前查询设备收件箱中的本地 seq | 仅当前设备可映射;不可映射时为 `null` |
121
+ | `mapping_status` | 映射结果 | `resolved`、`not_received` 或 `unresolved` |
122
+ | `updated_at` | 服务端更新时间,毫秒 | 仅用于展示或诊断,不用于猜测未知消息的公共顺序 |
123
+
124
+ `resolved` 表示当前设备有对应投递映射;`not_received` 表示公共消息位置已知,但当前设备没有收到该消息;`unresolved` 表示消息位置未知或公共索引已清理。`null` 映射不是失败,也不能被应用转换成 0。
125
+
126
+ TS/JS 的 `last_read_order`、非空 `current_device_seq` 在安全整数范围内是 `number`,超出时是精确十进制字符串;需要计算或比较时可转为 `BigInt`,不能先转成 Number。网络 JSON 仍为整数,Python/Go 保留精确整数。
127
+
128
+ 合并结果只表示同一 AID 的最新共享位置,不暴露其他设备的 `device_id` 或设备 seq。定向 P2P 消息的设备事实不会覆盖共享事实,也不会通过共享 `read_changed` 事件广播。
129
+
130
+ ## 7. 订阅变更事件
131
+
132
+ `client.on()` 返回 `Subscription`,调用 `unsubscribe()` 取消订阅。事件只在状态已经提交、且同一 AID 的共享汇总位置前进后发送。SDK 要求 `owner_aid` 非空且等于当前身份,`scope` 与原事件名严格匹配;Gateway 即使收到显式 route 也只向该 owner 分发。
133
+
134
+ ```ts
135
+ const messageSub = client.on("message.read_changed", (payload) => {
136
+ const event = payload as Record<string, unknown>;
137
+ console.log(event.topic_key, event.last_read_message_id);
138
+ });
139
+ const groupSub = client.on("group.read_changed", (payload) => {
140
+ const event = payload as Record<string, unknown>;
141
+ console.log(event.group_id, event.topic_key, event.last_read_message_id);
142
+ });
143
+ ```
144
+
145
+ 事件 payload 包含 `owner_aid`、`scope`、`topic_key`、`last_read_message_id`、`last_read_order` 和 `updated_at`;Group 事件还包含 `group_id`。事件不承载统一的 `current_device_seq`,应用需要用 `readState()` 在当前设备上重新映射。
146
+
147
+ 事件不是可靠历史流,不进入 `pull` 队列。处理器应快速更新 UI 或本地提示,不能把事件当作唯一持久状态来源。退出页面或客户端时取消订阅:
148
+
149
+ ```ts
150
+ messageSub.unsubscribe();
151
+ groupSub.unsubscribe();
152
+ await client.close();
153
+ store.close();
154
+ ```
155
+
156
+ ## 8. 重连和本地状态一致性
157
+
158
+ SDK 将 `readState` 查询和有效的 `read_changed` 写入本地汇总缓存;`read` 回执只登记输入话题的补偿条件。缓存键为:
159
+
160
+ - P2P:`(scope, "", topic_key)`;
161
+ - Group:`(scope, group_id, topic_key)`。
162
+
163
+ 缓存只保存汇总状态,不保存设备明细。已知公共位置按 `last_read_order`、同序的 `last_read_message_id` 比较;未知顺序不能按时间或 ID 猜测前进。旧事件不交付应用;同位置事件不重复交付并保留已有映射,新位置事件不继承旧映射。同位置查询可以更新当前设备的 `current_device_seq` 和 `mapping_status`。切换 AID 或设备会清理缓存及补偿条件;旧身份在途结果按身份代际隔离,切到其他身份再切回也不写当前缓存或补偿完成事件,原 RPC 调用仍可返回结果。已排队的 `read_changed` 和 `read_state_sync` 同样按身份代际丢弃;事件处理器等待期间发生身份切换后,停止向余下处理器交付旧事件。
164
+
165
+ SDK 独立登记成功查询的原过滤条件:空结果也登记,全量和按群查询不会缩小为当时返回的话题。认证连接成功后逐项补偿,完成时在 SDK 本地发布 `read_state_sync`;消息 `background_sync=false` 不关闭该补偿。连接返回不代表补偿已完成,应用应在 `connect()` 前订阅完成事件。
166
+
167
+ | 完成事件字段 | 含义 |
168
+ | --- | --- |
169
+ | `owner_aid` | 本轮补偿所属 AID |
170
+ | `states` | 本轮成功查询涉及的状态键在完成时已接受的最新状态,按键去重;空查询不夹带旧缓存 |
171
+ | `errors` | 每项为 `{method, params, error}`,记录失败查询及错误文本 |
172
+
173
+ 应用使用 `states` 更新共享状态,再计算本地未读数;若本轮期间较新的实时事件替代旧查询,状态可能没有设备映射字段,需另行查询映射。失败查询保留旧缓存并在 `errors` 中报告;没有登记项也会发布空完成事件。该事件只在 SDK 本地发布,服务器 RPC/WS 类型保持现有契约。
174
+
175
+ ## 9. 输入约束和错误处理
176
+
177
+ 同域批量先校验该域全部项,校验失败不提交该域。混合域的 `group.read` 按所属域首次出现的顺序逐域校验和提交,任一域失败则整个 RPC 返回错误;已提交域不回滚。数据库也可能分多个批次提交,后续写入失败不回滚先前批次;原样幂等重试可以收敛。应用应处理以下情况:
178
+
179
+ 1. `states` 为空、`topic_key` 或消息 ID 为空;
180
+ 2. `scope` 与方法不匹配,或 Group 缺少 `group_id`;
181
+ 3. 请求中的 Group ID、话题键、消息 ID 或非空 `aid` 筛选值超过 128 个字符;`owner_aid` 和设备 ID 来自认证连接,不是已读 RPC 的业务参数;
182
+ 4. Group 当前 AID 不是成员、被移除或没有访问权限;
183
+ 5. Gateway 返回网络错误或超时;可以重试同一请求;
184
+ 6. 旧服务端返回 `Method not found`;SDK 不会自动改用 ACK/Pull 降级,应用应按能力协商或版本策略处理。
185
+
186
+ 可选字符串字段可以省略,显式传入数字、布尔值、对象或 `null` 会被拒绝。四个已读门面保留显式 `None/null/nil` 字段供服务端校验,不能依赖 SDK 删除空值后将其当作省略字段。Message 的 `scope` 缺省或为空时按 `p2p` 处理,非空值只能为 `p2p`;Group 的 `scope` 缺省或为空时按 `group` 处理,非空值只能为 `group`。查询提供非空 `aid` 时必须与当前认证 AID 相同。
187
+
188
+ `topic_key` 的 128 字符限制按字符计算,服务端存储使用 `utf8mb4` 二进制排序。不要在应用层把两个看起来相近但原始字符串不同的话题合并,例如 `"Topic"`、`"topic"` 和带首尾空格的值是不同键。
189
+
190
+ ## 10. 推荐接入流程
191
+
192
+ 1. 在连接前订阅两个 `read_changed` 及本地 `read_state_sync` 事件。
193
+ 2. 连接成功后,为需要显示未读数的 P2P/Group 话题调用一次 `readState()`,或按显示范围登记全量/按群查询。
194
+ 3. 用户打开话题并确认阅读位置后调用对应的 `read()`;连续位置可以合并成一次批量上报。
195
+ 4. 收到查询或事件后按 `mapping_status` 更新本地 UI;`not_received` 和 `unresolved` 不应伪装成已映射的设备 seq。
196
+ 5. 断线重连后从 `read_state_sync` 取得本轮状态和错误,再重新计算本地未读数。
197
+ 6. 关闭客户端时先取消订阅,再等待 `client.close()` 完成,之后释放应用自己的消息索引等资源。
198
+
199
+ ## 11. 验证清单
200
+
201
+ - 同一 AID 的两个设备交错上报同一话题,查询结果始终取公共顺序较新的位置;
202
+ - 重复上报返回幂等结果,旧上报不能回退;
203
+ - P2P 与 Group 使用相同 `topic_key` 时互不影响;
204
+ - 当前设备有映射、无投递映射、未知公共位置分别得到 `resolved`、`not_received`、`unresolved`;
205
+ - 断开 WebSocket 后重新连接,事件丢失仍能通过 `readState()` 恢复;
206
+ - Group 非成员无法写入或查询;
207
+ - 测试只连接本地 Docker 的 `agentid.pub` 和 `agentid.cn`,不连接线上环境。
@@ -0,0 +1,252 @@
1
+ # AUN 跨设备已读未读代码审查报告
2
+
3
+ > 审查日期:2026-10-11
4
+ > 审查类型:跨设备已读未读补充修复、文档和隔离回归
5
+ > 测试边界:本轮隔离/mock 回归与静态检查见第 7.3 节,部署验收未执行;2026-10-09 Docker 与 2026-10-10 隔离回归均为历史证据,不代表本轮结果
6
+
7
+ ## 1. 上一轮审查结论(2026-10-10)
8
+
9
+ 2026-10-10 补充审查发现的服务端提交、汇总、目标元数据、GC、迁移、Group 跨域及四端 SDK 差异当时已修复,具体闭环见第 4.7 节。设计、实施计划、集成/移植指南、正式 RPC 手册和 Group 协议已按确认的契约同步。
10
+
11
+ 当轮结论为 **代码修复和隔离回归通过,部署层验收待执行**。当轮没有重建或启动 Kite/Docker,真实 MySQL 多实例事务、单域集成/E2E、shared-AID cross-SDK 及本地双域需在更新后的镜像上验证;不能以历史通过记录替代当轮结果。
12
+
13
+ | 审查项 | 结论 |
14
+ | --- | --- |
15
+ | 功能实现 | 已修复请求级提交等待、提交时汇总核实、跨域批量/全量查询及缓存边界 |
16
+ | 文档与代码一致性 | 已统一回执、部分提交、迁移重试、ACK 独立语义及本地完成事件 |
17
+ | Python/TypeScript/JavaScript/Go 对齐 | 已统一应用交付、原过滤范围、身份隔离、精确整数与 ID 排序;部署交叉验证待执行 |
18
+ | 测试充分性 | 当轮隔离回归及静态检查通过;真实数据库和部署层验收未执行 |
19
+
20
+ ## 2. 审查范围与依据
21
+
22
+ 审查对象包括:
23
+
24
+ - 设计与计划:`docs/跨设备已读未读方案.md`、`docs/跨设备已读未读详细设计.md`、`docs/跨设备已读未读实施计划.md`;
25
+ - Message 服务:`../extensions/services/message/go/internal/store/read_state.go`、`../extensions/services/message/go/cmd/message-go/main.go`;
26
+ - Group 服务:`../extensions/services/group/go/internal/repository/read_state.go`、`../extensions/services/group/go/internal/store/schema.go`、`../extensions/services/group/go/internal/rpc/service_handlers.go`;
27
+ - Gateway 与事件:`../extensions/services/gateway/go/internal/httpserver/federation_binding.go`、`server.go`、`../extensions/services/group/go/internal/events/dispatch.go`;
28
+ - Python、TypeScript、JavaScript、Go SDK 的 facade、事件接收、缓存和重连逻辑;
29
+ - Message/Group Go 单测、Python 单域集成和 E2E、cross-SDK runner、双域 Docker 测试脚本及测试日志;
30
+ - 正式 SDK RPC 手册和根级文档索引。
31
+
32
+ 判定按 P1/P2 分级:P1 表示会导致功能契约或数据正确性失败,应阻断完成;P2 表示在特定时序、运维或文档场景下会产生错误或验收缺口。
33
+
34
+ ## 3. P1 问题
35
+
36
+ P1-01~P1-04 与第 4.7 节保留历史审查闭环;2026-10-11 本轮补充修复见第 4.8 节,实际验证边界见第 7.3 节。
37
+
38
+ ### P1-01 定向 P2P 消息会进入 AID 汇总状态(已关闭)
39
+
40
+ **期望行为**:详细设计规定 `to_device_id` 定向消息可以形成设备事实,但不能改变 AID 共享汇总;只有面向整个 AID 的收件消息进入共享已读状态(`docs/跨设备已读未读详细设计.md:162`,`docs/跨设备已读未读方案.md:187`)。
41
+
42
+ **修复**:Message 状态行增加 `is_targeted` 事实维度,`message_delivery_targets` 保存目标元数据;定向事实与 AID 共享事实分离,汇总只读取共享行,定向写入不广播共享变更。
43
+
44
+ **证据**:`message_read_states` 的唯一键包含 `is_targeted`,服务端通过目标表判定定向消息;`python/tests/e2e_test_read_state_multi_device.py --phase targeted` 在加密和非加密两种模式均通过,证明定向消息不改变共享状态且不产生汇总事件。
45
+
46
+ ### P1-02 Message `read_changed` 实际关闭 federation 转发(已关闭)
47
+
48
+ **期望行为**:实施计划要求跨域验证在线事件和事件目标 AID(`docs/跨设备已读未读实施计划.md:60,89`);Gateway 事件契约允许 `message.read_changed`(`../extensions/services/gateway/go/internal/httpserver/server.go:17354`)。
49
+
50
+ **修复**:Message sink 开启 `federation_forward`,Gateway 按 `owner_aid` 校验来源绑定;Group 继续按群所属域校验。事件目标只包含对应 AID,不进入 Pull 队列,重连仍由查询补偿。
51
+
52
+ **证据**:`federation_binding.go` 已包含 `message.read_changed` 的 owner 绑定分支;历史本地双域日志 `read_state_review_cross_domain_final.log` 包含双方向 `READ_STATE_FEDERATION_EVENT_OK`、`READ_STATE_P2P_OK`、`READ_STATE_GROUP_OK` 和 `PASS phase=read-state`。
53
+
54
+ ### P1-03 未知或已清理消息没有实现 `unresolved`(已关闭)
55
+
56
+ **期望行为**:设计要求消息已投递但公共顺序暂时无法解析时返回 `mapping_status=unresolved`,未知消息或历史清理场景不得通过设备 seq 猜测(`docs/跨设备已读未读详细设计.md:88,140`;实施计划的映射测试要求见 `docs/跨设备已读未读实施计划.md:37`)。
57
+
58
+ **修复**:公共位置未知时保留 `order=0` 的待解析事实,不参与汇总前进;查询返回 `mapping_status=unresolved`,已知位置但当前设备未收到时返回 `not_received`,不再用设备 seq 或 message ID 字典序猜测。
59
+
60
+ **证据**:Message/Group repository 已增加未知位置和 `unresolved` 分支;`read_state_test.go`、`read_state_test.go`(Group)分别覆盖未知写入不触发汇总和未知映射不虚构 seq。
61
+
62
+ ### P1-04 旧状态表迁移没有校验全部 128 字符字段(已关闭)
63
+
64
+ **期望行为**:详细设计要求迁移前检查 `owner_aid`、`device_id`、`topic_key`、`group_id`、`last_read_message_id` 的长度,并在异常数据存在时保留旧表、明确失败(`docs/跨设备已读未读详细设计.md:130`;计划 `:41-42`)。
65
+
66
+ **修复**:Message/Group 迁移均先检查全部 128 字符字段、scope 和唯一键冲突;异常数据保留旧表并返回错误,成功迁移后使用 8 字节自增主键和紧凑唯一索引。Message 投递目标旧表也执行同样的幂等迁移。
67
+
68
+ **证据**:`read_state_review_mysql_message.log` 覆盖 owner/device/topic/message ID 超长拒绝;`read_state_review_mysql_group.log` 另覆盖 group ID;两域 `read_state_review_schema_final.log` 证明当前结构一致。
69
+
70
+ ## 4. P2 问题复核
71
+
72
+ ### P2-01 异步批写的提交语义(已关闭)
73
+
74
+ 设计、代码和 RPC 手册已统一为“缓存先行、按键合并、后台多值批写;RPC 只等待本次依赖的提交句柄”。事务内锁定已提交设备事实,核实同设备更新及共享汇总前进,提交后校正回执,每次真实汇总前进只由一个句柄领取事件。批写失败重新入队,仅对应句柄收到错误;后续同主体/设备的写入不延长本次等待。跨批次或跨域失败不回滚已提交部分,通过幂等重试收敛。
75
+
76
+ **证据**:当轮 Message/Group 提交句柄、事务锁定、数据库事实校正回执/事件、提交失败不发事件等单元回归通过。真实 MySQL conditional upsert 为历史验证,当轮多实例事务验证待执行。成功 RPC 已持久化;成功返回前进程退出时,调用方按幂等语义重试。
77
+
78
+ ### P2-02 四端 SDK 乱序回退(已关闭)
79
+
80
+ 四端比较已知 `last_read_order`,同序按 Unicode 码点/UTF-8 BINARY 顺序比较 message ID;未知顺序不按时间或 ID 猜测前进。旧事件不交付应用,同位置事件保留查询映射且不重复交付,新位置不继承旧映射。TS/JS 的超安全整数在 JSON 解码时保留为精确十进制字符串,内部按精确整数比较。
81
+
82
+ **证据**:`python/tests/unit/test_sdk_facades.py`、`ts/tests/unit/client.test.ts`、`js/tests/unit/client.test.ts`、`go/facades_test.go` 的已读状态乱序测试通过。
83
+
84
+ ### P2-03 Message 非法查询 scope(已关闭)
85
+
86
+ `message.read_state` 对非空且非 `p2p` 的 scope 返回参数错误,不再把非法请求解释为空状态;handler 单元及历史 SDK 集成测试覆盖 `scope=group` 的拒绝路径。
87
+
88
+ ### P2-04 已读状态观测指标(已关闭)
89
+
90
+ Message 和 Group repository 现在提供运行时 counters,记录 RPC/write/query 状态数量、`changed`、`ignored`、汇总前进、批写成功/失败以及 `resolved`、`not_received`、`unresolved` 映射计数;指标只包含计数,不记录 payload 或 topic。服务入口把状态并入现有 runtime status。
91
+
92
+ ### P2-05 正式 RPC 手册缺失(已关闭)
93
+
94
+ `docs/sdk/09-message-rpc-manual.md` 和 `docs/sdk/09-group-rpc-manual.md` 已加入方法索引及完整 `read`、`read_state`、`read_changed` 契约,包含 128 字符限制、批量与幂等、权限、公共顺序、`current_device_seq`、`mapping_status`、定向消息和重连补偿语义。
95
+
96
+ ### P2-06 迁移、异步、乱序和跨域测试缺口(隔离回归已补齐,部署验收待执行)
97
+
98
+ 2026-10-09 历史记录补充了迁移、MySQL、映射、定向隔离、四端缓存和双域事件测试;这些记录不覆盖本轮新修复的竞态和 SDK 完成交付。本轮已在已有测试文件中补充失败回归并按断点续跑验证,部署层场景仍待复测。
99
+
100
+ ### 4.7 历史问题闭环(2026-10-10)
101
+
102
+ 下列项目均已完成代码修复及对应隔离回归,真实部署验证统一保留为第 7 节的未执行项。
103
+
104
+ | 编号 | 发现的问题 | 修复及依据 |
105
+ | --- | --- | --- |
106
+ | R-01 | 四端补偿只改私有缓存,应用无完成交付 | 本地 `read_state_sync` 交付本轮 states/errors,服务器类型不变 |
107
+ | R-02 | 空查询不登记,全量/按群过滤缩水 | 独立登记原查询条件,空结果也登记,补偿可发现离线新增话题 |
108
+ | R-03 | Go 使用 Connect 返回后取消的 context | 使用连接后台任务 context,取消 Connect 调用不终止补偿 |
109
+ | R-04 | pending 隐藏较新 DB 事实,汇总事件提前判定 | DB/pending 同时作为候选,事务中锁定并核实真实汇总前进 |
110
+ | R-05 | Message 定向目标 false 永久负缓存 | 否定结果不缓存,先查公共位置再核实目标元数据 |
111
+ | R-06 | Group 全量只查本域,混合批量只按首项路由 | 完整成员目录发现远域;混合域逐域校验/提交,回执保持输入顺序 |
112
+ | R-07 | SDK 把设备写入回执当成共享汇总 | 回执不进入共享缓存,只从写入输入登记补偿条件 |
113
+ | R-08 | 缓存拒绝旧事件后仍向应用交付 | 旧事件直接丢弃,应用交付保持公共位置单调 |
114
+ | R-09 | 同位置事件抹掉当前设备映射 | 同位置不重复交付并保留映射,新位置不继承旧映射 |
115
+ | R-10 | AID/device 切换沿用缓存与过滤项 | 切换后清理;旧身份在途响应及补偿不写入新缓存 |
116
+ | R-11 | TS/JS UTF-16 ID 排序偏离服务端 BINARY | 按 Unicode 码点比较,对有效 Unicode 与 Python/Go/UTF-8 一致 |
117
+ | R-12 | TS/JS int64 精度丢失 | 安全范围保留 number,超出用精确字符串;普通/短连接解码及比较回归通过 |
118
+ | R-13 | 等待和错误绑定整个 owner/device/writer | 绑定本次提交句柄,后续写入及不相关批次错误不污染本请求 |
119
+ | R-14 | GC 后热/冷进程映射状态不同 | 设备映射全缺失时绕过公共位置缓存回源核实 |
120
+ | R-15 | 顺序列回填中断被跳过,无 scope 迁移漏长度检查 | 每次初始化重试 order=0 回填,所有旧表分支均预检查长度 |
121
+ | R-16 | 参数类型校验不统一 | 服务端统一拒绝字符串字段的数字/布尔/对象/null,明确缺省 scope |
122
+ | R-17 | Group 跨域结果排序 trim topic | 合并结果按原始 topic 比较,保留空格、大小写及 Unicode |
123
+
124
+ 三项经用户确认的差异契约:仅 SDK 本地发布补偿完成事件;TS/JS 使用 number/精确字符串表示整数;混合 Group 域逐域提交及幂等重试,不承诺跨域事务回滚。
125
+
126
+ ### 4.8 本轮补充修复(2026-10-11)
127
+
128
+ 本轮 11 项已确证问题逐项闭环如下;编号 N-01~N-11 与第 4.7 节历史 R 编号独立。原清单及补充收尾均已完成并经独立复核;隔离验证范围见第 7.3 节。
129
+
130
+ | 编号 | 问题 | 修复 | 代码/回归索引 |
131
+ | --- | --- | --- | --- |
132
+ | N-01 | 联邦 Message `read/read_state` 可伪造其他域 `_auth.aid` | Gateway 将主体绑定认证连接;P2P 状态仅由 home 域管理,拒绝两个方法的 federation 入站/出站 | Gateway `internal/httpserver/federation_identity_boundary_test.go` 的 `TestFederationReadStateActorAndHomeDomainBoundary` |
133
+ | N-02 | Gateway/四 SDK 未将原事件名绑定 scope,Group 可被解释为 P2P | 原事件严格绑定 `scope`;owner 必需且等于当前身份 | Gateway `federation_identity_boundary_test.go`;Python `test_sdk_facades.py`、Go `facades_test.go`、TS/JS `tests/unit/client.test.ts` 的 raw 事件回归 |
134
+ | N-03 | order=0 晚到位置不持久化,旧事务位置广播、GC 后回退 | 精确消息 ID 条件持久回填,CAS 未命中重读,在事务中使用相同公共顺序 | Message `internal/store/read_state{,_test}.go` 的 ResolvedOrder/FlushResolves 回归;Group `internal/repository/read_state{,_test}.go` 的 LatePosition 回归 |
135
+ | N-04 | Gateway 显式 route 绕过 owner 隔离 | 显式 route 也只能送给 owner AID | Gateway `internal/events/dispatcher_test.go` 的 `TestReadChangedDispatchCannotRouteToAnotherOwner` |
136
+ | N-05 | Group 历史状态 ID 与 canonical 消息 ID 不一致导致 unresolved | 新写键与 wire 使用 canonical ID,历史 aliases 参与事实/公共位置/设备映射事务查询 | Group `internal/repository/read_state_test.go` 的 `TestGroupReadStateResolvesHistoricalGroupAliases` |
137
+ | N-06 | Group 多实例 read 权限未刷新 Redis 群版本 | 复用既有 `RequireSendGroup` 与版本同步,刷新跨实例成员权限 | Group `internal/rpc/service_handlers.go` 的已读授权分支与 `service_handlers_test.go` 权限回归 |
138
+ | N-07 | 冒号拼接事件 ID 可发生合法 topic/message ID 碰撞 | 对身份、话题和公共位置的 JSON 元组取 SHA-256 | Message `cmd/message-go/gateway_push_activity_test.go`、Group `cmd/group-go/group_event_metadata_test.go` 的 EventIDHasUnambiguous 回归 |
139
+ | N-08 | 设计支持顶层单条,实现只接受 states 数组 | 仅省略 states 时兼容顶层单条;显式空/null/非数组拒绝 | Message `gateway_push_activity_test.go` 的 `TestMessageReadAcceptsTopLevelOnlyWhenStatesAbsent`;Group `internal/rpc/federation_router_test.go` 的 `TestDispatcherReadAcceptsTopLevelState` |
140
+ | N-09 | 四 SDK 门面删掉显式 None/null/nil,非法过滤扩大为全量 | 仅四个已读门面保留显式空值供服务端拒绝 | Python `facades.py/test_sdk_facades.py`;Go `facades.go/facades_test.go`;TS/JS `facades.ts/tests/unit/client.test.ts` |
141
+ | N-10 | Gateway 丢失 Kernel 可信 source,无法核实发布者 | 保留可信 source 并验证 Message/Group 原事件来源 | Gateway `federation_identity_boundary_test.go` 的 `TestKernelReadChangedRejectsMissingTrustedSource` |
142
+ | N-11 | 事件发布失败日志输出私有 topic | 失败日志不输出 topic 或含 topic 的原始错误 | Message `gateway_push_activity_test.go` 的 `TestMessageReadEventFailureLogOmitsTopic`;Group `internal/rpc/service_handlers.go` 的已读事件失败日志分支代码复核 |
143
+
144
+ 补充收尾:Gateway 本域/federation `read_state` 回程保留 int64 精度;四 SDK 用身份代际隔离真实公开关闭/加载路径的 A→B→A 结果,旧结果不写缓存/补偿完成事件,原 RPC 仍可返回;两服务批次全部键先 `FOR UPDATE` 并关闭 Rows,再执行普通位置查询,Group 包含 aliases;Message 缓存/pending 键包含定向维度,读取核实 pending/缓存/数据库候选;提交后的晚到定向事实在事务内单调合并到定向槽,并按完整键及旧消息 ID/order 精确清除共享槽,保留已有更前定向事实,使正常共享位置真实提交。对应回归分别为 Gateway ExactIntegers、四 SDK ABA、Message `TestMessageReadStateFlushLocksAllFactsBeforeSnapshot`、Group `TestGroupReadStateLocksAllTopicsBeforeResolvingLatePositions` 和 Message LateTarget;新增 `TestMessageReadStateCommittedLateTargetDoesNotBlockSharedWrite` 覆盖热/冷缓存及保留既有更前定向事实,核对提交回执、事件、SQL 写入投影和最终查询。
145
+
146
+ 验收工具同步修正分页等待、定向排除、每设备 seq 精确比对和真实 `read_state_sync` 观测;同设备游标独立分配不保证数值不同,不再用强制 seq 不等作为验收。双域默认四方向完成才输出 `READ_STATE_CROSS_DEVICE_OK`,非默认起点续跑只输出 `READ_STATE_RESUMED_OK`;脚本逻辑的离线验证不能代替真实部署结果。
147
+ ## 5. 上一轮文档与代码一致性矩阵(2026-10-10)
148
+
149
+ | 契约 | 代码状态 | 判定 |
150
+ | --- | --- | --- |
151
+ | Message/Group 使用独立状态表,`scope` 只出现在 RPC/响应/事件 | 两张表不落 `scope`,查询用服务固定字面量补回 | 一致 |
152
+ | 主键为 8 字节自增 ID | 状态表和投递目标表使用 `BIGINT UNSIGNED AUTO_INCREMENT`;聚簇主键 8 字节 | 一致 |
153
+ | 字段最大 128 字符 | owner/device/topic/group/message 读写和迁移均校验 128;旧表异常保留并失败 | 一致 |
154
+ | 唯一索引不超过 InnoDB 上限 | Message 状态索引最坏 1537 bytes、Group 2048 bytes、投递目标 1024 bytes,均低于 3072 | 一致 |
155
+ | `topic_key` 原样保存、`utf8mb4_0900_bin` 精确比较 | DDL、迁移和 SDK 均保留空格/大小写/Unicode | 一致 |
156
+ | 按公共消息顺序汇总,不能比较不同设备 seq | Message 使用公共逻辑时间+ID,Group 使用群内 seq+ID;持久 `last_read_order` 支持清理后比较 | 一致 |
157
+ | 定向 P2P 只形成设备事实 | `is_targeted` 与目标表分离,汇总过滤定向行且不广播共享变更 | 一致 |
158
+ | `resolved` / `not_received` / `unresolved` 映射 | 两端按公共位置和当前设备投递映射返回三种状态 | 一致 |
159
+ | RPC 成功后状态可查询、异步批写可恢复 | pending 合并查询、提交等待、失败重试、关闭刷盘和数据库条件更新均实现 | 一致 |
160
+ | 汇总前进且提交成功才广播已读事件 | 事务核实已提交事实,handler 等待本次提交句柄并领取真实汇总事件 | 一致 |
161
+ | Message/Group 在线事件支持 federation | Message 按 `owner_aid`、Group 按群/成员绑定;allowlist 已实现,双域事件历史测试通过,本轮部署复测待执行 | 一致 |
162
+ | 记录已读 RPC/changed/映射计数 | 两个 repository 的 runtime status 暴露对应 counters | 一致 |
163
+ | 四端 facade、wire、事件和重连查询 | 原过滤范围、空结果登记、单调应用交付、映射保留、身份隔离和本地完成事件已统一 | 一致 |
164
+ | TS/JS 完整 int64 与 BINARY 排序 | 无损 JSON 解码,number/精确字符串 API,精确整数及 Unicode 码点比较 | 一致 |
165
+ | Group 全量和混合域批量 | 全量发现远域群;逐域提交,部分提交可幂等重试 | 一致 |
166
+ | 正式 SDK RPC 手册提供新增契约 | Message/Group 09 手册已列方法、字段和事件 | 一致 |
167
+
168
+ ## 6. 上一轮各 SDK 对齐审查(2026-10-10)
169
+
170
+ 1. Python、TypeScript、JavaScript、Go 均提供 Message/Group 的单条或批量 `read` 和条件 `read_state` facade;语言层命名差异不改变 wire 方法名。
171
+ 2. `scope`、`group_id`、`topic_key`、`last_read_message_id`、`last_read_order`、`current_device_seq`、`mapping_status`、`updated_at` 在四端保持一致,`null` 映射不被转换为设备 seq。
172
+ 3. 两个事件名统一为 `message.read_changed` 和 `group.read_changed`;缓存键按 scope、group、topic 隔离,事件与查询都执行公共顺序单调保护。
173
+ 4. 四端成功查询的原过滤条件独立登记,空、全量和按群范围均保留;重连本地完成事件交付去重状态与逐项错误。AID/device 切换清理登记与缓存。
174
+ 5. TS/JS 精确整数和 Unicode 排序与 Python/Go 对齐;Go 使用后台 context。历史 shared-AID cross-SDK 的 24 个查询组合仅为当轮记录,不能作为本轮部署矩阵证据。
175
+
176
+ 没有某个 SDK 版本可整体作为标准。共同基准为 RPC/WS 协议和上述已确认契约:精确整数与有效 Unicode 排序参照服务端、Python/Go 语义;TS/JS 保持本语言 number/字符串 API,应用完成交付及身份隔离统一到四端。
177
+
178
+ ## 7. 测试证据
179
+
180
+ ### 7.1 历史隔离验证(2026-10-10)
181
+
182
+ 当轮只执行隔离单元回归和静态检查,没有启动或重建 Kite/Docker。遇到失败时立即停止,修复失败用例后从断点继续;此前通过且未受后续修改影响的用例未重复运行。
183
+
184
+ | 层级 | 证据 | 结果 |
185
+ | --- | --- | --- |
186
+ | Message Go | `go test ./internal/store ./cmd/message-go -count=1 -failfast`;对应包 `go vet` | 两个受影响包通过 |
187
+ | Group Go | `go test ./internal/repository ./internal/rpc ./internal/store ./internal/service ./cmd/group-go -count=1 -failfast`;对应包 `go vet` | 五个受影响包通过;原始 topic 排序和混合域幂等重试回归通过 |
188
+ | Python SDK | 现有 `test_sdk_facades.py`、`test_client.py` 单元用例,使用 `python -X utf8` 断点续跑;修改文件 `py_compile` | 累计 `529 passed`(facade 27、client 502);编译通过 |
189
+ | TypeScript SDK | 现有 client/transport 单元用例;`tsc --noEmit`;最终已读回归 | `253 passed`(client 210、transport 43);类型检查及最终已读 12 项通过 |
190
+ | JavaScript SDK | 现有 client 单元用例(jsdom、单 worker);`tsc --noEmit`;最终已读回归 | `155 passed`;类型检查及最终已读 12 项通过 |
191
+ | Go SDK | `go test . -count=1 -failfast`;最终 `go test . -run ReadState -count=1 -failfast`;`go vet ./...` | 根包单元、最终已读回归及 vet 通过 |
192
+
193
+ Python 环境未安装 ruff/mypy/pyright,当轮没有执行 Python 专用 lint 或类型检查;`py_compile` 只验证语法编译,不替代这些检查。真实 MySQL、多实例事务、单域集成/E2E、shared-AID cross-SDK 和双域部署验证均未执行。
194
+
195
+ ### 7.2 历史部署证据(2026-10-09)
196
+
197
+ 下列日志属于上一轮本机 Docker 验收,跨域为 `agentid.pub` ↔ `agentid.cn`;仅保留为历史证据,不能证明本轮提交竞态、补偿交付及混合域修复已通过部署验证。
198
+
199
+ | 层级 | 历史证据 | 当时结果 |
200
+ | --- | --- | --- |
201
+ | Message/Group MySQL | `read_state_review_mysql_message.log`、`read_state_review_mysql_group.log` | 条件 upsert、128 字符迁移边界通过 |
202
+ | 单域集成 | `read_state_review_integration_p2p.log`、`read_state_review_integration_group.log` | P2P/Group、幂等、topic 原值、未知映射和非法批量通过 |
203
+ | 单域 E2E | `read_state_review_e2e_p2p.log`、`read_state_review_e2e_targeted_final.log` | 多设备事件、重连补偿、定向 P2P 加密/非加密隔离通过 |
204
+ | cross-SDK 独立 AID 矩阵 | `tests/cross-sdk/artifacts/read-state/results.json` | Message/Group 基础矩阵通过 |
205
+ | cross-SDK 共享 AID 矩阵 | `tests/cross-sdk/artifacts/read-state-shared/results.json`、`read_state_review_cross_sdk_shared.log` | `read_state_matrix_shared_aid`、`group_read_state_matrix_shared_aid` 通过 |
206
+ | 本地双域 | `read_state_review_cross_domain_final.log`,run id `agentid-read-state-review-final-20261009` | 双方向 `READ_STATE_FEDERATION_EVENT_OK`、`READ_STATE_P2P_OK`、`READ_STATE_GROUP_OK`、`READ_STATE_CROSS_DEVICE_OK` 和 `PASS phase=read-state` |
207
+ | 两域 schema | `read_state_review_schema_final.log` | 两域结构一致,字段均为 128,topic 为 `utf8mb4_0900_bin`,主键和唯一索引符合设计 |
208
+
209
+ 历史双域日志中的 `agent.md` 警告与本功能无关;当时的已读阶段 federation binding、目标 AID 和状态断言有明确 PASS 标记。
210
+
211
+ ### 7.3 本轮验证(2026-10-11)
212
+
213
+ 以下记录本轮实际执行的隔离/mock 回归与静态检查。没有启动或重建 Kite/Docker,不引用历史结果作为本轮通过证据;失败即停,修复后从断点继续,分段通过数不当作同一次完整运行。
214
+
215
+ | 层级 | 实际命令与范围 | 结果 |
216
+ | --- | --- | --- |
217
+ | Message(`extensions/services/message/go`) | `go test ./internal/store -run '^(TestMessageReadState\|TestReadStatePosition\|TestValidateMessageReadState)' -skip 'MySQL' -count=1 -failfast -v`;`go test ./cmd/message-go -run '^(TestMessageReadAcceptsTopLevelOnlyWhenStatesAbsent\|TestGatewayMessageReadChangedEventIDHasUnambiguousParts\|TestMessageReadEventFailureLogOmitsTopic\|TestMessageReadStateInputsRejectNonStringScope\|TestGatewayMessageReadChangedTargetsOwnerAndAllowsFederation)$' -count=1 -failfast -v`;`go vet ./internal/store ./cmd/message-go` | 最终 store 37 个顶层测试通过,cmd 5 项保持通过,补修后两包 vet 再次通过;MySQL 显式跳过;独立复核确认热/冷缓存和已有更前定向事实的收敛,无剩余问题 |
218
+ | Group(`extensions/services/group/go`) | `go test -count=1 -failfast -skip '(MySQL\|Mysql\|MYSQL\|Integration)' ./internal/repository ./internal/store ./internal/rpc ./internal/service`;`go test -count=1 -failfast -run 'NewBusinessDispatcher\|GroupEvent\|GroupReadChanged' ./cmd/group-go`;`go vet ./internal/repository ./internal/store ./internal/rpc ./internal/service ./cmd/group-go` | 全部通过;真实 MySQL/Integration 显式跳过。RR 定向 `go test -count=1 -failfast -run '^TestGroupReadStateLocksAllTopicsBeforeResolvingLatePositions$' ./internal/repository` 红转绿,独立复核批次锁顺序与 CAS 无剩余问题 |
219
+ | Gateway(`extensions/services/gateway/go`) | `go test ./internal/httpserver -count=1 -failfast`;`go test ./internal/events ./internal/jsonrpc ./internal/kernelrpc ./cmd/gateway-go -count=1 -failfast`;`go vet ./...` | 全部通过,httpserver 16.494s;覆盖 home 域、scope/owner、Kernel source、显式 route 及本域/federation 无损整数 |
220
+ | Python SDK(`aun-sdk-core/python`) | `python -X utf8 -B -m pytest -x -p no:cacheprovider tests/unit/test_client.py::test_read_state_identity_recovery_cannot_reenter_after_aid_round_trip tests/unit/test_sdk_facades.py -q`;四修改文件内存 `compile` | 80 passed,2.83s;四 RPC 的真实 RpcPipeline/Recover/Initialize 身份往返先红后绿,编译通过 |
221
+ | Python 客户端相邻回归 | 临时目录隔离 wrapper 内执行 `pytest.main(['-x','-p','no:cacheprovider','tests/unit/test_client.py','-k','read_state or load_identity or connect or disconnect or close or app_event or protocol_event or call','-q','--tb=short'])`,以 `python -X utf8 -B -` 运行 | 90 passed、416 deselected,8.87s;不与上行累加 |
222
+ | Go SDK(`aun-sdk-core/go`) | 首轮 `go test . -run '^TestReadState' -count=1 -failfast`;最终窗口 `go test . -run '^TestReadState(InterleavedIdentityRoundTripDiscardsOldResult\|IdentityRoundTripDiscardsOldResult\|IdentitySwitchClearsCacheAndFilters\|CallDiscardsOldIdentityResponse)$' -count=1 -failfast`;`go vet .` | 首轮 12 个顶层测试通过,最终四项通过(含新增第 13 项);身份/cache/generation 同一临界区窗口独立复核闭合 |
223
+ | TS/JS SDK(各 `aun-sdk-core/ts`、`js`) | 下述分段 vitest 命令;各 `node --max-opt=0 node_modules/typescript/bin/tsc --noEmit --incremental false` | 每端分段累计 20 项通过;真实公开 close/loadIdentity 与 RpcPipeline 恢复 ABA 先红后绿,类型检查通过 |
224
+ | 验收工具(`aun-sdk-core`) | `python -X utf8 -m pytest tests/cross-sdk/runner/test_run_cross_sdk_e2e.py -k read_state -p no:cacheprovider -q -x --tb=short`;7 个修改 Python 文件 `ast.parse`;TS/JS 各 `node node_modules/typescript/bin/tsc --noEmit`;Go `go test ./cmd/cross-sdk-agent -run '^Test' -count=1` | 30 passed、169 deselected,2.17s;AST、两端类型检查和 Go agent 通过 |
225
+ | runner 全量 | 按 `-x` 在首个失败处停止 | 49 passed 后失败于既有 `test_cross_sdk_agents_skip_agent_md_upload_when_etag_is_in_sync[source_path2]`;保存的修改前 TS 原文已缺 `uploadAgentMd` 等字样,未改此无关逻辑,不能宣称全量通过 |
226
+ | 文档 | 专项契约、正式 RPC/API、Python 内置镜像、两处 Group 协议及索引引用;原文字节加精确替换重放与 `git diff --check` | 22 份既有文档逐项重放一致,14 个变更链接目标存在,索引行区间与差异检查通过;保留原有混合换行及其他工作区修改 |
227
+ | 部署链路 | 真实 MySQL 多实例、单域集成/E2E、shared-AID cross-SDK、双域、更新镜像 | 本轮未执行 |
228
+
229
+ Python 两组回归先设置 `PYTHONDONTWRITEBYTECODE=1` 和 `PYTHONPATH=D:/modelunion/kite/aun-sdk-core/python/src`。90 项 wrapper 仅把未显式指定 `aun_path/aunPath` 的 `AUNConfig.from_dict` 默认目录与 `AUN_PATH` 指向 `TemporaryDirectory`,保留测试自身 `tmp_path`,退出时清理;未创建脚本文件、未访问外部服务。验收工具 pytest 同样设置 `PYTHONDONTWRITEBYTECODE=1`。
230
+
231
+ TS/JS 每端实际命令统一为 `node --max-opt=0 node_modules/vitest/vitest.mjs run tests/unit/client.test.ts -t '<筛选>' --pool forks --maxWorkers=1 --bail=1 --cache=false`,依次执行:
232
+
233
+ | 筛选 | 每端实际结果 |
234
+ | --- | --- |
235
+ | `raw 已读事件拒绝\|保留显式 null` | 先 4 passed 后在 raw 首项失败停止;不计为完整通过 |
236
+ | `raw 已读事件拒绝` | 修复后 2 passed |
237
+ | `身份恢复请求跨公开关闭` | 真实恢复路径修复后 2 passed |
238
+ | `facade 原样传递\|缓存和重连\|回执不覆盖\|重连保留空查询\|身份及设备切换\|未知公共顺序\|公共位置比较\|网络 JSON\|缓存拒绝旧事件` | 12 passed |
239
+
240
+ TS 使用 node;JS 使用既有 jsdom/fake-indexeddb 配置。初段已通过的空值用例未受后续 raw/ABA 修改影响,按断点规则未重跑。
241
+
242
+ 检查限制:Python 的 ruff/mypy/pyright 未安装,未执行专用 lint/类型检查,内存编译与 AST 不替代这些检查;Go SDK 实际执行 `go test -race . -run '^TestReadState' -count=1 -failfast` 因缺 gcc 失败,不能称 race 通过。服务端与四端 SDK tracked 差异检查通过;CRLF 转换提示不是检查失败。此次没有真实 MySQL、跨进程多实例、网络部署或 Windows/Linux/macOS 全平台实跑证据。
243
+
244
+ ## 8. 本轮验收判定
245
+
246
+ - **功能正确性**:第 4.8 节 11 项修复及补充收尾已完成,独立代码复核与第 7.3 节列明的离线回归通过;不沿用 R-01~R-17 历史通过结论。
247
+ - **跨域能力**:双域在线事件、权限跨实例同步与 seq 映射部署验证本轮未执行。
248
+ - **文档一致性**:方案、详细设计、实施计划、集成/移植指南、正式 RPC/API、Python 内置镜像、Group 协议和既有索引已同步校核。
249
+ - **SDK 对齐**:本轮空值保留、原事件 scope、必需 owner 与真实 A→B→A 身份代际边界四端隔离回归已确认;共享 AID cross-SDK 部署矩阵本轮未执行。
250
+ - **交付结论**:**本轮代码修复与已执行的隔离验证完成,部署层验收未执行**。最终结果在第 7.3 节记录,不将历史结果作为本轮通过依据。
251
+
252
+ 本报告区分当前工作区代码验证与历史 Docker 证据;工作区中其它任务的修改按约束保留,不纳入本功能审查结论。