Download codex-rs/core/src/unified_exec/mod.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 8.08 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core/src/unified_exec/mod.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/core/src/unified_exec/mod.rs
-
curl -L -o mod.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core/src/unified_exec/mod.rs
8.08 kB
| //! Unified Exec: interactive process execution orchestrated with approvals + sandboxing. | |
| //! | |
| //! Responsibilities | |
| //! - Manages interactive processes (create, reuse, buffer output with caps). | |
| //! - Supports completion-only calls that terminate on timeout or cancellation. | |
| //! - Uses the shared ToolOrchestrator to handle approval, sandbox selection, and | |
| //! retry semantics in a single, descriptive flow. | |
| //! - Spawns the PTY from a sandbox-transformed `ExecRequest`; on sandbox denial, | |
| //! retries without sandbox when policy allows (no re‑prompt thanks to caching). | |
| //! - Uses the shared `is_likely_sandbox_denied` heuristic to keep denial messages | |
| //! consistent with other exec paths. | |
| //! | |
| //! Flow at a glance (open process) | |
| //! 1) Build a small request `{ command, cwd }`. | |
| //! 2) Orchestrator: approval (bypass/cache/prompt) → select sandbox → run. | |
| //! 3) Runtime: transform `SandboxTransformRequest` -> `ExecRequest` -> spawn PTY. | |
| //! 4) If denial, orchestrator retries with `SandboxType::None`. | |
| //! 5) Process handle is returned with streaming output + metadata. | |
| //! | |
| //! This keeps policy logic and user interaction centralized while the PTY/process | |
| //! concerns remain isolated here. The implementation is split between: | |
| //! - `process.rs`: PTY process lifecycle + output buffering. | |
| //! - `process_state.rs`: shared exit/failure state for local and remote processes. | |
| //! - `process_manager.rs`: orchestration (approvals, sandboxing, reuse) and request handling. | |
| use std::collections::HashMap; | |
| use std::collections::HashSet; | |
| use std::sync::Arc; | |
| use std::sync::Weak; | |
| use codex_network_proxy::NetworkProxy; | |
| use codex_protocol::models::AdditionalPermissionProfile; | |
| use codex_tools::UnifiedExecShellMode; | |
| use codex_utils_output_truncation::TruncationPolicy; | |
| use codex_utils_path_uri::PathUri; | |
| use rand::Rng; | |
| use rand::rng; | |
| use tokio::sync::Mutex; | |
| use tokio_util::sync::CancellationToken; | |
| use crate::sandboxing::SandboxPermissions; | |
| use crate::session::session::Session; | |
| use crate::session::step_context::StepContext; | |
| use crate::session::turn_context::TurnContext; | |
| use crate::session::turn_context::TurnEnvironment; | |
| use crate::shell::ShellType; | |
| use crate::tools::network_approval::DeferredNetworkApproval; | |
| use codex_core_plugins::PluginMetricsSidecar; | |
| mod async_watcher; | |
| mod errors; | |
| mod head_tail_buffer; | |
| mod oneshot; | |
| mod process; | |
| mod process_manager; | |
| mod process_state; | |
| mod shell_snapshot; | |
| mod stdin_approval; | |
| pub(crate) fn set_deterministic_process_ids_for_tests(enabled: bool) { | |
| process_manager::set_deterministic_process_ids_for_tests(enabled); | |
| } | |
| pub(crate) use errors::UnifiedExecError; | |
| pub(crate) use process::NoopSpawnLifecycle; | |
| pub(crate) use process::SpawnLifecycle; | |
| pub(crate) use process::SpawnLifecycleHandle; | |
| pub(crate) use process::UnifiedExecProcess; | |
| pub(crate) use stdin_approval::TerminalPermissions; | |
| pub(crate) use stdin_approval::TerminalSandboxSource; | |
| pub(crate) const MIN_YIELD_TIME_MS: u64 = 250; | |
| pub(crate) const WINDOWS_INITIAL_EXEC_YIELD_TIME_FLOOR_MS: u64 = 10_000; | |
| // Minimum yield time for an empty `write_stdin`. | |
| pub(crate) const MIN_EMPTY_YIELD_TIME_MS: u64 = 5_000; | |
| pub(crate) const MAX_YIELD_TIME_MS: u64 = 30_000; | |
| pub(crate) const DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS: u64 = 300_000; | |
| pub(crate) const DEFAULT_MAX_OUTPUT_TOKENS: usize = 10_000; | |
| pub(crate) const UNIFIED_EXEC_OUTPUT_MAX_BYTES: usize = 1024 * 1024; // 1 MiB | |
| pub(crate) const UNIFIED_EXEC_OUTPUT_MAX_TOKENS: usize = UNIFIED_EXEC_OUTPUT_MAX_BYTES / 4; | |
| pub(crate) const MAX_UNIFIED_EXEC_PROCESSES: usize = 64; | |
| const MAX_TRACE_ID_BYTES: usize = 256; | |
| fn trace_id(id: &str) -> Option<&str> { | |
| (!id.is_empty() && id.len() <= MAX_TRACE_ID_BYTES).then_some(id) | |
| } | |
| pub(crate) struct UnifiedExecContext { | |
| pub session: Arc<Session>, | |
| pub step_context: Arc<StepContext>, | |
| pub cancellation_token: CancellationToken, | |
| pub call_id: String, | |
| } | |
| impl UnifiedExecContext { | |
| pub fn new( | |
| session: Arc<Session>, | |
| step_context: Arc<StepContext>, | |
| cancellation_token: CancellationToken, | |
| call_id: String, | |
| ) -> Self { | |
| Self { | |
| session, | |
| step_context, | |
| cancellation_token, | |
| call_id, | |
| } | |
| } | |
| } | |
| pub(crate) struct ExecCommandRequest { | |
| pub command: Vec<String>, | |
| pub shell_type: ShellType, | |
| pub hook_command: String, | |
| pub process_id: i32, | |
| pub yield_time_ms: u64, | |
| pub max_output_tokens: Option<usize>, | |
| pub cwd: PathUri, | |
| pub sandbox_cwd: PathUri, | |
| pub turn_environment: TurnEnvironment, | |
| pub shell_mode: UnifiedExecShellMode, | |
| pub network: Option<NetworkProxy>, | |
| pub tty: bool, | |
| pub sandbox_permissions: SandboxPermissions, | |
| pub additional_permissions: Option<AdditionalPermissionProfile>, | |
| pub additional_permissions_preapproved: bool, | |
| pub justification: Option<String>, | |
| pub prefix_rule: Option<Vec<String>>, | |
| } | |
| pub(crate) struct WriteStdinRequest<'a> { | |
| pub process_id: i32, | |
| pub input: &'a str, | |
| pub yield_time_ms: u64, | |
| pub max_output_tokens: Option<usize>, | |
| pub truncation_policy: TruncationPolicy, | |
| pub interaction_event: Option<WriteStdinInteractionEvent<'a>>, | |
| } | |
| pub(crate) struct WriteStdinInteractionEvent<'a> { | |
| pub session: &'a Arc<Session>, | |
| pub turn: &'a Arc<TurnContext>, | |
| } | |
| impl std::fmt::Debug for WriteStdinInteractionEvent<'_> { | |
| fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| formatter.write_str("WriteStdinInteractionEvent") | |
| } | |
| } | |
| pub(crate) struct ProcessStore { | |
| processes: HashMap<i32, ProcessEntry>, | |
| reserved_process_ids: HashSet<i32>, | |
| } | |
| impl ProcessStore { | |
| fn remove(&mut self, process_id: i32) -> Option<ProcessEntry> { | |
| self.reserved_process_ids.remove(&process_id); | |
| self.processes.remove(&process_id) | |
| } | |
| } | |
| pub(crate) struct UnifiedExecProcessManager { | |
| process_store: Mutex<ProcessStore>, | |
| max_write_stdin_yield_time_ms: u64, | |
| } | |
| impl UnifiedExecProcessManager { | |
| pub(crate) fn new(max_write_stdin_yield_time_ms: u64) -> Self { | |
| Self { | |
| process_store: Mutex::new(ProcessStore::default()), | |
| max_write_stdin_yield_time_ms: max_write_stdin_yield_time_ms | |
| .max(MIN_EMPTY_YIELD_TIME_MS), | |
| } | |
| } | |
| } | |
| impl Default for UnifiedExecProcessManager { | |
| fn default() -> Self { | |
| Self::new(DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS) | |
| } | |
| } | |
| struct ProcessEntry { | |
| process: Arc<UnifiedExecProcess>, | |
| plugin_metrics_sidecar: Option<SharedPluginMetricsSidecar>, | |
| call_id: String, | |
| process_id: i32, | |
| cwd: PathUri, | |
| initial_exec_command_active: Arc<std::sync::atomic::AtomicBool>, | |
| hook_command: String, | |
| tty: bool, | |
| environment_id: String, | |
| permissions: TerminalPermissions, | |
| network_approval: Option<DeferredNetworkApproval>, | |
| session: Weak<Session>, | |
| last_used: tokio::time::Instant, | |
| } | |
| type SharedPluginMetricsSidecar = Arc<std::sync::Mutex<Option<PluginMetricsSidecar>>>; | |
| fn take_plugin_metrics_sidecar( | |
| sidecar: &SharedPluginMetricsSidecar, | |
| ) -> Option<PluginMetricsSidecar> { | |
| sidecar | |
| .lock() | |
| .unwrap_or_else(std::sync::PoisonError::into_inner) | |
| .take() | |
| } | |
| pub(crate) fn clamp_yield_time(yield_time_ms: u64) -> u64 { | |
| let yield_time_ms = if cfg!(windows) { | |
| yield_time_ms.max(WINDOWS_INITIAL_EXEC_YIELD_TIME_FLOOR_MS) | |
| } else { | |
| yield_time_ms | |
| }; | |
| yield_time_ms.clamp(MIN_YIELD_TIME_MS, MAX_YIELD_TIME_MS) | |
| } | |
| pub(crate) fn resolve_max_tokens(max_tokens: Option<usize>) -> usize { | |
| max_tokens.unwrap_or(DEFAULT_MAX_OUTPUT_TOKENS) | |
| } | |
| pub(crate) fn format_output_omission_marker(omitted_bytes: usize) -> String { | |
| format!("... {omitted_bytes} bytes omitted ...") | |
| } | |
| pub(crate) fn generate_chunk_id() -> String { | |
| let mut rng = rng(); | |
| (0..6) | |
| .map(|_| format!("{:x}", rng.random_range(0..16))) | |
| .collect() | |
| } | |
| mod process_tests; | |
| mod tests; | |