Michael Bolin
90d · built 2026-08-09
90-day totals
- Commits
- 141
- Grow
- 13.1
- Maintenance
- 20.5
- Fixes
- 1.6
- Total ETV
- 35.1
Where this dev ranks
Percentile against the global top-100 leaderboard (all-time totals).
- By commits
- Top 19 %
- By Growth share
- Top 58 %
30-day trajectory
Last 30 days vs. the 30 days before. Up arrows on Growth and ETV mean improvement; up arrow on Fixes share means more time on fixes (worse).
↓-36.1 %
vs 36 prior
↓-4.6 pp
recent vs prior
↑+5.9 pp
recent vs prior
Daily performance
Daily ETV, stacked by Growth, Maintenance and Fixes.
Work-mix over time
Share of Growth / Maintenance / Fixes over a rolling 7-day window. Reads as 'where is effort flowing right now'.
Repository spread
Where this developer's commits land. Concentrated work (top1 > 80%) vs polymath spread (top1 < 30%).
Most impactful commits
Top 20 by ETV in the 90-day window.
- 2.0ETVconfig: add strict config parsing (#20559) ## Why Codex intentionally ignores unknown `config.toml` fields by default so older and newer config files keep working across versions. That leniency also makes typo detection hard because misspelled or misplaced keys disappear silently. This change adds an opt-in strict config mode so users and tooling can fail fast on unrecognized config fields without changing the default permissive behavior. This feature is possible because `serde_ignored` exposes the exact signal Codex needs: it lets Codex run ordinary Serde deserialization while recording fields Serde would otherwise ignore. That avoids requiring `#[serde(deny_unknown_fields)]` across every config type and keeps strict validation opt-in around the existing config model. ## What Changed ### Added strict config validation - Added `serde_ignored`-based validation for `ConfigToml` in `codex-rs/config/src/strict_config.rs`. - Combined `serde_ignored` with `serde_path_to_error` so strict mode preserves typed config error paths while also collecting fields Serde would otherwise ignore. - Added strict-mode validation for unknown `[features]` keys, including keys that would otherwise be accepted by `FeaturesToml`'s flattened boolean map. - Kept typed config errors ahead of ignored-field reporting, so malformed known fields are reported before unknown-field diagnostics. - Added source-range diagnostics for top-level and nested unknown config fields, including non-file managed preference source names. ### Kept parsing single-pass per source - Reworked file and managed-config loading so strict validation reuses the already parsed `TomlValue` for that source. - For actual config files and managed config strings, the loader now reads once, parses once, and validates that same parsed value instead of deserializing multiple times. - Validated `-c` / `--config` override layers with the same base-directory context used for normal relative-path resolution, so unknown override keys are still reported when another override contains a relative path. ### Scoped `--strict-config` to config-heavy entry points - Added support for `--strict-config` on the main config-loading entry points where it is most useful: - `codex` - `codex resume` - `codex fork` - `codex exec` - `codex review` - `codex mcp-server` - `codex app-server` when running the server itself - the standalone `codex-app-server` binary - the standalone `codex-exec` binary - Commands outside that set now reject `--strict-config` early with targeted errors instead of accepting it everywhere through shared CLI plumbing. - `codex app-server` subcommands such as `proxy`, `daemon`, and `generate-*` are intentionally excluded from the first rollout. - When app-server strict mode sees invalid config, app-server exits with the config error instead of logging a warning and continuing with defaults. - Introduced a dedicated `ReviewCommand` wrapper in `codex-rs/cli` instead of extending shared `ReviewArgs`, so `--strict-config` stays on the outer config-loading command surface and does not become part of the reusable review payload used by `codex exec review`. ### Coverage - Added tests for top-level and nested unknown config fields, unknown `[features]` keys, typed-error precedence, source-location reporting, and non-file managed preference source names. - Added CLI coverage showing invalid `--enable`, invalid `--disable`, and unknown `-c` overrides still error when `--strict-config` is present, including compound-looking feature names such as `multi_agent_v2.subagent_usage_hint_text`. - Added integration coverage showing both `codex app-server --strict-config` and standalone `codex-app-server --strict-config` exit with an error for unknown config fields instead of starting with fallback defaults. - Added coverage showing unsupported command surfaces reject `--strict-config` with explicit errors. ## Example Usage Run Codex with strict config validation enabled: ```shell codex --strict-config ``` Strict config mode is also available on the supported config-heavy subcommands: ```shell codex --strict-config exec "explain this repository" codex review --strict-config --uncommitted codex mcp-server --strict-config codex app-server --strict-config --listen off codex-app-server --strict-config --listen off ``` For example, if `~/.codex/config.toml` contains a typo in a key name: ```toml model = "gpt-5" approval_polic = "on-request" ``` then `codex --strict-config` reports the misspelled key instead of silently ignoring it. The path is shortened to `~` here for readability: ```text $ codex --strict-config Error loading config.toml: ~/.codex/config.toml:2:1: unknown configuration field `approval_polic` | 2 | approval_polic = "on-request" | ^^^^^^^^^^^^^^ ``` Without `--strict-config`, Codex keeps the existing permissive behavior and ignores the unknown key. Strict config mode also validates ad-hoc `-c` / `--config` overrides: ```text $ codex --strict-config -c foo=bar Error: unknown configuration field `foo` in -c/--config override $ codex --strict-config -c features.foo=true Error: unknown configuration field `features.foo` in -c/--config override ``` Invalid feature toggles are rejected too, including values that look like nested config paths: ```text $ codex --strict-config --enable does_not_exist Error: Unknown feature flag: does_not_exist $ codex --strict-config --disable does_not_exist Error: Unknown feature flag: does_not_exist $ codex --strict-config --enable multi_agent_v2.subagent_usage_hint_text Error: Unknown feature flag: multi_agent_v2.subagent_usage_hint_text ``` Unsupported commands reject the flag explicitly: ```text $ codex --strict-config cloud list Error: `--strict-config` is not supported for `codex cloud` ``` ## Verification The `codex-cli` `strict_config` tests cover invalid `--enable`, invalid `--disable`, the compound `multi_agent_v2.subagent_usage_hint_text` case, unknown `-c` overrides, app-server strict startup failure through `codex app-server`, and rejection for unsupported commands such as `codex cloud`, `codex mcp`, `codex remote-control`, and `codex app-server proxy`. The config and config-loader tests cover unknown top-level fields, unknown nested fields, unknown `[features]` keys, source-location reporting, non-file managed config sources, and `-c` validation for keys such as `features.foo`. The app-server test suite covers standalone `codex-app-server --strict-config` startup failure for an unknown config field. ## Documentation The Codex CLI docs on developers.openai.com/codex should mention `--strict-config` as an opt-in validation mode for supported config-heavy entry points once this ships.github.com-openai-codex · 889ee018 · 2026-05-13
- 2.0ETVpermissions: support workspace roots in profiles (#22610) ## Why This is the configuration/model half of the alternative permissions migration we discussed as a comparison point for [#22401](https://github.com/openai/codex/pull/22401) and [#22402](https://github.com/openai/codex/pull/22402). The old `workspace-write` model mixes three concerns that we want to keep separate: - reusable profile rules that should stay immutable once selected - user/runtime workspace roots from `cwd`, `--add-dir`, and legacy workspace-write config - internal Codex writable roots such as memories, which should not be shown as user workspace roots This PR gives permission profiles first-class `workspace_roots` so users can opt multiple repositories into the same `:workspace_roots` rules without using broad absolute-path write grants. It also starts separating the raw selected profile from the effective runtime profile by making `Permissions` expose explicit accessors instead of public mutable fields. A representative `config.toml` looks like this: ```toml default_permissions = "dev" [permissions.dev.workspace_roots] "~/code/openai" = true "~/code/developers-website" = true [permissions.dev.filesystem.":workspace_roots"] "." = "write" ".codex" = "read" ".git" = "read" ".vscode" = "read" ``` If Codex starts in `~/code/codex` with that profile selected, the effective workspace-root set becomes: - `~/code/codex` from the runtime `cwd` - `~/code/openai` from the profile - `~/code/developers-website` from the profile The `:workspace_roots` rules are materialized across each root, so `.git`, `.codex`, and `.vscode` stay scoped the same way everywhere. Runtime additions such as `--add-dir` can still layer on later stack entries without mutating the selected profile. ## Stack Shape This PR intentionally stops before the profile-identity cleanup in [#22683](https://github.com/openai/codex/pull/22683) so the base review stays focused on config loading, workspace-root materialization, and compatibility with legacy `workspace-write`. The representation in this PR is therefore transitional: `Permissions` carries enough state to distinguish the raw constrained profile from the effective runtime profile, and there are still call sites that must keep the active profile identity and constrained profile value in sync. The follow-up PR replaces that with a single resolved profile state (`ResolvedPermissionProfile` / `PermissionProfileState`) that keeps the profile id, immutable `PermissionProfile`, and profile-declared workspace roots together. That follow-up removes APIs such as `set_constrained_permission_profile_with_active_profile()` where separate arguments could drift out of sync. Downstream PRs then build on this base to switch app-server turn updates to profile ids plus runtime workspace roots and to finish the user-visible summary behavior. Reviewers should judge this PR as the workspace-roots foundation, not as the final in-memory shape of selected permission profiles. ## Review Guide Suggested review order: 1. Start with `codex-rs/core/src/config/mod.rs`. This is the main shape change in the base slice. `Permissions` now stores a private raw `Constrained<PermissionProfile>` plus runtime `workspace_roots`. Callers use `permission_profile()` when they need the raw constrained value and `effective_permission_profile()` when they need a materialized runtime profile. As noted above, [#22683](https://github.com/openai/codex/pull/22683) replaces this transitional shape with a resolved profile state that keeps identity and profile data together. 2. Review `codex-rs/config/src/permissions_toml.rs` and `codex-rs/core/src/config/permissions.rs`. These add `[permissions.<id>.workspace_roots]`, resolve enabled entries relative to the policy cwd, and keep `:workspace_roots` deny-read glob patterns symbolic until the actual roots are known. 3. Review `codex-rs/protocol/src/permissions.rs` and `codex-rs/protocol/src/models.rs`. These add the policy/profile materialization helpers that expand exact `:workspace_roots` entries and scoped deny-read globs over every workspace root. This is also where `ActivePermissionProfileModification` is removed from the core model. 4. Review the legacy bridge in `Config::load_from_base_config_with_overrides` and `Config::set_legacy_sandbox_policy`. This is where legacy `workspace-write` roots become runtime workspace roots, while Codex internal writable roots stay internal and do not appear as user-facing workspace roots. 5. Then skim downstream call sites. The interesting pattern is raw-vs-effective access: state/proxy/bwrap paths keep the raw constrained profile, while execution, summaries, and user-visible status use the effective profile and workspace-root list. ## What Changed - added `[permissions.<id>.workspace_roots]` to the config model and schema - added runtime `workspace_roots` state to `Config`/`Permissions` and `ConfigOverrides` - made `Permissions` profile fields private and replaced direct mutation with accessors/setters - added `PermissionProfile` and `FileSystemSandboxPolicy` helpers for materializing `:workspace_roots` exact paths and deny-read globs across all roots - moved legacy additional writable roots into runtime workspace-root state instead of active profile modifications - removed `ActivePermissionProfileModification` and its app-server protocol/schema export - updated sandbox/status summary paths so internal writable roots are not reported as user workspace roots ## Verification Strategy The targeted tests cover the behavior at the layers where regressions are most likely: - `codex-rs/core/src/config/config_tests.rs` verifies config loading, legacy workspace-root seeding, effective profile materialization, and memory-root handling. - `codex-rs/core/src/config/permissions_tests.rs` verifies profile `workspace_roots` parsing and `:workspace_roots` scoped/glob compilation. - `codex-rs/protocol/src/permissions.rs` unit tests verify exact and glob materialization over multiple workspace roots. - `codex-rs/tui/src/status/tests.rs` and `codex-rs/utils/sandbox-summary/src/sandbox_summary.rs` verify the user-facing summaries show effective workspace roots and hide internal writes. I also ran `cargo check --tests` locally after the latest stack refresh to catch cross-crate API breakage from the private-field/accessor changes. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22610). * #22612 * #22611 * #22683 * __->__ #22610github.com-openai-codex · c25d905f · 2026-05-15
- 1.2ETVapp-server: use permission ids and runtime workspace roots (#22611) ## Why This PR builds on [#22610](https://github.com/openai/codex/pull/22610) and is the app-server side of the migration from mutable per-turn `SandboxPolicy` replacement toward selecting immutable permission profiles by id plus mutable runtime workspace roots. Once permission profiles can carry their own immutable `workspace_roots`, app-server no longer needs to mutate the selected `PermissionProfile` just to represent thread-specific filesystem context. The mutable part now lives on the thread as explicit `runtimeWorkspaceRoots`, while `:workspace_roots` remains symbolic until the sandbox is realized for a turn. ## What Changed - Replaced the v2 permission-selection wrapper surface with plain profile ids for `thread/start`, `thread/resume`, `thread/fork`, and `turn/start`. - Removed the API surface for profile modifications (`PermissionProfileSelectionParams`, `PermissionProfileModificationParams`, `ActivePermissionProfileModification`). - Added experimental `runtimeWorkspaceRoots` fields to the thread lifecycle and turn-start APIs. - Threaded runtime workspace roots through core session/thread snapshots, turn overrides, app-server request handling, and command execution permission resolution. - Kept session permission state symbolic so later runtime root updates and cwd-only implicit-root retargeting rebind `:workspace_roots` correctly. - Updated the embedded clients just enough to send and restore the new thread state. - Refreshed the generated schema/TypeScript artifacts and the app-server README to match the new contract. ## Verification Targeted coverage for this layer lives in: - `codex-rs/app-server-protocol/src/protocol/v2/tests.rs` - `codex-rs/app-server/tests/suite/v2/thread_start.rs` - `codex-rs/app-server/tests/suite/v2/thread_resume.rs` - `codex-rs/app-server/tests/suite/v2/turn_start.rs` - `codex-rs/core/src/session/tests.rs` The key regression checks exercise that: - `runtimeWorkspaceRoots` resolve against the effective cwd on thread start. - Profile-declared workspace roots are excluded from the runtime workspace roots returned by app-server. - A turn-level runtime workspace-root update persists onto the thread and is returned by `thread/resume`. - A named permission profile selected on one turn remains symbolic so a later runtime-root-only turn update changes the actual sandbox writes. - A cwd-only turn update retargets the implicit runtime cwd root while preserving additional runtime roots. - The protocol fixtures and generated client artifacts stay in sync with the string-based permission selection contract. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22611). * #22612 * __->__ #22611github.com-openai-codex · 8a5306ff · 2026-05-15
- 0.9ETVcli: add package path from install context (#26189) ## Why Codex package installs include helper binaries in `codex-path`, such as the bundled `rg`. Package-layout launches should add that directory before user commands run, but standalone launches were missing it while npm launches only worked because `codex.js` had its own legacy `PATH` rewrite. That made npm and standalone package behavior diverge. Shell snapshot restoration can also reset `PATH` after runtime setup. Any package-owned `PATH` prepend has to be recorded as an explicit runtime override so shells, unified exec, and user-shell commands keep access to `codex-path` after a snapshot is sourced. ## Repro Before this change, a curl-installed package could contain `rg` under `codex-path` but still fail to put it on `PATH`: ```shell mkdir /tmp/test-codex-curl curl -fsSL https://chatgpt.com/codex/install.sh \ | CODEX_HOME=/tmp/test-codex-curl CODEX_NON_INTERACTIVE=1 sh /tmp/test-codex-curl/packages/standalone/current/bin/codex exec \ --skip-git-repo-check 'print `which -a rg`' find /tmp/test-codex-curl -name rg ``` The `which -a rg` output omitted the packaged helper even though `find` showed it under `/tmp/test-codex-curl/packages/standalone/releases/.../codex-path/rg`. The npm install path behaved differently only because `codex-cli/bin/codex.js` had legacy `PATH` rewriting: ```shell mkdir /tmp/test-codex-npm cd /tmp/test-codex-npm npm install @openai/codex ./node_modules/.bin/codex exec --skip-git-repo-check 'print `which -a rg`' ``` That printed the npm package's `vendor/<target>/codex-path/rg` first. This PR moves that behavior into Rust-side package launch setup so curl/standalone and npm/bun launches agree without JS rewriting `PATH`. ## What Changed - `codex-rs/arg0` now uses `InstallContext::current().package_layout.path_dir` to prepend the package helper directory before any threads are created. - Package helper `PATH` setup is independent from the temporary arg0 alias setup, so `codex-path` is still added even if CODEX_HOME tempdir, lock, or symlink setup fails. - `codex-rs/install-context` detects the canonical package layout we ship: `bin/`, `codex-resources/`, and `codex-path/` next to `codex-package.json`. - Shell, local unified exec, and user-shell runtimes now record package `codex-path` prepends in `explicit_env_overrides`, matching the existing zsh-fork behavior so shell snapshots cannot restore over the package helper path. - Remote unified exec requests do not receive the local app-server package path overlay. - `codex-cli/bin/codex.js` no longer computes or overrides `PATH`; it only locates the native binary in the canonical package layout and passes npm/bun management metadata. - Added regression tests for `PATH` ordering, package layout detection, and shell snapshot preservation of package path prepends. ## Verification - `node --check codex-cli/bin/codex.js` - `just test -p codex-install-context -p codex-arg0` - `just test -p codex-core user_shell_snapshot_preserves_package_path_prepend` - `just test -p codex-core tools::runtimes::tests` - `just bazel-lock-update` - `just bazel-lock-check` - `just fix -p codex-install-context -p codex-arg0 -p codex-core`github.com-openai-codex · 6bcccb0e · 2026-06-04
- 0.9ETVRespect system proxies during plugin startup sync (#34506) ## Why The curated plugin startup sync can fall back from Git to HTTP, but those requests did not use Codex's effective outbound proxy policy. ## What changed - Thread the configured HTTP client factory into curated plugin sync. - Route GitHub API, zipball, backup metadata, and returned download URLs through the route-aware client when system proxy support is enabled. - Preserve the existing default HTTP client behavior, including its custom CA fallback, when system proxy support is disabled. - Apply the standard Codex headers to startup-sync HTTP requests. ## Testing Add coverage that records route selection for repository, archive, metadata, and backend-supplied download URLs, and verifies standard headers are sent. GitOrigin-RevId: e1ea13bdab58a84c9948a2c8b82a5ec5c964ea99github.com-openai-codex · 94bb6a09 · 2026-07-21
- 0.9ETVtest: construct permission profiles directly (#23030) ## Why `SandboxPolicy` is now a legacy compatibility shape, but several tests still built a `SandboxPolicy` only to immediately convert it into `PermissionProfile` for APIs that already accept canonical runtime permissions. Those detours make it harder to audit where legacy sandbox policy is still required, because boundary-only usages are mixed together with ordinary test setup. ## What Changed - Updated tests in `codex-core`, `codex-exec`, `codex-analytics`, and `codex-config` to construct `PermissionProfile` values directly when the code under test takes a permission profile. - Changed exec-policy, request-permissions, session, and sandbox test helpers to pass `PermissionProfile` through instead of converting from `SandboxPolicy` internally. - Left `SandboxPolicy` in place where tests are explicitly exercising legacy compatibility or request/response boundaries. ## Test Plan - `cargo test -p codex-analytics -p codex-config` - `cargo test -p codex-core --lib safety::tests` - `cargo test -p codex-core --lib exec_policy::tests::` - `cargo test -p codex-core --lib exec::tests` - `cargo test -p codex-core --lib guardian_review_session_config` - `cargo test -p codex-core --lib tools::network_approval::tests` - `cargo test -p codex-core --lib tools::runtimes::shell::unix_escalation::tests` - `cargo test -p codex-core --lib managed_network` - `cargo test -p codex-core --test all request_permissions::` - `cargo test -p codex-exec sandbox` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23030). * #23036 * __->__ #23030github.com-openai-codex · d91bc156 · 2026-05-16
- 0.9ETVPrecompute app-server protocol exports (#36212) ## Why Normal app-server protocol builds do not need the `ts-rs` and `schemars` implementations used to generate schema exports. ## What changed - Embed compressed stable and experimental TypeScript and JSON schema exports, and serve the existing export APIs from those artifacts. - Use no-op schema derives in non-test builds while keeping the real generators available for fixture regeneration and tests. - Move schema fixture regeneration behind a Python helper and update `just write-app-server-schema --experimental` to refresh the experimental embedded exports. ## Testing - Verify embedded exports match freshly generated stable and experimental schemas. - Verify export options and on-disk output remain compatible. GitOrigin-RevId: e8536338b457e6eec34bdf29ec0684144bd13734github.com-openai-codex · acd540f1 · 2026-07-30
- 0.8ETVSurface filesystem permission profiles in prompt context (#23924) ## Summary Some permission profiles can encode filesystem reads that should remain unavailable to the agent. Before this change, the model-visible context and automatic approval review prompt summarized the effective permissions as a legacy sandbox mode, which can omit permission-profile filesystem entries from escalation decisions. For example, a profile can grant workspace access while denying a private subtree across every workspace root: ```toml default_permissions = "restricted-workspace" [permissions.restricted-workspace.workspace_roots] "/Users/alice/project" = true "/Users/alice/other-project" = true [permissions.restricted-workspace.filesystem] ":minimal" = "read" [permissions.restricted-workspace.filesystem.":workspace_roots"] "." = "write" "private" = "deny" "private/**" = "deny" ``` The context window now describes the workspace roots and effective filesystem side of the `PermissionProfile` directly, with deny entries marked as non-escalatable: ```xml <environment_context> <cwd>/Users/alice/project</cwd> <shell>zsh</shell> <filesystem><workspace_roots><root>/Users/alice/project</root><root>/Users/alice/other-project</root></workspace_roots><permission_profile type="managed"><file_system type="restricted"><entry access="read"><special>:minimal</special></entry><entry access="write"><path>/Users/alice/project</path></entry><entry access="write"><path>/Users/alice/other-project</path></entry><entry access="deny" escalatable="false"><path>/Users/alice/project/private</path></entry><entry access="deny" escalatable="false"><path>/Users/alice/other-project/private</path></entry><entry access="deny" escalatable="false"><glob>/Users/alice/project/private/**</glob></entry><entry access="deny" escalatable="false"><glob>/Users/alice/other-project/private/**</glob></entry></file_system></permission_profile></filesystem> </environment_context> ``` Managed requirements can impose the same kind of deny-read restriction: ```toml [permissions.filesystem] deny_read = [ "/Users/alice/project/private", "/Users/alice/project/private/**", ] ``` The automatic approval review prompt also receives the parent turn's denied-read context, so review decisions can account for the active permission profile. ## What Changed - Render the effective filesystem profile in `<environment_context>`, including profile type, filesystem entries, workspace roots, and non-escalatable deny entries. - Persist effective `workspace_roots` in `TurnContextItem` so resumed/replayed context does not have to bind `:workspace_roots` through legacy `cwd` fallback. - Add explicit permission instructions that denied reads are policy restrictions, not escalation targets. - Pass the parent turn's denied-read context into automatic approval reviews. - Add targeted coverage for prompt rendering, workspace-root materialization, replay context, and review prompt context. - Keep the prompt-context test expectations platform-aware so the same filesystem rendering assertions pass on Unix and Windows paths. ## Testing - `just test -p codex-core context::environment_context::tests::serialize_environment_context_with_full_filesystem_profile` - `just test -p codex-core context::environment_context::tests::turn_context_item_filesystem_uses_workspace_roots_instead_of_cwd` - `just test -p codex-core context::permissions_instructions::permissions_instructions_tests::builds_permissions_from_profile_with_denied_reads` - `just fix -p codex-core` I also attempted `just test -p codex-core`; the changed prompt-context tests passed, but the full local run did not complete cleanly in this sandboxed macOS environment due unrelated user-shell `CODEX_SANDBOX*` expectations and integration-test timeouts.github.com-openai-codex · e7dda807 · 2026-05-28
- 0.8ETVHonor system proxy settings in the daemon updater (#34495) ## Why The PID-managed daemon updater downloaded the standalone installer directly, bypassing Codex's configured outbound proxy policy. ## What changed - Build the updater's HTTP client from the effective Codex configuration so `features.respect_system_proxy` also applies to installer downloads. - Use the route-aware client pool for the installer request. - Warn and fall back to the default `reqwest` proxy behavior when configuration cannot be loaded, allowing updates to continue. ## Testing Added tests for proxy-policy selection, configuration-load fallback, exact installer URL usage, byte preservation, and non-success HTTP responses. GitOrigin-RevId: 559668eb40314d43afa6512da66e59e32cc1b93cgithub.com-openai-codex · 99eb5756 · 2026-07-21
- 0.8ETVmodel-provider: route model discovery through HTTP client factory (#31361)github.com-openai-codex · 5892c7b6 · 2026-07-09
- 0.7ETVAdd a route-aware HTTP client pool (#34447) ## Why PAC and system-proxy resolution can select a different outbound route for each request URL. Request creation and route selection must use the same URL while still allowing transport clients to be reused. ## What changed - Add `RouteAwareClientPool` and a request builder that resolve the outbound proxy route when sending each request. - Reuse HTTP clients by resolved route, with a bounded cache of 16 routes. - Preserve request tracing, optional diagnostic logging, custom CA settings, Cloudflare cookies, and default transport redirect behavior. - Export route-aware response and error types, including status, timeout, and connection-error helpers. ## Testing Add tests for exact URL routing, client reuse and eviction, default redirects, and redaction of URL secrets from debug output. GitOrigin-RevId: a1688b64305a0bb276c371df63a039ccbe821b76github.com-openai-codex · 9078e323 · 2026-07-21
- 0.6ETVfix: preserve deny-read sandboxing for safe commands (#23943) ## 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`github.com-openai-codex · 6e101421 · 2026-05-29
- 0.6ETVsdk: launch packaged Codex runtimes (#23786) ## Why The Python and TypeScript SDKs launch the native Codex runtime directly, so they need to consume the same package artifact shape that release jobs now produce. The runtime wheel should be built from the canonical Codex package archive rather than reconstructing a parallel layout from loose binaries. ## What Changed - Stage `openai-codex-cli-bin` by extracting `codex-package-<target>.tar.gz` into `src/codex_cli_bin` and validating the expected package layout. - Update release workflows to pass the generated package archive into `stage-runtime` instead of the temporary package directory. - Update Python runtime setup to download `codex-package-*.tar.gz` release assets directly. - Expose Python runtime helpers for the bundled package directory and `codex-path`, and prepend that path when `openai_codex` launches the installed runtime without duplicating Windows `Path`/`PATH` keys. - Teach the TypeScript SDK to resolve package-layout optional dependencies while keeping the existing npm fallback layout, and preserve the existing Windows path variable casing when prepending `codex-path`. ## Test Plan - `python3 -m py_compile sdk/python/scripts/update_sdk_artifacts.py sdk/python/_runtime_setup.py sdk/python/src/openai_codex/client.py sdk/python-runtime/src/codex_cli_bin/__init__.py` - `uv run --frozen --project sdk/python --extra dev ruff check sdk/python/scripts/update_sdk_artifacts.py sdk/python/_runtime_setup.py sdk/python/src/openai_codex/client.py sdk/python/tests/test_artifact_workflow_and_binaries.py sdk/python-runtime/src/codex_cli_bin/__init__.py` - `uv run --frozen --project sdk/python --extra dev pytest sdk/python/tests/test_artifact_workflow_and_binaries.py` - `pnpm eslint src/exec.ts tests/exec.test.ts` - `pnpm test --runInBand tests/exec.test.ts`github.com-openai-codex · 0b4f8609 · 2026-05-21
- 0.6ETVwebsocket-client: add proxy-aware connector (#31622) ## Why The route-aware WebSocket connection setup in #31441 is transport infrastructure rather than Responses API protocol logic. Landing it first in a dedicated crate keeps `codex-api` focused on request and response behavior and makes the transport reusable by future WebSocket clients. WebSockets must also apply the same effective outbound proxy and custom-CA policy as HTTP without disabling the lower-latency WebSocket path. Requiring an `HttpClientFactory` when constructing the connector makes proxy-policy resolution part of the API instead of an optional call-site convention. This PR is an independent prerequisite based directly on `main`. After it merges, #31441 can rebase onto it and replace its in-crate connector with this API. ## What changed - Add a new `codex-websocket-client` workspace crate with a `WebSocketConnector` constructed from the effective `HttpClientFactory`. - Resolve every destination through that factory before connecting, then support direct connections, transport-default routing, HTTP proxies, and TLS-encrypted HTTPS proxies. - Preserve custom-CA trust for proxy and target TLS handshakes and preserve Happy Eyeballs fallback for explicit direct and proxy routes. - Expose an established `WebSocketConnection` as a uniform `Stream` and `Sink`, hiding route-specific transport types from protocol clients. - Add focused integration-style coverage for the public connector and message stream, real WSS over direct and CONNECT routes, implicit and explicit HTTPS proxy ports, and stalled-address-family fallback. ## Review guide 1. `codex-rs/websocket-client/src/lib.rs` defines the small public API and the factory-required policy invariant. 2. `codex-rs/websocket-client/src/dialer.rs` contains DNS, TCP, proxy tunneling, TLS, and WebSocket handshake setup. 3. `codex-rs/websocket-client/src/dialer_tests.rs` verifies the public stream, direct and proxied WSS paths, HTTPS port preservation, and Happy Eyeballs timing. 4. There is intentionally no consumer migration here; #31441 will become the first consumer after this prerequisite merges. ## Test plan - `cargo check -p codex-websocket-client --tests` - `just test -p codex-websocket-client` - `cargo shear` - `just bazel-lock-check`github.com-openai-codex · 2780bd58 · 2026-07-08
- 0.6ETVcore: expose permission profile to shell tools (#29941) ## 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`.github.com-openai-codex · c65cfeab · 2026-06-25
- 0.6ETVpermissions: resolve profile identity with constraints (#22683) ## Why This PR is the invariant-cleanup layer that follows the workspace-roots base merged in [#22610](https://github.com/openai/codex/pull/22610). #22610 adds `[permissions.<id>.workspace_roots]` and keeps runtime workspace roots separate from the raw permission profile, but its in-memory representation is intentionally transitional: `Permissions` still carries the selected profile identity next to a constrained `PermissionProfile`. That makes APIs such as `set_constrained_permission_profile_with_active_profile()` fragile because the id and value only mean the right thing when every caller keeps them in sync. This PR introduces a single resolved profile state so profile identity, `extends`, the profile value, and profile-declared workspace roots travel together. The next PR, [#22611](https://github.com/openai/codex/pull/22611), builds on this by changing the app-server turn API to select permission profiles by id plus runtime workspace roots. ## Stack Context - #22610, now merged: adds profile-declared `workspace_roots`, runtime workspace roots, and `:workspace_roots` materialization. - This PR: replaces the parallel active-profile/profile-value fields with `PermissionProfileState`. - #22611: switches app-server turn updates toward profile ids plus runtime workspace roots. - #22612: updates TUI/exec summaries to show the effective workspace roots. Keeping this separate from #22611 is deliberate: reviewers can validate the internal state invariant before reviewing the app-server protocol migration. ## What Changed - Added `ResolvedPermissionProfile::{Legacy, BuiltIn, Named}` and `PermissionProfileState`. - Typed built-in profile ids with `BuiltInPermissionProfileId`. - Moved selected profile identity and profile-declared workspace roots into the resolved state. - Replaced `Permissions` parallel profile fields with one `permission_profile_state`. - Removed `set_constrained_permission_profile_with_active_profile()` from session sync paths. - Kept trusted session replay/`SessionConfigured` compatibility through explicit session snapshot helpers. - Updated session configuration, MCP initialization, app-server, exec, TUI, and guardian call sites to consume `&PermissionProfile` directly. ## Review Guide Start with `codex-rs/core/src/config/resolved_permission_profile.rs`; it is the new invariant boundary. Then review `codex-rs/core/src/config/mod.rs` to see how config loading records active profile identity and profile workspace roots. The remaining call-site changes are mostly mechanical fallout from `Permissions::permission_profile()` returning `&PermissionProfile` instead of `&Constrained<PermissionProfile>`. ## Verification The existing config/session coverage now constructs and asserts through `PermissionProfileState`. The workspace-root config test also asserts that profile-declared roots are preserved in the resolved state, which is the behavior #22611 relies on when runtime roots become mutable through the app-server API. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22683). * #22612 * #22611 * __->__ #22683github.com-openai-codex · 3c6d7278 · 2026-05-15
- 0.6ETVwindows-sandbox: drive write roots from resolved permissions (#22923) ## Why This is the third PR in the Windows sandbox `SandboxPolicy` -> `PermissionProfile` migration stack. #22896 introduced `ResolvedWindowsSandboxPermissions`, and #22918 moved elevated runner IPC to carry `PermissionProfile`. This PR starts moving the remaining setup/spawn helpers away from asking legacy enum questions like “is this `WorkspaceWrite`?” and toward resolved runtime permission questions like “does this profile require write capability roots?” ## What changed - Added resolved-permissions helpers for network identity and write-capability detection. - Moved setup write-root gathering to operate on `ResolvedWindowsSandboxPermissions`, with the legacy `SandboxPolicy` wrapper left in place for existing call sites. - Updated identity setup, elevated capture setup, and world-writable audit denies to use resolved write roots. - Updated spawn preparation to carry resolved permissions in `SpawnContext` and use them for network blocking, setup write roots, elevated capability SID selection, and legacy capability roots. - Removed a now-unused legacy write-root helper. ## Verification - `cargo test -p codex-windows-sandbox` - `just fix -p codex-windows-sandbox` - Existing stack checks are green on #22896 and #22918; CI has started for this PR. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22923). * #23715 * #23714 * #23167 * __->__ #22923github.com-openai-codex · e1ec0eee · 2026-05-20
- 0.6ETVAdd a policy-aware HTTP client builder (#34630) ## What changed - Add `HttpClientBuilder` for configuring default headers, redirects, the Cloudflare cookie store, and request diagnostics without exposing the underlying transport. - Provide factory-backed construction for fixed destinations that respects outbound proxy policy, alongside explicit direct and legacy transport-default construction paths. - Preserve the request-logging setting in `ReqwestTransport`, so disabling diagnostics also suppresses transport-level URL and request-body traces. - Add `HEAD`, `DELETE`, and query-parameter helpers to the shared client wrappers. ## Testing - Verify builder configuration survives policy-aware construction and custom CA fallback. - Verify disabled request logging omits request URLs and bodies from transport traces. GitOrigin-RevId: 529c33abefeb88f38ff9a0ead374d29fcc872e6agithub.com-openai-codex · adb143a2 · 2026-07-21
- 0.6ETVResolve outbound proxy routes explicitly (#34435) ## Why System proxy discovery can block, and delegating fallback to each transport can repeat discovery or apply inconsistent environment proxy behavior. ## What changed - Resolve unavailable system proxy decisions to an explicit environment proxy or direct route, including `NO_PROXY` settings and WebSocket-specific fallbacks. - Add asynchronous system proxy resolution that uses cached decisions first and serializes blocking platform lookups on Windows and macOS. - Preserve `NO_PROXY` handling for WebSocket connections, including HTTP and HTTPS proxies, and hash proxy cache keys on every platform. ## Testing - Add coverage for explicit environment fallback, cached asynchronous resolution, and proxied or bypassed WebSocket connections. GitOrigin-RevId: 3b7cf170dcfe639eec53c3c1514f92bfcf13e7e9github.com-openai-codex · c9ef7eff · 2026-07-21
- 0.5ETVRoute backend requests through the HTTP client factory (#34490) ## Why Backend-backed features need to use the same outbound routing configuration as the rest of Codex. ## What changed - Build the backend client on a route-aware client pool supplied by `HttpClientFactory`. - Pass the configured factory through app-server account requests, cloud config loading, cloud tasks, and memory rate-limit checks. - Preserve task query encoding, authentication headers, JSON request bodies, and ChatGPT cookie handling during the migration. ## Testing - Add backend-client tests for proxy-policy propagation, task query encoding, bearer authentication, and JSON request bodies. GitOrigin-RevId: c17d162e0dce9cec758df7cdeecf982207c6512bgithub.com-openai-codex · dc21b46a · 2026-07-21