## Why
Idle reads from local MCP servers on Windows can occupy Tokio's blocking threads and delay unrelated filesystem operations.
## What changed
Use IOCP-backed named pipes for local MCP server stdin, stdout, and stderr on Windows. Connect child endpoints before launch, restrict pipe access to the owner and SYSTEM, and reject remote clients. Preserve the shared legacy and modern protocol framing.
## Testing
Add Windows regression tests to verify that idle MCP reads leave blocking I/O available in both protocol modes and that child output handles support querying the pipe mode.
GitOrigin-RevId: ca39b940a54427a2cd7bab5291011b0b09143b3f
## Why
Background helpers launched from a detached Windows process can allocate console windows. Job Object launches and containment fallbacks need to preserve console suppression as well.
## What changed
- Add `codex_utils_process::background_command` to set `CREATE_NO_WINDOW` on Windows, and use it across background subprocess launches, including Git, plugins, authentication, diagnostics, and sandbox helpers.
- Centralize console suppression in Job Object launches. Keep `CREATE_NO_WINDOW` when retrying without containment, and clear `CREATE_SUSPENDED` so the fallback child can run.
- Preserve console inheritance for interactive shell launches and interactive AWS authentication refreshes.
## Testing
Add Windows regression tests covering console-free launches from detached parents through standard and Tokio commands, stdout/stderr capture, successful containment, Job Object creation and assignment failures, and sandbox setup refresh helpers.
GitOrigin-RevId: b2c238fa7d4f9fbd868e5adf7d4be9958ff01e6c
## Why
Piped child processes launched from a detached Windows process should not allocate a console window.
## What changed
Set `CREATE_NO_WINDOW` by default for `codex-rs/utils/pty` child commands. Preserve it alongside `CREATE_SUSPENDED` when preparing a child for Job Object containment, since Tokio's `creation_flags` replaces existing flags.
## Testing
Add a Windows regression test that runs from a detached process and verifies that children have no console window and retain working piped stdin and stdout, both with and without Job Object containment.
GitOrigin-RevId: 990386032d74fa783a66bb20faff769539ea41c1
## What changed
Expose Windows process creation flags on the shared command wrapper and use
`CREATE_NO_WINDOW` when launching local stdio MCP servers, including fallback
launches without Job Object containment. Preserve `CREATE_SUSPENDED` when
assigning the server to a Job Object before execution.
## Testing
Add a Windows regression test covering direct launches and launches through
`cmd.exe`. Verify that the server has no console, initializes successfully,
returns tools, and exits after shutdown.
GitOrigin-RevId: 84475e82999d91a431e09327d4f5639b59c01427
## Why
Passing extra descriptors to a PTY previously selected behavior intended for inherited escalation sockets: no EOF on stdin close, different signal exit statuses, and no default `SHELL`. Launch attachments need ordinary PTY behavior and must survive execution even when marked `FD_CLOEXEC`.
## What changed
- Add `ChildFds::Attached` alongside `ChildFds::Inherited` to distinguish launch attachments from legacy inherited descriptors.
- Preserve ordinary EOF, default `SHELL`, and signal exit status behavior for PTY attachments.
- Allow explicitly supplied close-on-exec descriptors through pipe and PTY launches without changing the parent's descriptor flags, using child-side flag updates and macOS spawn inheritance actions.
- Update existing callers to use `ChildFds::Inherited`.
## Testing
Add Unix coverage for attachment accessibility, unchanged parent descriptor flags, default `SHELL`, and signal exit statuses. Extend the PTY stdin-draining test to cover attachments and EOF delivery.
GitOrigin-RevId: 864aedc6c5717d8b43c9d71057c9f54409834509
## Why
Linux descriptor cleanup used allocating directory enumeration after `fork`. The child needs allocation-free cleanup while preserving Rust's spawn-error channel until `exec` succeeds.
## What changed
- Mark unrelated descriptors close-on-exec with `close_range(CLOSE_RANGE_CLOEXEC)`, leaving stdio and explicitly preserved descriptors' flags untouched.
- Fall back to stack-backed `getdents64` enumeration of the child's descriptor table when `close_range` fails.
- Document best-effort cleanup: failures allow launch to proceed and may leave unrelated descriptors inherited.
## Testing
Add Linux tests for descriptor inheritance, unsorted and duplicate allowlists, unchanged parent descriptors, fallback cleanup above a lowered descriptor limit, preserved exec-error reporting, and successful launch when both cleanup paths fail. Retain MCP coverage for descriptor exclusion in servers and descendants.
GitOrigin-RevId: 13abb95f82557324c7b40feb36bd6f806d60674e
## Why
PTY launches need to establish a controlling terminal and reset signal state. Extend the Linux process setup helper to perform this work in a fresh, single-threaded process image without forking the application when the native helper is available.
## What changed
- Add a PTY setup mode and support caller-owned stdout and stderr descriptors so all three standard streams use the PTY slave.
- Use the helper for eligible Linux PTY launches, including launches with explicitly preserved descriptors, and wait for child exit asynchronously.
- Preserve portable PTY behavior for `PATH` lookup, custom `argv[0]`, missing working directories, default `SHELL`, stdin EOF, and signal exit status.
- Retain compatibility fallbacks and release the initial PTY descriptors before allocating a portable replacement.
## Testing
Add regression tests for controlling-terminal setup without forking, portable signal exit status, and successful fallback under file-descriptor pressure. Resolve the Python test executable to an absolute path to exercise the helper path.
GitOrigin-RevId: 4b07ce68aa33c5b79548581e221caf5953b0eda1
## Why
Unix command hooks use a `pre_exec` callback to detach from the controlling terminal, forcing the launcher to fork. Native spawn attributes can create the detached session without that callback.
## What changed
- Route Unix hooks through `codex_utils_pty::Command` with `ProcessMode::NewSession`.
- Extend the native `posix_spawn` launcher to detached Linux sessions, retaining compatibility fallbacks for unsupported libc features and executable handling.
- Preserve the session environment snapshot, hook overrides, and restricted-variable filtering.
## Testing
Add regression coverage for launches without parent forks, terminal detachment, environment and argument handling, Linux `PATH` lookup, and executable compatibility. Cover process-group cleanup on cancellation and timeout, including descendants holding output pipes open, and preservation of background descendants after completed hooks.
GitOrigin-RevId: dbe2cf12d84aa79868cfb55ace40c7d5b5282703
## Why
Pipe spawning installs Unix `pre_exec` hooks, forcing a fork on macOS even though the shared child launcher supports native spawning.
## What changed
- Use the shared `Command` for pipe processes, enabling native macOS spawning while preserving session isolation, explicit descriptor inheritance, and Linux parent-death handling.
- Select `ReapOnly` so the pipe adapter retains ownership of process-tree termination.
- Track embedded NUL bytes in original Unix command inputs and reject them before spawning, including when invalid `current_dir` or `arg0` values are later replaced.
## Testing
Add macOS integration coverage verifying fork avoidance, executable path lookup, pipe I/O, session isolation, and descriptor inheritance. Add Unix regression tests for pipe and PTY fallback under descriptor pressure and for rejecting NUL inputs without running the child.
GitOrigin-RevId: f4fa2c1d3c1aee2c4ffa1dc2ff946f9f0b08eb20
## What changed
- Add `ProcessMode::NewSession`, null stdin, and opt-in Linux parent-death termination to the shared child command API.
- Replace `DescriptorPolicy::StdioOnly` with `Explicit` and add `preserve_fds` to allow selected inheritable Unix descriptors alongside stdio. Preserve descriptor numbers without duplicating or closing the parent's descriptors, keeping POSIX record locks intact.
- Support session creation and preserved descriptors in the native macOS backend, with a compatible fallback for high descriptor numbers rejected by native file actions.
## Testing
Add regression coverage for process groups and sessions, descriptor preservation across native and executable-text launches, the last descriptor slot below the file limit, and retention of parent record locks.
GitOrigin-RevId: 29ecd859d3b673da6ae6412cc06a32fa10d11346
## What changed
Add a crate-private `ChildDropPolicy` to choose whether dropping a child kills it or only reaps it after exit. Keep `KillAndReap` as the default.
For `ReapOnly` on Unix, transfer dropped children to a shared reaper thread that operates independently of the Tokio runtime. Support both Tokio children and native macOS children, without blocking drop or letting a live child delay reaping others.
## Testing
Add an isolated regression test verifying that reap-only children survive handle drop after runtime shutdown, are reaped after exit, and do not prevent other exited children from being reaped.
GitOrigin-RevId: 5ec639597dba7f3b61694d6d465991e054ae3397
## Why
Filesystem helpers use a `pre_exec` callback to close inherited descriptors on macOS, forcing a fork before execution. Native spawning needs to preserve that isolation and support the socket used for file descriptor transfer.
## What changed
- Launch filesystem helpers through `codex_utils_pty::Command`, using `posix_spawn` with `POSIX_SPAWN_CLOEXEC_DEFAULT` on macOS and returning native launch errors without a fork fallback.
- Extend the shared command wrapper with explicit descriptor and fallback policies, socket-backed stdin, and custom `argv[0]` support.
- Add `Child::wait_with_output` to drain stdout and stderr concurrently, retain kill-on-drop behavior on cancellation, and keep output pipes open until the child exits.
## Testing
Add regression tests for fork-free sandboxed reads, writes, and file descriptor transfers; sandbox denial of outside paths and symlink escapes; descriptor isolation; bidirectional socket stdin; custom `argv[0]`; executable-format errors; and output-pipe lifetimes.
GitOrigin-RevId: d49c00787f30ec0bc196f9e066a0770fc8151152
## Why
The native macOS backend cannot inspect every setting or callback on a Tokio command. A shared, constrained launch API makes the supported settings explicit for both backends.
## What changed
- Replace `spawn_child` with `codex_utils_pty::Command`, enforcing an explicitly supplied environment, piped stdio, and kill-on-drop behavior.
- Add `ProcessMode::Inherit` and `ProcessMode::NewGroup`, honoring the selected mode in native macOS and Tokio spawning.
- Share the `Child` wrapper across platforms and migrate local MCP server launching to the new API, retaining its new process group and Windows suspended-spawn support.
## Testing
Adapt the existing macOS spawn compatibility and child lifecycle tests to the new API. Add a regression test that checks inherited and new process groups with both backends.
GitOrigin-RevId: 3e7a401659e3a8a94a13c4ff58d67bdcd2eea33d