diff --git a/codex-rs/core/src/tasks/user_shell.rs b/codex-rs/core/src/tasks/user_shell.rs index eddfe00511..b98108d8e0 100644 --- a/codex-rs/core/src/tasks/user_shell.rs +++ b/codex-rs/core/src/tasks/user_shell.rs @@ -1,28 +1,28 @@ use std::sync::Arc; use async_trait::async_trait; -use codex_protocol::models::ShellToolCallParams; use codex_protocol::user_input::UserInput; -use tokio::sync::Mutex; use tokio_util::sync::CancellationToken; use tracing::error; use uuid::Uuid; use crate::codex::TurnContext; +use crate::exec::SandboxType; +use crate::exec::StdoutStream; +use crate::exec::execute_exec_env; +use crate::exec_env::create_env; use crate::protocol::EventMsg; use crate::protocol::TaskStartedEvent; +use crate::sandboxing::SandboxManager; use crate::state::TaskKind; -use crate::tools::context::ToolPayload; -use crate::tools::parallel::ToolCallRuntime; -use crate::tools::router::ToolCall; -use crate::tools::router::ToolRouter; -use crate::turn_diff_tracker::TurnDiffTracker; +use crate::tools::events::ToolEmitter; +use crate::tools::events::ToolEventCtx; +use crate::tools::runtimes::build_command_spec; +use crate::tools::sandboxing::ToolError; use super::SessionTask; use super::SessionTaskContext; -const USER_SHELL_TOOL_NAME: &str = "local_shell"; - #[derive(Clone)] pub(crate) struct UserShellCommandTask { command: String, @@ -78,37 +78,75 @@ impl SessionTask for UserShellCommandTask { } }; - let params = ShellToolCallParams { - command: shell_invocation, - workdir: None, - timeout_ms: None, - with_escalated_permissions: None, - justification: None, + if cancellation_token.is_cancelled() { + return None; + } + + let call_id = Uuid::new_v4().to_string(); + let emitter = ToolEmitter::shell(shell_invocation.clone(), turn_context.cwd.clone(), true); + let event_ctx = ToolEventCtx::new(session.as_ref(), turn_context.as_ref(), &call_id, None); + emitter.begin(event_ctx).await; + + let env = create_env(&turn_context.shell_environment_policy); + let spec = match build_command_spec( + &shell_invocation, + &turn_context.cwd, + &env, + None, + None, + None, + ) { + Ok(spec) => spec, + Err(err) => { + let event_ctx = + ToolEventCtx::new(session.as_ref(), turn_context.as_ref(), &call_id, None); + if let Err(finish_err) = emitter.finish(event_ctx, Err(err)).await { + error!(?finish_err, "user shell command failed: {finish_err:?}"); + } + return None; + } }; - let tool_call = ToolCall { - tool_name: USER_SHELL_TOOL_NAME.to_string(), - call_id: Uuid::new_v4().to_string(), - payload: ToolPayload::LocalShell { - params, - is_user_shell_command: true, - }, + let manager = SandboxManager::new(); + let exec_env = match manager.transform( + &spec, + &turn_context.sandbox_policy, + SandboxType::None, + &turn_context.cwd, + turn_context.codex_linux_sandbox_exe.as_ref(), + ) { + Ok(env) => env, + Err(err) => { + let event_ctx = + ToolEventCtx::new(session.as_ref(), turn_context.as_ref(), &call_id, None); + let tool_error = ToolError::Codex(err.into()); + if let Err(finish_err) = emitter.finish(event_ctx, Err(tool_error)).await { + error!(?finish_err, "user shell command failed: {finish_err:?}"); + } + return None; + } }; - let router = Arc::new(ToolRouter::from_config(&turn_context.tools_config, None)); - let tracker = Arc::new(Mutex::new(TurnDiffTracker::new())); - let runtime = ToolCallRuntime::new( - Arc::clone(&router), - Arc::clone(&session), - Arc::clone(&turn_context), - Arc::clone(&tracker), - ); + let stdout_stream = StdoutStream { + sub_id: turn_context.sub_id.clone(), + call_id: call_id.clone(), + tx_event: session.get_tx_event(), + }; - if let Err(err) = runtime - .handle_tool_call(tool_call, cancellation_token) - .await - { - error!("user shell command failed: {err:?}"); + let exec_result = tokio::select! { + _ = cancellation_token.cancelled() => None, + res = execute_exec_env(exec_env, &turn_context.sandbox_policy, Some(stdout_stream)) => Some(res), + }; + + if let Some(result) = exec_result { + let event_ctx = + ToolEventCtx::new(session.as_ref(), turn_context.as_ref(), &call_id, None); + if let Err(err) = emitter + .finish(event_ctx, result.map_err(ToolError::Codex)) + .await + { + error!(?err, "user shell command failed: {err:?}"); + } } None }