8.6 KiB
Subagent Orchestration Enhancement
This document describes the enhanced Task tool with proper subagent orchestration support using plugin agents.
Overview
The Task tool has been enhanced to support a new architecture for spawning and managing specialized subagents. The system now integrates with the plugin system's AgentDefinition type, allowing both built-in and plugin-provided agents to be orchestrated.
Key Components
1. SubagentConfig
Configuration structure for spawning subagents:
pub struct SubagentConfig {
/// Agent type/name (e.g., "code-reviewer", "explore")
pub agent_type: String,
/// Task prompt for the agent
pub prompt: String,
/// Optional model override
pub model: Option<String>,
/// Tool whitelist (if None, uses agent's default)
pub tools: Option<Vec<String>>,
/// Parsed agent definition (if from plugin)
pub definition: Option<AgentDefinition>,
}
Builder Pattern:
let config = SubagentConfig::new("explore".to_string(), "Find all Rust files".to_string())
.with_model("claude-3-opus".to_string())
.with_tools(vec!["read".to_string(), "glob".to_string()]);
2. SubagentRegistry
Thread-safe registry for tracking available agents:
pub struct SubagentRegistry {
agents: Arc<RwLock<HashMap<String, AgentDefinition>>>,
}
Key Methods:
new()- Create empty registryregister_builtin()- Register built-in agentsregister_from_plugins(Vec<AgentDefinition>)- Register plugin agentsget(name: &str)- Get agent by namelist()- List all agents with descriptionscontains(name: &str)- Check if agent existsagent_names()- Get all agent names
Usage:
let registry = SubagentRegistry::new();
registry.register_builtin();
// Load plugin agents
let plugin_manager = PluginManager::new();
plugin_manager.load_all()?;
let plugin_agents = plugin_manager.load_all_agents();
registry.register_from_plugins(plugin_agents);
// Use registry
if let Some(agent) = registry.get("explore") {
println!("Agent: {} - {}", agent.name, agent.description);
}
3. Built-in Agents
The system includes six specialized built-in agents:
explore
- Purpose: Codebase exploration
- Tools: read, glob, grep, ls
- Color: blue
- Use Cases: Finding files, understanding structure
plan
- Purpose: Implementation planning
- Tools: read, glob, grep
- Color: green
- Use Cases: Designing architectures, creating strategies
code-reviewer
- Purpose: Code analysis
- Tools: read, grep, glob (read-only)
- Color: yellow
- Use Cases: Quality review, bug detection
test-writer
- Purpose: Test creation
- Tools: read, write, edit, grep, glob
- Color: cyan
- Use Cases: Writing unit tests, integration tests
doc-writer
- Purpose: Documentation
- Tools: read, write, edit, grep, glob
- Color: magenta
- Use Cases: Writing READMEs, API docs
refactorer
- Purpose: Code refactoring
- Tools: read, write, edit, grep, glob (no bash)
- Color: red
- Use Cases: Improving structure, applying patterns
Future Implementation
The following functions will be implemented to complete the orchestration system:
spawn_subagent
/// Spawn a subagent with the given configuration
pub async fn spawn_subagent<P: LlmProvider>(
provider: &P,
registry: &SubagentRegistry,
config: SubagentConfig,
perms: &PermissionManager,
) -> Result<String>
Behavior:
- Look up agent definition from registry or config
- Extract system prompt and tool whitelist from definition
- Build full prompt combining system prompt + task
- Create filtered permission manager if tool whitelist specified
- Run agent loop with system prompt
- Return result string
spawn_parallel
/// Spawn multiple subagents in parallel and collect results
pub async fn spawn_parallel<P: LlmProvider + Clone>(
provider: &P,
registry: &SubagentRegistry,
configs: Vec<SubagentConfig>,
perms: &PermissionManager,
) -> Vec<Result<String>>
Behavior:
- Create futures for each config
- Execute all in parallel using
join_all - Return vector of results
Note: Requires PermissionManager to implement Clone. This may need to be added to the permissions crate.
Integration Points
With Plugin System
use plugins::PluginManager;
use tools_task::SubagentRegistry;
let mut plugin_manager = PluginManager::new();
plugin_manager.load_all()?;
let registry = SubagentRegistry::new();
registry.register_builtin();
registry.register_from_plugins(plugin_manager.load_all_agents());
With Agent Core
The subagent execution will integrate with agent-core by:
- Calling the same
run_agent_loopfunction used by main agent - Passing filtered tool definitions based on agent's tool whitelist
- Using agent-specific system prompts
- Inheriting parent's permission manager (or creating restricted copy)
With Permission System
Subagents respect the permission system:
- Tool whitelist from agent definition restricts available tools
- Permission manager checks are still applied
- Parent's mode (plan/acceptEdits/code) is inherited
Example Usage Patterns
Basic Exploration
let registry = SubagentRegistry::new();
registry.register_builtin();
let config = SubagentConfig::new(
"explore".to_string(),
"Find all test files in the codebase".to_string()
);
let result = spawn_subagent(&provider, ®istry, config, &perms).await?;
println!("Found files:\n{}", result);
Parallel Analysis
let configs = vec![
SubagentConfig::new("explore".to_string(), "Find all Rust files".to_string()),
SubagentConfig::new("code-reviewer".to_string(), "Review auth module".to_string()),
SubagentConfig::new("test-writer".to_string(), "Check test coverage".to_string()),
];
let results = spawn_parallel(&provider, ®istry, configs, &perms).await;
for (i, result) in results.iter().enumerate() {
match result {
Ok(output) => println!("Agent {} completed:\n{}", i, output),
Err(e) => eprintln!("Agent {} failed: {}", i, e),
}
}
Custom Plugin Agent
// Plugin provides custom-analyzer agent
let config = SubagentConfig::new(
"custom-analyzer".to_string(),
"Analyze security vulnerabilities".to_string()
);
if registry.contains("custom-analyzer") {
let result = spawn_subagent(&provider, ®istry, config, &perms).await?;
} else {
eprintln!("Agent not found. Available: {:?}", registry.agent_names());
}
Migration Guide
From Legacy Subagent API
The legacy Subagent struct and keyword-based matching is still available for backward compatibility:
// Legacy API (still works)
let agent = Subagent::new(
"reader".to_string(),
"Read-only agent".to_string(),
vec!["read".to_string()],
vec![Tool::Read, Tool::Grep],
);
Migrate to new API:
- Use
SubagentRegistryinstead of custom keyword matching - Use
SubagentConfiginstead of direct agent instantiation - Use
spawn_subagentinstead of manual tool execution
Testing
Run tests:
cargo test -p tools-task
All tests pass, including:
- Registry builtin registration
- Plugin agent registration
- Config builder pattern
- Legacy API backward compatibility
Dependencies
plugins- ForAgentDefinitiontypeparking_lot- ForRwLockin thread-safe registrypermissions- For tool permission checkscolor-eyre- For error handlingserde/serde_json- For serialization
Future Work
- Implement spawn_subagent: Complete the actual subagent spawning logic
- Add Clone to PermissionManager: Required for parallel execution
- System Prompt Support: Ensure agent loop respects system prompts
- Tool Filtering: Implement filtered tool definitions based on whitelist
- Progress Tracking: Add hooks for monitoring subagent progress
- Error Recovery: Handle subagent failures gracefully
- Resource Limits: Add timeout and resource constraints
- Inter-Agent Communication: Allow agents to share context
Related Files
/home/cnachtigall/data/git/projects/Owlibou/owlen/crates/tools/task/src/lib.rs- Main implementation/home/cnachtigall/data/git/projects/Owlibou/owlen/crates/tools/task/Cargo.toml- Dependencies/home/cnachtigall/data/git/projects/Owlibou/owlen/crates/platform/plugins/src/lib.rs- AgentDefinition type/home/cnachtigall/data/git/projects/Owlibou/owlen/crates/core/agent/src/lib.rs- Agent execution loop/home/cnachtigall/data/git/projects/Owlibou/owlen/crates/platform/permissions/src/lib.rs- Permission system