新增: idea source 消息级溯源 + workspace_root 去固定根 + run_command 偏好修复
idea source 结构化(消息级溯源闭环 — 灵感来源 conv_msg:{id}):
- 新建 idea_source.rs(IDEA_SOURCE_AUTO_FILL_ENABLED + maybe_fill_idea_source 保守五条件:
开关/create_idea/source 空(不覆盖 AI 填)/message_id 非 None/result.id)
- audit/mod low_risk + chat ai_approve 双执行路径接补全(create_idea Medium 审批)
workspace_root 去固定根(F-260619-03 收尾):
- is_authorized 删 workspace_root 固定放行(代码硬编码),改走 persistent/session 白名单
- default_with_root 保留(默认初始 persistent,首次免授权行为不变)
- 用户从白名单删工程根后也需授权(动态白名单完整语义)
run_command 偏好修复(db 分析:591ff4a0 run_command 31/56=55% 严重偏好):
- P1 描述收紧(明确仅命令执行 + 负向引导:读取 read_file/编辑 patch_file/列目录 list_directory/搜索 search_files)
- P2 ToolDomain::Exec(run_command 独立 domain,仅 Debug 含 Exec,Code/File/Search 不含)
- P3 注册顺序(run_command 放 File 层最后)+ DEBUG_RULES 加命令执行关键词
自验: df-ai 228(intent Exec)+ devflow 169(idea source)+ workspace EXIT 0
This commit is contained in:
@@ -126,6 +126,12 @@ pub(super) use cache::{find_cached_high_risk_result, PENDING_APPROVAL_PLACEHOLDE
|
||||
mod data_change;
|
||||
pub(crate) use data_change::emit_data_changed;
|
||||
|
||||
// idea_source(audit/idea_source.rs):F-260619-04 P2(方案 B) 灵感来源消息级溯源补全。
|
||||
// create_idea 工具执行后,若 AI 未填 source 且有 message_id → 补 conv_msg:{id}(低侵入,不改 handler 接口)。
|
||||
// pub(crate) use 供本文件 process_tool_calls + chat.rs 审批执行路径调用(单点逻辑,多调用点)。
|
||||
mod idea_source;
|
||||
pub(crate) use idea_source::maybe_fill_idea_source;
|
||||
|
||||
/// F-260619-03 Phase B: 提取文件工具的路径参数(用于路径授权预校验)。
|
||||
///
|
||||
/// 仅对走 resolve_workspace_path 校验的文件工具返回路径;非文件工具返回空 Vec(不预校验)。
|
||||
@@ -488,7 +494,8 @@ pub(crate) async fn process_tool_calls(
|
||||
// push tool_result / audit 在 join_all 后串行回填(持锁,与 Med/High 占位拼接)。
|
||||
// join_all 保序——结果顺序 = low_risk 输入顺序 = tc_list 原始 index 顺序,不额外 sort
|
||||
if !low_risk.is_empty() {
|
||||
let results: Vec<(ToolCallDraft, Result<String, String>)> =
|
||||
// 携带 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 tools = tools_arc.clone();
|
||||
let app_clone = app_handle.clone();
|
||||
@@ -505,7 +512,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, Ok(val.to_string()))
|
||||
(draft, val.clone(), Ok(val.to_string()))
|
||||
}
|
||||
Err(e) => {
|
||||
// AR-6(定向B):Low 工具失败不 emit AiError。
|
||||
@@ -519,18 +526,25 @@ pub(crate) async fn process_tool_calls(
|
||||
result: serde_json::Value::String(err_msg.clone()),
|
||||
conversation_id: Some(conv_clone),
|
||||
});
|
||||
(draft, Err(err_msg))
|
||||
(draft, serde_json::Value::Null, Err(err_msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
})).await;
|
||||
|
||||
// 串行回填 tool_result + 审计(持 session 锁)
|
||||
for (draft, outcome) in results {
|
||||
for (draft, raw_result, outcome) in results {
|
||||
let (status, content) = match outcome {
|
||||
Ok(c) => ("completed", c),
|
||||
Err(c) => ("failed", c),
|
||||
};
|
||||
// F-260619-04 P2(方案 B): create_idea source 消息级溯源补全(仅 source 空 + 有 message_id)。
|
||||
// 注:低风险路径目前 create_idea 不会进(Medium→pending),此处为防御/未来若调级别覆盖。
|
||||
// 仅 Ok(completed) 时补(失败 result 无 idea_id 意义);args 从 draft.args 反解(JSON 原样)。
|
||||
if status == "completed" {
|
||||
let args_val = serde_json::from_str(&draft.args).unwrap_or(serde_json::Value::Null);
|
||||
maybe_fill_idea_source(db, &draft.name, &args_val, &raw_result, current_message_id).await;
|
||||
}
|
||||
// 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;
|
||||
|
||||
Reference in New Issue
Block a user