Commit Graph

1298 Commits

  • guardian initial feedback / tweaks (#13897)
    ## Summary
    - remove the remaining model-visible guardian-specific `on-request`
    prompt additions so enabling the feature does not change the main
    approval-policy instructions
    - neutralize user-facing guardian wording to talk about automatic
    approval review / approval requests rather than a second reviewer or
    only sandbox escalations
    - tighten guardian retry-context handling so agent-authored
    `justification` stays in the structured action JSON and is not also
    injected as raw retry context
    - simplify guardian review plumbing in core by deleting dead
    prompt-append paths and trimming some request/transcript setup code
    
    ## Notable Changes
    - delete the dead `permissions/approval_policy/guardian.md` append path
    and stop threading `guardian_approval_enabled` through model-facing
    developer-instruction builders
    - rename the experimental feature copy to `Automatic approval review`
    and update the `/experimental` snapshot text accordingly
    - make approval-review status strings generic across shell, patch,
    network, and MCP review types
    - forward real sandbox/network retry reasons for shell and unified-exec
    guardian review, but do not pass agent-authored justification as raw
    retry context
    - simplify `guardian.rs` by removing the one-field request wrapper,
    deduping reasoning-effort selection, and cleaning up transcript entry
    collection
    
    ## Testing
    - `just fmt`
    - full validation left to CI
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat(tui) render request_permissions calls (#14004)
    ## Summary
    Adds support for tui rendering of request_permission calls
    
    <img width="724" height="245" alt="Screenshot 2026-03-08 at 9 04 07 PM"
    src="https://github.com/user-attachments/assets/e1997825-a496-4bfb-bbda-43d0006460a5"
    />
    
    
    ## Testing
    - [x] Added snapshot test
  • Add request permissions tool (#13092)
    Adds a built-in `request_permissions` tool and wires it through the
    Codex core, protocol, and app-server layers so a running turn can ask
    the client for additional permissions instead of relying on a static
    session policy.
    
    The new flow emits a `RequestPermissions` event from core, tracks the
    pending request by call ID, forwards it through app-server v2 as an
    `item/permissions/requestApproval` request, and resumes the tool call
    once the client returns an approved subset of the requested permission
    profile.
  • tui: clarify pending steer follow-ups (#13841)
    ## Summary
    - split the pending input preview into labeled pending-steer and queued
    follow-up sections
    - explain that pending steers submit after the next tool call and that
    Esc can interrupt and send them immediately
    - treat Esc as an interrupt-plus-resubmit path when pending steers
    exist, with updated TUI snapshots and tests
    
    Queues and steers:
    <img width="1038" height="263" alt="Screenshot 2026-03-07 at 10 17
    17 PM"
    src="https://github.com/user-attachments/assets/4ef433ef-27a3-4b7c-ad69-2046f6eb89e6"
    />
    
    After pressing Esc:
    <img width="1046" height="320" alt="Screenshot 2026-03-07 at 10 17
    21 PM"
    src="https://github.com/user-attachments/assets/0f4d89e0-b6b9-486a-9f04-b6021f169ba7"
    />
    
    ## Codex author
    `codex resume 019cc6f4-2cca-7803-b717-8264526dbd97`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • app-server: include experimental skill metadata in exec approval requests (#13929)
    ## Summary
    
    This change surfaces skill metadata on command approval requests so
    app-server clients can tell when an approval came from a skill script
    and identify the originating `SKILL.md`.
    
    - add `skill_metadata` to exec approval events in the shared protocol
    - thread skill metadata through core shell escalation and delegated
    approval handling for skill-triggered approvals
    - expose the field in app-server v2 as experimental `skillMetadata`
    - regenerate the JSON/TypeScript schemas and cover the new field in
    protocol, transport, core, and TUI tests
    
    ## Why
    
    Skill-triggered approvals already carry skill context inside core, but
    app-server clients could not see which skill caused the prompt. Sending
    the skill metadata with the approval request makes it possible for
    clients to present better approval UX and connect the prompt back to the
    relevant skill definition.
    
    
    ## example event in app-server-v2
    verified that we see this event when experimental api is on:
    ```
    < {
    <   "id": 11,
    <   "method": "item/commandExecution/requestApproval",
    <   "params": {
    <     "additionalPermissions": {
    <       "fileSystem": null,
    <       "macos": {
    <         "accessibility": false,
    <         "automations": {
    <           "bundle_ids": [
    <             "com.apple.Notes"
    <           ]
    <         },
    <         "calendar": false,
    <         "preferences": "read_only"
    <       },
    <       "network": null
    <     },
    <     "approvalId": "25d600ee-5a3c-4746-8d17-e2e61fb4c563",
    <     "availableDecisions": [
    <       "accept",
    <       "acceptForSession",
    <       "cancel"
    <     ],
    <     "command": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/scripts/notes_info",
    <     "commandActions": [
    <       {
    <         "command": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/scripts/notes_info",
    <         "type": "unknown"
    <       }
    <     ],
    <     "cwd": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes",
    <     "itemId": "call_jZp3xFpNg4D8iKAD49cvEvZy",
    <     "skillMetadata": {
    <       "pathToSkillsMd": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/SKILL.md"
    <     },
    <     "threadId": "019ccc10-b7d3-7ff2-84fe-3a75e7681e69",
    <     "turnId": "019ccc10-b848-76f1-81b3-4a1fa225493f"
    <   }
    < }`
    ```
    
    & verified that this is the event when experimental api is off:
    ```
    < {
    <   "id": 13,
    <   "method": "item/commandExecution/requestApproval",
    <   "params": {
    <     "approvalId": "5fbbf776-261b-4cf8-899b-c125b547f2c0",
    <     "availableDecisions": [
    <       "accept",
    <       "acceptForSession",
    <       "cancel"
    <     ],
    <     "command": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/scripts/notes_info",
    <     "commandActions": [
    <       {
    <         "command": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/scripts/notes_info",
    <         "type": "unknown"
    <       }
    <     ],
    <     "cwd": "/Users/celia/code/codex/codex-rs",
    <     "itemId": "call_OV2DHzTgYcbYtWaTTBWlocOt",
    <     "threadId": "019ccc16-2a2b-7be1-8500-e00d45b892d4",
    <     "turnId": "019ccc16-2a8e-7961-98ec-649600e7d06a"
    <   }
    < }
    ```
  • [elicitations] Support always allow option for mcp tool calls. (#13807)
    - [x] Support always allow option for mcp tool calls, writes to
    config.toml.
    - [x] Fix config hot-reload after starting a new thread for TUI.
  • chore: use @plugin instead of $plugin for plaintext mentions (#13921)
    change plaintext plugin-mentions from `$plugin` to `@plugin`, ensure TUI
    can correctly decode these from history.
    
    tested locally, added/updated tests.
  • Fix TUI context window display before first TokenCount (#13896)
    The TUI was showing the raw configured `model_context_window` until the
    first
    `TokenCount` event arrived, even though core had already emitted the
    effective
    runtime window on `TurnStarted`. This made the footer, status-line
    context
    window, and `/status` output briefly inconsistent for models/configs
    where the
    effective window differs from the configured value, such as the
    `gpt-5.4`
    1,000,000-token override reported in #13623.
    
    Update the TUI to cache `TurnStarted.model_context_window` immediately
    so
    pre-token-count displays use the runtime effective window, and add
    regression
    coverage for the startup path.
    
    ---------
    
    Co-authored-by: Charles Cunningham <ccunningham@openai.com>
    Co-authored-by: Codex <noreply@openai.com>
  • fix(ci) fix guardian ci (#13911)
    ## Summary
    #13910 was merged with some unused imports, let's fix this
    
    ## Testing
    - [x] Let's make sure CI is green
    
    ---------
    
    Co-authored-by: Charles Cunningham <ccunningham@openai.com>
    Co-authored-by: Codex <noreply@openai.com>
  • Add guardian approval MVP (#13692)
    ## Summary
    - add the guardian reviewer flow for `on-request` approvals in command,
    patch, sandbox-retry, and managed-network approval paths
    - keep guardian behind `features.guardian_approval` instead of exposing
    a public `approval_policy = guardian` mode
    - route ordinary `OnRequest` approvals to the guardian subagent when the
    feature is enabled, without changing the public approval-mode surface
    
    ## Public model
    - public approval modes stay unchanged
    - guardian is enabled via `features.guardian_approval`
    - when that feature is on, `approval_policy = on-request` keeps the same
    approval boundaries but sends those approval requests to the guardian
    reviewer instead of the user
    - `/experimental` only persists the feature flag; it does not rewrite
    `approval_policy`
    - CLI and app-server no longer expose a separate `guardian` approval
    mode in this PR
    
    ## Guardian reviewer
    - the reviewer runs as a normal subagent and reuses the existing
    subagent/thread machinery
    - it is locked to a read-only sandbox and `approval_policy = never`
    - it does not inherit user/project exec-policy rules
    - it prefers `gpt-5.4` when the current provider exposes it, otherwise
    falls back to the parent turn's active model
    - it fail-closes on timeout, startup failure, malformed output, or any
    other review error
    - it currently auto-approves only when `risk_score < 80`
    
    ## Review context and policy
    - guardian mirrors `OnRequest` approval semantics rather than
    introducing a separate approval policy
    - explicit `require_escalated` requests follow the same approval surface
    as `OnRequest`; the difference is only who reviews them
    - managed-network allowlist misses that enter the approval flow are also
    reviewed by guardian
    - the review prompt includes bounded recent transcript history plus
    recent tool call/result evidence
    - transcript entries and planned-action strings are truncated with
    explicit `<guardian_truncated ... />` markers so large payloads stay
    bounded
    - apply-patch reviews include the full patch content (without
    duplicating the structured `changes` payload)
    - the guardian request layout is snapshot-tested using the same
    model-visible Responses request formatter used elsewhere in core
    
    ## Guardian network behavior
    - the guardian subagent inherits the parent session's managed-network
    allowlist when one exists, so it can use the same approved network
    surface while reviewing
    - exact session-scoped network approvals are copied into the guardian
    session with protocol/port scope preserved
    - those copied approvals are now seeded before the guardian's first turn
    is submitted, so inherited approvals are available during any immediate
    review-time checks
    
    ## Out of scope / follow-ups
    - the sandbox-permission validation split was pulled into a separate PR
    and is not part of this diff
    - a future follow-up can enable `serde_json` preserve-order in
    `codex-core` and then simplify the guardian action rendering further
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add Fast mode status-line indicator (#13670)
    Addresses feature request #13660
    
    Adds new option to `/statusline` so the status line can display "fast
    on" or "fast off"
    
    Summary
    - introduce a `FastMode` status-line item so `/statusline` can render
    explicit `Fast on`/`Fast off` text for the service tier
    - wire the item into the picker metadata and resolve its string from
    `ChatWidget` without adding any unrelated `thread-name` logic or storage
    changes
    - ensure the refresh paths keep the cached footer in sync when the
    service tier (fast mode) changes
    
    Testing
    - Manually tested
    
    Here's what it looks like when enabled:
    
    <img width="366" height="75" alt="image"
    src="https://github.com/user-attachments/assets/7f992d2b-6dab-49ed-aa43-ad496f56f193"
    />
  • fix: support managed network allowlist controls (#12752)
    ## Summary
    - treat `requirements.toml` `allowed_domains` and `denied_domains` as
    managed network baselines for the proxy
    - in restricted modes by default, build the effective runtime policy
    from the managed baseline plus user-configured allowlist and denylist
    entries, so common hosts can be pre-approved without blocking later user
    expansion
    - add `experimental_network.managed_allowed_domains_only = true` to pin
    the effective allowlist to managed entries, ignore user allowlist
    additions, and hard-deny non-managed domains without prompting
    - apply `managed_allowed_domains_only` anywhere managed network
    enforcement is active, including full access, while continuing to
    respect denied domains from all sources
    - add regression coverage for merged-baseline behavior, managed-only
    behavior, and full-access managed-only enforcement
    
    ## Behavior
    Assuming `requirements.toml` defines both
    `experimental_network.allowed_domains` and
    `experimental_network.denied_domains`.
    
    ### Default mode
    - By default, the effective allowlist is
    `experimental_network.allowed_domains` plus user or persisted allowlist
    additions.
    - By default, the effective denylist is
    `experimental_network.denied_domains` plus user or persisted denylist
    additions.
    - Allowlist misses can go through the network approval flow.
    - Explicit denylist hits and local or private-network blocks are still
    hard-denied.
    - When `experimental_network.managed_allowed_domains_only = true`, only
    managed `allowed_domains` are respected, user allowlist additions are
    ignored, and non-managed domains are hard-denied without prompting.
    - Denied domains continue to be respected from all sources.
    
    ### Full access
    - With managed requirements present, the effective allowlist is pinned
    to `experimental_network.allowed_domains`.
    - With managed requirements present, the effective denylist is pinned to
    `experimental_network.denied_domains`.
    - There is no allowlist-miss approval path in full access.
    - Explicit denylist hits are hard-denied.
    - `experimental_network.managed_allowed_domains_only = true` now also
    applies in full access, so managed-only behavior remains in effect
    anywhere managed network enforcement is active.
  • chore(otel): rename OtelManager to SessionTelemetry (#13808)
    ## Summary
    This is a purely mechanical refactor of `OtelManager` ->
    `SessionTelemetry` to better convey what the struct is doing. No
    behavior change.
    
    ## Why
    
    `OtelManager` ended up sounding much broader than what this type
    actually does. It doesn't manage OTEL globally; it's the session-scoped
    telemetry surface for emitting log/trace events and recording metrics
    with consistent session metadata (`app_version`, `model`, `slug`,
    `originator`, etc.).
    
    `SessionTelemetry` is a more accurate name, and updating the call sites
    makes that boundary a lot easier to follow.
    
    ## Validation
    
    - `just fmt`
    - `cargo test -p codex-otel`
    - `cargo test -p codex-core`
  • utils/pty: add streaming spawn and terminal sizing primitives (#13695)
    Enhance pty utils:
    * Support closing stdin
    * Separate stderr and stdout streams to allow consumers differentiate them
    * Provide compatibility helper to merge both streams back into combined one
    * Support specifying terminal size for pty, including on-demand resizes while process is already running
    * Support terminating the process while still consuming its outputs
  • feat: structured plugin parsing (#13711)
    #### What
    
    Add structured `@plugin` parsing and TUI support for plugin mentions.
    
    - Core: switch from plain-text `@display_name` parsing to structured
    `plugin://...` mentions via `UserInput::Mention` and
    `[$...](plugin://...)` links in text, same pattern as apps/skills.
    - TUI: add plugin mention popup, autocomplete, and chips when typing
    `$`. Load plugin capability summaries and feed them into the composer;
    plugin mentions appear alongside skills and apps.
    - Generalize mention parsing to a sigil parameter, still defaults to `$`
    
    <img width="797" height="119" alt="image"
    src="https://github.com/user-attachments/assets/f0fe2658-d908-4927-9139-73f850805ceb"
    />
    
    Builds on #13510. Currently clients have to build their own `id` via
    `plugin@marketplace` and filter plugins to show by `enabled`, but we
    will add `id` and `available` as fields returned from `plugin/list`
    soon.
    
    ####Tests
    
    Added tests, verified locally.
  • feat(otel, core): record turn TTFT and TTFM metrics in codex-core (#13630)
    ### Summary
    This adds turn-level latency metrics for the first model output and the
    first completed agent message.
    - `codex.turn.ttft.duration_ms` starts at turn start and records on the
    first output signal we see from the model. That includes normal
    assistant text, reasoning deltas, and non-text outputs like tool-call
    items.
    - `codex.turn.ttfm.duration_ms` also starts at turn start, but it
    records when the first agent message finishes streaming rather than when
    its first delta arrives.
    
    ### Implementation notes
    The timing is tracked in codex-core, not app-server, so the definition
    stays consistent across CLI, TUI, and app-server clients.
    
    I reused the existing turn lifecycle boundary that already drives
    `codex.turn.e2e_duration_ms`, stored the turn start timestamp in turn
    state, and record each metric once per turn.
    
    I also wired the new metric names into the OTEL runtime metrics summary
    so they show up in the same in-memory/debug snapshot path as the
    existing timing metrics.
  • [elicitations] Switch to use MCP style elicitation payload for mcp tool approvals. (#13621)
    - [x] Switch to use MCP style elicitation payload for mcp tool
    approvals.
    - [ ] TODO: Update the UI to support the full spec.
  • Enabling CWD Saving for Image-Gen (#13607)
    Codex now saves the generated image on to your current working
    directory.
  • fix(tui) remove config check for trusted setting (#11874)
    ## Summary
    Simplify the trusted directory flow. This logic was originally designed
    several months ago, to determine if codex should start in read-only or
    workspace-write mode. However, that's no longer the purpose of directory
    trust - and therefore we should get rid of this logic.
    
    ## Testing
    - [x] Unit tests pass
  • refactor: remove proxy admin endpoint (#13687)
    ## Summary
    - delete the network proxy admin server and its runtime listener/task
    plumbing
    - remove the admin endpoint config, runtime, requirement, protocol,
    schema, and debug-surface fields
    - update proxy docs to reflect the remaining HTTP and SOCKS listeners
    only
  • tui: sort resume picker by last updated time (#13654)
    ## Summary
    - default the resume picker sort key to UpdatedAt instead of CreatedAt
    - keep Tab sort toggling behavior and update the test expectation for
    the new default
    
    ## Testing
    - just fmt
    - cargo test -p codex-tui
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix(tui): clean up pending steer preview wrapping (#13642)
    ## Summary
    - render pending steer previews with a single `pending steer:` prefix
    instead of repeating it for each source line
    - reuse the same truncation path for pending steers and queued drafts so
    multiline previews behave consistently
    - add snapshot coverage for the multiline pending steer case
    
    Before
    <img width="969" height="219" alt="Screenshot 2026-03-05 at 3 55 11 PM"
    src="https://github.com/user-attachments/assets/b062c9c8-43d3-4a52-98e0-3c7643d1697b"
    />
    
    After
    <img width="965" height="203" alt="Screenshot 2026-03-05 at 3 56 08 PM"
    src="https://github.com/user-attachments/assets/40935863-55b3-444f-9e14-1ac63126b2e1"
    />
    
    ## Codex author
    `codex resume 019cc054-385e-79a3-bb85-ec9499623bd8`
    
    Co-authored-by: Codex <noreply@openai.com>
  • Update models.json (#13617)
    - Update `models.json` to surface the new model entry.
    - Refresh the TUI model picker snapshot to match the updated catalog
    ordering.
    
    ---------
    
    Co-authored-by: aibrahim-oai <219906144+aibrahim-oai@users.noreply.github.com>
  • core/protocol: add structured macOS additional permissions and merge them into sandbox execution (#13499)
    ## Summary
    - Introduce strongly-typed macOS additional permissions across
    protocol/core/app-server boundaries.
    - Merge additional permissions into effective sandbox execution,
    including macOS seatbelt profile extensions.
    - Expand docs, schema/tool definitions, UI rendering, and tests for
    `network`, `file_system`, and `macos` additional permissions.
  • add @plugin mentions (#13510)
    ## Note-- added plugin mentions via @, but that conflicts with file
    mentions
    
    depends and builds upon #13433.
    
    - introduces explicit `@plugin` mentions. this injects the plugin's mcp
    servers, app names, and skill name format into turn context as a dev
    message.
    - we do not yet have UI for these mentions, so we currently parse raw
    text (as opposed to skills and apps which have UI chips, autocomplete,
    etc.) this depends on a `plugins/list` app-server endpoint we can feed
    the UI with, which is upcoming
    - also annotate mcp and app tool descriptions with the plugin(s) they
    come from. this gives the model a first class way of understanding what
    tools come from which plugins, which will help implicit invocation.
    
    ### Tests
    Added and updated tests, unit and integration. Also confirmed locally a
    raw `@plugin` injects the dev message, and the model knows about its
    apps, mcps, and skills.
  • feat(core): persist trace_id for turns in RolloutItem::TurnContext (#13602)
    This PR adds a durable trace linkage for each turn by storing the active
    trace ID on the rollout TurnContext record stored in session rollout
    files.
    
    Before this change, we propagated trace context at runtime but didn’t
    persist a stable per-turn trace key in rollout history. That made
    after-the-fact debugging harder (for example, mapping a historical turn
    to the corresponding trace in datadog). This sets us up for much easier
    debugging in the future.
    
    ### What changed
    - Added an optional `trace_id` to TurnContextItem (rollout schema).
    - Added a small OTEL helper to read the current span trace ID.
    - Captured `trace_id` when creating `TurnContext` and included it in
    `to_turn_context_item()`.
    - Updated tests and fixtures that construct TurnContextItem so
    older/no-trace cases still work.
    
    ### Why this approach
    TurnContext is already the canonical durable per-turn metadata in
    rollout. This keeps ownership clean: trace linkage lives with other
    persisted turn metadata.
  • [diagnostics] show diagnostics earlier in workflow (#13604)
    <img width="591" height="243" alt="Screenshot 2026-03-05 at 10 17 06 AM"
    src="https://github.com/user-attachments/assets/84a6658b-6017-4602-b1f8-2098b9b5eff9"
    />
    
    - show feedback earlier
    - preserve raw literal env vars (no trimming, sanitizing, etc.)
  • feat(app-server): support mcp elicitations in v2 api (#13425)
    This adds a first-class server request for MCP server elicitations:
    `mcpServer/elicitation/request`.
    
    Until now, MCP elicitation requests only showed up as a raw
    `codex/event/elicitation_request` event from core. That made it hard for
    v2 clients to handle elicitations using the same request/response flow
    as other server-driven interactions (like shell and `apply_patch`
    tools).
    
    This also updates the underlying MCP elicitation request handling in
    core to pass through the full MCP request (including URL and form data)
    so we can expose it properly in app-server.
    
    ### Why not `item/mcpToolCall/elicitationRequest`?
    This is because MCP elicitations are related to MCP servers first, and
    only optionally to a specific MCP tool call.
    
    In the MCP protocol, elicitation is a server-to-client capability: the
    server sends `elicitation/create`, and the client replies with an
    elicitation result. RMCP models it that way as well.
    
    In practice an elicitation is often triggered by an MCP tool call, but
    not always.
    
    ### What changed
    - add `mcpServer/elicitation/request` to the v2 app-server API
    - translate core `codex/event/elicitation_request` events into the new
    v2 server request
    - map client responses back into `Op::ResolveElicitation` so the MCP
    server can continue
    - update app-server docs and generated protocol schema
    - add an end-to-end app-server test that covers the full round trip
    through a real RMCP elicitation flow
    - The new test exercises a realistic case where an MCP tool call
    triggers an elicitation, the app-server emits
    mcpServer/elicitation/request, the client accepts it, and the tool call
    resumes and completes successfully.
    
    ### app-server API flow
    - Client starts a thread with `thread/start`.
    - Client starts a turn with `turn/start`.
    - App-server sends `item/started` for the `mcpToolCall`.
    - While that tool call is in progress, app-server sends
    `mcpServer/elicitation/request`.
    - Client responds to that request with `{ action: "accept" | "decline" |
    "cancel" }`.
    - App-server sends `serverRequest/resolved`.
    - App-server sends `item/completed` for the mcpToolCall.
    - App-server sends `turn/completed`.
    - If the turn is interrupted while the elicitation is pending,
    app-server still sends `serverRequest/resolved` before the turn
    finishes.
  • [tui] Show speed in session header (#13446)
    - add a speed row to the startup/session header under the model row
    - render the speed row with the same styling pattern as the model row,
    using /fast to change
    - show only Fast or Standard to users and update the affected snapshots
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [apps] Fix the issue where apps is not enabled after codex resume. (#13533)
    - [x] Fix the issue where apps is not enabled after codex resume.
  • [tui] Update fast mode plan usage copy (#13515)
    ## Summary
    - update the /fast slash command description from 3X to 2X plan usage
    
    ## Testing
    - not run (copy-only change)
  • [tui] rotate paid promo tips to include fast mode (#13438)
    - rotate the paid-plan startup promo slot 50/50 between the existing
    Codex App promo and a new Fast mode promo
    - keep the Fast mode call to action platform-neutral so Windows can show
    the same tip
    - add a focused unit test to ensure the paid promo pool actually rotates
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • image-gen-event/client_processing (#13512)
    enabling client-side to process with image-generation capabilities
    (setting app-server)
  • Notify TUI about plan mode prompts and user input requests (#13495)
    Addresses #13478
    
    Summary
    - Add two new scopes for `tui.notifications` config: `plan-mode-prompt`
    and `user-input-requested`.
    - Add Plan Mode prompt and user-input-requested notifications to the TUI
    so these events surface consistently outside of plan mode
    - Add helpers and tests to ensure the new notification types publish the
    right titles, summaries, and type tags for filtering
    - Add prioritization mechanism to fix an existing bug where one
    notification event could arbitrarily overwrite others
    
    Testing
    - Manually tested plan mode to ensure that notification appeared
  • fix(tui): decode ANSI alpha-channel encoding in syntax themes (#13382)
    ## Problem
    
    The `ansi`, `base16`, and `base16-256` syntax themes are designed to
    emit ANSI palette colors so that highlighted code respects the user's
    terminal color scheme. Syntect encodes this intent in the alpha channel
    of its `Color` struct — a convention shared with `bat` — but
    `convert_style` was ignoring it entirely, treating every foreground
    color as raw RGB. This caused ANSI-family themes to produce hard-coded
    RGB values (e.g. `Rgb(0x02, 0, 0)` instead of `Green`), defeating their
    purpose and rendering them as near-invisible dark colors on most
    terminals.
    
    Reported in #12890.
    
    ## Mental model
    
    Syntect themes use a compact encoding in their `Color` struct:
    
    | `alpha` | Meaning of `r` | Mapped to |
    |---------|----------------|-----------|
    | `0x00` | ANSI palette index (0–255) | `RtColor::Black`…`Gray` for 0–7,
    `Indexed(n)` for 8–255 |
    | `0x01` | Unused (sentinel) | `None` — inherit terminal default fg/bg |
    | `0xFF` | True RGB red channel | `RtColor::Rgb(r, g, b)` |
    | other | Unexpected | `RtColor::Rgb(r, g, b)` (silent fallback) |
    
    This encoding is a bat convention that three bundled themes rely on. The
    new `convert_syntect_color` function decodes it; `ansi_palette_color`
    maps indices 0–7 to ratatui's named ANSI variants.
    
    | macOS - Dark | macOS - Light | Windows - ansi | Windows - base16 |
    |---|---|---|---|
    | <img width="1064" height="1205" alt="macos-dark"
    src="https://github.com/user-attachments/assets/f03d92fb-b44b-4939-b2b9-503fde133811"
    /> | <img width="1073" height="1227" alt="macos-light"
    src="https://github.com/user-attachments/assets/2ecb2089-73b5-4676-bed8-e4e6794250b4"
    /> |
    ![windows-ansi](https://github.com/user-attachments/assets/d41029e6-ffd3-454e-ab72-6751607e5d5c)
    |
    ![windows-base16](https://github.com/user-attachments/assets/b48aafcc-0196-4977-8ee1-8f8eaddd1698)
    |
    
    ## Non-goals
    
    - Background color decoding — we intentionally skip backgrounds to
    preserve the terminal's own background. The decoder supports it, but
    `convert_style` does not apply it.
    - Italic/underline changes — those remain suppressed as before.
    - Custom `.tmTheme` support for ANSI encoding — only the bundled themes
    use this convention.
    
    ## Tradeoffs
    
    - The alpha-channel encoding is an undocumented bat/syntect convention,
    not a formal spec. We match bat's behavior exactly, trading formality
    for ecosystem compatibility.
    - Indices 0–7 are mapped to ratatui's named variants (`Black`, `Red`, …,
    `Gray`) rather than `Indexed(0)`…`Indexed(7)`. This lets terminals apply
    bold/bright semantics to named colors, which is the expected behavior
    for ANSI themes, but means the two representations are not perfectly
    round-trippable.
    
    ## Architecture
    
    All changes are in `codex-rs/tui/src/render/highlight.rs`, within the
    style-conversion layer between syntect and ratatui:
    
    ```
    syntect::highlighting::Color
      └─ convert_syntect_color(color)  [NEW — alpha-dispatch]
           ├─ a=0x00 → ansi_palette_color()  [NEW — index→named/indexed]
           ├─ a=0x01 → None (terminal default)
           ├─ a=0xFF → Rgb(r,g,b) (standard opaque path)
           └─ other  → Rgb(r,g,b) (silent fallback)
    ```
    
    `convert_style` delegates foreground mapping to `convert_syntect_color`
    instead of inlining the `Rgb(r,g,b)` conversion. The core highlighter is
    refactored into `highlight_to_line_spans_with_theme` (accepts an
    explicit theme reference) so tests can highlight against specific themes
    without mutating process-global state.
    
    ### ANSI-family theme contract
    
    The ANSI-family themes (`ansi`, `base16`, `base16-256`) rely on upstream
    alpha-channel encoding from two_face/syntect. We intentionally do
    **not** validate this contract at runtime — if the upstream format
    changes, the `ansi_themes_use_only_ansi_palette_colors` test catches it
    at build time, long before it reaches users. A runtime warning would be
    unactionable noise.
    
    ### Warning copy cleanup
    
    User-facing warning messages were rewritten for clarity:
    - Removed internal jargon ("alpha-encoded ANSI color markers", "RGB
    fallback semantics", "persisted override config")
    - Dropped "syntax" prefix from "syntax theme" — users just think "theme"
    - Downgraded developer-only diagnostics (duplicate override, resolve
    fallback) from `warn` to `debug`
    
    ## Observability
    
    - The `ansi_themes_use_only_ansi_palette_colors` test enforces the
    ANSI-family contract at build time.
    - The snapshot test provides a regression tripwire for palette color
    output.
    - User-facing warnings are limited to actionable issues: unknown theme
    names and invalid custom `.tmTheme` files.
    
    ## Tests
    
    - **Unit tests for each alpha branch:** `alpha=0x00` with low index
    (named color), `alpha=0x00` with high index (`Indexed`), `alpha=0x01`
    (terminal default), unexpected alpha (falls back to RGB), ANSI white →
    Gray mapping.
    - **Integration test:**
    `ansi_family_themes_use_terminal_palette_colors_not_rgb` — highlights a
    Rust snippet with each ANSI-family theme and asserts zero `Rgb`
    foreground colors appear.
    - **Snapshot test:** `ansi_family_foreground_palette_snapshot` — records
    the exact set of unique foreground colors each ANSI-family theme
    produces, guarding against regressions.
    - **Warning validation tests:** verify user-facing warnings for missing
    custom themes, invalid `.tmTheme` files, and bundled theme resolution.
    
    ## Test plan
    
    - [ ] `cargo test -p codex-tui` passes all new and existing tests
    - [ ] Select `ansi`, `base16`, or `base16-256` theme and verify code
    blocks render with terminal palette colors (not near-black RGB)
    - [ ] Select a standard RGB theme (e.g. `dracula`) and verify no
    regression in color output
  • [tui] Update Fast slash command description (#13458)
    ## Summary
    - update the /fast slash command description to mention fastest
    inference
    - mention the 3X plan usage tradeoff in the help copy
    
    ## Testing
    - cargo test -p codex-tui slash_command (currently blocked by an
    unrelated latest-main codex-tui compile error in chatwidget.rs:
    refresh_queued_user_messages missing)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • chore: Nest skill and protocol network permissions under network.enabled (#13427)
    ## Summary
    
    Changes the permission profile shape from a bare network boolean to a
    nested object.
    
    Before:
    
    ```yaml
    permissions:
      network: true
    ```
    
    After:
    
    ```yaml
    permissions:
      network:
        enabled: true
    ```
    
    This also updates the shared Rust and app-server protocol types so
    `PermissionProfile.network` is no longer `Option<bool>`, but
    `Option<NetworkPermissions>` with `enabled: Option<bool>`.
    
    ## What Changed
    
    - Updated `PermissionProfile` in `codex-rs/protocol/src/models.rs`:
    - `pub network: Option<bool>` -> `pub network:
    Option<NetworkPermissions>`
    - Added `NetworkPermissions` with:
      - `pub enabled: Option<bool>`
    - Changed emptiness semantics so `network` is only considered empty when
    `enabled` is `None`
    - Updated skill metadata parsing to accept `permissions.network.enabled`
    - Updated core permission consumers to read
    `network.enabled.unwrap_or(false)` where a concrete boolean is needed
    - Updated app-server v2 protocol types and regenerated schema/TypeScript
    outputs
    - Updated docs to mention `additionalPermissions.network.enabled`
  • config: enforce enterprise feature requirements (#13388)
    ## Why
    
    Enterprises can already constrain approvals, sandboxing, and web search
    through `requirements.toml` and MDM, but feature flags were still only
    configurable as managed defaults. That meant an enterprise could suggest
    feature values, but it could not actually pin them.
    
    This change closes that gap and makes enterprise feature requirements
    behave like the other constrained settings. The effective feature set
    now stays consistent with enterprise requirements during config load,
    when config writes are validated, and when runtime code mutates feature
    flags later in the session.
    
    It also tightens the runtime API for managed features. `ManagedFeatures`
    now follows the same constraint-oriented shape as `Constrained<T>`
    instead of exposing panic-prone mutation helpers, and production code
    can no longer construct it through an unconstrained `From<Features>`
    path.
    
    The PR also hardens the `compact_resume_fork` integration coverage on
    Windows. After the feature-management changes,
    `compact_resume_after_second_compaction_preserves_history` was
    overflowing the libtest/Tokio thread stacks on Windows, so the test now
    uses an explicit larger-stack harness as a pragmatic mitigation. That
    may not be the ideal root-cause fix, and it merits a parallel
    investigation into whether part of the async future chain should be
    boxed to reduce stack pressure instead.
    
    ## What Changed
    
    Enterprises can now pin feature values in `requirements.toml` with the
    requirements-side `features` table:
    
    ```toml
    [features]
    personality = true
    unified_exec = false
    ```
    
    Only canonical feature keys are allowed in the requirements `features`
    table; omitted keys remain unconstrained.
    
    - Added a requirements-side pinned feature map to
    `ConfigRequirementsToml`, threaded it through source-preserving
    requirements merge and normalization in `codex-config`, and made the
    TOML surface use `[features]` (while still accepting legacy
    `[feature_requirements]` for compatibility).
    - Exposed `featureRequirements` from `configRequirements/read`,
    regenerated the JSON/TypeScript schema artifacts, and updated the
    app-server README.
    - Wrapped the effective feature set in `ManagedFeatures`, backed by
    `ConstrainedWithSource<Features>`, and changed its API to mirror
    `Constrained<T>`: `can_set(...)`, `set(...) -> ConstraintResult<()>`,
    and result-returning `enable` / `disable` / `set_enabled` helpers.
    - Removed the legacy-usage and bulk-map passthroughs from
    `ManagedFeatures`; callers that need those behaviors now mutate a plain
    `Features` value and reapply it through `set(...)`, so the constrained
    wrapper remains the enforcement boundary.
    - Removed the production loophole for constructing unconstrained
    `ManagedFeatures`. Non-test code now creates it through the configured
    feature-loading path, and `impl From<Features> for ManagedFeatures` is
    restricted to `#[cfg(test)]`.
    - Rejected legacy feature aliases in enterprise feature requirements,
    and return a load error when a pinned combination cannot survive
    dependency normalization.
    - Validated config writes against enterprise feature requirements before
    persisting changes, including explicit conflicting writes and
    profile-specific feature states that normalize into invalid
    combinations.
    - Updated runtime and TUI feature-toggle paths to use the constrained
    setter API and to persist or apply the effective post-constraint value
    rather than the requested value.
    - Updated the `core_test_support` Bazel target to include the bundled
    core model-catalog fixtures in its runtime data, so helper code that
    resolves `core/models.json` through runfiles works in remote Bazel test
    environments.
    - Renamed the core config test coverage to emphasize that effective
    feature values are normalized at runtime, while conflicting persisted
    config writes are rejected.
    - Ran `compact_resume_after_second_compaction_preserves_history` inside
    an explicit 8 MiB test thread and Tokio runtime worker stack, following
    the existing larger-stack integration-test pattern, to keep the Windows
    `compact_resume_fork` test slice from aborting while a parallel
    investigation continues into whether some of the underlying async
    futures should be boxed.
    
    ## Verification
    
    - `cargo test -p codex-config`
    - `cargo test -p codex-core feature_requirements_ -- --nocapture`
    - `cargo test -p codex-core
    load_requirements_toml_produces_expected_constraints -- --nocapture`
    - `cargo test -p codex-core
    compact_resume_after_second_compaction_preserves_history -- --nocapture`
    - `cargo test -p codex-core compact_resume_fork -- --nocapture`
    - Re-ran the built `codex-core` `tests/all` binary with
    `RUST_MIN_STACK=262144` for
    `compact_resume_after_second_compaction_preserves_history` to confirm
    the explicit-stack harness fixes the deterministic low-stack repro.
    - `cargo test -p codex-core`
    - This still fails locally in unrelated integration areas that expect
    the `codex` / `test_stdio_server` binaries or hit existing `search_tool`
    wiremock mismatches.
    
    ## Docs
    
    `developers.openai.com/codex` should document the requirements-side
    `[features]` table for enterprise and MDM-managed configuration,
    including that it only accepts canonical feature keys and that
    conflicting config writes are rejected.
  • Feat: Preserve network access on read-only sandbox policies (#13409)
    ## Summary
    
    `PermissionProfile.network` could not be preserved when additional or
    compiled permissions resolved to
    `SandboxPolicy::ReadOnly`, because `ReadOnly` had no network_access
    field. This change makes read-only + network
    enabled representable directly and threads that through the protocol,
    app-server v2 mirror, and permission-
      merging logic.
    
    ## What changed
    
    - Added `network_access: bool` to `SandboxPolicy::ReadOnly` in the core
    protocol and app-server v2 protocol.
    - Kept backward compatibility by defaulting the new field to false, so
    legacy read-only payloads still
        deserialize unchanged.
    - Updated `has_full_network_access()` and sandbox summaries to respect
    read-only network access.
      - Preserved PermissionProfile.network when:
          - compiling skill permission profiles into sandbox policies
          - normalizing additional permissions
          - merging additional permissions into existing sandbox policies
    - Updated the approval overlay to show network in the rendered
    permission rule when requested.
      - Regenerated app-server schema fixtures for the new v2 wire shape.
  • [feedback] diagnostics (#13292)
    - added header logic to display diagnostics on cli
    - added logic for collecting env vars
    
    <img width="606" height="327" alt="Screenshot 2026-03-03 at 3 49 31 PM"
    src="https://github.com/user-attachments/assets/05e78c56-8cb3-47fa-abaf-3e57f1fdd8e2"
    />
    
    <img width="690" height="353" alt="Screenshot 2026-03-02 at 6 47 54 PM"
    src="https://github.com/user-attachments/assets/e470b559-13f4-44d9-897f-bc398943c6d1"
    />
  • tui: align pending steers with core acceptance (#12868)
    ## Summary
    - submit `Enter` steers immediately while a turn is already running
    instead of routing them through `queued_user_messages`
    - keep those submitted steers visible in the footer as `pending_steers`
    until core records them as a user message or aborts the turn
    - reconcile pending steers on `ItemCompleted(UserMessage)`, not
    `RawResponseItem`
    - emit user-message item lifecycle for leftover pending input at task
    finish, then remove the TUI `TurnComplete` fallback
    - keep `queued_user_messages` for actual queued drafts, rendered below
    pending steers
    
    ## Problem
    While the assistant was generating, pressing `Enter` could send the
    input into `queued_user_messages`. That queue only drains after the turn
    ends, so ordinary steers behaved like queued drafts instead of landing
    at the next core sampling boundary.
    
    The first version of this fix also used `RawResponseItem` to decide when
    a steer had landed. Review feedback was that this is the wrong
    abstraction for client behavior.
    
    There was also a late edge case in core: if pending steer input was
    accepted after the final sampling decision but before `TurnComplete`,
    core would record that user message into history at task finish without
    emitting `ItemStarted(UserMessage)` / `ItemCompleted(UserMessage)`. TUI
    had a fallback to paper over that gap locally.
    
    ## Approach
    - `Enter` during an active turn now submits a normal `Op::UserTurn`
    immediately
    - TUI keeps a local pending-steer preview instead of rendering that user
    message into history immediately
    - when core records the steer as `ItemCompleted(UserMessage)`, TUI
    matches and removes the corresponding pending preview, then renders the
    committed user message
    - core now emits the same user-message lifecycle when
    `on_task_finished(...)` drains leftover pending user input, before
    `TurnComplete`
    - with that lifecycle gap closed in core, TUI no longer needs to flush
    pending steers into history on `TurnComplete`
    - if the turn is interrupted, pending steers and queued drafts are both
    restored into the composer, with pending steers first
    
    ## Notes
    - `Tab` still uses the real queued-message path
    - `queued_user_messages` and `pending_steers` are separate state with
    separate semantics
    - the pending-steer matching key is built directly from `UserInput`
    - this removes the new TUI dependency on `RawResponseItem`
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-core
    task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input --
    --nocapture`
    - `cargo test -p codex-tui`
  • app-server: source /feedback logs from sqlite at trace level (#12969)
    ## Summary
    - write app-server SQLite logs at TRACE level when SQLite is enabled
    - source app-server `/feedback` log attachments from SQLite for the
    requested thread when available
    - flush buffered SQLite log writes before `/feedback` queries them so
    newly emitted events are not lost behind the async inserter
    - include same-process threadless SQLite rows in those `/feedback` logs
    so the attachment matches the process-wide feedback buffer more closely
    - keep the existing in-memory ring buffer fallback unchanged, including
    when the SQLite query returns no rows
    
    ## Details
    - add a byte-bounded `query_feedback_logs` helper in `codex-state` so
    `/feedback` does not fetch all rows before truncating
    - scope SQLite feedback logs to the requested thread plus threadless
    rows from the same `process_uuid`
    - format exported SQLite feedback lines with the log level prefix to
    better match the in-memory feedback formatter
    - add an explicit `LogDbLayer::flush()` control path and await it in
    app-server before querying SQLite for feedback logs
    - pass optional SQLite log bytes through `codex-feedback` as the
    `codex-logs.log` attachment override
    - leave TUI behavior unchanged apart from the updated `upload_feedback`
    call signature
    - add regression coverage for:
      - newest-within-budget ordering
      - excluding oversized newest rows
      - including same-process threadless rows
      - keeping the newest suffix across mixed thread and threadless rows
      - matching the feedback formatter shape aside from span prefixes
      - falling back to the in-memory snapshot when SQLite returns no logs
      - flushing buffered SQLite rows before querying
    
    ## Follow-up
    - SQLite feedback exports still do not reproduce span prefixes like
    `feedback-thread{thread_id=...}:`; there is a `TODO(ccunningham)` in
    `codex-rs/state/src/log_db.rs` for that follow-up.
    
    ## Testing
    - `cd codex-rs && cargo test -p codex-state`
    - `cd codex-rs && cargo test -p codex-app-server`
    - `cd codex-rs && just fmt`
  • app-server service tier plumbing (plus some cleanup) (#13334)
    followup to https://github.com/openai/codex/pull/13212 to expose fast
    tier controls to app server
    (majority of this PR is generated schema jsons - actual code is +69 /
    -35 and +24 tests )
    
    - add service tier fields to the app-server protocol surfaces used by
    thread lifecycle, turn start, config, and session configured events
    - thread service tier through the app-server message processor and core
    thread config snapshots
    - allow runtime config overrides to carry service tier for app-server
    callers
    
    cleanup:
    - Removing useless "legacy" code supporting "standard" - we moved to
    None | "fast", so "standard" is not needed.