## Why
Capability discovery needs the roots captured with each environment snapshot. Threads sharing an executor can select different roots, and changing environment order must preserve executor skill aliases.
## What changed
- Attach the receiving thread's capability roots when constructing `TurnEnvironmentSelection` at startup and settings updates, including roots loaded from saved history.
- Resolve capability roots from environment snapshots while preserving their original order. Retain startup roots so deselecting and reselecting an environment restores its capabilities.
- Match inherited configuration by environment and workspace so differences in capability root metadata do not interrupt configuration propagation.
## Testing
Add regression coverage for root ordering across environment states and configuration changes, independent root selections on a shared executor, reselection with startup or saved roots, active turn updates, and pending configuration failures reaching child and grandchild threads.
GitOrigin-RevId: 4f2e4bb414d27195a6752406117a6050718a1198
## What changed
- Remove the private-desktop opt-out from elevated and unelevated Windows sandbox launches.
- Remove `windows.sandbox_private_desktop` and its managed requirement and API fields. Warn users to remove the obsolete setting.
- Require a private desktop name when launching through the Windows sandbox wrapper and command runner.
## Testing
Add coverage for the obsolete-setting migration warning and update wrapper tests to verify a live private desktop is passed and a missing desktop name is rejected.
GitOrigin-RevId: c7135f8d211aac8d812180691c2c1e433d77cde4
## What changed
- Accept `windows.sandbox = "mxc"` and preserve the selected backend through environment configuration, command execution, patch writes, and sandbox metadata.
- Treat MXC as enabled in the TUI and report Windows sandbox readiness as `ready`, avoiding legacy setup prompts.
- Keep `allowed_sandbox_implementations` scoped to the legacy elevated and unelevated backends without restricting MXC.
- Default `windows.sandbox_private_desktop` to `false` for MXC while retaining `true` for legacy sandboxes.
## Testing
Add coverage for MXC configuration precedence, legacy requirement handling, sandbox selection, and TUI state. Add a Wine integration test that verifies command and patch routing fails when native MXC is unavailable and reports `windows_mxc` in turn metadata.
GitOrigin-RevId: e2162447d0750f60753864c92a20e02a7f297bca
## Why
Probing Windows network paths can send ambient credentials, even during metadata checks. Entries in `project_doc_fallback_filenames` must be validated before filesystem probes.
## What changed
Ignore entries containing path syntax according to the executor's OS, rather than the host's. Reject `.` and `..`, slashes, and NUL characters on all executors; also reject backslashes and colons on Windows. Log a warning for rejected entries and continue considering valid filenames.
## Testing
Add tests that verify invalid entries never reach metadata probes under POSIX and Windows path conventions, preserve backslashes and colons in POSIX filenames, and confirm valid fallback instructions still load alongside invalid entries.
GitOrigin-RevId: 4369e9b97ae6a3560d09e79efd8c85adac3d1d90
## What changed
- Expose `ThreadInstructionsProvider` through `StartThreadOptions`. Load its snapshot at startup and model-request boundaries, composing it after global instructions and before repository instructions. Empty or blank output clears only the thread contribution.
- Reject thread instructions exceeding 10,000 estimated tokens independently of the repository instruction budget. Allow host-provided instructions without a filesystem source and rename the shared future type to `LoadInstructionsFuture`.
- Retain the provider across warm resumes; require hosts to supply it again for cold resumes and offline forks. Live forks and subagents inherit applied snapshots without inheriting the source thread's provider, including when the parent is unloaded during setup or reload.
- Include thread instructions in guardian reviewer inheritance and reuse decisions.
## Testing
Add coverage for composition and clearing, refresh within an active turn, size rejection before sampling, cancellation-safe refresh, fork and resume behavior, parent eviction, and reviewer reuse invalidation.
GitOrigin-RevId: 7be9a523cbbfd67704067dfd526188dad89a3c88
## What changed
- Add effective workspace roots to `EnvironmentConfig` and use them when
materializing permission profiles and filesystem context.
- Preserve selection roots for thread-owned configurations while allowing a
ready environment attachment to supply its resolved roots.
- Propagate resolved roots to child environments and validate both command and
`apply_patch` writes in owner-provided secondary workspaces.
GitOrigin-RevId: 1832c168ce1fc37fa67efc2a472998ab4f448c70
## What changed
Rename the `UserInstructions` type to `Instructions` across the extension API,
its core API re-export, and all consumers. This keeps loading behavior and the
`LoadedUserInstructions` provider interface unchanged.
GitOrigin-RevId: 1ac236b34c31718e7aa38ff5eae5e9b179aeedd8
## What changed
- Add Windows sandbox mode, private-desktop behavior, and legacy Landlock selection to each `EnvironmentConfig`.
- Centralize sandbox context construction on `TurnEnvironment`, preserving environment-owned permissions, paths, and backend settings while applying additional permission grants.
- Use the environment-derived context for project instructions, capability discovery, file uploads, and filesystem-oriented tools.
## Testing
- Verify capability discovery uses the selected environment's permission profile and sandbox backends when they differ from the thread defaults.
GitOrigin-RevId: 7ae2fed90c8ada950f2e8431e571e9b2c793c291
## What changed
- Make `ExecutorFileSystem::walk` a required backend operation instead of
providing a fallback built from directory reads and metadata requests.
- Implement bounded local walks on a blocking task with cancellation, symlink
cycle detection, deterministic ordering, error collection, and response-size
limits.
- Have remote filesystems use the server's walk operation directly.
## Testing
- Cover local and remote handling of invalid roots and limits, directory
symlinks, non-UTF-8 names, cancellation, sandbox contexts, and response
budgets.
GitOrigin-RevId: 7499bf05080c3f9965a5eb7ffd593de604d62c2a
## Why
An `apply_patch` path can be replaced with a symlink after verification, allowing an unsandboxed patch operation to reach a different file than the one that was approved.
## What changed
- Add `follow_symlinks` options to executor filesystem reads, writes, metadata lookups, directory creation, and removal, including the corresponding `followSymlinks` protocol fields.
- Implement no-follow filesystem operations on Unix and Windows that reject links in any path component and restrict file access to regular files.
- Run `apply_patch` with symlink traversal disabled when an otherwise-required sandbox is bypassed, while retaining the existing follow-symlink default for standalone callers.
## Testing
- Cover leaf and ancestor symlinks across patch add, update, delete, and move operations, including a path swap after verification.
- Exercise local and remote no-follow filesystem behavior, concurrent directory creation, special-file rejection, and Windows reparse points.
GitOrigin-RevId: 43fd479084891493ce13564fbd894b98f329c6dd
## Why
Project instructions must respect the selected environment's filesystem read
permissions. Tightening those permissions for a later turn must not allow
previously cached instructions to reach the model.
## What changed
- Apply each environment's filesystem sandbox while discovering and reading
`AGENTS.md` files.
- Fail thread or turn setup when sandboxing blocks a discovered instruction
file, while allowing a restricted project with no instructions to start.
- Clear cached instructions before refresh and include the Windows sandbox
level in the cache key.
- Ignore inaccessible ancestor marker probes so readable instructions in the
selected working directory can still load.
## Testing
Added coverage for restricted projects, denied instruction files, cache
invalidation after permissions tighten, and unreadable ancestor markers.
GitOrigin-RevId: 6ea1a27b9c873a6260e4f87d42ae1317a6a4ae4b
## What changed
- Add environment-provided MCP restrictions for configured and plugin-provided servers.
- Disable attachment-scoped servers while their environment configuration is pending, failed, or unselected, while preserving selected-plugin access and the controller-owned Apps server.
- Apply the resolved policy to runtime startup, model tool exposure, telemetry, OAuth flows, and skill dependency installation.
## Testing
- Cover policy filtering for configured and plugin servers, environment state transitions, Apps ownership, and managed OAuth rejection.
GitOrigin-RevId: 7ca5d4dda14068e758497f88835d5cba685e159e
## What changed
- Add `EnvironmentNetworkPolicy` as a serializable view of portable domain,
Unix-socket, upstream-proxy, and local-binding restrictions without exposing
controller-owned proxy runtime settings.
- Add an optional `network_policy` field to `EnvironmentConfig` and re-export
the policy and related permission types through the core APIs.
- Reject owner-provided network policies during environment preview and
readiness until runtime enforcement is implemented, while preserving the
existing controller policy when the field is absent.
## Testing
- Cover rejection through both environment preview and readiness, including
preservation of the existing environment selection.
GitOrigin-RevId: 80ad4cf4d4a45632daa7a823e6cf568eb0e8bb80
## What changed
- Add an optional restrictive execution policy to `EnvironmentConfig` and merge it over the active command policy for shell execution and approval decisions.
- Reject environment policies that contain allow rules, so environment configuration can only tighten command access.
- Include the environment policy fingerprint in cached approval keys so policy changes require fresh session approvals.
## Testing
- Cover restrictions overriding saved prefix approvals, policy changes invalidating session approvals, and environment overlays remaining effective when model-specific prefix filtering is active.
GitOrigin-RevId: e94f877549ed5e0f577b8cb52f3c1872c58f029d
## What changed
- Carry `ShellEnvironmentPolicy` in each resolved `EnvironmentConfig` and use
the selected turn environment's policy for shell commands, user shell tasks,
and unified exec.
- Infer the policy from the thread configuration when an environment does not
provide its own resolved configuration.
- Redact the policy from `EnvironmentConfig` debug output because it can contain
explicit environment variable values.
## Testing
- Verify shell handlers and unified exec filter inherited variables according
to the selected environment while preserving its explicit overrides.
GitOrigin-RevId: 9f6a52aa4af60e5f17251a0e1b11e1926b779055
## Why
Environment attachments can either inherit configuration from their thread or
provide their own. Later thread setting updates must refresh inherited
configuration without overwriting attachment-owned permissions and capability
roots.
## What changed
- Resolve each attachment's configuration when it is selected and retain
whether it came from the thread or the attachment owner.
- Apply subsequent thread configuration updates only to thread-owned
attachments, while preserving ownership across snapshots and child threads.
- Keep the resolved configuration with the environment selection so runtime
consumers use a single canonical value.
## Testing
- Extend remote-environment coverage to verify that thread-owned permissions
follow thread updates while owner-provided read-only permissions remain in
effect.
- Cover configuration inheritance, attachment replacement, and owner-configured
capability roots.
GitOrigin-RevId: fe70c4be5f151432b69bf4b141e316faa89036ae
## What changed
- Add a resolved `permission_profile` to each `EnvironmentConfig` and use the
complete attachment config for execution and capability-root selection.
- Let `Ready` environment configurations override thread permissions while
`FromThread` selections continue to inherit them.
- Restrict inherited Guardian environment profiles to read-only permissions.
## Testing
- Add coverage proving that a read-only environment blocks writes even when
the thread permits workspace writes.
- Update environment inheritance, Guardian review, and capability-root tests
for the resolved attachment configuration.
GitOrigin-RevId: 1a313b9e4892b1a579a0e880e322a782b4f6c0a7
## What changed
- Add `EnvironmentConfigState` to `TurnEnvironmentSelection` so each environment attachment can inherit thread configuration or carry ready configuration for its login-shell policy and selected capability roots.
- Apply ready configuration during thread creation and thread-settings updates while reusing the existing environment connection.
- Validate selection configuration before applying settings, rejecting unsupported pending state and invalid capability roots.
## Testing
- Cover attachment-scoped capability roots, ready configuration at thread startup and turn updates, and rejection of invalid selections without changing existing settings.
GitOrigin-RevId: 86cf8f2148fdaa746ea0dd12890ab7b1577d7587
## What changed
- Store `TurnEnvironmentSelection` directly on each resolved `TurnEnvironment`
instead of copying its environment ID, working directory, and workspace roots
into separate fields.
- Update environment resolution, tool execution, approvals, and related tests to
access selection state through the retained value.
GitOrigin-RevId: ab723d21e112b8af5bcaf1f772a5e376553312bc
## Why
Threads that share an executor environment can require different capability roots and login-shell policies. Treating readiness configuration as executor-wide can expose one thread's settings to another.
## What changed
- Add `CodexThread::environment_ready` and `EnvironmentConfig` so hosts can install capability roots and login-shell policy for an exact thread, environment, and working-directory attachment.
- Validate attachment readiness updates, merge ready attachment roots with persisted thread roots, and refresh MCP capabilities when those roots change.
- Preserve installed environment policy across thread settings updates and child-thread inheritance while resetting it when the selected working directory changes.
## Testing
- Cover attachment config inheritance and reset behavior.
- Verify that threads sharing an executor retain independent model-visible capability roots and login-shell tool schemas across updates.
GitOrigin-RevId: 60e7a296b3e41b835b0413aa2837451e8a55c230
## What changed
- Rename `EnvironmentConfig` to `TurnEnvironmentConfig` to make its scope explicit.
- Rename the corresponding `SessionConfiguration` accessor and update all call sites.
GitOrigin-RevId: f47d745566199e699bd8e7c671c313eaf3bba79e
## Why
Applying `project_doc_max_bytes` independently to every selected environment
allows the total project instruction payload to grow with the number of
environments.
## What changed
Treat `project_doc_max_bytes` as one shared byte budget. Consume it in selected
environment order, truncate the entry that reaches the remaining limit, and
skip later environments once the budget is exhausted.
Update the configuration descriptions to document the aggregate limit.
## Testing
Add unit and integration coverage for partial and exhausted budgets across
multiple environments.
GitOrigin-RevId: 4664bdf1f23b66986349d503f44cf51560b9024f
## What changed
- Include the current permission profile in each turn environment's configuration.
- Refresh retained environment configurations when session settings change, while preserving the configuration already captured by active turns.
- Keep environment connection and shell snapshot resolution reusable across configuration updates.
## Testing
- Cover permission profile updates applied through both session settings updates and per-turn updates.
- Verify that pending environment resolutions use the configuration captured by their turn and that configuration-only updates do not retry failed environments.
GitOrigin-RevId: 923694f0729b87a118d7635ade3a8ca9bcd4e7b1
## What changed
- Store the effective `allow_login_shell` setting on each turn environment, including inherited environments whose child thread has a different policy.
- Expose the `login` argument for shell tools when any selected environment permits login shells.
- Validate each command against the policy of its selected environment instead of the turn-wide configuration.
## Testing
- Cover tool schema generation for single and multiple environments.
- Cover login-shell rejection by both the command handler and the unified exec integration.
GitOrigin-RevId: 5a93149a5c86f4087f2b92d663ebc33feff8a57c
## Why
With deferred execution enabled, rebuilding a step context from the latest
thread environment snapshot could apply a settings update to a turn that was
already in progress.
## What changed
- Preserve each turn's captured environment selections when creating later
step contexts.
- Refresh only the readiness of environments that were already starting, while
retaining their original selection order.
- Apply updated environment selections to the next turn.
## Testing
Add coverage for changing the selected workspace while a turn is paused,
verifying that the active turn keeps its original working directory and the
next turn uses the new one.
GitOrigin-RevId: c6f4388c843384ef045b7abdbc303dcfca11d230
## Why
Multi-environment turns can run against different filesystems, so a single
thread-wide workspace-root list cannot accurately describe every selected
environment.
## What changed
- Add optional `runtimeWorkspaceRoots` to each app-server environment selection.
Omitted roots default to that environment's `cwd`, while an empty list selects
no roots.
- Carry environment-native roots through turn selection, model-visible context,
permission materialization, and tool sandboxing.
- Keep the top-level `runtimeWorkspaceRoots` field as a compatibility input when
constructing default environment selections; explicit selections use their
own roots.
## Testing
- Cover thread and turn API defaults, overrides, empty roots, and schema
round-tripping.
- Verify foreign roots remain model-visible and remote execution applies the
target environment's roots before sandbox selection.
GitOrigin-RevId: c6e7a62883c45c676c153055f46a55ee15a323a4
## What changed
Add regression coverage that verifies ancestor metadata searches:
- keep at most 256 project-root marker or repository skill-root probes in flight;
- start the next probe as capacity becomes available; and
- preserve `AGENTS.md` and repository skill-root discovery order.
GitOrigin-RevId: e7ac90bbeb94573d01e67705001af85ce59165b6
## Why
Serial metadata probes add remote round trips while Codex discovers project
instructions, repository skills, and project-root markers during startup.
## What changed
- Probe ancestor directories concurrently while preserving discovery order.
- Raise the shared find-up probe window to cover typical project hierarchies.
- Refresh project instructions in parallel with plugin and skill warmup.
## Testing
Add coverage that blocks one metadata request and verifies all expected project-root
and `AGENTS.md` ancestor probes have started before it is released.
GitOrigin-RevId: 11449837c7a212ec8ed385e7290476e926d93531
## Why
With deferred executors, a turn can begin before a remote environment
attaches. AGENTS.md discovery previously ran only during session setup,
so instructions from a later environment never reached the model or the
session instruction sources.
WorldState persistence has now landed, so this uses the durable
model-visible baseline directly instead of carrying a temporary
resume/fork compatibility path.
## What
- Add an `AgentsMdManager` in `SessionServices` to own host
instructions, loaded state, and refresh caching.
- When `DeferredExecutor` is enabled, refresh AGENTS.md when attached
environment selections change and freeze the result in the corresponding
`StepContext`.
- Represent AGENTS.md as a persisted WorldState section for every
session, with bounded initial, replacement, and removal updates.
- Remove duplicate AGENTS.md state and rendering from
`SessionConfiguration` and `TurnContext`.
- Build initial context, per-request updates, and compaction context
from the same step-scoped value.
- On resume and fork, compare current instructions with the restored
WorldState baseline and inject a replacement exactly once when they
differ.
Builds on #29833, #29835, and #29837.
## Tests
- Covers a remote environment becoming ready mid-turn, with AGENTS.md
appearing on the next request exactly once and updating canonical
instruction sources.
- Covers full, unchanged, replaced, and removed AGENTS.md WorldState
rendering.
- Covers changed instructions across cold resume and fork without
duplicate reinjection.
- Covers remote-v2 compaction retaining creation-time instructions in
the live session and cold resume appending one replacement when the
source changed.
- Ran focused `codex-core` AGENTS.md, WorldState, and context-update
test suites.
## Why
When Codex uses a remote `ExecutorFileSystem`, every `get_metadata` call
is an exec-server round trip. Upward discovery currently pays those
round trips serially in two latency-sensitive places:
- session startup, while locating the configured project root before
loading `AGENTS.md`; and
- Git-root discovery, which runs before per-turn Git diff enrichment.
The goal is to remove the serial ancestor dependency without adding a
new filesystem RPC, JSON-RPC batch method, Git executable dependency, or
cache.
## Example
Assume this layout, with `.git` as the configured project-root marker:
```text
/workspace/repo/.git
/workspace/repo/AGENTS.md
/workspace/repo/crates/core/ <- cwd
```
The marker probes have this required precedence:
```text
1. /workspace/repo/crates/core/.git
2. /workspace/repo/crates/.git
3. /workspace/repo/.git
4. /workspace/.git
5. /.git
```
Previously, probe 2 was not sent until probe 1 returned, and probe 3 was
not sent until probe 2 returned. With this change, the client lazily
keeps up to eight ordinary `fs/getMetadata` requests in flight, but
consumes their results in the order above. Codex must still learn that
probes 1 and 2 are absent before accepting probe 3, so the nearest root
always wins. Once probe 3 succeeds, the client has its answer and stops
awaiting probes 4 and 5. Requests that were already sent may still
finish on the worker.
For the marker phase alone, with a 50 ms client-to-worker round trip and
fast local metadata calls, finding the root at probe 3 changes from
roughly three serialized round trips (150 ms) to one round trip plus
worker processing. The later `AGENTS.md` candidate phase remains
separate and ordered.
Only after `/workspace/repo` is selected does `AGENTS.md` discovery
check instruction candidates, in root-to-cwd order:
```text
/workspace/repo/AGENTS.override.md
/workspace/repo/AGENTS.md
/workspace/repo/crates/AGENTS.override.md
/workspace/repo/crates/AGENTS.md
/workspace/repo/crates/core/AGENTS.override.md
/workspace/repo/crates/core/AGENTS.md
```
The first configured candidate found in each directory wins. These
checks remain ordered and no instruction candidate above
`/workspace/repo` is issued. Git-root discovery uses the same bounded
lookup with only `.git` as the marker.
## What changed
- Added a client-side find-up helper that generates `ancestor x marker`
probes lazily, nearest directory first and configured marker order
within each directory.
- Uses an ordered concurrency window of eight scalar metadata requests.
This bounds executor load while preserving nearest-root and marker
precedence.
- Reuses the helper for both configured project-root discovery and
remote Git-root discovery.
- Keeps Git ancestor and marker construction in `AbsolutePathBuf`,
converting only each complete `.git` probe to `PathUri`. This preserves
native paths that require an opaque URI fallback, such as Windows
namespace paths.
- Preserves existing error behavior: `AGENTS.md` discovery propagates
non-`NotFound` metadata errors, while Git discovery treats a failed
marker probe as absent and continues upward.
- Reads each discovered `AGENTS.md` directly instead of statting it a
second time.
No filesystem trait or exec-server protocol method is added. An empty
`project_root_markers` list performs no ancestor-marker I/O and checks
instruction candidates only in `cwd`. This change also deliberately does
not cache roots across turns.
## Symlinks
Upward traversal remains **lexical**. The helper does not canonicalize
`cwd`; it appends marker names to the supplied path and walks that
path's textual parents. The filesystem performs the actual metadata/read
operation, and the current local and exec-server implementations follow
live symlink targets.
For example:
```text
/tmp/pkg -> /workspace/repo/packages/pkg
cwd = /tmp/pkg/src
actual Git marker = /workspace/repo/.git
```
The lexical probes are `/tmp/pkg/src/.git`, `/tmp/pkg/.git`,
`/tmp/.git`, and `/.git`. They do not jump from `/tmp/pkg` to the
target's parent `/workspace/repo`, so this spelling of `cwd` does not
discover `/workspace/repo/.git`. That is the existing behavior and is
unchanged by this PR.
Conversely, if `/tmp/repo -> /workspace/repo`, then probing
`/tmp/repo/.git` follows the directory symlink and finds
`/workspace/repo/.git`; the reported root remains the lexical path
`/tmp/repo`. A live symlink used directly as `.git`, another configured
marker, or `AGENTS.md` is also followed. A symlinked `AGENTS.md` is
loaded when its target is a regular file, while a broken symlink behaves
as `NotFound`.
## Why
Remote environments may still be resolving when Codex creates a session
or turn. Waiting for the existing all-or-nothing environment snapshot
can hold startup until the selected environment is usable.
Behind the default-off `deferred_executor` feature, let callers take a
useful snapshot immediately: completed environments remain available
normally, while unfinished environments are reported without blocking
startup. With the feature disabled, snapshots preserve the existing
blocking behavior.
Depends on #28674.
## What changed
- Store one ordered list of selected environments in
`ThreadEnvironments`. Each selection owns one shared resolution that
produces its complete `TurnEnvironment`.
- Start new resolutions in the background with `remote_handle()`,
allowing snapshots and the future wait tool to share the same result
while cancellation follows the retained handles.
- Make `snapshot()` a read-only operation: nonblocking snapshots collect
completed resolutions and retain handles for unfinished ones, while
blocking snapshots await every resolution.
- Replace completed failed resolutions from the current manager entry
and log when failed environments are omitted.
- Return attached and starting environments as a point-in-time view, and
count starting environments when deciding whether a snapshot is
local-only.
- Keep existing consumers attached-only. `to_selections()` derives from
attached environments, so child threads do not inherit an environment
that is still starting.
## Test plan
- `just test -p codex-core environment_selection`
- `just test -p codex-core
deferred_executor_reaches_model_before_remote_environment_is_ready`
## Landing note
Keep `deferred_executor` disabled for slow-starting executors until
configurable `environment/add` connection timeouts and caller support
land. When enabled, an environment that attaches after session startup
may remain absent from environment-derived model context, tools,
instructions, skills, and related state until follow-up refresh work
lands.
## Why
`child_agents_md` is a disabled, under-development experiment that adds
a second model-visible explanation of hierarchical `AGENTS.md` behavior.
Keeping it leaves unused prompt, configuration, documentation, and test
surface.
## What changed
- remove the `ChildAgentsMd` feature and `child_agents_md` config schema
entry
- remove the hierarchical prompt asset, export, and instruction
injection
- remove feature-specific tests and documentation
- keep the generic unstable-feature warning coverage using
`apply_patch_streaming_events`
Normal project `AGENTS.md` discovery and composition are unchanged.
## Testing
- `just test -p codex-features`
- `just test -p codex-prompts`
- `just test -p codex-core agents_md`
- `just test -p codex-core unstable_features_warning`
## Why
Make it possible to load AGENTS.md from remote exec-servers whose OS is
different than app-server.
## What
- keep `AGENTS.md` discovery and provenance as `PathUri`, with
root-aware parent and ancestor traversal
- expose lifecycle instruction sources as legacy app-server path strings
in events while retaining `PathUri` internally
- preserve and test mixed POSIX and Windows paths in model context and
TUI status output
- cover remote Windows loading end to end by seeding the Wine prefix
through host filesystem APIs
- fix bug in `PathUri`'s parent() implementation that would erase
Windows drive letters
## Why
app-server may run on a different OS from the selected exec-server
environment. Parsing that environment’s cwd with the Codex host’s path
rules prevents thread startup.
## What
Carry environment cwd values as `LegacyAppPathString` at the app-server
boundary and `PathUri` internally. Existing tool-call schemas and
relative-path behavior stay host-native; remaining local-only consumers
convert explicitly and leave follow-up TODOs.
The Wine integration test verifies app-server can start a thread and
complete an ordinary turn with a Windows environment cwd from Linux.
## Validation
- `bazel test //codex-rs/core/tests/remote_env_windows:smoke-test
--test_output=errors`
- focused app-server environment-selection and protocol schema tests
- scoped Clippy for `codex-core` and `codex-app-server-protocol`
## Why
`fs/readFile` buffers the entire file in one response, which makes large
remote reads expensive and prevents callers from applying backpressure.
We need an opt-in streaming path with bounded block sizes while
preserving the existing single-call API for small and sandboxed reads.
## What changed
- Add `ExecServerClient::stream`, returning a named `FileReadStream`
that implements `futures::Stream` and yields immutable 1 MiB byte
blocks.
- Add internal `fs/open`, `fs/readBlock`, and `fs/close` RPCs.
`fs/readBlock` accepts an explicit offset and length.
- Keep unsandboxed files open between block reads, cap open handles per
connection, and clean them up on EOF, error, stream drop, explicit
close, or connection shutdown.
- Reject platform-sandboxed streaming opens instead of turning the
one-shot sandbox helper into a persistent server. Existing `fs/readFile`
behavior is unchanged.
## Testing
- `just test -p codex-exec-server`
- Integration coverage for 1 MiB chunking, exact block-boundary EOF,
sandbox rejection, and continued reads from the opened file after path
replacement.
- Handle-manager coverage for non-sequential offsets, variable block
lengths, the 128-handle limit, and capacity release after close.
## Why
Shell snapshots are currently session-scoped even though shell and cwd
are properties of a selected turn environment. That makes snapshot
refresh depend on separate session-cwd plumbing, prevents retained
environments from retaining their snapshot work, and can make snapshot
construction use a different shell than command execution.
This follows #27955 by making the retained thread-environment service
own environment snapshot lifecycles. Session configuration remains the
requested selection state, while `ThreadEnvironments` remains the source
of successfully resolved environments.
## What changed
- Configure the shell-snapshot builder before initial environment
resolution.
- Start each local environment snapshot task when its `TurnEnvironment`
is built and retain that shared task while environment ID and cwd still
match.
- Inherit retained environment snapshots into spawned child threads.
- Carry the selected `TurnEnvironment` through shell runtimes so
snapshot construction and command execution use the same
environment-specific shell and cwd.
- Load project instructions and warm plugins/skills after initial
environment resolution.
- Continue decoding invalid UTF-8 instruction files lossily without
emitting a startup warning.
- Keep requested selections in `SessionConfiguration`; failed or
duplicate resolutions only affect the resolved environment snapshot.
## Validation
- `cargo check -p codex-core --tests`
- `just test -p codex-home instructions` (6 passed)
- Focused environment, instruction, shell-snapshot, and user-shell tests
(84 passed)
- Focused shell-snapshot, user-shell, and unified-exec tests (126
passed; two event-timing tests passed on retry)
## Why
Selected execution environments are thread-scoped resources, but startup
and turn construction repeatedly resolved their IDs and working
directories. That discarded existing environment handles and shell
metadata even when a selection had not changed.
Session configuration updates also need to affect future turns without
changing the resolved environment set already captured by a running
turn.
## What changed
- Create a `ThreadEnvironments` service inside `Codex` from the spawned
`EnvironmentManager` and raw environment selections, then store it on
`SessionServices`.
- Split service construction from `update_selections`, allowing session
configuration updates to mutate the resolved set in place.
- Retain an existing `TurnEnvironment` when its environment ID and
working directory match; resolve only added or changed selections and
remove selections that are no longer present.
- Normalize duplicate IDs by keeping the first selection and skip
individual selections that fail to resolve instead of rejecting the
entire update.
- Give each `TurnContext` a cloned `TurnEnvironmentSnapshot`, so later
session configuration updates affect future turns without rewriting an
active turn.
- Reuse the service-owned environment manager and resolved snapshot for
startup work, MCP initialization, and child-thread spawning instead of
flowing resolved environments through spawn arguments.
## Test plan
- `cargo check -p codex-core --tests`
- `just test -p codex-core environment_selection`
- `just test -p codex-core turn_environments`
- `just test -p codex-core
session_update_settings_does_not_rewrite_sticky_environment_cwds`
- `just test -p codex-core
default_turn_does_not_overlay_legacy_fallback_cwd_onto_stored_thread_environments`
## Why
`PathUri::from_abs_path` can fail for absolute paths that do not have a
normal `file:` URI representation, forcing filesystem call sites to
handle a conversion error even though the original path can be preserved
losslessly.
## What
Make `from_abs_path` infallible and migrate its callers. Unrepresentable
paths use `file:///%00/bad/path/<base64>`, encoding Unix bytes or
Windows UTF-16LE; `to_abs_path` validates and decodes that fallback. The
leading encoded null reserves a namespace that cannot collide with a
real Unix or Windows path, and fallback URIs remain opaque to lexical
path operations.
## Validation
Added path-URI coverage for Unix null and non-UTF-8 paths, Windows
device/verbatim and non-Unicode paths, serialization, malformed
fallbacks, opaque lexical operations, invalid native payloads, and
literal `/bad/path` collision resistance.
## Why
Sandbox cwd values can cross app-server and exec-server host boundaries.
They should retain URI semantics until the receiving host validates them
instead of being interpreted early as native paths.
## What
- Carry `PathUri` through filesystem sandbox contexts, sandbox commands,
and transform inputs.
- Convert command and policy cwd once in `SandboxManager::transform`,
then keep launch requests native.
- Preserve sandbox cwd over remote filesystem transport and reject
non-native URIs without fallback.
- Cache paired native/URI turn-environment cwd values during migration,
with immutable access to keep them synchronized.
- Extend existing protocol, forwarding, transform, and core runtime
tests.
## Why
We already have the machinery to support multiple environments on a
single thread, but we only show the model the contents of `AGENTS.md`
files in the primary environment.
We should show the model all of the relevant project instructions when
we know there's more than one environment.
## Known Gaps
As discussed in the RFC, this implementation:
1. doesn't handle environments being added/removed to/from the thread
after its creation
2. it doesn't enforce an aggregate context budget across environments,
and instead applies the configured project maximum independently to each
environment
## Implementation
- Discover project instructions in environment order with an independent
byte budget per environment and preserve source provenance/order.
- Keep the legacy fragment byte-for-byte when exactly one environment
contributes project instructions; use environment-labeled sections when
two or more environments contribute.
- Freeze the complete rendered fragment in `LoadedAgentsMd`, insert it
directly into requests, and recognize both layouts in contextual and
memory filtering.
- Add exact rendering, independent-budget, source-order,
creation-snapshot, and consumer coverage without changing app-server
schemas.
## Why
First-party async traits should expose their `Send` contracts explicitly
without requiring `async_trait`. This completes the migration pattern
established in #27303 and #27304.
## What changed
- Replaced the remaining first-party `async_trait` traits with native
return-position `impl Future + Send` where statically dispatched and
explicit boxed `Send` futures where object safety is required.
- Kept implementations behavior-preserving, outlining existing async
bodies into inherent methods where that keeps the diff reviewable.
- Removed all direct first-party `async-trait` dependencies and the
workspace dependency declaration.
- Added a cargo-deny policy that permits `async-trait` only through the
remaining transitive wrapper crates.
- Updated `rand` from 0.8.5 to 0.8.6 to resolve RUSTSEC-2026-0097 and
keep the full cargo-deny check passing.
## Validation
- `just test -p codex-exec-server`: 216 passed, 2 skipped.
- `just test -p codex-model-provider`: 39 passed.
- `just test -p codex-core` and `just test`: changed tests passed;
remaining failures are environment-sensitive suites unrelated to this
migration.
- `cargo deny check`
- `just fix`
- `just fmt`
- `cargo shear`
- `just bazel-lock-check`
## Why
We want to remove implicit use of `$CODEX_HOME` from `codex-core` and
make embedders responsible for supplying user-level instructions. This
also ensures user instructions load when no primary environment is
selected.
## What changed
Stacked on #27415, which makes `codex exec` surface thread-scoped
runtime warnings.
- Added `UserInstructionsProvider` to `codex-extension-api`, with
absolute source attribution and recoverable loading warnings.
- Added `codex-home` with the filesystem-backed provider for
`AGENTS.override.md` and `AGENTS.md`, preserving precedence, fallback,
trimming, lossy UTF-8 handling, and the existing uncapped global
instruction size.
- Removed global instruction loading from `Config` and require
`ThreadManager` callers to inject a provider.
- Load provider instructions once for each fresh root runtime, including
runtimes without a primary environment. Running sessions retain their
snapshot, while child agents inherit the parent snapshot without
invoking the provider.
- Keep provider instructions separate while loading project `AGENTS.md`,
then assemble the model-visible instructions with the existing ordering,
source attribution, warning, and turn-context behavior.
- Wired the Codex home provider through the CLI, app server, MCP server,
core facade, and thread-manager sample.
## Validation
- `just test -p codex-home -p codex-extension-api`
- `just test -p codex-core agents_md`
- `just test -p codex-core guardian`
- `just test -p codex-app-server
thread_start_without_selected_environment_includes_only_global_instruction_source`
- `just test -p codex-exec warning`
- `just bazel-lock-check`
## Intent
Follow up on #26205 by avoiding unnecessary filesystem canonicalization
during `AGENTS.md` discovery. The configured working directory is
already absolute, and canonicalization incorrectly switches symlinked
workspaces from their logical parent hierarchy to the target's
hierarchy.
## User-facing behavior
For a symlinked working directory such as:
```text
test-root/
|-- logical-repo/
| |-- AGENTS.md ("logical parent doc")
| `-- workspace ------------> physical-repo/workspace/
`-- physical-repo/
|-- AGENTS.md ("physical parent doc")
`-- workspace/
`-- AGENTS.md ("workspace doc")
```
Before this change, Codex canonicalized `logical-repo/workspace` to
`physical-repo/workspace` before discovery. It therefore loaded
`physical-repo/AGENTS.md` and `physical-repo/workspace/AGENTS.md`,
ignoring the instructions from the repository through which the user
entered the workspace.
After this change, ancestor discovery walks the configured logical path,
so Codex loads `logical-repo/AGENTS.md`. Opening
`logical-repo/workspace/AGENTS.md` still follows the symlink through the
host filesystem, so the workspace document is also loaded.
`physical-repo/AGENTS.md` is not loaded.
## Implementation
Use the logical absolute working directory when discovering project
instructions and reporting instruction sources. Filesystem reads still
follow the working-directory symlink, so an `AGENTS.md` in the target
workspace continues to load while ancestor discovery uses the symlink's
parents.
## Validation
Added integration coverage proving that discovery loads the logical
parent's instructions and the target workspace's instructions, but not
the target parent's instructions.
## Why
Workspace-specific `AGENTS.md` loading needs to use the selected
environment filesystem so remote workspaces and child agents read
instructions from their actual environment instead of the host
filesystem. The app-server should report the same instruction sources
the initialized thread actually loaded, rather than independently
rescanning configuration and filesystem state.
## What changed
- Introduce `LoadedAgentsMd` to retain ordered user, project, and
internal instructions with their provenance.
- Load and canonicalize workspace `AGENTS.md` paths through the primary
`EnvironmentManager` environment, then render the loaded instructions
when constructing turn context.
- Expose cached loaded instruction sources from initialized threads and
use them for app-server start, resume, and fork responses.
- Preserve global `CODEX_HOME` loading and separator behavior while
excluding empty project files that did not supply model-visible
instructions.
- Add integration coverage for CLI injection, selected-environment
provenance and rendering, empty environment selection, and cached
sources on loaded-thread resume.
## Validation
- `just test -p codex-core agents_md`
- `just test -p codex-core
selected_environment_sources_match_model_visible_instructions`
- `just test -p codex-exec agents_md`
- `just test -p codex-app-server instruction_sources`
- `just test -p codex-app-server --status-level fail`
Fixes#23223.
## Why
Malformed AGENTS instructions should not fail silently. The reported
issue had invalid UTF-8 in a global `AGENTS.md`; before this change,
Codex treated that decode failure like a missing file, so the personal
instructions disappeared without a user-visible explanation and the
rollout had no `# AGENTS.md instructions` block.
Project-level AGENTS files already used lossy decoding, so their
instructions still appeared, but invalid bytes were replaced without
telling the user. Global and project AGENTS files should behave
consistently: keep usable instruction text when possible, and surface a
diagnostic when bytes had to be replaced.
## What changed
Global `AGENTS.override.md` and `AGENTS.md` loading now reads bytes and
decodes with replacement characters on invalid UTF-8, matching
project-level AGENTS behavior. Both global and project AGENTS loading
now emit a startup warning when invalid UTF-8 is found, and both keep
the instruction text with invalid byte sequences replaced.
Missing files, non-file candidates, empty files, and the existing
`AGENTS.override.md` before `AGENTS.md` precedence keep their current
behavior.
## How users see it
The warnings flow through the existing startup warning surface.
App-server clients receive config-time startup warnings as
`configWarning` notifications during initialization, and thread startup
emits startup warnings as thread-scoped `warning` notifications.
Global AGENTS invalid UTF-8 warnings can appear on both surfaces.
Project-level AGENTS invalid UTF-8 warnings are discovered while
building thread instructions, so they appear as thread-scoped `warning`
notifications. Clients that render warning notifications in the
conversation surface show the message as a visible diagnostic instead of
silently hiding or altering instructions.
## Summary
`codex.emitImage` accepted arbitrary image MIME types for byte payloads
and data URLs. That allowed a value like `image/rgba` to be wrapped as
an `input_image`, even though it is not a supported encoded image
format, so the invalid image could reach the model-input path and
trigger output sanitization.
This results in a panic in debug builds because the output sanitization
is meant as a final safety net, not a primary means of rejecting invalid
image types. I've hit this case multiple times when executing certain
long-running tasks.
This PR rejects unsupported image MIME types before they are emitted
from `js_repl`.
## Changes
- Validate `codex.emitImage({ bytes, mimeType })` in the JS kernel so
only encoded PNG, JPEG, WebP, or GIF payloads are accepted.
- Apply the same MIME allowlist to direct image data URLs, including the
Rust host-side validation path.
- Clarify the JS REPL instructions so agents know byte payloads must
already be encoded as PNG/JPEG/WebP/GIF.