Commit Graph

1037 Commits

  • Fix admin skills. (#8305)
    We were assembling the skill roots in two different places, and the
    admin root was missing in one of them. This change centralizes root
    selection into a helper so both paths stay in sync.
  • Support admin scope skills. (#8296)
    a new scope reads from /etc/codex
  • UI tweaks on skills popup. (#8250)
    Only display the skill name (not the folder), and truncate the skill
    description to a maximum of two lines.
  • feat: introduce ExternalSandbox policy (#8290)
    ## Description
    
    Introduced `ExternalSandbox` policy to cover use case when sandbox
    defined by outside environment, effectively it translates to
    `SandboxMode#DangerFullAccess` for file system (since sandbox configured
    on container level) and configurable `network_access` (either Restricted
    or Enabled by outside environment).
    
    as example you can configure `ExternalSandbox` policy as part of
    `sendUserTurn` v1 app_server API:
    
    ```
     {
                "conversationId": <id>,
                "cwd": <cwd>,
                "approvalPolicy": "never",
                "sandboxPolicy": {
                      "type": ""external-sandbox",
                      "network_access": "enabled"/"restricted"
                },
                "model": <model>,
                "effort": <effort>,
                ....
            }
    ```
  • chore: migrate from Config::load_from_base_config_with_overrides to ConfigBuilder (#8276)
    https://github.com/openai/codex/pull/8235 introduced `ConfigBuilder` and
    this PR updates all call non-test call sites to use it instead of
    `Config::load_from_base_config_with_overrides()`.
    
    This is important because `load_from_base_config_with_overrides()` uses
    an empty `ConfigRequirements`, which is a reasonable default for testing
    so the tests are not influenced by the settings on the host. This method
    is now guarded by `#[cfg(test)]` so it cannot be used by business logic.
    
    Because `ConfigBuilder::build()` is `async`, many of the test methods
    had to be migrated to be `async`, as well. On the bright side, this made
    it possible to eliminate a bunch of `block_on_future()` stuff.
  • fix: change codex/sandbox-state/update from a notification to a request (#8142)
    Historically, `accept_elicitation_for_prompt_rule()` was flaky because
    we were using a notification to update the sandbox followed by a `shell`
    tool request that we expected to be subject to the new sandbox config,
    but because [rmcp](https://crates.io/crates/rmcp) MCP servers delegate
    each incoming message to a new Tokio task, messages are not guaranteed
    to be processed in order, so sometimes the `shell` tool call would run
    before the notification was processed.
    
    Prior to this PR, we relied on a generous `sleep()` between the
    notification and the request to reduce the change of the test flaking
    out.
    
    This PR implements a proper fix, which is to use a _request_ instead of
    a notification for the sandbox update so that we can wait for the
    response to the sandbox request before sending the request to the
    `shell` tool call. Previously, `rmcp` did not support custom requests,
    but I fixed that in
    https://github.com/modelcontextprotocol/rust-sdk/pull/590, which made it
    into the `0.12.0` release (see #8288).
    
    This PR updates `shell-tool-mcp` to expect
    `"codex/sandbox-state/update"` as a _request_ instead of a notification
    and sends the appropriate ack. Note this behavior is tied to our custom
    `codex/sandbox-state` capability, which Codex honors as an MCP client,
    which is why `core/src/mcp_connection_manager.rs` had to be updated as
    part of this PR, as well.
    
    This PR also updates the docs at `shell-tool-mcp/README.md`.
  • Support skills shortDescription. (#8278)
    Allow SKILL.md to specify a more human-readable short description as
    skill metadata.
  • model list (#8286)
    <img width="200" alt="7ff2254b-e96f-42fc-8232-b4e76cb26248"
    src="https://github.com/user-attachments/assets/1f56799d-e2cd-4b69-9290-854943f7c6b6"
    />
  • feat: add support for /etc/codex/requirements.toml on UNIX (#8277)
    This implements the new config design where config _requirements_ are
    loaded separately (and with a special schema) as compared to config
    _settings_. In particular, on UNIX, with this PR, you could define
    `/etc/codex/requirements.toml` with:
    
    ```toml
    allowed_approval_policies = ["never", "on-request"]
    ```
    
    to enforce that `Config.approval_policy` must be one of those two values
    when Codex runs.
    
    We plan to expand the set of things that can be restricted by
    `/etc/codex/requirements.toml` in short order.
    
    Note that requirements can come from several sources:
    
    - new MDM key on macOS (not implemented yet)
    - `/etc/codex/requirements.toml`
    - re-interpretation of legacy MDM key on macOS
    (`com.openai.codex/config_toml_base64`)
    - re-interpretation of legacy `/etc/codex/managed_config.toml`
    
    So our resolution strategy is to load TOML data from those sources, in
    order. Later TOMLs are "merged" into previous TOMLs, but any field that
    is already set cannot be overwritten. See
    `ConfigRequirementsToml::merge_unset_fields()`.
  • Terminal Detection Metadata for Per-Terminal Scroll Scaling (#8252)
    # Terminal Detection Metadata for Per-Terminal Scroll Scaling
    
    ## Summary
    Expand terminal detection into structured metadata (`TerminalInfo`) with
    multiplexer awareness, plus a testable environment shim and
    characterization tests.
    
    ## Context / Motivation
    - TUI2 owns its viewport and scrolling model (see
    `codex-rs/tui2/docs/tui_viewport_and_history.md`), so scroll behavior
    must be consistent across terminals and independent of terminal
    scrollback quirks.
    - Prior investigations show mouse wheel scroll deltas vary noticeably by
    terminal. To tune scroll scaling (line increments per wheel tick) we
    need reliable terminal identification, including when running inside
    tmux/zellij.
    - tmux is especially tricky because it can mask the underlying terminal;
    we now consult `tmux display-message` client termtype/name to attribute
    sessions to the actual terminal rather than tmux itself.
    - This remains backwards compatible with the existing OpenTelemetry
    user-agent token because `user_agent()` is still derived from the same
    environment signals (now via `TerminalInfo`).
    
    ## Changes
    - Introduce `TerminalInfo`, `TerminalName`, and `Multiplexer` with
    `TERM_PROGRAM`/`TERM`/multiplexer detection and user-agent formatting in
    `codex-rs/core/src/terminal.rs`.
    - Add an injectable `Environment` trait + `FakeEnvironment` for testing,
    and comprehensive characterization tests covering known terminals, tmux
    client termtype/name, and zellij.
    - Document module usage and detection order; update `terminal_info()` to
    be the primary interface for callers.
    
    ## Testing
    - `cargo test -p codex-core terminal::tests`
    - manually checked ghostty, iTerm2, Terminal.app, vscode, tmux, zellij,
    Warp, alacritty, kitty.
    ```
    2025-12-18T07:07:49.191421Z  INFO Detected terminal info terminal=TerminalInfo { name: Iterm2, term_program: Some("iTerm.app"), version: Some("3.6.6"), term: None, multiplexer: None }
    2025-12-18T07:07:57.991776Z  INFO Detected terminal info terminal=TerminalInfo { name: AppleTerminal, term_program: Some("Apple_Terminal"), version: Some("455.1"), term: None, multiplexer: None }
    2025-12-18T07:08:07.732095Z  INFO Detected terminal info terminal=TerminalInfo { name: WarpTerminal, term_program: Some("WarpTerminal"), version: Some("v0.2025.12.10.08.12.stable_03"), term: None, multiplexer: None }
    2025-12-18T07:08:24.860316Z  INFO Detected terminal info terminal=TerminalInfo { name: Kitty, term_program: None, version: None, term: None, multiplexer: None }
    2025-12-18T07:08:38.302761Z  INFO Detected terminal info terminal=TerminalInfo { name: Alacritty, term_program: None, version: None, term: None, multiplexer: None }
    2025-12-18T07:08:50.887748Z  INFO Detected terminal info terminal=TerminalInfo { name: VsCode, term_program: Some("vscode"), version: Some("1.107.1"), term: None, multiplexer: None }
    2025-12-18T07:10:01.309802Z  INFO Detected terminal info terminal=TerminalInfo { name: WezTerm, term_program: Some("WezTerm"), version: Some("20240203-110809-5046fc22"), term: None, multiplexer: None }
    2025-12-18T08:05:17.009271Z  INFO Detected terminal info terminal=TerminalInfo { name: Ghostty, term_program: Some("ghostty"), version: Some("1.2.3"), term: None, multiplexer: None }
    2025-12-18T08:05:23.819973Z  INFO Detected terminal info terminal=TerminalInfo { name: Ghostty, term_program: Some("ghostty"), version: Some("1.2.3"), term: Some("xterm-ghostty"), multiplexer: Some(Tmux { version: Some("3.6a") }) }
    2025-12-18T08:05:35.572853Z  INFO Detected terminal info terminal=TerminalInfo { name: Ghostty, term_program: Some("ghostty"), version: Some("1.2.3"), term: None, multiplexer: Some(Zellij) }
    ```
    
    ## Notes / Follow-ups
    - Next step is to wire `TerminalInfo` into TUI2’s scroll scaling
    configuration and add a per-terminal tuning table.
    - The log output in TUI2 helps validate real-world detection before
    applying behavior changes.
  • splash screen (#8270)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
    
    Include a link to a bug report or enhancement request.
  • feat: migrate to new constraint-based loading strategy (#8251)
    This is a significant change to how layers of configuration are applied.
    In particular, the `ConfigLayerStack` now has two important fields:
    
    - `layers: Vec<ConfigLayerEntry>`
    - `requirements: ConfigRequirements`
    
    We merge `TomlValue`s across the layers, but they are subject to
    `ConfigRequirements` before creating a `Config`.
    
    How I would review this PR:
    
    - start with `codex-rs/app-server-protocol/src/protocol/v2.rs` and note
    the new variants added to the `ConfigLayerSource` enum:
    `LegacyManagedConfigTomlFromFile` and `LegacyManagedConfigTomlFromMdm`
    - note that `ConfigLayerSource` now has a `precedence()` method and
    implements `PartialOrd`
    - `codex-rs/core/src/config_loader/layer_io.rs` is responsible for
    loading "admin" preferences from `/etc/codex/managed_config.toml` and
    MDM. Because `/etc/codex/managed_config.toml` is now deprecated in favor
    of `/etc/codex/requirements.toml` and `/etc/codex/config.toml`, we now
    include some extra information on the `LoadedConfigLayers` returned in
    `layer_io.rs`.
    - `codex-rs/core/src/config_loader/mod.rs` has major changes to
    `load_config_layers_state()`, which is what produces `ConfigLayerStack`.
    The docstring has the new specification and describes the various layers
    that will be loaded and the precedence order.
    - It uses the information from `LoaderOverrides` "twice," both in the
    spirit of legacy support:
    - We use one instances to derive an instance of `ConfigRequirements`.
    Currently, the only field in `managed_config.toml` that contributes to
    `ConfigRequirements` is `approval_policy`. This PR introduces
    `Constrained::allow_only()` to support this.
    - We use a clone of `LoaderOverrides` to derive
    `ConfigLayerSource::LegacyManagedConfigTomlFromFile` and
    `ConfigLayerSource::LegacyManagedConfigTomlFromMdm` layers, as
    appropriate. As before, this ends up being a "best effort" at enterprise
    controls, but is enforcement is not guaranteed like it is for
    `ConfigRequirements`.
    - Now we only create a "user" layer if `$CODEX_HOME/config.toml` exists.
    (Previously, a user layer was always created for `ConfigLayerStack`.)
    - Similarly, we only add a "session flags" layer if there are CLI
    overrides.
    - `config_loader/state.rs` contains the updated implementation for
    `ConfigLayerStack`. Note the public API is largely the same as before,
    but the implementation is quite different. We leverage the fact that
    `ConfigLayerSource` is now `PartialOrd` to ensure layers are in the
    correct order.
    - A `Config` constructed via `ConfigBuilder.build()` will use
    `load_config_layers_state()` to create the `ConfigLayerStack` and use
    the associated `ConfigRequirements` when constructing the `Config`
    object.
    - That said, a `Config` constructed via
    `Config::load_from_base_config_with_overrides()` does _not_ yet use
    `ConfigBuilder`, so it creates a `ConfigRequirements::default()` instead
    of loading a proper `ConfigRequirements`. I will fix this in a
    subsequent PR.
    
    Then the following files are mostly test changes:
    
    ```
    codex-rs/app-server/tests/suite/v2/config_rpc.rs
    codex-rs/core/src/config/service.rs
    codex-rs/core/src/config_loader/tests.rs
    ```
    
    Again, because we do not always include "user" and "session flags"
    layers when the contents are empty, `ConfigLayerStack` sometimes has
    fewer layers than before (and the precedence order changed slightly),
    which is the main reason integration tests changed.
  • feat: add name to beta features (#8266)
    Add a name to Beta features
    
    <img width="906" height="153" alt="Screenshot 2025-12-18 at 16 42 49"
    src="https://github.com/user-attachments/assets/d56f3519-0613-4d9a-ad4d-38b1a7eb125a"
    />
  • caribou (#8265)
    Welcome caribou
    
    <img width="1536" height="1024" alt="image"
    src="https://github.com/user-attachments/assets/2a67b21f-40cf-4518-aee4-691af331ab50"
    />
  • chore: prefer AsRef<Path> to &Path (#8249)
    This is some minor API cleanup that will make it easier to use
    `AbsolutePathBuf` in more places in a subsequent PR.
  • chores: clean picker (#8232)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
    
    Include a link to a bug report or enhancement request.
  • chore: simplify loading of Mac-specific logic in config_loader (#8248)
    Over in `config_loader/macos.rs`, we were doing this complicated `mod`
    thing to expose one version of `load_managed_admin_config_layer()` for
    Mac:
    
    
    https://github.com/openai/codex/blob/580c59aa9af61cb4bffb5b204bd16a5dcc4bc911/codex-rs/core/src/config_loader/macos.rs#L4-L5
    
    While exposing a trivial implementation for non-Mac:
    
    
    https://github.com/openai/codex/blob/580c59aa9af61cb4bffb5b204bd16a5dcc4bc911/codex-rs/core/src/config_loader/macos.rs#L110-L117
    
    That was being used like this:
    
    
    https://github.com/openai/codex/blob/580c59aa9af61cb4bffb5b204bd16a5dcc4bc911/codex-rs/core/src/config_loader/layer_io.rs#L47-L48
    
    This PR simplifies that callsite in `layer_io.rs` to just be:
    
    ```rust
        #[cfg(not(target_os = "macos"))]
        let managed_preferences = None;
    ```
    
    And updates `config_loader/mod.rs` so we only pull in `macos.rs` on Mac:
    
    ```rust
    #[cfg(target_os = "macos")]
    mod macos;
    ```
    
    This simplifies `macos.rs` considerably, though it looks like a big
    change because everything gets unindented and reformatted because we can
    drop the whole `mod native` thing now.
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8248).
    * #8251
    * #8249
    * __->__ #8248
  • Reintroduce feature flags for skills. (#8244)
    1. Reintroduce feature flags for skills;
    2. UI tweaks (truncate descriptions, better validation error display).
  • fix: introduce ConfigBuilder (#8235)
    Introduce `ConfigBuilder` as an alternative to our existing `Config`
    constructors.
    
    I noticed that the existing constructors,
    `Config::load_with_cli_overrides()` and
    `Config::load_with_cli_overrides_and_harness_overrides()`, did not take
    `codex_home` as a parameter, which can be a problem.
    
    Historically, when Codex was purely a CLI, we wanted to be extra sure
    that the creation of `codex_home` was always done via
    `find_codex_home()`, so we did not expose `codex_home` as a parameter
    when creating `Config` in business logic. But in integration tests,
    `codex_home` nearly always needs to be configured (as a temp directory),
    which is why callers would have to go through
    `Config::load_from_base_config_with_overrides()` instead.
    
    Now that the Codex harness also functions as an app server, which could
    conceivably load multiple threads where `codex_home` is parameterized
    differently in each one, I think it makes sense to make this
    configurable. Going to a builder pattern makes it more flexible to
    ensure an arbitrary permutation of options can be set when constructing
    a `Config` while using the appropriate defaults for the options that
    aren't set explicitly.
    
    Ultimately, I think this should make it possible for us to make
    `Config::load_from_base_config_with_overrides()` private because all
    integration tests should be able to leverage `ConfigBuilder` instead.
    Though there could be edge cases, so I'll pursue that migration after we
    get through the current config overhaul.
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8235).
    * #8237
    * __->__ #8235
  • Support SYSTEM skills. (#8220)
    1. Remove PUBLIC skills and introduce SYSTEM skills embedded in the
    binary and installed into $CODEX_HOME/skills/.system at startup.
    2. Skills are now always enabled (feature flag removed).
    3. Update skills/list to accept forceReload and plumb it through (not
    used by clients yet).
  • Show migration link (#8228)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
    
    Include a link to a bug report or enhancement request.
  • chore: cleanup Config instantiation codepaths (#8226)
    This PR does various types of cleanup before I can proceed with more
    ambitious changes to config loading.
    
    First, I noticed duplicated code across these two methods:
    
    
    https://github.com/openai/codex/blob/774bd9e432fa2e0f4e059e97648cf92216912e19/codex-rs/core/src/config/mod.rs#L314-L324
    
    
    https://github.com/openai/codex/blob/774bd9e432fa2e0f4e059e97648cf92216912e19/codex-rs/core/src/config/mod.rs#L334-L344
    
    This has now been consolidated in
    `load_config_as_toml_with_cli_overrides()`.
    
    Further, I noticed that `Config::load_with_cli_overrides()` took two
    similar arguments:
    
    
    https://github.com/openai/codex/blob/774bd9e432fa2e0f4e059e97648cf92216912e19/codex-rs/core/src/config/mod.rs#L308-L311
    
    The difference between `cli_overrides` and `overrides` was not
    immediately obvious to me. At first glance, it appears that one should
    be able to be expressed in terms of the other, but it turns out that
    some fields of `ConfigOverrides` (such as `cwd` and
    `codex_linux_sandbox_exe`) are, by design, not configurable via a
    `.toml` file or a command-line `--config` flag.
    
    That said, I discovered that many callers of
    `Config::load_with_cli_overrides()` were passing
    `ConfigOverrides::default()` for `overrides`, so I created two separate
    methods:
    
    - `Config::load_with_cli_overrides(cli_overrides: Vec<(String,
    TomlValue)>)`
    - `Config::load_with_cli_overrides_and_harness_overrides(cli_overrides:
    Vec<(String, TomlValue)>, harness_overrides: ConfigOverrides)`
    
    The latter has a long name, as it is _not_ what should be used in the
    common case, so the extra typing is designed to draw attention to this
    fact. I tried to update the existing callsites to use the shorter name,
    where possible.
    
    Further, in the cases where `ConfigOverrides` is used, usually only a
    limited subset of fields are actually set, so I updated the declarations
    to leverage `..Default::default()` where possible.
  • feat: model picker (#8209)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
    
    Include a link to a bug report or enhancement request.
  • Load models from static file (#8153)
    - Load models from static file as a fallback
    - Make API users use this file directly
    - Add tests to make sure updates to the file always serialize
  • fix: PathBuf -> AbsolutePathBuf in ConfigToml struct (#8205)
    We should not have any `PathBuf` fields in `ConfigToml` or any of the
    transitive structs we include, as we should use `AbsolutePathBuf`
    instead so that we do not have to keep track of the file from which
    `ConfigToml` was loaded such that we need it to resolve relative paths
    later when the values of `ConfigToml` are used.
    
    I only found two instances of this: `experimental_instructions_file` and
    `experimental_compact_prompt_file`. Incidentally, when these were
    specified as relative paths, they were resolved against `cwd` rather
    than `config.toml`'s parent, which seems wrong to me. I changed the
    behavior so they are resolved against the parent folder of the
    `config.toml` being parsed, which we get "for free" due to the
    introduction of `AbsolutePathBufGuard ` in
    https://github.com/openai/codex/pull/7796.
    
    While it is not great to change the behavior of a released feature,
    these fields are prefixed with `experimental_`, which I interpret to
    mean we have the liberty to change the contract.
    
    For reference:
    
    - `experimental_instructions_file` was introduced in
    https://github.com/openai/codex/pull/1803
    - `experimental_compact_prompt_file` was introduced in
    https://github.com/openai/codex/pull/5959
  • fix: session downgrade (#8196)
    The problem is that the `tokio` task own an `Arc` reference of the
    session and that this task only exit with the broadcast channel get
    closed. But this never get closed if the session is not dropped. So it's
    a snake biting his tail basically
    
    The most notable result was that non of the `Drop` implementation were
    triggered (temporary files, shell snapshots, session cleaning etc etc)
    when closing the session (through a `/new` for example)
    
    The fix is just to weaken the `Arc` and upgrade it on the fly
  • feat: experimental menu (#8071)
    This will automatically render any `Stage::Beta` features.
    
    The change only gets applied to the *next session*. This started as a
    bug but actually this is a good thing to prevent out of distribution
    push
    
    <img width="986" height="288" alt="Screenshot 2025-12-15 at 15 38 35"
    src="https://github.com/user-attachments/assets/78b7a71d-0e43-4828-a118-91c5237909c7"
    />
    
    
    <img width="509" height="109" alt="Screenshot 2025-12-15 at 17 35 44"
    src="https://github.com/user-attachments/assets/6933de52-9b66-4abf-b58b-a5f26d5747e2"
    />
  • feat: Constrain values for approval_policy (#7778)
    Constrain `approval_policy` through new `admin_policy` config.
    
    This PR will:
    1. Add a `admin_policy` section to config, with a single field (for now)
    `allowed_approval_policies`. This list constrains the set of
    user-settable `approval_policy`s.
    2. Introduce a new `Constrained<T>` type, which combines a current value
    and a validator function. The validator function ensures disallowed
    values are not set.
    3. Change the type of `approval_policy` on `Config` and
    `SessionConfiguration` from `AskForApproval` to
    `Constrained<AskForApproval>`. The validator function is set by the
    values passed into `allowed_approval_policies`.
    4. `GenericDisplayRow`: add a `disabled_reason: Option<String>`. When
    set, it disables selection of the value and indicates as such in the
    menu. This also makes it unselectable with arrow keys or numbers. This
    is used in the `/approvals` menu.
    
    Follow ups are:
    1. Do the same thing to `sandbox_policy`.
    2. Propagate the allowed set of values through app-server for the
    extension (though already this should prevent app-server from setting
    this values, it's just that we want to disable UI elements that are
    unsettable).
    
    Happy to split this PR up if you prefer, into the logical numbered areas
    above. Especially if there are parts we want to gavel on separately
    (e.g. admin_policy).
    
    Disabled full access:
    <img width="1680" height="380" alt="image"
    src="https://github.com/user-attachments/assets/1fb61c8c-1fcb-4dc4-8355-2293edb52ba0"
    />
    
    Disabled `--yolo` on startup:
    <img width="749" height="76" alt="image"
    src="https://github.com/user-attachments/assets/0a1211a0-6eb1-40d6-a1d7-439c41e94ddb"
    />
    
    CODEX-4087
  • feat: change ConfigLayerName into a disjoint union rather than a simple enum (#8095)
    This attempts to tighten up the types related to "config layers."
    Currently, `ConfigLayerEntry` is defined as follows:
    
    
    https://github.com/openai/codex/blob/bef36f4ae765f471d7cd69372fcf1b92c8f0367a/codex-rs/core/src/config_loader/state.rs#L19-L25
    
    but the `source` field is a bit of a lie, as:
    
    - for `ConfigLayerName::Mdm`, it is
    `"com.openai.codex/config_toml_base64"`
    - for `ConfigLayerName::SessionFlags`, it is `"--config"`
    - for `ConfigLayerName::User`, it is `"config.toml"` (just the file
    name, not the path to the `config.toml` on disk that was read)
    - for `ConfigLayerName::System`, it seems like it is usually
    `/etc/codex/managed_config.toml` in practice, though on Windows, it is
    `%CODEX_HOME%/managed_config.toml`:
    
    
    https://github.com/openai/codex/blob/bef36f4ae765f471d7cd69372fcf1b92c8f0367a/codex-rs/core/src/config_loader/layer_io.rs#L84-L101
    
    All that is to say, in three out of the four `ConfigLayerName`, `source`
    is a `PathBuf` that is not an absolute path (or even a true path).
    
    This PR tries to uplevel things by eliminating `source` from
    `ConfigLayerEntry` and turning `ConfigLayerName` into a disjoint union
    named `ConfigLayerSource` that has the appropriate metadata for each
    variant, favoring the use of `AbsolutePathBuf` where appropriate:
    
    ```rust
    pub enum ConfigLayerSource {
        /// Managed preferences layer delivered by MDM (macOS only).
        #[serde(rename_all = "camelCase")]
        #[ts(rename_all = "camelCase")]
        Mdm { domain: String, key: String },
        /// Managed config layer from a file (usually `managed_config.toml`).
        #[serde(rename_all = "camelCase")]
        #[ts(rename_all = "camelCase")]
        System { file: AbsolutePathBuf },
        /// Session-layer overrides supplied via `-c`/`--config`.
        SessionFlags,
        /// User config layer from a file (usually `config.toml`).
        #[serde(rename_all = "camelCase")]
        #[ts(rename_all = "camelCase")]
        User { file: AbsolutePathBuf },
    }
    ```
  • Add public skills + improve repo skill discovery and error UX (#8098)
    1. Adds SkillScope::Public end-to-end (core + protocol) and loads skills
    from the public cache directory
    2. Improves repo skill discovery by searching upward for the nearest
    .codex/skills within a git repo
    3. Deduplicates skills by name with deterministic ordering to avoid
    duplicates across sources
    4. Fixes garbled “Skill errors” overlay rendering by preventing pending
    history lines from being injected during the modal
    5. Updates the project docs “Skills” intro wording to avoid hardcoded
    paths
  • Revert "chore: review in read-only (#7593)" (#8127)
    This reverts commit 291b54a762.
    
    This commit was intended to prevent the model from making code changes
    during `/review`, which is sometimes does. Unfortunately, it has other
    unintended side effects that cause `/review` to fail in a variety of
    ways. See #8115 and #7815. We've therefore decided to revert this
    change.
  • nit: trace span for regular task (#8053)
    Logs are too spammy
    
    ---------
    
    Co-authored-by: Anton Panasenko <apanasenko@openai.com>