This document describes the tool orchestration layer that coordinates tool execution with approval checks, sandbox selection, and retry logic. The ToolOrchestrator provides a unified interface for tool handlers to execute commands while respecting approval policies and sandbox constraints.
For information about tool registration and configuration, see Tool Registry and Configuration. For sandbox implementation details, see Sandboxing Implementation. For tool event emission, see Tool Event Emission and Output.
Tool orchestration separates policy enforcement (approval, sandboxing) from execution (runtime process management). The orchestrator acts as a mediator between tool handlers and runtimes, providing:
SandboxType::None when sandbox denial occursThe orchestration pattern enables tool handlers to focus on command construction while the orchestrator handles cross-cutting concerns.
Sources: codex-rs/core/src/unified_exec/mod.rs1-22 codex-rs/core/src/tools/handlers/shell.rs229-336
Sources: codex-rs/core/src/tools/handlers/shell.rs318-360 codex-rs/core/src/unified_exec/process_manager.rs157-295 codex-rs/core/src/tools/handlers/apply_patch.rs118-169
Tool handlers follow a consistent pattern when using the orchestrator:
Sources: codex-rs/core/src/tools/handlers/shell.rs229-336 codex-rs/core/src/unified_exec/process_manager.rs115-239
Tool specifications include parameters that control approval behavior:
| Parameter | Type | Description | Required When |
|---|---|---|---|
sandbox_permissions | SandboxPermissions | Whether to use default sandbox or request escalation | Always (defaults to UseDefault) |
justification | Option<String> | User-facing explanation for escalation request | sandbox_permissions = RequireEscalated |
prefix_rule | Option<Vec<String>> | Suggested command prefix pattern for future auto-approval | sandbox_permissions = RequireEscalated and RequestRule feature enabled |
The SandboxPermissions enum (defined in sandboxing.rs) controls escalation requests:
When requires_escalated_permissions() returns true, the tool handler validates that the approval policy allows escalation before proceeding.
Sources: codex-rs/core/src/sandboxing.rs codex-rs/core/src/tools/spec.rs180-219
Sources: codex-rs/core/src/tools/spec.rs194-266
The ExecPolicy service (session.services.exec_policy) creates approval requirements based on command characteristics:
Sources: codex-rs/core/src/tools/handlers/shell.rs318-330 codex-rs/core/src/exec_policy.rs
Before orchestration begins, tool handlers validate that escalated permissions are compatible with the approval policy:
This guard prevents the model from requesting escalation when:
approval_policy = Never (never prompt for approval, auto-approve safe commands only)approval_policy = OnFailure (prompt only after sandbox denial)approval_policy = UnlessTrusted (prompt unless project is trusted)Only AskForApproval::OnRequest allows the model to explicitly request escalation via sandbox_permissions = "require_escalated".
Sources: codex-rs/core/src/tools/handlers/shell.rs279-292 codex-rs/core/src/tools/handlers/unified_exec.rs153-164
| Policy | Behavior | Escalation Allowed |
|---|---|---|
Never | No approval prompts, auto-approve safe commands only | ❌ No |
OnRequest | Prompt only when model requests escalation | ✅ Yes |
OnFailure | Prompt after sandbox denial occurs | ❌ No (handled by retry logic) |
UnlessTrusted | Prompt unless project is in trusted directory | ❌ No (conditional auto-approval) |
The OnRequest policy is the only one that allows explicit escalation via sandbox_permissions = "require_escalated" in tool calls.
Sources: codex-rs/core/src/protocol.rs codex-rs/core/src/tools/handlers/shell.rs279-292 codex-rs/core/tests/suite/prompt_caching.rs256-438
The orchestrator caches approval decisions to avoid redundant prompts:
["git", "pull"])Caching applies for the duration of a session and prevents re-prompting for:
Sources: codex-rs/core/src/exec_policy.rs codex-rs/core/src/tools/spec.rs179-189
When the model requests escalation, it can suggest a prefix_rule to enable auto-approval for similar future commands:
If approved, future commands starting with ["git", "pull"] are automatically approved without re-prompting.
Prefix rules match command arguments from the start:
["git", "pull"] matches ["git", "pull", "origin", "main"] ✅["git", "pull"] does not match ["git", "push"] ❌["git"] matches any git command ✅ (too broad, discouraged)Sources: codex-rs/core/src/tools/spec.rs179-189 codex-rs/core/tests/suite/prompt_caching.rs
The orchestrator automatically retries with SandboxType::None when:
is_likely_sandbox_denied())SandboxPolicy allows fallback to no sandboxThis pattern enables seamless fallback when sandboxed execution fails due to permission constraints.
Sources: codex-rs/core/src/unified_exec/mod.rs6-17 codex-rs/core/src/tools/orchestrator.rs codex-rs/core/src/sandboxing.rs
The system uses is_likely_sandbox_denied() to identify sandbox denials in command output by matching common error patterns:
When these patterns appear in stderr or stdout and the exit code is non-zero, the orchestrator classifies it as a sandbox denial and triggers retry logic if the sandbox policy allows.
The unified exec implementation includes an additional check via process.check_for_sandbox_denial_with_text() to validate denial before releasing process resources.
Sources: codex-rs/core/src/sandboxing.rs codex-rs/core/src/unified_exec/mod.rs8-9 codex-rs/core/src/unified_exec/process_manager.rs254
The ToolCtx struct (defined in tools/sandboxing.rs) provides orchestration context:
This context enables the orchestrator to:
session.services.exec_policysession.send_event()turn.approval_policy.value() and turn.sandbox_policy.get()turn.cwd and turn.resolve_path()Sources: codex-rs/core/src/tools/sandboxing.rs codex-rs/core/src/tools/handlers/shell.rs345-351
Complete flow for shell command execution:
Sources: codex-rs/core/src/tools/handlers/shell.rs254-367
Unified exec uses orchestration for the initial process spawn, then manages long-running processes separately:
The key differences from shell execution:
ProcessStore and can be reused via write_stdinwrite_stdin calls can write to the PTY and collect additional outputspawn_exit_watcher() emits ExecCommandEnd when the PTY exitsstart_streaming_output() emits ExecCommandOutputDelta events in real-timeSources: codex-rs/core/src/unified_exec/process_manager.rs157-295 codex-rs/core/src/tools/handlers/unified_exec.rs108-245 codex-rs/core/src/unified_exec/async_watcher.rs39-140
The prefix_rule parameter in tool specifications is controlled by the Feature::RequestRule flag (currently Stage::Removed as of the codebase):
When this feature was active, it allowed models to suggest prefix rules for auto-approval. The feature has been removed but the parameter definitions remain in tool specs for backward compatibility.
Tool handlers would check:
Sources: codex-rs/core/src/features.rs532-536 codex-rs/core/src/tools/spec.rs206-216
Tool orchestration coordinates event emission at key points:
| Event | Timing | Purpose |
|---|---|---|
ExecCommandBegin | Before orchestrator.run() | Notify clients execution starting |
ExecCommandOutputDelta | During execution | Stream output in real-time |
ExecCommandEnd | After orchestrator.run() | Report exit code and final output |
ApprovalRequest | When approval needed | Prompt user for permission |
ApprovalResponse | After user responds | Record approval decision |
The ToolEmitter abstracts event emission for different tool types (Shell, UnifiedExec, ApplyPatch).
Sources: codex-rs/core/src/tools/events.rs50-335 codex-rs/core/src/tools/handlers/shell.rs287-330
The test suite validates orchestration behavior:
Tests cover:
Sources: codex-rs/core/tests/suite/prompt_caching.rs256-438 codex-rs/core/tests/suite/unified_exec.rs158-284
The tool orchestration layer provides:
All tool execution flows through the orchestrator, ensuring uniform policy enforcement and error handling across shell, unified exec, and apply patch tools.
Sources: codex-rs/core/src/tools/handlers/shell.rs229-336 codex-rs/core/src/unified_exec/mod.rs1-22 codex-rs/core/src/unified_exec/process_manager.rs115-239
Refresh this wiki