oudecheng
|
73c25e5a20
|
feat(web): Task 卡片差异显示子代理模型名 + 子代理完成状态转发修复
- Task/tool 卡片头部在子代理模型与主代理不同(model 或 provider 任一不同)时显示模型徽章
- useChat 放行带 subagent_task_id 的 execution_completed 消息,更新卡片 running→completed 状态
|
2026-08-15 15:35:32 +08:00 |
|
oudecheng
|
53e2fb6dc6
|
feat(web): per-topic 输入草稿记忆(切换暂存/恢复 + localStorage 持久化 + 删除清理)
- 文本草稿 debounce 持久化到 localStorage(key 按 topicId),附件仅存内存
- 切换话题时暂存当前草稿并恢复目标话题草稿,组件卸载时补齐落盘
- 删除话题时同步清除其持久化草稿,避免孤儿 key
|
2026-08-15 15:35:07 +08:00 |
|
oudecheng
|
8f59b6b93a
|
feat(web): 话题级模型选择前端(ModelSelector topicId 化 + 竞态防护)+ 专家系统开关隐藏
- ModelSelector 支持按 topicId 读写话题级选择(topic 优先,session 兜底)
- 快速切换话题时用递增令牌丢弃过期响应,防止乱序覆盖
- ChatContainer 透传 topicId,向后端双写新话题默认模型
- ExpertSelector 依据后端 experts_system_enabled 隐藏选择器并清除已选专家
|
2026-08-15 15:34:38 +08:00 |
|
oudecheng
|
9e503f2672
|
feat(web): 工具卡片统一格式,子智能体显示 '工具名 · 任务描述' 并移除下方徽章与时间
|
2026-08-15 09:32:16 +08:00 |
|
oudecheng
|
f705536fb1
|
feat(web): 通道/会话切换移至设置页 ConnectionTab,修复通道失效回退;连接状态上移 Logo 行并支持窄侧栏 compact 降级
|
2026-08-15 08:38:12 +08:00 |
|
oudecheng
|
eaee29841d
|
feat(web): 对齐 deepseek-harness 设计语言,重构前端视觉与三栏布局
- 设计 token 重映射为 DeepSeek 调色板(深浅双主题),正文改无衬线字体栈,全面去霓虹化
- 移除顶部 Header,控件迁入左栏(Logo/连接/通道/会话/tabs/底部操作区),左右栏支持拖拽调宽并持久化,折叠为图标 rail
- 对话区对齐 DeepSeek 聊天风:hero/docked 状态机、736px 居中列、用户右对齐柔和气泡、助手无气泡平铺、composer 卡片与反差发送键;子智能体点击导航完整保留
- 全功能区同步换肤:侧栏列表、右栏面板、ConfigPage、弹窗、选择器等
- fix: 修复 virtual-core 3.17.x 陈旧行高导致消息行重叠(宽度变化/滚动停止后强制 resizeItem 重测)
- fix: 修复 ConfigPage/Modal 引用不存在的 fadeIn/scaleIn keyframes 致入场动画失效
|
2026-08-14 19:15:45 +08:00 |
|
oudecheng
|
daf973a7dc
|
fix(web): 子代理状态显示与渲染崩溃修复
MessageBubble:
- taskStatusConfig 补全 running/completed/cancelled/interrupted 状态配色,
修复未知状态访问 undefined.borderColor 导致的白屏崩溃
- 添加安全回退到 failed 状态
main.tsx:
- 添加 ErrorBoundary 捕获 React 渲染错误,防止白屏并提供错误详情与恢复入口
useMessages:
- 收到子代理 execution_completed 事件后,更新主视图中 task tool result 占位消息的
status 字段(running->completed/failed/cancelled),消除永久黄色转圈
protocol.ts:
- ExecutionCompleted 接口添加 subagent_status 和 subagent_summary 字段
|
2026-08-13 22:12:43 +08:00 |
|
oudecheng
|
a9ce308c05
|
fix(web): 移除 ChatContainer 的 key remount,修复新建话题列表刷新延迟
话题切换时 key={selectedTopic} 导致整个 ChatContainer 子树卸载重建,
触发 ExpertSelector/ModelSelector 重新挂载并发起 3 次冗余 HTTP 请求
(getSelectedExpert/listModelOptions/模型选择刷新),引入 200-500ms 延迟。
改用 topicId prop 透传话题 ID:
- App.tsx: 移除 key,viewKey 纳入 selectedTopic 保持各话题独立滚动位置
- ChatContainer: 新增 topicId prop 透传给 MessageInput
- MessageInput: 监听 topicId 变化清空草稿,替代原 key remount 重置机制
|
2026-08-12 09:16:38 +08:00 |
|
oudecheng
|
77a0eac2c8
|
fix(mcp): 修复前端空输入重置为不超时,增加超时路径单元测试
P3: 清空输入框时回退到默认值 300 而非 0;新增两个 tokio 测试覆盖有超时和无超时(error pass-through)路径
|
2026-08-11 23:07:00 +08:00 |
|
oudecheng
|
3e97ed903c
|
feat(mcp): 为 MCP 工具调用增加超时保护,默认 5 分钟
在 McpToolWrapper 适配层用 tokio::time::timeout 包裹 call_tool,防止外部 MCP server 挂起导致 agent loop 无限阻塞。超时时间通过 config.mcp_tool_timeout_secs 配置(默认 300 秒,0=不超时),前端 McpTab 设置页提供输入框。
|
2026-08-11 21:53:07 +08:00 |
|
oudecheng
|
7de9a8a054
|
feat(web): 顶栏显示后端版本号
复用现有 /health 端点返回的 CARGO_PKG_VERSION,避免在 package.json 重复维护版本号;挂载时拉取一次,失败则不显示徽标。
|
2026-08-07 14:52:11 +08:00 |
|
oudecheng
|
79d099a3bc
|
chore(compaction): 调整压缩阈值默认值
工程化压缩阈值 threshold_ratio: 0.7 → 0.5(50%)
LLM 压缩阈值 llm_compaction_threshold_ratio: 0.5 → 0.3(30%)
降低触发门槛使压缩更积极介入,减少上下文溢出风险。
同步更新相关注释、日志信息和前端 hint 文案。
|
2026-08-07 11:53:54 +08:00 |
|
oudecheng
|
a9429a5657
|
fix(web): 输入框/发送按钮状态按话题隔离,避免处理中仍可发送
将单一全局 isLoading 布尔值重构为按 topic_id 跟踪的 processingTopicIds
集合,isLoading 派生自当前选中话题是否在集合中。导航响应不再清空处理
状态,切换话题后切回原话题仍能正确禁用输入。重连时通过新增的
/api/executions 端点对账后端权威执行状态,修正断连期间丢失的
execution_completed 信号导致的状态漂移。
|
2026-08-07 11:45:47 +08:00 |
|
oudecheng
|
d3ee430aa9
|
test(web): MessageList 虚拟化的对抗性 smoke test
对抗性审查 commit 03d8840 的虚拟化实现,新增 5 个组件测试:
- 用例 1: 空消息渲染空状态
- 用例 2: 100 条消息不崩溃(虚拟化 smoke test)
- 用例 3: highlight 指向视口外消息不崩溃
- 用例 4: viewKey 切换不崩溃
- 用例 5: 流式 rerender 不崩溃
发现:jsdom 下 getBoundingClientRect 返回 0 + rAF 同步化,
virtualizer 测量后可见范围可能变空(textContent 不可靠)。
这是测试环境限制,不反映浏览器真实行为(浏览器 rAF 异步、
高度真实)。用例 5 聚焦不崩溃验证,内容正确性由真实布局保证。
|
2026-08-07 08:34:17 +08:00 |
|
oudecheng
|
2367d87db6
|
perf(web): 消息列表虚拟化,长对话不再卡顿
原实现 messages.map 渲染所有消息,500 条消息会产生 10000+ DOM
节点(每条 MessageBubble 含 ReactMarkdown 解析),导致首次渲染
卡顿、滚动掉帧、流式输出每 token 都全量 diff。
引入 @tanstack/react-virtual 实现虚拟化:
- 只渲染视口内 + overscan=6 条消息,DOM 节点数恒定
- measureElement 动态测量变高度消息(一行文本 vs 50 行代码块)
- estimateSize=120 提供初始估计,测量后自动校正
- Firefox 特殊处理 measureElement(getBoundingClientRect)
适配现有功能:
- 自动滚到底部:bottomRef.scrollIntoView 改为 virtualizer.scrollToIndex
- viewKey 滚动位置记忆:保留 scrollTop 保存/恢复逻辑
- highlightedMessageId:先 scrollToIndex 渲染目标项,rAF 后加 class
- newMessageCount:流式 delta 不计数(消息条数不变)
- space-y-6 改为每项 pb-6(虚拟化下相邻 margin 不生效)
性能对比(500 条消息):
- DOM 节点:10000+ → ~200(仅可见+overscan)
- 首次渲染:数百 ms → <16ms
- 流式 diff:全量列表 → 仅可见项
|
2026-08-07 08:34:07 +08:00 |
|
oudecheng
|
934c7aa804
|
test(web): 补充流式断连的对抗性测试(stream_end 幂等性 + 多周期循环)
对抗性审查 commit 30b7d90 的修复健壮性,新增两个边缘场景测试:
- 用例 18: 验证断连后 stream_end 到达是幂等的(不崩溃、不改变
已 flush 内容),且后续 assistant_response 能正常按 id 替换
- 用例 19: 验证多次断连-重连循环下 finishStreaming 幂等
(连续调用不崩溃,消息状态稳定)
|
2026-08-07 08:33:57 +08:00 |
|
oudecheng
|
c749df4e0a
|
fix(web): WebSocket 断连时清理流式状态,防止重连后消息串流
流式优化引入的回归风险:当 WebSocket 在流式输出中途断连时,
streamingRef(contentChunks/index)不会被重置。重连后新消息的
delta 会通过 s.index >= 0 分支追加到旧消息的错误位置。
修复链路:useWebSocket 的 onDisconnect 回调 → useChat 的
finishStreaming → useMessages 的 finishStreaming(重置 ref +
flush pending chunks)。
- useMessages.ts: 在 UseMessagesReturn 接口和 return 对象暴露
finishStreaming(原为内部函数)
- useChat.ts: 在返回对象透传 finishStreaming
- App.tsx: useWebSocket 调用新增 onDisconnect 回调触发 finishStreaming
- useChat.test.ts: 新增用例 17 验证断连后新 delta 不污染旧消息
|
2026-08-07 08:33:48 +08:00 |
|
oudecheng
|
00735395ca
|
fix(web): 修复正在执行的会话消息泄露到定时任务视图
handleSchedulerMessage (Tier 1 路由) 原先无条件吞掉所有可转换的
chat 消息,导致用户打开定时任务历史视图时,当前主会话正在执行
的 stream_delta/assistant_response 等实时消息被错误追加到
schedulerView.messages。
修复:调度器视图是历史会话查看器,只接收不带 topic_id 的历史
加载消息;实时流式消息(stream_delta/stream_end)和带 topic_id
的实时消息 fall through 到 Tier 2/3 路由到主视图,不再泄露。
- useSchedulerView.ts: handleSchedulerMessage 增加流式和 topic_id
过滤,实时消息 fall through
- useChat.test.ts: 新增用例 16 验证流式和带 topic_id 消息不进入
schedulerView,无 topic_id 历史消息仍正常进入
|
2026-08-07 08:33:37 +08:00 |
|
oudecheng
|
f8c984aef4
|
perf(web): 优化流式 delta 处理,避免每 token 全数组拷贝
用 ref 累加 delta chunks + requestAnimationFrame 批量 flush,
将每秒数十次 O(n) 数组拷贝合并为一帧一次状态更新。
- useMessages.ts: 新增 streamingRef 累加器 + rAF 调度,所有 delta
只 push 到 ref,由 flushStreaming 批量落盘;用 rafScheduledRef
boolean 追踪调度状态(避免 rAF handle 为 0 时与 null 检查冲突);
flushStreaming 基于 prev 实际状态判断创建/更新消息(批处理安全)
- useChat.ts: 5 处 setMessages([]) 改为 clearMessages(),切话题/
会话/通道时重置流式 ref,避免脏状态残留
- test/setup.ts: 同步化 rAF 并返回递增非零 handle,保持测试同步语义
- useChat.test.ts: 新增对抗性测试(批量 delta 累加、stream_end 后
assistant_response 替换含 reasoning)
复杂度:O(n²·L) → O(n) 每帧,15 个测试全部通过
|
2026-08-07 08:33:27 +08:00 |
|
oudecheng
|
7b459b8ca1
|
feat(config): 压缩算法关键参数暴露到设置页面
将 4 个压缩参数(threshold_ratio、llm_compaction_threshold_ratio、
truncate_max_tokens、preserve_count)从硬编码提取到 config.json 顶层
compaction 节,前端新增 CompactionTab 供用户调整。
后端:
- config: 新增 CompactionConfig 结构体,Config 新增 compaction 字段
- context_compressor: 4 个参数内聚到 ContextCompressor 实例字段;
新增 with_compaction_config() 构造函数和 truncate_tool_results() 方法;
对用户配置做防御性 clamp(ratio ∈ [0.1,1.0],整数 ≥ 1)
- agent_loop: 调用 compressor.truncate_tool_results() 实现零参数耦合
- agent_factory: 新增 build_compressor() 方法,in-loop 与 sync 兜底
两条压缩路径共用同一套用户配置
- session: with_factories 改用 agent_factory.build_compressor(),修复
sync 压缩路径忽略用户配置的 P0 问题
前端:
- types: 新增 CompactionConfig 接口,TabId 新增 'compaction'
- 新建 CompactionTab.tsx,4 个 number input 分两组 SectionCard
- constants + ConfigPage: 注册"上下文压缩" Tab(Archive 图标)
清理:删除无调用方的 from_provider_config/from_runtime_config/with_config
方法及 DEFAULT_THRESHOLD_RATIO/LLM_COMPACTION_THRESHOLD_RATIO 常量。
测试:context_compressor 18 + session 31 + agent_loop 42 全通过。
|
2026-08-06 11:03:56 +08:00 |
|
oudecheng
|
fa420b713f
|
feat(gateway): 添加 HTTP API 认证与 CORS 防护,修复密钥泄露风险
- 新增 auth_token 配置项:非 loopback 绑定强制认证,loopback 可选加固
- 新增 auth.rs:loopback 判定(IpAddr 解析)、常量时间 token 比较、RFC 6750 Bearer 解析
- 新增 Bearer token 中间件(/api/*)与 WebSocket ?token= 校验
- 新增 CORS layer:loopback 镜像 Origin 防 DNS rebinding,非 loopback permissive
- 修复 .gitignore 遗漏 tests/test.env,补充通用 *.env 规则
- 修复前端 experts/skills/subagents.ts 直接 fetch 不带 token(13 处),统一走 authedFetch
- config.json host 从 0.0.0.0 改回 127.0.0.1
- mask_config/save_config 处理 auth_token 掩码与保留
- ConnectionTab/GatewayTab 增加认证 token 配置入口
|
2026-08-05 22:41:00 +08:00 |
|
oudecheng
|
bb2774c6b5
|
feat(web): token 统计从侧边栏迁移到右侧面板,支持子代理每轮刷新
- 新增 TopicTokenStatsPanel 面板,显示五字段 + 上下文占用进度条
- 移除 TopicList 侧边栏的 token 统计显示
- 后端新增 get_session_token_stats,按 session_id 精确查询子代理 token
- TaskMessagesLoaded 协议扩展 token_stats 字段,ws.rs 转发至前端
- useMessages/useSubAgentView 每轮 assistant_response/execution_completed 触发刷新
- App.tsx 根据当前视图分派 list_topics(主代理)或 load_task_messages(子代理)
- 抽取 utils/tokenStats.ts 共享格式化与占用率计算函数
|
2026-08-05 12:09:40 +08:00 |
|
oudecheng
|
beb7b581a6
|
fix(web): 主代理执行结束时刷新 topic 列表以更新 token 统计
LLM usage 在消息持久化后已写入 DB,主代理本次执行结束时触发 bumpTopicRefreshTrigger,list_topics 重新读取最新 token 统计并更新侧边栏。
|
2026-08-05 11:15:12 +08:00 |
|
oudecheng
|
235f57c9ae
|
feat(settings): 新增设置页重启按钮,区分保存后触发与手动触发
状态从 showRestartDialog: bool 改为 restartDialogMode: 'saved' | 'manual' | null,三态清晰区分触发来源。
改动:- 保存后自动弹出 'saved' 模式对话框(标题 '配置已保存',按钮 '稍后') - 新增独立 '重启' 按钮,点击弹出 'manual' 模式对话框(标题 '重启服务',按钮 '取消'),dirty 时提示未保存更改 - 重启中禁用保存与重启按钮,handleClose 阻断关闭并提示 - ESC 在对话框打开时仅关闭对话框,不关闭整个页面 - 重启按钮 loading 状态与 restarting 同步
|
2026-08-05 09:43:05 +08:00 |
|
oudecheng
|
4e74d66a7c
|
fix(web): 修复流式期间消息重渲染、WebSocket 无谓重连与新消息计数错误
- MessageBubble 用 React.memo 包裹,流式 delta 只追加到最后一条消息,
其余消息引用不变可被 memo 跳过,避免 N-1 条历史消息无谓重渲染
- useWebSocket 用 useRef 存储回调最新引用,connect 不再依赖回调引用,
避免调用方回调变化触发 disconnect+connect 重连
- MessageList 用 prevMessageCountRef 追踪消息条数变化,仅当真正新增
消息条数时才累加 newMessageCount;流式 delta 不增加条数不再误计数,
滑到底部按钮触发的清零路径与时序经审查覆盖各种边界场景
|
2026-08-05 08:10:44 +08:00 |
|
oudecheng
|
adada2652c
|
fix(web): 修复话题重命名预填字段与写入字段不一致
Bug: 点击铅笔编辑时,输入框显示的是 topic.title(系统生成的 id+时间),
而不是用户看到的 topic.description(AI 生成的摘要)。且后端 rename 写入 title,
但列表显示优先 description,导致改名后视觉上无变化。
根因:显示用 'description || title',编辑预填用 'title',写入也改 'title',
三者不一致。
修复:
- 前端 startEdit: 预填 'description || title'(与显示逻辑一致)
- 后端 rename_topic: 改用 update_topic_description 写入显示字段,
保留 title 作为内部标识;'未变化'比较也基于当前显示值
- 新增测试: 验证有 description 时比较逻辑基于 description 而非 title
|
2026-08-04 23:57:04 +08:00 |
|
oudecheng
|
c2cc072b2e
|
feat(settings): 拆分设置页为懒加载标签页并支持子代理创建/删除
- ConfigPage 拆分为 16 个懒加载 tab + 2 个 modal,首屏体积大幅下降
- 抽取 CapabilityTabs 共享组件,统一专家/子代理能力配置 UI
- 后端新增 POST /api/subagents/create、DELETE /api/subagents/delete
- SubagentRuntime 新增 create_subagent/delete_subagent,含路径校验与 builtin 保护
- SubagentModal 支持 create/edit 双模式、body 编辑、自身排除防自递归
- SubagentsTab 增加搜索、provider/model 标签、删除二次确认
- 修复 reload() 使用进程 cwd 而非 self.cwd 的隔离缺陷
|
2026-08-04 21:06:17 +08:00 |
|
oudecheng
|
2e4b1931a6
|
feat(tokens): 添加 topic 维度 token 消耗与上下文窗口占用统计
后端:
- openai provider 流式响应启用 stream_options.include_usage,从 SSE 末帧捕获 usage
- ChatMessage 新增 MessageUsage(prompt/completion/total/context_window_tokens)
- agent_loop 三处 LLM 调用点持久化 usage,含 context_window_tokens
- storage 新增 context_window_tokens 列及 migration,batch_session_token_stats 聚合查询
- last_prompt_tokens 查询过滤 prompt_tokens IS NOT NULL,跳过 error/cancel 消息
- build_topic_summaries 简化签名,context_window_tokens 从消息记录读取
- protocol 扩展 TopicTokenStats,按 session_id 聚合天然隔离子代理
前端:
- protocol.ts 新增 TopicTokenStats 类型
- useTopics 映射 token_stats 到 Topic
- TopicList 展示总 token 消耗与上下文占用百分比(绿/黄/红三色)
测试:outbound_dispatcher 4 个测试修复(drop(bus) 不关闭 bus,改用 abort)
|
2026-08-04 17:01:54 +08:00 |
|
oudecheng
|
457f6b2408
|
feat(retry): 为 LLM 主调用添加可配置重试机制,默认 3 次
- config: ProviderConfig 增加 max_retries 字段(serde default=3,向后兼容)
- config: LLMProviderConfig 透传 max_retries,不进 ProviderRuntimeConfig(保持 provider 构造包纯净)
- agent: AgentRuntimeConfig 增加 max_retries,归属 agent 行为层
- agent_loop: 流式 + summary 两个调用点实现重试循环
- 指数退避 1s/2s/4s,仅对 429/502/503/504/timeout/connection reset 重试
- 流式仅在未 emit delta 时重试(AtomicBool 跟踪),避免重复输出
- 退避 sleep 期间响应 cancel_signal,取消优先
- 前端: ProviderConfig 类型和表单增加 max_retries 字段
- 测试: 7 个单元测试(3 判定 + 4 行为),540 个 lib 测试全绿
|
2026-08-04 12:53:33 +08:00 |
|
oudecheng
|
6901659849
|
fix(mcp): 限制 server 名称字符并兜底 tool_name 清洗
OpenAI 要求 function name 匹配 ^[a-zA-Z0-9_-]+$,否则整个请求 400。MCP 工具名由 mcp_{server_key}_{tool_name} 拼成,两个输入源:server_key 用户可控(前端 addMcp/renameMcp 正则校验 + toast),tool_name 由 MCP server 上报(后端 sanitize_tool_name 替换非法字符为 _)。
前端:MCP 卡片头部改用 MapEntryHeader 支持点击重命名,状态指示灯移到上方独立行。顺手修了 MapEntryHeader 进入编辑时 val 未同步当前 name 的 bug。
后端:保留 server_key 和 tool_name 原值用于路由,仅清洗 LLM 可见的 full_name,并在发生清洗时打 warn 日志便于定位。
|
2026-08-04 11:17:41 +08:00 |
|
oudecheng
|
14d903e067
|
fix: Agent 执行与显示层解耦,修复锁屏冻结根因
浏览器锁屏导致 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 ✓
|
2026-08-04 10:55:52 +08:00 |
|
oudecheng
|
c724bbf864
|
chore(web): prettier 一次性格式化并在 CI 启用 format:check
对 47 个前端文件统一执行 npm run format,消除存量格式差异。
随后在 CI 与 Makefile check 中启用 format:check,确保后续提交强制遵守 prettier 风格。
|
2026-08-03 23:25:22 +08:00 |
|
oudecheng
|
cda14360af
|
chore: 建立工程化基线(rustfmt + clippy + CI + eslint + prettier)
配置:
- rustfmt.toml: 固化 max_width=100 / 4 空格缩进,cargo fmt 全量格式化
- Cargo.toml: 配置 [lints.rust] 与 [lints.clippy] 渐进式规则
- .github/workflows/ci.yml: Rust(fmt+clippy+test) + 前端(eslint+tsc+test) 双平台 CI
- Makefile: 新增 check/fmt/fix 目标,clippy 对齐 --all-targets --all-features
- web: eslint flat config + prettier 配置 + package.json 脚本与依赖
- src/main.rs: loop→while 修复 clippy::never_loop
对抗性审查发现并修复:
- eslint 缺 caughtErrorsIgnorePattern 导致 catch(_) 误报为 error
- 前端 lint 未接入 CI,现已补上 Lint 步骤
- Makefile 与 CI 的 clippy flags 不一致,已对齐
|
2026-08-03 23:24:02 +08:00 |
|
oudecheng
|
2a5a0277c0
|
feat(web): 添加话题重命名功能
新增 RenameTopic 命令与 TopicRenamed 协议消息,复用存储层已有的
update_topic_title 方法。后端响应携带刷新后的完整 topic 列表,
前端零额外往返即可同步侧边栏。
前端 TopicList 侧边栏增加内联编辑入口:悬停显示铅笔图标,点击进入
编辑模式,Enter 提交 / Esc 取消 / blur 取消;通过 onMouseDown
preventDefault 防止按钮点击时 input 提前失焦。
包含 5 个单元测试覆盖成功、空标题、不存在、标题未变跳过写入、
can_handle 等场景。
|
2026-08-03 22:10:25 +08:00 |
|
oudecheng
|
649ea2998d
|
feat(web): 集成 ModelSelector 实现主 agent 模型选择 UI
新建 ModelSelector 组件(参考 ExpertSelector 交互模式): 显示当前生效模型,下拉支持 provider/model 选择,草稿暂存+应用提交,重置为默认。
ChatContainer 用共享 flex 容器并排渲染 ExpertSelector 和 ModelSelector,ExpertSelector 简化外层 wrapper(去除冗余 padding/max-width)。
API 层: 新增 sessionSelectedModel 端点常量和 getSelectedModel 函数; types.ts 为 ModelOptionsResponse 添加 current 字段。
按钮语义: 已覆盖时显示 provider/model,未覆盖时显示 '默认 provider/model'。
|
2026-07-30 23:25:14 +08:00 |
|
oudecheng
|
5ff1e8455c
|
feat(web,docs): 前端支持模型下拉选择并更新 README 专家系统章节
- 前端类型 ExpertItem/SubagentItem 新增 provider/model 字段,新增 ModelOptionsResponse 类型
- API 层 createExpert/updateExpert/updateSubagent payload 支持 provider/model,新增 listModelOptions
- ConfigPage 专家/子代理模态框新增模型配置 SectionCard(Provider/Model 下拉框,留空继承默认)
- 空字符串转 undefined 传后端,避免 P0 反序列化为 Some(vec![]) 导致全禁
- README 新增第 8 节专家系统,更新子代理 SUBAGENT.md 字段说明(含 provider/model 和子代理黑白名单)
- README 新增 max_nesting_depth 配置项说明,重排后续章节编号
|
2026-07-30 22:44:28 +08:00 |
|
oudecheng
|
003eab4f21
|
fix(web): 修复点击设置后白屏(React hooks 顺序违规)
子模态框 ESC 处理的 useEffect 被放在 if(loading)/if(!config) 条件 return 之后,导致首次渲染(loading=true)时不执行该 hook,config 加载后重新渲染才执行 → hooks 数量不一致 → React error #310 白屏崩溃。
修复:将 useEffect 移到所有条件 return 之前,与其他 hooks 放在一起。
|
2026-07-30 18:56:43 +08:00 |
|
oudecheng
|
951e31aca4
|
feat(web): 子代理模态框改造并暴露 max_nesting_depth 配置项
对抗审查修复 4 个 P0 问题:
- 子代理 modal handleSave 缺失 allowed_subagents/denied_subagents 字段,导致保存时既有策略被后端 #[serde(default)] 静默清空(数据丢失)
- 子代理 modal 缺失子代理黑白名单 CheckboxList UI
- 子代理 modal 未改造为 ModalHeader/ModalFooter/SectionCard,无 X 关闭按钮与遮罩 onClick
- experts tab 未加载 subagentList,编辑专家时子代理勾选列表显示空
新增改动:
- ui.tsx 新增 ModalHeader/ModalFooter 通用组件
- ConfigPage renderTools 新增最大嵌套深度输入框(后端 TaskConfig.max_nesting_depth 早已存在,默认 2)
- 子代理 modal 子代理选项排除自身避免意外自递归
|
2026-07-30 17:38:08 +08:00 |
|
oudecheng
|
9381ed5dd4
|
feat(web): 专家/子代理 capability 字段改为勾选式编辑
- 专家编辑模态框:4 个 textarea 替换为 CheckboxList,技能/工具按来源分组展示
- 新增子代理编辑模态框:非 builtin 子代理显示编辑按钮,支持描述 + 4 个 capability 勾选字段
- 进入 experts/subagents 标签页时自动拉取技能与工具列表
- allowed_* 为空时传 undefined(后端 None=不限),避免空数组触发白名单空集语义
|
2026-07-30 16:55:38 +08:00 |
|
oudecheng
|
3ea0c19262
|
feat(web): 新增工具列表 API 客户端与 CheckboxList 勾选组件
- api/client.ts: 注册 tools 与 subagentsUpdate 端点常量
- api/tools.ts: 新增 listTools() 调用 /api/tools
- api/subagents.ts: 新增 updateSubagent() 调用 PUT /api/subagents/update
- types.ts: 新增 ToolItem / ToolsListResponse 类型
- ui.tsx: 新增 CheckboxList 组件,支持分组展示、legacy 已选值以可移除标签呈现
|
2026-07-30 16:55:12 +08:00 |
|
oudecheng
|
cea4bd3cfb
|
feat: 子代理支持数据驱动的工具过滤(白名单+黑名单),删除内置 explore
SUBAGENT.md frontmatter 新增 denied_tools 黑名单字段,启用已有的 allowed_tools 白名单;新增 ToolRegistry::only 白名单方法;抽取 build_subagent_tools_registry(白名单→黑名单→depth 兜底);安全修复:resume 在 def 失踪时拒绝恢复而非降级为完整工具集(避免权限提升);depth 阈值改为引用 max_nesting_depth 配置;SubagentWithStatus 暴露工具字段供前端只读展示;删除内置 explore 子代理及专属 explore_max_execution_secs 配置(全栈清理);新增 23 个测试覆盖过滤矩阵、frontmatter 解析、状态投影
|
2026-07-09 14:42:38 +08:00 |
|
oudecheng
|
0a8d21fe40
|
fix: 修复专家发现与前端选择多项问题
后端:
- split_frontmatter 兼容 CRLF/LF 行尾符,修复 Windows 下 EXPERT.md 解析失败
- Config::load_from 移除 fallback 到 cwd/config.json 的逻辑,避免静默加载项目目录配置导致 experts 字段缺失
- ExpertRuntime 新增 update_config 方法,save_config 时同步更新 ExpertRuntime 内部 config,sources 变更即时生效
- source_order 空数组不再兜底返回 [User, Project],尊重用户关闭所有源的意图
- from_config_with_cwd 增加诊断日志,输出 enabled/sources/cwd/discovered
前端:
- ExpertSelector 弹窗改为按钮上方居中展开
- 专家卡片 name 完整显示(break-all),路径显示在卡片底部
- "管理专家"入口仅在无专家时显示
- 每次打开下拉都重新拉取列表和选中状态
- 监听设置弹窗关闭事件刷新已选专家(处理已选专家被禁用的情况)
|
2026-07-08 14:53:30 +08:00 |
|
oudecheng
|
46a1ca6853
|
refactor: 修复 3 项 P1 技术债(硬编码 URL / any 类型 / dead_code)
P1-1: 硬编码 wechat URL 抽常量统一两处
- config/mod.rs: 定义 pub const WECHAT_DEFAULT_BASE_URL
- cli/init.rs: import 并引用常量,消除字面量重复
- 消除两处不一致风险(default_wechat_base_url 与 init 向导)
P1-2: ConfigPage any 类型定义具体类型(8 处)
- types.ts: 新增 FeishuChannelConfig/WechatChannelConfig/ChannelConfig/
SchedulerJobConfig 类型;jobs?: any[] → SchedulerJobConfig[];
channels: Record<string, any> → Record<string, ChannelConfig>
- api/config.ts: 新增 RestartResponse 类型,data: any → RestartResponse
- ConfigPage.tsx: catch (e: any) ×2 → catch (e: unknown) + 类型守卫;
as any → as SchedulerConfig['misfire_policy'];
Record<string, any> → Partial<ChannelConfig>;(ch: any) → (ch: ChannelConfig)
P1-3: dead_code 18 处逐一审查清理
- 删除 10 处未使用函数/struct(YAGNI 原则):
context_compressor.rs (into_messages, is_tool_round)
agent_loop.rs (EmptySkillProvider struct + impl)
memory_maintenance.rs (run_for_scope)
session.rs (try_start/finish_background_compaction)
session_history.rs (try_start/finish_background_compaction)
tool_registry_factory.rs (shell_session_manager)
task/runtime.rs (effective_allowed_tools)
- 保留 8 处 serde 反序列化字段(删除会破坏 JSON 反序列化):
feishu.rs/openai.rs/anthropic.rs/skills/mod.rs/task/runtime.rs
|
2026-07-08 14:51:42 +08:00 |
|
oudecheng
|
fef5ae7626
|
fix: 修复 P0 级技术债(useEffect 依赖项缺失 + prompt 注入吞错)
P0-1: web/src/App.tsx 话题刷新 useEffect 依赖项不完整
- 原依赖数组仅 [topicRefreshTrigger],但 effect 体内使用了
status/handleCommand/sendMessage/requestTopicList
- 导致断线重连后 status 从 disconnected→connected 时
effect 不会重新运行,话题刷新被静默丢失(stale closure bug)
- 补全依赖数组为 [topicRefreshTrigger, status, handleCommand,
sendMessage, requestTopicList],与同文件 200-208 行 effect 风格一致
P0-2: src/gateway/agent_prompt_provider.rs 静默吞错
- record_injection 中 let _ = repository.mark_agent_prompt_reinjected(...)
丢弃了 Result 错误
- 失败时 Agent prompt 注入计数错乱且无任何日志
- 改为 if let Err(e) = ... { tracing::warn!(...) } 记录 session_id 和 error
|
2026-07-08 14:33:22 +08:00 |
|
oudecheng
|
7c578af0bc
|
refactor: 重写 useChat 为组合根,消除上帝 Hook
将 1290 行的 useChat 重写为 360 行组合根:handleServerMessage 从 462 行降为 80 行纯路由分发(Tier1 调度器→Tier2 子智能体→Tier3 主视图),消除组合根层 5 个跨域 ref 同步 effect。UseChatReturn 接口零变更,App.tsx 无需改动。对抗式审查修复 1 个 Critical 项(todo_write 自动刷新的 topic_id 检查)。
|
2026-07-08 11:28:51 +08:00 |
|
oudecheng
|
f3e3ebf55f
|
test: 添加 useChat handleServerMessage 特征测试
新增 13 条特征测试,覆盖 handleServerMessage 全部 20 个 case 与 3 层路由(调度器→子智能体→主视图),锚定行为基线以保护后续组合根重写的回归风险。
|
2026-07-08 11:28:28 +08:00 |
|
oudecheng
|
7038d58207
|
refactor: 抽取 useChat 的领域子 hook 与 messageMappers
将原上帝 Hook 的状态域拆分为 7 个职责单一的子 hook:useConnection/useSessions/useTopics/useMessages/useSubAgentView/useSchedulerView/useSideData,并将消息映射纯函数提取到 messageMappers.ts。各子 hook 自管 ref 同步 effect,跨域依赖通过 options 注入,为组合根重写奠定基础。
|
2026-07-08 11:24:42 +08:00 |
|
oudecheng
|
f5ccf490ba
|
test: 添加 vitest 测试基础设施
新增 vitest 配置、jsdom 环境与 jest-dom 匹配器,并在 package.json 中加入 test/test:watch 脚本与相关 devDependencies,为后续特征测试与子 hook 独立测试提供运行环境。
|
2026-07-08 11:24:10 +08:00 |
|
oudecheng
|
ffc1f79de4
|
refactor: 建立前端 API 客户端层,消除硬编码路径与重复 fetch
- 新增 web/src/api/ 目录(6 个文件):client.ts 基础设施 + 5 个领域模块
- 封装 18 处 fetch 调用为强类型函数,消除 17 处硬编码 /api/ 路径
- 统一 13 处重复的 fetch+json+错误处理模式
- 删除旧 web/src/components/Settings/api/expert.ts,合并到新 api/experts.ts
- ConfigPage.tsx 替换 15 处 fetch 调用,ExpertSelector.tsx 改用新 API
- 保持现有组件行为不变(静默失败仍静默,错误处理等价)
|
2026-07-08 10:03:34 +08:00 |
|
oudecheng
|
c8660df14b
|
refactor: 拆分 storage/mod.rs 与 ConfigPage.tsx (P2)
storage/mod.rs (2957 -> 1834 行):
- 抽出 tests.rs: 17 个测试函数
- 抽出 row_mapping.rs: 8 个 row<->record 映射与单记录查询函数
- 抽出 migrations.rs: 7 个 schema 迁移函数 (ensure_*_schema, has_column, add_column_if_missing)
ConfigPage.tsx (1568 -> 1235 行):
- 抽出 types.ts: 所有接口/类型定义
- 抽出 constants.ts: TABS, inputCls, selectCls, TIMEZONE_OPTIONS
- 抽出 ui.tsx: Field, Toggle, TagEditor, SectionCard, SourceEditor, MapEntryHeader
- 抽出 api/expert.ts: getSelectedExpert, selectExpert (经 ConfigPage 再导出保持兼容)
验证: cargo build, cargo test --lib storage:: 17/17, npm run build
对抗性检查: 8/8 通过 (无丢失/重复函数, 无循环依赖, 无未使用导入)
|
2026-07-07 18:36:23 +08:00 |
|