新增: 待发箱/唤醒原语/预算随窗口 + 治理门禁与场景护栏
- 待发箱地基: V51 ai_outbox 表 + 手写 OutboxRepo(claim CAS/崩溃残留复位/瞬时失败保留) + outbox.rs 调度器(KV 默认关/Notify 唤醒/会话忙跳过不丢)
- Wake 异步唤醒原语: 三条件(delay/file-appear/process-exit)+纯判定+注入走正典 spawn_agentic_session,默认关
- 上下文预算随模型窗口: model_context.rs 三级优先(池显式>预设表>回退 128K);上界取 1M 而非 128K——max_tokens 仅裁剪预算,卡 128K 等于不治根因
- 文件租约补纯函数与单测(未另造双轨:LEASE 早随 cb260ab 落地) / 跨会话召回补纯函数单测(主体早已落地)
- 子代理: 完整卡已消化 + 子会话压缩层 + P3 批量
- 隧道/中继: 指令离线不丢 + Hello proto 版本 + 心跳与 runbook
- df-mcp: 写工具后落 KV 变更标记(供 GUI 轮询兜底,补独立 stdio 进程无感知)
- 迁移链顶点改单一真值常量 LATEST_SCHEMA_VERSION(修 2 处因 V51 失效的断言)
- 治理: i18n 特殊字符提交门禁(含反引号覆盖 + 10 自测) / 根目录卫生门禁 / 看板归档器 / 单一 lockfile(bun) / swap-install 落 temp/ 且兼容无 Get-FileHash
- 场景护栏: 流式正文可见性 e2e(playwright 免真机事件回放) + 列表续写/外置句柄 node 测试
This commit is contained in:
@@ -75,6 +75,17 @@ fn validate_device_id(id: &str) -> bool {
|
||||
!id.is_empty() && id.len() <= 128 && id.chars().all(|c| !c.is_control())
|
||||
}
|
||||
|
||||
/// 当前 Hello 握手协议版本。
|
||||
///
|
||||
/// 协商规则:旧客户端 Hello 缺 `proto` 字段 → serde default 视作 v1(向后兼容);
|
||||
/// 高于本端支持版本时记录告警,为后续破坏性变更预留通知位(当前仅日志,不拒绝握手)。
|
||||
pub const HELLO_PROTO_VERSION: u32 = 1;
|
||||
|
||||
/// `proto` 缺省值:旧客户端不带该字段时按 v1 处理(向后兼容)。
|
||||
fn default_proto() -> u32 {
|
||||
HELLO_PROTO_VERSION
|
||||
}
|
||||
|
||||
/// 客户端首消息:身份宣告(简单协议)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Hello {
|
||||
@@ -84,6 +95,9 @@ pub struct Hello {
|
||||
pub device_id: String,
|
||||
/// 配对 token
|
||||
pub token: String,
|
||||
/// 协议版本(缺省按 v1,兼容旧客户端)
|
||||
#[serde(default = "default_proto")]
|
||||
pub proto: u32,
|
||||
}
|
||||
|
||||
/// Hello.kind 的传输表示(serde 字符串,与 ClientKind 解耦避免 rename 歧义)
|
||||
@@ -298,6 +312,16 @@ async fn handle_connection(socket: WebSocket, state: RelayState, expected: Clien
|
||||
return;
|
||||
}
|
||||
|
||||
// 版本协商:proto 缺省按 v1(旧客户端不带字段,向后兼容)。
|
||||
// 高于本端支持版本时告警 —— 为后续破坏性变更预留的通知位(当前仅日志,不拒绝握手)。
|
||||
if hello.proto > HELLO_PROTO_VERSION {
|
||||
tracing::warn!(
|
||||
client_proto = hello.proto,
|
||||
server_proto = HELLO_PROTO_VERSION,
|
||||
"客户端协议版本高于本端,可能存在不兼容(预留通知位)"
|
||||
);
|
||||
}
|
||||
|
||||
let conn_id = next_conn_id();
|
||||
let kind: ClientKind = hello.kind.into();
|
||||
let device_id = hello.device_id.clone();
|
||||
@@ -305,6 +329,7 @@ async fn handle_connection(socket: WebSocket, state: RelayState, expected: Clien
|
||||
conn_id = conn_id.0,
|
||||
?kind,
|
||||
device_id = %device_id,
|
||||
proto = hello.proto,
|
||||
"连接握手通过,进入收发循环"
|
||||
);
|
||||
|
||||
@@ -545,4 +570,19 @@ mod tests {
|
||||
fn max_frame_is_one_mib() {
|
||||
assert_eq!(MAX_FRAME_BYTES, 1024 * 1024);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_proto_defaults_to_v1_when_absent() {
|
||||
// 旧客户端不发 proto → 反序列化按 v1,保证向后兼容
|
||||
let json = r#"{"kind":"device","device_id":"dev-1","token":"t"}"#;
|
||||
let h: Hello = serde_json::from_str(json).expect("缺 proto 应可解析");
|
||||
assert_eq!(h.proto, HELLO_PROTO_VERSION);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_proto_parsed_when_present() {
|
||||
let json = r#"{"kind":"device","device_id":"dev-1","token":"t","proto":2}"#;
|
||||
let h: Hello = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(h.proto, 2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user