工具运行时抽象
Codex 当前没有独立的 ShellRuntime。模型看到的 exec_command 虽然仍是 shell 风格的命令工具,但它已经统一进入 UnifiedExecProcessManager 和 UnifiedExecRuntime:一次性命令、后台进程、TTY、远端 executor、shell snapshot 与 zsh-fork 共享同一条 runtime 主线。工具 runtime 层剩下两个核心实现:负责进程启动的 Unified Exec,以及直接调用 environment filesystem 的 Apply Patch。
这两个 runtime 的输出完全不同。Unified Exec 返回刚启动的进程和 plugin metrics sidecar,真正的输出收集、后台保存与 Deferred network approval 收尾发生在 process manager;Apply Patch 返回 ExecToolCallOutput 与累计 AppliedPatchDelta。它们共享的是审批、sandbox 选择和 denial retry,不共享资源生命周期。
本文面向已经读过ToolOrchestrator执行流程、 工具审批架构和SandboxPolicy选择的读者。读完后,读者应能解释:
- 为什么 shell 工具存在,但
ShellRuntime不再存在; - 为什么
sandbox_requested = true时 hostSandboxType仍可能是 None; - 为什么 Unified Exec 的 runtime 输出不是最终工具文本;
- 为什么 Apply Patch 失败后仍要保存 delta。
1. Runtime边界
1.1 两种实现
runtimes 模块现在只公开 Apply Patch、Unified Exec 和 zsh-fork 辅助模块。zsh-fork 不是第三种 ToolRuntime,而是 Unified Exec 在满足条件时选择的一种启动 backend。shell snapshot、PATH prepend、proxy 环境清理等 shell 公共逻辑则留在 runtimes/mod.rs,由 Unified Exec 调用。
源码位置:codex-rs/core/src/tools/runtimes/mod.rs
pub(crate) mod apply_patch;
pub(crate) mod unified_exec;
pub(crate) mod zsh_fork;源码位置:codex-rs/core/src/tools/runtimes/unified_exec.rs :: UnifiedExecRuntime
pub struct UnifiedExecRuntime<'a> {
manager: &'a UnifiedExecProcessManager,
shell_mode: UnifiedExecShellMode,
}
pub(crate) struct UnifiedExecAttempt {
pub(crate) process: UnifiedExecProcess,
pub(crate) metrics_sidecar: Option<PluginMetricsSidecar>,
}源码位置:codex-rs/core/src/tools/runtimes/apply_patch.rs :: ApplyPatchRuntime
#[derive(Default)]
pub struct ApplyPatchRuntime {
committed_delta: AppliedPatchDelta,
}
pub struct ApplyPatchRuntimeOutput {
pub exec_output: ExecToolCallOutput,
pub delta: AppliedPatchDelta,
}这组类型已经说明所有权差异:Unified Exec 的成功值携带 live process,Apply Patch 的成功值携带已完成的文件变化快照。
1.2 三层契约
Approvable、Sandboxable 和 ToolRuntime 分别回答批准、sandbox 偏好和实际执行。当前 ToolRuntime 还增加了 uses_executor_managed_process_sandbox:runtime 可以告诉 Orchestrator,隔离由远端 executor 或 executor-side snapshot 负责,host 不应再套一层平台 wrapper。
相关源码:
codex-rs/core/src/tools/sandboxing.rs :: Approvablecodex-rs/core/src/tools/sandboxing.rs :: Sandboxablecodex-rs/core/src/tools/sandboxing.rs :: ToolRuntime
pub(crate) trait ToolRuntime<Req, Out>: Approvable<Req> + Sandboxable {
fn turn_environment<'a>(&self, req: &'a Req) -> &'a TurnEnvironment;
fn uses_executor_managed_process_sandbox(&self, _req: &Req) -> bool {
false
}
fn network_approval_spec(
&self,
_req: &Req,
_ctx: &ToolCtx,
) -> Option<NetworkApprovalSpec> {
None
}
fn sandbox_cwd<'a>(&self, _req: &'a Req) -> Option<&'a PathUri> {
None
}
async fn run(
&mut self,
req: &Req,
attempt: &SandboxAttempt<'_>,
ctx: &ToolCtx,
) -> Result<Out, ToolError>;
}1.3 调用上下文
ToolCtx 持有完整 StepContext 与 cancellation token;runtime 可以读取当前 Turn 的环境、审批路由和配置,也能把调用级 取消传到下游。但 runtime 仍不能修改 Orchestrator 已经选定的权限。
源码位置:codex-rs/core/src/tools/sandboxing.rs :: ToolCtx
pub(crate) struct ToolCtx {
pub session: Arc<Session>,
pub step_context: Arc<StepContext>,
pub cancellation_token: CancellationToken,
pub call_id: String,
pub tool_name: ToolName,
}2. Attempt模型
2.1 三种状态
SandboxAttempt 同时保留三个容易混淆的状态:
sandbox_requested:策略是否要求隔离;sandbox:当前 host 实际使用哪个 wrapper;exec_server_permissions:executor 应使用的 canonical profile。
源码位置:codex-rs/core/src/tools/sandboxing.rs :: SandboxAttempt
pub(crate) struct SandboxAttempt<'a> {
pub sandbox: SandboxType,
pub sandbox_requested: bool,
pub permissions: &'a PermissionProfile,
pub exec_server_permissions: &'a PermissionProfile,
pub enforce_managed_network: bool,
pub(crate) manager: &'a SandboxManager,
pub(crate) sandbox_cwd: &'a PathUri,
pub(crate) workspace_roots: &'a [PathUri],
pub codex_linux_sandbox_exe: Option<&'a PathBuf>,
pub use_legacy_landlock: bool,
pub windows_sandbox_level: WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
pub network_denial_cancellation_token: Option<CancellationToken>,
pub(crate) network_proxy: Option<&'a NetworkProxy>,
}远端 Unified Exec 和 remote Apply Patch 的典型组合是:sandbox_requested = true,host sandbox = None, exec_server_permissions 保留 symbolic workspace roots。None 只代表 host 不包装,不代表权限被禁用。
2.2 Host出口
本地进程使用 env_for。它选择 attempt 的 execution-only proxy,调用 SandboxManager::transform 生成 host argv,再把 workspace roots 转为 native path,构造 Core ExecRequest。
源码位置:codex-rs/core/src/tools/sandboxing.rs :: SandboxAttempt::env_for
let network = self.network_proxy(network);
let request = self.manager.transform(SandboxTransformRequest {
command,
permissions: self.permissions,
sandbox: self.sandbox,
enforce_managed_network: self.enforce_managed_network,
environment_id,
network,
sandbox_policy_cwd: self.sandbox_cwd,
codex_linux_sandbox_exe: self
.codex_linux_sandbox_exe
.map(PathBuf::as_path),
use_legacy_landlock: self.use_legacy_landlock,
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
})?;2.3 Executor出口
远端或 executor-side snapshot 使用 env_for_exec_server。host 强制传入 SandboxType::None,避免把 Seatbelt/Linux helper argv 发给另一种操作系统;如果 sandbox_requested 为 true,再单独附加 FileSystemSandboxContext 和 managed network 标志。
源码位置:codex-rs/core/src/tools/sandboxing.rs :: SandboxAttempt::env_for_exec_server
let request = self.manager.transform(SandboxTransformRequest {
command,
permissions: self.permissions,
sandbox: SandboxType::None,
enforce_managed_network: self.enforce_managed_network,
environment_id: None,
network: None,
sandbox_policy_cwd: self.sandbox_cwd,
codex_linux_sandbox_exe: None,
use_legacy_landlock: self.use_legacy_landlock,
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
})?;
if self.sandbox_requested {
exec_request.exec_server_sandbox = Some(FileSystemSandboxContext {
permissions: exec_server_permissions.into(),
cwd: Some(exec_request.windows_sandbox_policy_cwd.clone()),
workspace_roots: self.workspace_roots.to_vec(),
windows_sandbox_level: executor_windows_sandbox_level(
self.windows_sandbox_level,
self.sandbox_cwd,
),
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
windows_sandbox_proxy_settings_mode: None,
use_legacy_landlock: self.use_legacy_landlock,
});
exec_request.exec_server_enforce_managed_network =
self.enforce_managed_network;
}3. 编排职责
3.1 构造Attempt
Orchestrator 从 runtime 取得 environment 和 owner policy。executor-managed request 不在 host 物化 workspace roots;owner network policy 禁止 escalation 绕过。最终 concrete host sandbox 只有在策略要求且不是 executor-managed 时才选择。
源码位置:codex-rs/core/src/tools/orchestrator.rs :: ToolOrchestrator::run
let environment = tool.turn_environment(req);
let owner_network_policy = environment.config().network_policy.is_some();
let executor_managed_process_sandbox =
tool.uses_executor_managed_process_sandbox(req);
let permission_profile = environment.permission_profile();
let permissions = if executor_managed_process_sandbox {
permission_profile.clone()
} else {
environment.permission_profile_with_workspace_roots()
};
let initial_sandbox = if sandbox_requested
&& !executor_managed_process_sandbox
{
self.sandbox.select_initial(
&permissions,
sandbox_preference,
turn_ctx.windows_sandbox_level,
managed_network_active,
)
} else {
SandboxType::None
};3.2 注入网络审批
run_attempt 在调用 runtime 前创建 ActiveNetworkApproval,把 execution proxy 与 cancellation token 写入一个派生 attempt。 当前 runtime 不再选择不同的收尾模式;工具成功后活动审批统一转换成 DeferredNetworkApproval,工具失败则在 Orchestrator 内立即 finish。
源码位置:codex-rs/core/src/tools/orchestrator.rs :: ToolOrchestrator::run_attempt
let network_approval = begin_network_approval(
&tool_ctx.session,
&tool_ctx.step_context.turn,
attempt.enforce_managed_network,
network_approval_spec,
)
.await?;
let run_result = tool
.run(req, &attempt_with_network_approval, &attempt_tool_ctx)
.await;
let Some(network_approval) = network_approval else {
return (run_result, None);
};
let deferred = network_approval.into_deferred();3.3 Denial重试
只有 ToolError::Codex 中的 SandboxErr::Denied 才进入 retry。ToolError::Rejected 表示请求或环境在 spawn 前就不成立, 不会通过换 sandbox 重试。第二次 attempt 仍由 Orchestrator 构造,runtime 看不到“第几次尝试”标志,只消费新的 attempt。
4. exec_command入口
4.1 Handler解析
模型调用 exec_command 后,handler 先解析 environment、workdir、shell、TTY、additional permissions 和 approval policy, 再向 process manager 申请 process id。远端 cwd 可以保持 PathUri;只有 host 确实需要本地平台 sandbox 时,才要求 cwd 使用 host native path convention。
源码位置:codex-rs/core/src/tools/handlers/unified_exec/exec_command.rs :: ExecCommandHandler::run
let requires_host_native_cwd = !environment.is_remote()
&& SandboxManager::new().select_initial(
turn_environment.permission_profile(),
SandboxablePreference::Auto,
turn.windows_sandbox_level,
turn.network.is_some(),
) != SandboxType::None;
let cwd_uses_native_convention =
cwd.infer_path_convention() == Some(PathConvention::native());
let native_cwd = match cwd.to_abs_path() {
Ok(cwd) if cwd_uses_native_convention => Some(cwd),
_ if !requires_host_native_cwd => None,
Err(err) => return Err(FunctionCallError::RespondToModel(err.to_string())),
Ok(_) => return Err(FunctionCallError::RespondToModel(/* path mismatch */)),
};这一步解释了“shell 工具为何没有 ShellRuntime”:shell 参数、默认 shell 和 command argv 已在 handler 中解析,实际执行全部交给 Unified Exec。
4.2 Manager装配
open_session_with_sandbox 构建 shell environment、Session/Thread/permission profile 环境变量、exec-server env policy、shell snapshot request 和 exec approval requirement,然后创建 UnifiedExecRequest 与 ToolCtx,最后进入 Orchestrator。
源码位置:codex-rs/core/src/unified_exec/process_manager.rs :: UnifiedExecProcessManager::open_session_with_sandbox
let shell_snapshot = shell_snapshot_request(request, &cwd, context);
let mut orchestrator = ToolOrchestrator::new();
let mut runtime = UnifiedExecRuntime::new(self, request.shell_mode.clone());
let req = UnifiedExecRequest {
command: request.command.clone(),
shell_type: request.shell_type,
hook_command: request.hook_command.clone(),
process_id: request.process_id,
cwd,
sandbox_cwd: request.sandbox_cwd.clone(),
turn_environment: request.turn_environment.clone(),
env,
exec_server_env_config: Some(exec_server_env_config),
shell_snapshot,
explicit_env_overrides,
network: request.network.clone(),
tty: request.tty,
sandbox_permissions: request.sandbox_permissions,
additional_permissions: request.additional_permissions.clone(),
justification: request.justification.clone(),
exec_approval_requirement,
};5. Unified Exec
5.1 Request与审批
UnifiedExecRequest 已包含原 ShellRuntime 曾经拥有的字段:command、shell type、env、snapshot、network、sandbox permissions、 additional permissions 和 approval requirement。审批 key 还包含 environment、cwd、TTY 与权限,避免不同 executor 复用同一个 批准。
源码位置:codex-rs/core/src/tools/runtimes/unified_exec.rs :: UnifiedExecRequest
pub struct UnifiedExecRequest {
pub command: Vec<String>,
pub shell_type: ShellType,
pub hook_command: String,
pub process_id: i32,
pub cwd: PathUri,
pub sandbox_cwd: PathUri,
pub turn_environment: TurnEnvironment,
pub env: HashMap<String, String>,
pub exec_server_env_config: Option<ExecServerEnvConfig>,
pub shell_snapshot: Option<ShellSnapshotRequest>,
pub explicit_env_overrides: HashMap<String, String>,
pub network: Option<NetworkProxy>,
pub tty: bool,
pub sandbox_permissions: SandboxPermissions,
pub additional_permissions: Option<AdditionalPermissionProfile>,
pub justification: Option<String>,
pub exec_approval_requirement: ExecApprovalRequirement,
}5.2 Executor判定
remote environment 或存在 executor-side shell snapshot 时,Unified Exec 声明由 executor 管理 sandbox。前者由远端 OS 执行, 后者即使环境是 local,也通过 exec-server 的 snapshot 协议启动,因此不能先套 host wrapper。
源码位置:codex-rs/core/src/tools/runtimes/unified_exec.rs :: UnifiedExecRuntime::uses_executor_managed_process_sandbox
fn uses_executor_managed_process_sandbox(
&self,
req: &UnifiedExecRequest,
) -> bool {
req.turn_environment.environment.is_remote()
|| req.shell_snapshot.is_some()
}5.3 命令预处理
runtime 先保留 deny-read,按 escalation 决定是否携带 managed proxy,安装 plugin metrics sidecar 环境变量,再处理 package/zsh PATH、shell snapshot、Elevated Windows PowerShell -NoProfile 和 PowerShell UTF-8 前缀。plugin sidecar 所需文件权限与用户 additional permissions 最后通过 merge_permission_profiles 合并。
源码位置:codex-rs/core/src/tools/runtimes/unified_exec.rs :: UnifiedExecRuntime::run
let launch_sandbox_permissions = sandbox_permissions_preserving_denied_reads(
req.sandbox_permissions,
&file_system_sandbox_policy,
);
let managed_network = attempt.network_proxy(
managed_network_for_sandbox_permissions(
req.network.as_ref(),
launch_sandbox_permissions,
),
);
let env = exec_env_for_sandbox_permissions(
&req.env,
launch_sandbox_permissions,
);
let sidecar_permissions = metrics_sidecar
.as_ref()
.map(PluginMetricsSidecar::additional_permissions);
let additional_permissions = merge_permission_profiles(
req.additional_permissions.as_ref(),
sidecar_permissions.as_ref(),
);5.4 三种启动路径
Unified Exec 不是单一 spawn:
- zsh-fork 条件满足时,先由 interception backend 准备
ExecRequest; - remote 或 executor snapshot 通过
env_for_exec_server; - 普通 local direct 通过
env_for和 host platform wrapper。
zsh-fork 对 remote environment 明确拒绝;条件不满足时记录 warning 并回落到 direct execution。runtime 最终只返回 UnifiedExecAttempt,不会在这里等待完整 stdout。
5.5 进程后半程
runtime 返回后,exec_command 才开始收集初始输出。process manager 把 Deferred network approval、network denial monitor、 metrics sidecar、transcript 和 process 一起保存;若命令仍存活,write_stdin 后续继续消费同一个 entry。短命命令则在返回工具 结果前 finish network approval。
源码位置:codex-rs/core/src/unified_exec/process_manager.rs :: UnifiedExecProcessManager::exec_command
let UnifiedExecAttempt {
process,
metrics_sidecar,
} = attempt;
let process = Arc::new(process);
let network_denial_monitor = deferred_network_approval.as_ref().map(|deferred| {
terminate_process_on_network_denial(
Arc::clone(&process),
Arc::downgrade(&context.session),
deferred.clone(),
)
});
if process_started_alive {
self.store_process(
Arc::clone(&process),
context,
&request.command,
request.hook_command.clone(),
cwd.clone(),
plugin_attribution.clone(),
start,
request.process_id,
request.tty,
deferred_network_approval.clone(),
network_denial_monitor,
metrics_sidecar,
Arc::clone(&transcript),
Arc::clone(&initial_exec_command_active),
)
.await;
}6. Apply Patch
6.1 文件系统Context
Apply Patch 不创建子进程,也不调用 SandboxAttempt::env_for。它从 environment 获取 filesystem,并把 attempt 转成 FileSystemSandboxContext。判断条件是 sandbox_requested,不是 concrete SandboxType;因此 remote executor 的 SandboxType::None 仍能获得文件系统隔离上下文。
源码位置:codex-rs/core/src/tools/runtimes/apply_patch.rs :: ApplyPatchRuntime::file_system_sandbox_context_for_attempt
if !attempt.sandbox_requested {
return None;
}
let permissions = effective_permission_profile(
attempt.exec_server_permissions,
req.additional_permissions.as_ref(),
);
Some(FileSystemSandboxContext {
permissions: permissions.into(),
cwd: Some(attempt.sandbox_cwd.clone()),
workspace_roots: attempt.workspace_roots.to_vec(),
windows_sandbox_level: executor_windows_sandbox_level(
attempt.windows_sandbox_level,
attempt.sandbox_cwd,
),
windows_sandbox_private_desktop: attempt.windows_sandbox_private_desktop,
windows_sandbox_proxy_settings_mode: None,
use_legacy_landlock: attempt.use_legacy_landlock,
})6.2 Symlink策略
apply_patch_with_options 的 follow_symlinks 不是简单等于“host sandbox 是否存在”。只要策略请求了 sandbox,就允许 executor 按照其隔离机制处理链接;若策略本来不需要 sandbox,也允许普通文件系统语义。只有“策略需要 sandbox 却被绕过”的情况拒绝 跟随链接。
源码位置:codex-rs/core/src/tools/runtimes/apply_patch.rs :: ApplyPatchRuntime::run
follow_symlinks: attempt.sandbox_requested
|| !attempt.manager.should_sandbox(
attempt.permissions,
self.sandbox_preference(),
attempt.enforce_managed_network,
),6.3 累计Delta
patch engine 无论成功还是部分失败,都可能产生 delta。runtime 先从 result 中取出 delta 并追加到 committed_delta,之后才判断 输出是否像 sandbox denial。这样即使 Orchestrator 再次调用 runtime,最终事件仍能包含前一次已经发生的文件变化。
源码位置:codex-rs/core/src/tools/runtimes/apply_patch.rs :: ApplyPatchRuntime::run
let failed = result.is_err();
let delta = match result {
Ok(delta) => delta,
Err(failure) => failure.into_parts().1,
};
self.committed_delta.append(delta);
let sandbox_denied = failed
&& if attempt.sandbox == SandboxType::None {
attempt.sandbox_requested
&& is_likely_executor_managed_sandbox_denied(&output)
} else {
is_likely_sandbox_denied(attempt.sandbox, &output)
};6.4 Handler收尾
Apply Patch handler 在 Orchestrator 成功时取 output.delta;失败时也从 runtime 读取 committed_delta。因此 delta 的 owner 是 runtime,不是成功返回值。
源码位置:codex-rs/core/src/tools/handlers/apply_patch.rs
let result = orchestrator
.run(&mut runtime, &request, &tool_ctx, turn, turn.approval_policy())
.await
.map(|result| result.output);
let (result, delta) = match result {
Ok(output) => (Ok(output.exec_output), Some(output.delta)),
Err(error) => (Err(error), Some(runtime.committed_delta().clone())),
};
emitter.finish(event_ctx, result, delta.as_ref()).await7. 两种Runtime
7.1 差异表
| 维度 | Unified Exec | Apply Patch |
|---|---|---|
| Request | UnifiedExecRequest | ApplyPatchRequest |
| Output | UnifiedExecAttempt | ApplyPatchRuntimeOutput |
| 执行对象 | process / PTY / exec-server | environment filesystem |
| 审批动作 | ExecCommand | ApplyPatch |
| executor-managed | remote 或 shell snapshot | remote environment |
| network spec | 支持 | 默认无 |
| 长生命周期 owner | process manager | runtime 的 committed delta |
| denial 识别 | process/backend error | 文件系统输出特征 |
exec_command 的最终工具文本不属于 runtime 输出;Apply Patch 的最终文本在 runtime 内已经形成,但事件层仍需要 delta。泛型 ToolRuntime<Req, Out> 正是为了允许这种差异。
7.2 共同流程
7.3 新Runtime原则
新增 runtime 前应先判断:
- 是否真的需要新的
Req与Out,还是 Unified Exec 的新 backend; - 成功值是否拥有跨 handler 生命周期的资源;
- executor-managed 时哪些策略必须保持 URI-native;
- sandbox denial 后再次调用
run是否会重复不可逆副作用。
shell snapshot 和 zsh-fork 的演进说明,命令启动差异通常应进入 Unified Exec 内部 backend,而不是复制一套审批和 retry runtime。只有 Apply Patch 这种执行对象、输出所有权和失败提交语义都不同的工具,才值得保留独立 runtime。
8. 异常边界
8.1 Rejected
ToolError::Rejected 覆盖 spawn 前无法继续的条件,例如 command 为空、remote executor 不支持 proxy launch、remote 环境使用 zsh-fork,或路径无法按当前边界解释。这些错误不会触发 sandbox retry。
8.2 Deferred网络
Unified Exec 成功启动进程后,network approval 可能仍未结束。process manager 在进程退出、failure message、late denial 和 write_stdin 路径 finish 同一个 Deferred handle。runtime 只负责把 network cancellation token 放进 ExecOptions,不能提前 宣告网络结果成功。
8.3 部分副作用
Unified Exec 的失败 attempt 如果已经启动并交给 manager,必须由 manager 清理 process id、monitor 和 sidecar;Apply Patch 则必须保留已经提交的 delta。Orchestrator 的 retry 只统一策略流程,不会自动回滚 runtime 的外部副作用。
9. 测试路径
9.1 Unified Exec
当前单元测试覆盖:approval key 包含 environment id;命令 cwd 与 trusted sandbox cwd 分离;默认 timeout 与 network denial cancellation 组合;zsh-fork 保留父请求的 escalation、additional permissions 和 ExecPolicy bypass 决定。
相关源码:
codex-rs/core/src/tools/runtimes/unified_exec.rs :: approval_key_includes_environment_idcodex-rs/core/src/tools/runtimes/unified_exec.rs :: unified_exec_uses_the_trusted_sandbox_cwdcodex-rs/core/src/tools/runtimes/unified_exec.rs :: zsh_fork_first_attempt_preserves_parent_sandbox_override
9.2 Apply Patch
Apply Patch 测试覆盖 approval path URI、environment 隔离、patch cwd、executor workspace permissions,以及 file_system_sandbox_context_respects_sandbox_request。最后一项直接验证 context 的开关是 sandbox_requested,而不是 host SandboxType。
相关源码:
codex-rs/core/src/tools/runtimes/apply_patch_tests.rs :: file_system_sandbox_context_preserves_executor_workspace_permissionscodex-rs/core/src/tools/runtimes/apply_patch_tests.rs :: file_system_sandbox_context_respects_sandbox_request
9.3 定向命令
在 codex-rs workspace 运行:
RUST_MIN_STACK=16777216 cargo test -p codex-core --lib tools::runtimes::unified_exec::tests::approval_key_includes_environment_id -- --exact --nocapture
RUST_MIN_STACK=16777216 cargo test -p codex-core --lib tools::runtimes::unified_exec::tests::unified_exec_uses_the_trusted_sandbox_cwd -- --exact --nocapture
RUST_MIN_STACK=16777216 cargo test -p codex-core --lib tools::runtimes::unified_exec::tests::zsh_fork_first_attempt_preserves_parent_sandbox_override -- --exact --nocapture
RUST_MIN_STACK=16777216 cargo test -p codex-core --lib tools::runtimes::apply_patch::tests::file_system_sandbox_context_preserves_executor_workspace_permissions -- --exact --nocapture
RUST_MIN_STACK=16777216 cargo test -p codex-core --lib tools::runtimes::apply_patch::tests::file_system_sandbox_context_respects_sandbox_request -- --exact --nocapture这些测试证明接口与状态传递,不证明真实 remote executor、Windows token、Linux namespace 或 macOS Seatbelt 的完整强制效果。
10. 阅读实践
阅读 exec_command 时,不要再搜索已删除的 ShellRuntime。从 handlers/unified_exec/exec_command.rs 追到 UnifiedExecProcessManager::exec_command,再进入 open_session_with_sandbox、ToolOrchestrator 和 UnifiedExecRuntime::run。shell snapshot 与环境恢复位于 runtimes/mod.rs,zsh-fork 位于 runtimes/zsh_fork.rs 及其子模块。
阅读 Apply Patch 时,从 handler 构造 request 开始,重点跟踪 sandbox_requested、exec_server_permissions、 follow_symlinks 和 committed_delta。不要用 host SandboxType::None 判断远端文件系统是否无隔离。
最后尝试回答三个问题:
- 为什么 executor snapshot 会让 host sandbox 为 None,但仍向 exec-server 发送
FileSystemSandboxContext? - 为什么
UnifiedExecAttempt不直接包含最终 stdout,而 Apply Patch output 可以包含完整ExecToolCallOutput? - Apply Patch 第一次尝试部分成功、第二次尝试成功时,最终事件为什么必须读取累计 delta?
能从 handler、Orchestrator、runtime 一直追到 process owner 或 filesystem owner,就掌握了当前 Codex 工具运行时抽象的真实边界。
