浏览器锁屏导致 WebSocket 半死,ws_sender.send().await 永久阻塞,
级联阻塞 dispatcher → MessageBus → Agent Loop,后端停止执行直到解锁。
基于第一性原理建立"执行-显示解耦"原则:agent 执行只依赖 SQLite
持久化,实时广播是可丢弃的最佳努力通道。
核心改动:
- MessageBus::publish_outbound 由 send().await 改为 try_send(),
bus 满时丢弃消息并告警,agent 不再被显示层阻塞
- WebSocket writer task 包裹 30s 超时,使用每连接独立的
CancellationToken(非共享 CliChannel 级 token),避免一个连接
超时关闭所有连接;writer 退出时 cancel 通知主 loop 退出
- CliChannel::send 由 send().await 改为 try_send(),避免 dispatcher
单线程被卡住的 writer 阻塞 37s
- 全仓 13 处 publish_outbound 调用统一区分 Dropped(warn)/Closed(error)
- scheduler 3 处 ? 改为 warn,避免 Dropped 触发 misfire 重试风暴
- 前端 WebSocket 添加 25s ping + 指数退避重连(3s→60s封顶)
- 前端 session_list 区分重连恢复/首次连接,重连时保留 messages
并刷新 topic 列表
经五轮对抗性审查验证,修复了共享 cancel token、dispatcher 阻塞、
load_chat_messages 跨 topic 污染、原 session 删除后状态不一致等
回归问题。
同时升级版本号至 0.3.0 并更新 CHANGELOG。
验证:cargo clippy --all-targets --all-features ✓
npm run build ✓ | useChat.test.ts 13 passed ✓
12 KiB
12 KiB
变更日志
本文件记录 Picobot 各版本的显著变更,遵循 Keep a Changelog 风格。
[0.3.0] - 2026-08-04
较 0.2.0 的 14 个已提交 commit + 本次版本一并提交的锁屏冻结架构修复(12 文件),聚焦 Agent 执行与显示层解耦、工程化基线、并发持久化稳定性 与 安全加固 四大方向。架构修复经五轮对抗性审查验证。
新增功能
话题重命名
- 新增
RenameTopic命令与TopicRenamed协议消息,复用存储层已有的update_topic_title方法。后端响应携带刷新后的完整 topic 列表,前端零额外往返同步侧边栏。 - 前端
TopicList侧边栏增加内联编辑入口:悬停显示铅笔图标,Enter 提交 / Esc 取消 / blur 取消,通过onMouseDown preventDefault防止按钮点击时 input 提前失焦。
前端自动构建集成
- 新增
build.rs,cargo build时自动执行npm install + npm run build,消除前后端构建割裂。支持SKIP_FRONTEND_BUILD环境变量跳过。
工程化基线
rustfmt.toml固化max_width=100/ 4 空格缩进;Cargo.toml配置[lints.rust]与[lints.clippy]渐进式规则。.github/workflows/ci.yml:Rust(fmt + clippy + test)+ 前端(eslint + tsc + test + prettier format:check)双平台 CI。Makefile新增check/fmt/fix目标,clippy 对齐--all-targets --all-features。- 前端 eslint flat config + prettier 配置,对 47 个前端文件统一格式化并在 CI 强制
format:check。
默认迭代上限调整
max_tool_iterations默认值调整为 1000,匹配长任务子代理的实际需求。
架构修复
Agent 执行与显示层解耦(锁屏冻结根因修复)
根因:浏览器锁屏导致 WebSocket 半死,ws_sender.send().await 永久阻塞,级联阻塞 dispatcher → MessageBus → Agent Loop,后端停止执行直到解锁。
基于第一性原理建立"执行-显示解耦"原则:agent 执行只依赖 SQLite 持久化,实时广播是可丢弃的最佳努力通道。
MessageBus::publish_outbound由send().await改为try_send():bus 满时丢弃消息并告警,agent 不再被显示层阻塞。新增BusError::Dropped变体。- WebSocket writer task 包裹
tokio::time::timeout(30s):半死连接超时即关闭。使用每连接独立的CancellationToken(非共享的 CliChannel 级 token),避免一个连接超时关闭所有连接。writer 退出时 cancel 通知主 loop 退出,确保unregister_connection执行。 CliChannel::send由send().await改为try_send():dispatcher 单线程顺序处理,原阻塞式发送在 writer 卡住时会阻塞所有连接 37s,改后立即返回。- 全仓 13 处
publish_outbound调用统一区分Dropped(warn,预期背压)/Closed(error,异常)日志级别,消除监控噪音。scheduler 的 3 处?改为 warn,避免Dropped触发 misfire 重试风暴。 - 前端 WebSocket 添加 25s 客户端 ping + 指数退避重连(3s→6s→12s→24s→60s 封顶,上限 999 次)。重连后区分"重连恢复"与"首次/切换通道":前者保留断连前 messages 并刷新 topic 列表,后者清空数据避免污染。
安全加固
SSRF 重定向绕过修复
web_fetch工具禁用 HTTP 重定向(redirect::Policy::none()):原validate_url只校验初始 URL 的 host,跟随 302 跳转可重定向到169.254.169.254(云元数据)或127.0.0.1等内网地址,绕过is_private_host的 SSRF 防护。与http_request工具保持一致。
Safety Guard 正则收紧
format正则收紧为\bformat\s+.*[a-z]:,要求出现盘符才拦截,避免误伤dart format、buf format、pytest --format等合法命令。- 按平台分组注入规则,Unix 不再注入 Windows 专用规则;
Remove-Item正则改为小写,与guard_command的大小写处理一致。
稳定性修复
SQLite 并发写入死锁
- 7 个写事务从
BEGIN DEFERRED改为BEGIN IMMEDIATE:在事务开始即获取写锁,消除多 sub-agent 并发写入时的死锁路径。busy_timeout从 5s 提升至 30s。 - 根因:
BEGIN DEFERRED下多个事务可同时读MAX(seq)不持写锁,提交时互相阻塞,5s timeout 耗尽后返回SQLITE_BUSY。BEGIN IMMEDIATE强制写者串行排队,顺带消除MAX(seq)+1竞态导致的 UNIQUE 约束冲突。
测试与代码质量
- 修复
anthropic错误链嵌套测试为真正的#[source]链(原测试是无效的,inner 变量被let _抑制)。 - 补充
anthropicprovider 15 个纯函数单测(原 396 行零测试),覆盖 data URL 解析、图片过滤、字段过滤、响应反序列化、错误链格式化。 - 修复 3 个失败测试:
agent_md_template写入模板内容 /StreamingAccumulatorBTreeMap 保序 /source_order断言补全。 - 清理未使用的 dead code 函数与方法。
- 新增
ARCHITECTURE.md架构文档,聚焦数据流与 7 个关键设计决策。
CI 覆盖范围扩大
cargo build --lib改为cargo build,确保main.rs二进制入口被编译验证。cargo test --lib改为cargo test,纳入tests/目录集成测试。test_request_format.rs的 8 个序列化测试此前从未在 CI 中运行。
跨平台构建修复
- 修正依赖分类错误:
rmcp/schemars/http/tower-http/rust-embed均为跨平台 crate,错放在[target.'cfg(windows)'.dependencies]下导致 Linux 构建失败,移出 target 段。
0.2.0 - 2026-07-31
较 0.1.2 的 47 个 commit 迭代,聚焦 能力策略、模型独立配置、话题级并发隔离 与 Agent Loop 性能优化 四大方向。
新增功能
能力策略(CapabilityPolicy)统一模型
- 新增
CapabilityPolicy领域模型,统一管理专家/子代理/技能的allowed_skills/denied_skills/allowed_tools/denied_tools四类白/黑名单。 - 专家和子代理的 Markdown frontmatter 支持上述四类字段,在
create_agent时按策略过滤ToolRegistry(快照语义)。 - 子代理支持数据驱动的工具过滤,删除旧版内置
explore白名单。 - 新增 HTTP 端点
GET /api/tools与PUT /api/subagents/update,供前端动态加载工具清单与更新子代理配置。 - 前端引入通用
CheckboxList勾选组件,将 capability 字段从自由文本 textarea 改为按来源分组的勾选式编辑,避免手输工具名。
模型独立配置
- 专家和子代理的 Markdown frontmatter 新增
provider与model可选字段,引用config.json中的providers/models表,可独立指定模型而不再继承主 agent。 - 主 agent 支持会话级模型覆盖(
provider/model在配置层覆盖)。 - 前端编辑模态框新增"模型配置"卡片,含 provider/model 下拉选择,选项由
GET /api/model-options提供。 - 专家引用不存在的 provider/model 时阻断会话并报错;子代理引用失败时将错误回传给 LLM。
话题级并发隔离
- 实现聊天消息串行锁,确保同一聊天的消息处理串行执行。
- 串行锁按
topic_id键化,支持多话题并发执行,互不阻塞。 topic_id全程显式传递(从消息接收到历史加载),修复消息错投与并发阻塞。- 内存历史缓存按
topic_id键化(原按chat_id),修复 DB 压缩时的 TOCTOU 覆盖问题。
子代理嵌套深度配置
- 暴露
tools.task.max_nesting_depth为可配置项(默认 2),前端"Task 子代理"卡片新增输入框。 - 保留双重防护:工具调用前校验 + 工具集构造时按深度移除
task工具。
专家配置热更新
- 专家配置保存后即时生效,无需重启网关。
工具调用序列前向检查
- 添加工具调用序列的前向校验,确保 tool 消息紧随 assistant 的 tool_calls 消息,避免 OpenAI/DeepSeek API 400 错误。
前端测试基础设施
- 引入 vitest 测试基础设施,添加
useChat handleServerMessage特征测试。
性能优化
Agent Loop 性能(P0)
- 工具定义缓存:
get_definitions()从每轮迭代调用一次改为process()循环外构建一次。基于快照语义——单次process()内Arc<ToolRegistry>不变,专家切换只影响下一次create_agent。 - 轻量 token 估算:新增
estimate_text_tokens_from_chat_messages,直接基于ChatMessage累加字段长度,消除中间text_only_messages: Vec<Message>构造(原方案每条消息 × 5 字段双克隆)。 - 工具 token 估算循环外预算:
serde_json::to_string全量序列化工具定义从每轮一次降为process()内一次。 - DB 写 offload:
PersistingEmittedMessageHandler用tokio::task::spawn_blocking包装 SQLite 同步写,解除 tokio worker 线程阻塞,不改变失败语义(仅记日志不阻断主流程)。
SessionPool 锁范围收敛
ensure_session_internal改为 double-checked locking:先短暂持锁检查存在性,释放锁后执行耗时的 session 创建(含配置加载、agent 工厂构造),再次持锁插入并处理竞态。避免跨session_factory.create().await持有全局锁导致所有 channel 的 session 访问串行化。
修复
安全修复
- 修复 SSRF 重定向绕过漏洞。
- 修复符号链接路径遍历漏洞。
- 修复
TodoItemSummary字段缺失。
并发与稳定性
replace_todos用transaction()包裹 DELETE + INSERT,保证原子性——中途失败自动回滚,避免 todos 列表丢失。事务内复用同一连接查询返回值,消除drop(conn)后重新pool.get()的冗余。- 修复话题隔离下
topic_id全程显式传递,解决消息错投与并发阻塞。 - 修复内存历史按
topic_id键化,新增replace_topic_history修复 DB 压缩覆盖。 - 修复
http.rs重启信号吞错(P1)。 - 修复 prompt 注入吞错(P0)。
前端修复
- 修复点击设置后白屏:React hooks 顺序违规(
useEffect放在条件 return 之后导致 #310 错误)。 - 修复
useEffect依赖项缺失(P0)。 - 修复子智能体视图间歇性丢消息:同步前置
subAgentViewRef/subAgentStackRef。 - 修复子智能体工具消息去重:改为按
id + type去重,修复工具状态卡在执行中。 - 修复高亮消息时退出子智能体视图的问题。
- 修复专家发现与前端选择多项问题。
- 兼容
base_url末尾斜杠,避免拼接产生双斜杠导致 404。 TodoWriteToolmerge 模式内存为空时从 DB 回填,防止丢失旧项。
依赖与构建
- 升级 wechatbot 的
reqwest至 0.13,消除版本冲突。 - 将
rmcp依赖从 git branch 迁移到 crates.io 版本。 - 提交
Cargo.lock以保证二进制构建可复现(binary 项目约定)。
重构
前端架构
- 重写
useChat为组合根,消除上帝 Hook。 - 抽取
useChat的领域子 hook 与messageMappers。 - 建立前端 API 客户端层,消除硬编码路径与重复
fetch。 - 拆分
ConfigPage.tsx与storage/mod.rs(P2)。 - 清理 P0/P1 技术债(
any类型、React key、dead_code、硬编码 URL)。
后端架构
- 重构
AgentLoop中的消息处理逻辑,简化工具结果处理和 LLM 请求构建。 - 重构工具提示提供者,整合工具使用说明;删除冗余的
TodoPromptProvider。 get_current/save_topic直读 DB 加载消息,移除SessionManager依赖。- 改用
gray_matter解析 Markdown frontmatter,增加相应日志。 - 优化工具使用说明,简化记忆检索和写入规则。
- 移除未使用的
cli_sessions变量,优化ws.rs中的代码。
内部改进
- 增加整理意识,确保同类工作输出到同一文件夹。
- 修复 MCP 日志输出。
0.1.2 - 2026-07-28
新增
- WebSocket 连接泄漏修复,使用
CancellationToken实现优雅重启。 - 交互式 Shell 会话管理。
- 数据库连接池优化会话存储,新增错误处理。
- 平台特定的进程输入等待检查,增强 Bash 工具的用户授权检测。
- 前端静态文件嵌入二进制。
- React Web UI 前端界面。