Commit Graph

261 Commits

  • add(core): safety check downgrade warning (#11964)
    Add per-turn notice when a request is downgraded to a fallback model due
    to cyber safety checks.
    
    **Changes**
    
    - codex-api: Emit a ServerModel event based on the openai-model response
    header and/or response payload (SSE + WebSocket), including when the
    model changes mid-stream.
    - core: When the server-reported model differs from the requested model,
    emit a single per-turn warning explaining the reroute to gpt-5.2 and
    directing users to Trusted
        Access verification and the cyber safety explainer.
    - app-server (v2): Surface these cyber model-routing warnings as
    synthetic userMessage items with text prefixed by Warning: (and document
    this behavior).
  • feat(core): add structured network approval plumbing and policy decision model (#11672)
    ### Description
    #### Summary
    Introduces the core plumbing required for structured network approvals
    
    #### What changed
    - Added structured network policy decision modeling in core.
    - Added approval payload/context types needed for network approval
    semantics.
    - Wired shell/unified-exec runtime plumbing to consume structured
    decisions.
    - Updated related core error/event surfaces for structured handling.
    - Updated protocol plumbing used by core approval flow.
    - Included small CLI debug sandbox compatibility updates needed by this
    layer.
    
    #### Why
    establishes the minimal backend foundation for network approvals without
    yet changing high-level orchestration or TUI behavior.
    
    #### Notes
    - Behavior remains constrained by existing requirements/config gating.
    - Follow-up PRs in the stack handle orchestration, UX, and app-server
    integration.
    
    ---------
    
    Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
  • fix: send unfiltered models over model/list (#11793)
    ### What
    to unblock filtering models in VSCE, change `model/list` app-server
    endpoint to send all models + visibility field `showInPicker` so
    filtering can be done in VSCE if desired.
    
    ### Tests
    Updated tests.
  • codex-rs: fix thread resume rejoin semantics (#11756)
    ## Summary
    - always rejoin an in-memory running thread on `thread/resume`, even
    when overrides are present
    - reject `thread/resume` when `history` is provided for a running thread
    - reject `thread/resume` when `path` mismatches the running thread
    rollout path
    - warn (but do not fail) on override mismatches for running threads
    - add more `thread_resume` integration tests and fixes; including
    restart-based resume-with-overrides coverage
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-app-server --test all thread_resume`
    - manual test with app-server-test-client
    https://github.com/openai/codex/pull/11755
    - manual test both stdio and websocket in app
  • [app-server] add fuzzyFileSearch/sessionCompleted (#11773)
    this is to allow the client to know when to stop showing a spinner.
  • [apps] Improve app listing filtering. (#11697)
    - [x] If an installed app is not on the app listing, remove it from the
    final list.
  • [apps] Fix app loading logic. (#11518)
    When `app/list` is called with `force_refetch=True`, we should seed the
    results with what is already cached instead of starting from an empty
    list. Otherwise when we send app/list/updated events, the client will
    first see an empty list of accessible apps and then get the updated one.
  • Add cwd as an optional field to thread/list (#11651)
    Add's the ability to filter app-server thread/list by cwd
  • [apps] Add is_enabled to app info. (#11417)
    - [x] Add is_enabled to app info and the response of `app/list`.
    - [x] Update TUI to have Enable/Disable button on the app detail page.
  • fix(app-server): surface more helpful errors for json-rpc (#11638)
    Propagate client JSON-RPC errors for app-server request callbacks.
    Previously a number of possible errors were collapsed to `channel
    closed`. Now we should be able to see the underlying client error.
    
    ### Summary
    This change stops masking client JSON-RPC error responses as generic
    callback cancellation in app-server server->client request flows.
    
    Previously, when the client responded with a JSON-RPC error, we removed
    the callback entry but did not send anything to the waiting oneshot
    receiver. Waiters then observed channel closure (for example, auth
    refresh request canceled: channel closed), which hid the actual client
    error.
    
    Now, client JSON-RPC errors are forwarded through the callback channel
    and handled explicitly by request consumers.
    
    ### User-visible behavior
    - External auth refresh now surfaces real client JSON-RPC errors when
    provided.
    - True transport/callback-drop cases still report
    canceled/channel-closed semantics.
    
    ### Example: client JSON-RPC error is now propagated (not masked as
    "canceled")
    
    When app-server asks the client to refresh ChatGPT auth tokens, it sends
    a server->client JSON-RPC request like:
    
    ```json
    {
      "id": 42,
      "method": "account/chatgptAuthTokens/refresh",
      "params": {
        "reason": "unauthorized",
        "previousAccountId": "org-abc"
      }
    }
    ```
    
    If the client cannot refresh and responds with a JSON-RPC error:
    ```
    {
      "id": 42,
      "error": {
        "code": -32000,
        "message": "refresh failed",
        "data": null
      }
    }
    ```
    
    app-server now forwards that error through the callback path and
    surfaces:
    `auth refresh request failed: code=-32000 message=refresh failed`
    
    Previously, this same case could be reported as:
    `auth refresh request canceled: channel closed`
  • app-server: stabilize detached review start on Windows (#11646)
    ## Why
    
    `review_start_with_detached_delivery_returns_new_thread_id` has been
    failing on Windows CI. The failure mode is a process crash
    (`tokio-runtime-worker` stack overflow) during detached review setup,
    which causes EOF in the test harness.
    
    This test is intended to validate detached review thread identity, not
    shell snapshot behavior. We also still want detached review to avoid
    unnecessary rollout-path rediscovery when the parent thread is already
    loaded.
    
    ## What Changed
    
    - Updated detached review startup in
    `codex-rs/app-server/src/codex_message_processor.rs`:
      - `start_detached_review` now receives the loaded parent thread.
      - It prefers `parent_thread.rollout_path()`.
    - It falls back to `find_thread_path_by_id_str(...)` only if the
    in-memory path is unavailable.
    - Hardened the review test fixture in
    `codex-rs/app-server/tests/suite/v2/review.rs` by setting
    `shell_snapshot = false` in test config, so this test no longer depends
    on unrelated Windows PowerShell snapshot initialization.
    
    ## Verification
    
    - `cargo test -p codex-app-server`
    - Verified
    `suite::v2::review::review_start_with_detached_delivery_returns_new_thread_id`
    passes locally.
    
    ## Notes
    
    - Related context: rollout-path lookup behavior changed in #10532.
  • feat: introduce Permissions (#11633)
    ## Why
    We currently carry multiple permission-related concepts directly on
    `Config` for shell/unified-exec behavior (`approval_policy`,
    `sandbox_policy`, `network`, `shell_environment_policy`,
    `windows_sandbox_mode`).
    
    Consolidating these into one in-memory struct makes permission handling
    easier to reason about and sets up the next step: supporting named
    permission profiles (`[permissions.PROFILE_NAME]`) without changing
    behavior now.
    
    This change is mostly mechanical: it updates existing callsites to go
    through `config.permissions`, but it does not yet refactor those
    callsites to take a single `Permissions` value in places where multiple
    permission fields are still threaded separately.
    
    This PR intentionally **does not** change the on-disk `config.toml`
    format yet and keeps compatibility with legacy config keys.
    
    ## What Changed
    - Introduced `Permissions` in `core/src/config/mod.rs`.
    - Added `Config::permissions` and moved effective runtime permission
    fields under it:
      - `approval_policy`
      - `sandbox_policy`
      - `network`
      - `shell_environment_policy`
      - `windows_sandbox_mode`
    - Updated config loading/building so these effective values are still
    derived from the same existing config inputs and constraints.
    - Updated Windows sandbox helpers/resolution to read/write via
    `permissions`.
    - Threaded the new field through all permission consumers across core
    runtime, app-server, CLI/exec, TUI, and sandbox summary code.
    - Updated affected tests to reference `config.permissions.*`.
    - Renamed the struct/field from
    `EffectivePermissions`/`effective_permissions` to
    `Permissions`/`permissions` and aligned variable naming accordingly.
    
    ## Verification
    - `just fix -p codex-core -p codex-tui -p codex-cli -p codex-app-server
    -p codex-exec -p codex-utils-sandbox-summary`
    - `cargo build -p codex-core -p codex-tui -p codex-cli -p
    codex-app-server -p codex-exec -p codex-utils-sandbox-summary`
  • feat(app-server): experimental flag to persist extended history (#11227)
    This PR adds an experimental `persist_extended_history` bool flag to
    app-server thread APIs so rollout logs can retain a richer set of
    EventMsgs for non-lossy Thread > Turn > ThreadItems reconstruction (i.e.
    on `thread/resume`).
    
    ### Motivation
    Today, our rollout recorder only persists a small subset (e.g. user
    message, reasoning, assistant message) of `EventMsg` types, dropping a
    good number (like command exec, file change, etc.) that are important
    for reconstructing full item history for `thread/resume`, `thread/read`,
    and `thread/fork`.
    
    Some clients want to be able to resume a thread without lossiness. This
    lossiness is primarily a UI thing, since what the model sees are
    `ResponseItem` and not `EventMsg`.
    
    ### Approach
    This change introduces an opt-in `persist_full_history` flag to preserve
    those events when you start/resume/fork a thread (defaults to `false`).
    
    This is done by adding an `EventPersistenceMode` to the rollout
    recorder:
    - `Limited` (existing behavior, default)
    - `Extended` (new opt-in behavior)
    
    In `Extended` mode, persist additional `EventMsg` variants needed for
    non-lossy app-server `ThreadItem` reconstruction. We now store the
    following ThreadItems that we didn't before:
    - web search
    - command execution
    - patch/file changes
    - MCP tool calls
    - image view calls
    - collab tool outcomes
    - context compaction
    - review mode enter/exit
    
    For **command executions** in particular, we truncate the output using
    the existing `truncate_text` from core to store an upper bound of 10,000
    bytes, which is also the default value for truncating tool outputs shown
    to the model. This keeps the size of the rollout file and command
    execution items returned over the wire reasonable.
    
    And we also persist `EventMsg::Error` which we can now map back to the
    Turn's status and populates the Turn's error metadata.
    
    #### Updates to EventMsgs
    To truly make `thread/resume` non-lossy, we also needed to persist the
    `status` on `EventMsg::CommandExecutionEndEvent` and
    `EventMsg::PatchApplyEndEvent`. Previously it was not obvious whether a
    command failed or was declined (similar for apply_patch). These
    EventMsgs were never persisted before so I made it a required field.
  • chore: drop mcp validation of dynamic tools (#11609)
    Drop validation of dynamic tools using MCP names to reduce latency
  • app-server: thread resume subscriptions (#11474)
    This stack layer makes app-server thread event delivery connection-aware
    so resumed/attached threads only emit notifications and approval prompts
    to subscribed connections.
    
    - Added per-thread subscription tracking in `ThreadState`
    (`subscribed_connections`) and mapped subscription ids to `(thread_id,
    connection_id)`.
    - Updated listener lifecycle so removing a subscription or closing a
    connection only removes that connection from the thread’s subscriber
    set; listener shutdown now happens when the last subscriber is gone.
    - Added `connection_closed(connection_id)` plumbing (`lib.rs` ->
    `message_processor.rs` -> `codex_message_processor.rs`) so disconnect
    cleanup happens immediately.
    - Scoped bespoke event handling outputs through `TargetedOutgoing` to
    send requests/notifications only to subscribed connections.
    - Kept existing threadresume behavior while aligning with the latest
    split-loop transport structure.
  • refactor: codex app-server ThreadState (#11419)
    this is a no-op functionality wise. consolidates thread-specific message
    processor / event handling state in ThreadState
  • Promote Windows Sandbox (#11341)
    1. Move Windows Sandbox NUX to right after trust directory screen
    2. Don't offer read-only as an option in Sandbox NUX.
    Elevated/Legacy/Quit
    3. Don't allow new untrusted directories. It's trust or quit
    4. move experimental sandbox features to `[windows]
    sandbox="elevated|unelevatd"`
    5. Copy tweaks = elevated -> default, non-elevated -> non-admin
  • Reapply "Add app-server transport layer with websocket support" (#11370)
    Reapply "Add app-server transport layer with websocket support" with
    additional fixes from https://github.com/openai/codex/pull/11313/changes
    to avoid deadlocking.
    
    This reverts commit 47356ff83c.
    
    ## Summary
    
    To avoid deadlocking when queues are full, we maintain separate tokio
    tasks dedicated to incoming vs outgoing event handling
    - split the app-server main loop into two tasks in
    `run_main_with_transport`
       - inbound handling (`transport_event_rx`)
       - outbound handling (`outgoing_rx` + `thread_created_rx`)
    - separate incoming and outgoing websocket tasks
    
    ## Validation
    
    Integration tests, testing thoroughly e2e in codex app w/ >10 concurrent
    requests
    
    <img width="1365" height="979" alt="Screenshot 2026-02-10 at 2 54 22 PM"
    src="https://github.com/user-attachments/assets/47ca2c13-f322-4e5c-bedd-25859cbdc45f"
    />
    
    ---------
    
    Co-authored-by: jif-oai <jif@openai.com>
  • Cache cloud requirements (#11305)
    We're loading these from the web on every startup. This puts them in a
    local file with a 1hr TTL.
    
    We sign the downloaded requirements with a key compiled into the Codex
    CLI to prevent unsophisticated tampering (determined circumvention is
    outside of our threat model: after all, one could just compile Codex
    without any of these checks).
    
    If any of the following are true, we ignore the local cache and re-fetch
    from Cloud:
    * The signature is invalid for the payload (== requirements, sign time,
    ttl, user identity)
    * The identity does not match the auth'd user's identity
    * The TTL has expired
    * We cannot parse requirements.toml from the payload
  • feat: split codex-common into smaller utils crates (#11422)
    We are removing feature-gated shared crates from the `codex-rs`
    workspace. `codex-common` grouped several unrelated utilities behind
    `[features]`, which made dependency boundaries harder to reason about
    and worked against the ongoing effort to eliminate feature flags from
    workspace crates.
    
    Splitting these utilities into dedicated crates under `utils/` aligns
    this area with existing workspace structure and keeps each dependency
    explicit at the crate boundary.
    
    ## What changed
    
    - Removed `codex-rs/common` (`codex-common`) from workspace members and
    workspace dependencies.
    - Added six new utility crates under `codex-rs/utils/`:
      - `codex-utils-cli`
      - `codex-utils-elapsed`
      - `codex-utils-sandbox-summary`
      - `codex-utils-approval-presets`
      - `codex-utils-oss`
      - `codex-utils-fuzzy-match`
    - Migrated the corresponding modules out of `codex-common` into these
    crates (with tests), and added matching `BUILD.bazel` targets.
    - Updated direct consumers to use the new crates instead of
    `codex-common`:
      - `codex-rs/cli`
      - `codex-rs/tui`
      - `codex-rs/exec`
      - `codex-rs/app-server`
      - `codex-rs/mcp-server`
      - `codex-rs/chatgpt`
      - `codex-rs/cloud-tasks`
    - Updated workspace lockfile entries to reflect the new dependency graph
    and removal of `codex-common`.
  • feat: support multiple rate limits (#11260)
    Added multi-limit support end-to-end by carrying limit_name in
    rate-limit snapshots and handling multiple buckets instead of only
    codex.
    Extended /usage client parsing to consume additional_rate_limits
    Updated TUI /status and in-memory state to store/render per-limit
    snapshots
    Extended app-server rate-limit read response: kept rate_limits and added
    rate_limits_by_name.
    Adjusted usage-limit error messaging for non-default codex limit buckets
  • chore: persist turn_id in rollout session and make turn_id uuid based (#11246)
    Problem:
    1. turn id is constructed in-memory;
    2. on resuming threads, turn_id might not be unique;
    3. client cannot no the boundary of a turn from rollout files easily.
    
    This PR does three things:
    1. persist `task_started` and `task_complete` events;
    1. persist `turn_id` in rollout turn events;
    5. generate turn_id as unique uuids instead of incrementing it in
    memory.
    
    This helps us resolve the issue of clients wanting to have unique turn
    ids for resuming a thread, and knowing the boundry of each turn in
    rollout files.
    
    example debug logs
    ```
    2026-02-11T00:32:10.746876Z DEBUG codex_app_server_protocol::protocol::thread_history: built turn from rollout items turn_index=8 turn=Turn { id: "019c4a07-d809-74c3-bc4b-fd9618487b4b", items: [UserMessage { id: "item-24", content: [Text { text: "hi", text_elements: [] }] }, AgentMessage { id: "item-25", text: "Hi. I’m in the workspace with your current changes loaded and ready. Send the next task and I’ll execute it end-to-end." }], status: Completed, error: None }
    2026-02-11T00:32:10.746888Z DEBUG codex_app_server_protocol::protocol::thread_history: built turn from rollout items turn_index=9 turn=Turn { id: "019c4a18-1004-76c0-a0fb-a77610f6a9b8", items: [UserMessage { id: "item-26", content: [Text { text: "hello", text_elements: [] }] }, AgentMessage { id: "item-27", text: "Hello. Ready for the next change in `codex-rs`; I can continue from the current in-progress diff or start a new task." }], status: Completed, error: None }
    2026-02-11T00:32:10.746899Z DEBUG codex_app_server_protocol::protocol::thread_history: built turn from rollout items turn_index=10 turn=Turn { id: "019c4a19-41f0-7db0-ad78-74f1503baeb8", items: [UserMessage { id: "item-28", content: [Text { text: "hello", text_elements: [] }] }, AgentMessage { id: "item-29", text: "Hello. Send the specific change you want in `codex-rs`, and I’ll implement it and run the required checks." }], status: Completed, error: None }
    ```
    
    backward compatibility:
    if you try to resume an old session without task_started and
    task_complete event populated, the following happens:
    - If you resume and do nothing: those reconstructed historical IDs can
    differ next time you resume.
    - If you resume and send a new turn: the new turn gets a fresh UUID from
    live submission flow and is persisted, so that new turn’s ID is stable
    on later resumes.
    I think this behavior is fine, because we only care about deterministic
    turn id once a turn is triggered.
  • feat: opt-out of events in the app-server (#11319)
    Add `optOutNotificationMethods` in the app-server to opt-out events
    based on exact method matching
  • Fix: update parallel tool call exec approval to approve on request id (#11162)
    ### Summary
    
    In parallel tool call, exec command approvals were not approved at
    request level but at a turn level. i.e. when a single request is
    approved, the system currently treats all requests in turn as approved.
    
    ### Before
    
    https://github.com/user-attachments/assets/d50ed129-b3d2-4b2f-97fa-8601eb11f6a8
    
    ### After
    
    https://github.com/user-attachments/assets/36528a43-a4aa-4775-9e12-f13287ef19fc
  • feat: retain NetworkProxy, when appropriate (#11207)
    As of this PR, `SessionServices` retains a
    `Option<StartedNetworkProxy>`, if appropriate.
    
    Now the `network` field on `Config` is `Option<NetworkProxySpec>`
    instead of `Option<NetworkProxy>`.
    
    Over in `Session::new()`, we invoke `NetworkProxySpec::start_proxy()` to
    create the `StartedNetworkProxy`, which is a new struct that retains the
    `NetworkProxy` as well as the `NetworkProxyHandle`. (Note that `Drop` is
    implemented for `NetworkProxyHandle` to ensure the proxies are shutdown
    when it is dropped.)
    
    The `NetworkProxy` from the `StartedNetworkProxy` is threaded through to
    the appropriate places.
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/11207).
    * #11285
    * __->__ #11207
  • [apps] Add thread_id param to optionally load thread config for apps feature check. (#11279)
    - [x] Add thread_id param to optionally load thread config for apps
    feature check
  • fix(app-server): for external auth, replace id_token with chatgpt_acc… (#11240)
    …ount_id and chatgpt_plan_type
    
    ### Summary
    Following up on external auth mode which was introduced here:
    https://github.com/openai/codex/pull/10012
    
    Turns out some clients have a differently shaped ID token and don't have
    a chosen workspace (aka chatgpt_account_id) encoded in their ID token.
    So, let's replace `id_token` param with `chatgpt_account_id` and
    `chatgpt_plan_type` (optional) when initializing the external ChatGPT
    auth mode (`account/login/start` with `chatgptAuthTokens`).
    
    The client was able to test end-to-end with a Codex build from this
    branch and verified it worked!
  • feat: do not close unified exec processes across turns (#10799)
    With this PR we do not close the unified exec processes (i.e. background
    terminals) at the end of a turn unless:
    * The user interrupt the turn
    * The user decide to clean the processes through `app-server` or
    `/clean`
    
    I made sure that `codex exec` correctly kill all the processes
  • feat: include NetworkConfig through ExecParams (#11105)
    This PR adds the following field to `Config`:
    
    ```rust
    pub network: Option<NetworkProxy>,
    ```
    
    Though for the moment, it will always be initialized as `None` (this
    will be addressed in a subsequent PR).
    
    This PR does the work to thread `network` through to `execute_exec_env()`, `process_exec_tool_call()`, and `UnifiedExecRuntime.run()` to ensure it is available whenever we span a process.
  • [apps] Improve app loading. (#10994)
    There are two concepts of apps that we load in the harness:
    
    - Directory apps, which is all the apps that the user can install.
    - Accessible apps, which is what the user actually installed and can be
    $ inserted and be used by the model. These are extracted from the tools
    that are loaded through the gateway MCP.
    
    Previously we wait for both sets of apps before returning the full apps
    list. Which causes many issues because accessible apps won't be
    available to the UI or the model if directory apps aren't loaded or
    failed to load.
    
    In this PR we are separating them so that accessible apps can be loaded
    separately and are instantly available to be shown in the UI and to be
    provided in model context. We also added an app-server event so that
    clients can subscribe to also get accessible apps without being blocked
    on the full app list.
    
    - [x] Separate accessible apps and directory apps loading.
    - [x] `app/list` request will also emit `app/list/updated` notifications
    that app-server clients can subscribe. Which allows clients to get
    accessible apps list to render in the $ menu without being blocked by
    directory apps.
    - [x] Cache both accessible and directory apps with 1 hour TTL to avoid
    reloading them when creating new threads.
    - [x] TUI improvements to redraw $ menu and /apps menu when app list is
    updated.
  • Defer persistence of rollout file (#11028)
    - Defer rollout persistence for fresh threads (`InitialHistory::New`):
    keep rollout events in memory and only materialize rollout file + state
    DB row on first `EventMsg::UserMessage`.
    - Keep precomputed rollout path available before materialization.
    - Change `thread/start` to build thread response from live config
    snapshot and optional precomputed path.
    - Improve pre-materialization behavior in app-server/TUI: clearer
    invalid-request errors for file-backed ops and a friendlier `/fork` “not
    ready yet” UX.
    - Update tests to match deferred semantics across
    start/read/archive/unarchive/fork/resume/review flows.
    - Improved resilience of user_shell test, which should be unrelated to
    this change but must be affected by timing changes
    
    For Reviewers:
    * The primary change is in recorder.rs
    * Most of the other changes were to fix up broken assumptions in
    existing tests
    
    Testing:
    * Manually tested CLI
    * Exercised app server paths by manually running IDE Extension with
    rebuilt CLI binary
    * Only user-visible change is that `/fork` in TUI generates visible
    error if used prior to first turn
  • app-server: treat null mode developer instructions as built-in defaults (#10983)
    ## Summary
    - make `turn/start` normalize
    `collaborationMode.settings.developer_instructions: null` to the
    built-in instructions for the selected mode
    - prevent app-server clients from accidentally clearing mode-switch
    developer instructions by sending `null`
    - document this behavior in the v2 protocol and app-server docs
    
    ## What changed
    - `codex-rs/app-server/src/codex_message_processor.rs`
      - added a small `normalize_turn_start_collaboration_mode` helper
      - in `turn_start`, apply normalization before `OverrideTurnContext`
    - `codex-rs/app-server/tests/suite/v2/turn_start.rs`
    - extended `turn_start_accepts_collaboration_mode_override_v2` to assert
    the outgoing request includes default-mode instruction text when the
    client sends `developer_instructions: null`
    - `codex-rs/app-server-protocol/src/protocol/v2.rs`
    - clarified `TurnStartParams.collaboration_mode` docs:
    `settings.developer_instructions: null` means use built-in mode
    instructions
    - regenerated schema fixture:
    - `codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts`
    - docs:
      - `codex-rs/app-server/README.md`
      - `codex-rs/docs/codex_mcp_interface.md`
  • feat(core): add network constraints schema to requirements.toml (#10958)
    ## Summary
    
    Add `requirements.toml` schema support for admin-defined network
    constraints in the requirements layer
    
    example config:
    
    ```
    [experimental_network]
    enabled = true
    allowed_domains = ["api.openai.com"]
    denied_domains = ["example.com"]
    ```
  • Add resume_agent collab tool (#10903)
    Summary
    - add the new resume_agent collab tool path through core, protocol, and
    the app server API, including the resume events
    - update the schema/TypeScript definitions plus docs so resume_agent
    appears in generated artifacts and README
    - note that resumed agents rehydrate rollout history without overwriting
    their base instructions
    
    Testing
    - Not run (not requested)
  • feat: add support for allowed_web_search_modes in requirements.toml (#10964)
    This PR makes it possible to disable live web search via an enterprise
    config even if the user is running in `--yolo` mode (though cached web
    search will still be available). To do this, create
    `/etc/codex/requirements.toml` as follows:
    
    ```toml
    # "live" is not allowed; "disabled" is allowed even though not listed explicitly.
    allowed_web_search_modes = ["cached"]
    ```
    
    Or set `requirements_toml_base64` MDM as explained on
    https://developers.openai.com/codex/security/#locations.
    
    ### Why
    - Enforce admin/MDM/`requirements.toml` constraints on web-search
    behavior, independent of user config and per-turn sandbox defaults.
    - Ensure per-turn config resolution and review-mode overrides never
    crash when constraints are present.
    
    ### What
    - Add `allowed_web_search_modes` to requirements parsing and surface it
    in app-server v2 `ConfigRequirements` (`allowedWebSearchModes`), with
    fixtures updated.
    - Define a requirements allowlist type (`WebSearchModeRequirement`) and
    normalize semantics:
      - `disabled` is always implicitly allowed (even if not listed).
      - An empty list is treated as `["disabled"]`.
    - Make `Config.web_search_mode` a `Constrained<WebSearchMode>` and apply
    requirements via `ConstrainedWithSource<WebSearchMode>`.
    - Update per-turn resolution (`resolve_web_search_mode_for_turn`) to:
    - Prefer `Live → Cached → Disabled` when
    `SandboxPolicy::DangerFullAccess` is active (subject to requirements),
    unless the user preference is explicitly `Disabled`.
    - Otherwise, honor the user’s preferred mode, falling back to an allowed
    mode when necessary.
    - Update TUI `/debug-config` and app-server mapping to display
    normalized `allowed_web_search_modes` (including implicit `disabled`).
    - Fix web-search integration tests to assert cached behavior under
    `SandboxPolicy::ReadOnly` (since `DangerFullAccess` legitimately prefers
    `live` when allowed).
  • Removed "exec_policy" feature flag (#10851)
    This is no longer needed because it's on by default
  • feat(app-server): turn/steer API (#10821)
    This PR adds a dedicated `turn/steer` API for appending user input to an
    in-flight turn.
    
    ## Motivation
    Currently, steering in the app is implemented by just calling
    `turn/start` while a turn is running. This has some really weird quirks:
    - Client gets back a new `turn.id`, even though streamed
    events/approvals remained tied to the original active turn ID.
    - All the various turn-level override params on `turn/start` do not
    apply to the "steer", and would only apply to the next real turn.
    - There can also be a race condition where the client thinks the turn is
    active but the server has already completed it, so there might be bugs
    if the client has baked in some client-specific behavior thinking it's a
    steer when in fact the server kicked off a new turn. This is
    particularly possible when running a client against a remote app-server.
    
    Having a dedicated `turn/steer` API eliminates all those quirks.
    
    `turn/steer` behavior:
    - Requires an active turn on threadId. Returns a JSON-RPC error if there
    is no active turn.
    - If expectedTurnId is provided, it must match the active turn (more
    useful when connecting to a remote app-server).
    - Does not emit `turn/started`.
    - Does not accept turn overrides (`cwd`, `model`, `sandbox`, etc.) or
    `outputSchema` to accurately reflect that these are not applied when
    steering.
  • Add stage field for experimental flags. (#10793)
    - [x] Add stage field for experimental flags.
  • Sync app-server requirements API with refreshed cloud loader (#10815)
    configRequirements/read now returns updated cloud requirements after
    login.
  • Add app-server transport layer with websocket support (#10693)
    - Adds --listen <URL> to codex app-server with two listen modes:
          - stdio:// (default, existing behavior)
          - ws://IP:PORT (new websocket transport)
      - Refactors message routing to be connection-aware:
    - Tracks per-connection session state (initialize/experimental
    capability)
          - Routes responses/errors to the originating connection
    - Broadcasts server notifications/requests to initialized connections
    - Updates initialization semantics to be per connection (not
    process-global), and updates app-server docs accordingly.
    - Adds websocket accept/read/write handling (JSON-RPC per text frame,
    ping/pong handling, connection lifecycle events).
    
    Testing
    
    - Unit tests for transport URL parsing and targeted response/error
    routing.
      - New websocket integration test validating:
          - per-connection initialization requirements
          - no cross-connection response leakage
          - same request IDs on different connections route independently.
  • [app-server] Add a method to list experimental features. (#10721)
    - [x] Add a method to list experimental features.
  • fix(auth): isolate chatgptAuthTokens concept to auth manager and app-server (#10423)
    So that the rest of the codebase (like TUI) don't need to be concerned
    whether ChatGPT auth was handled by Codex itself or passed in via
    app-server's external auth mode.
  • Leverage state DB metadata for thread summaries (#10621)
    Summary:
    - read conversation summaries and cwd info from the state DB when
    possible so we no longer rely on rollout files for metadata and avoid
    extra I/O
    - persist CLI version in thread metadata, surface it through summary
    builders, and add the necessary DB migration hooks
    - simplify thread listing by using enriched state DB data directly
    rather than reading rollout heads
    
    Testing:
    - Not run (not requested)
  • Reload cloud requirements after user login (#10725)
    Reload cloud requirements after user login so it could take effect
    immediately.
  • feat(app-server, core): allow text + image content items for dynamic tool outputs (#10567)
    Took over the work that @aaronl-openai started here:
    https://github.com/openai/codex/pull/10397
    
    Now that app-server clients are able to set up custom tools (called
    `dynamic_tools` in app-server), we should expose a way for clients to
    pass in not just text, but also image outputs. This is something the
    Responses API already supports for function call outputs, where you can
    pass in either a string or an array of content outputs (text, image,
    file):
    https://platform.openai.com/docs/api-reference/responses/create#responses_create-input-input_item_list-item-function_tool_call_output-output-array-input_image
    
    So let's just plumb it through in Codex (with the caveat that we only
    support text and image for now). This is implemented end-to-end across
    app-server v2 protocol types and core tool handling.
    
    ## Breaking API change
    NOTE: This introduces a breaking change with dynamic tools, but I think
    it's ok since this concept was only recently introduced
    (https://github.com/openai/codex/pull/9539) and it's better to get the
    API contract correct. I don't think there are any real consumers of this
    yet (not even the Codex App).
    
    Old shape:
    `{ "output": "dynamic-ok", "success": true }`
    
    New shape:
    ```
    {
        "contentItems": [
          { "type": "inputText", "text": "dynamic-ok" },
          { "type": "inputImage", "imageUrl": "data:image/png;base64,AAA" }
        ]
      "success": true
    }
    ```