feat: update README and documentation for new tools and features

- Updated README.md to reflect the addition of 3 new built-in tools, bringing the total to 37.
- Revised architecture documentation to indicate the increase in tool count.
- Enhanced backend documentation with updated line counts for various modules.
- Modified data documentation to change edit log format from JSON to JSONL.
- Updated dependencies documentation to reflect version upgrades for several crates.
- Improved prompts for auto-reviewer, division implementer, planner, tester, and quality reviewer to enforce stricter coding standards regarding linter bypasses.
- Refactored code in various modules to improve clarity and performance, including updates to error handling and tool execution logic.
- Added comprehensive tests for IPC frame serialization and deserialization.
This commit is contained in:
asepharyana
2026-07-13 14:39:39 +07:00
parent 00e29139c5
commit 1d50b94eec
23 changed files with 327 additions and 176 deletions
+57 -44
View File
@@ -1017,8 +1017,7 @@ fn run_agent_turn(
q.push_back(TurnEvent::SystemNote {
kind: "pipeline".to_string(),
message: format!(
"CEO is planning workflow (mode={})...",
mode_label,
"CEO is planning workflow (mode={mode_label})...",
),
});
}
@@ -1054,12 +1053,11 @@ fn run_agent_turn(
);
let user_msg = ChatMessage::user(format!(
"Design a structured multi-agent workflow plan for the following task:\n\n\
\"{}\"\n\n\
You must output a JSON object representing the 'specialists' configuration for {}.\n\
\"{user_request}\"\n\n\
You must output a JSON object representing the 'specialists' configuration for {required_divisions}.\n\
Each division must have a list of custom specialists defined by a pair of [label, focus_description].\n\n\
Return ONLY a JSON object with this exact structure, with no markdown codeblocks and no explanation:\n\
{}",
user_request, required_divisions, example_json
{example_json}"
));
let planner_result = tc.client.chat_with_tools_non_streaming(&[system_msg, user_msg], None);
@@ -1070,7 +1068,7 @@ fn run_agent_turn(
let mut lines = reply_text.lines();
lines.next();
let mut content = lines.collect::<Vec<&str>>();
if content.last().map(|s| s.trim() == "```").unwrap_or(false) {
if content.last().is_some_and(|s| s.trim() == "```") {
content.pop();
}
content.join("\n")
@@ -1103,7 +1101,7 @@ fn run_agent_turn(
&tc.workspace_roots,
Some(events_q),
&pipeline_abort,
custom_specialists,
&custom_specialists,
)
} else {
crate::app::workflow::company::run_company_pipeline_quick(
@@ -1112,14 +1110,14 @@ fn run_agent_turn(
&tc.workspace_roots,
Some(events_q),
&pipeline_abort,
custom_specialists,
&custom_specialists,
)
}
}
Err(e) => Err(anyhow::anyhow!("Failed to parse LLM planning JSON: {}. Cleaned JSON was: {}", e, clean_json)),
Err(e) => Err(anyhow::anyhow!("Failed to parse LLM planning JSON: {e}. Cleaned JSON was: {clean_json}")),
}
}
Err(e) => Err(anyhow::anyhow!("Failed to query LLM for planning workflow: {}", e)),
Err(e) => Err(anyhow::anyhow!("Failed to query LLM for planning workflow: {e}")),
};
match pipeline_result {
@@ -1304,44 +1302,60 @@ fn run_agent_turn(
let tool_calls = response.tool_calls.clone().unwrap_or_default();
archive_message(tc.db.as_ref(), &tc.session_id, &response);
msgs.push(response);
for tool_call in tool_calls {
let mut results_vec = Vec::new();
std::thread::scope(|s| {
let mut handles = Vec::new();
let tc_ref = tc;
for tool_call in &tool_calls {
let handle = s.spawn(move || {
let tool_name = tool_call.function.name.clone();
let args = crate::dto::chat::tool::sanitize_tool_arguments(
&tool_call.function.arguments,
);
let ws_roots: Vec<&std::path::Path> =
tc_ref.workspace_roots.iter().map(std::path::PathBuf::as_path).collect();
let verdict = crate::app::harness::Harness::gate_tool_call(
&tool_name,
&args,
&ws_roots,
);
let is_edit_tool = tool_name == "write" || tool_name == "edit";
let (output, is_error, is_edit) = match verdict {
Verdict::Allow => match execute_one_tool(
&tc_ref.tools,
&tc_ref.ctx,
&tool_name,
&tool_call.id,
&args,
&tc_ref.edit_log_session_dir,
&tc_ref.session_id,
tc_ref.db.as_ref(),
) {
Ok(result) => (result, false, is_edit_tool),
Err(e) => (e.to_string(), true, false),
},
Verdict::Block(reason) => (format!("Blocked: {reason}"), true, false),
};
(tool_call, tool_name, args, output, is_error, is_edit)
});
handles.push(handle);
}
for h in handles {
if let Ok(res) = h.join() {
results_vec.push(res);
}
}
});
for (tool_call, tool_name, args, output, is_error, is_edit) in results_vec {
if tc.abort_flag.load(std::sync::atomic::Ordering::SeqCst) {
if let Ok(mut q) = events_q.lock() {
q.push_back(TurnEvent::Error("Turn aborted by user".to_string()));
}
return Ok(());
}
let tool_name = tool_call.function.name.clone();
let args = crate::dto::chat::tool::sanitize_tool_arguments(
&tool_call.function.arguments,
);
let ws_roots: Vec<&std::path::Path> =
tc.workspace_roots.iter().map(std::path::PathBuf::as_path).collect();
let verdict = crate::app::harness::Harness::gate_tool_call(
&tool_name,
&args,
&ws_roots,
);
let is_edit_tool = tool_name == "write" || tool_name == "edit";
let (output, is_error, is_edit) = match verdict {
Verdict::Allow => match execute_one_tool(
&tc.tools,
&tc.ctx,
&tool_name,
&tool_call.id,
&args,
&tc.edit_log_session_dir,
&tc.session_id,
tc.db.as_ref(),
) {
Ok(result) => (result, false, is_edit_tool),
Err(e) => (e.to_string(), true, false),
},
Verdict::Block(reason) => (format!("Blocked: {reason}"), true, false),
};
if is_edit {
edits_this_turn += 1;
@@ -1399,7 +1413,6 @@ fn run_agent_turn(
}
}
let tool_path = args.get("path").and_then(|v| v.as_str()).map(std::string::ToString::to_string);
{