use async_trait::async_trait; use codex_protocol::models::ShellToolCallParams; use std::sync::Arc; use crate::codex::TurnContext; use crate::exec::ExecParams; use crate::exec_env::create_env; use crate::function_tool::FunctionCallError; use crate::tools::context::ToolInvocation; use crate::tools::context::ToolOutput; use crate::tools::context::ToolPayload; use crate::tools::handle_container_exec_with_params; use crate::tools::registry::ToolHandler; use crate::tools::registry::ToolKind; pub struct ShellHandler; impl ShellHandler { fn to_exec_params(params: ShellToolCallParams, turn_context: &TurnContext) -> ExecParams { ExecParams { command: params.command, cwd: turn_context.resolve_path(params.workdir.clone()), timeout_ms: params.timeout_ms, env: create_env(&turn_context.shell_environment_policy), with_escalated_permissions: params.with_escalated_permissions, justification: params.justification, } } } #[async_trait] impl ToolHandler for ShellHandler { fn kind(&self) -> ToolKind { ToolKind::Function } fn matches_kind(&self, payload: &ToolPayload) -> bool { matches!( payload, ToolPayload::Function { .. } | ToolPayload::LocalShell { .. } ) } async fn handle(&self, invocation: ToolInvocation) -> Result { let ToolInvocation { session, turn, tracker, sub_id, call_id, tool_name, payload, } = invocation; match payload { ToolPayload::Function { arguments } => { let params: ShellToolCallParams = serde_json::from_str(&arguments).map_err(|e| { FunctionCallError::RespondToModel(format!( "failed to parse function arguments: {e:?}" )) })?; let exec_params = Self::to_exec_params(params, turn.as_ref()); let content = handle_container_exec_with_params( tool_name.as_str(), exec_params, Arc::clone(&session), Arc::clone(&turn), Arc::clone(&tracker), sub_id.clone(), call_id.clone(), ) .await?; Ok(ToolOutput::Function { content, success: Some(true), }) } ToolPayload::LocalShell { params } => { let exec_params = Self::to_exec_params(params, turn.as_ref()); let content = handle_container_exec_with_params( tool_name.as_str(), exec_params, Arc::clone(&session), Arc::clone(&turn), Arc::clone(&tracker), sub_id.clone(), call_id.clone(), ) .await?; Ok(ToolOutput::Function { content, success: Some(true), }) } _ => Err(FunctionCallError::RespondToModel(format!( "unsupported payload for shell handler: {tool_name}" ))), } } }