Commit Graph

4266 Commits

  • 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.
  • Clarify js_repl image emission and encoding guidance (#13639)
    ## Summary
    
    This updates the `js_repl` prompt and docs to make the image guidance
    less confusing.
    
    ## What changed
    
    - Clarified that `codex.emitImage(...)` adds one image per call and can
    be called multiple times to emit multiple images.
    - Reworded the image-encoding guidance to be general `js_repl` advice
    instead of `ImageDetailOriginal`-specific behavior.
    - Updated the guidance to recommend JPEG at about quality 85 when lossy
    compression is acceptable, and PNG when transparency or lossless detail
    matters.
    - Mirrored the same wording in the public `js_repl` docs.
  • Improve macOS Seatbelt network and unix socket handling (#12702)
    This improves macOS Seatbelt handling for sandboxed tool processes.
    
    ## Changes
    - Allow dual-stack local binding in proxy-managed sessions, while still
    keeping traffic limited to loopback and configured proxy endpoints.
    - Replace the old generic unix-socket path rule with explicit AF_UNIX
    permissions for socket creation, bind, and outbound connect.
    - Keep explicitly approved wrapper sockets connect-only.
    
    Local helper servers are less likely to fail when binding on macOS.
    Tools using local unix-socket IPC should work more reliably under the
    sandbox.
    Full-network sessions, proxy fail-closed behavior, and proxy lifecycle
    are unchanged.
  • fix(linux-sandbox): always unshare bwrap userns (#13624)
    ## Summary
    - always pass `--unshare-user` in the Linux bubblewrap argv builders
    - stop relying on bubblewrap's auto-userns behavior, which is skipped
    for `uid 0`
    - update argv expectations in tests and document the explicit user
    namespace behavior
    
    The installed Codex binary reproduced the same issue with:
    - `codex -c features.use_linux_sandbox_bwrap=true sandbox linux -- true`
    - `bwrap: Creating new namespace failed: Operation not permitted`
    
    This happens because Codex asked bubblewrap for mount/pid/network
    namespaces without explicitly asking for a user namespace. In a
    root-inside-container environment without ambient `CAP_SYS_ADMIN`, that
    fails. Adding `--unshare-user` makes bubblewrap create the user
    namespace first and then the remaining namespaces succeed.
  • 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.
  • Harden js_repl emitImage to accept only data: URLs (#13507)
    ### Motivation
    
    - Prevent untrusted js_repl code from supplying arbitrary external URLs
    that the host would forward into model input and cause external fetches
    / data exfiltration. This change narrows the emitImage contract to safe,
    self-contained data URLs.
    
    ### Description
    
    - Kernel: added `normalizeEmitImageUrl` and enforce that string-valued
    `codex.emitImage(...)` inputs and `input_image`/content-item paths only
    accept non-empty `data:` URLs; byte-based paths still produce data URLs
    as before (`kernel.js`).
    - Host: added `validate_emitted_image_url` and check `EmitImage`
    requests before creating `FunctionCallOutputContentItem::InputImage`,
    returning an error to the kernel if the URL is not a `data:` URL
    (`mod.rs`).
    - Tests/docs: added a runtime test
    `js_repl_emit_image_rejects_non_data_url` to assert rejection of
    non-data URLs and updated user-facing docs/instruction text to state
    `data URL` support instead of generic direct image URLs (`mod.rs`,
    `docs/js_repl.md`, `project_doc.rs`).
    
    ### Testing
    
    - Ran `just fmt` in `codex-rs`; it completed successfully.
    - Added a runtime test (`cargo test -p codex-core
    js_repl_emit_image_rejects_non_data_url`) but executing the test in this
    environment failed due to a missing system dependency required by
    `codex-linux-sandbox` (the vendored `bubblewrap` build requires
    `libcap.pc` via `pkg-config`), so the test could not be run here.
    - Attempted a focused `cargo test` invocation with and without default
    features; both compile/test attempts were blocked by the same missing
    system `libcap` dependency in this environment.
    
    ------
    [Codex
    Task](https://chatgpt.com/codex/tasks/task_i_69a7837bce98832d91db92d5f76d6cbe)
  • feat: merge skill permission profiles into the turn sandbox for zsh-fork execs (#13496)
    ## Summary
    
    This changes the Unix shell escalation path for skill-matched
    executables to apply a skill's `PermissionProfile` as additive
    permissions on top of the existing turn/request sandbox policy.
    
    Previously, skill-matched executables compiled the skill permission
    profile into a standalone sandbox policy and executed against that
    replacement policy. Now they go through the same
    `additional_permissions` merge path used elsewhere in shell sandbox
    preparation.
    
    ## What Changed
    
    - Changed `skill_escalation_execution()` to return
    `EscalationPermissions::PermissionProfile(...)` for non-empty skill
    permission profiles.
    - Kept empty or missing skill permission profiles on the `TurnDefault`
    path.
    - Added tests covering the new additive skill-permission behavior.
    - Added inline comments in `prepare_escalated_exec()` clarifying the
    difference between additive permission merging and fully specified
    replacement sandbox policies.
    - Removed the now-unused skill permission compiler module after
    switching this path away from standalone compiled skill sandbox
    policies.
    
    ## Testing
    
    - Ran `just fmt` in `codex-rs`
    - Ran `cargo test -p codex-core`
    
    `cargo test -p codex-core` still hits an unrelated existing failure:
    `shell_snapshot::tests::snapshot_shell_does_not_inherit_stdin`
    
    ## Follow-up
    
    This change intentionally does not merge skill-specific macOS seatbelt
    profile extensions through the `additional_permissions` path yet.
    Filesystem and network permissions now follow the additive merge path,
    but seatbelt extension permissions still need separate handling in a
    follow-up PR.
  • [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.)
  • Persist initialized js_repl bindings after failed cells (#13482)
    ## Summary
    
    - Change `js_repl` failed-cell persistence so later cells keep prior
    bindings plus only the current-cell bindings whose initialization
    definitely completed before the throw.
    - Preserve initialized lexical bindings across failed cells via
    module-namespace readability, including top-level destructuring that
    partially succeeds before a later throw.
    - Preserve hoisted `var` and `function` bindings only when execution
    clearly reached their declaration site, and preserve direct top-level
    pre-declaration `var` writes and updates through explicit write-site
    markers.
    - Preserve top-level `for...in` / `for...of` `var` bindings when the
    loop body executes at least once, using a first-iteration guard to avoid
    per-iteration bookkeeping overhead.
    - Keep prior module state intact across link-time failures and
    evaluation failures before the prelude runs, while still allowing failed
    cells that already recreated prior bindings to persist updates to those
    existing bindings.
    - Hide internal commit hooks from user `js_repl` code after the prelude
    aliases them, so snippets cannot spoof committed bindings by calling the
    raw `import.meta` hooks directly.
    - Add focused regression coverage for the supported failed-cell
    behaviors and the intentionally unsupported boundaries.
    - Update `js_repl` docs and generated instructions to describe the new,
    narrower failed-cell persistence model.
    
    ## Motivation
    
    We saw `js_repl` drop bindings that had already been initialized
    successfully when a later statement in the same cell threw, for example:
    
        const { context: liveContext, session } =
          await initializeGoogleSheetsLiveForTab(tab);
        // later statement throws
    
    That was surprising in practice because successful earlier work
    disappeared from the next cell.
    
    This change makes failed-cell persistence more useful without trying to
    model every possible partially executed JavaScript edge case. The
    resulting behavior is narrower and easier to reason about:
    
    - prior bindings are always preserved
    - lexical bindings persist when their initialization completed before
    the throw
    - hoisted `var` / `function` bindings persist only when execution
    clearly reached their declaration or a supported top-level `var` write
    site
    - failed cells that already recreated prior bindings can persist writes
    to those existing bindings even if they introduce no new bindings
    
    The detailed edge-case matrix stays in `docs/js_repl.md`. The
    model-facing `project_doc` guidance is intentionally shorter and focused
    on generation-relevant behavior.
    
    ## Supported Failed-Cell Behavior
    
    - Prior bindings remain available after a failed cell.
    - Initialized lexical bindings remain available after a failed cell.
    - Top-level destructuring like `const { a, b } = ...` preserves names
    whose initialization completed before a later throw.
    - Hoisted `function` bindings persist when execution reached the
    declaration statement before the throw.
    - Direct top-level pre-declaration `var` writes and updates persist, for
    example:
      - `x = 1`
      - `x += 1`
      - `x++`
    - short-circuiting logical assignments only persist when the write
    branch actually runs
    - Non-empty top-level `for...in` / `for...of` `var` loops persist their
    loop bindings.
    - Failed cells can persist updates to existing carried bindings after
    the prelude has run, even when the cell commits no new bindings.
    - Link failures and eval failures before the prelude do not poison
    `@prev`.
    
    ## Intentionally Unsupported Failed-Cell Cases
    
    - Hoisted function reads before the declaration, such as `foo(); ...;
    function foo() {}`
    - Aliasing or inference-based recovery from reads before declaration
    - Nested writes inside already-instrumented assignment RHS expressions
    - Destructuring-assignment recovery for hoisted `var`
    - Partial `var` destructuring recovery
    - Pre-declaration `undefined` reads for hoisted `var`
    - Empty top-level `for...in` / `for...of` loop vars
    - Nested or scope-sensitive pre-declaration `var` writes outside direct
    top-level expression statements
  • treat SIGTERM like ctrl-c for graceful shutdown (#13594)
    treat SIGTERM the same as SIGINT for graceful app-server websocket
    shutdown
  • 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.
  • feat: skills for artifacts (#13525)
    Co-authored-by: Dibyo Majumdar <dibyo@openai.com>
  • refactor: prepare unified exec for zsh-fork backend (#13392)
    ## Why
    
    `shell_zsh_fork` already provides stronger guarantees around which
    executables receive elevated permissions. To reuse that machinery from
    unified exec without pushing Unix-specific escalation details through
    generic runtime code, the escalation bootstrap and session lifetime
    handling need a cleaner boundary.
    
    That boundary also needs to be safe for long-lived sessions: when an
    intercepted shell session is closed or pruned, any in-flight approval
    workers and any already-approved escalated child they spawned must be
    torn down with the session, and the inherited escalation socket must not
    leak into unrelated subprocesses.
    
    ## What Changed
    
    - Extracted a reusable `EscalationSession` and
    `EscalateServer::start_session(...)` in `shell-escalation` so callers
    can get the wrapper/socket env overlay and keep the escalation server
    alive without immediately running a one-shot command.
    - Documented that `EscalationSession::env()` and
    `ShellCommandExecutor::run(...)` exchange only that env overlay, which
    callers must merge into their own base shell environment.
    - Clarified the prepared-exec helper boundary in `core` by naming the
    new helper APIs around `ExecRequest`, while keeping the legacy
    `execute_env(...)` entrypoints as thin compatibility wrappers for
    existing callers that still use the older naming.
    - Added a small post-spawn hook on the prepared execution path so the
    parent copy of the inheritable escalation socket is closed immediately
    after both the existing one-shot shell-command spawn and the
    unified-exec spawn.
    - Made session teardown explicit with session-scoped cancellation:
    dropping an `EscalationSession` or canceling its parent request now
    stops intercept workers, and the server-spawned escalated child uses
    `kill_on_drop(true)` so teardown cannot orphan an already-approved
    child.
    - Added `UnifiedExecBackendConfig` plumbing through `ToolsConfig`, a
    `shell::zsh_fork_backend` facade, and an opaque unified-exec
    spawn-lifecycle hook so unified exec can prepare a wrapped `zsh -c/-lc`
    request without storing `EscalationSession` directly in generic
    process/runtime code.
    - Kept the existing `shell_command` zsh-fork behavior intact on top of
    the new bootstrap path. Tool selection is unchanged in this PR: when
    `shell_zsh_fork` is enabled, `ShellCommand` still wins over
    `exec_command`.
    
    ## Verification
    
    - `cargo test -p codex-shell-escalation`
      - includes coverage for `start_session_exposes_wrapper_env_overlay`
      - includes coverage for `exec_closes_parent_socket_after_shell_spawn`
    - includes coverage for
    `dropping_session_aborts_intercept_workers_and_kills_spawned_child`
    - `cargo test -p codex-core
    shell_zsh_fork_prefers_shell_command_over_unified_exec`
    - `cargo test -p codex-core --test all
    shell_zsh_fork_prompts_for_skill_script_execution`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13392).
    * #13432
    * __->__ #13392
  • [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>
  • chore: add web_search_tool_type for image support (#13538)
    add `web_search_tool_type` on model_info that can be populated from
    backend. will be used to filter which models can use `web_search` with
    images and which cant.
    
    added small unit test.
  • Reduce realtime audio submission log noise (#13539)
    - lower `submission_dispatch` span logging to debug for realtime audio
    submissions only
    - keep other submission spans at info and add a targeted test for the
    level selection
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [js_repl] Support local ESM file imports (#13437)
    ## Summary
    - add `js_repl` support for dynamic imports of relative and absolute
    local ESM `.js` / `.mjs` files
    - keep bare package imports on the native Node path and resolved from
    REPL-global search roots (`CODEX_JS_REPL_NODE_MODULE_DIRS`, then `cwd`),
    even when they originate from imported local files
    - restrict static imports inside imported local files to other local
    relative/absolute `.js` / `.mjs` files, and surface a clear error for
    unsupported top-level static imports in the REPL cell
    - run imported local files inside the REPL VM context so they can access
    `codex.tmpDir`, `codex.tool`, captured `console`, and Node-like
    `import.meta` helpers
    - reload local files between execs so later `await import("./file.js")`
    calls pick up edits and fixed failures, while preserving package/builtin
    caching and persistent top-level REPL bindings
    - make `import.meta.resolve()` self-consistent by allowing the returned
    `file://...` URLs to round-trip through `await import(...)`
    - update both public and injected `js_repl` docs to clarify the narrowed
    contract, including global bare-import resolution behavior for local
    absolute files
    
    ## Testing
    - `cargo test -p codex-core js_repl_`
    - built codex binary and verified behavior
    
    ---------
    
    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>
  • feat: track plugins mcps/apps and add plugin info to user_instructions (#13433)
    ### first half of changes, followed by #13510
    
    Track plugin capabilities as derived summaries on `PluginLoadOutcome`
    for enabled plugins with at least one skill/app/mcp.
    
    Also add `Plugins` section to `user_instructions` injected on session
    start. These introduce the plugins concept and list enabled plugins, but
    do NOT currently include paths to enabled plugins or details on what
    apps/mcps the plugins contain (current plan is to inject this on
    @-mention). that can be adjusted in a follow up and based on evals.
    
    ### tests
    Added/updated tests, confirmed locally that new `Plugins` section +
    currently enabled plugins show up in `user_instructions`.
  • chore(deps): bump actions/upload-artifact from 6 to 7 (#13207)
    Bumps
    [actions/upload-artifact](https://github.com/actions/upload-artifact)
    from 6 to 7.
    <details>
    <summary>Release notes</summary>
    <p><em>Sourced from <a
    href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's
    releases</a>.</em></p>
    <blockquote>
    <h2>v7.0.0</h2>
    <h2>v7 What's new</h2>
    <h3>Direct Uploads</h3>
    <p>Adds support for uploading single files directly (unzipped). Callers
    can set the new <code>archive</code> parameter to <code>false</code> to
    skip zipping the file during upload. Right now, we only support single
    files. The action will fail if the glob passed resolves to multiple
    files. The <code>name</code> parameter is also ignored with this
    setting. Instead, the name of the artifact will be the name of the
    uploaded file.</p>
    <h3>ESM</h3>
    <p>To support new versions of the <code>@actions/*</code> packages,
    we've upgraded the package to ESM.</p>
    <h2>What's Changed</h2>
    <ul>
    <li>Add proxy integration test by <a
    href="https://github.com/Link"><code>@​Link</code></a>- in <a
    href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li>
    <li>Upgrade the module to ESM and bump dependencies by <a
    href="https://github.com/danwkennedy"><code>@​danwkennedy</code></a> in
    <a
    href="https://redirect.github.com/actions/upload-artifact/pull/762">actions/upload-artifact#762</a></li>
    <li>Support direct file uploads by <a
    href="https://github.com/danwkennedy"><code>@​danwkennedy</code></a> in
    <a
    href="https://redirect.github.com/actions/upload-artifact/pull/764">actions/upload-artifact#764</a></li>
    </ul>
    <h2>New Contributors</h2>
    <ul>
    <li><a href="https://github.com/Link"><code>@​Link</code></a>- made
    their first contribution in <a
    href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li>
    </ul>
    <p><strong>Full Changelog</strong>: <a
    href="https://github.com/actions/upload-artifact/compare/v6...v7.0.0">https://github.com/actions/upload-artifact/compare/v6...v7.0.0</a></p>
    </blockquote>
    </details>
    <details>
    <summary>Commits</summary>
    <ul>
    <li><a
    href="https://github.com/actions/upload-artifact/commit/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f"><code>bbbca2d</code></a>
    Support direct file uploads (<a
    href="https://redirect.github.com/actions/upload-artifact/issues/764">#764</a>)</li>
    <li><a
    href="https://github.com/actions/upload-artifact/commit/589182c5a4cec8920b8c1bce3e2fab1c97a02296"><code>589182c</code></a>
    Upgrade the module to ESM and bump dependencies (<a
    href="https://redirect.github.com/actions/upload-artifact/issues/762">#762</a>)</li>
    <li><a
    href="https://github.com/actions/upload-artifact/commit/47309c993abb98030a35d55ef7ff34b7fa1074b5"><code>47309c9</code></a>
    Merge pull request <a
    href="https://redirect.github.com/actions/upload-artifact/issues/754">#754</a>
    from actions/Link-/add-proxy-integration-tests</li>
    <li><a
    href="https://github.com/actions/upload-artifact/commit/02a8460834e70dab0ce194c64360c59dc1475ef0"><code>02a8460</code></a>
    Add proxy integration test</li>
    <li>See full diff in <a
    href="https://github.com/actions/upload-artifact/compare/v6...v7">compare
    view</a></li>
    </ul>
    </details>
    <br />
    
    
    [![Dependabot compatibility
    score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/upload-artifact&package-manager=github_actions&previous-version=6&new-version=7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
    
    Dependabot will resolve any conflicts with this PR as long as you don't
    alter it yourself. You can also trigger a rebase manually by commenting
    `@dependabot rebase`.
    
    [//]: # (dependabot-automerge-start)
    [//]: # (dependabot-automerge-end)
    
    ---
    
    <details>
    <summary>Dependabot commands and options</summary>
    <br />
    
    You can trigger Dependabot actions by commenting on this PR:
    - `@dependabot rebase` will rebase this PR
    - `@dependabot recreate` will recreate this PR, overwriting any edits
    that have been made to it
    - `@dependabot show <dependency name> ignore conditions` will show all
    of the ignore conditions of the specified dependency
    - `@dependabot ignore this major version` will close this PR and stop
    Dependabot creating any more for this major version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this minor version` will close this PR and stop
    Dependabot creating any more for this minor version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this dependency` will close this PR and stop
    Dependabot creating any more for this dependency (unless you reopen the
    PR or upgrade to it yourself)
    
    
    </details>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    Co-authored-by: Eric Traut <etraut@openai.com>
  • Preserve persisted thread git info in resume (#13504)
    ## Summary
    - ensure `thread.resume` reuses the stored `gitInfo` instead of
    rebuilding it from the live working tree
    - persist and apply thread git metadata through the resume flow and add
    a regression test covering branch mismatch cases
    
    ## Testing
    - Not run (not requested)
  • chore(deps): bump serde_with from 3.16.1 to 3.17.0 in /codex-rs (#13209)
    Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.16.1 to
    3.17.0.
    <details>
    <summary>Release notes</summary>
    <p><em>Sourced from <a
    href="https://github.com/jonasbb/serde_with/releases">serde_with's
    releases</a>.</em></p>
    <blockquote>
    <h2>serde_with v3.17.0</h2>
    <h3>Added</h3>
    <ul>
    <li>Support <code>OneOrMany</code> with <code>smallvec</code> v1 (<a
    href="https://redirect.github.com/jonasbb/serde_with/issues/920">#920</a>,
    <a
    href="https://redirect.github.com/jonasbb/serde_with/issues/922">#922</a>)</li>
    </ul>
    <h3>Changed</h3>
    <ul>
    <li>Switch to <code>yaml_serde</code> for a maintained yaml dependency
    by <a href="https://github.com/kazan417"><code>@​kazan417</code></a> (<a
    href="https://redirect.github.com/jonasbb/serde_with/issues/921">#921</a>)</li>
    <li>Bump MSRV to 1.82, since that is required for
    <code>yaml_serde</code> dev-dependency.</li>
    </ul>
    </blockquote>
    </details>
    <details>
    <summary>Commits</summary>
    <ul>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/4031878a4cfced7261105447d8683c296147864b"><code>4031878</code></a>
    Bump version to v3.17.0 (<a
    href="https://redirect.github.com/jonasbb/serde_with/issues/924">#924</a>)</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/204ae56f8ba08bd911ad0f122719bf07f3dcdbbb"><code>204ae56</code></a>
    Bump version to v3.17.0</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/7812b5a006e23e0204c687868e68a8b9dae75cd1"><code>7812b5a</code></a>
    serde_yaml 0.9 to yaml_serde 0.10 (<a
    href="https://redirect.github.com/jonasbb/serde_with/issues/921">#921</a>)</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/614bd8950bc179f4f23c1d9f26866ac216257fed"><code>614bd89</code></a>
    Bump MSRV to 1.82 as required by yaml_serde</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/518d0ed7873616a81c987d7961d78f5f26210694"><code>518d0ed</code></a>
    Suppress RUSTSEC-2026-0009 since we don't have untrusted time input in
    tests ...</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/a6579a89841f269c7f63912e8e808e82212c672e"><code>a6579a8</code></a>
    Suppress RUSTSEC-2026-0009 since we don't have untrusted time input in
    tests</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/9d4d0696e6794da4babf8204d17d11dadb79dd60"><code>9d4d069</code></a>
    Implement OneOrMany for smallvec_1::SmallVec (<a
    href="https://redirect.github.com/jonasbb/serde_with/issues/922">#922</a>)</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/fc78243e8c60c4fcc11a99f2c6ccc0d449a57fd9"><code>fc78243</code></a>
    Add changelog</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/2b8c30bf679309c27143f13070dbeef068310ab5"><code>2b8c30b</code></a>
    Implement OneOrMany for smallvec_1::SmallVec</li>
    <li><a
    href="https://github.com/jonasbb/serde_with/commit/2d9b9a1815cb6d58b17ab6403e57e7c2f62b84cc"><code>2d9b9a1</code></a>
    Carg.lock update</li>
    <li>Additional commits viewable in <a
    href="https://github.com/jonasbb/serde_with/compare/v3.16.1...v3.17.0">compare
    view</a></li>
    </ul>
    </details>
    <br />
    
    
    [![Dependabot compatibility
    score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_with&package-manager=cargo&previous-version=3.16.1&new-version=3.17.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
    
    Dependabot will resolve any conflicts with this PR as long as you don't
    alter it yourself. You can also trigger a rebase manually by commenting
    `@dependabot rebase`.
    
    [//]: # (dependabot-automerge-start)
    [//]: # (dependabot-automerge-end)
    
    ---
    
    <details>
    <summary>Dependabot commands and options</summary>
    <br />
    
    You can trigger Dependabot actions by commenting on this PR:
    - `@dependabot rebase` will rebase this PR
    - `@dependabot recreate` will recreate this PR, overwriting any edits
    that have been made to it
    - `@dependabot show <dependency name> ignore conditions` will show all
    of the ignore conditions of the specified dependency
    - `@dependabot ignore this major version` will close this PR and stop
    Dependabot creating any more for this major version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this minor version` will close this PR and stop
    Dependabot creating any more for this minor version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this dependency` will close this PR and stop
    Dependabot creating any more for this dependency (unless you reopen the
    PR or upgrade to it yourself)
    
    
    </details>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    Co-authored-by: Eric Traut <etraut@openai.com>
  • chore(deps): bump strum_macros from 0.27.2 to 0.28.0 in /codex-rs (#13210)
    Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.27.2
    to 0.28.0.
    <details>
    <summary>Changelog</summary>
    <p><em>Sourced from <a
    href="https://github.com/Peternator7/strum/blob/master/CHANGELOG.md">strum_macros's
    changelog</a>.</em></p>
    <blockquote>
    <h2>0.28.0</h2>
    <ul>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/461">#461</a>:
    Allow any kind of passthrough attributes on
    <code>EnumDiscriminants</code>.</p>
    <ul>
    <li>Previously only list-style attributes (e.g.
    <code>#[strum_discriminants(derive(...))]</code>) were supported. Now
    path-only
    (e.g. <code>#[strum_discriminants(non_exhaustive)]</code>) and
    name/value (e.g. <code>#[strum_discriminants(doc =
    &quot;foo&quot;)]</code>)
    attributes are also supported.</li>
    </ul>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/462">#462</a>:
    Add missing <code>#[automatically_derived]</code> to generated impls not
    covered by <a
    href="https://redirect.github.com/Peternator7/strum/pull/444">#444</a>.</p>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/466">#466</a>:
    Bump MSRV to 1.71, required to keep up with updated <code>syn</code> and
    <code>windows-sys</code> dependencies. This is a breaking change if
    you're on an old version of rust.</p>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/469">#469</a>:
    Use absolute paths in generated proc macro code to avoid
    potential name conflicts.</p>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/465">#465</a>:
    Upgrade <code>phf</code> dependency to v0.13.</p>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/473">#473</a>:
    Fix <code>cargo fmt</code> / <code>clippy</code> issues and add GitHub
    Actions CI.</p>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/477">#477</a>:
    <code>strum::ParseError</code> now implements
    <code>core::fmt::Display</code> instead
    <code>std::fmt::Display</code> to make it <code>#[no_std]</code>
    compatible. Note the <code>Error</code> trait wasn't available in core
    until <code>1.81</code>
    so <code>strum::ParseError</code> still only implements that in std.</p>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/476">#476</a>:
    <strong>Breaking Change</strong> - <code>EnumString</code> now
    implements <code>From&lt;&amp;str&gt;</code>
    (infallible) instead of <code>TryFrom&lt;&amp;str&gt;</code> when the
    enum has a <code>#[strum(default)]</code> variant. This more accurately
    reflects that parsing cannot fail in that case. If you need the old
    <code>TryFrom</code> behavior, you can opt back in using
    <code>parse_error_ty</code> and <code>parse_error_fn</code>:</p>
    <pre lang="rust"><code>#[derive(EnumString)]
    #[strum(parse_error_ty = strum::ParseError, parse_error_fn =
    make_error)]
    pub enum Color {
        Red,
        #[strum(default)]
        Other(String),
    }
    <p>fn make_error(x: &amp;str) -&gt; strum::ParseError {
    strum::ParseError::VariantNotFound
    }
    </code></pre></p>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/431">#431</a>:
    Fix bug where <code>EnumString</code> ignored the
    <code>parse_err_ty</code>
    attribute when the enum had a <code>#[strum(default)]</code>
    variant.</p>
    </li>
    <li>
    <p><a
    href="https://redirect.github.com/Peternator7/strum/pull/474">#474</a>:
    EnumDiscriminants will now copy <code>default</code> over from the
    original enum to the Discriminant enum.</p>
    <pre lang="rust"><code>#[derive(Debug, Default, EnumDiscriminants)]
    #[strum_discriminants(derive(Default))] // &lt;- Remove this in 0.28.
    enum MyEnum {
        #[default] // &lt;- Will be the #[default] on the MyEnumDiscriminant
        #[strum_discriminants(default)] // &lt;- Remove this in 0.28
        Variant0,
        Variant1 { a: NonDefault },
    }
    </code></pre>
    </li>
    </ul>
    <!-- raw HTML omitted -->
    </blockquote>
    <p>... (truncated)</p>
    </details>
    <details>
    <summary>Commits</summary>
    <ul>
    <li><a
    href="https://github.com/Peternator7/strum/commit/7376771128834d28bb9beba5c39846cba62e71ec"><code>7376771</code></a>
    Peternator7/0.28 (<a
    href="https://redirect.github.com/Peternator7/strum/issues/475">#475</a>)</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/26e63cd964a2e364331a5dd977d589bb9f649d8c"><code>26e63cd</code></a>
    Display exists in core (<a
    href="https://redirect.github.com/Peternator7/strum/issues/477">#477</a>)</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/9334c728eedaa8a992d1388a8f4564bbccad1934"><code>9334c72</code></a>
    Make TryFrom and FromStr infallible if there's a default (<a
    href="https://redirect.github.com/Peternator7/strum/issues/476">#476</a>)</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/0ccbbf823c16e827afc263182cd55e99e3b2a52e"><code>0ccbbf8</code></a>
    Honor parse_err_ty attribute when the enum has a default variant (<a
    href="https://redirect.github.com/Peternator7/strum/issues/431">#431</a>)</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/2c9e5a9259189ce8397f2f4967060240c6bafd74"><code>2c9e5a9</code></a>
    Automatically add Default implementation to EnumDiscriminant if it
    exists on ...</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/e241243e48359b8b811b8eaccdcfa1ae87138e0d"><code>e241243</code></a>
    Fix existing cargo fmt + clippy issues and add GH actions (<a
    href="https://redirect.github.com/Peternator7/strum/issues/473">#473</a>)</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/639b67fefd20eaead1c5d2ea794e9afe70a00312"><code>639b67f</code></a>
    feat: allow any kind of passthrough attributes on
    <code>EnumDiscriminants</code> (<a
    href="https://redirect.github.com/Peternator7/strum/issues/461">#461</a>)</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/0ea1e2d0fd1460e7492ea32e6b460394d9199ff8"><code>0ea1e2d</code></a>
    docs: Fix typo (<a
    href="https://redirect.github.com/Peternator7/strum/issues/463">#463</a>)</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/36c051b91086b37d531c63ccf5a49266832a846d"><code>36c051b</code></a>
    Upgrade <code>phf</code> to v0.13 (<a
    href="https://redirect.github.com/Peternator7/strum/issues/465">#465</a>)</li>
    <li><a
    href="https://github.com/Peternator7/strum/commit/9328b38617dc6f4a3bc5fdac03883d3fc766cf34"><code>9328b38</code></a>
    Use absolute paths in proc macro (<a
    href="https://redirect.github.com/Peternator7/strum/issues/469">#469</a>)</li>
    <li>Additional commits viewable in <a
    href="https://github.com/Peternator7/strum/compare/v0.27.2...v0.28.0">compare
    view</a></li>
    </ul>
    </details>
    <br />
    
    
    [![Dependabot compatibility
    score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=strum_macros&package-manager=cargo&previous-version=0.27.2&new-version=0.28.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
    
    Dependabot will resolve any conflicts with this PR as long as you don't
    alter it yourself. You can also trigger a rebase manually by commenting
    `@dependabot rebase`.
    
    [//]: # (dependabot-automerge-start)
    [//]: # (dependabot-automerge-end)
    
    ---
    
    <details>
    <summary>Dependabot commands and options</summary>
    <br />
    
    You can trigger Dependabot actions by commenting on this PR:
    - `@dependabot rebase` will rebase this PR
    - `@dependabot recreate` will recreate this PR, overwriting any edits
    that have been made to it
    - `@dependabot show <dependency name> ignore conditions` will show all
    of the ignore conditions of the specified dependency
    - `@dependabot ignore this major version` will close this PR and stop
    Dependabot creating any more for this major version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this minor version` will close this PR and stop
    Dependabot creating any more for this minor version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this dependency` will close this PR and stop
    Dependabot creating any more for this dependency (unless you reopen the
    PR or upgrade to it yourself)
    
    
    </details>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    Co-authored-by: Eric Traut <etraut@openai.com>
  • image-gen-event/client_processing (#13512)
    enabling client-side to process with image-generation capabilities
    (setting app-server)
  • chore(deps): bump actions/download-artifact from 7 to 8 (#13208)
    Bumps
    [actions/download-artifact](https://github.com/actions/download-artifact)
    from 7 to 8.
    <details>
    <summary>Release notes</summary>
    <p><em>Sourced from <a
    href="https://github.com/actions/download-artifact/releases">actions/download-artifact's
    releases</a>.</em></p>
    <blockquote>
    <h2>v8.0.0</h2>
    <h2>v8 - What's new</h2>
    <h3>Direct downloads</h3>
    <p>To support direct uploads in <code>actions/upload-artifact</code>,
    the action will no longer attempt to unzip all downloaded files.
    Instead, the action checks the <code>Content-Type</code> header ahead of
    unzipping and skips non-zipped files. Callers wishing to download a
    zipped file as-is can also set the new <code>skip-decompress</code>
    parameter to <code>false</code>.</p>
    <h3>Enforced checks (breaking)</h3>
    <p>A previous release introduced digest checks on the download. If a
    download hash didn't match the expected hash from the server, the action
    would log a warning. Callers can now configure the behavior on mismatch
    with the <code>digest-mismatch</code> parameter. To be secure by
    default, we are now defaulting the behavior to <code>error</code> which
    will fail the workflow run.</p>
    <h3>ESM</h3>
    <p>To support new versions of the @actions/* packages, we've upgraded
    the package to ESM.</p>
    <h2>What's Changed</h2>
    <ul>
    <li>Don't attempt to un-zip non-zipped downloads by <a
    href="https://github.com/danwkennedy"><code>@​danwkennedy</code></a> in
    <a
    href="https://redirect.github.com/actions/download-artifact/pull/460">actions/download-artifact#460</a></li>
    <li>Add a setting to specify what to do on hash mismatch and default it
    to <code>error</code> by <a
    href="https://github.com/danwkennedy"><code>@​danwkennedy</code></a> in
    <a
    href="https://redirect.github.com/actions/download-artifact/pull/461">actions/download-artifact#461</a></li>
    </ul>
    <p><strong>Full Changelog</strong>: <a
    href="https://github.com/actions/download-artifact/compare/v7...v8.0.0">https://github.com/actions/download-artifact/compare/v7...v8.0.0</a></p>
    </blockquote>
    </details>
    <details>
    <summary>Commits</summary>
    <ul>
    <li><a
    href="https://github.com/actions/download-artifact/commit/70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3"><code>70fc10c</code></a>
    Merge pull request <a
    href="https://redirect.github.com/actions/download-artifact/issues/461">#461</a>
    from actions/danwkennedy/digest-mismatch-behavior</li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/f258da9a506b755b84a09a531814700b86ccfc62"><code>f258da9</code></a>
    Add change docs</li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/ccc058e5fbb0bb2352213eaec3491e117cbc4a5c"><code>ccc058e</code></a>
    Fix linting issues</li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/bd7976ba57ecea96e6f3df575eb922d11a12a9fd"><code>bd7976b</code></a>
    Add a setting to specify what to do on hash mismatch and default it to
    <code>error</code></li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/ac21fcf45e0aaee541c0f7030558bdad38d77d6c"><code>ac21fcf</code></a>
    Merge pull request <a
    href="https://redirect.github.com/actions/download-artifact/issues/460">#460</a>
    from actions/danwkennedy/download-no-unzip</li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/15999bff51058bc7c19b50ebbba518eaef7c26c0"><code>15999bf</code></a>
    Add note about package bumps</li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/974686ed5098c7f9c9289ec946b9058e496a2561"><code>974686e</code></a>
    Bump the version to <code>v8</code> and add release notes</li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/fbe48b1d2756394be4cd4358ed3bc1343b330e75"><code>fbe48b1</code></a>
    Update test names to make it clearer what they do</li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/96bf374a614d4360e225874c3efd6893a3f285e7"><code>96bf374</code></a>
    One more test fix</li>
    <li><a
    href="https://github.com/actions/download-artifact/commit/b8c4819ef592cbe04fd93534534b38f853864332"><code>b8c4819</code></a>
    Fix skip decompress test</li>
    <li>Additional commits viewable in <a
    href="https://github.com/actions/download-artifact/compare/v7...v8">compare
    view</a></li>
    </ul>
    </details>
    <br />
    
    
    [![Dependabot compatibility
    score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/download-artifact&package-manager=github_actions&previous-version=7&new-version=8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
    
    Dependabot will resolve any conflicts with this PR as long as you don't
    alter it yourself. You can also trigger a rebase manually by commenting
    `@dependabot rebase`.
    
    [//]: # (dependabot-automerge-start)
    [//]: # (dependabot-automerge-end)
    
    ---
    
    <details>
    <summary>Dependabot commands and options</summary>
    <br />
    
    You can trigger Dependabot actions by commenting on this PR:
    - `@dependabot rebase` will rebase this PR
    - `@dependabot recreate` will recreate this PR, overwriting any edits
    that have been made to it
    - `@dependabot show <dependency name> ignore conditions` will show all
    of the ignore conditions of the specified dependency
    - `@dependabot ignore this major version` will close this PR and stop
    Dependabot creating any more for this major version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this minor version` will close this PR and stop
    Dependabot creating any more for this minor version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this dependency` will close this PR and stop
    Dependabot creating any more for this dependency (unless you reopen the
    PR or upgrade to it yourself)
    
    
    </details>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Log non-audio realtime events (#13516)
    Improve observability of realtime conversation event handling by logging
    non-audio events with payload details in the event loop, while skipping
    audio-out events to reduce noise.
  • plugin: support local-based marketplace.json + install endpoint. (#13422)
    Support marketplace.json that points to a local file, with
    ```
        "source":
        {
            "source": "local",
            "path": "./plugin-1"
        },
     ```
     
     Add a new plugin/install endpoint which add the plugin to the cache folder and enable it in config.toml.
  • Prefix handoff messages with role (#13505)
    Format handoff context by prefixing each message with its role (for
    example "user:" and "assistant:") before forwarding to the agent.
  • 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
  • feat(app-server-test-client): OTEL setup for tracing (#13493)
    ### Overview
    This PR:
    - Updates `app-server-test-client` to load OTEL settings from
    `$CODEX_HOME/config.toml` and initializes its own OTEL provider.
    - Add real client root spans to app-server test client traces.
    
    This updates `codex-app-server-test-client` so its Datadog traces
    reflect the full client-driven flow instead of a set of server spans
    stitched together under a synthetic parent.
    
    Before this change, the test client generated a fake `traceparent` once
    and reused it for every JSON-RPC request. That kept the requests in one
    trace, but there was no real client span at the top, so Datadog ended up
    showing the sequence in a slightly misleading way, where all RPCs were
    anchored under `initialize`.
    
    Now the test client:
    - loads OTEL settings from the normal Codex config path, including
    `$CODEX_HOME/config.toml` and existing --config overrides
    - initializes tracing the same way other Codex binaries do when trace
    export is enabled
    - creates a real client root span for each scripted command
    - creates per-request client spans for JSON-RPC methods like
    `initialize`, `thread/start`, and `turn/start`
    - injects W3C trace context from the current client span into
    request.trace instead of reusing a fabricated carrier
    
    This gives us a cleaner trace shape in Datadog:
    - one trace URL for the whole scripted flow
    - a visible client root span
    - proper client/server parent-child relationships for each app-server
    request
  • feat: external artifacts builder (#13485)
    This PR reverts the built-in artifact render while a decision is being
    reached. No impact expected on any features
  • 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>
  • feat(core, tracing): add a span representing a turn (#13424)
    This is PR 3 of the app-server tracing rollout.
    
    PRs https://github.com/openai/codex/pull/13285 and
    https://github.com/openai/codex/pull/13368 gave us inbound request spans
    in app-server and propagated trace context through Submission. This
    change finishes the next piece in core: when a request actually starts a
    turn, we now create a core-owned long-lived span that stays open for the
    real lifetime of the turn.
    
    What changed:
    - `Session::spawn_task` can now optionally create a long-lived turn span
    and run the spawned task inside it
    - `turn/start` uses that path, so normal turn execution stays under a
    single core-owned span after the async handoff
    - `review/start` uses the same pattern
    - added a unit test that verifies the spawned turn task inherits the
    submission dispatch trace ancestry
    
    **Why**
    The app-server request span is intentionally short-lived. Once work
    crosses into core, we still want one span that covers the actual
    execution window until completion or interruption. This keeps that
    ownership where it belongs: in the layer that owns the runtime
    lifecycle.
  • allow apps to specify cwd for sandbox setup. (#13484)
    The electron app doesn't start up the app-server in a particular
    workspace directory.
    So sandbox setup happens in the app-installed directory instead of the
    project workspace.
    
    This allows the app do specify the workspace cwd so that the sandbox
    setup actually sets up the ACLs instead of exiting fast and then having
    the first shell command be slow.
  • add new scopes to login (#12383)
    Validated login + refresh flows. Removing scopes from the refresh
    request until we have upgrade flow in place. Confirmed that tokens
    refresh with existing scopes.