use crate::message::Message;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubAgentRequest {
pub description: String,
pub prompt: String,
pub subagent_type: String,
pub model: Option<String>,
pub parent_session_id: String,
pub current_depth: usize,
#[serde(default)]
pub initial_messages: Vec<Message>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubAgentResponse {
pub child_session_id: String,
pub response: String,
pub conversation_turns: Option<usize>,
}
#[async_trait::async_trait]
pub trait AgentRuntime: Send + Sync {
async fn spawn_subagent(&self, request: SubAgentRequest) -> Result<SubAgentResponse, String>;
fn supports_subagent_type(&self, _subagent_type: &str) -> bool {
true
}
fn max_depth(&self) -> usize {
5
}
}
#[derive(Debug, Clone, Default)]
pub struct NoOpRuntime;
impl NoOpRuntime {
pub fn new() -> Self {
Self
}
}
#[async_trait::async_trait]
impl AgentRuntime for NoOpRuntime {
async fn spawn_subagent(&self, request: SubAgentRequest) -> Result<SubAgentResponse, String> {
tracing::warn!(
subagent_type = %request.subagent_type,
description = %request.description,
"TaskTool operating in no-op mode: no AgentRuntime configured. \
To enable real sub-agent execution, inject an AgentRuntime via TaskTool::with_runtime()"
);
Err(format!(
"Sub-agent execution is not available. No AgentRuntime is configured. \
Task '{}' (type: {}) cannot be executed. \
Configure an AgentRuntime to enable sub-agent spawning.",
request.description, request.subagent_type
))
}
fn supports_subagent_type(&self, _subagent_type: &str) -> bool {
false
}
}
pub fn no_op_runtime() -> Arc<dyn AgentRuntime> {
Arc::new(NoOpRuntime::new())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_no_op_runtime_returns_error() {
let runtime = NoOpRuntime::new();
let request = SubAgentRequest {
description: "Test task".to_string(),
prompt: "Do something".to_string(),
subagent_type: "general-purpose".to_string(),
model: None,
parent_session_id: "test-session".to_string(),
current_depth: 0,
initial_messages: vec![],
};
let result = runtime.spawn_subagent(request).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.contains("No AgentRuntime is configured"));
}
#[test]
fn test_no_op_runtime_does_not_support_types() {
let runtime = NoOpRuntime::new();
assert!(!runtime.supports_subagent_type("general-purpose"));
assert!(!runtime.supports_subagent_type("explore"));
}
#[test]
fn test_default_max_depth() {
let runtime = NoOpRuntime::new();
assert_eq!(runtime.max_depth(), 5);
}
}