## Why
App-server logs can be persisted or included in submitted diagnostics, so credentials used by model providers, authentication refreshes, and attestation requests must not appear in diagnostic output.
## What changed
- Add `RedactedString`, which preserves serialization and string access while replacing debug output with `<redacted>`.
- Use it for model-provider bearer tokens, header and query values, authentication command arguments, and attestation tokens.
- Avoid logging JSON-RPC error payloads and parser or authentication errors that may echo credentials; retain safe context such as error codes and categories.
## Testing
- Add an app-server regression test that exercises provider credentials, refreshed authentication tokens, and attestation tokens, then verifies none appear in persisted SQLite or submitted diagnostic logs.
GitOrigin-RevId: 8c50408adf94d93847658b1320682cf3b637d2cc
## Why
External tool events may need to enter thread history without a preceding function call and therefore do not have a `call_id`.
## What changed
- Allow `function_call_output` items to omit `call_id` and carry optional `name` and `namespace` fields.
- Preserve named standalone outputs during history normalization and agent forks while retaining existing pairing behavior for outputs with a `call_id`.
- Accept, persist, and forward these outputs through `thread/inject_items`, and update the app-server schemas and documentation.
## Testing
- Cover paired and standalone JSON round trips, history normalization, agent forks, and injected thread history.
GitOrigin-RevId: a3258163a7dc93777c7c3023116fe204819bdbb0
## 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
## 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
A checkout could point its `.git` file at a trusted repository's worktree
directory without proving that the repository had registered that checkout.
This could cause project configuration from an unrelated checkout to be treated
as trusted.
## What changed
- Verify the linked worktree's `gitdir` backlink, `commondir`, registered
checkout, and main checkout ownership before resolving the main repository's
trust key.
- Reject missing, oversized, symlinked, mismatched, or swapped Git metadata.
- Preserve valid linked worktrees that use path aliases, separate Git
directories, or non-UTF-8 POSIX paths.
## Testing
Add resolver and config-loading coverage for forged worktrees, metadata races,
case-sensitive paths, moved worktrees, and host MCP startup from project config.
GitOrigin-RevId: 6052a7d10ad2d613436f20175c356abdef8c758e
## Why
Plugin installation skips symlinks while copying a plugin into the cache. A
symlinked manifest could therefore be used during source validation but omitted
from the staged copy, allowing a lower-precedence manifest to take its place.
## What changed
- Require discoverable manifests and their parent directories to be regular
files and directories, rejecting symlinks and other non-regular entries at a
higher-precedence manifest path.
- Verify that staging preserves the selected manifest path and contents before
activating the cached plugin.
- Preserve generated fallback manifests by injecting and validating them in the
staged plugin.
## Testing
Add coverage for symlinked manifest files and directories, precedence changes,
and fallback-manifest staging.
GitOrigin-RevId: 1b69c1e75cdfecb8cab4070a32aa9739833a70de
## Why
The Windows PTY implementation probed `conpty.dll` by bare name. Windows could
resolve that lookup to a DLL in the process current directory and load it when
creating a pseudoconsole.
## What changed
Resolve the ConPTY entry points from `kernel32.dll` without probing
`conpty.dll`.
## Testing
Add a Windows regression test that places a compatible `conpty.dll` in the
current directory, creates a ConPTY, and verifies that the DLL was not loaded.
GitOrigin-RevId: be78974362a99dce0c18186cf3e7bbd86a8de73c
## Why
Filesystem permission paths can use a convention that differs from the host
running Codex. Converting them immediately to native absolute paths can change
the meaning of ambiguous paths such as `/C:/secret` or Windows UNC paths.
## What changed
- Store literal filesystem permission paths as `PathUri` values through the
runtime policy and execution protocol.
- Keep legacy string-based serialization at explicit protocol boundaries and
reject conversions that cannot be represented losslessly.
- Encode native paths as opaque URIs when a normal file URI would imply the
wrong path convention.
## Testing
Added coverage for cross-platform and ambiguous path round trips, UNC path
variants, permission-profile serialization, and deny-policy enforcement.
GitOrigin-RevId: 5247713796d1f2bb4e02f94eb9fc82d4698060f0
## What changed
- Preserve valid image content from completed `node_repl` Code Mode responses alongside text evidence for Guardian reviews.
- Enable multimodal evidence for models that require automatic `node_repl` review, or when both `guardian_enhanced_node_repl_transcripts` and `guardian_node_repl_transcript_images` are enabled.
- Bound retained and rendered evidence, deduplicate images already present in review history, and fall back to text-only evidence when the reviewer lacks image support or prompt capacity.
- Keep review evidence out of the parent turn input.
## Testing
- Cover feature gating, mixed text and image ordering, truncation and eviction, unsupported reviewers, prompt pressure, image detail normalization, and reused review sessions.
GitOrigin-RevId: a3e482402f7b3484401b231dadc662e6e3504fdf
## Why
Streaming reads previously rejected requests that used a platform filesystem
sandbox.
## What changed
- Open streamed files in the sandbox helper and return the open file to the
exec-server by passing a file descriptor on Unix or duplicating a file handle
on Windows.
- Advertise support through the `sandboxedFileStreaming` environment capability.
- Preserve close-on-exec behavior for transferred descriptors, including the
required inherited-descriptor cleanup on macOS.
## Testing
- Cover bounded sandboxed streams, continued reads after path replacement, and
rejection of symlink escapes outside readable roots.
GitOrigin-RevId: 677b2444b74e834b78b87a8554bc119c1c6e08b2
## What changed
- Add a scoped `AbsolutePathBufGuard::with_home_directory` override so `~`
paths can resolve against an explicitly supplied home directory while
preserving the existing base-directory behavior. Nested overrides restore
the previous home directory.
- Expose `compose_requirements_for_hostname` so callers can evaluate
`remote_sandbox_config` for a supplied execution-host hostname.
- Cover cloud bundle conversion with execution-host path expansion and
hostname-specific sandbox requirements.
GitOrigin-RevId: 6fd486992b45ff92e22bb4073b888252b544f285
## What changed
- Remove the path-only `effective_skill_roots` API and its type-erasing trait in favor of `effective_plugin_skill_roots`, which preserves plugin metadata.
- Remove skill-path ancestor lookup helpers and keep namespace resolution scoped to an explicit plugin root.
- Update plugin manager and namespace tests to exercise the retained APIs directly.
GitOrigin-RevId: 507bf43bc6147bb5ab2deba4fa176bea2535159c
## Why
Aborting the child waiter during termination can leave an exited PTY child
unreaped and prevent the session from recording its exit status.
## What changed
- Detach the child waiter instead of aborting it when terminating or dropping a
`ProcessHandle`, while continuing to abort the I/O helper tasks.
- Cover explicit termination and handle drops for pipe processes, plus queued
PTY waiters and PTY process-group termination on Unix.
GitOrigin-RevId: 313787f4edec80b30cf5a78f0cc6733f1520d9a4
## Why
A local stdio MCP server can exit while its descendants remain alive. Cleaning up
only the server process can therefore leak processes after client shutdown.
## What changed
- Contain locally launched MCP servers in non-breakaway Windows job objects and
terminate the job during shutdown so descendants exit with the server.
- Preserve compatibility when job creation or assignment is unavailable by
retrying without containment and terminating an owned process handle instead.
- Use the process-group member fallback when terminating piped processes on
macOS.
## Testing
- Cover descendant cleanup and breakaway prevention for both local MCP protocol
modes on Windows.
- Cover the Windows nested-job assignment fallback and verify the suspended
process resumes before handle-based termination.
GitOrigin-RevId: 33d3cc122de8f7ce0ae69a649b12ea0ff64b7c6f
## What changed
- Add a storage-neutral queue extension for listing, adding, editing, reordering, deleting, and explicitly starting queued user messages.
- Dispatch queued messages in FIFO order when a thread becomes idle after a completed turn, while leaving them queued after interruptions or failures.
- Wait for user input to be persisted before removing its queue item, and report hook rejection, task termination, and persistence failures distinctly.
- Snapshot local image and audio attachments into validated data URLs before storing them, enforce input limits, and emit `ThreadQueueChanged` events after mutations.
## Testing
- Cover queue mutations, automatic and explicit dispatch, interruption and failure behavior, hook rejection, invalid records, input limits, and local attachment snapshotting.
GitOrigin-RevId: 14f39d7c8cbaccb767a6da84b82cced0edfcb4b9
## What changed
- Make `PathUri` equality and hashing ignore ASCII case for inferred Windows
drive and UNC paths while preserving case-sensitive POSIX behavior.
- Apply the same convention-aware comparison to `starts_with` and
`relative_path_from`, including equivalent percent-encoded path spellings.
- Continue to fail closed for encoded path separators, opaque fallback URIs,
and non-ASCII case differences.
## Testing
- Add unit coverage for equality and hash consistency, Windows containment and
relative paths, POSIX case sensitivity, encoded separators, and non-ASCII
case differences.
GitOrigin-RevId: cd828874686a9cd242a36262ee3e7a9a172bd5b7
## Why
Signaling an MCP server's process group can fail with `EPERM` on macOS,
preventing owned descendants from being terminated.
## What changed
- When a group signal is denied, enumerate the group's processes and signal
members that still belong to the expected process group.
- Apply the fallback to both `SIGTERM` and `SIGKILL` during MCP stdio server
cleanup, while retaining the existing group signaling behavior elsewhere.
- Reject invalid process group IDs and avoid signaling processes that have
moved to another group.
## Testing
Added macOS tests covering cleanup with a live or exited group leader,
`SIGTERM`-resistant descendants, escalation to `SIGKILL`, and unsafe group IDs.
GitOrigin-RevId: db722475b94dece74244ce8109b3e92f6cfb202a
## What changed
- Add `codex-utils-audio` as a workspace crate for canonicalizing audio inputs
and estimating their token usage.
- Update `codex-core` to consume the new crate while preserving the existing
audio preparation tests.
GitOrigin-RevId: d719ecc08363ef52778aa37f3df0ca14f7778324
## Why
Timing out a Git metadata command must not leave helper processes running after
the command wrapper exits.
## What changed
- Run Git metadata commands in a dedicated process group on Unix and a Job
Object on Windows so timeout cleanup terminates their full process trees.
- Start Windows commands suspended, assign them to the Job Object, and then
resume them so immediate descendants cannot escape containment.
- Preserve descendants when a Git command completes normally, and retain the
existing direct-spawn fallback if Windows Job Object setup fails.
## Testing
Added cross-platform regression tests for cleanup both while the command wrapper
is running and after it exits, plus Windows coverage for immediate-child Job
Object containment.
GitOrigin-RevId: 351851708e23ff06b89fe1894bd09a3558f67293
## Why
Agent Plugins use a schema-declared root `plugin.json` and can have dotted names or versions that do not fit Codex's directory-safe version format. The packaging and installation paths still assumed the legacy manifest layout and identifier rules.
## What changed
- Recognize valid root Agent Plugin manifests when discovering, packing, and installing plugins, while leaving unrelated root manifests on the legacy path.
- Accept safe dotted plugin names, default missing Agent Plugin versions to `1.0.0`, and derive stable directory-safe versions when necessary without rewriting the portable manifest.
- Skip legacy command migration for Agent Plugins and reject symlinks or other unsupported file types while copying plugin sources.
## Testing
Add coverage for portable bundle round trips, manifest discovery, dotted names, version handling, command preservation, and symlink rejection.
GitOrigin-RevId: 61476c4c4100495842253d8b429c0b896490962d
## What changed
- Load `environments.toml` in one operation, falling back to the default
environment provider only when the read reports that the file is missing.
- Attempt the default daemon socket connection directly instead of checking the
socket path first.
- Preserve each file search entry's type from the directory walker, avoiding a
second metadata lookup when producing matches and correctly classifying
followed directory symlinks.
- Write atomic-file contents through the existing temporary-file handle rather
than reopening its path.
## Testing
- Add Unix coverage for classifying a followed directory symlink as a
directory in file search results.
GitOrigin-RevId: aa6739cac23953ca8b9a3fcb53e43e72c41cfdf9
## What changed
- Record the effective detail setting and source and prepared dimensions for
successfully decoded images.
- Associate message images with their role and tool-output images with their
originating call ID.
- Include the collected image preparation metadata in the corresponding turn
analytics event.
## Testing
- Cover image resize metadata, tool-output attribution, turn aggregation, event
serialization, and app-server analytics output.
GitOrigin-RevId: dbfb5ba1ce7cb02dd93d9b579c5109817f16aef5
## What changed
- Add `--approve-for-me` to interactive and exec commands to route approval requests through automatic review.
- Configure the mode with `approval_policy="on-request"` and the `workspace-write` sandbox.
- Propagate the option across root, `exec`, `resume`, and `fork` argument handling while preserving later subcommand permission overrides.
## Testing
- Cover parsing, permission conflicts, root/subcommand precedence, resume handling, and the effective exec approval and sandbox modes.
GitOrigin-RevId: ae969e8c18f925f943049fefff56255f10b25659
## Why
Read command actions were omitted when a selected environment used a path
convention foreign to the app-server host. Clients need these actions to refer
to files in the executor's filesystem, not paths interpreted on the host.
## What changed
- Resolve read paths against the executor's `PathUri` and serialize them using
the executor's native POSIX, Windows drive, or UNC convention.
- Accept same-drive Windows-relative paths while continuing to reject paths
that require another drive's executor-local current directory.
- Document the path convention used by `commandExecution` notifications.
## Testing
Cover native and cross-platform path serialization, Windows drive-relative
joins, and selected-environment `item/started` and `item/completed`
notifications.
GitOrigin-RevId: 3c5a9bb1caafd35e04a0a0cceab91fee590372b9
## Why
Ollama requests need to honor Codex's configured outbound proxy policy and custom CA handling.
## What changed
- Replace Ollama's direct `reqwest` client with a route-aware client created by `codex-http-client`.
- Preserve the five-second connection timeout and legacy system-root fallback for default-routed requests.
- Reuse one Ollama client for connectivity, version, model discovery, and model-pull checks.
- Surface HTTP transport initialization errors instead of replacing them with the generic Ollama connection error.
## Testing
- Cover system-proxy routing and invalid `CODEX_CA_CERTIFICATE` and `SSL_CERT_FILE` values under both outbound proxy policies.
- Verify that version and model checks reuse the existing Ollama client.
GitOrigin-RevId: c7cc36845a9bceb57ce5524b9e1b3cbe317897a3
## What changed
- Convert Windows device-namespace drive paths such as `\\?\D:\reports` and
`\\.\D:\reports` to canonical drive-based `file:` URIs.
- Convert device-namespace UNC paths to canonical hosted `file:` URIs.
- Preserve unsupported or ambiguous namespace paths as opaque path URIs instead
of interpreting them as filesystem paths.
## Testing
Add coverage for namespace path conversion and joining, including malformed UNC
paths, reserved device names, and volume identifiers.
GitOrigin-RevId: 234f69bc2787e5d318bc0320b94e7392abfb5602
## Why
Windows non-TTY exec sessions reported interrupts as unsupported, so sending
Ctrl-C through `write_stdin` did not stop the running process.
## What changed
- Route interrupt requests for Windows non-TTY processes through their existing
termination callback, including pipe-backed processes.
- Track whether driver-backed Windows sessions use a TTY so PTY interrupts keep
their existing behavior.
- Consume the terminator after a successful interrupt to avoid invoking it again
when the process handle is dropped.
## Testing
Added coverage for local and remote exec-server sessions, unified exec, pipe
fallbacks, and legacy Windows sandbox processes.
GitOrigin-RevId: 34504d01f091ef57bb961e98ad5a8d9f1acee4ee
## What changed
- Carry a plugin's local and remote identities together from plugin loading into
`SkillMetadata`.
- Resolve remote IDs from the installed-plugin snapshot when available, falling
back to persisted install metadata only when no snapshot exists.
- Include plugin identity in skill cache keys so identity changes refresh cached
skill metadata.
## Testing
- Cover snapshot and persisted identity resolution, local marketplace isolation,
cached skill refreshes, and propagation through plugin skill snapshots.
GitOrigin-RevId: aabeeb631a43361fe817358ace7f1ea8ba5db708
## What changed
- Recognize root `plugin.json` files using the Agent Plugins 1.0 schema and map their portable metadata, `skills/`, and `mcp.json` into Codex plugin manifests.
- Apply Codex-specific apps, hooks, and interface settings from the inline `com.openai` extension, with `.codex-plugin/plugin.json` as a fallback overlay.
- Preserve legacy manifest precedence when a root `plugin.json` is unrelated, and reject unsupported Agent Plugins schema versions.
- Add a direct-child skill discovery mode that excludes nested skills and paths resolving outside the plugin root.
## Testing
- Cover manifest metadata, validation, extension precedence, legacy fallback, and direct-child skill path boundaries.
GitOrigin-RevId: eab24139f13a5cc5cb3ad3fb444d8e904511aca6
## Why
Turn diffs need to render paths from remote environments whose path convention may differ from the Codex host.
## What changed
- Preserve `PathUri` values through applied patch deltas, display-root discovery, and turn-diff tracking.
- Add host-independent relative-path rendering and normalize Windows drive-letter case in `PathUri`.
- Render paths relative to each environment's repository root while keeping Git diff headers slash-separated.
## Testing
- Cover POSIX, Windows, UNC, encoded-separator, and opaque URI relative paths.
- Verify an applied patch in a remote Windows working directory emits a portable unified diff.
GitOrigin-RevId: 526f70ae36a40627de5496d375310d12295b69bb
## Why
The PTY and pipe spawn helpers now require callers to specify which file
descriptors should be inherited, following
https://github.com/openai/codex/pull/13644.
## What changed
Pass empty inherited-FD slices from the Windows process-tree tests, which do
not need to preserve any file descriptors.
GitOrigin-RevId: 5e578df34e195251490608a163f8aaee0a6e681c
## Why
Terminating a Windows execution session must also stop child processes, while a
normal root-process exit must continue to allow background descendants to run.
## What changed
- Assign Windows pipe, ConPTY, and sandbox processes to job objects and terminate
the job when a session is cancelled, times out, or is explicitly stopped.
- Preserve descendants when the root process exits normally.
- Attach ConPTY and sandbox processes to their jobs atomically at creation; keep
root-process termination as a fallback where job setup is unavailable.
## Testing
Added Windows coverage for descendant termination and preservation across pipe,
ConPTY, capture, cancellation, and legacy sandbox execution paths.
GitOrigin-RevId: 8f831f2fc4caaa7b79ce842a3ed7192bd02dd3b4
## Why
Sandboxed process launch through the exec server was unsupported on Windows.
## What changed
- Add a shared native process launcher that selects the Windows sandbox session backend when required and otherwise launches PTY or pipe-based processes.
- Pass Windows permission, workspace-root, proxy, filesystem-override, and desktop settings through exec-server process preparation.
- Preserve piped stdin and inherited file descriptors across the shared launch path.
## Testing
- Verify that a remote Windows sandbox process accepts stdin while blocking a write under a read-only policy.
- Enable workspace-root sandbox tests on native Windows while continuing to skip environments that cannot emulate Windows sandbox semantics.
GitOrigin-RevId: 6719ccbe87ad0d5a57af56503e3f23f894c60a71
## Why
Audio attachments could be dropped from tool outputs and reconstructed thread history, while inline base64 payload size could overstate their model context cost.
## What changed
- Preserve remote and local audio attachments in legacy user-message events and thread history.
- Keep audio from function and custom tool outputs when the model supports it and the output fits the truncation budget; otherwise replace it with an omission marker.
- Estimate inline audio cost from its duration, with a serialized-size fallback for malformed or unsupported data URLs, so compaction and truncation use model-visible cost instead of raw base64 size.
- Replace audio in messages and tool outputs with an explanatory placeholder for models without audio input support.
## Testing
Add coverage for history round trips, dynamic tool responses, modality filtering, duration-based context estimates, output-budget truncation, and remote compaction.
GitOrigin-RevId: 5ed4628850a668f8383b7cd73db71b144cc02cd1
## What changed
- Remove unused APIs and test helpers across the Rust workspace.
- Route Linux sandbox test setup through the shared exec request path.
- Drop dependencies that became unnecessary after the cleanup.
GitOrigin-RevId: 48fd9b2b1575bad7add0819786b4d8ec88d8486f
## What changed
- Add remote and local audio variants to core and app-server user inputs, including generated JSON and TypeScript schemas.
- Preserve audio fields when converting app-server inputs and response content into thread items.
- Keep unsupported audio out of model requests, TUI rendering, text extraction, and truncated tool output, using explicit text placeholders where audio is submitted as a model input.
## Testing
- Cover app-server media conversion, unsupported-input placeholders, TUI omission, and tool-output truncation.
GitOrigin-RevId: 16152bf3af674575ce569764a8b472e032e7569d
## What changed
Remove tests that only restate feature registry metadata or constant values. Keep coverage for behavioral contracts such as feature aliases, dependency normalization, deprecated settings, and ignored removed settings.
GitOrigin-RevId: 45e2919dc3d854da3192ad7057a0f1c5c60271b9
## Why
Selected capability roots can contribute plugins, MCP servers, connectors, and
skills. Discovering each contribution separately requires repeated access to the
executor filesystem.
## What changed
- Add the `capabilityRoots/discoverV1` exec-server RPC to scan selected roots and
materialize recognized plugin manifests, configuration files, skill
instructions, and skill metadata in one bounded request.
- Add the opt-in `executor_capability_discovery` feature, with a thread-scoped
cache and per-step snapshot shared by MCP and skill discovery.
- Parse MCP, connector, and skill contributions from the materialized snapshot,
including serving cached skill instructions without another filesystem read.
## Testing
- Cover discovery limits, manifest precedence, root-local failures, cache reuse,
plugin contributions, and parity with the existing environment skill loader.
GitOrigin-RevId: f98fd2321cafb58c596db02da1f83c09d8eb375d
## What changed
- Detect and import supported Cursor settings, sandbox permissions, MCP servers,
project instructions, hooks, agents, commands, plugins, and recent chat
sessions.
- Update the `/import` flow to check Claude Code and Cursor, prompting for a
source when both have importable data.
- Add `migrationSource` to the app-server detect and import requests so each
operation uses the selected adapter, while retaining `source` for import
attribution and backwards compatibility.
## Testing
- Add coverage for Cursor config conversion, hooks, plugins, session discovery
and conversion, app-server imports, and TUI source selection.
GitOrigin-RevId: a65a1511b6c1c2f9e3e0cfe4f816bb435807282e
## Why
Special filesystem subpaths can contain relative or foreign-platform path
spellings. Treating this wire text as a host-native path can reinterpret it.
## What changed
- Store special filesystem subpaths as strings in the core permissions model.
- Represent app-server subpaths as `LegacyAppPathString` and convert them at
the protocol boundary without changing their spelling.
- Add `LegacyAppPathString::from_string` for callers that already own legacy
app-server path text.
GitOrigin-RevId: 2214f03aa997fd0e6eb2ab60d425304bb4bd6132
## Why
`PathUri::join` normalized `..` for relative paths, but its
absolute-path branch rebuilt URIs through `url::PathSegmentsMut::push`,
which skips dot segments. `/tmp/a/../b` therefore resolved to `/tmp/a/b`
instead of `/tmp/b`.
## What changed
Normalize absolute native path segments before constructing the file
URI. Parent traversal now clamps at POSIX roots, Windows drive roots,
and UNC share roots, including paths with repeated separators.
Add platform-independent coverage for POSIX, drive, UNC, root-clamping,
and repeated-separator cases.
## Manual validation
- `just test -p codex-utils-path-uri`
## Why
Windows unified-exec TTY input did not behave like the non-Windows PTY
path. ConPTY sessions could receive the wrong line ending or mishandle
backspace, especially when sending input to a foreground program through
PowerShell or cmd. The local, legacy restricted, and elevated paths also
handled this normalization separately.
## What changed
- share one stateful Windows TTY input normalizer across local, legacy
restricted, and elevated runner paths
- translate LF and split CRLF into one Windows terminal Enter, encode
backspace as DEL, and preserve UTF-8 and control bytes such as Ctrl-C
- add Windows integration coverage for Unicode input, backspace, Enter,
and PowerShell foreground-child Ctrl-C behavior
## Validation
- `just test -p codex-utils-pty` (13 tests passed; the Unicode
integration test retried once)
- the Unicode integration test passed five consecutive runs with retries
disabled
- integration coverage sends `cafeé 漢字` through cmd and PowerShell and
verifies that Ctrl-C interrupts a running PowerShell foreground child
## Why
Executor skill discovery runs before the remote skills catalog is
available. For a remote environment, each `ExecutorFileSystem` operation
becomes an exec-server RPC.
Previously, every discovered `SKILL.md` independently resolved its
plugin namespace by walking its ancestors and probing both supported
manifest locations. In the common `plugin/skills/<skill>/SKILL.md`
layout, that repeats 8 RPCs per skill even though every skill under the
plugin root uses the same namespace. These lookups happen while skills
are parsed, so their cost grows linearly with the skill count and adds
directly to first-turn latency.
A selected capability root can also contain standalone skills, multiple
sibling plugins, nested plugins, or symlinked directories. The
optimization therefore needs to retain the nearest-ancestor namespace
for each skill rather than assuming the selected root represents exactly
one plugin.
## What changed
- record plugin-root candidates from directory entries already returned
during skill discovery
- prune candidates that are not ancestors of any discovered `SKILL.md`
before reading manifests
- resolve each relevant plugin root once, with one fallback lookup per
canonical traversal root for symlinked directories
- select the nearest cached plugin namespace for each discovered skill
- avoid namespace lookup entirely when the root contains no skills
No additional directory traversal is required. Namespace work now scales
with the number of plugin roots that contain discovered skills, rather
than the total number of skills or unrelated sibling plugins. Standalone
and nested-plugin names keep their previous behavior.
## Benchmarks
I used a temporary counting `ExecutorFileSystem` around the real local
filesystem. Each filesystem operation was counted as one remote RPC and
given 1 ms of injected latency. Each variant ran three times; times
below are medians.
### One plugin with 100 skills
| Operation | Before | After | Delta |
| --- | ---: | ---: | ---: |
| `get_metadata` | 1,002 | 303 | -699 |
| `read_file` | 200 | 101 | -99 |
| `read_directory` | 102 | 102 | 0 |
| **Total filesystem RPCs** | **1,304** | **506** | **-798 (-61.2%)** |
| **Median load time** | **2.890 s** | **0.997 s** | **2.90× faster** |
The namespace-specific work drops from 800 RPCs to 2 in this layout.
### Multiple plugins under one selected root
These runs compare the correct pre-optimization implementation with the
final nearest-plugin-root cache. The total plugin skill count stays at
100 while the number of plugin roots changes.
| Layout | Before RPCs | After RPCs | Reduction | Before | After |
Speedup |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| 2 plugins × 50 skills | 1,312 | 530 | 59.6% | 1,819 ms | 711 ms |
2.56× |
| 10 plugins × 10 skills | 1,344 | 578 | 57.0% | 1,850 ms | 778 ms |
2.38× |
| 50 plugins × 2 skills | 1,504 | 818 | 45.6% | 2,094 ms | 1,086 ms |
1.93× |
| 10 plugins × 10 skills + 10 standalone skills | 1,596 | 630 | 60.5% |
2,209 ms | 860 ms | 2.57× |
The remaining cost grows with the number of relevant plugin manifests.
Each relevant manifest is read once instead of once per skill, while
sibling plugins with no discovered skills are not read. Absolute latency
savings depend on the executor's real RPC latency.
## Tests
- `just test -p codex-core-skills` (109 passed across the library and
integration-test binaries)
- one integration test covers standalone, outer-plugin, nested-plugin,
and unused sibling-plugin layouts, and asserts the exact set of
manifests read
## Why
After #28918, selected skill roots are `PathUri`, but the executor skill
provider still converts them to the app-server host's `AbsolutePathBuf`.
A foreign Windows root therefore cannot be discovered by a Unix host,
and the inverse has the same problem.
This PR keeps executor skill discovery and reads on the filesystem that
owns the selected root while reusing the existing skill rules.
## What changed
- Generalize the existing skill traversal to operate on `PathUri`
through `ExecutorFileSystem`, preserving its depth, directory, symlink,
and sibling-metadata concurrency behavior.
- Add a small environment skill loader that reuses the shared discovery,
frontmatter validation, dependency parsing, product policy, and
prompt-visibility rules.
- Keep the environment id and entrypoint `PathUri` in the skill catalog,
then route `skills.read` back through the same environment filesystem.
- Preserve the executor's path convention when deriving catalog handles,
including literal backslashes in POSIX filenames.
- Resolve plugin namespaces from nearby manifests through URI-native
filesystem reads.
- Cover foreign Windows roots, executor-owned reads, namespaces,
metadata, policy, and path identity.
```text
selected root (PathUri)
|
v
shared discovery over ExecutorFileSystem
|
v
environment-bound catalog entry --skills.read--> same ExecutorFileSystem
```
No second filesystem abstraction or duplicate traversal implementation
is introduced.
## Stack
1. #29614 — add lexical `PathUri` containment.
2. #29620 — share URI-native manifest path resolution.
3. #28918 — keep selected plugin roots and resources URI-native.
4. **This PR** — load executor skills without host path conversion.
5. #29628 — resolve executor MCP working directories without host path
conversion.
## Why
Selected capability roots belong to the executor filesystem, not the
app-server host. Converting their path strings into the host's native
`Path` breaks whenever the two machines use different path conventions,
such as a Windows executor behind a Unix app-server.
This PR establishes `PathUri` as the selected-plugin boundary so the
executor remains authoritative for its paths.
## What changed
- Require `selectedCapabilityRoots[].location.path` to be a canonical
`file:` URI and deserialize it directly as `PathUri`; native path
strings are rejected.
- Update the app-server schema, generated TypeScript, examples, and
request coverage for the URI contract.
- Keep selected roots, resolved plugin locations, manifest paths, and
manifest resources as `PathUri`.
- Inspect and read plugin roots and manifests only through the selected
environment's `ExecutorFileSystem`.
- Parse executor manifests with the shared URI-native parser from #29620
instead of projecting them onto the host filesystem.
- Enforce resource containment lexically and preserve the root URI's
POSIX or Windows path convention.
- Cover foreign Windows plugin roots and URI-native manifest resources.
```text
thread/start
selectedCapabilityRoots[].location.path = "file:///C:/plugins/demo"
| PathUri
v
ExecutorFileSystem
|
+--> plugin.json
+--> manifest resources
```
This PR stops at the shared selected-plugin representation. The next two
PRs remove the remaining host-path projections in the skill and MCP
consumers.
## Stack
1. #29614 — add lexical `PathUri` containment.
2. #29620 — share URI-native manifest path resolution.
3. **This PR** — keep selected plugin roots and resources URI-native.
4. #29626 — load executor skills without host path conversion.
5. #29628 — resolve executor MCP working directories without host path
conversion.
## Why
I'd originally added `PathUri` legacy path deserialization thinking we'd
want it for having `PathUri` in public app-server APIs. Since then we've
added `LegacyAppPathString` to handle the messy conversions that we need
for backcompat. It's confusing for `PathUri` to support deserializing
legacy paths when we don't yet want to actually expose app-server
callers or rollout storage to the new URI format.
Stacked on top of #29472 to avoid breaking compatibility in case those
types ended up stored somewhere for someone.
## What changed
- Parse deserialized `PathUri` values exclusively as valid `file:` URIs.
- Replace legacy acceptance coverage with rejection coverage for
top-level filesystem paths and sandbox working directories.
- Serialize CWDs in hand-built exec-server process requests as `PathUri`
values.
## Why
Plugin manifests use the same schema whether the package lives on the
host or in an executor. Only the path representation differs: host
callers need native `Path` inputs and `AbsolutePathBuf` outputs, while
executor callers need `PathUri` throughout.
Maintaining separate parsing or resolver implementations would duplicate
the manifest rules and allow them to drift. This PR instead makes
URI-native resolution the single parsing path and keeps host conversion
at the boundary.
## What changed
- Make `parse_plugin_manifest_uri` the shared manifest parser and
resolve every path-bearing field as `PathUri`.
- Keep the existing host entrypoint as a thin adapter: convert its
native root and manifest path to `PathUri`, run the shared parser, then
map resources back to `AbsolutePathBuf`.
- Expose `PluginManifest::try_map_resources` so callers can convert the
generic resource type without duplicating manifest construction.
- Resolve relative manifest paths using the root URI's convention:
backslashes are separators for Windows roots and ordinary filename
characters for POSIX roots.
- Apply lexical containment after URI resolution, rejecting absolute
paths and parent traversal outside the plugin root.
- Make encoded backslashes fail containment only for Windows URIs;
encoded `/` remains unsafe for every convention.
- Use a host-native synthetic root for marketplace fallback manifests so
the host adapter also works on Windows.
```text
host Path --------> PathUri --\
+--> one manifest parser --> PluginManifest<PathUri>
executor PathUri -------------/
host result: PluginManifest<PathUri> --> PluginManifest<AbsolutePathBuf>
```
Existing host manifest behavior is preserved; #28918 is the first
executor consumer.
## Verification
- `just test -p codex-utils-path-uri`
- `just test -p codex-plugin`
- `just test -p codex-core-plugins`
## Stack
1. #29614 — add lexical `PathUri` containment.
2. **This PR** — share URI-native manifest path resolution.
3. #28918 — keep selected plugin roots and resources URI-native.
4. #29626 — load executor skills without host path conversion.
5. #29628 — resolve executor MCP working directories without host path
conversion.
## Why
view_image needs to support foreign OS remote executors.
## What
- resolve image paths against the selected environment as `PathUri` and
read them through that environment's filesystem
- keep app-server's public path field wire-compatible as
`LegacyAppPathString`, with purpose-specific UI rendering
- cover relative and absolute target-native paths in the core
integration test and run the full `view_image` suite under wine-exec
without skips