优化: 设置页UX与AI自动执行三档
This commit is contained in:
@@ -427,17 +427,43 @@ pub(crate) async fn process_tool_calls(
|
||||
// 若 LLM 重试同命令(同 tool_name + 同 args,键序无关),命中已落定的旧 tool_result,
|
||||
// 把缓存结果作为新 tool_call_id 的 tool_result 回传 LLM,跳过 insert pending + 跳过审批,
|
||||
// 断「超时→重试→重新审批」循环。Med 不去重(去重易误伤),Low 无审批本就不进此分支。
|
||||
let mut low_risk: Vec<(ToolCallDraft, serde_json::Value)> = Vec::new();
|
||||
// F-#97:low_risk 向量携带 risk_level(治 securityReview blocker :710 审计失真)。
|
||||
// 原 Vec 仅 (draft,args),回填审计硬编码 RiskLevel::Low;#97 让 Med/High(all/medium 模式)
|
||||
// 也进此向量,需透传真实等级,审计方可追溯「all 模式执行了多少高危命令」。
|
||||
let mut low_risk: Vec<(ToolCallDraft, serde_json::Value, RiskLevel)> = Vec::new();
|
||||
// F-#97 自动执行范围三档(2026-06-22):KV df-ai-auto-execute-mode 取值 low/medium/all,默认 low(=现状)。
|
||||
// low:仅 Low 自动(等价旧行为);medium:Low+Medium 自动;all:全自动无审批(完全 AI 接管)。
|
||||
// 锁内 await KV 快读,不竞争 session 锁(settings 独立 repo)。
|
||||
let auto_exec_mode = app_handle
|
||||
.state::<crate::state::AppState>()
|
||||
.settings
|
||||
.get("df-ai-auto-execute-mode")
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_else(|| "low".to_string());
|
||||
// trust_hits 收集:AE-04 会话信任命中(同会话已批准同类操作),真实执行但移到锁外 spawn
|
||||
// (对齐 Low risk L673-720 不持锁模式)。命中时此处只 emit toast + 收集,不 .await execute。
|
||||
// 元组携带 risk_level: 回填审计需原等级(Med/High 才进此分支),避免闭包内再查 registry
|
||||
let mut trust_hits: Vec<(ToolCallDraft, serde_json::Value, String, RiskLevel)> = Vec::new();
|
||||
for (_, draft, args) in drafts {
|
||||
let risk_level = tools_arc.get(&draft.name).map(|t| t.risk_level).unwrap_or(RiskLevel::High);
|
||||
match risk_level {
|
||||
RiskLevel::Low => low_risk.push((draft, args)),
|
||||
RiskLevel::Medium | RiskLevel::High => {
|
||||
// AE-2025-04 会话级信任(Session Trust):首批 write_file / run_command,
|
||||
// F-#97:按 auto_exec_mode 判定是否自动执行。should_auto=true 走 low_risk(自动),false 走原审批块。
|
||||
// low(默认):Low→auto, Med/High→审批(等价现状)
|
||||
// medium:Low/Med→auto, High→审批
|
||||
// all:全 auto(完全接管,无审批)
|
||||
let should_auto = match risk_level {
|
||||
RiskLevel::Low => true,
|
||||
RiskLevel::Medium => auto_exec_mode == "medium" || auto_exec_mode == "all",
|
||||
RiskLevel::High => auto_exec_mode == "all",
|
||||
};
|
||||
if should_auto {
|
||||
low_risk.push((draft, args, risk_level));
|
||||
} else {
|
||||
// 原 Medium|High 审批分支(trust_key 命中/dedup 缓存/retry guard/pending insert + emit + audit_tool_call)
|
||||
// 整体保留,逻辑不变。trust_hits 元组携带 risk_level 语义不变:Med 进此处=Medium,
|
||||
// High 进此处(仅 low/medium 模式)时=High。
|
||||
// AE-2025-04 会话级信任(Session Trust):首批 write_file / run_command,
|
||||
// 同会话已批准过同工具+同目录 → TrustKey 命中 → 自动放行(跳过 pending + 二次确认)。
|
||||
// 命中后走与 F-05 去重命中相似的「直接执行 + Completed + 审计 decided_by=auto_trust」路径,
|
||||
// 但与 F-05 不同:F-05 复用缓存 tool_result 跳过执行;trust 放行**真实执行工具**
|
||||
@@ -559,7 +585,6 @@ pub(crate) async fn process_tool_calls(
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args, "pending", risk_level, None, None, current_message_id).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,8 +652,8 @@ pub(crate) async fn process_tool_calls(
|
||||
// join_all 保序——结果顺序 = low_risk 输入顺序 = tc_list 原始 index 顺序,不额外 sort
|
||||
if !low_risk.is_empty() {
|
||||
// 携带 args + 原始 JSON result(create_idea source 补全需解析 result.id + args.source)。
|
||||
let results: Vec<(ToolCallDraft, serde_json::Value, Result<String, String>)> =
|
||||
futures::future::join_all(low_risk.into_iter().map(|(draft, args)| {
|
||||
let results: Vec<(ToolCallDraft, serde_json::Value, RiskLevel, Result<String, String>)> =
|
||||
futures::future::join_all(low_risk.into_iter().map(|(draft, args, risk_level)| {
|
||||
let tools = tools_arc.clone();
|
||||
let app_clone = app_handle.clone();
|
||||
let conv_clone = conv_id.to_string();
|
||||
@@ -647,7 +672,7 @@ pub(crate) async fn process_tool_calls(
|
||||
// AR-11(方案A):数据变更工具执行成功后 emit df-data-changed,
|
||||
// 前端 store listen 刷新列表(仅命中映射的工具 emit,见 emit_data_changed)。
|
||||
emit_data_changed(&app_clone, &draft.name);
|
||||
(draft, val.clone(), Ok(val.to_string()))
|
||||
(draft, val.clone(), risk_level, Ok(val.to_string()))
|
||||
}
|
||||
Err(e) => {
|
||||
// AR-6(定向B):Low 工具失败不 emit AiError。
|
||||
@@ -664,14 +689,14 @@ pub(crate) async fn process_tool_calls(
|
||||
};
|
||||
let _ = app_clone.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_clone.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
(draft, serde_json::Value::Null, Err(err_msg))
|
||||
(draft, serde_json::Value::Null, risk_level, Err(err_msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
})).await;
|
||||
|
||||
// 串行回填 tool_result + 审计(持 session 锁)
|
||||
for (draft, raw_result, outcome) in results {
|
||||
for (draft, raw_result, risk_level, outcome) in results {
|
||||
let (status, content) = match outcome {
|
||||
Ok(c) => ("completed", c),
|
||||
Err(c) => ("failed", c),
|
||||
@@ -685,7 +710,15 @@ pub(crate) async fn process_tool_calls(
|
||||
}
|
||||
// F-260616-09 B 批2:写 per_conv.messages。
|
||||
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, content.clone()));
|
||||
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args, status, RiskLevel::Low, Some(content), Some("auto"), current_message_id).await;
|
||||
// F-#97 审计留痕:low_risk 向量中 risk_level != Low 即 mode 放行(Med 仅 medium/all、High 仅 all 才进)。
|
||||
// decided_by 区分接管来源,审计表可追溯 all 模式执行了多少高危命令(治 securityReview blocker:
|
||||
// 原硬编码 RiskLevel::Low 致 Med/High 接管操作全标 Low/auto 失真,等于没记)。
|
||||
let decided_by: &str = match risk_level {
|
||||
RiskLevel::Low => "auto",
|
||||
RiskLevel::Medium => "auto_takeover_medium",
|
||||
RiskLevel::High => "auto_takeover_all",
|
||||
};
|
||||
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args, status, risk_level, Some(content), Some(decided_by), current_message_id).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user