## What changed
- Add `permissions.approved_command_prefix_saved` to model messages and use valid plain-text headings in saved-prefix notices, with or without full permissions instructions. Reject empty, oversized, control-character, and markup-like headings.
- Keep `Approved command prefix saved:` as the leading marker for older clients and recognize saved-prefix content kinds when trimming rolled-back context.
- Shut down analytics test exporters asynchronously and serialize workspace-switching tests that share authentication environment state.
## Testing
Extend coverage for heading validation, sparse wire values, single-notice emission, rollback with and without metadata, and saved-prefix behavior across model switches.
GitOrigin-RevId: a70175deca64a6339abac2fa69af970f470e3f4f
## Why
Disabled or failed protected shell snapshots prevent commands from running when credential masking is enabled.
## What changed
Restore unmasked credentials and continue execution when a protected shell snapshot is unavailable. Log and emit a warning explaining that the command will run with unmasked credentials.
## Testing
Extend `brokered_shell_snapshot_fallback` coverage for disabled and failed snapshots, including managed feature settings. Verify the warning, successful execution with restored credentials, continued filesystem sandbox and network policy enforcement, and masking recovery after snapshot storage is repaired.
GitOrigin-RevId: 28561e571b2a043d0b6cfa248c9ba2a61dd558ba
## What changed
- Add the disabled-by-default `features.credential_masking` flag to enable credential brokerage through an already enabled network proxy, retaining custom credential providers and managed configuration precedence.
- Prevent project configuration from overriding credential masking and apply existing credential-broker protections to project shell settings when masking is enabled.
- Keep brokerage inactive when the proxy is disabled. Warn at startup and after permission-profile changes when masking is inactive because sandboxing or the proxy is disabled, or provider environment keys conflict.
## Testing
Add coverage for configuration precedence, project protections in both configuration loaders, permission-profile transitions, inactive-masking warnings, and shell snapshot masking of built-in and custom credentials.
GitOrigin-RevId: 4bcb293356e8c1524bdaa4f1b95cbc1e935a5271
## What changed
- Use the standard shell executor and remove the zsh fork escalation runtime, `codex-execve-wrapper`, and `shell-escalation` crate.
- Ignore `shell_zsh_fork` and `unified_exec_zsh_fork`, emit migration notices for legacy configuration, and warn when ignoring managed requirements for these flags.
- Keep the active session's `apply_patch` alias directory readable under restricted filesystem permissions.
## Testing
Add coverage for legacy configuration startup and shell execution, migration notice rendering, active alias directory selection, and nested `apply_patch` execution while other files in `CODEX_HOME` remain unreadable.
GitOrigin-RevId: 43af461fea32b4980550ee7237effd2bc0646ed8
## What changed
Add `TurnEnvironmentRequests` to carry environment requests together with their fallback working directory. Update thread settings overrides and active turn settings to accept requests, constructing `TurnEnvironmentSelection` values in the receiving thread before validation and runtime use.
Adapt app-server callers, reviewer settings, and existing tests to supply requests or explicitly convert runtime selections back into requests.
GitOrigin-RevId: 8028f804bebe3fde553f57bb450f6dca3970cc4a
## What changed
Add `codex.shell_snapshot.command` and `codex.shell_snapshot.wait_ms` metrics for eligible command preparations in the unified exec runtime and exec server. Tag observations with snapshot version, availability state, and whether replay was selected (`used`) or normal shell startup was used (`fallback`). Exclude background prewarming from command observations; `used` measures replay selection, not successful restoration or command completion.
## Testing
Extend snapshot retry tests to verify command counts and wait observations across fallback and recovery, excluding prewarm captures. Extend credential-broker approval tests to verify one `protected` snapshot observation for login-shell commands.
GitOrigin-RevId: d3d9f370b6bbba6dfc930a44044ce09373841799
## Why
Concurrent Linux test spawns can inherit writable descriptors for executable fixtures, preventing those fixtures from being executed even after the writing test closes its descriptor.
## What changed
Replace direct executable fixture writes and copies across the Rust tests with `codex_utils_cargo_bin::write_executable` and `copy_executable`. These existing helpers complete writes and copies in separate processes on Linux.
Remove the empty temporary inode before creating fake bubblewrap executables, and stage tampered bubblewrap bytes in a separate data file before copying them into the executable fixture. Add the required test dependencies.
GitOrigin-RevId: c875c83088731a4933ef1574e8d79d8cecc12f54
## Why
Denied-read policies previously kept explicitly approved commands within their original filesystem restrictions, preventing operations that need broader write access, such as updating Git metadata.
## What changed
- Apply broader filesystem permissions after approval of explicit escalation while retaining the sandbox, denied reads, and network policy. Command allow rules alone do not widen filesystem access.
- Reject escalation when the required local sandbox is unavailable or a remote exec server lacks support for preserving the restrictions.
- Track escalated terminal access so subsequent `write_stdin` calls still require review when stdin approval is enabled.
## Testing
Add coverage for approved and denied escalation, allow rules, remote server compatibility, and stdin approval before input delivery. Add a Windows integration test verifying that an approved `git pull` updates Git metadata while exact-path and glob-based read denials remain enforced. Serialize Windows sandbox integration tests across processes to protect shared test account credentials.
GitOrigin-RevId: 5dce6d41487ee4d39bd795c8d086398ab9794d5b
## Why
Saved model selections can outlive their catalog entries. Removing GPT-5.4 needs to preserve a migration path without applying it to other providers that may still support the same model slug.
## What changed
- Remove `gpt-5.4` from the bundled catalog and `openai.gpt-5.4` from the Bedrock catalogs, including GovCloud.
- Add fallback migration prompts to GPT-6 Sol for saved GPT-5.4 selections on OpenAI and Amazon Bedrock. Scope fallback migrations, including the existing GPT-5.4 Mini migration, to their owning providers while honoring catalog metadata.
- Update tests that depend on bundled GPT-5.4 metadata to use GPT-5.5 or explicit model overrides.
## Testing
Extend migration tests to cover catalog metadata precedence, acknowledged targets, missing or hidden replacements, and preserving the provider while saving the replacement model and its default reasoning effort. Update Bedrock catalog assertions and picker snapshots.
GitOrigin-RevId: f739795da423b9622db2a416be6c52b01b8e7a71
## Why
Saved model selections can outlive their bundled catalog entries and still need migration guidance.
## What changed
- Remove `gpt-5.2` and `gpt-5.4-mini` from the bundled model catalog.
- Preserve migration from saved `gpt-5.4-mini` selections to `gpt-5.6-luna` when the old model is absent, while respecting catalog-provided migration metadata.
- Update tests and snapshots to use current models or explicit test fixtures, and remove the obsolete prompt fixture.
## Testing
Extend migration tests to cover catalog metadata precedence, saved selections, replacement model and reasoning-effort persistence, acknowledged prompts, and missing or hidden migration targets.
GitOrigin-RevId: 82beea504cc4a15699677c28a61ec0939af3069c
## Why
Credential brokerage must retain trusted destination hints even when shell environment policy hides them from child processes. Snapshot replay also needs to preserve credential aliases when tokens are short or captured dummy values change.
## What changed
- Add `features.network_proxy.credentials` and preserve provider settings when toggling the proxy. Prevent project configuration from overriding providers or their credential and destination environment variables.
- Use provider metadata throughout snapshot capture and replay, and pass hidden destination context to the broker without exposing it in the child environment.
- Rewrite aliases containing short credentials or previously captured dummies, respecting environment filters and explicit overrides.
- Preserve case-distinct `shell_environment_policy.set` keys so Windows credential ambiguity checks survive configuration merging and permission profile changes.
- Restore independent MITM settings when credential brokerage is disabled.
## Testing
Add regression coverage for custom providers, hidden destination hints, Bash and Zsh alias replay, configuration edits and merging, Windows ambiguity handling, and credential protection during approved execution.
GitOrigin-RevId: b2bd935d2d8555be56baf8feb1a8e5b3a0c666bc
## Why
Shell quoting can hide credentials from raw-text checks, and startup files can restore real credentials after the broker replaces them with dummy values.
## What changed
- Decode shell literals without evaluating them and reject snapshots containing credentials in executable source, including aliases, functions, and heredocs.
- Preserve credential policy overrides, explicit unsets, and aliases whose source variables were removed. Support credential aliases in Zsh tied arrays while rejecting credentials that span array elements.
- Guard snapshot replay against credential restoration through shell startup files and preserve unrelated `ENV` settings.
- Apply Windows environment-key casing rules to credential overrides, suppress unredacted sandbox diagnostics during snapshot capture, and clear inherited environment variables before launching escalated commands.
## Testing
Add regression coverage for shell quoting and escaped credentials, Zsh tied arrays, startup-file replay, readonly credentials, policy overrides, and sensitive capture timeout and cancellation handling.
GitOrigin-RevId: 58274c07c715423241d26ce6dd2c2b4230cf0f64
## Why
Credential-brokered commands rebuild shell snapshots on every invocation. Snapshot capture failures can also include credential-bearing startup output, and descendants holding output pipes need cleanup even after the shell exits.
## What changed
- Cache successful protected snapshots per environment, keyed by working directory, shell, login mode, and sandbox configuration. Rebuild missing or stale snapshots while keeping concurrent captures independently cancellable.
- Invalidate snapshots when shell settings or credential broker configuration change, and retry capture once if broker configuration changes during startup.
- Restore credentials and associated provider context while respecting environment policy filters and explicit overrides.
- Keep full-buffer capture subject to timeout and cancellation through output draining, clean up descendants on capture expiration or drain failure, and omit startup output from snapshot errors.
## Testing
Add regression coverage for snapshot reuse, invalidation, recovery after storage failure, concurrent cancellation, credential-safe errors, and descendant cleanup. Verify that successful background startup and output beyond the shell output cap remain supported.
GitOrigin-RevId: 7c7c8455bf6e3b9ca173dbb4277783469f0b45e6
## Why
Shell startup files can introduce credentials or overwrite brokered dummy values. Snapshot capture and replay need to respect the command's sandbox and environment policy without persisting real credentials.
## What changed
- Capture and validate brokered snapshots per command using its requested shell, working directory, login mode, sandbox, and environment policy.
- Discover shell-initialized credentials and prepare snapshot exports for brokered replay. Preserve shell functions and aliases while preventing startup files from overwriting dummy credentials during replay.
- Rebuild snapshots as credential broker state changes and retain each command's snapshot for the lifetime of its process.
- Reject brokered POSIX shell execution when a protected snapshot cannot be created, and reject brokerage with `shell_zsh_fork`. Restore real credentials and remove brokerage markers for unsupported PowerShell and Cmd execution; escalated commands bypass protected snapshots.
## Testing
Add regression coverage for credential discovery and redaction, environment filtering, readonly exports, shell function and heredoc replay, command working directories and login modes, sandbox enforcement, and missing or disabled snapshots.
GitOrigin-RevId: 685356cb9e80f11cf69d053e4d239ee6ccf43a9e
## What changed
- Add `features.network_proxy.credential_broker` and preserve structured network proxy settings when toggling the feature through config APIs.
- Protect broker enablement and provider context variables from project config, while carrying trusted context into filtered shell environments.
- Propagate brokered credentials through shell snapshots and restore real credentials for unsandboxed escalated commands.
- Normalize broker context environment keys case-insensitively on Windows and avoid conflicting remote managed-network enforcement when no proxy launch configuration is available.
## Testing
- Cover config layering, editing, project sanitization, Windows environment handling, shell snapshots, escalated execution, and end-to-end GitHub credential brokering in the zsh-fork runtime.
GitOrigin-RevId: 500e51e804e1988022052ccfe00b8fac5e495f4f
## What changed
- Use the shared approval-policy check for unified exec sandbox escalation so
`require_escalated` commands can prompt when granular `sandbox_approval` is
enabled and remain rejected when it is disabled.
- Add an approval scenario covering sandbox escalation with granular approvals
enabled.
GitOrigin-RevId: 98907960cd9df181e1b2fd9d7fea11fa3c0ed7e6
## What changed
- Remove the legacy `shell_command` handler and runtime, leaving `exec_command`
and `write_stdin` as the shell execution tools.
- Treat legacy `shell_command` model metadata as `unified_exec`, and normalize
legacy user opt-outs so they do not disable command execution. Managed feature
requirements and `shell_tool` can still disable it.
- Preserve shell approvals, sandboxing, zsh-fork support, and output truncation
through the unified execution path.
## Testing
- Cover legacy configuration and model-metadata compatibility.
- Exercise unified shell execution, approvals, truncation, and `apply_patch`
serialization across the app-server and core test suites.
GitOrigin-RevId: 5c2fd6164fc3519cdae4944cb9db276b8467311c
## What changed
- Remove `untrusted` from the CLI, configuration schema, and MCP tool interface. Explicit `approval_policy = "untrusted"` settings now fail with an actionable error.
- Remove the known-safe command allowlist. Projects marked untrusted now request approval for every command unless an explicit exec policy rule allows it.
- Keep command parsing conservative by treating in-place `sed` forms as mutating and ignoring unrecognized commands when recording memory usage.
## Testing
- Cover rejection of the retired configuration value and approval requests for commands in untrusted projects.
GitOrigin-RevId: d6bf425edddfffbb325eee6acf383434af5fd33b
## Why
Reducing a heredoc shell script to its inner executable lets a prefix rule for
that executable apply to the entire wrapper, even though the full script was
not parsed as a plain command.
## What changed
- Fall back to evaluating the complete shell wrapper when plain-command parsing
fails, including for heredoc scripts.
- Keep these commands sandboxed when only the inner executable is allowed.
- Propose the full wrapper as the exec policy amendment when approval is needed.
## Testing
Added exec policy, Unix escalation, and approval scenario coverage for unparsed
and heredoc shell wrappers.
GitOrigin-RevId: 8f65133acb6b7c638263917e1d9137e45990772c
## Why
Tree-sitter can represent brace expansions, globs, and escaped text as plain
words even though the shell changes them at runtime. Treating their source text
as literal argv could let a command match a safe-command check or allow rule
that does not match what will actually execute.
## What changed
- Reject unquoted Bash and Zsh words containing expansion, glob, or escape
syntax from literal command parsing, including heredoc command prefixes.
- Reject double-quoted escape sequences that the shell removes or interprets,
while continuing to accept quoted metacharacters that remain literal.
- Require approval for affected commands under `UnlessTrusted`, even when a
policy contains an allow rule for the unexpanded source text.
## Testing
Add parser and approval scenarios covering brace expansion, globs, escapes,
Zsh-specific syntax, heredocs, and quoted literals.
GitOrigin-RevId: 31f2c6062680c42ac65362e3f3eebfaaf4eb71da
## What changed
- Add `TurnInputRequest` and typed submission results for atomically starting a turn, steering the active turn, or declining input with a specific reason.
- Expose `start_or_steer_turn`, `start_turn_if_idle`, and `steer_turn` on `CodexThread`, and migrate Core consumers to these APIs.
- Make app-server `turn/start` steer an active regular turn and return that turn's ID. Reject incompatible output schemas and non-steerable turns without applying settings or enqueueing input.
## Testing
- Cover concurrent start-or-steer submissions, accepted and rejected settings updates, output-schema compatibility, idle-start rejection, and app-server steering.
GitOrigin-RevId: dd9b5528d76ec650c019e97af420bc13190ea86a
## What changed
- Track approved command prefixes separately from the stable permissions
instructions in world-state snapshots.
- Emit only newly approved prefixes after an exec-policy amendment instead of
appending the full permissions block again.
- Preserve prefix updates when full permissions instructions are disabled and
remain compatible with legacy world-state snapshots.
## Testing
- Cover incremental prefix additions, removals, legacy snapshots, history
rollback, and approval flows with permissions instructions enabled or disabled.
GitOrigin-RevId: e6f68c6a91be82750e70e28456f2b7c58607fbd8
## What changed
- Seed approval scenarios with the bundled model catalog so their model metadata is deterministic.
- Make the patch approval helper surface unexpected exec approvals, errors, aborts, and shutdowns immediately instead of waiting for its timeout.
GitOrigin-RevId: b5ec88494ecaf571a480b4142300eb5f3f98def9
## Why
Default read-only protections for project metadata should apply when paths such
as `.git`, `.agents`, and `.codex` exist, without causing sandbox setup to
materialize missing paths as ACL targets.
## What changed
- Add an optional `missing_path_behavior` to filesystem sandbox entries and
mark default project-metadata protections with `skip`.
- Preserve the behavior through permission transforms and exec/MCP protocol
serialization while keeping existing path wire variants stable.
- Ignore skip-missing entries when projecting configuration or Windows sandbox
overrides, while retaining explicit metadata carveouts.
## Testing
- Cover protocol round trips for path and special-path entries.
- Verify default metadata protections and Windows explicit carveout handling.
GitOrigin-RevId: 6df13dadacdd131c44aab9f15a967c81051355c1
## Why
Patch approval events can take longer than the default test event timeout.
## What changed
Wait up to 15 seconds for a patch approval request or turn completion in the
approval test helper, while preserving the existing event checks and assertions.
GitOrigin-RevId: 73a50538482b5a88ccf0be07f8fad21000e54699
## What changed
- Change `ReviewDecision::Denied` to carry a rejection string and update the generated protocol schemas.
- Preserve specific rejection reasons through command, patch, network, MCP, delegated, and automatic approval flows so tool results can return them to the model.
- Distinguish invalid approval responses from user declines and truncate oversized rejection messages before adding them to model context.
## Testing
- Add coverage for denial serialization, invalid app-server approval responses, dropped automatic reviews, network rejection propagation, and rejection-message truncation.
GitOrigin-RevId: d48a55643aa03c7ecfa08a8e4b669913eec5464a
## Why
The shared core test harness does not need to spawn a login shell for every
test, and approval scenarios exercise tool behavior rather than rollout
persistence.
## What changed
- Disable `ShellSnapshot` by default in `test_codex()`; snapshot-specific tests
can continue to opt in explicitly.
- Give each approval scenario a named in-memory thread store so it does not use
durable rollout storage.
GitOrigin-RevId: 787670857aa4f77d5fc442ec1e0bfe6d22314f63
## What changed
- Explicitly shut down and wait for each approval scenario's Codex thread.
- Preserve and return the scenario verification result after shutdown, so cleanup also runs when verification returns an error.
GitOrigin-RevId: 78dfbe657700a47b457ea9abd5efec4edd8f8a25
## What changed
Resolve approval test targets outside the workspace relative to the test's
temporary home instead of the process working directory. This keeps the targets
outside the test workspace while containing them in the test environment.
GitOrigin-RevId: 871e840d658468c56d603afc10cc226f918bba5a
## Why
Policy-blocked proxy requests must terminate the corresponding active tool call and preserve the correct approval result, including when multiple calls are running concurrently.
## What changed
- Resolve a blocked request's execution ID to its registered active network call before recording the policy denial.
- Preserve an outcome already recorded for that call, and cancel the owning call after storing a new denial.
## Testing
Expand network approval integration coverage for Guardian and user decisions, permission hooks, persisted policy amendments, concurrent calls, timeouts, cancellations, and unattributed proxy requests.
GitOrigin-RevId: 961901bbc07cc498309453ec78cda1a56ab00a4b
## tl;dr
Inject a `CODEX_PERMISSION_PROFILE` environment variable with the name
of the current permission profile when invoking a shell tool.
## Why
Shell tool owners may need to launch nested commands under the same
named permission profile, including through `codex sandbox -P PROFILE
--include-managed-config`. Until now, child processes could observe
sandbox and network metadata but could not identify the active named
permission profile.
The `--include-managed-config` flag is essential when a helper
reconstructs the sandbox from a profile name: it ensures the nested
sandbox also loads managed enterprise requirements. Without it, using
the inherited profile could unintentionally create a sandbox that does
not enforce the organization's managed restrictions.
The new environment value is intentionally informational and **must not
be treated as trusted input**. Any process in the ancestry can overwrite
an environment variable, so a consumer that passes this value to `codex
sandbox -P` must first validate it against the profiles that helper is
authorized to use.
## Example Use Case
Suppose an organization provides a trusted `remote-bash` wrapper that
lets Codex run a command on an approved build host. The local shell
command uses the named `:workspace` permission profile:
```toml
default_permissions = ":workspace"
```
The command exposed to the model is a small zsh wrapper. It deliberately
delegates with `exec`, preserving the original arguments and process
environment:
```zsh
#!/usr/bin/env zsh
exec /opt/codex-tools/remote_bash.py "$@"
```
The model invokes the public wrapper, not its Python implementation:
```sh
/opt/codex-tools/remote-bash \
--host builder.example.com \
-- printf '%s' 'hello world'
```
Only the inner implementation is authorized to escape the local sandbox:
```starlark
prefix_rule(
pattern=["/opt/codex-tools/remote_bash.py"],
decision="allow",
)
```
With zsh-fork, execution begins with `remote-bash` inside the
`:workspace` sandbox. When the wrapper calls `exec`, the exact prefix
rule matches `remote_bash.py`, so that inner script is restarted
unsandboxed. The escalated process inherits:
```text
CODEX_PERMISSION_PROFILE=:workspace
```
Inheritance does not make the value trustworthy. `remote_bash.py`
independently allowlists both the remote host and the permission profile
before using either value. In particular, a forged value such as
`:danger-full-access` is rejected before it can reach `codex sandbox
-P`:
```python
import argparse
import os
import shlex
import sys
ALLOWED_HOSTS = {"builder.example.com"}
ALLOWED_PROFILES = {":workspace"}
parser = argparse.ArgumentParser()
parser.add_argument("--host", required=True)
separator = sys.argv.index("--")
args = parser.parse_args(sys.argv[1:separator])
command = sys.argv[separator + 1:]
if args.host not in ALLOWED_HOSTS:
parser.error("host is not allowlisted")
if not command:
parser.error("the remote command must not be empty")
profile = os.environ.get("CODEX_PERMISSION_PROFILE")
if not profile:
raise SystemExit("CODEX_PERMISSION_PROFILE must not be empty")
if profile not in ALLOWED_PROFILES:
raise SystemExit("CODEX_PERMISSION_PROFILE is not allowlisted")
remote_command = shlex.join(command)
sandbox_command = shlex.join([
"codex", "sandbox", "-P", profile,
"--include-managed-config", "--",
"bash", "-lc", remote_command,
])
print(shlex.join(["ssh", args.host, sandbox_command]))
```
This builds each command layer as an argument vector and uses
`shlex.join()` at the boundary, rather than interpolating untrusted
shell text. After validation and parsing, the nested command has this
structure:
```text
ssh argv:
["ssh", "builder.example.com", SANDBOX_COMMAND]
SANDBOX_COMMAND argv:
["codex", "sandbox", "-P", ":workspace",
"--include-managed-config", "--",
"bash", "-lc", "printf %s 'hello world'"]
bash -lc payload argv:
["printf", "%s", "hello world"]
```
A production implementation could execute that SSH command. The
integration fixture prints it and parses the result back into arguments,
verifying the complete flow:
```text
model invokes outer wrapper
-> zsh-fork starts wrapper under :workspace
-> wrapper execs allowlisted Python script
-> prefix rule restarts Python script unsandboxed
-> Python script inherits CODEX_PERMISSION_PROFILE=:workspace
-> Python script verifies :workspace is allowlisted
-> remote command runs codex sandbox -P :workspace
with --include-managed-config
-> nested sandbox honors managed enterprise requirements
```
This gives the trusted helper access to resources outside the local
sandbox—such as SSH credentials—while ensuring that it can select only
an explicitly authorized profile and that work on the remote host
remains subject to the organization's managed requirements.
## What changed
- Inject `CODEX_PERMISSION_PROFILE` after shell environment policy
evaluation so the active profile wins over inherited or configured stale
values.
- Apply the variable to both `shell_command` and unified `exec_command`,
including local, zsh-fork, and remote exec-server paths.
- Remove stale values when the session has no active named profile.
- Preserve the current profile value when loading a shell snapshot so a
parent snapshot cannot restore an older profile.
## Testing
- Added classic-shell integration coverage proving an exact prefix rule
can run a `require_escalated` script outside the `:workspace` sandbox
while preserving `CODEX_PERMISSION_PROFILE=:workspace`.
- Added zsh-fork integration coverage in which the model invokes an
outer zsh wrapper, an inner allowlisted `remote_bash.py` runs
unsandboxed, and its printed SSH command reconstructs the inherited
`:workspace` sandbox with `--include-managed-config` while preserving
every argument after `--`.
- The example helper treats `CODEX_PERMISSION_PROFILE` as untrusted and
validates it against `ALLOWED_PROFILES` before constructing the nested
command.
- Assert that the reconstructed sandbox command includes
`--include-managed-config` so nested use of the inherited profile cannot
bypass managed enterprise requirements.
- Added coverage for overriding and removing stale profile values.
- Verified `shell_command` receives the selected active profile.
- Added shell snapshot coverage using `printenv
CODEX_PERMISSION_PROFILE`.
## Summary
This keeps the apply_patch approval tests focused on approval behavior
instead of macOS sandboxed filesystem helper startup.
The changed cases still force patch approval with `UnlessTrusted`, but
use `DangerFullAccess` after approval so the patch write is direct and
cheap. Workspace-write and sandbox-helper behavior remain covered by the
filesystem and apply_patch sandbox tests.
The workspace denies `clippy::expect_used` in production. Although
`clippy.toml` allows `expect` in tests, Bazel Clippy compiles
integration-test helper code in a way that does not receive that
exemption, which encouraged verbose `unwrap_or_else(... panic!(...))`
and equivalent `match`/`let else` forms.
This allows `clippy::expect_used` once at each integration-test crate
root (including aggregated suites and test-support libraries), then
replaces manual panic-based Result and Option unwraps with
`expect`/`expect_err`. Standalone `tests/*.rs` files remain their own
crate roots. Intentional assertion and unexpected-variant panics remain
unchanged, and the production `expect_used = "deny"` lint remains in
place.
The cleanup is mechanical and net-negative in line count.
## Why
Thread cwd and environment selections are a single logical setting in
core: updating one without the other can silently desynchronize the
next-turn execution context. This change makes that relationship
explicit in the internal thread settings flow while preserving the
existing app-server public API shape.
## What changed
- Moved the cwd/environment pair through internal
`ThreadSettingsOverrides.environment_settings` instead of a top-level
internal `cwd` field.
- Kept `thread/settings/update` public params unchanged, with app-server
translating top-level `cwd` into the paired internal settings shape.
- Moved `Op::UserInput` environment overrides into thread settings so
user turns and settings updates use the same core path.
- Updated core, app-server, MCP, memories, sample, and test callsites to
construct the paired settings shape.
## Verification
- `git diff --check`
- Local test run starting after PR creation.
## Why
Thread settings cwd overrides are expected to be resolved before they
enter core. Keeping this boundary as a plain `PathBuf` made it easy for
core/session code to keep fallback normalization and relative-path
resolution logic in places that should only receive an already-resolved
cwd.
This is intentionally the absolute-cwd-only slice: it does not change
environment selection stickiness or cwd-to-default-environment fallback
behavior.
## What changed
- Changes `ThreadSettingsOverrides.cwd`,
`CodexThreadSettingsOverrides.cwd`, and `SessionSettingsUpdate.cwd` to
use `AbsolutePathBuf`.
- Removes core-side cwd normalization/resolution from session settings
updates.
- Updates affected core/app-server test helpers and callsites to pass
existing absolute cwd values or use `abs()` helpers.
## Validation
Opening as draft so CI can start while local validation continues.
## Summary
- Adapts the moved `codex-cloud-config` crate from the legacy cloud
requirements endpoint to the new config bundle endpoint.
- Switches runtime consumers from `CloudRequirementsLoader` to
`CloudConfigBundleLoader` so one shared bundle supplies cloud-delivered
config and requirements.
- Removes the legacy cloud requirements domain loader path.
## Details
This intentionally keeps `codex-cloud-config` monolithic for review
lineage: the previous PR establishes the crate move, and this PR shows
the behavior change against that moved implementation. A follow-up PR
splits the module back into focused files.
The new bundle path preserves the important cloud requirements loader
semantics where intended: account-scoped signed cache, 30 minute TTL, 5
minute refresh cadence, retry/backoff, auth recovery, and fail-closed
startup loading. The cached payload changes from a single requirements
TOML string to the backend-delivered bundle, and validation rejects
malformed config or requirements fragments before cache write/use.
## Why
Permission profiles can mark filesystem entries as unreadable with
`deny` rules, including glob patterns. Several shell execution paths
treated known-safe commands or execpolicy `allow` rules as sufficient to
run outside the filesystem sandbox. That is not valid for read-capable
commands: for example, `cat` or `ls` may be reasonable to allow
generally, but dropping the sandbox would also drop deny-read
constraints such as `**/*.env`.
## What changed
- Added a shared check that treats active deny-read restrictions as
incompatible with unsandboxed execution.
- Kept first-attempt execution sandboxed for explicit escalation and
execpolicy allow bypasses when deny-read entries are present.
- Prevented no-sandbox retry after a sandbox denial when the active
filesystem policy contains deny-read entries.
- Updated the zsh-fork execve path so prefix-rule `allow` decisions
continue inside the current sandbox when deny-read restrictions are
active.
## Verification
- `cargo test -p codex-core tools::sandboxing::tests`
- `cargo test -p codex-core
tools::runtimes::shell::unix_escalation::tests`
- `cargo test -p codex-core
shell_command_enforces_glob_deny_read_policy`
## Why
#23756 makes packaged Codex builds include and default to the bundled
zsh fork. The important reason to put that fork's directory at the front
of `PATH` is to keep executable-level escalation working after a command
leaves the original shell and later re-enters zsh through `env`.
The expected chain is:
1. The zsh fork runs the top-level shell command.
2. That command launches another program, such as `python3`, while
inheriting the `EXEC_WRAPPER` environment and the escalation socket fd.
3. That program spawns a shell script whose shebang is `#!/usr/bin/env
zsh` rather than `#!/bin/zsh`, and it does not close the escalation fd.
4. `/usr/bin/env` resolves `zsh` through `PATH`, so it must find the
packaged zsh fork before the system zsh.
5. Commands inside that nested script are intercepted by the zsh fork
and can still request escalation from Codex.
If `PATH` resolves `zsh` to the system shell instead, the nested script
loses zsh-fork exec interception. Commands that should request
escalation can then run only in the original sandbox, or fail there,
without Codex ever receiving the approval request.
Shell snapshots make this slightly more subtle: a snapshot can restore
an older `PATH` after the child shell starts. This PR treats the zsh
fork `PATH` prepend as an explicit environment override so snapshot
wrapping preserves it.
## What Changed
- Added shared zsh-fork runtime helpers that prepend the configured zsh
executable parent directory to `PATH` without duplicate entries.
- Applied the zsh fork `PATH` prepend to both zsh-fork `shell_command`
launches and unified-exec zsh-fork launches before sandbox command
construction.
- Kept the shell-command zsh-fork backend API narrow: it derives the
configured zsh path from session services and rebuilds its sandbox
environment from `req.env`, rather than accepting a second, competing
environment map or a separately threaded bin dir.
- Kept Unix-only zsh-fork `PATH` mutation out of Windows clippy-visible
mutability.
- Added coverage for duplicate `PATH` entries, for preserving the zsh
fork prepend through shell snapshot wrapping, and for the nested
`python3` -> `#!/usr/bin/env zsh` escalation flow.
## Testing
- `just fmt`
- `just fix -p codex-core`
I left final test validation to CI after the latest review-comment
cleanup. Before that cleanup, `just test -p codex-core zsh_fork` passed
locally for the zsh-fork-focused tests.
## Summary
Adds experimental `additionalContext` support to `turn/start` and
`turn/steer` so clients can provide ephemeral external context, such as
browser or automation state, without turning that plumbing into a
visible user prompt or triggering user-prompt lifecycle behavior.
## API Shape
The parameter shape is:
```ts
additionalContext?: Record<string, {
value: string
kind: "untrusted" | "application"
}> | null
```
Example:
```json
{
"additionalContext": {
"browser_info": {
"value": "Active tab is CI failures.",
"kind": "untrusted"
},
"automation_info": {
"value": "CI rerun is in progress.",
"kind": "application"
}
}
}
```
The keys are opaque and caller-defined.
## Context Injection
When provided, accepted entries are inserted into model context as
hidden contextual message items, not as visible thread user-message
items.
`kind: "untrusted"` entries are inserted with role `user`:
```text
<external_${key}>${value}</external_${key}>
```
`kind: "application"` entries are inserted with role `developer`:
```text
<${key}>${value}</${key}>
```
Values are not escaped. Each value is truncated to 1k approximate tokens
before wrapping.
For `turn/start`, accepted additional context is inserted before normal
user input. For `turn/steer`, additional context is merged only when the
steer includes non-empty user input; context-only steers still reject as
empty input.
## Dedupe Strategy
`AdditionalContextStore` lives on session state and stores the latest
complete additional-context map.
Each `turn/start` or non-empty `turn/steer` treats its
`additionalContext` as the current complete set of values. Entries are
injected only when the key is new or the exact entry for that key
changed, including `value` or `kind`. After merging, the store is
replaced with the provided map, so omitted keys are removed from the
retained set and can be injected again later if reintroduced.
Omitting `additionalContext`, passing `null`, or passing an empty object
resets the store to empty and injects nothing.
## What Changed
- Threads experimental v2 `additionalContext` through app-server into
core turn start and steer handling.
- Adds separate contextual fragment types for untrusted user-role
context and application developer-role context.
- Uses pending response input items so additional context can be
combined with normal user input without treating it as prompt text.
- Adds integration coverage for start/steer flow, role routing,
dedupe/reset behavior, deletion/re-add behavior, hook-blocked input
behavior, empty context-only steer rejection, external-fragment marker
matching, and truncation.
## Why
The
`approving_apply_patch_for_session_skips_future_prompts_for_same_file`
integration test writes `apply_patch_allow_session.txt` under the
process cwd while exercising outside-workspace patch approval behavior.
With `just test` now being the normal validation path, that file can be
left behind in the checkout when the test runs or fails, creating
confusing untracked state.
## What changed
- Registers the resolved `apply_patch_allow_session.txt` path with
`tempfile::TempPath` before the test removes and recreates it through
`apply_patch`.
- Preserves the existing outside-workspace path shape so the approval
behavior under test does not change.
- Lets `TempPath` remove the generated file when the test exits,
including panic paths.
## Verification
- `just test -p codex-core --test all
approving_apply_patch_for_session_skips_future_prompts_for_same_file`
## Why
The v1 sub-agent tools are a single tool family, but they were exposed
as separate flat function tools. This makes the model-visible surface
less clearly grouped and leaves the legacy names in the same flat
namespace as newer agent tooling.
## What
- Wraps the v1 `spawn_agent`, `send_input`, `resume_agent`,
`wait_agent`, and `close_agent` specs in the `multi_agent_v1` namespace.
- Registers the corresponding handlers with namespaced runtime tool
names.
- Updates tool-planning, deferred tool search, and sub-agent
notification tests to assert the namespace shape and child `spawn_agent`
lookup.
## Verification
- Updated `codex-core` coverage for the v1 multi-agent tool plan,
deferred tool search output, and sub-agent tool descriptions.
## Summary
Removes the feature since this is effectively on by default in all cases
where we should use it, or can be configured via models.json.
## Testing
- [x] unit tests pass
## Why
Some sandboxed integration tests enabled both ambient temp roots
(`TMPDIR` and literal `/tmp`) even though they were not testing
temp-root behavior. On Linux bwrap, making `/tmp` writable causes
protected metadata mount targets such as `/tmp/.git`, `/tmp/.agents`,
and `/tmp/.codex` to be synthesized. If a run is interrupted, those
top-level markers can be left behind and contaminate later tests.
## What changed
For the incidental integration tests that do not need ambient temp-root
access, set `exclude_tmpdir_env_var` and `exclude_slash_tmp` to `true`.
Dedicated protected-metadata coverage remains in the lower-level sandbox
tests that use isolated temp roots.
## Verification
Focused remote devbox repros passed with a watcher polling `/tmp/.git`,
`/tmp/.agents`, and `/tmp/.codex`; no leaked markers were observed.
## Summary
Restricts behavior of `is_known_safe_command` only to modes where it is
explicitly part of the documented behavior:
- when `environment_lacks_sandbox_protections`
- in `AskForApproval::UnlessTrusted`
Notably, as a result of this, escalations for commands that pass
`is_known_safe_commands` are no longer auto-approved in
AskForApproval::OnRequest or AskForApproval::Granular.
## Testing
- [x] Updated unit tests
- [x] Updated approvals scenario tests.
---------
Co-authored-by: Codex <noreply@openai.com>
## Why
`apply_patch` is now a freeform/custom tool. Keeping the old
JSON/function-style registration and parsing path left another way for
models and tests to invoke `apply_patch`, which made the tool surface
harder to reason about.
## What changed
- Removed the `ApplyPatchToolType::Function` variant, JSON `apply_patch`
spec, and handler support for function payloads.
- Kept `apply_patch_tool_type = freeform` as the supported model
metadata path, including Bedrock catalog metadata.
- Migrated `apply_patch` tests and SSE fixtures to custom/freeform tool
calls.
## Verification
- `cargo test -p codex-tools -p codex-protocol -p codex-model-provider`
- `cargo test -p codex-core tools::handlers::apply_patch --lib`
- `cargo test -p codex-core --test all
apply_patch_tool_executes_and_emits_patch_events`
- `cargo test -p codex-core --test all
apply_patch_reports_parse_diagnostics`
- `cargo test -p codex-exec test_apply_patch_tool`
- `just fix -p codex-core`
- `just fix -p codex-tools -p codex-protocol -p codex-model-provider -p
codex-exec`
## Summary
Related to
https://openai.slack.com/archives/C095U48JNL9/p1777537279707449
TLDR:
We update the meaning of session ids and thread ids:
* thread_id stays as now
* session_id become a shared id between every thread under a /root
thread (i.e. every sub-agent share the same session id)
This PR introduces an explicit `SessionId` and threads it through the
protocol/client boundary so `session_id` and `thread_id` can diverge
when they need to, while preserving compatibility for older serialized
`session_configured` events.
---------
Co-authored-by: Codex <noreply@openai.com>
## Summary
Fixes a regression introduced in #10941 so that heredocs do not permit
file redirects to be approved by rules, and adds scenario tests to cover
this behavior.
Previously, heredoc command parsing would allow redirects and
environment variables:
```bash
# commands_for_exec_policy() would parse this via parse_shell_lc_single_command_prefix
PATH=/tmp/bad:$PATH cat <<'EOF' > /tmp/bad/hello.txt
hello
EOF
```
This conflicts with the Codex Rules documentation; heredoc parsing logic
should abide by the same strictness of parsing.
## Tests
- [x] Updated unit tests accordingly
- [x] Added scenario tests for these cases
---------
Co-authored-by: Codex <noreply@openai.com>
## Summary
- Updates the zsh-fork test helper to configure `PermissionProfile`
directly instead of constructing a legacy `SandboxPolicy`.
- Sends permission-profile-backed turns from the skill approval zsh-fork
tests so the runtime and request path exercise the canonical permissions
model.
- Leaves the broader approvals suite on legacy policies for now, except
for the zsh-fork test that shares this helper.
## Verification
- `cargo check -p codex-core --tests`
- `just fmt`