diff --git a/src-tauri/src/commands/ai/audit/mod.rs b/src-tauri/src/commands/ai/audit/mod.rs index cf3d986..2fda4da 100644 --- a/src-tauri/src/commands/ai/audit/mod.rs +++ b/src-tauri/src/commands/ai/audit/mod.rs @@ -297,6 +297,21 @@ pub(crate) async fn process_tool_calls( } let drafts = resolved_drafts; + // 自动执行范围三档读取(前置到路径授权预校验之前:两处门控共用)。 + // KV df-ai-auto-execute-mode 取值 low/medium/all,默认 low; + // low 仅 Low 自动(等价旧行为),medium Low+Medium,all 全自动无审批(完全 AI 接管)。 + let auto_exec_mode = app_handle + .state::() + .settings + .get("df-ai-auto-execute-mode") + .await + .ok() + .flatten() + // 前端 appSettings.set 以 JSON.stringify 落库(字符串值带引号,"all" != all), + // 不归一则 medium/all 档静默失效全走审批(同 prompt.rs custom_prompt 的 trim 先例)。 + .map(|v| v.trim_matches('"').to_string()) + .unwrap_or_else(|| "low".to_string()); + // ── 文件工具路径授权预校验 ── // 在 RiskLevel 分类前,对文件工具(read/write/list/patch/info/append/delete/rename/search) // 逐条预校验路径授权(persistent + 会话 session_allowed_dirs + 黑名单): @@ -319,7 +334,16 @@ pub(crate) async fn process_tool_calls( // handler 闭包 read lock 与此处预校验读同一字段,两端授权判定一致。 match check_file_tool_auth(&draft.name, &args, &allowed_guard) { FileToolAuthOutcome::Authorized => authorized_drafts.push((idx, draft, args)), - FileToolAuthOutcome::NeedsAuth(req) => path_auth_pending.push((draft, args, req)), + FileToolAuthOutcome::NeedsAuth(req) => { + // all 档全自动(用户拍板):路径授权弹窗也不打断,与 RiskLevel 门同语义。 + // 黑名单 Denied 不受影响——系统敏感目录是绝对安全线,全接管也不放行。 + // 放行的 draft 走 authorized_drafts → RiskLevel 分类 → all 放行 → 审计留痕闭环。 + if auto_exec_mode == "all" { + authorized_drafts.push((idx, draft, args)); + } else { + path_auth_pending.push((draft, args, req)); + } + } FileToolAuthOutcome::Denied(reason) => path_denied.push((draft, reason)), } } @@ -460,16 +484,6 @@ pub(crate) async fn process_tool_calls( // 原 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 接管)。 - let auto_exec_mode = app_handle - .state::() - .settings - .get("df-ai-auto-execute-mode") - .await - .ok() - .flatten() - .unwrap_or_else(|| "low".to_string()); // F-#97 诊断:用户报「选 all 仍审批」时辅助定位 KV 是否真落库。 // df-ai-trace-approve 开关控制(默认开)。常见误以为已生效但实际未点确认按钮致 KV 未落。 let trace_on = app_handle @@ -479,7 +493,7 @@ pub(crate) async fn process_tool_calls( .await .ok() .flatten() - .map(|v| v == "true" || v == "1") + .map(|v| { let v = v.trim_matches('"'); v == "true" || v == "1" }) .unwrap_or(true); if trace_on { tracing::info!( diff --git a/src-tauri/src/commands/settings.rs b/src-tauri/src/commands/settings.rs index f00409e..a02517b 100644 --- a/src-tauri/src/commands/settings.rs +++ b/src-tauri/src/commands/settings.rs @@ -121,8 +121,9 @@ pub async fn set_script_safety( pub async fn get_script_safety( state: State<'_, AppState>, ) -> Result<(String, String), String> { - let wl = state.settings.get("df-script-whitelist").await.map_err(err_str)?.unwrap_or_default(); - let bl = state.settings.get("df-script-blacklist").await.map_err(err_str)?.unwrap_or_default(); + // appSettings.set(JSON.stringify 带引号)与 settingsApi.set(裸串)双通道写入并存,读端归一 + let wl = state.settings.get("df-script-whitelist").await.map_err(err_str)?.map(|v| v.trim_matches('"').to_string()).unwrap_or_default(); + let bl = state.settings.get("df-script-blacklist").await.map_err(err_str)?.map(|v| v.trim_matches('"').to_string()).unwrap_or_default(); Ok((wl, bl)) }