Improve research tool error reporting for scout agent failures
When the scout agent fails (e.g., context window exhaustion), now: - Captures both stdout and stderr from the scout process - Detects context window exhaustion errors with specific patterns - Provides detailed, actionable error messages to the user - Shows suggestions for how to work around the issue - Includes technical details (exit code, error output) for debugging Handles two failure modes: 1. Scout agent exits with non-zero status 2. Scout agent exits successfully but doesn't produce valid report markers Both cases now surface clear error messages instead of cryptic failures.
This commit is contained in:
@@ -143,6 +143,12 @@ fn truncate_command_snippet(cmd: &str, max_len: usize) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error patterns that indicate context window exhaustion
|
||||||
|
const CONTEXT_ERROR_PATTERNS: &[&str] = &[
|
||||||
|
"context length", "context_length_exceeded", "maximum context", "token limit",
|
||||||
|
"too many tokens", "exceeds the model", "context window", "max_tokens",
|
||||||
|
];
|
||||||
|
|
||||||
pub async fn execute_research<W: UiWriter>(
|
pub async fn execute_research<W: UiWriter>(
|
||||||
tool_call: &ToolCall,
|
tool_call: &ToolCall,
|
||||||
ctx: &mut ToolContext<'_, W>,
|
ctx: &mut ToolContext<'_, W>,
|
||||||
@@ -181,10 +187,24 @@ pub async fn execute_research<W: UiWriter>(
|
|||||||
let stdout = child.stdout.take()
|
let stdout = child.stdout.take()
|
||||||
.ok_or_else(|| anyhow::anyhow!("Failed to capture scout agent stdout"))?;
|
.ok_or_else(|| anyhow::anyhow!("Failed to capture scout agent stdout"))?;
|
||||||
|
|
||||||
|
// Also capture stderr for error messages
|
||||||
|
let stderr = child.stderr.take()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Failed to capture scout agent stderr"))?;
|
||||||
|
|
||||||
let mut reader = BufReader::new(stdout).lines();
|
let mut reader = BufReader::new(stdout).lines();
|
||||||
let mut all_output = Vec::new();
|
let mut all_output = Vec::new();
|
||||||
|
|
||||||
|
// Spawn a task to collect stderr
|
||||||
|
let stderr_handle = tokio::spawn(async move {
|
||||||
|
let mut stderr_reader = BufReader::new(stderr).lines();
|
||||||
|
let mut stderr_output = Vec::new();
|
||||||
|
while let Some(line) = stderr_reader.next_line().await.ok().flatten() {
|
||||||
|
stderr_output.push(line);
|
||||||
|
}
|
||||||
|
stderr_output
|
||||||
|
});
|
||||||
|
|
||||||
// Collect all lines, showing only translated progress messages
|
// Collect stdout lines, showing only translated progress messages
|
||||||
while let Some(line) = reader.next_line().await? {
|
while let Some(line) = reader.next_line().await? {
|
||||||
all_output.push(line.clone());
|
all_output.push(line.clone());
|
||||||
|
|
||||||
@@ -194,19 +214,91 @@ pub async fn execute_research<W: UiWriter>(
|
|||||||
ctx.ui_writer.update_tool_output_line(&progress_msg);
|
ctx.ui_writer.update_tool_output_line(&progress_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect stderr output
|
||||||
|
let stderr_output = stderr_handle.await.unwrap_or_default();
|
||||||
|
|
||||||
// Wait for the process to complete
|
// Wait for the process to complete
|
||||||
let status = child.wait().await
|
let status = child.wait().await
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to wait for scout agent: {}", e))?;
|
.map_err(|e| anyhow::anyhow!("Failed to wait for scout agent: {}", e))?;
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
return Ok(format!("❌ Scout agent failed with exit code: {:?}", status.code()));
|
// Build detailed error message
|
||||||
|
let exit_code = status.code().map(|c| c.to_string()).unwrap_or_else(|| "unknown".to_string());
|
||||||
|
let full_output = all_output.join("\n");
|
||||||
|
let stderr_text = stderr_output.join("\n");
|
||||||
|
|
||||||
|
// Check for context window exhaustion
|
||||||
|
let combined_output = format!("{} {}", full_output, stderr_text).to_lowercase();
|
||||||
|
let is_context_error = CONTEXT_ERROR_PATTERNS.iter()
|
||||||
|
.any(|pattern| combined_output.contains(pattern));
|
||||||
|
|
||||||
|
if is_context_error {
|
||||||
|
let error_msg = format!(
|
||||||
|
"❌ **Scout Agent Error: Context Window Exhausted**\n\n\
|
||||||
|
The research query required more context than the model supports.\n\n\
|
||||||
|
**Suggestions:**\n\
|
||||||
|
- Try a more specific, narrower query\n\
|
||||||
|
- Break the research into smaller sub-questions\n\
|
||||||
|
- Use a model with a larger context window\n\n\
|
||||||
|
**Technical Details:**\n\
|
||||||
|
Exit code: {}\n\
|
||||||
|
{}",
|
||||||
|
exit_code,
|
||||||
|
if !stderr_text.is_empty() { format!("Error output: {}", stderr_text.chars().take(500).collect::<String>()) } else { String::new() }
|
||||||
|
);
|
||||||
|
ctx.ui_writer.println(&error_msg);
|
||||||
|
return Ok(error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic error with details
|
||||||
|
let error_msg = format!(
|
||||||
|
"❌ **Scout Agent Failed**\n\n\
|
||||||
|
Exit code: {}\n\n\
|
||||||
|
{}{}",
|
||||||
|
exit_code,
|
||||||
|
if !stderr_text.is_empty() { format!("**Error output:**\n{}\n\n", stderr_text.chars().take(1000).collect::<String>()) } else { String::new() },
|
||||||
|
if all_output.len() > 0 { format!("**Last output lines:**\n{}", all_output.iter().rev().take(10).rev().cloned().collect::<Vec<_>>().join("\n")) } else { String::new() }
|
||||||
|
);
|
||||||
|
ctx.ui_writer.println(&error_msg);
|
||||||
|
return Ok(error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join all output and extract the report between markers
|
// Join all output and extract the report between markers
|
||||||
let full_output = all_output.join("\n");
|
let full_output = all_output.join("\n");
|
||||||
|
|
||||||
let report = extract_report(&full_output)?;
|
let report = match extract_report(&full_output) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
// Check if this looks like a context exhaustion issue
|
||||||
|
let combined = format!("{} {}", full_output, stderr_output.join(" ")).to_lowercase();
|
||||||
|
let is_context_error = CONTEXT_ERROR_PATTERNS.iter()
|
||||||
|
.any(|pattern| combined.contains(pattern));
|
||||||
|
|
||||||
|
let error_msg = if is_context_error {
|
||||||
|
format!(
|
||||||
|
"❌ **Scout Agent Error: Context Window Exhausted**\n\n\
|
||||||
|
The scout agent ran out of context before completing the research report.\n\n\
|
||||||
|
**Suggestions:**\n\
|
||||||
|
- Try a more specific, narrower query\n\
|
||||||
|
- Break the research into smaller sub-questions\n\n\
|
||||||
|
**Technical Details:**\n\
|
||||||
|
{}",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"❌ **Scout Agent Error: Report Extraction Failed**\n\n\
|
||||||
|
{}\n\n\
|
||||||
|
The scout agent completed but did not produce a valid report.\n\
|
||||||
|
This may indicate the agent encountered an error during research.",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
};
|
||||||
|
ctx.ui_writer.println(&error_msg);
|
||||||
|
return Ok(error_msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Print the research brief to the console for scrollback reference
|
// Print the research brief to the console for scrollback reference
|
||||||
// The report is printed without stripping ANSI codes to preserve formatting
|
// The report is printed without stripping ANSI codes to preserve formatting
|
||||||
|
|||||||
Reference in New Issue
Block a user