@agentunion/fastaun 0.5.27 → 0.5.29

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 (77) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/_packed_docs/CHANGELOG.md +55 -0
  3. package/_packed_docs/INDEX.md +23 -3
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +15 -11
  5. package/_packed_docs/sdk/0.5.28-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +228 -0
  6. package/_packed_docs/sdk/0.5.28-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +146 -0
  7. package/_packed_docs/sdk/0.5.29-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +180 -0
  8. package/_packed_docs/sdk/0.5.29-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +164 -0
  9. package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +90 -90
  10. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +6 -4
  11. package/_packed_docs/sdk/INDEX.md +14 -4
  12. 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
  13. 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 +1 -1
  14. package/_packed_docs//346/216/247/345/210/266/346/266/210/346/201/257/344/270/216/350/256/276/345/244/207/350/203/275/345/212/233/345/215/217/345/225/206/346/226/271/346/241/210.md +426 -0
  15. 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 +279 -229
  16. package/_packed_docs//351/243/216/346/216/247/345/223/215/345/272/224/344/270/216SDK/345/215/217/345/220/214/346/226/271/346/241/210.md +381 -0
  17. package/dist/agent-md-changes.d.ts +30 -0
  18. package/dist/agent-md-changes.js +168 -0
  19. package/dist/agent-md-changes.js.map +1 -0
  20. package/dist/agent-md-schema.d.ts +1 -0
  21. package/dist/agent-md-schema.js +29 -12
  22. package/dist/agent-md-schema.js.map +1 -1
  23. package/dist/agent-md.d.ts +9 -0
  24. package/dist/agent-md.js +107 -36
  25. package/dist/agent-md.js.map +1 -1
  26. package/dist/aid-store.d.ts +14 -1
  27. package/dist/aid-store.js +163 -61
  28. package/dist/aid-store.js.map +1 -1
  29. package/dist/auth.d.ts +10 -0
  30. package/dist/auth.js +44 -4
  31. package/dist/auth.js.map +1 -1
  32. package/dist/client/control-messages.d.ts +31 -0
  33. package/dist/client/control-messages.js +496 -0
  34. package/dist/client/control-messages.js.map +1 -0
  35. package/dist/client/delivery.js +12 -3
  36. package/dist/client/delivery.js.map +1 -1
  37. package/dist/client/group-state.js +2 -3
  38. package/dist/client/group-state.js.map +1 -1
  39. package/dist/client/lifecycle.js +8 -0
  40. package/dist/client/lifecycle.js.map +1 -1
  41. package/dist/client/rpc-pipeline.js +76 -13
  42. package/dist/client/rpc-pipeline.js.map +1 -1
  43. package/dist/client/v2-e2ee.d.ts +3 -0
  44. package/dist/client/v2-e2ee.js +13 -4
  45. package/dist/client/v2-e2ee.js.map +1 -1
  46. package/dist/client.d.ts +27 -1
  47. package/dist/client.js +348 -38
  48. package/dist/client.js.map +1 -1
  49. package/dist/discovery.d.ts +3 -0
  50. package/dist/discovery.js +53 -2
  51. package/dist/discovery.js.map +1 -1
  52. package/dist/events.js +14 -1
  53. package/dist/events.js.map +1 -1
  54. package/dist/ownership-v21.d.ts +2 -1
  55. package/dist/ownership-v21.js +17 -3
  56. package/dist/ownership-v21.js.map +1 -1
  57. package/dist/ownership-workflow.js +9 -5
  58. package/dist/ownership-workflow.js.map +1 -1
  59. package/dist/risk-control.d.ts +68 -0
  60. package/dist/risk-control.js +344 -0
  61. package/dist/risk-control.js.map +1 -0
  62. package/dist/semantic-facade.js +4 -2
  63. package/dist/semantic-facade.js.map +1 -1
  64. package/dist/semantic-protocol.js +2 -0
  65. package/dist/semantic-protocol.js.map +1 -1
  66. package/dist/storage/lowlevel.d.ts +1 -0
  67. package/dist/storage/lowlevel.js +2 -2
  68. package/dist/storage/lowlevel.js.map +1 -1
  69. package/dist/tools/cross-sdk-agent.d.ts +1 -1
  70. package/dist/tools/cross-sdk-agent.js +16 -34
  71. package/dist/tools/cross-sdk-agent.js.map +1 -1
  72. package/dist/transport.d.ts +21 -1
  73. package/dist/transport.js +199 -20
  74. package/dist/transport.js.map +1 -1
  75. package/dist/version.d.ts +1 -1
  76. package/dist/version.js +1 -1
  77. package/package.json +8 -8
