Commit Graph

63 Commits

  • Add remote thread config loader protos (#18892)
    ## Why
    
    Thread-scoped config needs a stable boundary between the app/session
    owner and the config stack. Instead of having call sites manually copy
    thread config fields into individual overrides, this adds the proto and
    Rust plumbing needed for a `ThreadConfigLoader` implementation to return
    typed sources that can be translated into ordinary config layer entries.
    
    Keeping the remote payload typed also makes precedence easier to reason
    about: session-owned thread config maps back to the existing session
    config source, while user-owned thread config is represented separately
    without introducing a new config-layer source until it has TOML-backed
    fields.
    
    ## What changed
    
    - Added the `codex.thread_config.v1` protobuf service and generated Rust
    module for loading thread config sources.
    - Added `RemoteThreadConfigLoader`, which calls the gRPC service, parses
    `SessionThreadConfig` / `UserThreadConfig`, and validates provider
    fields such as `wire_api`, auth timeout, and absolute auth cwd.
    - Added proto generation tooling under
    `config/scripts/generate-proto.sh` and
    `config/examples/generate-proto.rs`.
    - Added `ThreadConfigLoader::load_config_layers`, plus static/no-op
    loader helpers, so tests and callers can use the same typed loader
    interface while config-layer translation stays centralized.
    
    ## Verification
    
    - `cargo test -p codex-config thread_config`
  • Default Fast service tier for eligible ChatGPT plans (#19053)
    ## Why
    
    Enterprise and business-like ChatGPT plans should get Codex's Fast
    service tier by default when the user or caller has not made an explicit
    service-tier choice. At the same time, callers need a durable way to
    choose standard routing without adding a new persisted `standard`
    service tier value. This keeps existing config compatibility while
    letting core own the managed default policy.
    
    ## What changed
    
    - Resolve the effective service tier in core at session creation:
    explicit `fast` or `flex` wins, explicit null/clear or
    `[notice].fast_default_opt_out = true` resolves to standard routing, and
    otherwise eligible ChatGPT plans resolve to Fast when FastMode is
    enabled.
    - Add `[notice].fast_default_opt_out` as the persisted opt-out marker
    for managed Fast defaults.
    - Treat app-server/TUI `service_tier: null` as an explicit
    standard/clear choice by preserving that intent through config loading.
    - Update TUI rendering to use core's effective service tier for startup
    and status surfaces while still keeping `config.service_tier` as the
    explicit configured choice.
    - Update `/fast off` to clear `service_tier`, persist the opt-out
    marker, and send explicit standard for subsequent turns.
    
    ## Verification
    
    - Added unit coverage for config override/notice handling, service-tier
    resolution, runtime null clearing, and `/fast off` turn propagation.
    - `cargo build -p codex-cli`
    
    Full test suite was not run locally per author request.
  • codex: support hooks in config.toml and requirements.toml (#18893)
    ## Summary
    
    Support the existing hooks schema in inline TOML so hooks can be
    configured from both `config.toml` and enterprise-managed
    `requirements.toml` without requiring a separate `hooks.json` payload.
    
    This gives enterprise admins a way to ship managed hook policy through
    the existing requirements channel while still leaving script delivery to
    MDM or other device-management tooling, and it keeps `hooks.json`
    working unchanged for existing users.
    
    This also lays the groundwork for follow-on managed filtering work such
    as #15937, while continuing to respect project trust gating from #14718.
    It does **not** implement `allow_managed_hooks_only` itself.
    
    NOTE: yes, it's a bit unfortunate that the toml isn't formatted as
    closely as normal to our default styling. This is because we're trying
    to stay compatible with the spec for plugins/hooks that we'll need to
    support & the main usecase here is embedding into requirements.toml
    
    ## What changed
    
    - moved the shared hook serde model out of `codex-rs/hooks` into
    `codex-rs/config` so the same schema can power `hooks.json`, inline
    `config.toml` hooks, and managed `requirements.toml` hooks
    - added `hooks` support to both `ConfigToml` and
    `ConfigRequirementsToml`, including requirements-side `managed_dir` /
    `windows_managed_dir`
    - treated requirements-managed hooks as one constrained value via
    `Constrained`, so managed hook policy is merged atomically and cannot
    drift across requirement sources
    - updated hook discovery to load requirements-managed hooks first, then
    per-layer `hooks.json`, then per-layer inline TOML hooks, with a warning
    when a single layer defines both representations
    - threaded managed hook metadata through discovered handlers and exposed
    requirements hooks in app-server responses, generated schemas, and
    `/debug-config`
    - added hook/config coverage in `codex-rs/config`, `codex-rs/hooks`,
    `codex-rs/core/src/config_loader/tests.rs`, and
    `codex-rs/core/tests/suite/hooks.rs`
    
    ## Testing
    
    - `cargo test -p codex-config`
    - `cargo test -p codex-hooks`
    - `cargo test -p codex-app-server config_api`
    
    ## Documentation
    
    Companion updates are needed in the developers website repo for:
    
    - the hooks guide
    - the config reference, sample, basic, and advanced pages
    - the enterprise managed configuration guide
    
    ---------
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • Rename approvals reviewer variant to auto-review (#19056)
    ## Why
    
    `approvals_reviewer` now uses `auto_review` as the canonical config/API
    value after #18504, but the Rust enum variant and nearby helper/test
    names still used `GuardianSubagent` / guardian approval wording. That
    made follow-up code and reviews confusing even though the external value
    had already moved to Auto-review.
    
    ## What changed
    
    - Renamed `ApprovalsReviewer::GuardianSubagent` to
    `ApprovalsReviewer::AutoReview`.
    - Updated protocol, app-server, config, core, TUI, exec, and analytics
    test callsites.
    - Renamed nearby helper/test names from guardian approval wording to
    Auto-review wording where they refer to the approvals reviewer mode.
    - Preserved wire compatibility:
      - `auto_review` remains the canonical serialized value.
      - `guardian_subagent` remains accepted as a legacy alias.
    
    This intentionally does not rename the `[features].guardian_approval`
    key, `Feature::GuardianApproval`, `core/src/guardian`, analytics event
    names, or app-server Guardian review event types.
    
    ## Verification
    
    - `cargo test -p codex-protocol
    approvals_reviewer_serializes_auto_review_and_accepts_legacy_guardian_subagent`
    - `cargo test -p codex-app-server-protocol
    approvals_reviewer_serializes_auto_review_and_accepts_legacy_guardian_subagent`
    - `cargo test -p codex-config approvals_reviewer`
    - `cargo test -p codex-tui update_feature_flags`
    - `cargo test -p codex-core permissions_instructions`
    - `cargo test -p codex-tui permissions_selection`
  • Rebrand approvals reviewer config to auto-review (#18504)
    ### Why
    
    Auto-review is the user-facing name for the approvals reviewer, but the
    config/API value still exposed the old `guardian_subagent` name. That
    made new configs and generated schemas point users at Guardian
    terminology even though the intended product surface is Auto-review.
    
    This PR updates the external `approvals_reviewer` value while preserving
    compatibility for existing configs and clients.
    
    ### What changed
    
    - Makes `auto_review` the canonical serialized value for
    `approvals_reviewer`.
    - Keeps `guardian_subagent` accepted as a legacy alias.
    - Keeps `user` accepted and serialized as `user`.
    - Updates generated config and app-server schemas so
    `approvals_reviewer` includes:
      - `user`
      - `auto_review`
      - `guardian_subagent`
    - Updates app-server README docs for the reviewer value.
    - Updates analytics and config requirements tests for the canonical
    auto_review value.
    
    
    ### Compatibility
    
    Existing configs and API payloads using:
    
    ```toml
    approvals_reviewer = "guardian_subagent"
    ```
    
    continue to load and map to the Auto-review reviewer behavior. 
    
    New serialization emits: 
    ```toml
    approvals_reviewer = "auto_review" 
    ```
    
    This PR intentionally does not rename the [features].guardian_approval
    key or broad internal Guardian symbols. Those are split out for a
    follow-up PR to keep this migration small and avoid touching large
    TUI/internal surfaces.
    
    **Verification**
    cargo test -p codex-protocol
    approvals_reviewer_serializes_auto_review_and_accepts_legacy_guardian_subagent
    cargo test -p codex-app-server-protocol
    approvals_reviewer_serializes_auto_review_and_accepts_legacy_guardian_subagent
  • feat(auto-review) policy config (#18959)
    ## Summary
    Allow users to customize their own auto-review policy config.
    
    ## Testing
    - [x] added config_tests
  • chore(tui) debug-config guardian_policy_config (#18923)
    ## Summary
    List guardian_policy_config_source in `/debug-config` output
    
    ## Testing
     - [x] Ran locally
  • Add remote_sandbox_config to our config requirements (#18763)
    ## Why
    
    Customers need finer-grained control over allowed sandbox modes based on
    the host Codex is running on. For example, they may want stricter
    sandbox limits on devboxes while keeping a different default elsewhere.
    
    Our current cloud requirements can target user/account groups, but they
    cannot vary sandbox requirements by host. That makes remote development
    environments awkward because the same top-level `allowed_sandbox_modes`
    has to apply everywhere.
    
    ## What
    
    Adds a new `remote_sandbox_config` section to `requirements.toml`:
    
    ```toml
    allowed_sandbox_modes = ["read-only"]
    
    [[remote_sandbox_config]]
    hostname_patterns = ["*.org"]
    allowed_sandbox_modes = ["read-only", "workspace-write"]
    
    [[remote_sandbox_config]]
    hostname_patterns = ["*.sh", "runner-*.ci"]
    allowed_sandbox_modes = ["read-only", "danger-full-access"]
    ```
    
    During requirements resolution, Codex resolves the local host name once,
    preferring the machine FQDN when available and falling back to the
    cleaned kernel hostname. This host classification is best effort rather
    than authenticated device proof.
    
    Each requirements source applies its first matching
    `remote_sandbox_config` entry before it is merged with other sources.
    The shared merge helper keeps that `apply_remote_sandbox_config` step
    paired with requirements merging so new requirements sources do not have
    to remember the extra call.
    
    That preserves source precedence: a lower-precedence requirements file
    with a matching `remote_sandbox_config` cannot override a
    higher-precedence source that already set `allowed_sandbox_modes`.
    
    This also wires the hostname-aware resolution through app-server,
    CLI/TUI config loading, config API reads, and config layer metadata so
    they all evaluate remote sandbox requirements consistently.
    
    ## Verification
    
    - `cargo test -p codex-config remote_sandbox_config`
    - `cargo test -p codex-config host_name`
    - `cargo test -p codex-core
    load_config_layers_applies_matching_remote_sandbox_config`
    - `cargo test -p codex-core
    system_remote_sandbox_config_keeps_cloud_sandbox_modes`
    - `cargo test -p codex-config`
    - `cargo test -p codex-core` unit tests passed; `tests/all.rs`
    integration matrix was intentionally stopped after the relevant focused
    tests passed
    - `just fix -p codex-config`
    - `just fix -p codex-core`
    - `cargo check -p codex-app-server`
  • feat: add a built-in Amazon Bedrock model provider (#18744)
    ## Why
    
    Codex needs a first-class `amazon-bedrock` model provider so users can
    select Bedrock without copying a full provider definition into
    `config.toml`. The provider has Codex-owned defaults for the pieces that
    should stay consistent across users: the display `name`, Bedrock
    `base_url`, and `wire_api`.
    
    At the same time, users still need a way to choose the AWS credential
    profile used by their local environment. This change makes
    `amazon-bedrock` a partially modifiable built-in provider: code owns the
    provider identity and endpoint defaults, while user config can set
    `model_providers.amazon-bedrock.aws.profile`.
    
    For example:
    
    ```toml
    model_provider = "amazon-bedrock"
    
    [model_providers.amazon-bedrock.aws]
    profile = "codex-bedrock"
    ```
    
    ## What Changed
    
    - Added `amazon-bedrock` to the built-in model provider map with:
      - `name = "Amazon Bedrock"`
      - `base_url = "https://bedrock-mantle.us-east-1.api.aws/v1"`
      - `wire_api = "responses"`
    - Added AWS provider auth config with a profile-only shape:
    `model_providers.<id>.aws.profile`.
    - Kept AWS auth config restricted to `amazon-bedrock`; custom providers
    that set `aws` are rejected.
    - Allowed `model_providers.amazon-bedrock` through reserved-provider
    validation so it can act as a partial override.
    - During config loading, only `aws.profile` is copied from the
    user-provided `amazon-bedrock` entry onto the built-in provider. Other
    Bedrock provider fields remain hard-coded by the built-in definition.
    - Updated the generated config schema for the new provider AWS profile
    config.
  • Add session config loader interface (#18208)
    ## Why
    
    Cloud-hosted sessions need a way for the service that starts or manages
    a thread to provide session-owned config without treating all config as
    if it came from the same user/project/workspace TOML stack.
    
    The important boundary is ownership: some values should be controlled by
    the session/orchestrator, some by the authenticated user, and later some
    may come from the executor. The earlier broad config-store shape made
    that boundary too fuzzy and overlapped heavily with the existing
    filesystem-backed config loader. This PR starts with the smaller piece
    we need now: a typed session config loader that can feed the existing
    config layer stack while preserving the normal precedence and merge
    behavior.
    
    ## What Changed
    
    - Added `ThreadConfigLoader` and related typed payloads in
    `codex-config`.
    - `SessionThreadConfig` currently supports `model_provider`,
    `model_providers`, and feature flags.
    - `UserThreadConfig` is present as an ownership boundary, but does not
    yet add TOML-backed fields.
    - `NoopThreadConfigLoader` preserves existing behavior when no external
    loader is configured.
      - `StaticThreadConfigLoader` supports tests and simple callers.
    
    - Taught thread config sources to produce ordinary `ConfigLayerEntry`
    values so the existing `ConfigLayerStack` remains the place where
    precedence and merging happen.
    
    - Wired the loader through `ConfigBuilder`, the config loader, and
    app-server startup paths so app-server can provide session-owned config
    before deriving a thread config.
    
    - Added coverage for:
      - translating typed thread config into config layers,
    - inserting thread config layers into the stack at the right precedence,
    - applying session-provided model provider and feature settings when
    app-server derives config from thread params.
    
    ## Follow-Ups
    
    This intentionally stops short of adding the remote/service transport.
    The next pieces are expected to be:
    
    1. Define the proto/API shape for this interface.
    2. Add a client implementation that can source session config from the
    service side.
    
    ## Verification
    
    - Added unit coverage in `codex-config` for the loader and layer
    conversion.
    - Added `codex-core` config loader coverage for thread config layer
    precedence.
    - Added app-server coverage that verifies session thread config wins
    over request-provided config for model provider and feature settings.
  • Add experimental remote thread store config (#18714)
    Add experimental config to use remote thread store rather than local
    thread store implementation in app server
  • feat: add --ignore-user-config and --ignore-rules (#18646)
    Add those 2 flags to be able to fully isolate a run of `codex exec` from
    any rules or tools.
    This will be used by Chronicle
  • chore(multiagent) skills instructions toggle (#18596)
    ## Summary
    Support toggling the skills message off.
    
    ## Test Plan
    - [x] Updated unit tests
  • [5/6] Wire executor-backed MCP stdio (#18212)
    ## Summary
    - Add the executor-backed RMCP stdio transport.
    - Wire MCP stdio placement through the executor environment config.
    - Cover local and executor-backed stdio paths with the existing MCP test
    helpers.
    
    ## Stack
    ```text
    o  #18027 [6/6] Fail exec client operations after disconnect
    │
    @  #18212 [5/6] Wire executor-backed MCP stdio
    │
    o  #18087 [4/6] Abstract MCP stdio server launching
    │
    o  #18020 [3/6] Add pushed exec process events
    │
    o  #18086 [2/6] Support piped stdin in exec process API
    │
    o  #18085 [1/6] Add MCP server environment config
    │
    o  main
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Add marketplace remove command and shared logic (#17752)
    ## Summary
    
    Move the marketplace remove implementation into shared core logic so
    both the CLI command and follow-up app-server RPC can reuse the same
    behavior.
    
    This change:
    - adds a shared `codex_core::plugins::remove_marketplace(...)` flow
    - moves validation, config removal, and installed-root deletion out of
    the CLI
    - keeps the CLI as a thin wrapper over the shared implementation
    - adds focused core coverage for the shared remove path
    
    ## Validation
    
    - `just fmt`
    - focused local coverage for the shared remove path
    - heavier follow-up validation deferred to stacked PR CI
  • [TUI] add external config migration prompt when start TUI (#17891)
    - add a TUI startup migration prompt for external agent config
    - support migrating external configs including config, skills, AGENTS.md
    and plugins
    - gate the prompt behind features.external_migrate (default false)
    
    <img width="1037" height="480" alt="Screenshot 2026-04-14 at 9 29 14 PM"
    src="https://github.com/user-attachments/assets/6060849b-03cb-429a-9c13-c7bb46ad2e65"
    />
    <img width="713" height="183" alt="Screenshot 2026-04-14 at 9 29 26 PM"
    src="https://github.com/user-attachments/assets/d13f177e-d4c4-479c-8736-ef29636081e1"
    />
    
    ---------
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • fix: trust-gate project hooks and exec policies (#14718)
    ## Summary
    - trust-gate project `.codex` layers consistently, including repos that
    have `.codex/hooks.json` or `.codex/execpolicy/*.rules` but no
    `.codex/config.toml`
    - keep disabled project layers in the config stack so nested trusted
    project layers still resolve correctly, while preventing hooks and exec
    policies from loading until the project is trusted
    - update app-server/TUI onboarding copy to make the trust boundary
    explicit and add regressions for loader, hooks, exec-policy, and
    onboarding coverage
    
    ## Security
    Before this change, an untrusted repo could auto-load project hooks or
    exec policies from `.codex/` as long as `config.toml` was absent. This
    makes trust the single gate for project-local config, hooks, and exec
    policies.
    
    ## Stack
    - Parent of #15936
    
    ## Test
    - cargo test -p codex-core without_config_toml
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: config aliases (#18140)
    Rename `no_memories_if_mcp_or_web_search` →
    `disable_on_external_context` with backward compatibility
    
    While doing so, we add a key alias system on our layer merging system.
    What we try to avoid is a case where a company managed config use an old
    name while the user has a new name in it's local config (which would
    make the deserialization fail)
  • feat(config): support managed deny-read requirements (#17740)
    ## Summary
    - adds managed requirements support for deny-read filesystem entries
    - constrains config layers so managed deny-read requirements cannot be
    widened by user-controlled config
    - surfaces managed deny-read requirements through debug/config plumbing
    
    This PR lets managed requirements inject deny-read filesystem
    constraints into the effective filesystem sandbox policy.
    User-controlled config can still choose the surrounding permission
    profile, but it cannot remove or weaken the managed deny-read entries.
    
    ## Managed deny-read shape
    A managed requirements file can declare exact paths and glob patterns
    under `[permissions.filesystem]`:
    
    ```toml
    # /etc/codex/requirements.toml
    [permissions.filesystem]
    deny_read = [
      "/Users/alice/.gitconfig",
      "/Users/alice/.ssh",
      "./managed-private/**/*.env",
    ]
    ```
    
    Those entries are compiled into the effective filesystem policy as
    `access = none` rules, equivalent in shape to filesystem permission
    entries like:
    
    ```toml
    [permissions.workspace.filesystem]
    "/Users/alice/.gitconfig" = "none"
    "/Users/alice/.ssh" = "none"
    "/absolute/path/to/managed-private/**/*.env" = "none"
    ```
    
    The important difference is that the managed entries come from
    requirements, so lower-precedence user config cannot remove them or make
    those paths readable again.
    
    Relative managed `deny_read` entries are resolved relative to the
    directory containing the managed requirements file. Glob entries keep
    their glob suffix after the non-glob prefix is normalized.
    
    ## Runtime behavior
    - Managed `deny_read` entries are appended to the effective
    `FileSystemSandboxPolicy` after the selected permission profile is
    resolved.
    - Exact paths become `FileSystemPath::Path { access: None }`; glob
    patterns become `FileSystemPath::GlobPattern { access: None }`.
    - When managed deny-read entries are present, `sandbox_mode` is
    constrained to `read-only` or `workspace-write`; `danger-full-access`
    and `external-sandbox` cannot silently bypass the managed read-deny
    policy.
    - On Windows, the managed deny-read policy is enforced for direct file
    tools, but shell subprocess reads are not sandboxed yet, so startup
    emits a warning for that platform.
    - `/debug-config` shows the effective managed requirement as
    `permissions.filesystem.deny_read` with its source.
    
    ## Stack
    1. #15979 - glob deny-read policy/config/direct-tool support
    2. #18096 - macOS and Linux sandbox enforcement
    3. This PR - managed deny-read requirements
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Refactor config loading to use filesystem abstraction (#18209)
    Initial pass propagating FileSystem through config loading.
  • feat(sandbox): add glob deny-read platform enforcement (#18096)
    ## Summary
    - adds macOS Seatbelt deny rules for unreadable glob patterns
    - expands unreadable glob matches on Linux and masks them in bwrap,
    including canonical symlink targets
    - keeps Linux glob expansion robust when `rg` is unavailable in minimal
    or Bazel test environments
    - adds sandbox integration coverage that runs `shell` and `exec_command`
    with a `**/*.env = none` policy and verifies the secret contents do not
    reach the model
    
    ## Linux glob expansion
    
    ```text
    Prefer:   rg --files --hidden --no-ignore --glob <pattern> -- <search-root>
    Fallback: internal globset walker when rg is not installed
    Failure:  any other rg failure aborts sandbox construction
    ```
    
    ```
    [permissions.workspace.filesystem]
    glob_scan_max_depth = 2
    
    [permissions.workspace.filesystem.":project_roots"]
    "**/*.env" = "none"
    ```
    
    
    This keeps the common path fast without making sandbox construction
    depend on an ambient `rg` binary. If `rg` is present but fails for
    another reason, the sandbox setup fails closed instead of silently
    omitting deny-read masks.
    
    ## Platform support
    - macOS: subprocess sandbox enforcement is handled by Seatbelt regex
    deny rules
    - Linux: subprocess sandbox enforcement is handled by expanding existing
    glob matches and masking them in bwrap
    - Windows: policy/config/direct-tool glob support is already on `main`
    from #15979; Windows subprocess sandbox paths continue to fail closed
    when unreadable split filesystem carveouts require runtime enforcement,
    rather than silently running unsandboxed
    
    ## Stack
    1. #15979 - merged: cross-platform glob deny-read
    policy/config/direct-tool support for macOS, Linux, and Windows
    2. This PR - macOS/Linux subprocess sandbox enforcement plus Windows
    fail-closed clarification
    3. #17740 - managed deny-read requirements
    
    ## Verification
    - Added integration coverage for `shell` and `exec_command` glob
    deny-read enforcement
    - `cargo check -p codex-sandboxing -p codex-linux-sandbox --tests`
    - `cargo check -p codex-core --test all`
    - `cargo clippy -p codex-linux-sandbox -p codex-sandboxing --tests`
    - `just bazel-lock-check`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add codex_hook_run analytics event (#17996)
    # Why
    Add product analytics for hook handler executions so we can understand
    which hooks are running, where they came from, and whether they
    completed, failed, stopped, or blocked work.
    
    # What
    - add the new `codex_hook_run` analytics event and payload plumbing in
    `codex-rs/analytics`
    - emit hook-run analytics from the shared hook completion path in
    `codex-rs/core`
    - classify hook source from the loaded hook path as `system`, `user`,
    `project`, or `unknown`
    
    ```
    {
      "event_type": "codex_hook_run",
      "event_params": {
        "thread_id": "string",
        "turn_id": "string",
        "model_slug": "string",
        "hook_name": "string, // any HookEventName
        "hook_source": "system | user | project | unknown",
        "status": "completed | failed | stopped | blocked"
      }
    }
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add server-level approval defaults for custom MCP servers (#17843)
    ## Summary
    - Add `default_tools_approval_mode` support for custom MCP server
    configs, matching the existing `codex_apps` behavior
    - Apply approval precedence as per-tool override, then server default,
    then `auto`
    - Update config serialization, CLI display, schema generation, docs, and
    tests
    
    ## Testing
    - `cargo check -p codex-config`
    - `cargo check -p codex-core`
    - `just write-config-schema`
    - `just fmt`
    - `cargo test -p codex-config`
    - Targeted `codex-core` tests for config parsing, config writes, and MCP
    approval precedence
    - `just fix -p codex-config -p codex-core`
  • Auto-upgrade configured marketplaces (#17425)
    ## Summary
    - Add best-effort auto-upgrade for user-configured Git marketplaces
    recorded in `config.toml`.
    - Track the last activated Git revision with `last_revision` so
    unchanged marketplace sources skip clone work.
    - Trigger the upgrade from plugin startup and `plugin/list`, while
    preserving existing fail-open plugin behavior with warning logs rather
    than new user-visible errors.
    
    ## Details
    - Remote configured marketplaces use `git ls-remote` to compare the
    source/ref against the recorded revision.
    - Upgrades clone into a staging directory, validate that
    `.agents/plugins/marketplace.json` exists and that the manifest name
    matches the configured marketplace key, then atomically activate the new
    root.
    - Local `.agents/plugins/marketplace.json` marketplaces remain live
    filesystem state and are not auto-pulled.
    - Existing non-curated plugin cache refresh is kicked after successful
    marketplace root upgrades.
    
    ## Validation
    - `just write-config-schema`
    - `cargo test -p codex-core marketplace_upgrade`
    - `cargo check -p codex-cli -p codex-app-server`
    - `just fix -p codex-core`
    
    Did not run the complete `cargo test` suite because the repo
    instructions require asking before a full core workspace run.
  • [1/8] Add MCP server environment config (#18085)
    ## Summary
    - Add an MCP server environment setting with local as the default.
    - Thread the default through config serialization, schema generation,
    and existing config fixtures.
    
    ## Stack
    ```text
    o  #18027 [8/8] Fail exec client operations after disconnect
    │
    o  #18025 [7/8] Cover MCP stdio tests with executor placement
    │
    o  #18089 [6/8] Wire remote MCP stdio through executor
    │
    o  #18088 [5/8] Add executor process transport for MCP stdio
    │
    o  #18087 [4/8] Abstract MCP stdio server launching
    │
    o  #18020 [3/8] Add pushed exec process events
    │
    o  #18086 [2/8] Support piped stdin in exec process API
    │
    @  #18085 [1/8] Add MCP server environment config
    │
    o  main
    ```
    
    Co-authored-by: Codex <noreply@openai.com>
  • nit: add min values for memories (#18137)
    Just add min values to some memories config fields
  • Async config loading (#18022)
    Parts of config will come from executor. Prepare for that by making
    config loading methods async.
  • [codex] Support local marketplace sources (#17756)
    ## Summary
    
    - Port marketplace source support into the shared core marketplace-add
    flow
    - Support local marketplace directory sources
    - Support direct `marketplace.json` URL sources
    - Persist the new source types in config/schema and cover them in CLI
    and app-server tests
    
    ## Validation
    
    - `cargo test -p codex-core marketplace_add`
    - `cargo test -p codex-cli marketplace_add`
    - `cargo test -p codex-app-server marketplace_add`
    - `just write-config-schema`
    - `just fmt`
    - `just fix -p codex-core`
    - `just fix -p codex-cli`
    
    ## Context
    
    Current `main` moved marketplace-add behavior into shared core code and
    still assumed only git-backed sources. This change keeps that structure
    but restores support for local directories and direct manifest URLs in
    the shared path.
  • fix: Revert danger-full-access denylist-only mode (#17732)
    ## Summary
    
    - Reverts openai/codex#16946 and removes the danger-full-access
    denylist-only network mode.
    - Removes the corresponding config requirements, app-server
    protocol/schema, config API, TUI debug output, and network proxy
    behavior.
    - Drops stale tests that depended on the reverted mode while preserving
    newer managed allowlist-only coverage.
    
    ## Verification
    
    - `just write-app-server-schema`
    - `just fmt`
    - `cargo test -p codex-config network_requirements`
    - `cargo test -p codex-core network_proxy_spec`
    - `cargo test -p codex-core
    managed_network_proxy_decider_survives_full_access_start`
    - `cargo test -p codex-app-server map_requirements_toml_to_api`
    - `cargo test -p codex-tui debug_config_output`
    - `cargo test -p codex-app-server-protocol`
    - `just fix -p codex-config -p codex-core -p codex-app-server-protocol
    -p codex-app-server -p codex-tui`
    - `git diff --cached --check`
    
    Not run: full workspace `cargo test` (repo instructions ask for
    confirmation before that broader run).
  • Add supports_parallel_tool_calls flag to included mcps (#17667)
    ## Why
    
    For more advanced MCP usage, we want the model to be able to emit
    parallel MCP tool calls and have Codex execute eligible ones
    concurrently, instead of forcing all MCP calls through the serial block.
    
    The main design choice was where to thread the config. I made this
    server-level because parallel safety depends on the MCP server
    implementation. Codex reads the flag from `mcp_servers`, threads the
    opted-in server names into `ToolRouter`, and checks the parsed
    `ToolPayload::Mcp { server, .. }` at execution time. That avoids relying
    on model-visible tool names, which can be incomplete in
    deferred/search-tool paths or ambiguous for similarly named
    servers/tools.
    
    ## What was added
    
    Added `supports_parallel_tool_calls` for MCP servers.
    
    Before:
    
    ```toml
    [mcp_servers.docs]
    command = "docs-server"
    ```
    
    After:
    
    ```toml
    [mcp_servers.docs]
    command = "docs-server"
    supports_parallel_tool_calls = true
    ```
    
    MCP calls remain serial by default. Only tools from opted-in servers are
    eligible to run in parallel. Docs also now warn to enable this only when
    the server’s tools are safe to run concurrently, especially around
    shared state or read/write races.
    
    ## Testing
    
    Tested with a local stdio MCP server exposing real delay tools. The
    model/Responses side was mocked only to deterministically emit two MCP
    calls in the same turn.
    
    Each test called `query_with_delay` and `query_with_delay_2` with `{
    "seconds": 25 }`.
    
    | Build/config | Observed | Wall time |
    | --- | --- | --- |
    | main with flag enabled | serial | `58.79s` |
    | PR with flag enabled | parallel | `31.73s` |
    | PR without flag | serial | `56.70s` |
    
    PR with flag enabled showed both tools start before either completed;
    main and PR-without-flag completed the first delay before starting the
    second.
    
    Also added an integration test.
    
    Additional checks:
    
    - `cargo test -p codex-tools` passed
    - `cargo test -p codex-core
    mcp_parallel_support_uses_exact_payload_server` passed
    - `git diff --check` passed
  • Build remote exec env from exec-server policy (#17216)
    ## Summary
    - add an exec-server `envPolicy` field; when present, the server starts
    from its own process env and applies the shell environment policy there
    - keep `env` as the exact environment for local/embedded starts, but
    make it an overlay for remote unified-exec starts
    - move the shell-environment-policy builder into `codex-config` so Core
    and exec-server share the inherit/filter/set/include behavior
    - overlay only runtime/sandbox/network deltas from Core onto the
    exec-server-derived env
    
    ## Why
    Remote unified exec was materializing the shell env inside Core and
    forwarding the whole map to exec-server, so remote processes could
    inherit the orchestrator machine's `HOME`, `PATH`, etc. This keeps the
    base env on the executor while preserving Core-owned runtime additions
    like `CODEX_THREAD_ID`, unified-exec defaults, network proxy env, and
    sandbox marker env.
    
    ## Validation
    - `just fmt`
    - `git diff --check`
    - `cargo test -p codex-exec-server --lib`
    - `cargo test -p codex-core --lib unified_exec::process_manager::tests`
    - `cargo test -p codex-core --lib exec_env::tests`
    - `cargo test -p codex-core --lib exec_env_tests` (compile-only; filter
    matched 0 tests)
    - `cargo test -p codex-config --lib shell_environment` (compile-only;
    filter matched 0 tests)
    - `just bazel-lock-update`
    
    ## Known local validation issue
    - `just bazel-lock-check` is not runnable in this checkout: it invokes
    `./scripts/check-module-bazel-lock.sh`, which is missing.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
    Co-authored-by: pakrym-oai <pakrym@openai.com>
  • Add marketplace command (#17087)
    Added a new top-level `codex marketplace add` command for installing
    plugin marketplaces into Codex’s local marketplace cache.
    
    This change adds source parsing for local directories, GitHub shorthand,
    and git URLs, supports optional `--ref` and git-only `--sparse` checkout
    paths, stages the source in a temp directory, validates the marketplace
    manifest, and installs it under
    `$CODEX_HOME/marketplaces/<marketplace-name>`
    
    Included tests cover local install behavior in the CLI and marketplace
    discovery from installed roots in core. Scoped formatting and fix passes
    were run, and targeted CLI/core tests passed.
  • Render statusline context as a meter (#17170)
    Problem: The statusline reported context as an “X% left” value, which
    could be mistaken for quota, and context usage was included in the
    default footer.
    
    Solution: Render configured context status items as a filling context
    meter, preserve `context-used` as a legacy alias while hiding it from
    the setup menu, and remove context from the default statusline. It will
    still be available as an opt-in option for users who want to see it.
    
    <img width="317" height="39" alt="image"
    src="https://github.com/user-attachments/assets/3aeb39bb-f80d-471f-88fe-d55e25b31491"
    />
  • make webrtc the default experience (#17188)
    ## Summary
    - make realtime default to the v2 WebRTC path
    - keep partial realtime config tables inheriting
    `RealtimeConfig::default()`
    
    ## Validation
    - CI found a stale config-test expectation; fixed in 974ba51bb3
    - just fmt
    - git diff --check
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add TUI notification condition config (#17175)
    Problem: TUI desktop notifications are hard-gated on terminal focus, so
    terminal/IDE hosts that want in-focus notifications cannot opt in.
    
    Solution: Add a flat `[tui] notification_condition` setting (`unfocused`
    by default, `always` opt-in), carry grouped TUI notification settings
    through runtime config, apply method + condition together in the TUI,
    and regenerate the config schema.
  • Add realtime voice selection (#17176)
    - Add realtime voice selection for realtime/start.
    - Expose the supported v1/v2 voice lists and cover explicit, configured,
    default, and invalid voice paths.
  • Update guardian output schema (#17061)
    ## Summary
    - Update guardian output schema to separate risk, authorization,
    outcome, and rationale.
    - Feed guardian rationale into rejection messages.
    - Split the guardian policy into template and tenant-config sections.
    
    ## Validation
    - `cargo test -p codex-core mcp_tool_call`
    - `env -u CODEX_SANDBOX_NETWORK_DISABLED INSTA_UPDATE=always cargo test
    -p codex-core guardian::`
    
    ---------
    
    Co-authored-by: Owen Lin <owen@openai.com>
  • Add realtime transport config (#17097)
    Adds realtime.transport config with websocket as the default and webrtc
    wired through the effective config.
    
    Co-authored-by: Codex <noreply@openai.com>
  • Configure multi_agent_v2 spawn agent hints (#17071)
    Allow multi_agent_v2 features to have its own temporary configuration
    under `[features.multi_agent_v2]`
    
    ```
    [features.multi_agent_v2]
    enabled = true
    usage_hint_enabled = false
    usage_hint_text = "Custom delegation guidance."
    hide_spawn_agent_metadata = true
    ```
    
    Absent `usage_hint_text` means use the default hint.
    
    ```
    [features]
    multi_agent_v2 = true
    ```
    
    still works as the boolean shorthand.
  • feat: single app-server bootstrap in TUI (#16582)
    Before this, the TUI was starting 2 app-server. One to check the login
    status and one to actually start the session
    
    This PR make only one app-server startup and defer the login check in
    async, outside of the frame rendering path
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] reduce module visibility (#16978)
    ## Summary
    - reduce public module visibility across Rust crates, preferring private
    or crate-private modules with explicit crate-root public exports
    - update external call sites and tests to use the intended public crate
    APIs instead of reaching through module trees
    - add the module visibility guideline to AGENTS.md
    
    ## Validation
    - `cargo check --workspace --all-targets --message-format=short` passed
    before the final fix/format pass
    - `just fix` completed successfully
    - `just fmt` completed successfully
    - `git diff --check` passed
  • [codex] Add danger-full-access denylist-only network mode (#16946)
    ## Summary
    
    This adds `experimental_network.danger_full_access_denylist_only` for
    orgs that want yolo / danger-full-access sessions to keep full network
    access while still enforcing centrally managed deny rules.
    
    When the flag is true and the session sandbox is `danger-full-access`,
    the network proxy starts with:
    
    - domain allowlist set to `*`
    - managed domain `deny` entries enforced
    - upstream proxy use allowed
    - all Unix sockets allowed
    - local/private binding allowed
    
    Caveat: the denylist is best effort only. In yolo / danger-full-access
    mode, Codex or the model can use an allowed socket or other
    local/private network path to bypass the proxy denylist, so this should
    not be treated as a hard security boundary.
    
    The flag is intentionally scoped to `SandboxPolicy::DangerFullAccess`.
    Read-only and workspace-write modes keep the existing managed/user
    allowlist, denylist, Unix socket, and local-binding behavior. This does
    not enable the non-loopback proxy listener setting; that still requires
    its own explicit config.
    
    This also threads the new field through config requirements parsing,
    app-server protocol/schema output, config API mapping, and the TUI debug
    config output.
    
    ## How to use
    
    Add the flag under `[experimental_network]` in the network policy config
    that is delivered to Codex. The setting is not under `[permissions]`.
    
    ```toml
    [experimental_network]
    enabled = true
    danger_full_access_denylist_only = true
    
    [experimental_network.domains]
    "blocked.example.com" = "deny"
    "*.blocked.example.com" = "deny"
    ```
    
    With that configuration, yolo / danger-full-access sessions get broad
    network access except for the managed denied domains above. The denylist
    remains a best-effort proxy policy because the session may still use
    allowed sockets to bypass it. Other sandbox modes do not get the
    wildcard domain allowlist or the socket/local-binding relaxations from
    this flag.
    
    ## Verification
    
    - `cargo test -p codex-config network_requirements`
    - `cargo test -p codex-core network_proxy_spec`
    - `cargo test -p codex-app-server map_requirements_toml_to_api`
    - `cargo test -p codex-tui debug_config_output`
    - `cargo test -p codex-app-server-protocol`
    - `just write-app-server-schema`
    - `just fmt`
    - `just fix -p codex-config -p codex-core -p codex-app-server-protocol
    -p codex-app-server -p codex-tui`
    - `just fix -p codex-core -p codex-config`
    - `git diff --check`
    - `cargo clean`
  • Refactor config types into a separate crate (#16962)
    Move config types into a separate crate because their macros expand into
    a lot of new code.
  • feat(requirements): support allowed_approval_reviewers (#16701)
    ## Description
    
    Add requirements.toml support for `allowed_approvals_reviewers =
    ["user", "guardian_subagent"]`, so admins can now restrict the use of
    guardian mode.
    
    Note: If a user sets a reviewer that isn’t allowed by requirements.toml,
    config loading falls back to the first allowed reviewer and emits a
    startup warning.
    
    The table below describes the possible admin controls.
    | Admin intent | `requirements.toml` | User `config.toml` | End result |
    |---|---|---|---|
    | Leave Guardian optional | omit `allowed_approvals_reviewers` or set
    `["user", "guardian_subagent"]` | user chooses `approvals_reviewer =
    "user"` or `"guardian_subagent"` | Guardian off for `user`, on for
    `guardian_subagent` + `approval_policy = "on-request"` |
    | Force Guardian off | `allowed_approvals_reviewers = ["user"]` | any
    user value | Effective reviewer is `user`; Guardian off |
    | Force Guardian on | `allowed_approvals_reviewers =
    ["guardian_subagent"]` and usually `allowed_approval_policies =
    ["on-request"]` | any user reviewer value; user should also have
    `approval_policy = "on-request"` unless policy is forced | Effective
    reviewer is `guardian_subagent`; Guardian on when effective approval
    policy is `on-request` |
    | Allow both, but default to manual if user does nothing |
    `allowed_approvals_reviewers = ["user", "guardian_subagent"]` | omit
    `approvals_reviewer` | Effective reviewer is `user`; Guardian off |
    | Allow both, and user explicitly opts into Guardian |
    `allowed_approvals_reviewers = ["user", "guardian_subagent"]` |
    `approvals_reviewer = "guardian_subagent"` and `approval_policy =
    "on-request"` | Guardian on |
    | Invalid admin config | `allowed_approvals_reviewers = []` | anything |
    Config load error |
  • [codex] Move config types into codex-config (#16523)
    ## Why
    
    `codex-rs/core/src/config/types.rs` is a plain config-type module with
    no dependency on `codex-core`. Moving it into `codex-config` shrinks the
    core crate and gives config-only consumers a more natural dependency
    boundary.
    
    ## What Changed
    
    - Added `codex_config::types` with the moved structs, enums, constants,
    and unit tests.
    - Kept `codex_core::config::types` as a compatibility re-export to avoid
    a broad call-site migration in this PR.
    - Switched notice-table writes in `core/src/config/edit.rs` to a local
    `NOTICE_TABLE_KEY` constant.
    - Added the `wildmatch` runtime dependency and `tempfile` test
    dependency to `codex-config`.
  • Extract MCP into codex-mcp crate (#15919)
    - Split MCP runtime/server code out of `codex-core` into the new
    `codex-mcp` crate. New/moved public structs/types include `McpConfig`,
    `McpConnectionManager`, `ToolInfo`, `ToolPluginProvenance`,
    `CodexAppsToolsCacheKey`, and the `McpManager` API
    (`codex_mcp::mcp::McpManager` plus the `codex_core::mcp::McpManager`
    wrapper/shim). New/moved functions include `with_codex_apps_mcp`,
    `configured_mcp_servers`, `effective_mcp_servers`,
    `collect_mcp_snapshot`, `collect_mcp_snapshot_from_manager`,
    `qualified_mcp_tool_name_prefix`, and the MCP auth/skill-dependency
    helpers. Why: this creates a focused MCP crate boundary and shrinks
    `codex-core` without forcing every consumer to migrate in the same PR.
    
    - Move MCP server config schema and persistence into `codex-config`.
    New/moved structs/enums include `AppToolApproval`,
    `McpServerToolConfig`, `McpServerConfig`, `RawMcpServerConfig`,
    `McpServerTransportConfig`, `McpServerDisabledReason`, and
    `codex_config::ConfigEditsBuilder`. New/moved functions include
    `load_global_mcp_servers` and
    `ConfigEditsBuilder::replace_mcp_servers`/`apply`. Why: MCP TOML
    parsing/editing is config ownership, and this keeps config
    validation/round-tripping (including per-tool approval overrides and
    inline bearer-token rejection) in the config crate instead of
    `codex-core`.
    
    - Rewire `codex-core`, app-server, and plugin call sites onto the new
    crates. Updated `Config::to_mcp_config(&self, plugins_manager)`,
    `codex-rs/core/src/mcp.rs`, `codex-rs/core/src/connectors.rs`,
    `codex-rs/core/src/codex.rs`,
    `CodexMessageProcessor::list_mcp_server_status_task`, and
    `utils/plugins/src/mcp_connector.rs` to build/pass the new MCP
    config/runtime types. Why: plugin-provided MCP servers still merge with
    user-configured servers, and runtime auth (`CodexAuth`) is threaded into
    `with_codex_apps_mcp` / `collect_mcp_snapshot` explicitly so `McpConfig`
    stays config-only.
  • chore: clean up argument-comment lint and roll out all-target CI on macOS (#16054)
    ## Why
    
    `argument-comment-lint` was green in CI even though the repo still had
    many uncommented literal arguments. The main gap was target coverage:
    the repo wrapper did not force Cargo to inspect test-only call sites, so
    examples like the `latest_session_lookup_params(true, ...)` tests in
    `codex-rs/tui_app_server/src/lib.rs` never entered the blocking CI path.
    
    This change cleans up the existing backlog, makes the default repo lint
    path cover all Cargo targets, and starts rolling that stricter CI
    enforcement out on the platform where it is currently validated.
    
    ## What changed
    
    - mechanically fixed existing `argument-comment-lint` violations across
    the `codex-rs` workspace, including tests, examples, and benches
    - updated `tools/argument-comment-lint/run-prebuilt-linter.sh` and
    `tools/argument-comment-lint/run.sh` so non-`--fix` runs default to
    `--all-targets` unless the caller explicitly narrows the target set
    - fixed both wrappers so forwarded cargo arguments after `--` are
    preserved with a single separator
    - documented the new default behavior in
    `tools/argument-comment-lint/README.md`
    - updated `rust-ci` so the macOS lint lane keeps the plain wrapper
    invocation and therefore enforces `--all-targets`, while Linux and
    Windows temporarily pass `-- --lib --bins`
    
    That temporary CI split keeps the stricter all-targets check where it is
    already cleaned up, while leaving room to finish the remaining Linux-
    and Windows-specific target-gated cleanup before enabling
    `--all-targets` on those runners. The Linux and Windows failures on the
    intermediate revision were caused by the wrapper forwarding bug, not by
    additional lint findings in those lanes.
    
    ## Validation
    
    - `bash -n tools/argument-comment-lint/run.sh`
    - `bash -n tools/argument-comment-lint/run-prebuilt-linter.sh`
    - shell-level wrapper forwarding check for `-- --lib --bins`
    - shell-level wrapper forwarding check for `-- --tests`
    - `just argument-comment-lint`
    - `cargo test` in `tools/argument-comment-lint`
    - `cargo test -p codex-terminal-detection`
    
    ## Follow-up
    
    - Clean up remaining Linux-only target-gated callsites, then switch the
    Linux lint lane back to the plain wrapper invocation.
    - Clean up remaining Windows-only target-gated callsites, then switch
    the Windows lint lane back to the plain wrapper invocation.
  • chore: refactor network permissions to use explicit domain and unix socket rule maps (#15120)
    ## Summary
    
    This PR replaces the legacy network allow/deny list model with explicit
    rule maps for domains and unix sockets across managed requirements,
    permissions profiles, the network proxy config, and the app server
    protocol.
    
    Concretely, it:
    
    - introduces typed domain (`allow` / `deny`) and unix socket permission
    (`allow` / `none`) entries instead of separate `allowed_domains`,
    `denied_domains`, and `allow_unix_sockets` lists
    - updates config loading, managed requirements merging, and exec-policy
    overlays to read and upsert rule entries consistently
    - exposes the new shape through protocol/schema outputs, debug surfaces,
    and app-server config APIs
    - rejects the legacy list-based keys and updates docs/tests to reflect
    the new config format
    
    ## Why
    
    The previous representation split related network policy across multiple
    parallel lists, which made merging and overriding rules harder to reason
    about. Moving to explicit keyed permission maps gives us a single source
    of truth per host/socket entry, makes allow/deny precedence clearer, and
    gives protocol consumers access to the full rule state instead of
    derived projections only.
    
    ## Backward Compatibility
    
    ### Backward compatible
    
    - Managed requirements still accept the legacy
    `experimental_network.allowed_domains`,
    `experimental_network.denied_domains`, and
    `experimental_network.allow_unix_sockets` fields. They are normalized
    into the new canonical `domains` and `unix_sockets` maps internally.
    - App-server v2 still deserializes legacy `allowedDomains`,
    `deniedDomains`, and `allowUnixSockets` payloads, so older clients can
    continue reading managed network requirements.
    - App-server v2 responses still populate `allowedDomains`,
    `deniedDomains`, and `allowUnixSockets` as legacy compatibility views
    derived from the canonical maps.
    - `managed_allowed_domains_only` keeps the same behavior after
    normalization. Legacy managed allowlists still participate in the same
    enforcement path as canonical `domains` entries.
    
    ### Not backward compatible
    
    - Permissions profiles under `[permissions.<profile>.network]` no longer
    accept the legacy list-based keys. Those configs must use the canonical
    `[domains]` and `[unix_sockets]` tables instead of `allowed_domains`,
    `denied_domains`, or `allow_unix_sockets`.
    - Managed `experimental_network` config cannot mix canonical and legacy
    forms in the same block. For example, `domains` cannot be combined with
    `allowed_domains` or `denied_domains`, and `unix_sockets` cannot be
    combined with `allow_unix_sockets`.
    - The canonical format can express explicit `"none"` entries for unix
    sockets, but those entries do not round-trip through the legacy
    compatibility fields because the legacy fields only represent allow/deny
    lists.
    ## Testing
    `/target/debug/codex sandbox macos --log-denials /bin/zsh -c 'curl
    https://www.example.com' ` gives 200 with config
    ```
    [permissions.workspace.network.domains]
    "www.example.com" = "allow"
    ```
    and fails when set to deny: `curl: (56) CONNECT tunnel failed, response
    403`.
    
    Also tested backward compatibility path by verifying that adding the
    following to `/etc/codex/requirements.toml` works:
    ```
    [experimental_network]
    allowed_domains = ["www.example.com"]
    ```
  • Extract codex-core-skills crate (#15749)
    ## Summary
    - move skill loading and management into codex-core-skills
    - leave codex-core with the thin integration layer and shared wiring
    
    ## Testing
    - CI
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: harden plugin feature gating (#15104)
    Resubmit https://github.com/openai/codex/pull/15020 with correct
    content.
    
    1. Use requirement-resolved config.features as the plugin gate.
    2. Guard plugin/list, plugin/read, and related flows behind that gate.
    3. Skip bad marketplace.json files instead of failing the whole list.
    4. Simplify plugin state and caching.