From 065229b9da5945218f61882edbe1306b189e74a5 Mon Sep 17 00:00:00 2001 From: wunianxiao Date: Tue, 2 Jun 2026 15:30:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=85=88=E5=AE=A1=E6=89=B9=20+?= =?UTF-8?q?=20=E9=A6=96=E7=94=A8=E6=88=B7=E9=9A=94=E7=A6=BB=20+=20?= =?UTF-8?q?=E5=AE=A1=E6=89=B9DB=E9=97=AD=E7=8E=AF=20+=20memo=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. (High) 内置工具先审批再执行 — is_builtin 检查优先, High 风险通过 builtin_approve 后再调用 BuiltinRegistry 2. (High) 首用户 current_user_id — 移除 !is_empty() 守卫 3. (Medium) 审批 DB 状态闭环 — 取消/错误时按 user_id 更新最近 pending 4. (Medium) manage_memos 写入失败 — .ok() → 完整错误返回 --- src/main.rs | 14 ++++++++------ src/tools/approval.rs | 4 ++-- src/tools/builtin.rs | 12 ++++++++++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index a3e8bf8..dbe52bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -371,20 +371,22 @@ async fn cmd_listen( cp.get("name").and_then(|v| v.as_str()).map(|s| s.to_string()).unwrap_or_default() } else { n.clone() }; - // 内置工具 - if let Some(mut result) = tools::builtin::BuiltinRegistry::execute(&target_name, &args).await { + // 内置工具(高风险先审批再执行) + if tools::builtin::BuiltinRegistry::is_builtin(&target_name) { if tools::builtin::BuiltinRegistry::is_high_risk(&target_name) { let mut ctx = ExecutionContext::new(&user_id); ctx = ctx.with_approval(approval.clone()); - ctx = ctx.with_wechat(send); + ctx = ctx.with_wechat(send.clone()); let approved = builtin_approve(&ctx, &target_name).await; match approved { - Ok(true) => return Ok(result.output), + Ok(true) => {}, Ok(false) => return Ok(SkillResult::rejected(&target_name).output), Err(e) => return Ok(e), } } - return Ok(result.output); + if let Some(result) = tools::builtin::BuiltinRegistry::execute(&target_name, &args).await { + return Ok(result.output); + } } // 回退到外部 SkillRegistry @@ -539,7 +541,7 @@ async fn listen_loop( // 用户切换:清空上下文,加载新用户历史 { let mut last_user = last_user_id.lock().await; - if *last_user != from.to_string() && !last_user.is_empty() { + if *last_user != *from { info!("用户切换: {} → {}", *last_user, from); if let Some(conv) = &conversation { let session = conv.session(); diff --git a/src/tools/approval.rs b/src/tools/approval.rs index 48c5b63..d0dfb5d 100644 --- a/src/tools/approval.rs +++ b/src/tools/approval.rs @@ -126,12 +126,12 @@ impl ApprovalManager { ApprovalDecision::Approved => "approved", _ => "rejected", }; + // 按 user_id + 最近 pending 记录更新 let _ = sqlx::query( - "UPDATE pending_approvals SET status = $1, consumed_at = NOW() WHERE user_id = $2 AND code_hash = $3 AND status = 'pending'", + "UPDATE pending_approvals SET status = $1, consumed_at = NOW() WHERE user_id = $2 AND status = 'pending' ORDER BY created_at DESC LIMIT 1", ) .bind(status) .bind(user_id) - .bind(&format!("{:x}", Sha256::digest(reply.as_bytes()))) .execute(pool.as_ref()) .await; } diff --git a/src/tools/builtin.rs b/src/tools/builtin.rs index 232aa27..c7e6deb 100644 --- a/src/tools/builtin.rs +++ b/src/tools/builtin.rs @@ -36,6 +36,10 @@ impl BuiltinRegistry { pub fn is_high_risk(name: &str) -> bool { Self::specs().iter().any(|s| s.name == name && s.risk_level == RiskLevel::High) } + + pub fn is_builtin(name: &str) -> bool { + Self::specs().iter().any(|s| s.name == name) + } } fn execute_datetime() -> SkillResult { @@ -64,7 +68,9 @@ async fn handle_memos(args_json: &str) -> SkillResult { "id": next_id, "content": content, "created_at": Local::now().format("%Y-%m-%d %H:%M").to_string() })); - std::fs::write(path, serde_json::to_string_pretty(&data).unwrap()).ok(); + if let Err(e) = std::fs::write(path, serde_json::to_string_pretty(&data).unwrap()) { + return SkillResult::error(format!("写入备忘录失败: {}", e)); + } SkillResult::ok(format!("已添加备忘录 #{}", next_id)) } "list" => { @@ -80,7 +86,9 @@ async fn handle_memos(args_json: &str) -> SkillResult { let before = data.len(); data.retain(|m| m.get("id").and_then(|v| v.as_i64()) != Some(id)); if data.len() == before { return SkillResult::error(format!("未找到备忘录 #{}", id)); } - std::fs::write(path, serde_json::to_string_pretty(&data).unwrap()).ok(); + if let Err(e) = std::fs::write(path, serde_json::to_string_pretty(&data).unwrap()) { + return SkillResult::error(format!("写入备忘录失败: {}", e)); + } SkillResult::ok(format!("已删除备忘录 #{}", id)) } _ => SkillResult::error("未知操作,支持: add, list, delete"),