feat: enhance error handling in OAuth URL building and client creation; improve tool argument sanitization
This commit is contained in:
@@ -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!({
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user