feat: enhance edit logging in subagent execution and streamline edit tracking in run_agent_turn

This commit is contained in:
asepharyana
2026-07-13 14:47:03 +07:00
parent 4a297669b4
commit c60fadb88a
2 changed files with 83 additions and 15 deletions
+16 -14
View File
@@ -965,8 +965,8 @@ fn run_agent_turn(
) -> anyhow::Result<()> {
const MAX_TODO_RETRIES: usize = 5;
let mut msgs = messages.to_vec();
let mut edits_this_turn = 0u32;
let mut edited_paths: Vec<String> = Vec::new();
let initial_edits = crate::model::editlog::EditLog::new(&tc.edit_log_session_dir).len();
let mut inline_reviews_count: usize = 0;
let mut prev_shaped = false;
@@ -1366,8 +1366,6 @@ fn run_agent_turn(
}
if is_edit {
edits_this_turn += 1;
// ── Auto-subagent orchestration ──
// Extract path from tool args for auto-review and
// background subagent tracking.
@@ -1488,24 +1486,28 @@ fn run_agent_turn(
}
}
if edits_this_turn > 0 {
let el = crate::model::editlog::EditLog::new(&tc.edit_log_session_dir);
let final_edits = el.len();
let total_edits_this_turn = final_edits.saturating_sub(initial_edits);
if total_edits_this_turn > 0 {
if let Ok(mut q) = events_q.lock() {
q.push_back(TurnEvent::SystemNote {
kind: "edits".to_string(),
message: edits_this_turn.to_string(),
message: total_edits_this_turn.to_string(),
});
}
// Collect edited paths from the new edit log entries
let mut bg_paths = Vec::new();
for entry in el.entries.iter().skip(initial_edits) {
bg_paths.push(entry.path.clone());
}
bg_paths.sort();
bg_paths.dedup();
// ── Background auto-subagents ──
// After a turn with edits, spawn deeper-analysis subagents in the
// background (test generation, architecture review, security review).
// These run asynchronously on OS threads and report via SystemNote
// events, so they do not block the main agent or TUI.
//
// Only spawn background agents if we actually accumulated paths
// (safety check — should always be true when edits_this_turn > 0).
if !edited_paths.is_empty() {
let bg_paths = edited_paths.clone();
if !bg_paths.is_empty() {
let bg_session_dir = tc.edit_log_session_dir.clone();
let bg_workspaces = tc.workspace_roots.clone();
let bg_events = events_q.clone();
+67 -1
View File
@@ -424,7 +424,73 @@ pub fn run_subagent(ctx: &SubagentContext, tx: &mpsc::Sender<SubagentEvent>) ->
}
let result = match tools_ref.iter().find(|t| t.name() == tool_name.as_str()) {
Some(tool) => tool.run(tool_ctx_ref, &args),
Some(tool) => {
let is_edit = tool_name == "write" || tool_name == "edit";
if is_edit && !tool_call.id.is_empty() {
if let Ok(conn) = crate::model::msglog::open_or_create(&ctx.session_dir) {
let path = args.get("path").and_then(|v| v.as_str()).unwrap_or("");
if let Ok(abs_path) = crate::tool::resolve_path(&tool_ctx_ref.workspaces, path) {
if let Ok(bytes) = std::fs::read(&abs_path) {
let session_id = ctx.session_dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("unknown");
let _ = crate::model::msglog::store_blob(
&conn, session_id, &tool_call.id, &bytes, None,
);
}
}
}
}
let run_res = tool.run(tool_ctx_ref, &args);
if is_edit && run_res.is_ok() {
let reason = args
.get("reason")
.and_then(|v| v.as_str())
.unwrap_or("unnamed");
let path = args
.get("path")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let content_sha256 = {
let content = args.get("content").or_else(|| args.get("new"));
use sha2::Digest;
let hash = sha2::Sha256::digest(
content.and_then(|v| v.as_str()).unwrap_or("").as_bytes(),
);
hex::encode(hash)
};
let bytes_delta = if tool_name == "write" {
args.get("content")
.and_then(|v| v.as_str())
.map_or(0, |s| s.len() as i64)
} else {
let old = args.get("old").and_then(|v| v.as_str()).unwrap_or("");
let new = args.get("new").and_then(|v| v.as_str()).unwrap_or("");
(new.len() as i64 - old.len() as i64).abs()
};
let session_id = ctx.session_dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("unknown")
.to_string();
let entry = crate::model::editlog::EditLogEntry {
ts: chrono::Utc::now().timestamp_millis(),
tool: tool_name.clone(),
path: path.to_string(),
reason: reason.to_string(),
content_sha256,
bytes_delta,
origin: tool_ctx_ref.origin.tag(),
session_id,
};
let mut el = crate::model::editlog::EditLog::new(&ctx.session_dir);
el.append(entry).ok();
}
run_res
}
None => Err(anyhow::anyhow!("tool '{tool_name}' not found")),
};
(tool_call, result)