feat: enhance error handling in OAuth URL building and client creation; improve tool argument sanitization

This commit is contained in:
asepharyana
2026-07-12 10:23:26 +07:00
parent 0cc60c12ce
commit bb621fdff2
10 changed files with 79 additions and 43 deletions
+3 -3
View File
@@ -13,8 +13,8 @@ const MCP_CALL_TIMEOUT_MS: u64 = 60_000;
fn mcp_static_str(s: &str) -> &'static str {
static CACHE: OnceLock<Mutex<Vec<&'static str>>> = OnceLock::new();
let mut cache = CACHE.get_or_init(|| Mutex::new(Vec::new())).lock().unwrap();
if let Some(existing) = cache.iter().find(|e| **e == s) {
return *existing;
if let Some(&existing) = cache.iter().find(|e| **e == s) {
return existing;
}
let leaked: &'static str = Box::leak(s.to_string().into_boxed_str());
cache.push(leaked);
@@ -163,7 +163,7 @@ fn call_via_stdio(
let mut guard;
let child: &mut StdioChild = if let Some(mtx) = existing_handle {
guard = mtx.lock().map_err(|e| anyhow::anyhow!("MCP handle lock: {}", e))?;
&mut *guard
&mut guard
} else {
let mut fresh = spawn_stdio_child(command, extra_args)?;
let result = fresh.call("tools/call", json!({
+16 -27
View File
@@ -9,8 +9,10 @@ use crate::app::state::runtime::TurnEvent;
use crate::app::state::types::{Origin, Overlay, Toast, ToastKind};
use crate::dto::chat::message::{ChatMessage, Role};
const MAX_TOOL_ONLY_TURNS: usize = usize::MAX;
const MAX_AGENT_STEPS: usize = usize::MAX;
// Step bounds intentionally left unbounded (usize::MAX) so the agent can
// continue across as many turns as needed. Each iteration still honours
// `tc.abort_flag` and the per-call LLM timeout, so a runaway loop is
// observable and cancellable from the UI.
#[derive(Debug, Clone)]
pub enum Action {
@@ -555,19 +557,17 @@ fn generate_workspace_tree(roots: &[std::path::PathBuf]) -> String {
.git_ignore(true)
.build();
let mut count = 0;
for result in walker {
if let Ok(entry) = result {
let path = entry.path();
if let Ok(rel) = path.strip_prefix(root) {
if rel.as_os_str().is_empty() { continue; }
let is_dir = entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false);
let prefix = if is_dir { "[DIR] " } else { " " };
out.push_str(&format!(" {}{}\n", prefix, rel.display()));
count += 1;
if count > 1000 {
out.push_str(" ... (truncated)\n");
break;
}
for entry in walker.flatten() {
let path = entry.path();
if let Ok(rel) = path.strip_prefix(root) {
if rel.as_os_str().is_empty() { continue; }
let is_dir = entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false);
let prefix = if is_dir { "[DIR] " } else { " " };
out.push_str(&format!(" {}{}\n", prefix, rel.display()));
count += 1;
if count > 1000 {
out.push_str(" ... (truncated)\n");
break;
}
}
}
@@ -590,7 +590,6 @@ fn run_agent_turn(
) -> anyhow::Result<()> {
let mut msgs = messages.to_vec();
let mut edits_this_turn = 0u32;
let mut tool_only_rounds = 0usize;
let mut prev_shaped = false;
let tree_info = generate_workspace_tree(&tc.workspace_roots);
@@ -606,16 +605,7 @@ fn run_agent_turn(
msgs.insert(0, sys);
}
for _step in 0..MAX_AGENT_STEPS {
if tool_only_rounds >= MAX_TOOL_ONLY_TURNS {
let stop_msg = ChatMessage::user(
"Stop calling tools. Respond naturally now.".to_string(),
);
archive_message(&tc.db, &tc.session_id, &stop_msg);
msgs.push(stop_msg);
tool_only_rounds = 0;
}
loop {
let wire_msgs = if crate::app::runtime::shortsend::should_shape(msgs.len(), prev_shaped) {
let total_chars: usize = msgs.iter()
.filter_map(|m| m.content.as_deref())
@@ -728,7 +718,6 @@ fn run_agent_turn(
let content = response.content.clone().unwrap_or_default();
if has_tool_calls {
tool_only_rounds += 1;
let tool_calls = response.tool_calls.clone().unwrap_or_default();
archive_message(&tc.db, &tc.session_id, &response);
msgs.push(response);
+2 -2
View File
@@ -5,6 +5,7 @@ use crate::tool::{all_tools, tool_defs, tool_is_risky};
use super::context::SubagentContext;
use super::event::SubagentEvent;
#[allow(dead_code)]
pub const MAX_AGENT_STEPS: usize = usize::MAX;
/// Maps a subagent's allowed tool names to concrete Tool trait objects and
@@ -61,8 +62,7 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
let (tools, tdefs) = build_subagent_tools(&ctx.allowed_tools);
let tdefs_opt: Option<Vec<ToolDef>> = if tdefs.is_empty() { None } else { Some(tdefs) };
let max_steps = ctx.max_steps.min(MAX_AGENT_STEPS);
for step in 0..max_steps {
for step in 0..ctx.max_steps {
let (api_key, model, base_url) = resolve_provider_config();
let client = crate::service::provider::LlmClient::new(api_key, model, base_url);