//! `AgentDefinition` -- declarative specification for instantiating a //! subagent from workflow scripts or programmatic calls. //! //! Also provides a shared [`spawn_subagent_with_drain`] helper that //! eliminates the channel-creation + drain-thread boilerplate duplicated //! across `auto/mod.rs`, `review/mod.rs`, and `workflow/engine/mod.rs`. use super::event::SubagentEvent; use serde::{Deserialize, Serialize}; /// Declarative specification for instantiating a subagent: name, role, /// optional system prompt, allowed tools, step budget, and temperature. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AgentDefinition { pub name: String, pub role: String, pub system_prompt: Option, pub allowed_tools: Option>, pub max_steps: Option, pub temperature: Option, } impl AgentDefinition { /// Create an agent definition with the required name and role; all /// optional fields start as `None`. pub fn new(name: String, role: String) -> Self { AgentDefinition { name, role, system_prompt: None, allowed_tools: None, max_steps: None, temperature: None, } } /// Builder method: set the system prompt for this agent. pub fn with_system_prompt(mut self, prompt: String) -> Self { self.system_prompt = Some(prompt); self } /// Builder method: set the allowed tool list for this agent. pub fn with_allowed_tools(mut self, tools: Vec) -> Self { self.allowed_tools = Some(tools); self } /// Builder method: set the maximum step count for this agent. pub fn with_max_steps(mut self, steps: usize) -> Self { self.max_steps = Some(steps); self } } /// Shared subagent spawning utility: creates an mpsc channel and spawns a /// drain thread that forwards every [`SubagentEvent`] to `on_event`. /// /// Returns the sender half (for passing to [`run_subagent`](super::engine::run_subagent)) /// and the drain thread's join handle so the caller can keep it alive for /// the duration of the subagent run. /// /// # Example /// /// ```ignore /// let (tx, _drain) = spawn_subagent_with_drain(|event| { /// match &event { /// SubagentEvent::ToolCall { tool, .. } => tracing::debug!("tool: {tool}"), /// SubagentEvent::Completed => tracing::debug!("done"), /// _ => {} /// } /// }); /// let verdict = run_subagent(&ctx, &tx)?; /// ``` /// /// # Duplication eliminated /// /// Previously every subagent caller inlined the same 5-line pattern: /// /// ```ignore /// let (tx, mut rx) = tokio::sync::mpsc::channel(32); /// let _drain = std::thread::spawn(move || { /// while let Some(event) = rx.blocking_recv() { ... } /// }); /// ``` /// /// Callers that need a larger buffer (e.g. workflow engine uses 64) should /// create the channel manually instead of using this helper. pub fn spawn_subagent_with_drain( on_event: F, ) -> (tokio::sync::mpsc::Sender, std::thread::JoinHandle<()>) where F: Fn(SubagentEvent) + Send + 'static, { let (tx, mut rx) = tokio::sync::mpsc::channel(32); let drain = std::thread::spawn(move || { while let Some(event) = rx.blocking_recv() { on_event(event); } }); (tx, drain) }