Skip to main content

echo_agent/agent/subagent/
isolated.rs

1//! Isolated sub-agent execution — independent context, messages, and budget.
2//!
3//! Unlike the current sub-agent system which shares the parent's context,
4//! an isolated sub-agent gets its own system prompt and runs independently.
5//! The agent's `execute()` method creates a fresh context automatically.
6
7use crate::agent::Agent;
8use crate::error::Result;
9
10/// Configuration for an isolated sub-agent run.
11#[derive(Debug, Clone)]
12pub struct IsolatedSubAgentConfig {
13    /// System prompt for the sub-agent.
14    pub system_prompt: String,
15    /// Maximum iterations for the ReAct loop.
16    pub max_iterations: usize,
17    /// Token budget for the sub-agent's context (triggers compression when exceeded).
18    pub token_budget: usize,
19    /// Maximum tool calls before force-stop (0 = unlimited).
20    pub tool_call_limit: usize,
21    /// Timeout in seconds for the entire sub-agent run.
22    pub timeout_secs: u64,
23}
24
25impl Default for IsolatedSubAgentConfig {
26    fn default() -> Self {
27        Self {
28            system_prompt:
29                "You are a helpful sub-agent. Complete the task and report only the result.".into(),
30            max_iterations: 5,
31            token_budget: 16_000,
32            tool_call_limit: 20,
33            timeout_secs: 120,
34        }
35    }
36}
37
38/// Result of an isolated sub-agent execution.
39pub struct IsolatedSubAgentResult {
40    /// Final output from the sub-agent.
41    pub output: String,
42    /// Whether execution completed successfully.
43    pub success: bool,
44}
45
46/// Runs a task with an isolated sub-agent with budget enforcement.
47pub async fn run_isolated(
48    parent: &crate::prelude::ReactAgent,
49    task: &str,
50    config: &IsolatedSubAgentConfig,
51) -> Result<IsolatedSubAgentResult> {
52    let sub = crate::prelude::ReactAgentBuilder::new()
53        .model(parent.model_name())
54        .system_prompt(&config.system_prompt)
55        .max_iterations(config.max_iterations)
56        .token_limit(config.token_budget)
57        .enable_tools()
58        .build()?;
59
60    // Enforce timeout
61    let result = tokio::time::timeout(
62        std::time::Duration::from_secs(config.timeout_secs),
63        sub.execute(task),
64    )
65    .await;
66
67    match result {
68        Ok(Ok(output)) => Ok(IsolatedSubAgentResult {
69            output,
70            success: true,
71        }),
72        Ok(Err(e)) => Ok(IsolatedSubAgentResult {
73            output: format!("Sub-agent error: {e}"),
74            success: false,
75        }),
76        Err(_) => Ok(IsolatedSubAgentResult {
77            output: format!("Sub-agent timed out after {}s", config.timeout_secs),
78            success: false,
79        }),
80    }
81}