Skip to content

工具运行时抽象

从 ToolRuntime、SandboxAttempt、Unified Exec 与 Apply Patch 追踪命令执行和文件修改的共同策略边界。

基于rust-v0.150.0
CodexRustToolsRuntime

工具运行时抽象 ​

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选择的读者。读完后,读者应能解释:

  1. 为什么 shell 工具存在,但 ShellRuntime 不再存在;
  2. 为什么 sandbox_requested = true 时 host SandboxType 仍可能是 None;
  3. 为什么 Unified Exec 的 runtime 输出不是最终工具文本;
  4. 为什么 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

rust
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

rust
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

rust
#[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 :: Approvable
  • codex-rs/core/src/tools/sandboxing.rs :: Sandboxable
  • codex-rs/core/src/tools/sandboxing.rs :: ToolRuntime
rust
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

rust
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

rust
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

rust
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

rust
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

rust
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

rust
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

rust
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

rust
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

rust
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

rust
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

rust
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:

  1. zsh-fork 条件满足时,先由 interception backend 准备 ExecRequest;
  2. remote 或 executor snapshot 通过 env_for_exec_server;
  3. 普通 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

rust
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

rust
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

rust
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

rust
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

rust
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()).await

7. 两种Runtime ​

7.1 差异表 ​

维度Unified ExecApply Patch
RequestUnifiedExecRequestApplyPatchRequest
OutputUnifiedExecAttemptApplyPatchRuntimeOutput
执行对象process / PTY / exec-serverenvironment filesystem
审批动作ExecCommandApplyPatch
executor-managedremote 或 shell snapshotremote environment
network spec支持默认无
长生命周期 ownerprocess managerruntime 的 committed delta
denial 识别process/backend error文件系统输出特征

exec_command 的最终工具文本不属于 runtime 输出;Apply Patch 的最终文本在 runtime 内已经形成,但事件层仍需要 delta。泛型 ToolRuntime<Req, Out> 正是为了允许这种差异。

7.2 共同流程 ​

7.3 新Runtime原则 ​

新增 runtime 前应先判断:

  1. 是否真的需要新的 Req 与 Out,还是 Unified Exec 的新 backend;
  2. 成功值是否拥有跨 handler 生命周期的资源;
  3. executor-managed 时哪些策略必须保持 URI-native;
  4. 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_id
  • codex-rs/core/src/tools/runtimes/unified_exec.rs :: unified_exec_uses_the_trusted_sandbox_cwd
  • codex-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_permissions
  • codex-rs/core/src/tools/runtimes/apply_patch_tests.rs :: file_system_sandbox_context_respects_sandbox_request

9.3 定向命令 ​

在 codex-rs workspace 运行:

bash
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 判断远端文件系统是否无隔离。

最后尝试回答三个问题:

  1. 为什么 executor snapshot 会让 host sandbox 为 None,但仍向 exec-server 发送 FileSystemSandboxContext?
  2. 为什么 UnifiedExecAttempt 不直接包含最终 stdout,而 Apply Patch output 可以包含完整 ExecToolCallOutput?
  3. Apply Patch 第一次尝试部分成功、第二次尝试成功时,最终事件为什么必须读取累计 delta?

能从 handler、Orchestrator、runtime 一直追到 process owner 或 filesystem owner,就掌握了当前 Codex 工具运行时抽象的真实边界。