优化: 界面交互完善与设置体验

- 审批卡尊重滚动意图,等长切换回底,排队超时按钮修活
- 带图编辑提示不静默丢,删除目标回写,切换文案消歧
- 会话键盘可达+搜索态重命名,筛选计数口径对齐
- 子任务提示正名,看板加载态
- 主题跟随系统,表单脏态拦截,密钥显隐留空提示
This commit is contained in:
lxy
2026-10-03 16:27:48 +08:00
parent 31f7a9570b
commit 36bc485112
47 changed files with 1201 additions and 12 deletions
@@ -0,0 +1,81 @@
# S2 子代理审批语义勘察(只读,2026-10-03)
> 结论先行:任务前提不成立。sub_agent.rs grep approval 零命中≠语义空白——审批以
> 「无通道→一律拒绝(fail-closed)」实现,命名是 deny 非 approval。**非安全债**。
## 1. ToolExecPolicy::SubAgent 分流实况
`policy` 入参 → `is_sub_agent = matches!(policy, ToolExecPolicy::SubAgent)`
(src-tauri/src/commands/ai/audit/mod.rs:474-477;策略文档 mod.rs:100-112)
| 路径 | SubAgent 行为 | 证据 |
|---|---|---|
| spawn 入口白名单 | 非 Low 工具根本不进子 tool_defs,恒剔 spawn_agent | sub_agent.rs:210-251 |
| 路径授权 NeedsAuth | 就地拒绝(sub_agent_path_denied) | mod.rs:618-622 |
| 路径黑名单 Denied | 就地拒绝(同上收集) | mod.rs:633-636, 676-682 |
| Medium/High 风险 | 就地拒绝,**在 auto_exec_mode 判定之前,all 档也不放行** | mod.rs:795-803 |
| Low + 已授权 | 正常并行执行(与主会话同路径,含缓存/去重/信任) | mod.rs:774, 972+ |
拒绝形态 = `deny_sub_agent_tool` 单点(mod.rs:117-148):结构化 envelope
`{status:rejected, reason:sub_agent_denied}` + push tool_result + emit
AiToolCallCompleted + 审计 decided_by=sub_agent_denied。
**挂起对子会话实际不能工作**:SubAgent 策略下 pending 恒 0(不 insert);
子循环断言 pending>0 即 Failed 终止(sub_agent.rs:577-587)。即便 insert 也无
人消费——try_continue_agent_loop 是主 loop 恢复器(agentic/mod.rs:1888),
子 loop 是 spawn_agent handler 内同步 for-round 循环,且子会话跑完即从
per_conv 移除(sub_agent.rs:348),无任何恢复入口。
## 2. 若复用主会话审批链路到子会话:缺口清单
- 事件路由:AiApprovalRequired 带 conversation_id,前端 per-conv 幂等收集
(useAiToolEvents.ts:60-87)本可收;但子会话不在侧栏(mod.rs:235 注释
「挂父不挂子」),审批卡挂 child conv 下无处展示。
- 批准续跑:ai_approve 批准后调 try_continue_agent_loop(conv_id)
(commands/ai/commands/chat.rs:1051)恢复主 loop;子 loop 无对应机制——
deadline/tokens/fail_window/stall 计数全在 run_sub_agent_loop_inner 栈上,
挂起即栈销毁,resume 需整套状态快照/重建。
- 拒绝终止:主会话拒绝=replace tool_result+emit AiApprovalResult
(chat.rs:1004-1034);子会话侧缺 ① 不 emit AiAgentFinished 终态卡
(sub_agent.rs:320 仅单一出口处发)② 不落库子轨迹(:342)③ 不清
per_conv 条目(:348)④ payload 固定 user_denied 无子代理语义。
- 超时:arm_approval_timeout_timers 扫 pending 表可覆盖子条目
(mod.rs:826),但超时收尾不含子循环 truncated 终态,会留孤儿。
## 3. 现状语义定性
**默认拒绝(fail-closed)**:入口钳制 + 运行期就地处决双层,零挂起零放行,
Med/High 连 all 档都拒。风险评级:**非安全债**;真正可议是产品语义债——
子代理遇需审批操作只能拒绝并提示转交主会话(envelope 文案 mod.rs:130),
无「上报父会话审批」通道。
## 4. S2 最小方案骨架
**方案A(推荐,颗粒=单代理一轮)**:维持默认拒绝,增强转交闭环。
- audit/mod.rs:deny envelope 增结构化字段 tool/args/reason(现仅 message 文案)。
- sub_agent.rs:报告协议(system_prompt 段)要求列「被拒操作清单」→ 父会话
收报告后由主 loop 决定是否发起审批。
- 前端无需改。
**方案B(真审批通道,超单轮,拆 3 批)**:
1. 挂起路径:SubAgent 下 Med/High 不 deny 改 insert pending(kind 新增
SubAgentRisk{parent_conv_id})+ emit AiApprovalRequired(conversation_id
填父 conv 复用现有弹窗);子循环 pending>0 改返新状态 AwaitingApproval,
栈上状态快照存 per_conv 新字段(conv_state.rs),不删条目。
2. resume 通道:ai_approve 识别 kind==SubAgentRisk → 批准则调新函数
resume_sub_agent_loop(从快照重建上下文续 for-round);拒绝则替换
tool_result+emit AiAgentFinished(truncated)+落库+清条目。
3. 收尾:approval_timeout 超时终态补子循环 truncated 收尾;前端 agent 卡
消费 AiApprovalResult。
- 涉及文件:audit/mod.rs、agentic/sub_agent.rs、commands/ai/commands/chat.rs、
agentic/conv_state.rs、src/composables/ai/useAiToolEvents.ts、src/api/types.ts。
- 拆批理由:子循环暂停/恢复状态快照(deadline/tokens/fail_window/stall 等
7+ 项)是硬骨头,与事件路由、前端消费耦合度低,可独立验收。
## 关键文件索引
- E:\wk-lab\devflow\src-tauri\src\commands\ai\audit\mod.rs(策略分流+deny 单点)
- E:\wk-lab\devflow\src-tauri\src\commands\ai\agentic\sub_agent.rs(白名单钳制+子循环+单一出口)
- E:\wk-lab\devflow\src-tauri\src\commands\ai\commands\chat.rs(ai_approve/ai_authorize_dir)
- E:\wk-lab\devflow\src-tauri\src\commands\ai\agentic\helpers.rs(try_continue_agent_loop)
- E:\wk-lab\devflow\src\composables\ai\useAiToolEvents.ts(前端审批事件消费)