优化: UI/UX遗留批(审计/AI命令层/前端组件,会话前基线收尾)

- AuditLog +298(筛选/详情/i18n)+ audit 后端 record/mod

- AI 命令层:generate_image +81 / fetch_url / fetch_search / skills / tool_registry / tools/file / provider / conversation

- 前端组件:AiChat/TopBar/ConversationSidebar/GitChanges/ApprovalPopup/Dashboard/ProjectDetail 等 30+ + composables + i18n

- 诊断文档: aichat历史会话实证诊断-2026-08-04 + project_soft_delete 测试
This commit is contained in:
lxy
2026-08-05 22:15:34 +08:00
parent 28de5d6143
commit e68d283651
49 changed files with 1257 additions and 138 deletions
+38
View File
@@ -12,6 +12,7 @@ use df_storage::crud::AiToolExecutionRepo;
use df_storage::db::Database;
use super::{AiChatEvent, AiSession, ApprovalKind, PathAuthRequest, PendingApproval, ToolCallDraft};
use super::tools::entity_resolve;
// utils(audit/utils.rs):RiskLevel ↔ 字符串转换 + 字符串安全截断纯 helper。
// 第五批从本文件抽离,行为零变更。pub(crate) use 保持 finalize / restore 子模块
@@ -256,6 +257,43 @@ pub(crate) async fn process_tool_calls(
})
.collect();
// ── 实体参数 name→id 解析(机制层,治 list_tasks(project_id="moyu") 空返回)──
// resolve_entity_ids 只改 args / 返 Err;失败走结构化 failed envelope(镜像 path_denied),
// 不执行,让 LLM 拿到可行动错误自修。单点漏斗:auto + 审批 + 目录授权全部执行路径统一拿到已解析 id。
let mut resolved_drafts: Vec<(u32, ToolCallDraft, serde_json::Value)> = Vec::with_capacity(drafts.len());
for (idx, draft, args) in drafts {
match entity_resolve::resolve_entity_ids(db, &draft.name, &args).await {
Ok(resolved) => resolved_drafts.push((idx, draft, resolved)),
Err(e) => {
let err_payload = serde_json::json!({
"status": "failed",
"error": e.to_string(),
}).to_string();
// 短 lock 段:push tool_result(纯写,无 await)
{
let __lock_t = std::time::Instant::now();
let mut session = session_arc.lock().await;
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, &err_payload));
let __hold = __lock_t.elapsed();
if __hold > std::time::Duration::from_millis(30) {
eprintln!("[LOCK-SLOW] process_tool_calls:268 持锁 {:?} (含 lock 等待)", __hold);
}
}
let ev = AiChatEvent::AiToolCallCompleted {
id: draft.id.clone(),
result: serde_json::Value::String(err_payload.clone()),
conversation_id: Some(conv_id.to_string()),
};
let _ = app_handle.emit("ai-chat-event", ev.clone());
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
let risk = tools_arc.get(&draft.name).map(|t| t.risk_level).unwrap_or(RiskLevel::High);
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args,
"failed", risk, Some(err_payload.clone()), Some("auto_resolve_fail"), current_message_id).await;
}
}
}
let drafts = resolved_drafts;
// ── 文件工具路径授权预校验 ──
// 在 RiskLevel 分类前,对文件工具(read/write/list/patch/info/append/delete/rename/search
// 逐条预校验路径授权(persistent + 会话 session_allowed_dirs + 黑名单):