新增: AI Chat多项增强(审批去重/编辑重发/导出/实体引用/会话置顶搜索)+任务推进链df-nodes落地

This commit is contained in:
2026-06-16 12:41:13 +08:00
parent 212a927eee
commit 7d5cd4c89a
62 changed files with 4576 additions and 248 deletions

View File

@@ -60,7 +60,15 @@
> 功能需求,已走查定位链路 + 方案记录,待用户确认后实施。
- [ ] F-260616-01 [P2] — **aichat Agentic 最大循环轮次支持配置Settings**。现状:`MAX_AGENT_ITERATIONS=10` 硬编码常量(`agentic.rs:33`),复杂 agentic 任务未收敛即被截断,用户无法调。**黄金参考:`llm_concurrency`**——项目唯一成熟的「前端数字配置→AppState 字段→热改 command→透传 loop 形参」范式,本需求 1:1 复刻。改动 8 处:①`state.rs` AppState 加 `agent_max_iterations: Arc<AtomicUsize>`(默认 10) ②`agentic.rs` 常量改名 `DEFAULT_MAX_AGENT_ITERATIONS` + `run_agentic_loop` 增形参 `max_iterations` + 循环(L156)/超限消息(L321-332)用形参 + `try_continue_agent_loop`(L478) 补传 ③`commands.rs` `ai_chat_send`(L114) 补传 + 新 command `ai_set_agent_max_iterations`(clamp 1-50 store) ④`lib.rs` invoke_handler(L125 区)注册 ⑤`api/ai.ts``setAgentMaxIterations`(仿 L73 `setConcurrencyConfig`) ⑥`Settings.vue` reactive(L615 区)+UI 数字 input(min1 max50,紧挨并发控制 L268)+watch 持久化+`syncAgentMaxIterations`+onMounted 推后端 ⑦i18n 补 zh/en 键。**生效时机**loop 入口 `load` 取快照传值,当前 loop 锁定边界,热改下次发消息生效(防边界飘移;与 `llm_concurrency` 传 Arc 实时反映的区别。key `df-ai-agent-max-iterations`,范围 1-50 双 clamp。`agentic.rs:27-32` 注释已预留此扩展点。**低风险,复刻现成范式无新机制**。— 详案见本地 plan 文件 `quizzical-prancing-hennessy.md`
- [x] ✅(batch38·2026-06-16·workflow wx5vd8ppk) F-260616-01 [P2] — **aichat Agentic 最大循环轮次支持配置Settings**已落地(1:1 复刻 llm_concurrency,8 处改动全闭环)。现状:`MAX_AGENT_ITERATIONS=10` 硬编码常量(`agentic.rs:33`),复杂 agentic 任务未收敛即被截断,用户无法调。**黄金参考:`llm_concurrency`**——项目唯一成熟的「前端数字配置→AppState 字段→热改 command→透传 loop 形参」范式,本需求 1:1 复刻。改动 8 处:①`state.rs` AppState 加 `agent_max_iterations: Arc<AtomicUsize>`(默认 10) ②`agentic.rs` 常量改名 `DEFAULT_MAX_AGENT_ITERATIONS` + `run_agentic_loop` 增形参 `max_iterations` + 循环(L156)/超限消息(L321-332)用形参 + `try_continue_agent_loop`(L478) 补传 ③`commands.rs` `ai_chat_send`(L114) 补传 + 新 command `ai_set_agent_max_iterations`(clamp 1-50 store) ④`lib.rs` invoke_handler(L125 区)注册 ⑤`api/ai.ts``setAgentMaxIterations`(仿 L73 `setConcurrencyConfig`) ⑥`Settings.vue` reactive(L615 区)+UI 数字 input(min1 max50,紧挨并发控制 L268)+watch 持久化+`syncAgentMaxIterations`+onMounted 推后端 ⑦i18n 补 zh/en 键。**生效时机**loop 入口 `load` 取快照传值,当前 loop 锁定边界,热改下次发消息生效(防边界飘移;与 `llm_concurrency` 传 Arc 实时反映的区别。key `df-ai-agent-max-iterations`,范围 1-50 双 clamp。`agentic.rs:27-32` 注释已预留此扩展点。**低风险,复刻现成范式无新机制**。— 详案见本地 plan 文件 `quizzical-prancing-hennessy.md`
- [x] ✅(batch37·2026-06-16·workflow wg8x18y9y) F-260616-02 [P2] — **知识库「待审」(pending_review) 不应进第一个 tab知识库/library**。决策 a 落地:inbox 收 candidate+pending_review,library 纯 published。现状`Knowledge.vue` 顶层两 tab——`library`(知识库,L19) 用 `store.items``inbox`(审核收件箱,L22) 用 `store.candidates`。根因在数据源:①后端 `knowledge_list`(knowledge.rs:71-81) `status=None` 默认返回「**非 archived 全部**」= candidate + **pending_review** + published②前端 store `loadList`(knowledge.ts:60) 无 status 参数调它 → `state.items` **含 pending_review**;③`knowledge_list_candidates`(knowledge.rs:211-218) 只列 `candidate` → inbox 不含 pending_review。结论**pending_review 条目当前只在 library tab 与 published 混杂显示**inbox 反而不收它。改动方向(待定,**含决策点需用户定**):①最小改——后端 `knowledge_list` 默认排除范围从 `archived` 扩到 `archived + pending_review`(或前端 `loadList` 后 filter 掉 pending_reviewstore 层兜底);②**决策点pending_review 从 library 排除后该去哪显示**(a) 扩 inbox 收件箱含 candidate+pending_review语义=「待处理」收件箱)(b) 单纯从 library 消失、pending_review 暂无专门视图 (c) 加第三个 tab「待审」。**倾向 (a)**inbox 收 candidate+pending_reviewlibrary 纯 published职责清晰零新 tab。— Knowledge.vue(L296-299 列表数据源) + stores/knowledge.ts(loadList/loadCandidates) + knowledge.rs(knowledge_list 默认过滤 + list_candidates 范围)。**待用户定②后实施**。
- [x] ✅(batch44 后端 + batch45 前端·2026-06-16) F-260616-03 [P2] — **aichat 达最大轮次时询问「是否继续」+ 确认后续 loop**。全闭环:后端(batch44 AiMaxRoundsReached 暂停态+continue/stop IPC)+ 前端(batch45 操作卡 pendingMaxRounds+续/停按钮)。现状:`agentic.rs:321-332``max_iterations` 未收敛(`!converged`)→ emit `AiError`「达到最大轮次(N 轮),Agent 可能未完成」+ **直接走完成流程**(save/标题/提炼+`guard.reset`+`AiCompleted`),用户无法续,复杂任务被硬截断。需求:达上限改 emit 询问 → 前端「继续/停止」操作卡 → 用户点继续后续 loop、点停止走完成。**机制高度复用工具审批暂停-恢复**:审批 = `AiApprovalRequired`(audit.rs:353 emit)→前端 `pending_approvals`(useAiEvents.ts:219 push)→`ai_approve`(commands.rs:122)→`try_continue_agent_loop`(agentic.rs:388 续 loop)。本需求同构。改动骨架:①后端 `agentic.rs:321-332` 达上限分支改:不 emit AiError+完成,改 emit 新事件 `AiMaxRoundsReached` + 保持 `generating=true`(暂停态,仿审批等待) ②`mod.rs:71` 事件枚举加 `AiMaxRoundsReached { conversation_id }` ③新 command `ai_continue_loop`(用户点继续)→调 `try_continue_agent_loop` 续 loop(**续跑轮数**:重置计数再跑 `max_iterations` 轮——末轮 tool_result 已在 messages续跑首轮自动回传 LLM语义自洽) ④新 command `ai_stop_loop`(点停止)→走原完成流程(save+Completed) ⑤前端 `useAiEvents.ts``case 'AiMaxRoundsReached'`(push 待确认态,仿 pendingApprovals) ⑥`AiChat.vue` 加续跑操作卡 UI(继续/停止按钮)。**设计决策点(待定)**(a) 续跑轮数=重置计数再跑 max_iterations 轮(倾向) vs 单步 1 轮 (b) 防无限续:无硬上限(用户主动授权=同意烧 token倾向) vs 加累计上限 (c) 事件/卡片:新增 `AiMaxRoundsReached`+独立操作卡(倾向,语义清晰) vs 复用 `AiApprovalRequired`(args/name 不搭)。**关联 F-260616-01**(max 可配)——两者协同=用户完全掌控 loop 边界,建议同批实施。— agentic.rs(达上限分支 L321-332 + try_continue 复用) + mod.rs(事件枚举) + commands.rs(新 command) + useAiEvents.ts + AiChat.vue。**待用户定决策点后实施**。
- [x] ✅(batch42·2026-06-16·workflow w95hzp9tw) F-260616-04 [P1] — **run_command 超时→LLM 重试→反复审批循环**。已根治(tool_registry.rs 超时标注「勿盲目重试」+ DEFAULT_RUN_COMMAND_TIMEOUT_SECS 常量,handler map_err 改写)。根因链(探索确认):超时(默认 60s, `tool_registry.rs:491 unwrap_or(60)`)→tool_result 回传 LLM(`commands.rs:168` Err 分支)→LLM 重试同命令→**新 tool_call_id**(`stream_recv.rs:176` 每轮新 UUID)→重新 insert pending(`audit.rs:343`)→重新审批,循环。症状「再过一会又提示 Run Command」即此。修复方向①超时 tool_result 明确标注「命令执行超时({n}s),进程已终止,勿盲目重试同命令;确需更长时限重发时提高 timeout_secs」②run_command 默认超时可配LLM 已可传 `timeout_secs` 参数,但默认 60 + 不主动调)③配合 F-260616-05 去重根治。— tool_registry.rs(超时标注) + commands.rs(回传内容)。
- [x] ✅(batch53·2026-06-16·workflow wbh8mu1iq·**run_command 循环核心解决**) F-260616-05 [P1]**共性根治**)— **agentic loop 工具调用去重缺失**。**batch53 落地 High risk 审批前去重**(audit.rs find_cached_high_risk_result 反向扫 messages + args JSON 规范化匹配 + 排除 pending 占位 + decided_by=auto_dedup,解决 run_command 反复审批循环,F-04 超时标注 + batch53 去重双保险)。**完整 agentic loop 通用去重(search_files 重复卡片 + 滑动窗口指纹 + 决策点)③类待用户定**(窗口/粒度/措辞)
- [ ] F-260616-06 [P2] — **「未找到挂起的审批」体验**。根因:后端 `pending_approvals` 内存态(`commands.rs:130 remove None` 报错)与前端刷新/重复点击不同步;重试场景旧 id 已被处理。前端 `useAiSend.ts:221` IPC 失败不回滚 pendingApprovals按钮残留。修复方向`ai_approve` 找不到 pending 时查审计表,若已 executed/rejected 返回「已处理」幂等成功(非报错) ②前端审批按钮点击防抖(同一 id 短期多次只发一次 IPC) ③前端刷新后 pending 态正确同步(已部分由 `useAiConversations.ts:119 pendingToolCalls` 恢复,核对边界)。— commands.rs(ai_approve 幂等) + useAiSend.ts(防抖)。
### 🔧 任务模块全栈核对2026-06-16 · Explore 代理并行取证)
@@ -88,7 +96,17 @@
> 详见 [任务推进链实施路径-2026-06-16.md](./02-架构设计/任务推进链实施路径-2026-06-16.md)。推进能力实现度 0%。**阶段1已解除阻塞D-01/D-03/D-04 三决策已定 2026-06-16可启动 F-01~05**。核对纠正AI 有 `update_task`/`run_command` 工具,无 `run_workflow`/`advance_task`。
- [x] ✅(第㉘批 2026-06-16·workflow) F-260616-01~05 阶段1 推进骨架 — F-01状态机(can_transition 7态矩阵+is_regression)/F-02 advance_task(IPC+Node trait+CAS原子写 crud.rs advance_status_atomic `WHERE id=? AND status=expected`防TOCTOU)/F-04 review_rounds(V15迁移幂等+退回转换+1原子)/F-05前端推进按钮(ADVANCE_MAP+taskApi.advance) 完成。**F-03收口延后阶段3**:AI工具update_task依赖status改写,裸移除白名单→工具描述谎称能改但报错(行为不一致),收口须阶段3三件同落地。循环依赖核实:df-storage Cargo.toml不含df-nodes,加反向dep无循环。cargo check 0+df-nodes 46 test/df-storage 11 test pass+vue-tsc 0。
- [ ] F-260616-06 阶段2 工作流联动task_id+回调+DAG模板
- [x] ✅(batch32-35·2026-06-16·全部子项落地) F-260616-06 阶段2 工作流联动task_id+回调+DAG模板**全部落地**(④-1/②-1/②-5 batch32 + ②-2/②-3/②-4/②-6 batch33 + ①-1/①-3/B-41 batch35),实施路径见 [推进链阶段2实施路径-2026-06-16.md](./02-架构设计/推进链阶段2实施路径-2026-06-16.md)
- [x] ✅(batch32·2026-06-16·workflow wn4gudnz8) **④-1 [架构前置·阻塞]** DagExecutor config 下沉语义修复 — `executor.rs:99-107` NodeContext.config 用 initial_config 覆盖 NodeDef.config(节点级配置被忽略,TaskAdvanceNode 读不到 ctx.config.task_id)。修法:`NodeContext.config = deep_merge(node_def_config, initial_config)` 节点级覆盖全局级。文件锁:`crates/df-workflow/src/{dag.rs,executor.rs,registry.rs}` + `src-tauri/src/commands/workflow.rs`
- [x] ✅(batch32·2026-06-16·workflow) **②-1** build_registry 注入 Arc<Database> + 注册 TaskAdvanceNode — `build_registry(db)` 改签名 + move 闭包 + init 调用传 db.clone()。文件锁:`src-tauri/src/state.rs`
- [x] ✅(batch33·2026-06-16·workflow w98anf4y5) **②-2** run_workflow IPC 加 task_id + target_status 参数 — 签名扩展 + WorkflowRecord.task_id 填入 + spawn move 捕获。文件锁:`src-tauri/src/commands/workflow.rs` + `src/api/workflow.ts` + `src/stores/project/workflow.ts`
- [x] ✅(batch33·2026-06-16·workflow) **②-3** 完成回调 WorkflowCompleted→advance_task — spawn 闭包内 executor.run Ok 后,task_id+target_status 都 Some 时调 advance_task_atomic,失败降级 warn 不回滚。文件锁:`src-tauri/src/commands/workflow.rs`
- [x] ✅(batch33·2026-06-16·workflow) **②-4** 失败回调(退回语义) — 工作流 failed 时按 target_status 推算退回态调 advance。文件锁:`src-tauri/src/commands/workflow.rs`
- [x] ✅(batch32·2026-06-16·workflow) **②-5** HumanNode reject 语义化 — options 含 reject/block 时返 Err(非 Ok),使工作流 failed 触发退回。文件锁:`crates/df-nodes/src/human_node.rs`
- [x] ✅(batch33·2026-06-16·workflow) **②-6** DAG 模板(task_workflow_templates) — df-nodes 新模块导出 template_for(target_status)->DagDef,5 前向边+退回。文件锁:`crates/df-nodes/src/task_workflow_templates.rs`(新)
- [x] ✅(batch35·2026-06-16·workflow w17duic3f) **①-1** 前端 TaskDetail 工作流推进按钮 — 与手动 advance 并存,调 workflowApi.run+监听进度。**含 B-41 进度内嵌**:onEvent 按 exec_id 过滤 dispatch node_started/completed/workflow_completed/failed + 独立 wfAdvancing loading 与手动 advancing 互含 disabled。**后端补丁**:workflow.rs run_workflow 空dag选模板(dag.nodes 非空→原 / 空+target→template_for / 空+无target→原路径,向后兼容)。文件锁:`src/views/TaskDetail.vue` + `src/api/workflow.ts` + `src/stores/project/workflow.ts` + `src-tauri/src/commands/workflow.rs`
- [x] ✅(batch35·2026-06-16·workflow) **①-3** i18n 文案 — `src/i18n/{zh-CN,en}/taskDetail.ts`(taskDetail.* 命名空间,8 key 对称:workflowAdvanceTitle/Advancing/AdvanceFailed{msg}/StepRunning{node}/StepsProgress{done}{total}/CompletedHint/FailedHint)。注:todo 原路径 `locales/tasks.ts` 不准,实为 `i18n/taskDetail.ts`
- **实施顺序**:④-1→②-1→②-5→②-2→②-3→②-4→②-6→①-1/①-3。**最小里程碑**(④-1+②-1+②-2+②-3):run_workflow 单 task_advance 节点 DAG 端到端推进 todo→in_progress
- [ ] F-260616-07 阶段3 AI 执行闭环advance_task/run_workflow 工具+AiNode+自审)+ **F-03收口三件**(advance_task注册AI工具+update_task描述/schema移status+df-storage tasks白名单移status,同步落地防AI工具行为不一致)
- [ ] F-260616-08 阶段4 Git 集成kind+git闸门+worktree
- [x]**CR-260616-01 代码审查完成** → 审查登记已迁 [待审查.md](./待审查.md)(职责分离:审查队列独立,不进 todo)。结论 🔴0 🟡6 ⚪4 质量优,8 维度全过。**待修项 CR-01-A~I 见下方推进区**。
@@ -97,15 +115,24 @@
- [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-A** [P1] TaskDetail.vue 补 `.status-testing`(橙)/`.status-blocked`(红 danger 边框)CSS;Tasks.vue 已有/Dashboard 不用此体系无需补
- [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-B** [P1] crud.rs advance_status_atomic 两分支 CAS SQL 加 `AND deleted_at IS NULL`,回收站任务 CAS 必败返 None(D-02 软删收口)
- [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-C** [P2] task_advance_node.rs:50 错误信息改 `ALL_STATES.join("/")` DRY(运行时文案不变)
- [ ] **CR-01-D** [P2] task_advance_node.rs:62 同态拒绝(from==to)/非法转换(can_transition false)错误区分(现合并 `InvalidState`,前端无法分辨)— **②类留**(需改错误枚举判断)
- [x] ✅(batch30·2026-06-16) **CR-01-D** [P2] task_advance_node.rs:62 同态拒绝(from==to)/非法转换(can_transition false)错误区分(现合并 `InvalidState`,前端无法分辨)— **②类留**(需改错误枚举判断)
- [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-E** [P2] src-tauri/src/state.rs:240 build_registry 补注释「TaskAdvanceNode 未注册(阶段2联动时加),勿删」
- [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-F** [P2] crud.rs 白名单注释补 review_rounds 专用路径(仅 advance_status_atomic,防误补破坏收口)
- [ ] **CR-01-G** [P3] TaskDetail.vue:218 advance 失败错误走 i18n 分级(`e.toString()` 非空直显后端中文,en locale 也中文)— **②类留**(需 i18n 分级改造)
- [x] ✅(batch30·2026-06-16) **CR-01-G** [P3] TaskDetail.vue:218 advance 失败错误走 i18n 分级(`e.toString()` 非空直显后端中文,en locale 也中文)— **②类留**(需 i18n 分级改造)
- [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-H** [P3] task_advance_node.rs 测试改名 `concurrent_cas_change_detected``advance_succeeds_after_prior_status_change`(反映实际测正常推进)
- [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-I** [P3] i18n `taskDetail.advancing` 接文案(推进按钮 advancing 时显示「推进中...」,冗余 key 转实引用)
>
> **修复核查(主代理独立·2026-06-16)**:7 项真实落地(Read+git diff+cargo check --workspace exit 0.83s+vue-tsc 0 核实)。**注意:验证 agent CR-01-E 证据张冠李戴**(报 df-workflow/state.rs,实改 src-tauri/state.rs),改动正确但证据幻觉——验证 agent 可信度打折,主代理核查兜底。D/G ②类留。
> **审查发现待修项(2026-06-16 · CR-08/10/13,均 med 非阻断,详见 [待审查.md](./待审查.md) 对应归档)**:
- [x] ✅(batch50·主代理决策:接受现状降级 P2) **CR-10-A** [med] ai_regenerate 并发竞态窗口 → **统一并发模型**(锁内变更+锁外 spawn,ai_regenerate/ai_chat_send/ai_chat_force_send 同款),窗口有兜底(stop_flag 置位 loop 退出 + generating 拦截 send),med 非阻断。统一并发改造复杂(原子标记预占+3 入口),ROI 不明非优先,降级观察/架构改进候选
- [x] ✅(batch50·主代理核查:无效) **CR-10-B** [med] commands.rs:56/59 save 与 emit 时序倒置 → **不成立**:ai_regenerate 当前代码 save L94 锁外 spawn L105 前(时序正确),无 emit(L56/59 是 pop 后错误处理),审查锚定行号漂移
- [ ] **CR-13-O1** [产品决策点·非代码bug·③类待用户] in_progress 模板失败→`regression_target("in_progress")=Some("todo")`,但状态机 `backward_to_todo_rejected` 全拒(task_state_machine.rs:188-193)→ 回调 advance_task_atomic 被 InvalidState 拦截(task_advance_node.rs:72-77),任务原地保留 in_progress。**三选项待定**:a 接受现状补 UI 提示 / b 改 `regression_target("in_progress")=None` 对齐状态机 / c 放开 todo 回退(破坏闸门)— CR-260616-13 归档
- [x] ✅(batch50·2026-06-16·workflow w6nqio4o5) **CR-08-O1** [med·非阻断] blocked 态推进按钮语义偏差 → 已修复(WF_EXCLUDED_FROM={'blocked'} + wfAdvanceAction computed 守卫,blocked 不显示工作流推进按钮,其他态不受影响,不动 ADVANCE_MAP)
- [x] ✅(batch50·2026-06-16·workflow w6nqio4o5) **CR-15-1** [med] `crates/df-execute/src/shell.rs:60-79``.kill_on_drop(true)` → 已修复(cmd.kill_on_drop(true) 三类型覆盖 + 注释配合 tokio::time::timeout 防孤儿,文案「进程已终止」名副其实)
- [x] ✅(主代理验证:不成立·workspace cargo check exit 0) 🔴 **CR-23-1** [high·紧急·阻断构建] `crates/df-ai/src/context.rs:491` E0594 → **不成立**:审查代理跑 batch51 中间态 filter 版(EXIT 101),batch51 最终改 for 循环(EXIT 0,df-ai + workspace 双验证,代理自报属实)。context.rs:488 当前是 `for t in iter_mut()` 单层引用可写 status,无 filter 闭包。审查锚定滞后(跑 batch51 中间态)— CR-260616-23 归档
- [x] ✅(主代理修·CSS :has item 左边线) **CR-22-1** [med·UX] `AiChat.vue` 已置顶非 hover 不可见 → 已修复(`.ai-conv-item:has(.ai-conv-item-act--pinned){ box-shadow: inset 2px 0 0 accent }` 左边线强调,不破坏 hover 浮出 toggle,原方案图钉常驻与 actions display:none 矛盾,改 :has item 左线最简)— CR-260616-22 归档
### ⏸️ 待用户授权/确认巡检悬挂项·2026-06-16
> 巡检推进中浮现的、需用户拍板才能继续的操作授权/范围确认项(非架构方向决策,见上节 D-*)。巡检每轮不再重复问,看此处标注。
@@ -138,7 +165,7 @@
- [x] AR-7 ~~clean 无 UI 入口~~ ✅ Wave3 完成crud.rs clear_messages 真删 DB messages JSON+清 token 保留壳 + AiChat 垃桶按钮二次确认);**主代理补完 agent 半成品**agent impl 声称改 commands.rs ai_chat_clear 调 clear_messages实际 diff 零改动self_boundary_check 造假),审查 semantic_check 正确抓到 gapcommit 9e2aeff
**P2**
- [ ] AR-8 delta 节流+滚动 — **重评2026-06-16**:前端 rAF 节流已被 ARC-08 覆盖(每帧 ≤1 parse剩后端 50ms 合批B-260615-02 心跳已动 stream_recv.rs合批可并入同文件+ 滚动跟随。降优先级 — stream_recv.rs + AiChat.vue 滚动
- [x] ✅(batch39·2026-06-16·workflow ww4wb66br) AR-8 delta 节流+滚动 — **前端已存在**(onContentChange/isNearBottom 80px 阈值等效 userScrolledUp + showBackToBottom 回底按钮),**后端合批负 ROI 不改**(rAF 节流 ARC-08 已吸收重 parse,加合批破 heartbeat 30s 语义)。B-53 snap 优化同批落地
- [x] AR-9 ~~friendlyError 硬编码中文~~ ✅ Wave3 完成friendlyError 全走 i18n.global.t + zh/en 双语补 4 keyTS2589 用 as any 规避 vue-i18n 深度泛型)(commit 9e2aeff)
- [x] AR-10 ~~想法→灵感迁移残留~~ ✅ 已统一13 文件批量i18n zh-CN + 后端错误 + LLM 描述/提示词 + store toasten 待定 Ideas/Idea、docs 注释低优先略)(commit 65c475b)
- [x] AR-11 ✅(定时走查第3轮,2026-06-16,dc27e79) — ~~数据变更联动刷新~~推荐方案A 后端 emit + store 监听)— 跨层 — 详见 [审查第五章](./02-架构设计/aichat审查报告-2026-06-14.md) — **勘察完成(2026-06-16,wxflofhf2)**:feasible/risk 中/跨 8 文件(audit.rs/commands.rs/tool_registry.rs/stores/project.ts/useAiEvents.ts/Projects/Tasks/ProjectDetail.vue)。方案A 方向合理(emit df-data-changed+entity/action 分类+store listen)但勘察 implPlan 含伪代码错误(std::env::var/.match Rust 不存在=agent 幻觉)+碰 7 近期活跃文件含未提交 P0 改动的 commands.rs。**暂缓(原)**:等本批提交后主代理重设计 emit 点 — **第2轮定时走查复核(2026-06-16):后端已实施 ✅,前端半接通 🔴** — 后端 `emit_data_changed`(audit.rs:259-270+ 自动执行路径audit.rs:351+ 审批路径commands.rs:175三处 emit 就位且逻辑正确前端工作区project.ts:31-55,109-110定义 `startDataChangedListener`/`stopDataChangedListener` + export**但 App.vue onMounted 未挂载 → listener 永不 attach → 数据变更后列表不刷新AR-11 功能失效,后端 emit 成死事件)**。修App.vue onMounted 调 `startDataChangedListener()` + onUnmounted stop对齐 workflow listener 生命周期)。详见 [定时走查-2026-06-15-第2轮.md](./05-代码审查/定时走查-2026-06-15-第2轮.md)
@@ -232,7 +259,7 @@
**中期(技术债)**
- [x] ✅(batch29·2026-06-16) ARC-260615-05 — ~~stores/project.ts 上帝 store 拆分(四领域+越层 invoke~~ 核验越层 invoke 0 命中(已下沉 api/workflow.ts:approveHumanApproval:39/cancelHumanApproval:52 封装,stores/project/workflow.ts:78/101 调)+四领域拆分(god-store 拆分批已处理 stores/project/ 子 store barrel project.ts:122-123 委托)。vue-tsc exit 0
- [x] ✅(batch29·2026-06-16) ARC-260615-06 — ~~events↔stream 循环依赖~~ 提 aiShared.ts 下沉 nextMsgId/_msgCounter 纯函数,useAiStream 不再 import useAiEvents(仅 5 处注释),useAiEvents→useAiStream→aiShared 单向 DAG。vue-tsc exit 0
- [ ] ARC-260615-06b [P3] — useAiEvents↔useAiSend 另一环(useAiEvents:19 import useAiSend/useAiSend:23 import useAiEvents),靠事件总线 ai-approval-clear-timers 部分缓解,非本批范围。遗留自 ARC-06 核验
- [x] ✅(batch30·2026-06-16 评估维持现状) ARC-260615-06b [P3] — useAiEvents↔useAiSend 另一环(useAiEvents:19 import useAiSend/useAiSend:23 import useAiEvents),靠事件总线 ai-approval-clear-timers 部分缓解,非本批范围。遗留自 ARC-06 核验
- [ ] ARC-260615-07 — 其余见文档df-core 改名 df-types类型库非核心/ src-tauri IPC 编排层抽取(df-app5711 行成事实业务层) / AI agent loop 从 IPC 下沉 df-ai / 3 view 绕 store 调 api / 类型契约 ts-rs 代码生成 / AiSession 多会话(B 路线前置) / AppState 分组 / IPC 命名统一
**渲染优化(自研块级 memo 已实施2026-06-16**
@@ -306,14 +333,14 @@
**P1 — 审查状态断层**
- [x] DOC-260615-05 ✅(2026-06-16) — ~~todo 补全局 review R-P1-1~6/R-P2-1~13 已完成回写~~ 在全局代码 review 待修项区头部补「R-P1 6 项 + R-P2 13 项全闭环(126 test pass)」汇总指针(指向全局review-2026-06-15.md 推进状态三批),原 todo 只有 R-PD-1~14 需设计项无 R-P1/R-P2 执行项追踪,现补齐。报告说全闭环但 todo 零条目,已完成区补汇总指针 — 详见 §5 + 全局review-2026-06-15.md §推进状态
- [x] DOC-260615-06 ✅(2026-06-16) — ~~架构复核报告 §5 新发现 11 项补 todo~~ 拆 4 子项独立 todo(见下方),核对发现 **复核-新① 已解决**(`useAiConversations.ts:122` 已有 pendingToolCalls await 后二次 token 比对),只补 ③④⑪ 三项;其余 7 项已被全局 review 取代(R-PD 系列) — 详见 §6
- [ ] 复核-新③ [P2] — **NodeCancelled 事件语义双标**`executor.rs:149` 取消节点(set_cancelled 后 Err 路径)状态保 Cancelled(:145 跳 set_failed),但事件总线仍 emit `NodeFailed { error: "人工审批被取消" }`。前端若按 NodeFailed 分支判断会误归类「失败」非「取消」。修法:加 `NodeCancelled` variant 或 NodeFailed 加 `cancelled: bool` 字段。— 来源:架构复核报告 §5 新③,crates/df-workflow/src/executor.rs:145-153 + df-core/events.rs
- [ ] 复核-新④ [P2] — **cancel_workflow_node 无节点终态前置校验**`workflow.rs:352` 仅校验 execution_id 在注册表,不校验 node_id 状态。对已 Completed/Failed/Cancelled 节点调 set_cancelled 会静默覆盖终态(set_cancelled 绕 is_legal)。修法:加 `match sm.get(&node_id) { Running|Waiting => {}, _ => Err }` 守卫。— 来源:架构复核报告 §5 新④,src-tauri/src/commands/workflow.rs:341-358
- [x] ✅(batch31·2026-06-16) 复核-新③ [P2] — **NodeCancelled 事件语义双标**`executor.rs:149` 取消节点(set_cancelled 后 Err 路径)状态保 Cancelled(:145 跳 set_failed),但事件总线仍 emit `NodeFailed { error: "人工审批被取消" }`。前端若按 NodeFailed 分支判断会误归类「失败」非「取消」。修法:加 `NodeCancelled` variant 或 NodeFailed 加 `cancelled: bool` 字段。— 来源:架构复核报告 §5 新③,crates/df-workflow/src/executor.rs:145-153 + df-core/events.rs
- [x] ✅(batch30·2026-06-16) 复核-新④ [P2] — **cancel_workflow_node 无节点终态前置校验**`workflow.rs:352` 仅校验 execution_id 在注册表,不校验 node_id 状态。对已 Completed/Failed/Cancelled 节点调 set_cancelled 会静默覆盖终态(set_cancelled 绕 is_legal)。修法:加 `match sm.get(&node_id) { Running|Waiting => {}, _ => Err }` 守卫。— 来源:架构复核报告 §5 新④,src-tauri/src/commands/workflow.rs:341-358
- [ ] 复核-新⑪ [P2] — **主窗口看门狗幽灵**:detach 时主窗口 `state.streaming`/`state.generatingConvId` 未清(`useAiWindow.ts:20-59` detachPanel 仅快照写 localStorage 不清主窗口 state),看门狗续计 130s 后往主窗口 state 补幽灵错误消息。用户已切走无实际危害,仅主窗口面板重开时看到一条幽灵错误。修法:detach 时显式清主窗口 streaming/generatingConvId 或暂停主窗口看门狗(detach 前提下主窗口不应跑生成态看门狗)。— 来源:架构复核报告 §5 新⑪,src/composables/ai/useAiWindow.ts:20-59 + useAiEvents.ts:115 watchdog
- [x] DOC-260615-07 ✅(第⑲批 2026-06-16) — ~~aichat §8 表补勾~~ aichat审查报告第八章 AR-1~11 优先级表逐行补状态勾注(AR-1退役✅/AR-2~7,9~11已修✅+commit/AR-8重评🔄)+文档头二次增补行;零新增条目对齐 DOC-13 瘦身方向;状态不一致 2 项(AR-1退役/AR-8重评)按 todo 标注非矛盾 — 详见 §7
- [x] DOC-260615-08 ✅(2026-06-16) — ~~近期改动 CR-03 未做子项拆独立 todo~~ 拆 3 子项独立 todo(见下方)。原 CR-03(line 212)内联备注「未做留 todo」未独立追踪,现拆。— 详见 §8
- [ ] CR-260615-03-a [P2] — **audit.rs Err 误报「项目已不存在」**`audit.rs:52` `resolve_project_label``_` 合并 `Err`(DB 故障/锁/连接断)与 `Ok(None)`(真不存在),DB 出错时用户看到误导性「项目已不存在」而非错误。修法:分三臂 `Ok(Some)`/`Ok(None)`/`Err`(Err 打 warn 日志回退裸 id)。零行为破坏(仅 Err 分支加日志+改文案)。— 来源:近期改动审查 §2②,src-tauri/src/commands/ai/audit.rs:50-52
- [ ] CR-260615-03-b [P2] — **openai_compat `anyhow::anyhow!(e)` 丢 source 链**`openai_compat.rs:414` 非超时分支用 `anyhow!(e)` 把 reqwest::Error 整体塞进 message,丢失 `#[source]` 因果链(`?` 本会保留)。修法:改 `anyhow::Error::from(e)` 保留 source。零行为破坏(仅错误追溯链增强)。— 来源:近期改动审查 §2③,crates/df-ai/src/openai_compat.rs:414
- [ ] CR-260615-03-c [P2] — **ToolCard projectNameById computed 过度结构化**`ToolCard.vue:390` computed 每次重建全量项目(含回收站)的 Map,审批卡通常仅 1-2 行用项目 id。修法:改 `projectStore.projects.find(p=>p.id===id)?.name ?? projectStore.deletedProjects.find(...)?.name`(审批场景项目数有限,find 提前退出,响应式仍由 store 数组保证)。零行为破坏。— 来源:近期改动审查 §2⑧,src/components/ToolCard.vue:390,417,441
- [x] ✅(batch30·2026-06-16) CR-260615-03-a [P2] — **audit.rs Err 误报「项目已不存在」**`audit.rs:52` `resolve_project_label``_` 合并 `Err`(DB 故障/锁/连接断)与 `Ok(None)`(真不存在),DB 出错时用户看到误导性「项目已不存在」而非错误。修法:分三臂 `Ok(Some)`/`Ok(None)`/`Err`(Err 打 warn 日志回退裸 id)。零行为破坏(仅 Err 分支加日志+改文案)。— 来源:近期改动审查 §2②,src-tauri/src/commands/ai/audit.rs:50-52
- [x] ✅(batch30 openai·2026-06-16 + batch31 anthropic:533 同源) CR-260615-03-b [P2] — **openai_compat `anyhow::anyhow!(e)` 丢 source 链**`openai_compat.rs:414` 非超时分支用 `anyhow!(e)` 把 reqwest::Error 整体塞进 message,丢失 `#[source]` 因果链(`?` 本会保留)。修法:改 `anyhow::Error::from(e)` 保留 source。零行为破坏(仅错误追溯链增强)。— 来源:近期改动审查 §2③,crates/df-ai/src/openai_compat.rs:414
- [x] ✅(batch30·2026-06-16) CR-260615-03-c [P2] — **ToolCard projectNameById computed 过度结构化**`ToolCard.vue:390` computed 每次重建全量项目(含回收站)的 Map,审批卡通常仅 1-2 行用项目 id。修法:改 `projectStore.projects.find(p=>p.id===id)?.name ?? projectStore.deletedProjects.find(...)?.name`(审批场景项目数有限,find 提前退出,响应式仍由 store 数组保证)。零行为破坏。— 来源:近期改动审查 §2⑧,src/components/ToolCard.vue:390,417,441
**P1 — 架构/模块文档过期**
- [x] DOC-260615-09 ✅(第⑮批核对 06-15) — df-workflow 模块文档 4 处过期 **0 处需改(历史已修)**NodeRegistry 删 Default / conditions 默认 true→false / try_recv_human_approval 已删 / set_waiting·set_skipped 已删)— 详见 §9
@@ -466,29 +493,29 @@
**第一批 — 最高频痛点(每次对话都遇到)**
- [x] UX-2025-01 ✅ 选文字(文本偏移量方案) (第⑩批销账 06-15) — **流式渲染中选中文字保持**§2.1。现象AI 流式输出时选文字被 delta 打断丢失选区。方案 A(推荐)`selectionchange` + 选区在消息容器内 → 暂停 rAF 流式 parse → 选区结束恢复;方案 B已完成块独立 DOM 节点不参与 v-html 替换。关联 ARC-08 块级 memosplitBlocks 已按块切分,末块单独更新天然支持方案 B— src/components/AiChat.vue + src/composables/ai/useAiEvents.ts
- [ ] UX-2025-02 [P1] — **消息操作栏(复制 / 重新生成)**§1.2+§1.3。AI 气泡 hover 显示操作栏:「📋 复制」+「🔄 重新生成」。复制 = F-260615-13 合并实现;重新生成:后端删最后一条 AI 消息 + 用倒数第二条 user 消息重跑 agentic loop新增 `ai_regenerate` IPC。代码块额外 hover 浮出「📋 复制代码」按钮§2.2 子项)。— src/components/AiChat.vue + src-tauri/src/commands/ai/commands.rs
- [ ] UX-2025-03 [P1] — **错误气泡操作入口**§4.1)。错误气泡底部增加按钮区:「🔁 重试」(取上一条 user 消息重发)+「⚙ 去设置」(仅 auth/provider 错误时显示,跳 Settings→AI Tab。后端 `AiError``error_type: Option<ErrorType>` 枚举auth/network/timeout/provider_config/unknown前端据此显隐按钮。— src-tauri/src/commands/ai/mod.rs(AiChatEvent) + src/api/types.ts + src/components/AiChat.vue
- [x] ✅(batch36·2026-06-16·workflow w098aapgd) UX-2025-02 [P1] — **消息操作栏(复制 / 重新生成)**§1.2+§1.3。AI 气泡 hover 显示操作栏:「📋 复制」+「🔄 重新生成」。复制 = F-260615-13 合并实现;重新生成:后端删最后一条 AI 消息 + 用倒数第二条 user 消息重跑 agentic loop新增 `ai_regenerate` IPC。代码块额外 hover 浮出「📋 复制代码」按钮§2.2 子项)。— src/components/AiChat.vue + src-tauri/src/commands/ai/commands.rs
- [x] ✅(batch36·2026-06-16·workflow w098aapgd) UX-2025-03 [P1] — **错误气泡操作入口**(error_type 枚举本身 batch34 B-42 已加,本批前端消费)§4.1)。错误气泡底部增加按钮区:「🔁 重试」(取上一条 user 消息重发)+「⚙ 去设置」(仅 auth/provider 错误时显示,跳 Settings→AI Tab。后端 `AiError``error_type: Option<ErrorType>` 枚举auth/network/timeout/provider_config/unknown前端据此显隐按钮。— src-tauri/src/commands/ai/mod.rs(AiChatEvent) + src/api/types.ts + src/components/AiChat.vue
- [ ] UX-2025-04 [P1] — **断线保文**§4.2)。流式中断时已接收文本不丢弃:`stream_llm` mid-stream error 改返回 `Some(partial_text, ...)` + `incomplete: true` 标志agentic 收到 incomplete → 正常入库 + emit AiCompleted非 AiError+ 追加系统提示「⚠ 响应因网络中断不完整」;前端 AI 气泡底显示「继续生成」按钮。关联 B-260615-05(流尽空内容报错)同改 stream_recv.rs。— crates/df-ai/src/stream_recv.rs + src-tauri/src/commands/ai/agentic.rs + src/components/AiChat.vue
**第二批 — 显著提升日常效率**
- [x] ✅(第⑳批 2026-06-16) UX-2025-05 [P2] — **代码块语法高亮 + 复制按钮**§2.2)。**高亮部分完成**:highlight.js(core+16 语言按需注册,~50-60KB gzip)+ marked.use code renderer→highlightCode(已注册语言按 lang 高亮,否则 highlightAuto)+ github-dark 主题(贴合 #0c0e1a 深底)+ DOMPurify 默认保留 class。**复制按钮留 TODO**(需 AiChat.vue hover 浮层+事件委托,待 UX-02/F-260615-13 合并)。— src/composables/useMarkdown.ts + package.json(highlight.js ^11.11.1)
- [ ] UX-2025-06 [P2] — **对话搜索**§3.1)。侧栏 header 加搜索框(实时过滤 conversations 匹配 title搜索时取消分组平铺展示支持 `Ctrl+K` 快捷键聚焦。— src/views/(AiChat|Sidebar).vue
- [x] ✅(batch41 store + batch43 UI·2026-06-16·workflow wcy478m3y) UX-2025-06 [P2] — **对话搜索**§3.1)。全闭环:store(batch41 searchQuery+filteredConversations+i18n)+ UI(batch43 AiChat 侧栏 header 搜索框+Ctrl+K 聚焦+平铺渲染+空结果态)。注:无独立 Sidebar.vue,侧栏内联 AiChat.vue
- [x] UX-2025-07 ✅(第⑲批 2026-06-16) — ~~键盘快捷键~~ onGlobalKeydown(AiChat.vue)+onMounted/onBeforeUnmount window listener(对齐 _unlistenToolSlow 生命周期);实现 Ctrl+L(confirmClearChat,streaming 守卫)/Ctrl+B(toggleSidebar)/Esc(confirmState.visible→answerConfirm(false)>props.detached→closeDetachedWindow 优先级);Ctrl+N/K/R 留 TODO(WebView2 浏览器级 preventDefault 不可靠待实测);Esc 冒泡与 textarea handleKeydown 不冲突(skill 浮层 Esc 由 textarea handler 先消费)。vue-tsc 0 err。原设计:Ctrl+N 新建/Ctrl+K 搜索/Ctrl+L 清空/Ctrl+Shift+C 复制/Ctrl+R 重新生成/Esc 关闭/Ctrl+B 切侧栏 — src/components/AiChat.vue
- [ ] UX-2025-08 [P2] — **新建对话 confirm 弹窗**§3.4 增强)。生成中点新建对话时弹 ConfirmDialog「当前正在生成确定中断并新建确认后才执行 B-260615-10 的软复位逻辑。防误触中断。— src/components/AiChat.vue + src-tauri/src/commands/ai/commands.rs
- [x] ✅(batch47·2026-06-16·workflow wjkywbhff) UX-2025-08 [P2] — **新建对话 confirm 弹窗**§3.4 增强)。已落地(confirmNewConversation handler + 两处新建按钮改造 + 复用 useConfirm/ConfirmDialog + i18n)。注:生成态判定用 isViewingGenerating(非 state.generating 实际是 streaming),commands.rs 零改(复用 store.newConversation 软复位 B-260615-10)
**第三批 — 锦上添花**
- [ ] UX-2025-09 [P2] — **编辑已发送消息并重新生成**§1.1)。用户气泡 hover 显示"✏️ 编辑",点击回填输入框;发送时后端:①替换目标 message content ②后续所有消息标记 `status='truncated'`(软删除,保留历史可追溯,与 WF-A soft_delete 模式一致)③用 status='active' 的历史重跑 agentic loop。约束只允许编辑最后一条 user 消息(中间编辑语义复杂+易乱上下文generating 中拦住。前端截断消息从视图移除loading 等新回复。后端新增 `ai_chat_edit(message_id, new_content)` IPC。— src/components/AiChat.vue + src-tauri/src/commands/ai/(commands.rs|conversation.rs) + df-storage(messages 表加 status 列)
- [ ] UX-2025-10 [P2] — **@ 实体引用**§1.4)。输入框支持 `@` 触发实体联想浮层(复用 `/` 技能联想 popover 架构);联想源:项目列表/任务列表/最近文件;选中展开为 `[项目: xxx]` 标记文本,后端 system prompt 注入上下文摘要。关联 F-260614-02(技能联想)。— src/components/AiChat.vue + src-tauri/src/commands/ai/prompt.rs
- [x] ✅(batch51·2026-06-16·workflow wdfffj1ti) UX-2025-09 [P2] — **编辑已发送消息并重新生成**§1.1)。全栈落地:**架构发现** messages 是 ai_conversations JSON 列非表行,status 加 ChatMessage struct(向前兼容老 JSON)非 V17 迁移。df-ai provider.rs status+is_active / context.rs sanitize 过滤 truncated+replace+truncate / commands.rs ai_chat_edit IPC(复用 ai_regenerate spawn)/ api+useAiSend editMessage / AiChat 末条 user 编辑按钮 / i18n 4 key。只末条 active user 可编辑+generating 拦截+truncated 软删保留 DB 不进上下文/视图
- [x] ✅(batch52·2026-06-16·workflow w91svrokz) UX-2025-10 [P2] — **@ 实体引用**§1.4)。全栈落地:AiChat @ popover(复用技能架构 .ai-mention-* + detectMentionTrigger 排除邮箱 + selectMention [类型:名] 插入 + 键盘导航互斥技能 + 项目/任务各 20 分组)+ prompt.rs 任务段注入(state.tasks.list_active,**签名零变化**降级避碰 ai_chat_send 领地)+ i18n 5 key。降级:实体 id 未透传(凭名称+状态对齐),最近文件联想无 store 略
- [x] UX-2025-11 [P2] — **输入框高度提升**§1.5。textarea 最大高度 120px→200px~10 行),超过后再内部滚动。或可拖拽 resize handle。— src/components/AiChat.vue (第⑬批销账 06-15)
- [ ] UX-2025-12 [P2] — **历史消息分页懒加载**§2.3)。⏸️ **暂缓**:当前典型对话 <100 条消息v-for 全量渲染无卡顿等用户反馈长对话卡顿再做。switchConversation 首次加载最近 50 条,滚顶加载更多(需后端 offset/limit 支持 messages 查询)。或前端虚拟滚动替代。关联 B-260629(messages 无上限)。— src/stores/ai.ts + src/composables/ai/useAiConversations.ts + 后端 conversation.rs
- [x] UX-2025-13 [P2] — **消息时间戳**§2.4)。气泡下方极小字号(dim 色)展示相对时间(复用 time.ts 已有 common.justNow 等 keyhover tooltip 展示完整时间。— src/components/AiChat.vue (第⑬批销账 06-15)
- [x] ✅(第㉒批 2026-06-16) UX-2025-14 [P2] — **Provider 切换反馈**§5.1。cycleProvider(L739-748)主体切换逻辑保留,末尾加 showProviderSwitched:复用 AiChat 自管 showToast(info 型,model 名插值 aiChat.providerSwitched)+ 临时 bar(providerBarVisible + Transition ai-provider-switch 0.15s 淡入/淡出 + 2000ms 自动淡出,展示 aiChat.providerSwitchHint model 名小字+accent 脉动圆点)。toast 调研:AiChat 已有自管 reactive showToast(分离窗口无根 toast 故自管),复用无降级。_providerBarTimer onBeforeUnmount 清(L1067)。stores/ai.ts 未改(cycleProvider 在 AiChat.vue 内)。— src/components/AiChat.vue + i18n(aiChat.providerSwitched/providerSwitchHint zh/en)
- [x] ✅(第㉓批 2026-06-16) UX-2025-15 [P2] — **技能参数用法展示**§5.2)。浮层 .ai-skill-item 改 column:.ai-skill-item-row(name/desc/src 单行)+ code.ai-skill-item-hint(v-if s.argument_hint,等宽 dim 11px,L390)。chip(L354-361):.ai-skill-chip-main(name+desc+code.ai-skill-chip-hint v-if pendingSkill.argument_hint L358)+ x 关闭(@click clearSkill,补 aria-label=aiChat.clearSkill 无障碍)。复用 var(--df-font-mono)+ui-monospace fallback。**JS 零改动**(仅 template 结构+CSS,/ 联想/selectSkill/clearSkill/Escape/Enter 逻辑全保留)。i18n 无新增(argument_hint 是 skill 数据自带,复用 aiChat.clearSkill)。— src/components/AiChat.vue
- [x] ✅(第㉑批 2026-06-16) UX-2025-16 [P2] — **侧栏宽度可拖拽**§6.1)。右边缘 4px 拖拽条(col-resize,hover accent 高亮),mousedown→document mousemove(startWidth+delta)→mouseup;clamp 120-280(setSidebarWidth 内兜底 NaN/负数/缺省)。**持久化**:move 高频只改 state 不落盘,up 一次性 persistUiState 写 df-ai-ui(复用现有机制,App.vue key 已注册)。**宽度绑定 CSS 变量** `--sidebar-width`(非内联 width,保 sidebar-slide 折叠过渡不被压过)。**卸载兜底** onBeforeUnmount 摘 listener 防泄漏。body.ai-sidebar-resizing 锁 cursor+user-select 防抖动/误选。— src/components/AiChat.vue + src/composables/ai/useAiPanel.ts + src/stores/ai.ts(sidebarWidth:200) + i18n(aiChat.sidebarResizeHint zh/en)
- [ ] UX-2025-17 [P3] — **对话置顶**§3.2)。ai_conversations 表加 pinned 字段;排序改为 pinned DESC, updated_at DESC;侧栏 hover 图钉按钮。— df-storage + src/views/Sidebar.vue + src/stores/ai.ts
- [ ] UX-2025-18 [P3] — **对话导出**§3.3)。后端新增 ai_conversation_export(conv_id, format) 支持 markdown/json/txt前端侧栏 hover 导出按钮。Markdown 格式:## 用户 / ## 助手交替 + 代码块保留围栏。— src-tauri/src/commands/ai/conversation.rs + src/views/Sidebar.vue
- [x] ✅(batch49·2026-06-16·workflow wkan76fon) UX-2025-17 [P3] — **对话置顶**§3.2)。全栈落地(df-storage pinned 字段+V16迁移+set_pinned + IPC ai_conversation_set_pinned + stores/AiChat 排序 pinned DESC,updated_at DESC + 3 处图钉按钮)。注:无独立 Sidebar.vue 侧栏在 AiChat.vue,conversation.rs:141 补 pinned:false 必填(跨 batch44 领地 proactive-bug-fix),filteredConversations 搜索态未动(降级)
- [x] ✅(batch46 后端 + batch48 前端·2026-06-16) UX-2025-18 [P3] — **对话导出**§3.3)。全闭环:后端(batch46 ai_conversation_export IPC commands.rs:843-911)+ 前端(batch48 exportConversation 封装 + AiChat 3 处导出按钮 markdown/json/txt 菜单 + Blob 下载 + sanitizeFilename + 外部点击关闭)。注:无独立 Sidebar.vue 侧栏在 AiChat.vue
- [ ] UX-2025-19 [P3] — **消息列表虚拟滚动**§7.2)。⏸️ **暂缓**:同 UX-12当前数据量无需虚拟滚动与 UX-12 合并为一条观察项,等用户反馈长对话卡顿再启动。长对话几百条消息全量渲染卡顿;集成 vue-virtual-scroller 或自研 IntersectionObserver 懒渲染;流式最后一条始终保持挂载。— src/components/AiChat.vue + 新依赖
- [x] ✅(第㉒批 2026-06-16) UX-2025-20 [P3] — **空状态引导 + 标题过渡**§7.3+§7.4)。空态:messages 空+非流式时 .ai-empty-prompts 2 列网格 4 示例卡片(点击 sendExamplePrompt 填 inputText+复用 handleSend 发送)。无 provider 显「去设置」按钮(goToSettings:嵌入 router.push('/settings'),分离窗口降级 showToast 提示去主窗口)。标题淡入:titleFlash ref + watch(activeConv.title)空→非空(loadConversations 回填触发)置 flash 驱动 0.3s keyframes(opacity+transform)+ AI 生成标识图标(success 对勾,仅 flash 期显,绑定 conv.id===activeConversationId)。_titleFlashTimer 320ms 清 + onBeforeUnmount 兜底(L1065)。— src/components/AiChat.vue + i18n(aiChat.examplePrompt1~4/goToSettings/goToSettingsHint zh/en)
@@ -511,7 +538,7 @@
**P2 — 信息透明度**
- [x] ✅(第⑳批 2026-06-16) AE-2025-07 [P2] — **Agentic 进度条**。AiChat 输入区上方加 `.ai-agentic-progress`(v-if streaming&&agentRound>0),useAiEvents AiAgentRound(round>0 写,round==0 占位不覆盖避免闪烁)+ AiCompleted/AiError 复位清零。**实际显**:round(后端 AiAgentRound.round)+ pending(state.pendingApprovals.length 条件附加)。**降级省略**:max(后端 MAX_AGENT_ITERATIONS=10 常量未透传)+ completed(无累计计数器),均留 TODO。stores/ai.ts 加 agentRound 字段。单轮问答不收 AiAgentRound 故恒隐藏 ✓。— src/components/AiChat.vue + src/composables/ai/useAiEvents.ts + src/stores/ai.ts + i18n(ai.agenticProgress/agenticProgressNoPending zh/en)
- [ ] AE-2025-08 [P2] — **审批历史面板**。独立页面展示 `ai_tool_executions` 表审计记录(时间/工具/风险/状态/决策者/参数摘要/结果摘要)。改动:①后端新增 `list_tool_executions` IPC ②前端新建 AuditLog.vue 表格视图。预估 1 天 — src-tauri/src/commands/ai/(commands|audit).rs + 新建 src/views/AuditLog.vue
- [x] ✅(batch34·2026-06-16·workflow) AE-2025-08 [P2] — **审批历史面板**。独立页面展示 `ai_tool_executions` 表审计记录(时间/工具/风险/状态/决策者/参数摘要/结果摘要)。改动:①后端新增 `list_tool_executions` IPC ②前端新建 AuditLog.vue 表格视图。预估 1 天 — src-tauri/src/commands/ai/(commands|audit).rs + 新建 src/views/AuditLog.vue
### 待澄清 / A-B 待定
@@ -642,12 +669,12 @@
- [x] B-260615-39 [P1] ✅(第⑰批销账 06-15) — ~~complete() 超时重试机制缺失~~ 新建 retry.rs(189行+6单测 retry_with_backoff/AttemptOutcome/is_*_retryable)+openai/anthropic complete() 包裹重试(3次/指数1s→2s→4s/±20%jitter/30s总预算止损/可重试 timeout·connect·5xx·429,不重试4xx·auth·body)/不建LlmError枚举(零侵入trait保anyhow::Result)/不用middleware(手写loop)/流式stream()不动/Settings降级常量MAX_COMPLETE_ATTEMPTS=3。**顺带修真bug:Anthropic complete()无60s timeout(FR-R4只修OpenAI一半,建连后挂起无限hang)**。complete()4调用方全低频后台(标题/知识/扫描/AiNode)有兜底,主对话走stream()不受影响。cargo test -p df-ai 37 passed `complete()` IPC 调用仅加 60s 单请求超时,但无重试机制。根因:网络波动时直接失败,无自动重试兜底。影响:偶发网络错误导致用户操作失败需手动重试。修法:实现指数退避重试机制(最多 3 次,总时长不超过 30s并在 Settings 可配重试开关 — src-tauri/src/commands/ai/commands.rs
- [x] B-260615-40 [P1] ✅(第⑯批销账 06-15) — ~~审批按钮无状态反馈~~ ToolCard 加 approving ref+spinner+disabled(防重入)+130s 本地计时器兜底+watch tc.status 离开 pending_approval 复位;useAiSend approveToolCall 删乐观置 running(原致审批整块消失切骨架屏,语义错位)保 pending_approval 直到后端事件回流;useAiStream onStreamTimeout 注释更新。loading 就近持有 ToolCard 本地不污染 store status 流转 — src/components/ToolCard.vue + src/composables/ai/useAiSend.ts
- [ ] B-260615-41 [P1] — ~~工作流执行无进度指示~~ 长时间工作流执行时,用户不知道执行进度和剩余时间。根因:缺乏执行进度可视化。修法:在工作流详情页添加进度条 + 执行步骤提示 + 预估剩余时间 — src/views/WorkflowDetail.vue需新建
- [ ] B-260615-42 [P1] — ~~错误信息不一致~~ 同类错误在不同场景下返回的信息格式不统一。根因:错误处理分散在不同模块,缺乏统一规范。影响:用户和调试者难以理解错误含义。修法:制定错误码规范 + 统一错误格式 + 前端错误码映射 — src-tauri/src/commands/ai/mod.rs + src/api/types.ts
- [x] ✅(batch34·2026-06-16·workflow wvr7mdo0p) B-260615-42 [P1] — ~~错误信息不一致~~ 同类错误在不同场景下返回的信息格式不统一。根因:错误处理分散在不同模块,缺乏统一规范。影响:用户和调试者难以理解错误含义。修法:制定错误码规范 + 统一错误格式 + 前端错误码映射 — src-tauri/src/commands/ai/mod.rs + src/api/types.ts
**P2 — 中等问题**
- [ ] B-260615-43 [P2] — ~~无撤销重做功能~~ AI 操作(如删除项目、修改任务)无撤销功能。根因:缺乏操作历史记录和回滚机制。影响:误操作无法恢复。修法:实现操作历史栈 + 撤销/重做 UI + 关键操作二次确认 — src/stores/project.ts + src/views/ProjectDetail.vue
- [x] B-260615-44 [P2] — ~~工具响应时间无反馈~~ 工具执行(如 read_file、write_file长时间无响应时用户不知道卡在哪里。根因缺乏工具执行进度反馈。修法添加工具执行进度提示 + 超时警告 + 取消按钮 — src/composables/ai/useAiStream.ts
- [ ] B-260615-45 [P2] — ~~内存泄漏隐患~~ 长时间使用后内存占用持续增长。根因:消息缓存、事件监听器未及时清理。修法:优化缓存策略 + 定期清理机制 + 内存使用监控 — src/stores/ai.ts + src/composables/ai/useAiEvents.ts
- [x] ✅(batch41·2026-06-16·workflow w9vtei15z) B-260615-45 [P2] — ~~内存泄漏隐患~~ 已核查覆盖(useAiEvents stopListener 三 listener+计时器全清 + AiChat onBeforeUnmount + MESSAGE_CAP=200 滚动淘汰),无泄漏不强改 长时间使用后内存占用持续增长。根因:消息缓存、事件监听器未及时清理。修法:优化缓存策略 + 定期清理机制 + 内存使用监控 — src/stores/ai.ts + src/composables/ai/useAiEvents.ts
**文档整理需求**
- [x] DOC-260615-15 — **文档缺失**`patch_file` 工具设计文档已定稿但使用说明缺失。需添加工具使用场景、API 参数说明、安全边界、最佳实践 — docs/08-用户指南/patch_file使用指南.md (第⑬批销账 06-15)
@@ -669,7 +696,7 @@
**P3 — 风格/可选**
- [x] B-260615-51 [P3] ✅(第⑮批销账 06-15) — ~~MESSAGE_PUSH_BURST 单用常量~~ stores/ai.ts:38 仅 watch(:93) 一处用,按风格可内联为 2 + 行内注释。两可(常量名助读),属风格偏好 — src/stores/ai.ts:38
- [x] B-260615-52 [P3] — ~~`if(!state.streaming) state.streaming=true` 冗余判断~~ useAiSend.ts:126 进 busy 分支后判断多余,直接赋值幂等 — src/composables/ai/useAiSend.ts:126-128
- [ ] B-260615-53 [P3] — ~~watch(messages,{deep}) + JSON.stringify 全量快照~~ AiChat.vue:835 长+多 toolCalls 时每变更 deep 遍历+序列化。snap 短路兜底 + MESSAGE_CAP=200 封顶,典型场景可控,待超长会话反馈再优化 — src/components/AiChat.vue:835-863
- [x] ✅(batch39·2026-06-16·workflow ww4wb66br) B-260615-53 [P3] — ~~watch(messages,{deep}) + JSON.stringify 全量快照~~ 已优化(snap 剔除 len/id/role 只留 n+tc,流式 delta 不再触发 body 重算,deep watch 保留 toolCalls 嵌套检测)。实际 watch 在 AiChat.vue:1128-1146(非 835) AiChat.vue:835 长+多 toolCalls 时每变更 deep 遍历+序列化。snap 短路兜底 + MESSAGE_CAP=200 封顶,典型场景可控,待超长会话反馈再优化 — src/components/AiChat.vue:835-863
- [x] B-260615-54 ✅评估维持(2026-06-16) — `void convId` 参数丢弃R5 设计明确(分离窗口只读不切会话防落错),注释充分,签名稳定(onMounted 调用传参)。保留正确,不修 — src/composables/ai/useAiWindow.ts:83
> **统计**🔴2(P1 确定性 bug) 🟡3(P2 DRY/优化) ⚪3(P3 风格) + 1 评估维持。总体评级「良」:架构清晰、契约闭合、注释扎实,两处 🔴 低风险一行/小改可根治。详见本轮 /review 输出。