7919 Commits

  • log plugin MCP server names (#26002)
    ## Summary
    - emit the plugin capability summary's exact MCP server names in
    `codex_plugin_used`
    
    ## Test
    - `just test -p codex-analytics`
    - `just test -p codex-core
    explicit_plugin_mentions_track_plugin_used_analytics`
    - `just fix -p codex-analytics`
  • Use Windows setup marker as completion signal (#26074)
    # Why
    
    When an organization requires the elevated Windows sandbox, Codex
    launches an elevated helper to provision users, configure firewall and
    ACL rules, and lock persistent sandbox directories.
    
    We observed that closing the helper after setup started could leave the
    machine partially initialized while the TUI still announced **Sandbox
    ready**. Model-only turns continued to work, but the first shell command
    retried setup and failed with Windows cancellation error `1223`.
    
    This was not an enforcement bypass; command execution continued to fail
    closed. The issue was a false readiness signal: `setup_marker.json` was
    written during user provisioning, before the remaining setup stages had
    completed.
    
    # What
    
    Treat `setup_marker.json` as the commit record for Windows sandbox
    setup:
    
    1. Before full or provisioning setup begins, remove the existing marker
    and create the final marker path with a protected ACL.
    2. Keep the marker empty and therefore invalid while setup is in
    progress. Sandbox users cannot read, modify, or replace it.
    3. Run every synchronous setup stage.
    4. After setup succeeds, write the valid marker contents without
    changing its ACL.
    5. After the helper exits successfully, verify the existing readiness
    check before enabling the sandbox.
    
    If setup is canceled or fails, the marker remains invalid and Codex
    reports setup as incomplete instead of announcing readiness.
    
    Refresh-only and read-ACL-only helper runs continue to leave the marker
    untouched. The setup version remains `5` to avoid forcing all existing
    Windows users through elevated setup again.
    
    # Verification
    
    - Added coverage confirming sandbox users cannot read or modify the
    setup marker after elevated setup.
    - Added coverage confirming a successful helper exit without complete
    setup artifacts is rejected.
    - Ran `just test -p codex-windows-sandbox`.
  • codex-pr-body: avoid confidential references (#26260)
    ## Why
    
    PR descriptions can be visible outside the context used to generate
    them. In #23710, a generated description referenced an internal
    document, showing that the skill needs an explicit guardrail against
    exposing confidential context.
    
    ## What changed
    
    - Updated the `codex-pr-body` guidance to prohibit confidential
    references, including codenames and OpenAI-internal URLs.
  • Rewrite oversized tool outputs during remote compaction (#26251)
    ## Why
    
    When trying to fit history under compaction limit rewrite output items
    instead of removing them entirely. Otherwise we're breaking
    incrementality in relation to the previous response.
  • feat: catalog multi-agent v2 config (#26254)
    ## Why
    
    Model metadata can now select multi-agent v2 even when a user has not
    enabled `features.multi_agent_v2` in their config. Some existing configs
    still set the legacy `agents.max_threads` knob for v1 multi-agent
    behavior, so treating every v2 runtime as incompatible with
    `agents.max_threads` would break users whose only v2 signal came from
    the model catalog.
    
    The incompatible configuration is specifically enabling
    `features.multi_agent_v2` while also setting `agents.max_threads`.
    Catalog-forced v2 should use the v2 concurrency setting and ignore the
    legacy v1 cap instead of rejecting the config.
    
    ## What changed
    
    - Split config validation from runtime concurrency calculation:
    `effective_agent_max_threads` now just returns the effective cap for the
    resolved multi-agent runtime.
    - Added explicit validation for `features.multi_agent_v2` +
    `agents.max_threads` at session startup.
    - Preserved catalog-selected v2 behavior when `features.multi_agent_v2`
    is disabled, so existing configs with `agents.max_threads` keep
    starting.
    - Updated model-runtime selector coverage so a catalog v2 model still
    exposes v2 tools even when `agents.max_threads` is set and the config
    flag is disabled.
    
    ## Validation
    
    - `cargo check -p codex-core --lib`
    - `just test -p codex-core --lib -E
    "test(multi_agent_v2_feature_rejects_agents_max_threads) |
    test(catalog_v2_allows_agents_max_threads_when_feature_disabled)"`
  • [codex] Split Python runtime release workflow (#26226)
    ## Why
    
    Python SDK releases pin an exact `openai-codex-cli-bin` version, so all
    eight platform runtime wheels must be available on PyPI before the SDK
    package is built and published. PyPI does not support reusable workflows
    as Trusted Publishers, which means OIDC-backed publishing must run from
    each top-level release workflow.
    
    ## What changed
    
    - add reusable `python-runtime-build.yml` to prepare and upload all
    eight runtime wheels without publishing
    - add top-level `python-runtime-release.yml` for manual runtime
    publication before updating an SDK pin
    - have `python-sdk-release.yml` publish and verify the prepared runtime
    wheels from its own top-level trusted job before building the SDK
    - verify PyPI exposes exactly the expected eight runtime wheels before
    either release workflow continues
    
    ## PyPI configuration
    
    - keep the trusted publisher for
    `.github/workflows/python-sdk-release.yml` with environment `pypi`
    - add a trusted publisher for
    `.github/workflows/python-runtime-release.yml` with environment `pypi`
    - no trusted publisher is needed for
    `.github/workflows/python-runtime-build.yml`
    
    ## Validation
    
    - parsed all three workflow YAML files
    - validated all embedded shell blocks with `bash -n`
    - no local tests run; relying on online CI
  • Restore Windows coverage for code-mode image generation exposure (#25960)
    ## Summary
    
    Restore Windows coverage for standalone image generation in code mode.
    
    The previous test executed a V8-backed code-mode cell on Windows CI,
    where that runtime path is intentionally excluded because it is
    unreliable. The test was then ignored entirely on Windows, removing
    useful coverage.
    
    This splits the test into two checks:
    
    - All platforms verify that `image_gen__imagegen` is exposed to the
    model when image generation is configured for code mode only.
    - Non-Windows platforms continue to execute the full V8-backed flow and
    verify that the nested image-generation call succeeds.
    
    ## Verification
    
    - `just fmt`
    - `git diff --check`
    - `just test -p codex-app-server standalone_image_generation`
    
    Result: 3 tests passed, plus the required bench smoke check.
  • Fix forked thread name inheritance (#26075)
    Fixes #25950.
    
    ## Why
    Forking a renamed thread could fall back to the source thread's
    first-prompt title because the fork path did not preserve the source's
    explicit name. That meant fork-of-renamed-fork flows could show stale
    sidebar labels even though the user had renamed the parent.
    
    ## What changed
    `thread/fork` now reads the source thread's distinct `name`, normalizes
    it, persists it onto materialized forks, and applies it to the returned
    API thread. Because the source `name` already excludes first-prompt
    pseudo-titles, forks inherit only an explicit user rename instead of
    stale generated metadata.
  • [profile-switcher][rust] -- [1/2] Add app-server account session protocol (#25469)
    ## Summary
    
    Adds the app-server v2 `accountSession/*` protocol used by the Desktop
    profile switcher and the backend account metadata client needed to
    populate workspace choices.
    
    This is the protocol layer only. The app-server lifecycle and
    consolidated saved-session storage are split into a follow-up PR.
    
    ## Rust Stack
    
    1. This PR
    2. [openai/codex#25383](https://github.com/openai/codex/pull/25383) adds
    app-server session lifecycle behavior and consolidated saved-session
    storage.
    
    ## Validation
    
    - Generated app-server schema fixtures are included from the existing
    generation flow in the lifecycle PR where the routes are registered.
    - Did not run tests per requested scope.
  • Expose local image paths to models (#25944)
    ## Why
    
    Local image attachments include image bytes, but the adjacent
    model-visible label omits the source path. Exposing the path lets
    model-selected workflows refer back to the intended local image
    explicitly.
    
    ## What changed
    
    - Include an escaped `path` attribute in model-visible local image
    opening tags.
    - Reuse the path-aware marker generator in rollout coverage.
    - Update protocol, replay, and rollout coverage for the new request
    shape.
    
    ## Validation
    
    - `just fmt`
    - `just test -p codex-protocol`
    - `just test -p codex-core skips_local_image_label_text`
    - `just test -p codex-core
    copy_paste_local_image_persists_rollout_request_shape`
    - `git diff --check`
  • Preserve remote plugin default prompts (#25887)
    ## Summary
    
    - Read `default_prompts` from remote plugin release metadata.
    - Prefer the plural prompt list over legacy `default_prompt`.
    - Fall back to `default_prompt` as a single-item list for backward
    compatibility.
    
    ## Testing
    
    - `just test -p codex-core-plugins`
    - `just test -p codex-app-server`
  • [codex] Pin Python SDK to runtime 0.137.0a4 (#26216)
    ## Summary
    - pin the Python SDK runtime to `openai-codex-cli-bin==0.137.0a4`
    - refresh generated protocol artifacts from `rust-v0.137.0-alpha.4`
    - refresh `sdk/python/uv.lock` with all eight published runtime wheels
    
    ## Runtime publication
    - published `openai-codex-cli-bin==0.137.0a4` through the
    `python-sdk-release` workflow
    - includes macOS, manylinux, musllinux, and Windows wheels
    - publication run:
    https://github.com/openai/codex/actions/runs/26905608531
    
    ## Validation
    - ran `just fmt`
    - generated artifacts from the `rust-v0.137.0-alpha.4` release wheel
    - ran `uv lock --check --default-index https://pypi.org/simple`
    - did not run tests locally, per request; CI provides the test signal
  • [codex] Copy user Bazel settings into Codex worktrees (#25925)
    ## Why
    
    Codex-created linked worktrees do not include ignored files from the
    main worktree. Bazel users who keep local overrides in `user.bazelrc`
    therefore lose those settings in every new worktree.
    
    The setup must also work on Windows and must not overwrite a file that
    already exists in the worktree.
    
    ## What changed
    
    The checked-in Codex environment now invokes
    `.codex/environments/setup.py`. The script resolves the main worktree
    and current worktree, then uses
    `copy_from_main_worktree_to_worktree(repo_relative_path)` to copy
    ignored files into new worktrees without overwriting existing
    destinations.
    
    `main()` currently copies `user.bazelrc`. Additional repository-relative
    paths can be added as further calls to the same helper.
    
    ## Validation
    
    - Ran the setup script in a linked worktree and confirmed it handles a
    missing main-worktree `user.bazelrc`.
    - Verified the helper copies a main-worktree file, preserves an existing
    worktree file, and creates parent directories for a nested path.
  • core: stop threading SandboxPolicy through exec (#25700)
    ## Why
    
    #25450 attempts a broad `SandboxPolicy` removal across several unrelated
    surfaces, which makes it hard to review and still leaves new helper code
    moving legacy policies around. This PR is a narrower alternative:
    migrate only the exec-side Windows sandbox plumbing so the review can
    focus on one production path and one compatibility boundary.
    
    The goal is to stop threading `SandboxPolicy` through exec code without
    expanding the migration into app-server, protocol, telemetry, config, or
    session behavior.
    
    ## What changed
    
    - Removed `ExecRequest::compatibility_sandbox_policy()`.
    - Changed the Windows restricted-token and elevated filesystem override
    helpers to accept `PermissionProfile` plus the split filesystem/network
    policies instead of a `SandboxPolicy`.
    - Kept the remaining legacy projection local to the writable-root
    comparison that still needs to compare split policy behavior against the
    legacy Windows backend model.
    - Rejected restricted split filesystem policies that still grant
    full-disk writes before using the Windows restricted-token backend,
    preserving the previous clear-failure behavior for profiles that project
    to `ExternalSandbox`.
    - Updated the Windows sandbox override tests to exercise the new call
    shape and cover the full-write split-profile regression.
    
    ## Verification
    
    - `just test -p codex-core windows_restricted_token`
    - `just test -p codex-core windows_elevated`
  • Fix multiline paste in /goal edit (#26047)
    Fixes #26025.
    
    ## Why
    `/goal edit` opens `CustomPromptView`, which did not use the paste-burst
    handling that protects the main composer when terminals deliver paste as
    rapid key events. On Windows terminals, the first pasted newline could
    be treated as Enter-to-submit, truncating the goal edit and leaving the
    rest of the paste behind.
    
    ## What
    This reuses `PasteBurst` in `CustomPromptView` as a lightweight
    Enter-suppression detector for paste-like key streams. Characters still
    insert directly, explicit paste still goes through the view paste path,
    and ordinary text entry still submits on Enter.
  • feat: guard git enrichment (#26175)
    Skip turn git metadata enrichment when a turn has remote or multiple
    executors, so we do not report the orchestrator checkout as executor
    workspace metadata.
    
    Test: `just test -p codex-core` (blocked by existing
    `Session::conversation_id` compile error in `close_agent.rs`).
  • nit: small prompt update for MAv2 (#26179)
    Simple prompt change for MAv2 because of OOD compared to CBv9
  • [codex] Restore setup helper UAC manifest (#25949)
    ## Why
    
    #23764 removed Windows resource stamping from `codex-windows-sandbox`,
    but it also removed the setup helper's UAC manifest. That manifest was
    doing more than cosmetic version metadata: Microsoft documents
    `requestedExecutionLevel level="asInvoker"` as the setting that makes an
    executable run at the same permission level as the process that started
    it:
    https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#trustinfo
    
    In the reported session, `codex-windows-sandbox-setup.exe` was launched
    for a non-elevated setup refresh and `CreateProcess` failed with `os
    error 740` (`The requested operation requires elevation`). Restoring an
    explicit `asInvoker` manifest records the helper's intended default
    launch contract: normal launches inherit the caller's token, and
    elevation only happens through the code paths that request it
    explicitly.
    
    The setup helper has two launch modes:
    
    - setup refresh uses a normal `Command::new(...)` spawn and should never
    trigger UAC
    - full setup explicitly uses `ShellExecuteExW` with the `runas` verb
    when elevation is required
    
    Restoring `asInvoker` keeps refresh non-elevated by default while
    preserving the explicit elevated path for full setup.
    
    ## What changed
    
    - Restored a minimal `codex-windows-sandbox-setup.manifest` containing
    only `requestedExecutionLevel level="asInvoker"`.
    - Added a small build script that passes setup-helper-scoped manifest
    linker args for MSVC and the Windows GNU/LLVM target used by Bazel.
    - Wired the manifest into Bazel build-script data.
    
    This does not restore `winres`, `FileDescription`, `ProductName`, or
    package-wide resource stamping, so other Codex binaries that link
    `codex-windows-sandbox` do not inherit metadata from this package.
    
    ## Verification
    
    - `cargo fmt -p codex-windows-sandbox`
    - `cargo build -p codex-windows-sandbox --bin
    codex-windows-sandbox-setup`
    - `cargo build -p codex-windows-sandbox --bin codex-command-runner`
    - `cargo build -p codex-windows-sandbox --lib`
    - Build-script output simulation for `CARGO_CFG_TARGET_ENV=msvc` emits
    `/MANIFEST:EMBED` and `/MANIFESTINPUT:<manifest>`.
    - Build-script output simulation for `CARGO_CFG_TARGET_ENV=gnu` +
    `CARGO_CFG_TARGET_ABI=llvm` emits `-Wl,-Xlink=/manifest:embed` and
    `-Wl,-Xlink=/manifestinput:<manifest>`.
    - Inspected the built binaries and confirmed:
    - `codex-windows-sandbox-setup.exe` contains `requestedExecutionLevel` /
    `asInvoker`
      - `codex-command-runner.exe` does not contain those manifest strings
    - Windows `VersionInfo` remains blank for `FileDescription` /
    `ProductName`
    - `just test -p codex-windows-sandbox` ran through Nextest, with 114
    passing, 2 skipped, and 1 existing Windows sandbox failure:
    `unified_exec::tests::legacy_non_tty_cmd_emits_output` fails with
    `CreateRestrictedToken failed: 87`.
  • Implement v1 skills extension prompt injection (#26167)
    ## Why
    
    The skills extension needs a real turn-time path before host, executor,
    or remote skills can be routed through it. The previous code was mostly
    a placeholder catalog/provider sketch, so there was no bounded
    available-skills fragment, no source-owned `SKILL.md` read, and no place
    for warnings or per-turn selection state to live.
    
    This PR makes `ext/skills` the authority-preserving flow for listing
    candidate skills and injecting only explicitly selected main prompts,
    without adding more of that logic to `codex-core`.
    
    ## What changed
    
    - Expands catalog entries with `main_prompt`, display path, short
    description, dependency metadata, enabled/prompt visibility flags, and
    authority/package-aware read requests.
    - Replaces the placeholder `providers/*` modules with
    `SkillProviderSource` and `SkillProviders`, routing list/read/search
    calls by source kind and surfacing provider failures as warnings.
    - Adds bounded available-skills rendering and `SKILL.md` main-prompt
    truncation before the fragments enter model context.
    - Resolves explicit skill selections from structured `UserInput::Skill`,
    skill-file mentions, `skill://...` paths, and plain `$skill` text
    mentions, then reads selected prompts through their owning provider.
    - Stores mutable per-thread skills config and per-turn
    catalog/selection/warning state.
    - Adds `install_with_providers` so tests and future host wiring can
    supply concrete providers.
    
    ## Testing
    
    - Not run locally.
    - Added `codex-rs/ext/skills/tests/skills_extension.rs` coverage for
    available-catalog injection, selected prompt injection through the
    owning provider, and prompt-hidden skills that remain invokable.
  • chore: mechanical rename (#26156)
    Rename `Session::conversation_id` to `Session::thread_id` with an auto
    refactor in RustRover
  • fix: serialize goal progress accounting (#26155)
    ## Why
    
    Goal progress accounting can be reached from multiple completion paths
    for the same thread. Each path takes a progress snapshot, writes the
    usage delta, and then marks that snapshot as accounted. When two
    tool-completion hooks run at the same time, they can both observe the
    same unaccounted delta and charge it twice.
    
    ## What changed
    
    - Added a per-thread progress-accounting permit to
    `GoalAccountingState`.
    - Held that permit across the snapshot/write/mark-accounted critical
    section for active-turn, idle, and tool-finish accounting.
    - Added regression coverage for parallel tool-finish hooks so a shared
    token delta is charged once and only one progress event is emitted.
    
    ## Testing
    
    - Not run locally.
    - Added `parallel_tool_finish_accounts_active_goal_progress_once`.
  • skills: resolve per-turn catalogs from turn input context (#26106)
    ## Why
    
    The skills extension needs the resolved turn environments to build a
    real per-turn `SkillListQuery`. The previous `TurnLifecycleContributor`
    hook only had a turn id, so it could only seed a placeholder query and
    never carry the executor authorities that executor-scoped skill routing
    will need.
    
    Moving catalog resolution onto `TurnInputContributor` puts the skills
    extension on the same turn-preparation path that already has the
    environment ids and working directories for the submitted turn, while
    keeping the actual prompt injection work for follow-up changes.
    
    ## What changed
    
    - switch `ext/skills` from `TurnLifecycleContributor` to
    `TurnInputContributor`
    - build `executor_authorities` from `TurnInputContext.environments` and
    pass them through `SkillListQuery`
    - keep storing the resolved catalog in `SkillsTurnState`, but drop the
    placeholder query helper that no longer matches the real data flow
    - update the extension TODOs to reflect that per-turn catalog resolution
    now happens in the turn-input contributor, and that prompt/context
    injection still needs to move later
    
    ## Testing
    
    - Not run locally.
  • Reject MAv2 close_agent self-targets (#26144)
    ## Why
    
    `close_agent` is a parent-owned coordination tool: a worker should
    return its result, then let its parent decide when to close it. Before
    this change, if an MAv2 worker targeted itself, the resolved target
    could flow through the normal close path and ask the agent control layer
    to close the current conversation.
    
    ## What changed
    
    - Reject `close_agent` when the resolved target is the current session's
    `conversation_id`, returning a model-visible error that tells the worker
    to return its result instead.
    - Keep the guard after target resolution so it covers both thread-id
    targets and task-path targets.
    - Add coverage for self-targeting by thread id and by task name in
    `multi_agents_tests.rs`.
    
    Relevant code:
    
    -
    [`handle_close_agent`](https://github.com/openai/codex/blob/7c24e6641b693a3eed933dd376ce8f424ab6ea5f/codex-rs/core/src/tools/handlers/multi_agents_v2/close_agent.rs#L39-L57)
    - [`multi_agent_v2_close_agent_rejects_self_target_by_id` /
    `multi_agent_v2_close_agent_rejects_self_target_by_task_name`](https://github.com/openai/codex/blob/7c24e6641b693a3eed933dd376ce8f424ab6ea5f/codex-rs/core/src/tools/handlers/multi_agents_tests.rs#L3936-L4070)
    
    ## Testing
    
    Not run locally.
  • chore: extract context fragments into dedicated crate (#26122)
    ## Why
    
    `codex-core` currently owns the generic contextual-fragment trait and
    several reusable fragment implementations. That makes it harder for
    other crates to share the same host-owned model-input abstraction
    without depending on all of `codex-core`.
    
    This change extracts the reusable fragment machinery into a small
    `codex-context-fragments` crate so future extension and skills work can
    depend on the fragment abstraction directly.
    
    ## What Changed
    
    - Added the `codex-context-fragments` crate with:
      - `ContextualUserFragment`
      - `FragmentRegistration` / `FragmentRegistrationProxy`
      - additional-context fragment types
    - Moved `SkillInstructions` into `codex-core-skills`, since
    skill-specific rendering belongs with skills rather than generic core
    context machinery.
    - Kept `codex-core` re-exporting the fragment types it still uses
    internally, so existing call sites keep the same shape.
    - Updated Cargo and Bazel workspace metadata for the new crate.
    
    ## Verification
    
    - `cargo metadata --locked --format-version 1 --no-deps`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
  • feat(app-server): add remote control client management RPCs (#25785)
    ## Why
    
    Remote-control clients need to list and revoke controller-device grants
    without enabling or enrolling the local relay. These are signed-in
    account-management operations, so coupling them to websocket, pairing,
    enrollment, or persisted relay state would prevent clients from managing
    stale grants from the picker.
    
    Related enhancement request: N/A. This adds the Codex app-server surface
    for the planned upstream environment-scoped revoke endpoint.
    
    ## What Changed
    
    - Added experimental app-server v2 RPCs:
      - `remoteControl/client/list`
      - `remoteControl/client/revoke`
    - Added picker-oriented protocol types and standard generated schema
    fixtures. The list response intentionally omits backend account id,
    enrollment status, and location fields.
    - Added `app-server-transport/src/transport/remote_control/clients.rs`
    for environment-scoped GET and DELETE requests. It builds escaped URL
    path segments, forwards optional pagination query fields, sends ChatGPT
    auth plus `chatgpt-account-id`, converts RFC3339 `last_seen_at` values
    to Unix seconds, accepts `204 No Content` revoke responses, and retries
    once after a `401`.
    - Extracted shared ChatGPT auth loading and recovery into
    `app-server-transport/src/transport/remote_control/auth.rs` so
    websocket, pairing, and client management use the same account-auth
    boundary.
    - Retained the configured remote-control base URL on
    `RemoteControlHandle` and resolve management URLs lazily, preserving
    deferred validation while relay startup is disabled.
    - Registered list as `global_shared_read("remote-control-clients")` and
    revoke as `global("remote-control-clients")`.
    
    ## Verification
    
    - Added transport coverage proving list and revoke work while relay
    state is disabled, IDs are escaped, picker-only fields are returned,
    timestamps are converted, revoke accepts `204`, auth headers are
    forwarded, `401` retries exactly once, `403` is not retried, and
    malformed list payloads retain decode context.
    - Added an app-server integration test proving both JSON-RPC methods
    work before relay enablement and successful revoke returns `{}`.
    - Regenerated and validated experimental and standard app-server schema
    fixtures.
  • Allow EDU accounts to fetch cloud config bundles (#25963)
    ## Summary
    
    Allow EDU ChatGPT workspaces to fetch cloud config bundles. The existing
    cloud config eligibility gate only allowed business-like and enterprise
    plans, which meant EDU admins could configure managed policies in the UI
    but the Codex client would skip fetching them.
    
    This keeps individual/pro and team-like usage-based plans excluded, and
    adds service-level coverage for both `edu` and `education` plan aliases.
    
    ## Validation
    
    - `just fmt`
    - `just test -p codex-cloud-config`
    - Built the Codex app locally, created a new EDU ChatGPT workspace, and
    verified config bundles can be fetched and are properly applied.
  • feat: add extension turn-input contributors (#25959)
    ## Disclaimer
    Do not use for now
    
    ## Why
    
    Extensions can already contribute prompt fragments and request same-turn
    item injection, but there was no host-owned hook for contributing
    structured `ResponseItem`s while Codex is assembling a new turn's
    initial model input. This change adds that seam so extensions can attach
    turn-local input that depends on the submitted user input and resolved
    turn environments without routing through prompt text or late injection.
    
    ## What changed
    
    - add `TurnInputContributor` to `codex_extension_api` and export the new
    `TurnInputContext` / `TurnInputEnvironment` types it receives
    - teach `ExtensionRegistry` to register and expose turn-input
    contributors alongside the existing extension hooks
    - call registered turn-input contributors from
    `core/src/session/turn.rs` while building the initial injected input for
    a turn, then append their returned `ResponseItem`s after the skill and
    plugin injections
  • config: express implicit sandbox defaults as permission profiles (#25926)
    ## Why
    
    `PermissionProfile` is becoming the default way to represent Codex
    permissions, but the implicit default behavior should stay the same for
    now:
    
    - trusted projects use `:workspace`
    - untrusted projects also use `:workspace`
    - roots without a trust decision use `:read-only`
    - unsandboxed Windows falls back to `:read-only`
    
    This keeps the existing sandbox semantics while making silent config
    defaults observable as built-in permission profiles instead of treating
    the legacy `SandboxPolicy` projection as the primary shape.
    
    ## What Changed
    
    - Refactored legacy sandbox derivation to resolve the configured sandbox
    mode once, then apply the implicit project fallback only when no sandbox
    mode was configured.
    - Preserved the existing trust-decision fallback: trusted and untrusted
    projects default to workspace-write where supported.
    - Added empty-config coverage asserting that an untrusted project
    resolves to the built-in active permission profile (`:workspace` outside
    unsandboxed Windows).
    
    ## Verification
    
    - `just fmt`
    - `just test -p codex-core 'config::'`
    - `just test -p codex-config`
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/25926).
    * __->__ #25926
  • [codex] Fix Windows BuildBuddy Bazel wrapper execution (#25915)
    ## Why
    
    #25156 moved Bazel CI launches into a shared Python wrapper. On Windows,
    launching Bazel with `os.execvp` can split the spaced
    `--test_env=PATH=...` argument and fail to propagate the eventual Bazel
    exit status, allowing jobs to pass without running tests. This reapplies
    the wrapper after #25909 with a Windows-safe launch path.
    
    ## What changed
    
    Use a waited `subprocess.run` launch on Windows while preserving
    `os.execvp` on Unix. Add a process-level regression test for spaced
    arguments and child exit status, and run it on Windows Bazel shard 1.
    
    ## Experiment
    
    To confirm Bazel was actually invoking tests, patch `87b61d0be6`
    temporarily added an intentionally failing `codex-core` unit test. Bazel
    failed on that sentinel on all three major platforms:
    
    - [Linux Bazel
    test](https://github.com/openai/codex/actions/runs/26841132773/job/79151062486)
    - [macOS Bazel
    test](https://github.com/openai/codex/actions/runs/26841132773/job/79151062362)
    - [Windows Bazel test shard
    1/4](https://github.com/openai/codex/actions/runs/26841132773/job/79151062155)
    
    The sentinel was removed after collecting this evidence. Windows Bazel
    [clippy](https://github.com/openai/codex/actions/runs/26841132773/job/79151062914)
    and [release
    verification](https://github.com/openai/codex/actions/runs/26841132773/job/79151062739)
    also passed.
    
    ## Validation
    
    After removing the sentinel, `just test -p codex-core` no longer
    reported it. The local run retained two unrelated environment-specific
    failures.
  • feat: add skills extension scaffold (#25953)
    ## Disclaimer
    This is only here for iteration purpose! Do not make any code rely on
    this
    
    ## Why
    
    Skills still live behind `codex-core` discovery and injection paths, but
    the extension system needs an authority-aware home before that logic can
    move. This adds that boundary without changing current skills behavior,
    and keeps host, executor, and remote skills distinct so future
    list/read/search flows do not collapse back to ambient local paths.
    
    ## What changed
    
    - Add the `codex-skills-extension` workspace/Bazel crate under
    `ext/skills`.
    - Define the initial catalog, authority, provider, and turn-state types
    for authority-bound skill packages and resources.
    - Register placeholder thread/config/prompt/turn lifecycle contributors
    plus host, executor, and remote provider aggregation points.
    - Capture the remaining extraction work as TODOs, including the missing
    extension API hooks needed for per-turn catalog construction and typed
    skill injection.
    - Keep plugins outside the runtime skills model: plugin-installed skills
    are treated as materialized host-owned skill sources once available.
    
    ## Verification
    
    - Not run locally.
  • [codex] Publish Python runtime wheels with Python SDK releases (#25906)
    ## Summary
    - stop publishing Python runtime wheels as a side effect of Rust
    releases
    - publish runtime wheels from the Python SDK release workflow, either
    explicitly before updating the SDK pin or immediately before a
    `python-v*` SDK release
    - resolve the runtime release from the requested version or the SDK
    package's exact `openai-codex-cli-bin` pin
    - build two musllinux-tagged wheels from the Rust-release Linux package
    archives alongside the six existing runtime wheels
    - validate SDK beta tags before any PyPI write
    
    ## Release configuration
    - update the `openai-codex-cli-bin` PyPI trusted publisher to trust
    `.github/workflows/python-sdk-release.yml` and the
    `publish-python-runtime` job
    
    ## Pin update flow
    - run the `python-sdk-release` workflow manually with the new runtime
    version before opening or updating the SDK pin PR
    - after the pin lands, a `python-v*` SDK tag republishes with
    `skip-existing: true` before publishing the SDK package
    
    ## Validation
    - ran `just fmt`
    - validated the edited workflow YAML
    - validated the embedded `publish-python-runtime` Bash with `bash -n`
    - validated manual `0.136.0 -> rust-v0.136.0` mapping
    - validated tag-driven `python-v0.1.0b3 -> 0.132.0 -> rust-v0.132.0`
    mapping
    - validated rejection of an invalid SDK tag before publication
    - confirmed `rust-v0.136.0` contains the two required Linux package
    archives
    - CI will provide the full test signal
  • Expose standalone image generation in code mode (#25923)
    ## Why
    
    Standalone image generation remained top-level-only in code-mode
    sessions.
    
    ## What changed
    
    - Change imagegen exposure from `DirectModelOnly` to `Direct`.
    - Keep direct-mode access while enabling nested code-mode access.
    - Add a focused regression test for the exposure contract.
    
    ## Validation
    
    - `just test -p codex-image-generation-extension`
  • config: remove dead profile sandbox fallback (#25943)
    ## Why
    
    `profile_sandbox_mode` was left over from the old selected legacy
    profile path. Production now always derives permissions without that
    value, and legacy profile contents are ignored, so keeping a parameter
    that is always `None` makes `derive_permission_profile` look like it
    still supports a fallback that no longer exists.
    
    ## What Changed
    
    - Removed the `profile_sandbox_mode` argument from
    `ConfigToml::derive_permission_profile`.
    - Updated the production caller and legacy sandbox-policy test helper to
    match.
    - Dropped the stale unselected legacy-profile sandbox test that only
    protected the removed fallback shape.
    
    ## Verification
    
    - `just test -p codex-config`
    - `just test -p codex-core 'config::'`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/25943).
    * #25926
    * __->__ #25943
  • Add remote request permissions integration coverage (#25867)
    ## Stack
    
    1. #25850 - Key request-permission grants by environment: stores and
    applies sticky permission grants per environment id.
    2. #25858 - Add `environmentId` to `request_permissions`: lets the model
    target a selected environment and resolves relative permission paths
    against it.
    3. #25862 - Propagate permission approval environment id: carries the
    selected environment id through approval events, app-server requests,
    TUI prompts, and delegate forwarding.
    4. This PR (#25867) - Add remote request permissions integration
    coverage: verifies the selected remote environment across request,
    approval, grant reuse, and exec.
    
    This PR is stacked on #25862 and should be reviewed after #25850,
    #25858, and #25862.
    
    ## Why
    
    The environment-scoped permission stack needs one end-to-end check that
    exercises the CCA-shaped path, not only unit-level parsing. This
    verifies that a model-sent `environmentId` on `request_permissions`
    reaches the approval event, stores the grant under the selected
    environment, and is reused by a later tool call in that same
    environment.
    
    ## What Changed
    
    - Adds a remote executor integration test for `request_permissions` with
    `environmentId: remote` and a relative write root.
    - Asserts the permission event reports the remote environment and cwd,
    and that the normalized grant resolves under the remote cwd.
    - Approves the grant, then runs a remote `exec_command` without explicit
    per-call permissions and verifies it completes without another exec
    approval and writes only in the remote filesystem.
    
    ## Verification
    
    - Not run locally per instruction.
    - `git diff --check`
  • [codex] Keep hosted tools visible in code-only mode (#25890)
    ## Why
    
    `code_mode_only` moved ordinary runtime tools behind `exec`, but it also
    hid hosted Responses tools. Hosted `web_search` and `image_generation`
    do not have a nested `exec` runtime path, so code-only sessions lost
    those capabilities entirely even when their existing provider, auth,
    model, and configuration gates passed.
    
    ## What changed
    
    - Keep hosted Responses tools top-level in `code_mode_only` sessions
    after their existing gates pass.
    - Preserve the existing nested-tool behavior for ordinary runtimes and
    the direct-only behavior for multi-agent v2 tools.
    - Add planner coverage for `code_mode_only` with default multi-agent v2
    settings, hosted live web search, and hosted image generation.
    
    ## Verification
    
    - Added focused regression coverage in
    `codex-rs/core/src/tools/spec_plan_tests.rs`.
    - Left execution to CI per repository workflow.
  • Split cloud config bundle service modules (#25668)
    ## Summary
    
    - Splits the monolithic `codex-cloud-config` implementation into focused
    modules.
    - Keeps behavior unchanged from the preceding config bundle runtime
    switch.
    
    ## Details
    
    This is the reviewability follow-up after the lineage-preserving
    migration PRs. The split separates backend transport, loader
    construction, cache handling, metrics, validation, service
    orchestration, and focused tests into named files.
    
    Verification: `just fmt`; `just test -p codex-cloud-config`.
  • core: stop passing legacy SandboxPolicy to guardian reviews (#25911)
    ## Why
    
    Guardian review turns already submit a read-only `PermissionProfile`,
    which is the permissions model the runtime should honor. Passing the
    equivalent legacy `SandboxPolicy` through `ThreadSettingsOverrides`
    keeps two representations of the same read-only constraint alive on this
    path and makes the guardian flow depend on compatibility plumbing that
    is being phased out.
    
    ## What Changed
    
    - Set `sandbox_policy` to `None` when the guardian review session
    submits its child `Op::UserInput`.
    - Keep `permission_profile: Some(PermissionProfile::read_only())` and
    `approval_policy: Some(AskForApproval::Never)`, so the guardian review
    remains read-only and cannot request approvals.
    - Remove the now-unused `SandboxPolicy` import and redundant comment
    from `codex-rs/core/src/guardian/review_session.rs`.
    
    ## Verification
    
    Not run locally; this is a narrow cleanup of redundant thread-settings
    override state.
  • fix: update image generation test helper rename (#25938)
    ## Summary
    - update the app-server image generation integration test to use
    `TestAppServer`
    - completes the test helper rename from #25701 for this newer test file
    
    ## Validation
    - `cargo fmt -- --config imports_granularity=Item`
    - `cargo check -p codex-app-server --test all`
    
    Note: `just fmt` ran Rust formatting but failed on Python/SDK formatting
    because the sandbox could not access the local `uv` cache.
  • Switch runtime to cloud config bundle (#24622)
    ## Summary
    
    - Adapts the moved `codex-cloud-config` crate from the legacy cloud
    requirements endpoint to the new config bundle endpoint.
    - Switches runtime consumers from `CloudRequirementsLoader` to
    `CloudConfigBundleLoader` so one shared bundle supplies cloud-delivered
    config and requirements.
    - Removes the legacy cloud requirements domain loader path.
    
    ## Details
    
    This intentionally keeps `codex-cloud-config` monolithic for review
    lineage: the previous PR establishes the crate move, and this PR shows
    the behavior change against that moved implementation. A follow-up PR
    splits the module back into focused files.
    
    The new bundle path preserves the important cloud requirements loader
    semantics where intended: account-scoped signed cache, 30 minute TTL, 5
    minute refresh cadence, retry/backoff, auth recovery, and fail-closed
    startup loading. The cached payload changes from a single requirements
    TOML string to the backend-delivered bundle, and validation rejects
    malformed config or requirements fragments before cache write/use.
  • Populate workspace kind on Codex turn events (#25135)
    ## Summary
    - carry `workspace_kind` from Responses API client metadata into the
    turn resolved analytics fact
    - serialize the optional value on `codex_turn_event`
    - cover both the turn metadata source and turn event serialization
    
    The `workspace_kind` tells us whether a thread had a project attached vs
    projectless. this is an indicator for who is adopting Codex for
    knowledge work outside of coding
    
    ## Testing
    - `env UV_CACHE_DIR=/private/tmp/uv-cache
    /private/tmp/cargo-tools/bin/just fmt`
    - `env PATH=/private/tmp/cargo-tools/bin:$PATH
    CARGO_HOME=/private/tmp/cargo-home UV_CACHE_DIR=/private/tmp/uv-cache
    /private/tmp/cargo-tools/bin/just test -p codex-analytics`
    - `env PATH=/private/tmp/cargo-tools/bin:$PATH
    CARGO_HOME=/private/tmp/cargo-home UV_CACHE_DIR=/private/tmp/uv-cache
    /private/tmp/cargo-tools/bin/just test -p codex-core turn_metadata`
    
    Paired with openai/openai#970661, which keeps forwarding the same
    metadata key through Responses API headers.
  • Fix Windows running thread resume path normalization (#25509)
    ## Why
    
    Fixes #24944.
    
    On Windows, app-server resume could reject an active running thread when
    the requested session path used normal `C:\...` form and the
    already-running path used verbatim `\\?\C:\...` form. The paths point at
    the same JSONL file, but the resume stale-path guard compared raw
    `PathBuf`s, so desktop resume and heartbeat flows could fail with a
    mismatched-path error.
    
    ## What Changed
    
    - Compare requested and active rollout paths with
    `path_utils::paths_match_after_normalization`.
    - Extend the existing running-thread mismatched-path test with a
    Windows-only same-file resume case before the stale-path rejection.
    
    ## Verification
    
    - `just test -p codex-app-server
    thread_resume_rejects_mismatched_path_for_running_thread_id`
  • Use environment secrets for Azure signing (#24859)
    ## Summary
    - Move Azure Trusted Signing values out of reusable workflow-call
    secrets and into the `azure-artifact-signing` environment scope
    - Attach the Windows signing job to the `azure-artifact-signing`
    environment so it can resolve the signing secrets directly
    - Stop inheriting caller secrets for the Windows release reusable
    workflow
    
    ## Validation
    - `git diff --check -- .github/workflows/rust-release.yml
    .github/workflows/rust-release-windows.yml`
    - `ruby -e 'require "yaml"; ARGV.each { |path| YAML.load_file(path);
    puts "ok #{path}" }' .github/workflows/rust-release.yml
    .github/workflows/rust-release-windows.yml`
  • [codex] Pin Python SDK to glibc-compatible runtime (#25907)
    ## Summary
    - pin the Python SDK runtime package to `openai-codex-cli-bin==0.136.0`
    so Ubuntu/glibc installs resolve a compatible wheel
    - refresh generated SDK artifacts and lock data for the runtime update
    - keep newly generated client-message-id wire models internal to the
    generated protocol layer
    
    ## Dependency
    - merge #25906 first so the Python SDK release publishes both manylinux
    and musllinux runtime wheels before publishing the package with this pin
    
    ## Validation
    - ran `just fmt`
    - regenerated the Python public API helpers
    - validated the edited workflow YAML
    - CI passed 29/29 checks
  • Propagate permission approval environment id (#25862)
    ## Stack
    
    1. #25850 - Key request-permission grants by environment: stores and
    applies sticky permission grants per environment id.
    2. #25858 - Add `environmentId` to `request_permissions`: lets the model
    target a selected environment and resolves relative permission paths
    against it.
    3. This PR (#25862) - Propagate permission approval environment id:
    carries the selected environment id through approval events, app-server
    requests, TUI prompts, and delegate forwarding.
    4. #25867 - Add remote request permissions integration coverage:
    verifies the selected remote environment across request, approval, grant
    reuse, and exec.
    
    This PR is stacked on #25858, and #25867 is stacked on this PR.
    
    ## Why
    
    PR2 lets the model bind a `request_permissions` call to a selected
    environment, but the approval event and client-facing request still
    needed to carry that binding. For CCA, the user-facing prompt and
    delegated approval path should know which environment the grant applies
    to instead of relying on cwd alone.
    
    ## What Changed
    
    - Added optional `environmentId` to `RequestPermissionsEvent`.
    - Emit the selected environment id from core permission approval events.
    - Preserve the environment id through delegate forwarding, including
    cwd-based delegated requests.
    - Added `environmentId` to app-server permission approval params,
    generated schema/TypeScript artifacts, and README examples.
    - Preserve and display the environment id in TUI permission approval
    prompts.
    - Updated focused core, app-server protocol, and TUI conversion
    coverage.
    
    ## Testing
    
    Not run locally per instruction. Performed read-only `git diff --check`.
  • Fix Windows release PDB staging (#25916)
    ## Summary
    - Teach the Windows release prebuild staging step to locate Rust/MSVC
    PDBs emitted with crate-style underscore names.
    - Stage PDBs under the shipped hyphenated binary names so the downstream
    symbol archive step keeps the same artifact contract.
    - Keep a fallback for already-hyphenated PDB names and fail with a clear
    diagnostic if neither form exists.
    
    ## Root cause
    The recent symbol publishing change in #25649 started copying
    `${binary}.pdb` from `target/<triple>/release` during Windows prebuild
    staging. Cargo still emits the `.exe` with the hyphenated binary name,
    but MSVC PDBs for hyphenated Rust crates are emitted with underscores,
    for example `codex_app_server.pdb` for `codex-app-server.exe`. The
    release workflow was still building into the expected directory; the new
    PDB copy step was looking for the wrong filename.
    
    ## Impact
    This unblocks the `rust-release` Windows prebuilt-binary jobs for
    hyphenated binaries while preserving the hyphenated PDB names consumed
    by the final Windows release packaging and symbol archive steps.
    
    ## Validation
    - `just fmt` from `codex-rs`
    - `git diff --check -- .github/workflows/rust-release-windows.yml`
    - Parsed `.github/workflows/rust-release-windows.yml` as YAML locally
    - Local bash staging sanity test for both underscore-emitted and
    hyphenated PDB filenames
  • Route standalone image generation through host finalization md (#25176)
    ## Why
    
    Standalone image-generation extensions emitted turn items through the
    low-level event path, bypassing host-owned finalization such as image
    persistence and contributor processing. At the same time, the
    generated-image save-path hint must remain visible to the model through
    the extension tool's `FunctionCallOutput`, rather than the legacy
    built-in developer-message path.
    
    ## What changed
    
    - Extended `ExtensionTurnItem` to support image-generation items while
    keeping the extension-facing emitter API limited to `emit_started` and
    `emit_completed`.
    - Routed extension completion through core `finalize_turn_item`, so
    standalone image-generation items receive host-owned processing and
    persisted `saved_path` values before publication.
    - Kept legacy built-in image generation on its existing
    developer-message hint path, while standalone image generation returns
    its deterministic saved-path hint in `FunctionCallOutput`.
    - Shared the image artifact path and output-hint formatting used by core
    and the image-generation extension.
    - Passed thread identity through extension tool calls so standalone
    image generation can construct the same intended artifact path as core.
    - Added an app-server integration test covering real standalone image
    generation, saved artifact publication, model-visible output hint
    wiring, and absence of the legacy developer-message hint.
    
    ## Validation
    
    - `just fmt`
    - `just test -p codex-image-generation-extension`
    - `just test -p codex-web-search-extension`
    - `just test -p codex-goal-extension`
    - `just test -p codex-memories-extension`
    - Targeted `codex-core` tests for image save history, extension
    completion finalization, and contributor execution
    - `just test -p codex-app-server
    standalone_image_generation_returns_saved_path_hint_to_model`
    - `just fix -p codex-core`
    - `just fix -p codex-image-generation-extension`
    - `just bazel-lock-update`
    - `just bazel-lock-check`