Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1e0dcae14 | ||
|
|
c60fadb88a |
@@ -1,3 +1,10 @@
|
|||||||
|
# [1.3.0](https://github.com/asepharyana/zesdex/compare/v1.2.0...v1.3.0) (2026-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* enhance edit logging in subagent execution and streamline edit tracking in run_agent_turn ([c60fadb](https://github.com/asepharyana/zesdex/commit/c60fadb88ae63788a5bbe3c3443e2ce826e5778f))
|
||||||
|
|
||||||
# [1.2.0](https://github.com/asepharyana/zesdex/compare/v1.1.0...v1.2.0) (2026-07-13)
|
# [1.2.0](https://github.com/asepharyana/zesdex/compare/v1.1.0...v1.2.0) (2026-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Generated
+1
-1
@@ -4436,7 +4436,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zesdex"
|
name = "zesdex"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "zesdex"
|
name = "zesdex"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["asepharyana <superaseph@gmail.com>"]
|
authors = ["asepharyana <superaseph@gmail.com>"]
|
||||||
|
|
||||||
|
|||||||
@@ -965,8 +965,8 @@ fn run_agent_turn(
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
const MAX_TODO_RETRIES: usize = 5;
|
const MAX_TODO_RETRIES: usize = 5;
|
||||||
let mut msgs = messages.to_vec();
|
let mut msgs = messages.to_vec();
|
||||||
let mut edits_this_turn = 0u32;
|
|
||||||
let mut edited_paths: Vec<String> = Vec::new();
|
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 inline_reviews_count: usize = 0;
|
||||||
let mut prev_shaped = false;
|
let mut prev_shaped = false;
|
||||||
|
|
||||||
@@ -1366,8 +1366,6 @@ fn run_agent_turn(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if is_edit {
|
if is_edit {
|
||||||
edits_this_turn += 1;
|
|
||||||
|
|
||||||
// ── Auto-subagent orchestration ──
|
// ── Auto-subagent orchestration ──
|
||||||
// Extract path from tool args for auto-review and
|
// Extract path from tool args for auto-review and
|
||||||
// background subagent tracking.
|
// 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() {
|
if let Ok(mut q) = events_q.lock() {
|
||||||
q.push_back(TurnEvent::SystemNote {
|
q.push_back(TurnEvent::SystemNote {
|
||||||
kind: "edits".to_string(),
|
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 ──
|
// ── Background auto-subagents ──
|
||||||
// After a turn with edits, spawn deeper-analysis subagents in the
|
if !bg_paths.is_empty() {
|
||||||
// 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();
|
|
||||||
let bg_session_dir = tc.edit_log_session_dir.clone();
|
let bg_session_dir = tc.edit_log_session_dir.clone();
|
||||||
let bg_workspaces = tc.workspace_roots.clone();
|
let bg_workspaces = tc.workspace_roots.clone();
|
||||||
let bg_events = events_q.clone();
|
let bg_events = events_q.clone();
|
||||||
|
|||||||
@@ -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()) {
|
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")),
|
None => Err(anyhow::anyhow!("tool '{tool_name}' not found")),
|
||||||
};
|
};
|
||||||
(tool_call, result)
|
(tool_call, result)
|
||||||
|
|||||||
Reference in New Issue
Block a user