Commit Graph

418 Commits

  • Show spawned agent model and effort in TUI (#14273)
    - include the requested sub-agent model and reasoning effort in the
    spawn begin event\n- render that metadata next to the spawned agent name
    and role in the TUI transcript
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • unifying all image saves to /tmp to bug-proof (#14149)
    image-gen feature will have the model saving to /tmp by default + at all
    times
  • Use realtime transcript for handoff context (#14132)
    - collect input/output transcript deltas into active handoff transcript
    state
    - attach and clear that transcript on each handoff, and regenerate
    schema/tests
  • [apps] Fix apps enablement condition. (#14011)
    - [x] Fix apps enablement condition to check both the feature flag and
    that the user is not an API key user.
  • start of hooks engine (#13276)
    (Experimental)
    
    This PR adds a first MVP for hooks, with SessionStart and Stop
    
    The core design is:
    
    - hooks live in a dedicated engine under codex-rs/hooks
    - each hook type has its own event-specific file
    - hook execution is synchronous and blocks normal turn progression while
    running
    - matching hooks run in parallel, then their results are aggregated into
    a normalized HookRunSummary
    
    On the AppServer side, hooks are exposed as operational metadata rather
    than transcript-native items:
    
    - new live notifications: hook/started, hook/completed
    - persisted/replayed hook results live on Turn.hookRuns
    - we intentionally did not add hook-specific ThreadItem variants
    
    Hooks messages are not persisted, they remain ephemeral. The context
    changes they add are (they get appended to the user's prompt)
  • pass on save info to model + ui tweaks (#14123)
    Passing on more information to the model for context purposes, to
    streamline image-identification.
  • Stabilize guardian approval coverage (#14103)
    ## Summary
    - align the guardian permission test with the actual sandbox policy it
    widens and use a slightly larger Windows-only timeout budget
    - expose the additional-permissions normalization helper to the guardian
    test module
    - replace the guardian popup snapshot assertion with targeted string
    assertions
    
    ## Why this fixes the flake
    This group was carrying two separate sources of drift. The guardian core
    test widened derived sandbox policies without updating the source
    sandbox policy, and it used a Windows command/timeout combination that
    was too tight on slower runners. Separately, the TUI test was
    snapshotting the full popup even though unrelated feature text changes
    were the only thing moving. The new assertions keep coverage on the
    guardian entry itself while removing unrelated snapshot churn.
  • 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
  • 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"
    <   }
    < }
    ```
  • 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"
    />
  • 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`
  • 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.
  • Enabling CWD Saving for Image-Gen (#13607)
    Codex now saves the generated image on to your current working
    directory.
  • 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>
  • 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.
  • [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.)
  • [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>
  • 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
  • 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.
  • [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 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.
  • add fast mode toggle (#13212)
    - add a local Fast mode setting in codex-core (similar to how model id
    is currently stored on disk locally)
    - send `service_tier=priority` on requests when Fast is enabled
    - add `/fast` in the TUI and persist it locally
    - feature flag
  • chore: remove SkillMetadata.permissions and derive skill sandboxing from permission_profile (#13061)
    ## Summary
    
    This change removes the compiled permissions field from skill metadata
    and keeps permission_profile as the single source of truth.
    
    Skill loading no longer compiles skill permissions eagerly. Instead, the
    zsh-fork skill escalation path compiles `skill.permission_profile` when
    it needs to determine the sandbox to apply for a skill script.
    
      ## Behavior change
    
      For skills that declare:
    ```
      permissions: {}
    ```
    we now treat that the same as having no skill permissions override,
    instead of creating and using a default readonly sandbox. This change
    makes the behavior more intuitive:
    
      - only non-empty skill permission profiles affect sandboxing
    - omitting permissions and writing permissions: {} now mean the same
    thing
    - skill metadata keeps a single permissions representation instead of
    storing derived state too
    
    Overall, this makes skill sandbox behavior easier to understand and more
    predictable.
  • Update realtime websocket API (#13265)
    - migrate the realtime websocket transport to the new session and
    handoff flow
    - make the realtime model configurable in config.toml and use API-key
    auth for the websocket
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: enable ma through /agent (#13246)
    <img width="639" height="139" alt="Screenshot 2026-03-02 at 16 06 41"
    src="https://github.com/user-attachments/assets/c006fcec-c1e7-41ce-bb84-c121d5ffb501"
    />
    
    Then
    <img width="372" height="37" alt="Screenshot 2026-03-02 at 16 06 49"
    src="https://github.com/user-attachments/assets/aa4ad703-e7e7-4620-9032-f5cd4f48ff79"
    />
  • Add model availability NUX tooltips (#13021)
    - override startup tooltips with model availability NUX and persist
    per-model show counts in config
    - stop showing each model after four exposures and fall back to normal
    tooltips
  • notify: include client in legacy hook payload (#12968)
    ## Why
    
    The `notify` hook payload did not identify which Codex client started
    the turn. That meant downstream notification hooks could not distinguish
    between completions coming from the TUI and completions coming from
    app-server clients such as VS Code or Xcode. Now that the Codex App
    provides its own desktop notifications, it would be nice to be able to
    filter those out.
    
    This change adds that context without changing the existing payload
    shape for callers that do not know the client name, and keeps the new
    end-to-end test cross-platform.
    
    ## What changed
    
    - added an optional top-level `client` field to the legacy `notify` JSON
    payload
    - threaded that value through `core` and `hooks`; the internal session
    and turn state now carries it as `app_server_client_name`
    - set the field to `codex-tui` for TUI turns
    - captured `initialize.clientInfo.name` in the app server and applied it
    to subsequent turns before dispatching hooks
    - replaced the notify integration test hook with a `python3` script so
    the test does not rely on Unix shell permissions or `bash`
    - documented the new field in `docs/config.md`
    
    ## Testing
    
    - `cargo test -p codex-hooks`
    - `cargo test -p codex-tui`
    - `cargo test -p codex-app-server
    suite::v2::initialize::turn_start_notify_payload_includes_initialize_client_name
    -- --exact --nocapture`
    - `cargo test -p codex-core` (`src/lib.rs` passed; `core/tests/all.rs`
    still has unrelated existing failures in this environment)
    
    ## Docs
    
    The public config reference on `developers.openai.com/codex` should
    mention that the legacy `notify` payload may include a top-level
    `client` field. The TUI reports `codex-tui`, and the app server reports
    `initialize.clientInfo.name` when it is available.
  • Add model availability NUX metadata (#12972)
    - replace show_nux with structured availability_nux model metadata
    - expose availability NUX data through the app-server model API
    - update shared fixtures and tests for the new field
  • Add realtime audio device picker (#12850)
    ## Summary
    - add a dedicated /audio picker for realtime microphone and speaker
    selection
    - persist realtime audio choices and prompt to restart only local audio
    when voice is live
    - add snapshot coverage for the new picker surfaces
    
    ## Validation
    - cargo test -p codex-tui
    - cargo insta accept
    - just fix -p codex-tui
    - just fmt
  • Add realtime audio device config (#12849)
    ## Summary
    - add top-level realtime audio config for microphone and speaker
    selection
    - apply configured devices when starting realtime capture and playback
    - keep missing-device behavior on the system default fallback path
    
    ## Validation
    - just write-config-schema
    - cargo test -p codex-core realtime_audio
    - cargo test -p codex-tui
    - just fix -p codex-core
    - just fix -p codex-tui
    - just fmt
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Allow clients not to send summary as an option (#12950)
    Summary is a required parameter on UserTurn. Ideally we'd like the core
    to decide the appropriate summary level.
    
    Make the summary optional and don't send it when not needed.
  • feat: include available decisions in command approval requests (#12758)
    Command-approval clients currently infer which choices to show from
    side-channel fields like `networkApprovalContext`,
    `proposedExecpolicyAmendment`, and `additionalPermissions`. That makes
    the request shape harder to evolve, and it forces each client to
    replicate the server's heuristics instead of receiving the exact
    decision list for the prompt.
    
    This PR introduces a mapping between `CommandExecutionApprovalDecision`
    and `codex_protocol::protocol::ReviewDecision`:
    
    ```rust
    impl From<CoreReviewDecision> for CommandExecutionApprovalDecision {
        fn from(value: CoreReviewDecision) -> Self {
            match value {
                CoreReviewDecision::Approved => Self::Accept,
                CoreReviewDecision::ApprovedExecpolicyAmendment {
                    proposed_execpolicy_amendment,
                } => Self::AcceptWithExecpolicyAmendment {
                    execpolicy_amendment: proposed_execpolicy_amendment.into(),
                },
                CoreReviewDecision::ApprovedForSession => Self::AcceptForSession,
                CoreReviewDecision::NetworkPolicyAmendment {
                    network_policy_amendment,
                } => Self::ApplyNetworkPolicyAmendment {
                    network_policy_amendment: network_policy_amendment.into(),
                },
                CoreReviewDecision::Abort => Self::Cancel,
                CoreReviewDecision::Denied => Self::Decline,
            }
        }
    }
    ```
    
    And updates `CommandExecutionRequestApprovalParams` to have a new field:
    
    ```rust
    available_decisions: Option<Vec<CommandExecutionApprovalDecision>>
    ```
    
    when, if specified, should make it easier for clients to display an
    appropriate list of options in the UI.
    
    This makes it possible for `CoreShellActionProvider::prompt()` in
    `unix_escalation.rs` to specify the `Vec<ReviewDecision>` directly,
    adding support for `ApprovedForSession` when approving a skill script,
    which was previously missing in the TUI.
    
    Note this results in a significant change to `exec_options()` in
    `approval_overlay.rs`, as the displayed options are now derived from
    `available_decisions: &[ReviewDecision]`.
    
    ## What Changed
    
    - Add `available_decisions` to
    [`ExecApprovalRequestEvent`](https://github.com/openai/codex/blob/de00e932dd9801de0a4faac0519162099753f331/codex-rs/protocol/src/approvals.rs#L111-L175),
    including helpers to derive the legacy default choices when older
    senders omit the field.
    - Map `codex_protocol::protocol::ReviewDecision` to app-server
    `CommandExecutionApprovalDecision` and expose the ordered list as
    experimental `availableDecisions` in
    [`CommandExecutionRequestApprovalParams`](https://github.com/openai/codex/blob/de00e932dd9801de0a4faac0519162099753f331/codex-rs/app-server-protocol/src/protocol/v2.rs#L3798-L3807).
    - Thread optional `available_decisions` through the core approval path
    so Unix shell escalation can explicitly request `ApprovedForSession` for
    session-scoped approvals instead of relying on client heuristics.
    [`unix_escalation.rs`](https://github.com/openai/codex/blob/de00e932dd9801de0a4faac0519162099753f331/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs#L194-L214)
    - Update the TUI approval overlay to build its buttons from the ordered
    decision list, while preserving the legacy fallback when
    `available_decisions` is missing.
    - Update the app-server README, test client output, and generated schema
    artifacts to document and surface the new field.
    
    ## Testing
    
    - Add `approval_overlay.rs` coverage for explicit decision lists,
    including the generic `ApprovedForSession` path and network approval
    options.
    - Update `chatwidget/tests.rs` and app-server protocol tests to populate
    the new optional field and keep older event shapes working.
    
    ## Developers Docs
    
    - If we document `item/commandExecution/requestApproval` on
    [developers.openai.com/codex](https://developers.openai.com/codex), add
    experimental `availableDecisions` as the preferred source of approval
    choices and note that older servers may omit it.
  • Remove steer feature flag (#12026)
    All code should go in the direction that steer is enabled
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Enable request_user_input in Default mode (#12735)
    ## Summary
    - allow `request_user_input` in Default collaboration mode as well as
    Plan
    - update the Default-mode instructions to prefer assumptions first and
    use `request_user_input` only when a question is unavoidable
    - update request_user_input and app-server tests to match the new
    Default-mode behavior
    - refactor collaboration-mode availability plumbing into
    `CollaborationModesConfig` for future mode-related flags
    
    ## Codex author
    `codex resume 019c9124-ed28-7c13-96c6-b916b1c97d49`
  • make 5.3-codex visible in cli for api users (#12808)
    5.3-codex released in api, mark it visible for API users via bundled
    `models.json`.
  • Promote js_repl to experimental with Node requirement (#12712)
    ## Summary
    
    - Promote `js_repl` to an experimental feature that users can enable
    from `/experimental`.
    - Add `js_repl` experimental metadata, including the Node prerequisite
    and activation guidance.
    - Add regression coverage for the feature metadata and the
    `/experimental` popup.
    
    ## What Changed
    
    - Changed `Feature::JsRepl` from `Stage::UnderDevelopment` to
    `Stage::Experimental`.
    - Added experimental metadata for `js_repl` in `core/src/features.rs`:
      - name: `JavaScript REPL`
    - description: calls out interactive website debugging, inline
    JavaScript execution, and the required Node version (`>= v24.13.1`)
    - announcement: tells users to enable it, then start a new chat or
    restart Codex
    - Added a core unit test that verifies:
      - `js_repl` is experimental
      - `js_repl` is disabled by default
    - the hardcoded Node version in the description matches
    `node-version.txt`
    - Added a TUI test that opens the `/experimental` popup and verifies the
    rendered `js_repl` entry includes the Node requirement text.
    
    ## Testing
    
    - `just fmt`
    - `cargo test -p codex-tui`
    - `cargo test -p codex-core` (unit-test phase passed; stopped during the
    long `tests/all.rs` integration suite)
  • Surface skill permission profiles in zsh-fork exec approvals (#12753)
    ## Summary
    
    - Preserve each skill’s raw permissions block as a permission_profile on
    SkillMetadata during skill loading.
    - Keep compiling that same metadata into the existing runtime
    Permissions object, so current enforcement
        behavior stays intact.
    - When zsh-fork intercepts execution of a script that belongs to a
    skill, include the skill’s
        permission_profile in the exec approval request.
    - This lets approval UIs show the extra filesystem access the skill
    declared when prompting for approval.
  • fix: chatwidget was not honoring approval_id for an ExecApprovalRequestEvent (#12746)
    ## Why
    
    `ExecApprovalRequestEvent` can carry a distinct `approval_id` for
    subcommand approvals, including the `execve`-intercepted zsh-fork path.
    
    The session registers the pending approval callback under `approval_id`
    when one is present, but `ChatWidget` was stashing `call_id` in the
    approval modal state. When the user approved the command in the TUI, the
    response was sent back with the wrong identifier, so the pending
    approval could not be matched and the approval callback would not
    resolve.
    
    Note `approval_id` was introduced in
    https://github.com/openai/codex/pull/12051.
    
    ## What changed
    
    - In `tui/src/chatwidget.rs`, `ChatWidget` now uses
    `ExecApprovalRequestEvent::effective_approval_id()` when constructing
    `ApprovalRequest::Exec`.
    - That preserves the existing behavior for normal shell and
    `unified_exec` approvals, where `approval_id` is absent and the
    effective id still falls back to `call_id`.
    - For subcommand approvals that provide a distinct `approval_id`, the
    TUI now sends back the same key that
    `Session::request_command_approval()` registered.
    
    ## Verification
    
    - Traced the approval flow end to end to confirm the same effective
    approval id is now used on both sides of the round trip:
    - `Session::request_command_approval()` registers the pending callback
    under `approval_id.unwrap_or(call_id)`.
    - `ChatWidget` now emits `Op::ExecApproval` with that same effective id.