Restore research as first-class tool, remove research skill
Restores the research tool that was previously externalized as a skill: - Add pending_research.rs: PendingResearchManager with thread-safe task tracking - Add tools/research.rs: execute_research (async), execute_research_status - Add research/research_status tool definitions with exclude_research config - Integrate PendingResearchManager into Agent and ToolContext - Inject completed research results in streaming loop Remove research skill: - Clear EMBEDDED_SKILLS array in embedded.rs - Delete skills/research/ directory - Update all tests expecting embedded research skill - Update docs and memory to reflect the change The research tool now: - Spawns scout agent in background tokio task - Returns immediately with research_id - Automatically injects results into conversation when ready - Supports status checks via research_status tool
This commit is contained in:
@@ -12,6 +12,7 @@ use serde_json::json;
|
||||
pub struct ToolConfig {
|
||||
pub webdriver: bool,
|
||||
pub computer_control: bool,
|
||||
pub exclude_research: bool,
|
||||
}
|
||||
|
||||
impl ToolConfig {
|
||||
@@ -19,8 +20,16 @@ impl ToolConfig {
|
||||
Self {
|
||||
webdriver,
|
||||
computer_control,
|
||||
exclude_research: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a config with the research tool excluded.
|
||||
/// Used for scout agent to prevent recursion.
|
||||
pub fn with_research_excluded(mut self) -> Self {
|
||||
self.exclude_research = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Create tool definitions for native tool calling providers.
|
||||
@@ -28,7 +37,7 @@ impl ToolConfig {
|
||||
/// Returns a vector of Tool definitions that describe the available tools
|
||||
/// and their input schemas.
|
||||
pub fn create_tool_definitions(config: ToolConfig) -> Vec<Tool> {
|
||||
let mut tools = create_core_tools();
|
||||
let mut tools = create_core_tools(config.exclude_research);
|
||||
|
||||
if config.webdriver {
|
||||
tools.extend(create_webdriver_tools());
|
||||
@@ -38,7 +47,7 @@ pub fn create_tool_definitions(config: ToolConfig) -> Vec<Tool> {
|
||||
}
|
||||
|
||||
/// Create the core tools that are always available
|
||||
fn create_core_tools() -> Vec<Tool> {
|
||||
fn create_core_tools(exclude_research: bool) -> Vec<Tool> {
|
||||
let mut tools = vec![
|
||||
Tool {
|
||||
name: "shell".to_string(),
|
||||
@@ -258,6 +267,40 @@ fn create_core_tools() -> Vec<Tool> {
|
||||
}),
|
||||
});
|
||||
|
||||
// Conditionally add the research tool (excluded for scout agent to prevent recursion)
|
||||
if !exclude_research {
|
||||
tools.push(Tool {
|
||||
name: "research".to_string(),
|
||||
description: "Initiate web-based research on a topic. This tool is ASYNCHRONOUS - it spawns a research agent in the background and returns immediately with a research_id. Results are automatically injected into the conversation when ready. Use this when you need to research APIs, SDKs, libraries, approaches, bugs, or documentation. If you need the results before continuing, say so and yield the turn to the user. Check status with research_status tool.".to_string(),
|
||||
input_schema: json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "The research question or topic to investigate. Be specific about what you need to know."
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}),
|
||||
});
|
||||
|
||||
// research_status tool - check status of pending research
|
||||
tools.push(Tool {
|
||||
name: "research_status".to_string(),
|
||||
description: "Check the status of pending research tasks. Call without arguments to list all pending research, or with a research_id to check a specific task. Use this to see if research has completed before it's automatically injected.".to_string(),
|
||||
input_schema: json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"research_id": {
|
||||
"type": "string",
|
||||
"description": "Optional: specific research_id to check. If omitted, lists all pending research tasks."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
tools
|
||||
}
|
||||
|
||||
@@ -460,12 +503,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_core_tools_count() {
|
||||
let tools = create_core_tools();
|
||||
// Core tools: shell, background_process, read_file, read_image,
|
||||
// write_file, str_replace, code_search,
|
||||
// research, research_status, remember, plan_read, plan_write, plan_approve
|
||||
// (14 total - memory is auto-loaded, only remember tool needed)
|
||||
assert_eq!(tools.len(), 12);
|
||||
let tools = create_core_tools(false);
|
||||
// Core tools (with research): shell, background_process, read_file, read_image,
|
||||
// write_file, str_replace, code_search, plan_read, plan_write, plan_approve,
|
||||
// remember, rehydrate, research, research_status
|
||||
// (14 total)
|
||||
assert_eq!(tools.len(), 14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -479,7 +522,7 @@ mod tests {
|
||||
fn test_create_tool_definitions_core_only() {
|
||||
let config = ToolConfig::default();
|
||||
let tools = create_tool_definitions(config);
|
||||
assert_eq!(tools.len(), 12);
|
||||
assert_eq!(tools.len(), 14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -487,16 +530,24 @@ mod tests {
|
||||
let config = ToolConfig::new(true, true);
|
||||
let tools = create_tool_definitions(config);
|
||||
// 14 core + 15 webdriver = 29
|
||||
assert_eq!(tools.len(), 27);
|
||||
assert_eq!(tools.len(), 29);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tool_has_required_fields() {
|
||||
let tools = create_core_tools();
|
||||
let tools = create_core_tools(false);
|
||||
for tool in tools {
|
||||
assert!(!tool.name.is_empty(), "Tool name should not be empty");
|
||||
assert!(!tool.description.is_empty(), "Tool description should not be empty");
|
||||
assert!(tool.input_schema.is_object(), "Tool input_schema should be an object");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exclude_research_reduces_tool_count() {
|
||||
let with_research = create_core_tools(false);
|
||||
let without_research = create_core_tools(true);
|
||||
// Excluding research removes 2 tools (research and research_status)
|
||||
assert_eq!(with_research.len() - without_research.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user