use std::sync::Arc;
use agent_contracts::runtime::RuntimeView;
use agent_contracts::tool::{ToolExecutor, ToolSpecView};
use agent_types::common::ids::AgentId;
use agent_types::tool::{FinalToolCall, RawToolOutcome, ToolExecutionError, ToolExecutorOutput};
use async_trait::async_trait;
use subagent::{JoinSubagentRequest, JoinSubagentResult};
use crate::r#impl::ToolRuntimeServices;
use super::input::JoinSubagentInput;
use super::output::JoinSubagentOutput;
use super::spec::JoinSubagentToolSpec;
use super::validation;
pub struct JoinSubagentExecutor {
spec: Arc<JoinSubagentToolSpec>,
services: ToolRuntimeServices,
}
impl JoinSubagentExecutor {
pub fn new(spec: Arc<JoinSubagentToolSpec>, services: ToolRuntimeServices) -> Self {
Self { spec, services }
}
}
#[async_trait]
impl ToolExecutor for JoinSubagentExecutor {
fn spec(&self) -> &dyn ToolSpecView {
self.spec.as_ref()
}
async fn invoke(
&self,
call: &FinalToolCall,
runtime: &dyn RuntimeView,
) -> Result<ToolExecutorOutput, ToolExecutionError> {
let input: JoinSubagentInput =
serde_json::from_value(call.input.clone()).map_err(|error| {
ToolExecutionError::ExecutionFailed {
message: format!("Failed to parse input: {}", error),
}
})?;
let validation_result = validation::validate_input(&input);
if !validation_result.result {
let message = validation_result
.message
.unwrap_or_else(|| "Validation failed".to_string());
let error_code = validation_result.error_code.unwrap_or(0);
return Ok(ToolExecutorOutput::Completed {
raw_outcome: RawToolOutcome::Error {
message: format!("[error_code={}] {}", error_code, message),
},
});
}
let subagent_control = self.services.subagent_control.clone().ok_or_else(|| {
ToolExecutionError::ExecutionFailed {
message: "subagent control is not configured".to_string(),
}
})?;
let session_id = runtime
.agent_context()
.metadata()
.session_id
.clone()
.ok_or_else(|| ToolExecutionError::ExecutionFailed {
message: "join_subagent requires a session_id in runtime metadata".to_string(),
})?;
let waiter_agent_id = AgentId(runtime.agent_context().metadata().agent_id.clone());
let result = subagent_control
.join(JoinSubagentRequest {
session_id,
waiter_agent_id,
target_agent_id: input.target_agent_id,
})
.await
.map_err(|error| ToolExecutionError::ExecutionFailed {
message: error.to_string(),
})?;
match result {
JoinSubagentResult::Ready { terminal } => {
let output =
serde_json::to_string(&JoinSubagentOutput { terminal }).map_err(|error| {
ToolExecutionError::ExecutionFailed {
message: format!("Failed to serialize join_subagent output: {}", error),
}
})?;
Ok(ToolExecutorOutput::Completed {
raw_outcome: RawToolOutcome::Success { output },
})
}
JoinSubagentResult::Pending { join_id } => Ok(ToolExecutorOutput::Suspended {
suspend_token: join_id,
}),
}
}
}