重构: F-09批2 调用方迁移per-conv(双写桥接共存期)

- agentic.rs: GeneratingGuard加conv_id + 三处退出校验改conv存在性(决策e) + stop_flag/notify/messages/iteration per-conv + try_continue加conv_id参数
- commands.rs: IPC写路径双写桥接(per_conv新真相源+顶层双写,批4删) + ai_is_generating读per_conv fallback顶层
- audit/conversation/title/knowledge_inject/lib.rs: per_conv读写迁移
- conv()/conv_read()去allow(批2有调用方)
共存期双写: per_conv主+顶层双写兼容批4前IPC,批4签名改后删顶层
主代兜底: cargo check --workspace 0 + test 96 passed + grep三处退出/GeneratingGuard/双写印证
This commit is contained in:
2026-06-19 02:01:06 +08:00
parent 2c8764abee
commit 2a4d745c5b
9 changed files with 578 additions and 202 deletions

View File

@@ -444,8 +444,12 @@ pub(crate) fn emit_data_changed(app_handle: &AppHandle, tool_name: &str) {
/// - 返回 None 表示无缓存命中(走原审批流程)。
///
/// `session` 只读扫描 messages不写调用方据返回值决定是否跳过 insert pending。
///
/// F-260616-09 B 批2:经 `conv_id` 索引 per_conv.messages(顶层 messages 批2 后是死字段)。
/// conv_id 来源:process_tool_calls 入参 → 由 agentic.rs run_agentic_loop 入参透传。
async fn find_cached_high_risk_result(
session: &AiSession,
conv_id: &str,
audit_repo: &AiToolExecutionRepo,
tool_name: &str,
args: &serde_json::Value,
@@ -455,9 +459,12 @@ async fn find_cached_high_risk_result(
// 规范化新调用的 args 为可比字符串(排序键,键序无关)
let new_args_key = canonical_args_key(args);
// F-260616-09 B 批2:读 per_conv.messages。process_tool_calls 调用前 loop 入口已桥接建立 per_conv,
// 故 conv_read 必命中;防御性 None 时返 None(无缓存命中,走原审批流程)。
let conv = session.conv_read(conv_id)?;
// ContextManager::iter 返回 impl Iterator非 DoubleEndedcollect 成 Vec 再反向遍历。
// 单对话消息量小百级collect 开销可忽略。
let msgs: Vec<&ChatMessage> = session.messages.iter().collect();
let msgs: Vec<&ChatMessage> = conv.messages.iter().collect();
// 1) 反向扫描 assistant tool_calls找最近一条同名同参的 High 工具调用 → 拿到旧 tool_call_id
// 反向:循环是「最近一次超时→重试」,命中通常是末尾附近,反向先停省全扫。
@@ -629,7 +636,8 @@ pub(crate) async fn process_tool_calls(
// (用户信任同目录同类操作,但仍要看每次的真实结果)。
let trust_hit = super::trust_key_for(&draft.name, &args)
.and_then(|key| {
if session.session_trust.contains(&key) {
// F-260616-09 B 批2:读 per_conv.session_trust(conv_id 来源:本函数入参)。
if session.conv_read(conv_id).map(|c| c.session_trust.contains(&key)).unwrap_or(false) {
Some(key)
} else {
None
@@ -660,14 +668,15 @@ pub(crate) async fn process_tool_calls(
}
// F-05仅 High 查去重缓存Med 保持原审批流程
if matches!(risk_level, RiskLevel::High) {
if let Some((cached, status)) = find_cached_high_risk_result(session, &audit_repo, &draft.name, &args).await {
if let Some((cached, status)) = find_cached_high_risk_result(session, conv_id, &audit_repo, &draft.name, &args).await {
// 命中:把缓存结果作为新 tool_call_id 的 tool_result 回传,跳过审批
tracing::info!(
tool = %draft.name,
new_tool_call_id = %draft.id,
"[F-05] 高危工具去重命中:LLM 重试同命令,复用缓存结果跳过审批(断循环)"
);
session.messages.push(ChatMessage::tool_result(&draft.id, &cached));
// F-260616-09 B 批2:写 per_conv.messages(conv_id 来源:本函数入参)。
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, &cached));
let _ = app_handle.emit("ai-chat-event", AiChatEvent::AiToolCallCompleted {
id: draft.id.clone(),
result: serde_json::Value::String(cached.clone()),
@@ -696,7 +705,7 @@ pub(crate) async fn process_tool_calls(
recovered: false,
diff: approval_diff.clone(),
});
session.messages.push(ChatMessage::tool_result(&draft.id, PENDING_APPROVAL_PLACEHOLDER));
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, PENDING_APPROVAL_PLACEHOLDER));
let reason = build_approval_reason(&draft.name, &args, risk_level, db).await;
let _ = app_handle.emit("ai-chat-event", AiChatEvent::AiApprovalRequired {
id: draft.id.clone(),
@@ -757,7 +766,8 @@ pub(crate) async fn process_tool_calls(
Ok(c) => ("completed", c),
Err(c) => ("failed", c),
};
session.messages.push(ChatMessage::tool_result(&draft.id, content.clone()));
// F-260616-09 B 批2:写 per_conv.messages。
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, content.clone()));
// 审计:trust 放行仍记一条(decided_by=auto_trust),留痕可追溯
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args, status, risk_level, Some(content), Some("auto_trust")).await;
}
@@ -810,7 +820,8 @@ pub(crate) async fn process_tool_calls(
Ok(c) => ("completed", c),
Err(c) => ("failed", c),
};
session.messages.push(ChatMessage::tool_result(&draft.id, content.clone()));
// 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")).await;
}
}