@@ -0,0 +1,426 @@
1
+ # 控制消息与设备能力协商方案
2
+
3
+ > 状态:源码已实施并完成受影响本地回归,尚未部署新版服务;新增机制线上 E2E 与真实旧 SDK 的 2×2 验收待执行。日期:2026-10-29。
4
+
5
+ ## 1. 目标与已确定的规则
6
+
7
+ 本方案解决两类需求:agent.md 更新后由 SDK 及时通知 Owner,并在 Owner 离线后恢复感知;Menu Protocol 等应用操作优先于普通消息投递和处理。
8
+
9
+ | 项目 | 确定的规则 |
10
+ | --- | --- |
11
+ | 新增发送参数 | 仅 `message_class`,取值 `sdk_control`、`control`、`normal` |
12
+ | 默认类别 | 缺省等同 `normal`;不根据 payload 类型或 `need_render` 自动推断 |
13
+ | 优先级 | **`sdk_control > control > normal`**,不增加 `priority` 参数 |
14
+ | 消费方 | `sdk_control` 仅由 SDK 消费;`control`、`normal` 交给应用 |
15
+ | 保存方式 | `ttl > 0` 为 volatile;缺省或 `ttl <= 0` 为 persistent |
16
+ | 参数限制 | 不增加 `consumer`、`storage`、`app_control`;不存在 `sdk_normal` |
17
+ | 加密 | 复用现有明文和 E2EE 机制,与类别和 TTL 独立组合 |
18
+ | 旧端兼容 | 不支持的 SDK 控制投递由服务端丢弃;应用控制消息降级为普通消息 |
19
+ | 能力来源 | Gateway 持久记录设备能力,并保存当前连接的协商结果 |
20
+ | 跨服务使用 | Message / Group 使用 TTL 缓存,通过内部 RPC 回源 Gateway |
21
+ | 游标 | 三类持久消息隔离 SEQ、cursor、ACK;volatile cursor 同样隔离 |
22
+
23
+ 本文中的“不增加 storage”指不增加发送请求参数;现有响应的 `storage=persistent/volatile` 判别字段继续保留。
24
+
25
+ ## 2. 当前实现与目标差异
26
+
27
+ 1. Gateway 已在 `auth.connect` 保存连接能力,并通过上线事件、内部查询向业务服务传递部分能力;尚需增加本方案的设备能力持久字段和查询契约。
28
+ 2. Gateway 与 Message 各自维护自己的 `agent_devices`,不能因表名相同而假定同库、同表或同实例。
29
+ 3. 当前 P2P 的持久 SEQ 按接收设备分配,新 SDK 按 sender 隔离 peer cursor;这不等于已经按消息类别隔离。
30
+ 4. 当前 P2P 正 TTL 消息使用内存 mailbox,支持有效期内 Push、Pull、slot ACK,重启允许丢失;不能将 volatile 写成“只投在线连接”。
31
+ 5. 当前 Group 有独立消息 SEQ、事件 SEQ 和成员设备 cursor;控制流不能借用群事件 SEQ。Group 的三类分流、兼容投影及本文要求的正 TTL 消息路径属于待实施范围。
32
+ 6. 当前 Ownership 有特定 payload 的 SDK 内部消费路径,`need_render=false` 只表达渲染意图,不提供通用 SDK 控制隔离。
33
+ 7. 当前 ETag 观察可复用;本次源码核对确认 RPC 响应有注入,不能据部分历史文档认定所有通知路径均已注入。本方案的主动感知不依赖每条通知都带 ETag。
34
+
35
+ ## 3. 消息契约
36
+
37
+ ### 3.1 类别、TTL 与默认值
38
+
39
+ | `message_class` | 消费方 | 调度次序 | 不支持该能力的接收端 |
40
+ | --- | --- | --- | --- |
41
+ | `sdk_control` | SDK 内部处理器 | 第一 | 丢弃该接收目标的投递,禁止降级 |
42
+ | `control` | 应用原有消息处理器 | 第二 | 通过普通流投递,原业务 payload 不变 |
43
+ | `normal` / 缺省 | 应用原有消息处理器 | 第三 | 沿用现有流程 |
44
+
45
+ 显式非字符串、空字符串、未知枚举值应返回参数错误;只对字段缺省使用 `normal`。发送幂等比较包含类别和原 TTL 语义,同一逻辑消息的重试不能改类别或延长有效期。
46
+
47
+ | TTL | 保存与恢复 |
48
+ | --- | --- |
49
+ | 缺省、0、负数 | 现有持久存储、WAL 和保留策略,分配所属流的持久 SEQ |
50
+ | 正整数 | 仅使用 volatile 内存路径,不写正文、投递状态的数据库或 WAL,不分配持久 SEQ |
51
+
52
+ P2P 继续沿用当前正 TTL 的 1–3600 秒钳制规则;Group 新路径采用相同单位和范围。服务端计算有效创建、到期时间,跨域只传递剩余有效期,不能在中继或重试时重新开始完整 TTL。
53
+
54
+ 连接级 `delivery_mode` 仍决定 queue / fanout 投递方式,与消息类别、TTL 分开。Group 保持 fanout。`notify()` 仍是无 SEQ、无 ACK 的在线尽力通知,不替代本文消息通道。
55
+
56
+ ### 3.2 原始类别与实际投递流
57
+
58
+ 原始 `message_class` 是发送者的消息语义,保存后不可变。服务端内部另存实际投递流;它可能因兼容处理由 `control` 变为 `normal`,不是新的应用发送参数。
59
+
60
+ 控制类别放在 payload 外层、服务端可识别的封套中,不能仅藏在密文里。`payload.type` 继续表示 Menu、文本等业务类型,外层 `type` 继续表示既有加密封装。
61
+
62
+ E2EE 使用现有 protected headers 的认证机制保护原始类别,与外层声明一致;沿用现有 AAD 格式。降级只改变服务端投递位置,不篡改原始 protected headers、ciphertext、业务类型和签名材料。新增 SDK 校验两者关系;旧 SDK 仍按既有格式解密普通兼容投递。
63
+
64
+ 明文继续使用当前认证和消息签名机制,不因标记为控制消息获得额外操作权限。加密不被理解为无身份校验的任意密文转发。
65
+
66
+ ## 4. 能力声明与持久记录
67
+
68
+ ### 4.1 声明内容
69
+
70
+ `auth.connect.capabilities` 增加下列结构,字段名为本方案的拟定 wire 契约:
71
+
72
+ | 字段 | 内容 |
73
+ | --- | --- |
74
+ | `message_classes_v1` | 按 `p2p`、`group` 列出当前 SDK 完整支持的类别 |
75
+ | `sdk_control_protocols` | 按协议名列出支持的版本,例如 `aun.agent_md.changed: [1]` |
76
+
77
+ `normal` 为既有基础能力;某 scope 缺省时只支持普通消息。SDK 只有在分流、解密、内部消费、cursor 和 ACK 全链路实现后,才声明该 scope 支持控制类别。
78
+
79
+ 通用 `sdk_control` 能力不等于支持全部未来协议。SDK 处理器在既有封套元数据中自动填入 `sdk_control_protocol` 和 `sdk_control_version`,不是新的应用发送参数;加密路径放入 protected headers,明文路径沿用经过消息签名的封套元数据。发送、入队和投递时检查相应协议;未知协议不得落到应用通用回调。
80
+
81
+ Gateway 响应同时给出服务端支持范围与本次协商结果。连接可用能力取声明与服务端支持范围的交集,四端 SDK 不根据版本字符串推断支持。
82
+
83
+ ### 4.2 数据库与连接状态
84
+
85
+ | 状态 | 主键或作用域 | 保存内容 |
86
+ | --- | --- | --- |
87
+ | 设备最近声明 | `(aid, device_id)` | 完整规范化声明、协议版本、能力修订号、声明时间 |
88
+ | 当前连接协商态 | `(aid, device_id, slot_id, connection_id)` | 本连接声明、协商结果、连接代次、设备能力修订号 |
89
+
90
+ 设备持久记录由 Gateway 维护,可扩展其现有 `agent_devices` 表,增加 `capabilities_json`、`capability_revision`、`capabilities_declared_at_ms`。数据库字段为空与一次明确的旧端声明要能区分。
91
+
92
+ 不新增独立能力服务或共享库。Gateway 逻辑服务内部对同一设备的更新必须有持久、单调的修订号;多 Gateway 实例通过该服务自己的权威存储协调,不能分别写互不相关的数据库后各自声称权威。
93
+
94
+ 握手按完整快照替换本设备最近声明,不把缺省的新能力从历史记录中合并回来。声明变化才增加能力修订号;连接代次每次建立均变化。写库与发布顺序为:认证并校验声明 → 持久化 → 建立可投递连接态 → 返回协商结果、发布上线事实。
95
+
96
+ 新机制所需持久写入失败时,不宣告控制能力协商成功;调用方收到明确的可重试结果,不使用尚未提交的历史能力制造离线投递判断。普通旧连接沿用既有准入策略。
97
+
98
+ ### 4.3 离线、未知与不支持
99
+
100
+ - **离线但已有声明**:继续使用数据库中的最近声明,不因断线或缓存过期删除能力。
101
+ - **握手完成但没有新能力字段**:该连接明确不支持,持久记录更新为相应普通能力快照。
102
+ - **从未完成声明**:状态为未知,不能当成一次明确的不支持。
103
+ - **查询故障**:状态为不可确定,不持久写成不支持,也不触发不可逆丢弃。
104
+
105
+ 设备快照用于离线规划,在线投递以具体连接协商态准入。同设备不同 slot 同时运行新旧 SDK 时,不取能力并集给旧连接授权。某旧连接不支持,只丢弃该连接的 SDK 控制投递责任,不能删掉同设备其他支持连接仍需消费的正文。
106
+
107
+ 所有连接离线时,设备记录代表最近一次完整声明;重新上线再次握手校正。`slot_id` 不作为设备能力持久主键,但仍隔离连接和消费 cursor。
108
+
109
+ ## 5. Gateway 回源接口与 TTL 缓存
110
+
111
+ ### 5.1 唯一来源与内部接口
112
+
113
+ 新增服务内部批量查询 `gateway.query_device_capabilities`,复用既有 service plane 的认证、发现和超时机制。只允许有权限的 Message / Group 等服务调用,不开放成应用可枚举其他设备的接口。
114
+
115
+ | 请求 / 响应 | 契约 |
116
+ | --- | --- |
117
+ | 请求 | 目标 `(aid, device_id)` 数组;按服务现有请求大小限制分批 |
118
+ | 设备结果 | `found`、能力快照、能力修订号、声明时间、设备可用状态 |
119
+ | 在线结果 | 当前连接的内部 slot、connection ID、协商能力和连接代次 |
120
+ | 失败 | 查询错误与 `found=false` 分开,不能把错误缓存为不支持 |
121
+
122
+ Gateway 读自己的数据库和连接目录;Message / Group 不直连 Gateway 的数据库或 Redis。各模块只依赖协议,可分别用不同语言和数据库实现。
123
+
124
+ ### 5.2 两类缓存
125
+
126
+ | 缓存 | key | 建议初始 TTL | 失效后 |
127
+ | --- | --- | --- | --- |
128
+ | 设备能力 | `(aid, device_id)` | 30 秒 | 批量回源 Gateway |
129
+ | 连接 / slot 能力 | `(aid, device_id, slot_id)`,值含 connection ID | 30 秒 | 查询当前连接集合 |
130
+ | 设备未找到 | `(aid, device_id)` | 5 秒 | 重新查询,不永久认定不支持 |
131
+
132
+ 初始 TTL 复用现有在线查询缓存量级,实施时作为内部常量核验,不新增应用参数。服务本地内存缓存即可;缓存无需耐久化,服务重启后回源恢复。数据库能力记录不设这类缓存 TTL。
133
+
134
+ 相同 key 的并发回源合并,群 fanout 批量查询,避免每条消息、每个设备各发一次 RPC。缓存内旧值不能越过当前连接最终门禁。
135
+
136
+ ### 5.3 上下线事件与失效竞态
137
+
138
+ 1. `client.online` 携带设备能力快照、能力修订号、本次协商态、slot、connection ID 和连接代次。
139
+ 2. Message / Group 按修订号更新设备缓存,失效该设备连接列表及对应 slot 缓存;有完整本连接快照时可直接填充。
140
+ 3. `client.offline` 只删除匹配 connection ID / 代次的连接项,失效对应 `(aid, device_id, slot_id)` 及设备连接列表;保留设备历史能力。
141
+ 4. 事件使 key 的本地缓存代次变化;较早发出的查询响应不得在失效后重新填入旧值。
142
+ 5. 较旧修订号的事件或响应不能覆盖新能力;迟到旧连接离线不能删除已重连的新连接。
143
+
144
+ 事件不是唯一恢复来源。TTL 和查询覆盖丢事件、服务重启;Gateway 在实际推送、Pull / ACK 接口准入处再次核对本连接能力,缓存陈旧不能造成 SDK 消息泄漏。
145
+
146
+ ## 6. 接收判定与兼容矩阵
147
+
148
+ ### 6.1 按目标判定
149
+
150
+ | 能力状态 | `sdk_control` | `control` |
151
+ | --- | --- | --- |
152
+ | 支持类别及所需协议 | 进入 SDK 控制流 | 进入应用控制流 |
153
+ | 明确不支持 | 丢弃该目标投递,返回未投递原因 | 转换为普通流投递 |
154
+ | 未知,persistent | 在隔离的待判定投递中保留,声明明确后处理 | 可直接按普通流接收,保证业务可达 |
155
+ | 未知,volatile | 有效期内留在隔离内存待判定;到期自然失效 | 按普通 volatile 投递 |
156
+ | 回源暂时失败 | 不做不可逆丢弃,持久待判定或返回可重试错误 | 可按普通流接收 |
157
+
158
+ 设备存在在线连接时,先按各连接协商态建立投递责任;只要还有支持的目标,不能因较晚的旧 slot 更新了设备快照而提前丢弃整台设备的消息。全部连接离线时才按设备最近声明规划:明确支持可持久入队,明确不支持则丢弃。不可逆丢弃前使用本次权威查询或当前认证连接确认,不能只凭可能遗漏上线事件的旧负缓存。丢弃只终结对应目标责任,不是消费成功,不产生应用回调或伪造接收 ACK;发送结果区分 accepted、partial、dropped 及原因。
159
+
160
+ 普通 TTL、queue / fanout 和设备选择规则继续适用。不能因 `control` 降级就改为持久、重新延长 TTL 或把加密消息改成明文。
161
+
162
+ ### 6.2 最终准入与多设备
163
+
164
+ Message / Group 在入队时优先参考当前连接集合,全部离线再使用设备记录,在投递或拉取时根据认证连接判定。Gateway 在写入客户端 WebSocket 前执行最终检查;原始 SDK 控制内容不通过旧端通用消息事件发送。
165
+
166
+ 同一 AID 的各设备独立选择流;同设备各 slot 独立消费。一个设备或 slot 不支持,不代表整个 AID 不支持,也不影响其他目标的 ACK、队列和正文保留。
167
+
168
+ 服务端从认证上下文获得请求连接能力,客户端不能通过伪造 `_auth` 或 `message_class` 读取其他流。控制 SDK 的存在也不改变 Owner、群成员或业务命令授权。
169
+
170
+ ## 7. 持久 SEQ、cursor 与扫描证明
171
+
172
+ ### 7.1 P2P 空间
173
+
174
+ | 内容 | 作用域 |
175
+ | --- | --- |
176
+ | 分配器、可靠 Head、保留边界 | `(receiver_aid, device_id, message_class)` |
177
+ | global cursor | 上述作用域加 `slot_id` |
178
+ | peer cursor | 上述作用域加 `slot_id, sender_aid` |
179
+ | 去重与投递映射 | 来源 message ID、目标设备、流、投影代次;消费责任另按 slot 区分 |
180
+
181
+ 已有 `normal` SEQ、cursor 原值继续使用,不重新编号。`control`、`sdk_control` 各自从独立空间开始。三个空间可同时出现相同数字,单个 `seq` 没有跨流比较意义。
182
+
183
+ `peers / pull / ack / history` 增加可选 `message_class` 选择器,缺省只选择 `normal`,不把旧请求解释为三流聚合。新 SDK 内部独立查询和调度控制流,公开普通收件 API 不返回 SDK 控制正文。
184
+
185
+ ### 7.2 Group 空间
186
+
187
+ Group 按 `(group_id, message_class)` 分配三类消息 SEQ,cursor 按 `(group_id, aid, device_id, slot_id, message_class)` 隔离。现有群事件 `event_seq` 和事件 ACK 保持独立,不用来承载控制消息。
188
+
189
+ 成员加入、退出、屏蔽和密钥 epoch 继续决定来源消息可见性。新加入成员不能因为后来产生兼容投影而收到加入前的控制内容;兼容 SEQ 不能替代来源时刻的成员资格检查。
190
+
191
+ ### 7.3 ACK 与可靠覆盖
192
+
193
+ 每个流独立维护 `head_seq`、扫描起点、已覆盖终点、ACK 和 retention floor;ACK、Piggy ACK、强制拉取、Tail、History、缓存 key 和本地状态都必须带流作用域。
194
+
195
+ 按 sender、成员资格或兼容投影过滤后,数字 SEQ 可能稀疏。新 SDK 只根据绑定“身份、设备、slot、流、peer / group、固定 Head”的可靠扫描证明推进覆盖,不能用消息数、最大返回 seq 或空页猜测完成。
196
+
197
+ 旧 SDK 的普通流输出必须使用该版本已经理解的 `contiguous_seq`、既有完成 / 保留边界契约解释被过滤区间,尤其空末页。不能要求旧 SDK 理解新增 proof,也不能发送占位控制消息补洞;未通过指定旧版本验证的投影视图不得上线。
198
+
199
+ ACK 只确认本次所属流的实际处理或已证明扫描责任。SDK 控制 ACK 不改变普通 cursor;服务端丢弃、转换和过期有独立终态,不等价于应用已读。
200
+
201
+ ## 8. 应用控制降级与版本往返
202
+
203
+ ### 8.1 来源记录与普通兼容投影
204
+
205
+ `control` 保留一份不可变逻辑来源记录。面向不支持连接时创建 normal 投影,保持同一业务 `message_id`、原正文、加密材料、来源成员资格和有效期,分配 normal SEQ。
206
+
207
+ 投影是服务端投递引用,不是再次执行业务 send;不重新生成密文、不复制私钥、不重新认定发送者。服务端实际流为 normal,旧客户端看到其能消费的普通消息形态。
208
+
209
+ 初次入队即知道设备不支持时,可直接规划 normal 目标,不先分配无用控制投递。后续才发生降级时,必须执行下一节的转换。
210
+
211
+ ### 8.2 未完成投递的转换
212
+
213
+ 1. 按 slot 查明来源投递是否已成功消费;已完成的不补投。
214
+ 2. 为尚未完成且需要降级的责任创建带投影代次的 normal 引用,分配在该流当前已发布 Head 之后的新 SEQ。
215
+ 3. 持久原子记录来源、目标、投影代次、normal 位置与源控制位置的转换终态,再允许原控制扫描覆盖完成。
216
+ 4. normal 投影完成前,原来源仍保留对应未完成责任;重试以投影键幂等,不重复分配 SEQ。
217
+ 5. 控制流转换完成不能直接 ACK normal 流,normal 仍由接收应用正常消费、ACK。
218
+
219
+ 不能永久复用早先预留的 normal alias:新 SDK 可能已在普通流跨过它,而原 control 尚未处理;降级后复用旧 SEQ 会漏收。不能通过回退全局 cursor 恢复,以免漏掉本地高水位保护或无限重放普通历史。
220
+
221
+ P2P 的 normal 投影在同设备、同来源、同一转换批次内共享 SEQ,slot 只记录各自责任;不按每个 slot 重复分配。不同时间的新降级批次若已跨过旧位置,需要新投影代次。
222
+
223
+ Group 在群 normal 流为同来源、同一转换批次分配一次投影位置;各成员设备 / slot 单独记录可见与完成状态。不能每个设备各递增一次群普通 SEQ,也不能把整群消息统一降级。
224
+
225
+ Group 延迟投影必须保存来源发送时的成员资格与 epoch,按来源资格过滤;不能用 alias 的新 SEQ 与 `join_msg_seq` 直接比较。所有被过滤区间都要满足上一节的新旧扫描证明要求。
226
+
227
+ ### 8.3 新旧 slot 并存及在途结果
228
+
229
+ 支持连接从 control 消费,旧连接从 normal 投影消费。某 slot 一旦已建立未完成的兼容投递,升级后先按已有投影完成,不反复在两流间搬移。来源完成状态用于抑制之后的重复责任。
230
+
231
+ 能力变化期间已经在途的控制或普通回调可能重复。以来源 `message_id` 做业务幂等,不承诺跨重启 exactly-once。迟到 ACK 按原流、位置、投影代次核对,不能错误完成后来同数字 SEQ 的另一条消息。
232
+
233
+ `sdk_control` 永不创建 normal 投影。协议降级后,其不支持目标按规定终结为 dropped,不能用普通消息补偿。
234
+
235
+ ## 9. volatile 分流
236
+
237
+ 三类 volatile mailbox、去重、opaque cursor、slot lease 和 ACK 分别隔离;不进入任何持久 SEQ tracker。epoch 仍防止服务重启后的游标复用。
238
+
239
+ volatile 消息允许在内存有效期内补拉,TTL 到期、内存淘汰或服务重启后的边界沿用现有机制。它与“发送时必须在线”的 notify 不同。
240
+
241
+ control 降级在内存中建立 normal 投影,不延长原 `expires_at`;SDK 控制不支持则按目标丢弃。到期 ACK 幂等结束,不无限重试。Group 新增内存投递路径同样遵守成员权限及 epoch。
242
+
243
+ TTL 数据路径不得为控制类别偷偷增加持久消息正文、WAL 或持久投影。设备能力数据库属于握手元数据,不是 volatile 消息持久化。
244
+
245
+ ## 10. 优先调度
246
+
247
+ 固定次序为 **`sdk_control > control > normal`**,所有就绪消息工作项按此调度,不增加可配置数字优先级。
248
+
249
+ | 阶段 | 实施规则 |
250
+ | --- | --- |
251
+ | SDK 发送 | 等待发送的消息按三类出队;已提交的发送不撤销 |
252
+ | 服务端发送 / 入队 | 已进入调度器的消息工作按三类选择,不让普通批次占满待处理队列 |
253
+ | Gateway Push | 待写消息按三类选择;已写入 WebSocket / TCP 的数据不抢占 |
254
+ | SDK Pull | 三流独立待拉状态,共享既有网络 Gate 时优先高类别 |
255
+ | 解密与交付 | 有独立待处理队列,SDK 控制不排到普通应用回调之后 |
256
+ | 同类工作 | 复用 peer / group 公平调度;单个消费流保持可靠顺序 |
257
+
258
+ 同一客户端现有 N=4 共享 Pull Gate 不因为三流变成 12;调度 key 增加类别。同类中保留既有前后台策略,不能让前台 normal 越过已就绪 sdk_control。
259
+
260
+ 类别只决定消息工作优先级,不降低认证、心跳、ACK 等协议控制帧的既有必要保障。ACK 按各流责任提交,不为了“优先”跳过本流未完成消息。
261
+
262
+ 不承诺抢占正在运行的应用操作,也不承诺跨流全局回调顺序。持续高优先级积压时,普通消息可能延迟;本版不隐含轮转配额来改变用户确定的固定优先顺序,沿用现有配额和背压限制发送压力。
263
+
264
+ Menu 的请求、响应均显式使用 `control`,保持现有业务类型、请求关联 ID 和权限门。优先投递不代表旧请求可无条件补执行;业务继续按自身关联、幂等和有效期处理。
265
+
266
+ ## 11. SDK 内部消费与应用边界
267
+
268
+ 1. 在通用应用事件分发前检查消息原始类别、实际流及协议版本,解密并验证受保护语义。
269
+ 2. `sdk_control` 仅进入已注册内部处理器;未知协议或格式错误在内部按既定终态处理,不能 fallback 为应用消息。
270
+ 3. 内部处理结果可靠提交或已登记可恢复责任后,才推进该流 ACK;失败保留重试责任。
271
+ 4. `control` 和 `normal` 沿用应用消息回调与批量接口,保留既有 handler 成功后提交规则。
272
+ 5. 隔离覆盖 Push、Pull、History、原始 RPC 返回、聚合列表、batch、self-sync 和诊断展示,不能只过滤 `message.received`。
273
+
274
+ 公开普通 History 不混入 SDK 控制正文。服务内部的 SDK 控制历史读取只供处理器使用,并有连接能力门禁。SDK 控制不会自动进入聊天未读、渲染或用户离线推送。
275
+
276
+ SDK 内部状态按 issuer、当前 AID、device、slot、类别和来源隔离;切换身份、重连及 A→B→A 都使用身份代次丢弃迟到结果。
277
+
278
+ 已有 Ownership 特定控制 payload 不因本方案自动改 wire 类型;若以后迁入新流,需独立列出兼容迁移与测试,不能使旧端丧失原有流程。
279
+
280
+ ## 12. agent.md 更新后通知 Owner
281
+
282
+ ### 12.1 发送链路
283
+
284
+ 上传仍使用既有签名、CAS 和权限规则。只有确认远端提交成功后,SDK 才形成 `sdk_control` 通知:协议 `aun.agent_md.changed`、版本 1,内容包含目标 AID 与本次提交的 ETag。
285
+
286
+ 通知走现有 P2P E2EE,不传正 TTL,因此是持久消息;Owner 来自本次提交后的可信材料和相应生命周期规则。普通 Agent 使用 Ownership 当前事实,Group / Org 使用各自专属生命周期,不混用授权机制。
287
+
288
+ 提交成功后尽快发送,通知失败不重新 PUT agent.md、不把已成功上传报告成失败。SDK 为该上传后续通知保留最小待办,并复用原 message ID 重试;这不是恢复已废弃的 Ownership 持久任务。
289
+
290
+ 为覆盖“PUT 成功但响应丢失”及“提交后进程崩溃”,在上传阶段记录最小提交关联;恢复时先查询验证是否提交,再决定创建通知,不能盲目重复上传或通知未提交内容。上传者身份切换后不得用新身份发送旧待办。
291
+
292
+ 服务端明确丢弃不支持目标时,该目标不做无限补发;其他支持设备继续接收。接收端未来升级的首次对账负责恢复当前状态。
293
+
294
+ ### 12.2 Owner 接收与事件
295
+
296
+ 通知只是一条变更线索,不授予或改变 Ownership。Owner SDK 校验发送认证、协议与关联目标后,回源获取并验证最新 agent.md,再依据当前关系判断本端是否为相应 Owner。
297
+
298
+ ETag 为不透明版本标识,只能比较相等与否,不能按字符串、时间戳推断新旧。晚到通知不能把较新观察替换成旧正文;复用现有观察代次、下载单飞、验签与缓存提交保护。
299
+
300
+ 验证后的远端版本与本端确认状态不同时,形成 `agent_md_changed`,事件包含 `{aid, etag, event_id}`。将已验证版本与待交付事件一起持久提交,event ID 在同一次变化的重试中稳定;同进程重复通知、Push / Pull 和同版本回源复用已有责任。应用不收到控制消息原文。
301
+
302
+ 既有 `agentmd.changed` 的观察语义保留;新增事件在“已验证并确认实际变化”后发布,不把未验证提示冒充已更新。首次基线对账可形成当前快照事件;事件全部应用 handler 成功后再标记完成,重启重放未完成事件,应用按 event ID 幂等。不得仅因已更新缓存 ETag 就省略尚未交付的事件,也不承诺跨崩溃 exactly-once。
303
+
304
+ 通知在持久登记目标的刷新责任后可 ACK;下载失败继续刷新重试,不能因为已经 ACK 就丢失任务。刷新完成后将责任原子转为待交付事件;处理器下载后直接 ACK 的路径也必须先保存事件责任。事件 ID 标识一次已确认变化,不只按 ETag 永久去重,避免 A→B→A 的真实变化被抑制。
305
+
306
+ ### 12.3 上线对账与缺失通知
307
+
308
+ Owner 连接恢复后,针对其已知下属清单执行当前 ETag 对账;关系清单以 Storage 中规范 `sub_aids/.../owner.md` 等可验证权威材料恢复,辅以已登记目标,不凭 P2P 建立关系。
309
+
310
+ 按既有 peer / 下载并发限制批量完成,对正在持有的目标低频复核,覆盖旧上传 SDK 不发通知、通知被丢弃、发送方未恢复等情况。对账是状态收敛保证,P2P 是在线加速;超过消息保留窗口仍靠权威状态恢复。
311
+
312
+ 不宣称无网络或旧 Owner SDK 可立即得到新事件。新 Owner SDK 的在线感知延迟由消息传递与验签下载决定,重连感知由清单对账完成;验收记录实际耗时,不虚构固定毫秒保证。
313
+
314
+ ## 13. 跨域与服务能力协商
315
+
316
+ 设备能力由接收 AID 的 home 域 Gateway 权威提供。发送域不直接访问对端数据库,不把发送者声明当成接收者能力。
317
+
318
+ 目标域 Message / Group 使用本域 Gateway 查询;涉及外域成员时按 issuer 批量走已有 federation / service plane 查询或目标域投递入口,由目标域执行最终能力判断,禁止未经发现的任意地址回源。
319
+
320
+ 跨域封套携带原类别、协议版本、原身份材料与剩余 TTL。源域和目标域服务都必须支持相应 scope 的新契约,保留原签验和成员权限,不能静默剥离未知字段。
321
+
322
+ 目标服务不支持新协议时:`sdk_control` 返回明确不支持,不通过旧普通 send 兜底;`control` 可在可信边界降级为既有普通消息,TTL 和加密能力必须仍满足原要求。目标 TTL 能力不足时不能改为持久化。
323
+
324
+ 本方案不扩大当前 peer 模式的部署承诺。多实例实现仍需确保所属持久流的分配、ACK 和投影事务一致,以及 volatile 的 send / pull / ack 路由一致;不能仅增加能力缓存就声称解决全部消息分片。
325
+
326
+ ## 14. 服务端与 SDK 改动清单
327
+
328
+ ### 14.1 Gateway
329
+
330
+ - `go/internal/agentdevices/store.go`:扩展设备声明持久字段、修订号及批量读取;保留原设备启停逻辑。
331
+ - `go/internal/clientws/state.go`:声明规范化、服务端能力返回、连接协商态,不继承旧声明中缺失的新能力。
332
+ - `go/internal/httpserver/server.go` 及现有推送 / service plane 路径:持久提交顺序、内部能力查询、上线事实、连接代次过滤、最终 Push / RPC 准入。
333
+ - 多 Gateway / federation 路径:能力查询路由和可信来源校验;不依赖 Message / Group 共用数据库。
334
+
335
+ ### 14.2 Message
336
+
337
+ - `go/internal/online/tracker.go`:设备与连接能力分开缓存;上下线失效和在途回源代次隔离,审查现有能力合并路径。
338
+ - `go/internal/p2p/send.go`、`pull.go`、`ack.go`:类别选择、入队判定、扫描证明、三流 ACK 和普通兼容投影。
339
+ - `go/internal/p2p/volatile_mailbox.go`:类别作用域、内存降级投影、lease / ACK / TTL 隔离。
340
+ - `go/internal/store/message_store.go`、WAL / SEQ 分配和缓存:三流记录与事务、幂等约束、投影代次、GC、normal 查询排除 SDK 控制。
341
+ - `go/cmd/message-go/main.go`:内部查询接入、事件失效、分流调度、响应状态和联邦 ingress。
342
+
343
+ ### 14.3 Group
344
+
345
+ - `go/internal/online/capability_registry.go` 及调用侧:按设备 / 连接查控制能力,不能复用 AID 聚合的 E2EE 能力历史作为最终判断。
346
+ - `go/internal/messages/` 与 `go/internal/store/send_repo.go`:三流发送、成员设备判定、原正文引用及 normal 投影、volatile 新路径。
347
+ - `go/internal/store/redis_seq_allocator.go` 与存储 schema:独立控制 SEQ、Head、保留边界与兼容投影事务。
348
+ - `go/internal/repository/pull_sql_backend.go`、`ack_sql_backend.go`、`go/internal/store/device_cursor.go`:三流 cursor、源成员资格、扫描证明和旧版普通流兼容。
349
+ - 群事件、成员及密钥流程维持其业务职责;控制优先不绕过权限和 epoch。
350
+
351
+ 能力字段优先扩展 Gateway 原表;消息流和投影需要服务各自的 schema 迁移。normal 原表主键不原地破坏,控制数据使用独立表或等效隔离命名空间,具体 DDL 在实施首阶段与现有 WAL 约束一起冻结。
352
+
353
+ ### 14.4 四端 SDK
354
+
355
+ | 层 | 主要修改 |
356
+ | --- | --- |
357
+ | Auth / Transport | 声明 scope 与协议能力、保存协商结果、旧服务端检测与身份代次 |
358
+ | Send / Facade | 唯一新增 `message_class`,TTL / 加密保持原参数和默认行为 |
359
+ | V2 E2EE | 使用现有受保护元数据认证原始类别,接收校验,保留既有 AAD |
360
+ | Peers / Delivery / Pull Gate | 三流恢复、固定优先序、独立队列、控制内部分发和兼容投影去重 |
361
+ | SeqTracker / Store | namespace 加类别,持久 cursor / proof / ACK 隔离,normal 原状态原值继续使用 |
362
+ | AgentMd 管理器 | 上传后通知待办、提交不确定性恢复、Owner 刷新、事件与上线对账 |
363
+
364
+ 对应 Python `src/aun_core/`、Go SDK 根目录、TS / JS 的 `src/`。四端契约一致,跨端传整数不能丢精度;超过 JS 安全整数范围时沿用现有精确整数约定。内部 slot 不新增为应用参数,不写入普通消息 payload。
365
+
366
+ ## 15. 实施顺序与发布兼容
367
+
368
+ | 阶段 | 实施内容 | 完成条件 |
369
+ | --- | --- | --- |
370
+ | P0 | 冻结本方案字段、能力结构、状态机与共同向量 | 三类默认、TTL、优先序及丢弃 / 降级无歧义 |
371
+ | P1 | Gateway 能力持久化、查询、连接态及失效 | 独立数据库、乱序事件、升级降级和重启测试通过 |
372
+ | P2 | Message 三流持久 / volatile、投影和隔离 | 旧 SDK 普通通道不漏 SDK 控制,cursor 不跨流 |
373
+ | P3 | Group 三流、成员资格投影及 TTL 路径 | 混合设备、入退群和 epoch 矩阵通过 |
374
+ | P4 | 四 SDK 契约、调度、内部消费与 E2EE | 跨 SDK、断点恢复和 normal 非回归通过 |
375
+ | P5 | agent.md 主动通知与 Owner 对账 | 在线、离线、响应丢失、通知缺失均可收敛 |
376
+ | P6 | 服务端先行部署、受控 SDK 开启、线上 E2E | 按下节矩阵留存真实报告 |
377
+
378
+ 采用 TDD 在受影响现有测试中先构造失败证据,再实施最小改动;本次只保存设计,不新增测试代码。
379
+
380
+ 服务端完整支持前不广告相应 capability;SDK 未协商成功不得发送可能被旧服务端当普通消息的 SDK 控制内容。旧客户端字段缺省继续 normal,旧数据库历史行按 normal 解释,不回填成控制消息。
381
+
382
+ 关闭控制能力时先停止新发送,再完成或隔离既有投递。不能把已入队 SDK 控制数据交给不识别类别的旧服务端读取;降级部署须保留隔离读取门禁,未支持的控制数据留待兼容版本恢复或按规则终结,不破坏 normal 状态。
383
+
384
+ ## 16. 验收矩阵与现有证据
385
+
386
+ | 范围 | 必验项目 |
387
+ | --- | --- |
388
+ | 参数 | 缺省 normal、三个合法值、未知值拒绝;无 consumer / storage 发送参数;TTL 边界 |
389
+ | 能力 | 从未声明、旧端缺字段、设备离线、升级 / 降级、同 AID 多设备与同设备多 slot |
390
+ | 缓存 | 不同数据库、命中 / 过期、批量与单飞、丢事件、迟到离线、失效前在途响应、查询失败不误丢弃 |
391
+ | SDK 隔离 | 新旧 Push / Pull / History / 原始 RPC / batch / self-sync;未知 SDK 协议不交应用 |
392
+ | 持久流 | 三流同数字 SEQ、peer / group proof、空末页、Piggy ACK、retention、重启恢复、跨流 ACK 拒绝 |
393
+ | 兼容投影 | 原 control 未完成而 normal 已越过旧 alias;normal tail 新位置;多 slot 共享投影;重试与在途 ACK |
394
+ | Group | 新旧成员设备混合;加入前消息、退出后消息、退群重进代次、屏蔽、原 epoch;不全群降级 |
395
+ | volatile | 三类 TTL 内补拉、lease / ACK、过期、容量及重启;数据库 / WAL 不含消息正文与投影 |
396
+ | 优先级 | 先积压 normal,再加入 control 和 sdk_control,验证每个就绪调度阶段顺序;在途不抢占 |
397
+ | E2EE | 明文 / 加密分别组合,原类别认证、投影不篡改密文、旧 SDK 解密、SDK 协议版本门禁 |
398
+ | agent.md | 在线 / 离线 Owner、旧上传者、通知丢失、PUT 响应丢失、崩溃恢复、晚到 ETag、owner 已变更 |
399
+ | 跨端 / 跨域 | Python / Go / TS / JS 互通;目标域权威能力、剩余 TTL、目标服务不支持、整数精度 |
400
+ | 非回归 | normal、既有 Ownership、群事件、读状态、queue / fanout、notify 语义 |
401
+
402
+ 线上验收按 [公网测试指南](aun公网测试运行指南.md),目标 `evolagent.cn`,保留正式 TLS、独立身份与运行目录;首败停止,从检查点续跑,未受影响的已通过子项不重复。源码变更后先完成单元、lint / 类型检查及本地集成,再验收实际部署的新版本;不以本地新 SDK 配旧线上服务冒充新增机制通过。
403
+
404
+ 发送端 SDK × 接收端 SDK 的 2×2 兼容矩阵如下,前提是服务端已支持新协议;新旧服务混跑另由服务能力协商门禁约束。
405
+
406
+ | 发送端 → 接收端 | 旧 SDK | 新 SDK |
407
+ |---|---|---|
408
+ | 旧 SDK | 缺省普通消息保持原行为 | 缺省按 normal 处理,保持原行为 |
409
+ | 新 SDK | normal 保持原行为;control 降为普通流;sdk_control 不投递该目标 | 三类独立流与优先调度;sdk_control 仅由 SDK 消费 |
410
+
411
+ 既有 [P2P E2E](../python/tests/e2e_test_v2_p2p_e2ee.py) 新增可选 `capability_compat_matrix`,默认原 10 项保持不变。16 个子场景覆盖四格的持久 / TTL、普通 SEQ、解密、ACK 和应用回调;新发端同时覆盖控制降级及 SDK 控制消息在原始通知、普通 Pull、应用回调的隔离。当前旧端由新 SDK 隐去能力声明模拟,仅完成静态与 dry-run 校验,不代表真实旧 SDK 包或新增线上机制验收通过。
412
+
413
+ 2026-10-29 已运行现有 `e2e-ownership`、`integration-notify`、`e2e-notify`,最终报告为 3 passed,Ownership 断点累计 15 个子项。首轮 Windows 身份临时文件路径失败后改 Linux 容器;一次离线释放核验断言失败后保留检查点,续跑通过,原断言失败根因未确认,不能视为已修复。
414
+
415
+ 证据位于仓库本地 `.public-test-data/runs/evolagent.cn/control-acceptance-20261029-linux-02/report.json`,另保留前两轮失败报告。它们只证明当时实际执行的现有流程,不证明本文新增类别、独立 SEQ、缓存协商、Menu 优先级或 agent.md 通知已经实现 / 验收。
416
+
417
+ ## 17. 参考与源码入口
418
+
419
+ - [Message RPC 手册](sdk/09-message-rpc-manual.md):当前 TTL、peer SEQ / cursor、共享 Pull Gate、ACK 和渲染边界。
420
+ - [Group RPC 手册](sdk/09-group-rpc-manual.md):群消息、事件、成员和设备 cursor。
421
+ - [Notify 通知方案](sdk/Notify通知方案.md):在线尽力通知与可靠消息的区别。
422
+ - [ETag 观察与同步机制](sdk/agent.md与group.index的ETag观察与同步机制.md):名片缓存、签验和观察代次。
423
+ - [Ownership 协议 2.1](protocol/Ownership协议定义2.1.md):关系权威、Owner 锁和 Group / Org 生命周期边界。
424
+ - [Menu Protocol 应用场景](../../docs/org/discussion-points/menu-protocol.md):现有 Menu 业务结构和权限门。
425
+ - [单域测试指南](aun测试运行指南.md)、[分布式测试指南](aun分布式测试运行指南.md)、[公网测试指南](aun公网测试运行指南.md):实施与部署后的验证入口。
426
+ - 服务端源码从 `../../extensions/services/gateway/go/`、`../../extensions/services/message/go/`、`../../extensions/services/group/go/` 查阅;SDK 源码从 `../python/`、`../go/`、`../ts/`、`../js/` 查阅。