Files
codex/codex-rs/core
T
Michael Bolin b859a98e0f refactor: make unified-exec zsh-fork state explicit (#14633)
## Why

The unified-exec path was carrying zsh-fork state in a partially
flattened way.

First, the decision about whether zsh-fork was active came from feature
selection in `ToolsConfig`, while the real prerequisites lived in
session state. That left the handler and runtime defending against
partially configured cases later.

Second, once zsh-fork was active, its two runtime-only paths were
threaded through the runtime as separate arguments even though they form
one coherent piece of configuration.

This change keeps unified-exec on a single session-derived source of
truth and bundles the zsh-fork-specific paths into a named config type
so the runtime can pass them around as one unit.

In particular, this PR introduces this enum so the `ZshFork` variant can
carry the appropriate state with it:

```rust
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum UnifiedExecShellMode {
    Direct,
    ZshFork(ZshForkConfig),
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ZshForkConfig {
    pub(crate) shell_zsh_path: AbsolutePathBuf,
    pub(crate) main_execve_wrapper_exe: AbsolutePathBuf,
}
```

This cleanup was done in preparation for
https://github.com/openai/codex/pull/13432.

## What Changed

- Replaced the feature-only `UnifiedExecBackendConfig` split with
`UnifiedExecShellMode` in `codex-rs/core/src/tools/spec.rs`.
- Derived the unified-exec mode from session-backed inputs when building
turn `ToolsConfig`, and preserved that mode across model switches and
review turns.
- Introduced `ZshForkConfig`, which stores the resolved zsh-fork
`AbsolutePathBuf` values for the configured `zsh` binary and `execve`
wrapper.
- Threaded `ZshForkConfig` through unified-exec command construction and
the zsh-fork preparation path so zsh-fork-specific runtime code consumes
a single config object instead of separate path arguments.
- Added focused tests for constructing zsh-fork mode only when session
prerequisites are available, and updated the zsh-fork expectations to be
target-platform aware.

## Testing

- `cargo test -p codex-core zsh_fork --lib`


---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/14633).
* #13432
* __->__ #14633
b859a98e0f ยท 2026-03-13 17:20:01 -07:00
History
..
2026-03-05 16:22:39 -08:00

codex-core

This crate implements the business logic for Codex. It is designed to be used by the various Codex UIs written in Rust.

Dependencies

Note that codex-core makes some assumptions about certain helper utilities being available in the environment. Currently, this support matrix is:

macOS

Expects /usr/bin/sandbox-exec to be present.

When using the workspace-write sandbox policy, the Seatbelt profile allows writes under the configured writable roots while keeping .git (directory or pointer file), the resolved gitdir: target, and .codex read-only.

Network access and filesystem read/write roots are controlled by SandboxPolicy. Seatbelt consumes the resolved policy and enforces it.

Seatbelt also supports macOS permission-profile extensions layered on top of SandboxPolicy:

  • no extension profile provided: keeps legacy default preferences read access (user-preference-read).
  • extension profile provided with no macos_preferences grant: does not add preferences access clauses.
  • macos_preferences = "readonly": enables cfprefs read clauses and user-preference-read.
  • macos_preferences = "readwrite": includes readonly clauses plus user-preference-write and cfprefs shm write clauses.
  • macos_automation = true: enables broad Apple Events send permissions.
  • macos_automation = ["com.apple.Notes", ...]: enables Apple Events send only to listed bundle IDs.
  • macos_launch_services = true: enables LaunchServices lookups and open/launch operations.
  • macos_accessibility = true: enables com.apple.axserver mach lookup.
  • macos_calendar = true: enables com.apple.CalendarAgent mach lookup.
  • macos_contacts = "read_only": enables Address Book read access and Contacts read services.
  • macos_contacts = "read_write": includes the readonly Contacts clauses plus Address Book writes and keychain/temp helpers required for writes.

Linux

Expects the binary containing codex-core to run the equivalent of codex sandbox linux (legacy alias: codex debug landlock) when arg0 is codex-linux-sandbox. See the codex-arg0 crate for details.

Legacy SandboxPolicy / sandbox_mode configs are still supported on Linux. They can continue to use the legacy Landlock path when the split filesystem policy is sandbox-equivalent to the legacy model after cwd resolution.

Split filesystem policies that need direct FileSystemSandboxPolicy enforcement, such as read-only or denied carveouts under a broader writable root, automatically route through bubblewrap. The legacy Landlock path is used only when the split filesystem policy round-trips through the legacy SandboxPolicy model without changing semantics. That includes overlapping cases like /repo = write, /repo/a = none, /repo/a/b = write, where the more specific writable child must reopen under a denied parent.

All Platforms

Expects the binary containing codex-core to simulate the virtual apply_patch CLI when arg1 is --codex-run-as-apply-patch. See the codex-arg0 crate for details.