Shell execution tools enable the agent to run commands on the user's system. The codebase provides three distinct tool variants that execute shell commands with different capabilities and interfaces:
shell - Function-based tool for executing arbitrary shell commands (used with older models)shell_command - Function-based tool with explicit shell control and login shell support (freeform variant for newer models)exec_command / write_stdin - Unified exec tools for interactive process management with PTY sessionsFor interactive process management details, see Unified Exec Process Management. For approval and sandboxing flows, see Tool Orchestration and Approval and Sandboxing Implementation.
Sources: codex-rs/core/src/tools/handlers/shell.rs codex-rs/core/src/tools/handlers/unified_exec.rs
shell ToolThe shell tool executes a command using the session's configured shell. It accepts a ShellToolCallParams structure with the following schema:
| Parameter | Type | Description |
|---|---|---|
command | Vec<String> | Raw command vector to execute |
workdir | Option<String> | Working directory (relative to turn CWD) |
timeout_ms | Option<u64> | Execution timeout in milliseconds |
sandbox_permissions | Option<SandboxPermissions> | Sandbox permission overrides |
justification | Option<String> | Justification for escalated permissions |
prefix_rule | Option<Vec<String>> | Prefix rules for approval policy |
The handler converts the command vector directly to ExecParams using the session's shell without additional transformation.
Sources: codex-rs/core/src/tools/handlers/shell.rs46-62
shell_command ToolThe shell_command tool is a freeform variant that wraps commands with shell-specific invocation patterns. It provides explicit control over shell selection and login shell behavior:
| Parameter | Type | Description |
|---|---|---|
command | String | Command string to execute |
workdir | Option<String> | Working directory (relative to turn CWD) |
login | Option<bool> | Explicit login shell flag |
timeout_ms | Option<u64> | Execution timeout in milliseconds |
sandbox_permissions | Option<SandboxPermissions> | Sandbox permission overrides |
justification | Option<String> | Justification for escalated permissions |
prefix_rule | Option<Vec<String>> | Prefix rules for approval policy |
The handler constructs shell invocations using Shell::derive_exec_args, producing platform-appropriate command vectors (e.g., ["/bin/bash", "-lc", "<command>"] for Bash with login shell).
Sources: codex-rs/core/src/tools/handlers/shell.rs65-106
exec_command ToolThe exec_command tool creates interactive PTY sessions that persist across multiple tool calls. Unlike shell and shell_command, which execute and terminate, exec_command can leave a process running:
| Parameter | Type | Description |
|---|---|---|
cmd | String | Command string to execute |
workdir | Option<String> | Working directory (relative to turn CWD) |
shell | Option<String> | Shell path override |
login | Option<bool> | Login shell flag |
tty | bool | Enable TTY mode (default: false) |
yield_time_ms | u64 | Time to wait for output (default: 10000ms) |
max_output_tokens | Option<usize> | Maximum output tokens to return |
sandbox_permissions | SandboxPermissions | Sandbox permission overrides |
justification | Option<String> | Justification for escalated permissions |
prefix_rule | Option<Vec<String>> | Prefix rules for approval policy |
When a process remains running after yield_time_ms, it returns a process_id that can be used with write_stdin for continued interaction.
Sources: codex-rs/core/src/tools/handlers/unified_exec.rs28-49
write_stdin ToolThe write_stdin tool interacts with persistent processes created by exec_command:
| Parameter | Type | Description |
|---|---|---|
session_id | i32 | Process ID from previous exec_command |
chars | String | Input to write to stdin (empty for polling) |
yield_time_ms | u64 | Time to wait for output (default: 250ms) |
max_output_tokens | Option<usize> | Maximum output tokens to return |
Empty chars values act as output polls with longer minimum yield times (MIN_EMPTY_YIELD_TIME_MS = 5000ms) to allow background processes to produce output.
Sources: codex-rs/core/src/tools/handlers/unified_exec.rs51-61
Handler Dispatch Flow
All three handlers implement the ToolHandler trait with these key methods:
matches_kind - Checks if the handler supports the payload type (Function, LocalShell, Custom)is_mutating - Determines if the command is safe (uses is_known_safe_command heuristic)handle - Executes the tool call and returns ToolOutputThe ShellHandler and ShellCommandHandler both route through ToolOrchestrator to ShellRuntime, while UnifiedExecHandler routes to UnifiedExecProcessManager for PTY-based execution.
Sources: codex-rs/core/src/tools/handlers/shell.rs108-181 codex-rs/core/src/tools/handlers/shell.rs183-251 codex-rs/core/src/tools/handlers/unified_exec.rs75-246
The shell execution system supports two shell selection modes:
session.user_shell(), detected at session startupshell parameter in tool argumentsThe get_shell_by_model_provided_path function parses shell paths like /bin/bash, powershell, or cmd and constructs appropriate Shell instances with ShellType detection.
Sources: codex-rs/core/src/tools/handlers/unified_exec.rs249-272 codex-rs/core/src/tools/handlers/shell.rs79-105
Login shell behavior is controlled by three factors:
turn.tools_config.allow_login_shell from configurationlogin field in tool argumentsNoneThe resolution logic:
if login == Some(true) && !allow_login_shell:
return Error("login shell is disabled by config")
else if login == Some(value):
use_login_shell = value
else:
use_login_shell = allow_login_shell
Login shells source profile files (.bash_profile, .zshrc, etc.) and use the -l flag on Unix systems or -NoProfile on PowerShell to maintain consistency.
Sources: codex-rs/core/src/tools/handlers/shell.rs66-77 codex-rs/core/src/tools/handlers/unified_exec.rs261-269
The Shell::derive_exec_args method produces platform-specific command vectors:
| Shell Type | Login | Non-Login |
|---|---|---|
| Bash | ["/bin/bash", "-lc", cmd] | ["/bin/bash", "-c", cmd] |
| Zsh | ["/bin/zsh", "-lc", cmd] | ["/bin/zsh", "-c", cmd] |
| PowerShell | ["powershell", "-NoProfile", "-Command", cmd] | ["powershell", "-NoProfile", "-Command", cmd] |
| Cmd | ["cmd", "/c", cmd] | ["cmd", "/c", cmd] |
These vectors are recognized by is_known_safe_command when the command itself contains only safe operations (ls, pwd, echo, etc.).
Sources: codex-rs/core/src/tools/handlers/shell.rs394-435 codex-rs/core/src/tools/handlers/unified_exec.rs311-345
The exec_command tool applies additional environment variables to create a consistent execution environment:
This environment suppresses ANSI color codes, configures UTF-8 encoding, and sets minimal pagers to produce clean, parseable output.
Sources: codex-rs/core/src/unified_exec/process_manager.rs55-66
The shell tool uses structured output formatting via format_exec_output_for_model_structured:
Exit code: <exit_code>
stdout:
<stdout_text>
stderr:
<stderr_text>
Duration: <duration> seconds
This format provides clear separation of stdout/stderr and includes execution metadata.
Sources: codex-rs/core/src/tools/events.rs286-296
The shell_command tool uses freeform formatting optimized for LLM consumption:
<aggregated_output>
Stderr is only included if the command fails (exit code != 0):
<aggregated_output>
<stderr_text>
This format reduces token usage for successful commands and provides full context on failures.
Sources: codex-rs/core/src/tools/events.rs286-296
The exec_command and write_stdin tools use a specialized format via format_response:
Chunk ID: <chunk_id>
Wall time: <seconds> seconds
Process exited with code <exit_code>
Original token count: <token_count>
Output:
<output>
Or for running processes:
Chunk ID: <chunk_id>
Wall time: <seconds> seconds
Process running with session ID <process_id>
Original token count: <token_count>
Output:
<output>
The chunk_id is a 6-character hex identifier for correlating multi-part outputs, and Original token count indicates truncation when output exceeds max_output_tokens.
Sources: codex-rs/core/src/tools/handlers/unified_exec.rs274-301
All shell execution handlers intercept commands that match the apply_patch pattern:
When detected, the handler:
ApplyPatchHandler for proper patch applicationThis ensures file modifications go through the patch approval system regardless of which tool the model invokes.
Sources: codex-rs/core/src/tools/handlers/apply_patch.rs192-274 codex-rs/core/src/tools/handlers/shell.rs294-308 codex-rs/core/src/tools/handlers/unified_exec.rs171-185
All handlers enforce approval policy constraints before execution. Commands requesting escalated sandbox permissions must use AskForApproval::OnRequest:
This prevents the model from circumventing approval requirements by requesting dangerous permissions in stricter approval modes.
Sources: codex-rs/core/src/tools/handlers/shell.rs279-292 codex-rs/core/src/tools/handlers/unified_exec.rs153-164
The orchestrator handles:
Sources: codex-rs/core/src/tools/handlers/shell.rs254-368 codex-rs/core/src/tools/handlers/unified_exec.rs108-246
All shell handlers use ToolEmitter to emit standardized events:
Shell and ShellCommand Events:
ExecCommandBegin - Command start with parsed command, CWD, and sourceExecCommandEnd - Command completion with stdout, stderr, exit code, duration, and statusUnified Exec Events:
ExecCommandBegin - Includes optional process_id for persistent sessionsExecCommandOutputDelta - Streaming output chunks during execution (up to MAX_EXEC_OUTPUT_DELTAS_PER_CALL per process)TerminalInteraction - Emitted for write_stdin calls with input echoExecCommandEnd - Final aggregated output and exit code (may emit in background for long-lived processes)The emitter abstracts event construction and ensures consistent formatting across all execution types.
Sources: codex-rs/core/src/tools/events.rs89-357 codex-rs/core/src/tools/handlers/unified_exec.rs221-231
All handlers implement is_mutating to classify commands as safe or potentially dangerous:
The is_known_safe_command heuristic recognizes common read-only commands like:
ls, pwd, echo, catgit status, git log, git diffbash -c ls, zsh -lc pwd, etc.Non-safe commands trigger stricter approval requirements in approval policies like UnlessTrusted.
Sources: codex-rs/core/src/tools/handlers/shell.rs120-131 codex-rs/core/src/tools/handlers/shell.rs193-212 codex-rs/core/src/tools/handlers/unified_exec.rs85-106
Shell handlers inject dependency environment variables from session.dependency_env():
Dependencies are managed tools (like Node.js, Python) that Codex has installed or configured. Environment variables provide PATH updates and tool-specific configuration, allowing commands to reference managed binaries without absolute paths.
Sources: codex-rs/core/src/tools/handlers/shell.rs267-277
The test suite validates shell execution across multiple dimensions:
Command Construction Tests:
Unified Exec Integration Tests:
Key Test Utilities:
test_codex() - Builder for test session configurationsmount_sse_sequence() - Mock model responseswait_for_event() - Event stream assertionsparse_unified_exec_output() - Response format parsingSources: codex-rs/core/src/tools/handlers/shell.rs370-550 codex-rs/core/src/tools/handlers/unified_exec.rs303-391 codex-rs/core/tests/suite/unified_exec.rs
Refresh this wiki