Commit Graph

832 Commits

  • Move cloud requirements crate to cloud config (#24621)
    ## Summary
    
    - Moves the existing `codex-cloud-requirements` crate to
    `codex-cloud-config`.
    - Updates workspace dependencies and imports to the new crate name.
    - Intentionally keeps runtime behavior unchanged: this still fetches the
    legacy cloud requirements endpoint.
    
    ## Details
    
    This PR exists to make the lineage obvious before the bundle migration.
    GitHub should show the old `codex-rs/cloud-requirements/src/lib.rs`
    implementation as moved to `codex-rs/cloud-config/src/lib.rs`, rather
    than as unrelated new code.
    
    The follow-up PR adapts this moved crate to the new config bundle API
    and switches runtime consumers over.
  • app-server: remove experimental persist_extended_history bool flag (#25712)
    ## Summary
    
    Remove the dead experimental `persistExtendedHistory` app-server flag
    and collapse rollout persistence to the single policy app-server already
    used.
    
    ## What Changed
    
    - Removed `persistExtendedHistory` from v2 thread start/resume/fork
    params and deleted its deprecation notice path.
    - Removed the persistence-mode enums and plumbing through core, rollout,
    and thread-store.
    - Made rollout filtering mode-free, keeping the existing limited
    persisted-history behavior.
    
    ## Test Plan
    
    - `just write-app-server-schema`
    - `cargo nextest run --no-fail-fast -p codex-app-server-protocol
    schema_fixtures`
    - `cargo nextest run --no-fail-fast -p codex-app-server
    thread_shell_command_history_responses_exclude_persisted_command_executions`
    - `cargo nextest run --no-fail-fast -p codex-rollout -p
    codex-thread-store`
    - final `rg` for removed flag/type names
  • Wire managed MITM CA trust into child env (#22668)
    ## Stack
    1. Parent PR: #18240 uses named MITM permissions config.
    2. This PR wires managed MITM CA trust into spawned child processes.
    
    ## Why
    When Codex terminates HTTPS for limited mode or MITM hooks, child HTTPS
    clients need to trust Codex's managed MITM CA. Exporting proxy URLs
    alone is not enough, but blindly replacing user CA settings would be
    wrong: it can break custom enterprise/test roots, leak unreadable CA
    files into generated bundles, or make the child env disagree with its
    sandbox policy.
    
    ## Summary
    1. Build immutable managed CA bundles under `$CODEX_HOME/proxy` that
    include native roots, the managed MITM CA, and only inherited or
    command-scoped CA bundles the child is allowed to read.
    2. Export curated CA env vars alongside managed proxy env vars while
    preserving user CA override semantics, including nested Codex
    `SSL_CERT_FILE` precedence.
    3. Thread generated CA bundle paths into child sandbox readable roots,
    including debug sandbox execution, so the exported env vars work inside
    sandboxed commands.
    4. Remove only Codex-generated MITM CA bundle env when a child
    intentionally drops managed proxying for escalation or no-proxy retry.
    5. Document the managed CA bundle behavior and cover env injection,
    per-child bundle generation, sandbox readable roots, and no-proxy
    cleanup in tests.
    
    ## Validation
    1. Ran `just test -p codex-network-proxy`.
    2. Ran `just test -p codex-protocol`.
    3. Ran `just fix -p codex-network-proxy -p codex-protocol`.
    4. Tried focused `codex-core` validation, but the crate currently fails
    to compile in `core/tests/suite/guardian_review.rs` because an existing
    `Op::UserInput` initializer is missing `additional_context`.
    
    ---------
    
    Co-authored-by: Eva Wong <evawong@openai.com>
  • [codex] Consolidate shared prompts in codex-prompts (#25151)
    ## Why
    
    `codex_core` is consistently a bottleneck for incremental builds during
    iteration. The simplest fix is to make the crate smaller.
    
    ## Summary
    
    `codex-core` owns several reusable prompt renderers and static prompt
    assets, which makes the crate harder to split apart.
    
    Rename `codex-review-prompts` to `codex-prompts` and move shared review,
    goal, permissions, compaction, realtime, hierarchical AGENTS.md, and
    `apply_patch` prompts into it. Move prompt-only tests and update
    consumers and `CODEOWNERS`.
    
    ## Validation
    
    - `just test -p codex-prompts -p codex-apply-patch`
    - `just test -p codex-core prompt_caching`
    - Bazel builds for the affected crates
  • Preserve plugin app manifest order (#25491)
    ## Summary
    - Preserve app declaration order when loading plugin .app.json files.
    - Keep plugin connector summaries in plugin app order after connector
    metadata is merged and filtered.
    - Add regression coverage for .app.json order and connector summary
    order.
    
    ## Validation
    - just fmt
    - just test -p codex-chatgpt
    connectors_for_plugin_apps_returns_only_requested_plugin_apps
    - just test -p codex-core-plugins
    effective_apps_preserves_app_config_order
    - just fix -p codex-core-plugins (passes with existing clippy
    large_enum_variant warning in core-plugins/src/manifest.rs)
    - just fix -p codex-chatgpt
    - just bazel-lock-update
    - just bazel-lock-check
  • Read compressed rollouts and materialize before append (#25087)
    ## Why
    
    Local rollout compression needs a cold `.jsonl.zst` representation
    without letting compressed physical paths leak into append-mode writers.
    The unsafe case is resume or metadata update code successfully reading a
    compressed rollout and then appending raw JSONL bytes to the zstd file.
    
    This PR folds the former #25088 materialization slice into the
    read-support PR so the reader changes and append-safety invariant land
    together.
    
    ## What Changed
    
    - Teach rollout readers, discovery, listing, search, and ID lookup to
    understand compressed `.jsonl.zst` rollouts.
    - Keep `.jsonl` as the logical/stored rollout path while allowing read
    paths to open either plain or compressed storage.
    - Materialize compressed rollouts back to plain `.jsonl` before
    append-mode writes, including resume and direct metadata append paths.
    - Preserve compressed-file permissions when materializing back to plain
    JSONL.
    - Refresh thread-store resolved rollout paths after compatibility
    metadata writes so reconciliation follows the materialized file.
    - Avoid treating transient compression temp files as real rollout lookup
    results.
    
    ## Remaining Stack
    
    #25089 remains the separate worker PR. It is based directly on this PR
    and stays behind the disabled `local_thread_store_compression` feature
    flag.
    
    The worker still has a broader coordination question: a resume or
    metadata update can race with background compression while a plain file
    is being replaced by `.jsonl.zst`. This PR handles the read and
    materialize-before-append primitives; it does not make the worker
    production-ready.
    
    ## Validation
    
    - `just test -p codex-rollout`
    - `just test -p codex-thread-store`
    - `just fix -p codex-rollout`
    - `just fix -p codex-thread-store`
    - `just bazel-lock-check`
  • Use templates for goal steering prompts (#25576)
    ## Why
    
    Goal steering prompts have grown into long inline Rust strings, which
    makes the authored prompt text hard to review and easy to damage while
    changing the surrounding plumbing. Moving those prompts into embedded
    Markdown templates keeps the policy text in the shape reviewers actually
    read, while preserving the existing runtime substitution and objective
    escaping behavior.
    
    ## What changed
    
    - Added `ext/goal/templates/goals/continuation.md`, `budget_limit.md`,
    and `objective_updated.md` for the three goal steering prompts.
    - Updated `ext/goal/src/steering.rs` to parse those embedded templates
    once with `codex-utils-template` and render the existing goal values
    into them.
    - Kept user objectives XML-escaped before rendering and converted budget
    counters into template variables.
    - Added the template directory to `ext/goal/BUILD.bazel` `compile_data`
    so Bazel has the same embedded prompt inputs as Cargo.
    
    ## Testing
    
    - Not run locally.
  • code-mode: introduce durable session interface (#24180)
    ## Summary
    
    Introduce a `CodeModeSession` interface for executing and managing
    code-mode cells.
    
    This moves cell lifecycle, callback delegation, termination, and
    shutdown behind a session abstraction, while continuing to use the
    existing in-process implementation, and the ability to implement an
    external process one behind this interface.
    
    A Codex session owns one `CodeModeSession`, which in turn owns its
    running cells and stored code-mode state. Each cell is represented to
    the caller as a `StartedCell`, exposing its cell ID and initial
    response.
    
    It also introduces a `CodeModeSessionDelegate` callback interface. A
    session uses the delegate to invoke nested host tools and emit
    notifications while a cell is running, allowing the runtime to
    communicate with its owning Codex session without depending directly on
    core turn handling.
    
    <img width="2121" height="1001" alt="image"
    src="https://github.com/user-attachments/assets/c349a819-2a59-485c-bda4-2caf68ac4c31"
    />
  • Route extension image generation through the native image completion pipeline (#24972)
    ## Why
    
    The standalone `image_gen.imagegen` extension should behave like native
    image generation for artifact persistence and UI completion, while
    returning its save-location guidance as part of the tool result instead
    of injecting a developer message.
    
    ## What Changed
    
    - Added an image-generation completion hook for extension tools so core
    can persist generated images and emit the existing `ImageGeneration`
    lifecycle events.
    - Reused core image artifact persistence for extension output and
    removed extension-local save-path/file-writing logic.
    - Split shared image persistence from built-in finalization so native
    image generation keeps its existing developer-message instruction
    behavior.
    - Returned the generated image save-location instruction through the
    extension `FunctionCallOutput`, alongside the generated image input for
    model follow-up.
    - Preserved the existing image-generation event shape for current UI and
    replay compatibility.
    - Avoided cloning the full generated-image base64 payload when emitting
    the in-progress image item.
    - Removed dependencies no longer needed after moving persistence out of
    the extension crate.
    
    ## Fast Follow
    - Adjust the existing Extension API and add a general `TurnItem`
    finalization path for re-usability of code
    
    ## Validation
    
    - Ran `just fmt`.
    - Ran `just bazel-lock-update`.
    - Ran `just bazel-lock-check`.
    - Ran `just test -p codex-tools -p codex-extension-api -p
    codex-image-generation-extension`.
    - Ran `just test -p codex-core
    image_generation_publication_is_finalized_by_core`.
    - Ran `just test -p codex-core
    handle_output_item_done_records_image_save_history_message`.
    - Ran `just fix -p codex-tools -p codex-extension-api -p codex-core -p
    codex-image-generation-extension`.
  • Show activity for standalone web search calls (#24693)
    ## Why
    
    Standalone `web.run` calls run in the extension, so they need normal
    web-search progress activity while a request is in flight and durable
    completed activity after a thread is reloaded.
    
    Follow-up to #23823; uses the extension turn-item emission path added in
    #24813.
    
    ## What changed
    
    - Emit standalone `web.run` start/completion items through the host
    turn-item emitter, preserving standard client delivery and rollout
    persistence.
    - Include useful completion detail for queries, image queries, and
    literal-URL `open`/`find` commands.
    - Render completed searches as `Searched the web` or `Searched the web
    for <detail>`, with snapshot coverage for the detail-free case.
    - Extend the app-server round-trip test to verify completed search
    activity is reconstructed by `thread/read` after a fresh-process reload.
    
    ## Testing
    
    - `just test -p codex-web-search-extension`
    - `just test -p codex-app-server -E
    "test(standalone_web_search_round_trips_encrypted_output)"`
  • Add feature-gated standalone image generation extension (#24723)
    ## Why
    
    Add a standalone image generation path that can be exercised
    independently of hosted Responses image generation, while retaining the
    hosted tool as fallback unless the extension is actually available to
    the model.
    
    ## What changed
    
    - Added the `codex-image-generation-extension` crate with standalone
    generate/edit execution, prior-image selection for edits, model-visible
    image output, and local generated-image persistence.
    - Installed the extension in app-server behind the disabled-by-default
    `imagegenext` feature and backend eligibility checks.
    - Updated core tool planning so eligible `image_gen.imagegen` exposure
    replaces hosted `image_generation`, while unavailable configurations
    retain hosted fallback.
    - Added coverage for extension behavior, edit history reuse, feature
    gating, auth eligibility, and hosted-tool replacement.
    - The extension is installed through app-server only in this PR; other
    execution paths retain hosted image generation because hosted
    replacement occurs only when the standalone executor is actually
    registered and model-visible.
    - The initial extension contract intentionally fixes the image model to
    `gpt-image-2` and uses automatic image parameters.
    - Native generated-image history/card parity and rollout persistence
    cleanup are intentionally deferred follow-up work.
    
    ## Validation
    
    - `just test -p codex-image-generation-extension`
    - `just test -p codex-features`
    - `just test -p codex-core
    hosted_tools_follow_provider_auth_model_and_config_gates`
    - `just test -p codex-app-server`
    - `just fix -p codex-image-generation-extension -p codex-features -p
    codex-core -p codex-app-server`
    - `just fmt`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    
    ---------
    
    Co-authored-by: jif-oai <jif@openai.com>
  • Add app-server startup benchmark crate (#24651)
    ## Summary
    - Add a new `app-server-start-bench` crate to measure app-server startup
    performance
    - Wire the benchmark into the workspace and Bazel build so it can be run
    consistently
    - Update lockfiles and repo automation to account for the new package
  • Update rmcp to 1.7.0 (#24763)
    WIll make it easier to uprev when the new draft spec is supported.
    
    Also updates reqwest where needed for compatibility but doesn't update
    it everywhere since this is already a large diff.
    
    The new version of rmcp handles certain kinds of authentication failures
    differently, this patch includes support for identifying the failing scope
    in a WWW-Authenticate header.
  • Allow API-key auth for remote exec-server registration (#24666)
    ## Overview
    Allow remote `codex exec-server` registration to use existing API-key
    auth while restricting where those credentials can be sent.
    
    - Accept `CodexAuth::ApiKey` for the normal `--remote` registration
    path.
    - Restrict API-key remote registration to HTTPS `openai.com` and
    `openai.org` hosts and subdomains, with explicit HTTP loopback support
    for local development.
    - Disable registry registration redirects so credentials cannot be
    forwarded to an unvalidated destination.
    - Retain `--use-agent-identity-auth` as the explicit Agent Identity
    path.
    - Document remote registration using `CODEX_API_KEY`.
    
    ## Big picture
    Callers can now provide an API key directly to `exec-server`
    registration without first establishing ChatGPT login state:
    
    ```sh
    CODEX_API_KEY="$OPENAI_API_KEY" \
    codex exec-server \
      --remote "https://<host>.openai.org/api" \
      --environment-id "$ENVIRONMENT_ID"
    ```
    
    ## Validation
    - `cargo fmt --all` (`just fmt` is not installed on this host)
    - `cargo test -p codex-cli -p codex-exec-server`
  • Bump SQLx to pick up newer bundled SQLite (#24728)
    ## Why
    
    Codex stores thread, log, goal, and memory state in bundled SQLite
    databases through SQLx. We have a suspected SQLite WAL-reset corruption
    issue under heavy concurrent writer load, especially when multiple
    subagents are active. The existing `sqlx 0.8.6` dependency kept us on an
    older `libsqlite3-sys` / bundled SQLite, so this PR moves the SQLx stack
    far enough forward to pick up the newer bundled SQLite library.
    
    ## What changed
    
    - Bump the workspace `sqlx` dependency to `0.9.0`.
    - Use the SQLx 0.9 feature names explicitly: `runtime-tokio`,
    `tls-rustls`, and `sqlite-bundled`.
    - Update `Cargo.lock` so `sqlx-sqlite` resolves through `libsqlite3-sys
    0.37.0`.
    - Refresh `MODULE.bazel.lock` for the dependency changes.
    - Adapt `codex-state` to SQLx 0.9:
    - build dynamic state queries with `QueryBuilder<Sqlite>` instead of
    passing dynamic `String`s to `sqlx::query`;
    - remove the old `QueryBuilder` lifetime parameter from helper
    signatures;
    - preserve SQLx's new `Migrator` fields when constructing runtime
    migrators.
    
    ## Verification
    
    - `just test -p codex-state`
    - `just bazel-lock-check`
    - `cargo check -p codex-state --tests`
  • Attach Windows sandbox log to feedback reports (#24623)
    ## Why
    
    Windows sandbox diagnostics are currently hard to recover from
    `/feedback` even though they are often the most useful artifact when
    debugging sandbox behavior. Now that sandbox logging uses daily rolling
    files, feedback can safely include the current day's sandbox log without
    uploading the old ever-growing legacy `sandbox.log`.
    
    ## What changed
    
    - Add a `codex-windows-sandbox` helper that resolves the current daily
    sandbox log from `codex_home`.
    - When feedback is submitted with logs enabled on Windows, app-server
    attaches today's sandbox log if it exists.
    - Upload the attachment under the stable filename `windows-sandbox.log`,
    independent of the dated on-disk filename.
    - Keep existing raw `extra_log_files` behavior unchanged for rollout and
    desktop log attachments.
    
    ## Verification
    
    - `cargo fmt -p codex-app-server -p codex-windows-sandbox`
    - `cargo test -p codex-windows-sandbox
    current_log_file_path_for_codex_home_uses_sandbox_dir`
    - `cargo test -p codex-app-server
    windows_sandbox_log_attachment_uses_current_log`
    - Manual CLI/TUI `/feedback` test confirmed Sentry received
    `windows-sandbox.log`.
  • tui: keep inaccessible apps out of mentions (#24625)
    ## Summary
    
    Fix the TUI `$` app mention paths so App Directory rows that are not
    accessible are not treated as usable apps.
    
    This includes the core preservation fix from #24104, but expands it to
    the other app mention paths:
    
    - preserve app-server `is_accessible` flags when partial
    `app/list/updated` snapshots reach the TUI
    - require apps to be both accessible and enabled when resolving exact
    `$slug` mentions
    - require restored/stale `app://...` bindings to point at accessible,
    enabled apps before emitting structured app mentions
    - remove the now-unused `codex-chatgpt` dependency from `codex-tui`,
    which addresses the `cargo shear` failure seen on #24104
    
    ## Root Cause
    
    The app server already sends merged app snapshots with accessibility
    computed. The TUI handled app-server app list updates as partial app
    loads and re-ran the old accessible-app merge path. That path treated
    every notification row as accessible, so App Directory entries with
    `isAccessible=false` could appear in `$` suggestions.
    
    Regression source: #22914 routed app-list updates through the app server
    while reusing the old TUI partial-load handling. Related precursor:
    #14717 introduced the partial-load path, but #22914 made it user-visible
    for app-server updates.
    
    ## Issues
    
    Fixes #24145
    Fixes #24205
    Fixes #24319
    
    ## Validation
    
    - `just fmt`
    - `git diff --check`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `just argument-comment-lint -p codex-tui`
    - `just test -p codex-tui
    chatwidget::tests::popups_and_settings::apps_notification_update_excludes_inaccessible_apps_from_mentions
    chatwidget::tests::composer_submission::submit_user_message_ignores_inaccessible_app_mentions_from_bindings
    chatwidget::skills::tests::find_app_mentions_requires_accessible_enabled_apps_for_bound_paths
    chatwidget::skills::tests::find_app_mentions_requires_accessible_enabled_apps_for_slugs`
  • standalone websearch extension (#23823)
    ## Summary
    
    Add the extension-backed standalone `web.run` tool so Codex can call the
    standalone search endpoint through the `codex-api` search client and
    return its encrypted output to Responses.
    
    - gate the new tool behind `standalone_web_search`
    - install the extension in the app-server thread registry and hide
    hosted `web_search` when standalone search is enabled for OpenAI
    providers so the two paths stay mutually exclusive
    - build search context from persisted history using a small tail
    heuristic: previous user message, assistant text between the last two
    user turns capped at about 1k tokens, and current user message
    
    ## Test Plan
    
    - `cargo test -p codex-web-search-extension`
    - `cargo test -p codex-api`
    - `cargo test -p codex-core
    hosted_tools_follow_provider_auth_model_and_config_gates`
  • Add goal extension telemetry parity (#24615)
    ## Why
    
    `core/src/goals.rs` already emits OTEL metrics for goal creation,
    resume, terminal transitions, token counts, and duration. As `/goal`
    moves into `ext/goal`, the extension needs to preserve that telemetry
    contract instead of only emitting app-visible `ThreadGoalUpdated`
    events.
    
    This keeps the existing `codex.goal.*` metric surface intact while goal
    lifecycle ownership shifts toward the extension.
    
    ## What changed
    
    - Added an extension-local `GoalMetrics` helper that records the
    existing `codex.goal.*` counters and histograms through `codex-otel`.
    - Threaded an optional `MetricsClient` through `install_with_backend`,
    `GoalExtension`, `GoalRuntimeHandle`, and `GoalToolExecutor`.
    - Emitted created, resumed, and terminal goal metrics from the extension
    paths that create goals, restore active goals on thread resume, account
    budget limits, complete or block goals, and handle external goal
    mutations.
    - Updated existing goal extension test setup callsites to pass `None`
    for metrics when instrumentation is not under test.
    
    ## Verification
    
    Not run locally.
  • fix: drop flake (#24588)
    Dropping already commented out stuff
  • chore: move memory prompt builder into extension (#24558)
    ## Why
    
    The memories extension now owns the read-path developer instructions it
    injects at thread start. Keeping that prompt builder and template in
    `codex-memories-read` left the extension depending on a helper crate for
    extension-specific prompt assembly, and kept async template/truncation
    dependencies in the read crate after the remaining read surface no
    longer needed them.
    
    ## What changed
    
    - Moved `prompts.rs`, its tests, and `templates/memories/read_path.md`
    from `memories/read` into `ext/memories`.
    - Wired `MemoryExtension` to call the local prompt builder and added the
    moved templates to `ext/memories/BUILD.bazel` compile data.
    - Removed the now-unused prompt export and prompt-related dependencies
    from `codex-memories-read`.
    
    ## Testing
    
    - Not run locally.
  • chore: drop orphaned codex memories MCP crate (#24555)
    ## Why
    
    The memory read-tool surface had two implementations: the app-server
    extension path under `ext/memories`, and an unused `codex-memories-mcp`
    workspace crate under `memories/mcp`. The MCP crate no longer has
    reverse dependents, so keeping it around preserves duplicate backend,
    schema, and tool code that is not part of the live app-server memory
    path.
    
    Dropping the orphaned crate makes the remaining memory crate split
    clearer: `memories/read` owns read-path prompt/citation helpers,
    `memories/write` owns the write pipeline, and `ext/memories` owns the
    app-server extension integration.
    
    ## What changed
    
    - Removed the `memories/mcp` crate and its Bazel/Cargo metadata.
    - Removed `memories/mcp` from the Rust workspace and lockfile.
    - Updated `memories/README.md` so it only lists the remaining reusable
    memory crates.
    
    ## Verification
    
    - `cargo metadata --format-version 1 --no-deps` succeeds.
  • Add doctor thread inventory audit (#24305)
    ## Why
    
    Users have been reporting missing sessions in the app. The app server
    thread listing is backed by the SQLite state DB, but the durable source
    of truth for a thread still exists on disk as rollout JSONL. When the
    state DB is incomplete, doctor should be able to show the mismatch
    directly instead of leaving users with a generic state health result.
    
    ## What changed
    
    This adds a `threads` doctor check that compares active and archived
    rollout files under `CODEX_HOME` with rows in the SQLite `threads`
    table. The check reports missing rollout rows, stale DB rows, archive
    flag mismatches, duplicate rollout thread IDs, duplicate DB paths,
    source/provider summaries, and bounded samples of affected rollout
    paths.
    
    It also adds a read-only state audit helper in `codex-rs/state` so
    doctor can inspect thread rows without creating, migrating, or repairing
    the database.
    
    ## Sample output
    
    ```text
      ⚠ threads      rollout files are missing from the state DB
          default model provider   openai
          rollout DB active files  3910
          rollout DB archived files 2037
          rollout DB scan errors   0
          rollout DB malformed file names 0
          rollout DB scan cap reached false
          rollout DB rows          5499
          rollout DB active rows   3462
          rollout DB archived rows 2037
          rollout DB missing active rows 448
          rollout DB missing archived rows 0
          rollout DB stale rows    0
          rollout DB archive mismatches 0
          rollout DB duplicate rollout thread ids 0
          rollout DB duplicate DB paths 0
          rollout DB model providers openai=5359, lmstudio=35, mock_provider=33, lite_llm=26, proxy=26, ollama=15, lms=4, local-usage-limit=1
          rollout DB sources       vscode=2587, cli=1494, subagent:thread_spawn=577, subagent:other=502, exec=281, subagent:memory_consolidation=46, subagent:review=9, unknown=3
          rollout DB missing active sample ~/.codex/sessions/2026/0…857e-a923c712e066.jsonl
          rollout DB missing active sample ~/.codex/sessions/2025/0…877a-766dff25c68d.jsonl
          rollout DB missing active sample ~/.codex/sessions/2025/0…a8b1-7bbadc836f6e.jsonl
          rollout DB missing active sample ~/.codex/sessions/2025/0…a218-e6197f3f62f8.jsonl
          rollout DB missing active sample ~/.codex/sessions/2025/0…9011-7e30784f9932.jsonl
    ```
  • feat(doctor): add environment diagnostics (#24261)
    ## Why
    
    Issue #23031 was hard to diagnose from existing `codex doctor` output
    because support could not see the OS language, resolved Git install, Git
    repo metadata, Windows console mode/code page, or terminal-title inputs
    that affect the TUI startup path. This adds those read-only signals to
    `codex doctor` so Windows, Linux, and macOS reports carry the context
    needed to investigate similar terminal rendering regressions.
    
    Refs #23031
    
    ## What Changed
    
    - Add a `system.environment` check for OS type/version, OS language, and
    locale env vars.
    - Add a `git.environment` check for the selected Git executable, PATH
    Git candidates, version, exec path/build options, repository root,
    branch, `.git` entry, and `core.fsmonitor`.
    - Add Windows console code page and VT-processing mode details to
    terminal diagnostics.
    - Add a `terminal.title` check for configured/default title items and
    resolved project-title source/value.
    - Surface startup warning counts in config diagnostics and teach human
    output to render the new categories.
    
    ## How to Test
    
    1. On Windows, check out this branch and run `cargo run -p codex-cli --
    doctor --summary`.
    2. Confirm the Environment section includes `system`, `git`, `terminal`,
    and `title` rows.
    3. Run `cargo run -p codex-cli -- doctor --json`.
    4. Confirm the JSON contains `system.environment`, `git.environment`,
    and `terminal.title`; on Windows, confirm `terminal.env` details include
    console code pages and `VT processing` for stdout/stderr.
    5. From a non-git directory, run the same `doctor --json` command and
    confirm the Git check reports `repo detected: false` rather than
    warning.
    
    Targeted tests:
    
    - `cargo test -p codex-cli doctor`
    - `cargo test -p codex-cli`
  • package: include zsh fork in Codex package (#23756)
    ## Why
    
    The package layout gives Codex a stable place for runtime helpers that
    should travel with the entrypoint. `shell_zsh_fork` still required users
    to configure `zsh_path` manually, even though we already publish
    prebuilt zsh fork artifacts.
    
    This PR builds on #24129 and uses the shared DotSlash artifact fetcher
    to include the zsh fork in Codex packages when a matching target
    artifact exists. Packaged Codex builds can then discover the bundled
    fork automatically; the user/profile `zsh_path` override is removed so
    the feature uses the package-managed artifact instead of a legacy path
    knob.
    
    ## What Changed
    
    - Added `scripts/codex_package/codex-zsh`, a checked-in DotSlash
    manifest for the current macOS arm64 and Linux zsh fork artifacts.
    - Taught `scripts/build_codex_package.py` to fetch the matching zsh fork
    artifact and install it at `codex-resources/zsh/bin/zsh` when available
    for the selected target.
    - Added package layout validation for the optional bundled zsh resource.
    - Added `InstallContext::bundled_zsh_path()` and
    `InstallContext::bundled_zsh_bin_dir()` for package-layout resource
    discovery.
    - Threaded the packaged zsh path through config loading as the runtime
    `zsh_path` for packaged installs, and removed the config/profile/CLI
    override path.
    - Kept the packaged default zsh override typed as `AbsolutePathBuf`
    until the existing runtime `Config::zsh_path` boundary.
    - Updated app-server zsh-fork integration tests to spawn
    `codex-app-server` from a temporary package layout with
    `codex-resources/zsh/bin/zsh`, matching the new packaged discovery path
    instead of setting `zsh_path` in config.
    - Switched package executable copying from metadata-preserving `copy2()`
    to `copyfile()` plus explicit executable bits, which avoids macOS
    file-flag failures when local smoke tests use system binaries as inputs.
    
    ## Testing
    
    To verify that the `zsh` executable from the Codex package is picked up
    correctly, first I ran:
    
    ```shell
    ./scripts/build_codex_package.py
    ```
    
    which created:
    
    ```
    /private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/
    ```
    
    so then I ran:
    
    ```
    /private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/bin/codex exec --enable shell_zsh_fork 'run `echo $0`'
    ```
    
    which reported the following, as expected:
    
    ```
    /private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/codex-resources/zsh/bin/zsh
    ```
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23756).
    * #23768
    * __->__ #23756
  • chore: add JSON schema policy fixture coverage (#24152)
    ## Why
    
    Before changing the Codex Bridge JSON schema policy, add integration
    coverage around real connector-like MCP tool schemas. The existing unit
    tests cover individual sanitizer behaviors, but they do not make it easy
    to see whether full fixture schemas keep model-visible guidance, prune
    only unreachable definitions, drop unsupported JSON Schema fields, and
    stay within the Responses API schema budget.
    
    ## What Changed
    
    - Added `tools/tests/json_schema_policy_fixtures.rs`, which converts MCP
    tool fixtures through `mcp_tool_to_responses_api_tool` and validates the
    resulting Responses tool parameters.
    - Added connector-style fixtures for Slack, Google Calendar, Google
    Drive, Notion, and Microsoft Outlook Email under
    `tools/tests/fixtures/json_schema_policy/`.
    - Added fixture assertions for preserved guidance, pruned definitions,
    expected field drops after `JsonSchema` conversion, marker count
    baselines, and dangling local `$ref` prevention.
    - Added a real oversized golden Notion `create_page` input schema
    fixture to exercise the compaction path that strips descriptions, drops
    root `$defs`, rewrites local refs, and fits the compacted schema under
    the budget.
  • [codex] Add image re-encoding benchmarks (#23935)
    ## Summary
    - add Divan benchmarks for prompt image re-encoding paths
    - wire the image benchmark smoke test into Rust CI workflows
    
    ## Why
    Image prompt handling includes re-encoding work that benefits from
    repeatable benchmark coverage so changes can be measured in CI and
    locally.
    
    This already helped identify a potential regression from changing compiler flags.
    
    ## Impact
    Developers can run and compare the new image re-encoding benchmarks, and
    CI exercises the benchmark target via the Rust benchmark smoke test.
  • [codex] Use rolling files for Windows sandbox logs (#24117)
    ## Why
    
    Windows sandbox diagnostics currently append to a single `sandbox.log`
    under `CODEX_HOME/.sandbox`. That file never rolls over, which makes it
    hard to safely include sandbox diagnostics in future feedback reports
    without risking unbounded growth.
    
    ## What changed
    
    - Replaced direct append-open sandbox logging with
    `tracing_appender::rolling::RollingFileAppender`.
    - Configured sandbox logs to rotate daily using names like
    `sandbox.YYYY-MM-DD.log`.
    - Added a conservative `MAX_LOG_FILES` cap of 90 retained matching log
    files.
    - Routed the Windows sandbox setup helper through the same rolling
    writer.
    - Added helpers for resolving the current daily sandbox log path so
    future feedback upload work can use the same filename logic.
    - Updated tests and test diagnostics to read the dated daily log file.
    
    This intentionally does not include sandbox logs in `/feedback` yet;
    scrubbing and attachment behavior can happen in a follow-up.
    
    ## Testing
    
    - `cargo fmt -p codex-windows-sandbox`
    - `cargo check -p codex-windows-sandbox`
    - `cargo test -p codex-windows-sandbox`
    - `cargo test -p codex-windows-sandbox logging::tests`
    - `cargo clippy -p codex-windows-sandbox --all-targets -- -D warnings`
  • feat: support local refs and defs in tool input schemas (#23357)
    # Why
    
    Some connector tool input schemas use local JSON Schema references and
    definition tables to avoid duplicating large nested shapes. Codex
    previously lowered these schemas into the supported subset in a way that
    could discard `$ref`-only schema objects and lose the corresponding
    definitions, which made non-strict tool registration less faithful than
    the original connector schema.
    
    This keeps the existing minimal-lowering policy: Codex still does not
    raw-pass through arbitrary JSON Schema, but it now preserves local
    reference structure that fits the Responses-compatible subset and prunes
    definition entries that cannot be reached by following `$ref`s from the
    root schema after sanitization, including refs found transitively inside
    other reachable definitions. The pruning matters because Responses
    parses definition tables even when entries are unused, so keeping dead
    definitions wastes prompt tokens.
    
    # What changed
    
    - Added `$ref`, `$defs`, and legacy `definitions` fields to the tool
    `JsonSchema` representation.
    - Updated `parse_tool_input_schema` lowering so `$ref`-only schema
    objects survive sanitization instead of becoming `{}`.
    - Sanitized definition tables recursively and dropped malformed
    definition tables so non-strict registration degrades gracefully.
    - Added reachability pruning for root definition tables by starting from
    refs outside definition tables, then following refs inside reachable
    definitions.
    - Added JSON Pointer decoding for local definition refs such as
    `#/$defs/Foo~1Bar`.
    
    # Verification
    ran local golden-schema probes against representative connector schemas
    to validate behavior on real generated schemas:
    
    | Golden schema | Before bytes | After bytes | `$defs` before -> after |
    `$ref` before -> after | Result |
    |---|---:|---:|---:|---:|---|
    | `google_calendar/create_space` | 7111 | 4526 | 7 -> 7 | 7 -> 7 | all
    definitions preserved because all are reachable |
    | `figma/apply_file_variable_changes` | 4609 | 999 | 8 -> 5 | 8 -> 5 |
    unused defs pruned after unsupported `oneOf` shapes lower away |
    | `snowflake/list_catalog_integrations` | 1380 | 404 | 3 -> 0 | 0 -> 0 |
    all defs pruned because none are referenced |
    | `dropbox/create_shared_link` | 8894 | 1836 | 14 -> 4 | 9 -> 4 | only
    defs reachable from the root schema after sanitization are retained,
    including transitively through other retained defs |
    
    Token increase across golden schema due to this change:
    <img width="817" height="366" alt="Screenshot 2026-05-19 at 1 47 04 PM"
    src="https://github.com/user-attachments/assets/d5c80fe9-da85-41e6-8ac7-a01d1e0b0f71"
    />
  • [codex] Make thread search case-insensitive (#23921)
    ## Summary
    - make rollout content search prefilter rollout files case-insensitively
    - keep the no-ripgrep fallback scan and visible snippet matcher aligned
    with that behavior
    - cover a lowercase `thread/search` query matching mixed-case
    conversation content
    
    ## Why
    The rollout-backed `thread/search` path used exact string matching in
    both its `rg` prefilter and semantic snippet generation. A content
    result could be missed solely because the query casing did not match the
    stored conversation text.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-app-server thread_search_returns_content_matches`
    - `cargo test -p codex-rollout`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `cargo build -p codex-cli`
    - launched a local Electron dev instance with the rebuilt CLI binary
  • [codex] Add rollout-backed thread content search (#23519)
    ## Summary
    - add experimental `thread/search` for local rollout-backed thread
    search using `rg` over JSONL rollouts
    - return search-specific result rows with optional previews instead of
    storing preview data on `StoredThread` or ordinary `Thread` responses
    - keep `thread/list` separate from full-content search and document the
    new app-server surface
    
    ## Testing
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server
    thread_search_returns_content_and_title_matches -- --nocapture`
  • Use named MITM permissions config (#18240)
    ## Stack
    1. Parent PR: #18868 adds MITM hook config and model only.
    2. Parent PR: #20659 wires hook enforcement into the proxy request path.
    3. This PR changes the user facing PermissionProfile TOML shape.
    
    ## Why
    1. The broader goal is to make MITM clamping usable from the same
    permission profile that already controls network behavior.
    2. This PR is the config UX layer for the stack. It moves MITM policy
    into `[permissions.<profile>.network.mitm]` instead of exposing the flat
    runtime shape to users.
    3. The named hook and action tables belong here because users need
    reusable policy blocks that are easy to review, while the proxy runtime
    only needs a flat hook list.
    4. This PR validates action refs during config parsing so mistakes in
    the user facing policy fail before a proxy session starts.
    5. Keeping the lowering here lets the proxy keep its simpler runtime
    model and lets PermissionProfile remain the single source of network
    permission policy.
    
    ## Summary
    1. Keep MITM policy inside `[permissions.<profile>.network.mitm]` so the
    selected PermissionProfile owns network proxy policy.
    2. Use named MITM hooks under
    `[permissions.<profile>.network.mitm.hooks.<name>]`.
    3. Put host, methods, path prefixes, query, headers, body, and action
    refs on the hook table.
    4. Define reusable action blocks under
    `[permissions.<profile>.network.mitm.actions.<name>]`.
    5. Represent action blocks with `NetworkMitmActionToml`, then lower them
    into the proxy runtime action config.
    6. Reject unknown refs, empty refs, and empty action blocks during
    config parsing.
    7. Keep the runtime hook model unchanged by lowering config into the
    existing proxy hook list.
    8. Preserve the #20659 activation fix for nested MITM policy.
    
    ## Example
    ```toml
    [permissions.workspace.network.mitm]
    enabled = true
    
    [permissions.workspace.network.mitm.hooks.github_write]
    host = "api.github.com"
    methods = ["POST", "PUT"]
    path_prefixes = ["/repos/openai/"]
    action = ["strip_auth"]
    
    [permissions.workspace.network.mitm.actions.strip_auth]
    strip_request_headers = ["authorization"]
    ```
    
    ## Validation
    1. Regenerated the config schema.
    2. Ran the core MITM config parsing and validation tests.
    3. Ran the core PermissionProfile MITM proxy activation tests.
    4. Ran the core config schema fixture test.
    5. Ran the network proxy MITM policy tests.
    6. Ran the scoped Clippy fixer for the network proxy crate.
    7. Ran the scoped Clippy fixer for the core crate.
    
    ---------
    
    Co-authored-by: Winston Howes <winston@openai.com>
  • Remove Windows sandbox resource stamping (#23764)
    ## Why
    
    The `codex-windows-sandbox` crate was embedding Windows resource
    metadata through a package-level `build.rs`. Because that package also
    exposes the `codex_windows_sandbox` library, downstream binaries that
    link the library could inherit `FileDescription` / `ProductName` values
    of `codex-windows-sandbox`.
    
    That made ordinary Codex binaries, including the long-lived `codex.exe`
    app-server sidecar, appear as `codex-windows-sandbox` in Windows UI
    surfaces such as Task Manager / file properties.
    
    We do not rely on this metadata enough to justify a larger bin-only
    resource split, so this removes the resource stamping entirely.
    
    ## What changed
    
    - Removed the `windows-sandbox-rs` build script that invoked `winres`.
    - Removed the setup manifest that was only consumed by that build
    script.
    - Removed the `winres` build dependency and corresponding `Cargo.lock` /
    `MODULE.bazel.lock` entries.
    - Removed the now-unused Bazel build-script data.
    
    ## Verification
    
    - `cargo build -p codex-windows-sandbox --bins`
    - `cargo build -p codex-cli --bin codex`
    - `bazel mod deps --lockfile_mode=update` via Bazelisk, with local
    remote-cache-disabling flags because `bazel` is not installed on PATH
    here
    - `bazel mod deps --lockfile_mode=error` via Bazelisk, with the same
    local flags
    - Verified rebuilt `codex.exe`, `codex-command-runner.exe`, and
    `codex-windows-sandbox-setup.exe` now have blank `FileDescription` /
    `ProductName` fields.
    - `cargo test -p codex-windows-sandbox` still fails on two legacy
    Windows sandbox tests with `CreateRestrictedToken failed: 87` and the
    follow-on poisoned test lock; 85 passed, 2 ignored.
  • fix(config): resolve cloud requirements deny-read globs (#23729)
    ## Why
    
    Cloud-managed `requirements.toml` contents were deserialized without an
    `AbsolutePathBuf` base directory. Relative managed
    `permissions.filesystem.deny_read` glob entries therefore failed while
    the equivalent local system requirements path succeeded under its
    `AbsolutePathBufGuard`. This follows the `codex_home` base path
    convention clarified in https://github.com/openai/codex/pull/15707.
    
    ## What changed
    
    - Resolve cloud requirements TOML under an `AbsolutePathBufGuard` rooted
    at `codex_home`.
    - Reuse the same base for cloud requirements loaded from the signed
    cache.
    - Add a regression test for a relative cloud-managed `deny_read` glob.
    
    ## Validation
    
    - `just fmt`
    - `cargo test -p codex-cloud-requirements`
    - `cargo clippy -p codex-cloud-requirements --all-targets --no-deps`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - `git diff --check`
  • feat: add turn_id and truncation_policy to extension tool calls (#23666)
    ## Why
    
    Extension-owned tools currently receive a stripped `ToolCall` with only
    `call_id`, `tool_name`, and `payload`.
    That makes extension work that needs turn-local execution context
    awkward, especially web-search extension work that needs the active
    `truncation_policy` at tool invocation time.
    
    Reconstructing that value from config or `ExtensionData` would be
    indirect and could drift from the actual turn context, so the cleaner
    fix is to pass the needed turn metadata directly on the extension-facing
    invocation type.
    
    ## What changed
    
    - added `turn_id` and `truncation_policy` to `codex_tools::ToolCall`
    - populated those fields when core adapts `ToolInvocation` into an
    extension tool call
    - added a focused adapter test that verifies extension executors receive
    the forwarded turn metadata
    - updated the memories extension tests to construct the richer
    `ToolCall`
    - added the `codex-utils-output-truncation` dependency to `codex-tools`
    and refreshed lockfiles
    
    ## Testing
    
    - `cargo test -p codex-tools`
    - `cargo test -p codex-memories-extension`
    - `cargo test -p codex-core passes_turn_fields_to_extension_call`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
  • runtime: use install context for bundled bwrap (#23634)
    ## Summary
    
    The Linux sandbox should find bundled `bwrap` through the same
    package-layout abstraction as the rest of the runtime, instead of
    maintaining a separate standalone-specific lookup path.
    
    This adds an `InstallContext` helper for bundled resources and updates
    `codex-linux-sandbox` to ask the current install context for
    `codex-resources/bwrap` before falling back to the old
    executable-relative probes. The tests cover npm-style, standalone, and
    canonical package layouts so `bwrap` lookup follows the package
    structure introduced earlier in the stack.
    
    ## Test plan
    
    - `cargo test -p codex-install-context`
    - `cargo test -p codex-linux-sandbox --lib`
    - `just fix -p codex-install-context -p codex-linux-sandbox`
    - `just bazel-lock-check`
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23634).
    * #23638
    * #23637
    * #23636
    * #23635
    * __->__ #23634
  • feat: wire goal extension tools to the dedicated goal store (#23685)
    ## Why
    
    `ext/goal` already had the tool specs and contributor wiring for
    `/goal`, but the installed tools still depended on a placeholder backend
    that always errored. That meant the extension could not actually own
    goal persistence even though the dedicated `thread_goals` store already
    exists.
    
    This change wires the extension tools directly to the dedicated goal
    store so the extension can create, read, and complete goals against real
    state instead of falling back to host-side placeholders.
    
    ## What changed
    
    - make `install_with_backend(...)` require
    `Arc<codex_state::StateRuntime>` so goal storage is always available
    when the extension is installed
    - remove the unused no-backend/public backend abstraction from
    `ext/goal` and have the tool executors talk directly to `StateRuntime`
    - map `thread_goals` rows into the existing protocol response shape for
    `get_goal`, `create_goal`, and `update_goal`
    - preserve current thread-list behavior by filling an empty thread
    preview from the goal objective when a goal is created through the
    extension path
    - add integration coverage for the installed tool surface, including
    successful goal creation and duplicate-create rejection
    
    ## Testing
    
    - `cargo test -p codex-goal-extension`
  • runtime: detect Codex package layout (#23596)
    ## Why
    
    The package-builder stack now creates a canonical Codex package
    directory where the entrypoint lives under `bin/`, bundled helper
    resources live under `codex-resources/`, and bundled PATH-style tools
    live under `codex-path/`. That layout is not specific to the standalone
    installer: npm, brew, install scripts, and manually unpacked artifacts
    should all be able to use the same package shape.
    
    The Rust runtime still only knew about the legacy standalone release
    layout, where resources sit next to the executable. A packaged binary
    therefore would not identify its package root or prefer the bundled `rg`
    from `codex-path/`.
    
    ## What changed
    
    - Adds `CodexPackageLayout` to `codex-install-context` and detects it
    from an executable path shaped like `<package>/bin/<entrypoint>` when
    `<package>/codex-package.json` is present.
    - Splits `InstallContext` into an install `method` plus an optional
    package layout so the layout is shared across npm, bun, brew,
    standalone, and other launch contexts.
    - Stores package-layout paths as `AbsolutePathBuf` values.
    - Keeps `codex-resources/` and `codex-path/` optional so Codex can still
    run with degraded behavior if sidecar directories are missing.
    - Updates `InstallContext::rg_command()` to prefer bundled
    `codex-path/rg` or `rg.exe`, then fall back to the legacy standalone
    resources location, then system `rg`.
    - Updates `codex doctor` reporting so package installs show package,
    bin, resources, and path directories, and so bundled search detection
    recognizes `codex-path/` for any install method.
    
    ## Test plan
    
    - `cargo test -p codex-install-context`
    - `cargo test -p codex-cli`
    - `cargo test -p codex-tui
    update_action::tests::maps_install_context_to_update_action`
    - `just bazel-lock-check`
  • CI: Customize v8 building (#22086)
    ## Summary
    
    Move the rusty_v8 artifact production into hermetic Bazel path and bump
    the `v8` crate to `147.4.0`
    
    The new flow builds V8 release artifacts from source for Darwin and
    Linux targets, publishes both the current release-compatible artifacts
    and sandbox-enabled variants, and keeps Cargo consumers on prebuilt
    binaries by continuing to feed the `v8` crate the archive and generated
    binding files it already expects.
    
    ## Why
    
    We need control over V8 build-time features without giving up prebuilt
    artifacts for downstream Cargo builds.
    
    Upstream `rusty_v8` already supports source-only features such as
    `v8_enable_sandbox`, but its normal prebuilt release assets do not cover
    every feature combination we need. Building the artifacts ourselves lets
    us enable settings such as the V8 sandbox and pointer compression at
    artifact build time, then publish those outputs so ordinary Cargo builds
    can still consume prebuilts instead of compiling V8 locally.
    
    This keeps the fast consumer experience of prebuilt `rusty_v8` archives
    while giving us a reproducible path to ship featureful variants that
    upstream does not currently publish for us.
    
    ## Implementation Notes
    
    The Bazel graph in this PR is not copied wholesale from `rusty_v8`;
    `rusty_v8`'s normal source build is still GN/Ninja-based.
    
    Instead, this change starts from upstream V8's Bazel rules and adapts
    them to Codex's hermetic toolchains and dependency layout. Where we
    intentionally follow `rusty_v8`, we mirror its existing artifact
    contract:
    
    - the same `v8` crate version and generated binding expectations
    - the same sandbox feature relationship, where sandboxing requires
    pointer compression
    - the same custom libc++ model expected by Cargo's default
    `use_custom_libcxx` feature
    - the same release-style archive plus `src_binding` outputs consumed by
    the `v8` crate
    
    To preserve that contract, the Bazel release path pins the libc++,
    libc++abi, and llvm-libc revisions used by `rusty_v8 v147.4.0`, builds
    release artifacts with `--config=rusty-v8-upstream-libcxx`, and folds
    the matching runtime objects into the final static archive.
    
    ## Windows
    
    Windows is annoyingly handled differently.
    
    Codex's current hermetic Bazel Windows C++ platform is `windows-gnullvm`
    / `x86_64-w64-windows-gnu`, while upstream `rusty_v8` publishes Windows
    prebuilts for `*-pc-windows-msvc`. Those are different ABIs, so the
    Bazel graph cannot truthfully reproduce the upstream MSVC artifacts
    until we add a real MSVC-targeting C++ toolchain.
    
    For now:
    
    - Windows MSVC consumers continue to use upstream `rusty_v8` release
    archives.
    - Windows GNU targets are built in-tree so they link against a matching
    GNU ABI.
    - The canary workflow separately exercises upstream `rusty_v8` source
    builds for MSVC sandbox artifacts, but MSVC is not yet part of the
    Bazel-produced release matrix.
    
    ## Validation
    This PR is technically self validating through CI. I have already
    published it as a release tag so the artifacts from this branch are
    published to
    https://github.com/openai/codex/releases/tag/rusty-v8-v147.4.0 CI for
    this PR should therefore consume our own release targets. I have also
    locally tested for linux and darwin.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix(plugins): keep version upgrades additive (#23356)
    ## Why
    
    Windows can reject plugin cache upgrades when a running MCP server still
    has its working directory inside the currently active plugin version.
    The existing cache refresh path replaces
    `plugins/cache/<marketplace>/<plugin>` as a whole, so a live handle
    under the old version can make an otherwise ordinary version bump fail.
    
    This PR keeps the existing plugin-selection model intact while making
    version bumps less disruptive.
    
    ## What changed
    
    - When installing a new version beside an existing plugin cache root,
    move only the staged version directory into place instead of replacing
    the whole plugin root.
    - Best-effort prune older sibling version directories after the new
    version is activated.
    - Preserve the existing whole-root replacement path for first installs
    and same-version refreshes.
    - Add regression coverage for upgrading from `1.0.0` to `2.0.0` without
    replacing the plugin root.
    
    ## Verification
    
    - `cargo test -p codex-core-plugins install_with_new_version`
    - `cargo fmt --package codex-core-plugins --check`
  • Make extension lifecycle hooks async (#23291)
    ## Why
    
    Extension lifecycle hooks sit on the host/extension boundary, but the
    current trait surface only allows synchronous callbacks. That forces
    extensions that need to seed, rehydrate, observe, or flush
    extension-owned state during thread and turn transitions to either block
    inside the callback or move async work into separate host plumbing.
    
    This PR makes those lifecycle callbacks awaitable so extension
    implementations can perform async work directly at the lifecycle point
    where the host already has the relevant session, thread, or turn stores
    available.
    
    ## What changed
    
    - Makes `ThreadLifecycleContributor` and `TurnLifecycleContributor`
    async in `codex-extension-api`.
    - Awaits thread start/resume/stop and turn start/stop/abort lifecycle
    callbacks from `codex-core`.
    - Updates the guardian and memories extensions to implement the async
    lifecycle trait surface.
    - Updates the existing lifecycle tests to use async contributor
    implementations.
    - Adds `async-trait` to the crates that now expose or implement these
    async object-safe lifecycle traits.
    
    ## Testing
    
    - Existing `codex-core` lifecycle tests were updated to cover async
    implementations for thread stop and turn abort ordering.
  • chore: goal ext skeleton (#23288)
    Skeleton of `/goal` in extension
    Lot's of follow-ups coming
  • exec-server: support auth-backed remote executor registration (#22769)
    This updates remote `exec-server` registration to use normal Codex auth
    instead of a registry-issued credential. The registry request is built
    from the existing auth-provider path, which preserves the biscuit-only
    registry contract introduced in
    [openai/openai#924101](https://github.com/openai/openai/pull/924101)
    while removing the old remote registry bearer env var and its direct
    transport assumptions.
    
    The default remote flow uses persisted ChatGPT auth from the normal
    Codex config/storage path. This PR also includes the containerized Agent
    Identity path needed by
    [openai/openai#924260](https://github.com/openai/openai/pull/924260):
    remote `exec-server` accepts `--allow-agent-identity-auth`, permits
    Agent Identity auth loaded from `CODEX_ACCESS_TOKEN` only when that flag
    is present, and reuses the existing Agent task registration plus derived
    `AgentAssertion` header generation. API-key auth remains unsupported,
    and Agent Identity stays opt-in.
    
    Validation performed beyond normal presubmit coverage:
    - `cargo fmt --all --check`
    - `cargo check -p codex-cli`
    - `cargo test -p codex-exec-server`
    - `cargo test -p codex-cli exec_server_agent_identity_auth_flag_`
    - `cargo test -p codex-cli remote_exec_server_auth_mode_`
    
    I also attempted `cargo test -p codex-cli`. The new CLI tests passed
    inside that run, but the suite ended on an unrelated local
    marketplace-state failure in
    `plugin_list_excludes_unconfigured_repo_local_marketplaces`.
  • Move memory prompt injection to app-server extension (#22841)
    ## Why
    
    Memory prompt injection should be owned by the extension path that
    app-server composes at runtime, not by an inlined special case inside
    `codex-core`. This keeps `codex-core` focused on session orchestration
    while allowing the memories extension to own its app-server prompt
    behavior.
    
    ## What Changed
    
    - Registers `codex-memories-extension` in the app-server extension
    registry.
    - Moves the memory developer-instruction injection out of
    `core/src/session/mod.rs` and into the memories extension prompt
    contributor.
    - Adds config-change handling so the extension keeps its per-thread
    memory settings in sync after startup.
    - Leaves memories read/retrieval tools unregistered for now so this PR
    only changes prompt injection.
    - Removes the stale `cargo-shear` ignore now that app-server depends on
    the extension crate.
    
    ## Validation
    
    Not run locally; validation is left to CI.
  • permissions: support workspace roots in profiles (#22610)
    ## Why
    
    This is the configuration/model half of the alternative permissions
    migration we discussed as a comparison point for
    [#22401](https://github.com/openai/codex/pull/22401) and
    [#22402](https://github.com/openai/codex/pull/22402).
    
    The old `workspace-write` model mixes three concerns that we want to
    keep separate:
    - reusable profile rules that should stay immutable once selected
    - user/runtime workspace roots from `cwd`, `--add-dir`, and legacy
    workspace-write config
    - internal Codex writable roots such as memories, which should not be
    shown as user workspace roots
    
    This PR gives permission profiles first-class `workspace_roots` so users
    can opt multiple repositories into the same `:workspace_roots` rules
    without using broad absolute-path write grants. It also starts
    separating the raw selected profile from the effective runtime profile
    by making `Permissions` expose explicit accessors instead of public
    mutable fields.
    
    A representative `config.toml` looks like this:
    
    ```toml
    default_permissions = "dev"
    
    [permissions.dev.workspace_roots]
    "~/code/openai" = true
    "~/code/developers-website" = true
    
    [permissions.dev.filesystem.":workspace_roots"]
    "." = "write"
    ".codex" = "read"
    ".git" = "read"
    ".vscode" = "read"
    ```
    
    If Codex starts in `~/code/codex` with that profile selected, the
    effective workspace-root set becomes:
    - `~/code/codex` from the runtime `cwd`
    - `~/code/openai` from the profile
    - `~/code/developers-website` from the profile
    
    The `:workspace_roots` rules are materialized across each root, so
    `.git`, `.codex`, and `.vscode` stay scoped the same way everywhere.
    Runtime additions such as `--add-dir` can still layer on later stack
    entries without mutating the selected profile.
    
    ## Stack Shape
    
    This PR intentionally stops before the profile-identity cleanup in
    [#22683](https://github.com/openai/codex/pull/22683) so the base review
    stays focused on config loading, workspace-root materialization, and
    compatibility with legacy `workspace-write`.
    
    The representation in this PR is therefore transitional: `Permissions`
    carries enough state to distinguish the raw constrained profile from the
    effective runtime profile, and there are still call sites that must keep
    the active profile identity and constrained profile value in sync. The
    follow-up PR replaces that with a single resolved profile state
    (`ResolvedPermissionProfile` / `PermissionProfileState`) that keeps the
    profile id, immutable `PermissionProfile`, and profile-declared
    workspace roots together. That follow-up removes APIs such as
    `set_constrained_permission_profile_with_active_profile()` where
    separate arguments could drift out of sync.
    
    Downstream PRs then build on this base to switch app-server turn updates
    to profile ids plus runtime workspace roots and to finish the
    user-visible summary behavior. Reviewers should judge this PR as the
    workspace-roots foundation, not as the final in-memory shape of selected
    permission profiles.
    
    ## Review Guide
    
    Suggested review order:
    
    1. Start with `codex-rs/core/src/config/mod.rs`.
    This is the main shape change in the base slice. `Permissions` now
    stores a private raw `Constrained<PermissionProfile>` plus runtime
    `workspace_roots`. Callers use `permission_profile()` when they need the
    raw constrained value and `effective_permission_profile()` when they
    need a materialized runtime profile. As noted above,
    [#22683](https://github.com/openai/codex/pull/22683) replaces this
    transitional shape with a resolved profile state that keeps identity and
    profile data together.
    
    2. Review `codex-rs/config/src/permissions_toml.rs` and
    `codex-rs/core/src/config/permissions.rs`.
    These add `[permissions.<id>.workspace_roots]`, resolve enabled entries
    relative to the policy cwd, and keep `:workspace_roots` deny-read glob
    patterns symbolic until the actual roots are known.
    
    3. Review `codex-rs/protocol/src/permissions.rs` and
    `codex-rs/protocol/src/models.rs`.
    These add the policy/profile materialization helpers that expand exact
    `:workspace_roots` entries and scoped deny-read globs over every
    workspace root. This is also where `ActivePermissionProfileModification`
    is removed from the core model.
    
    4. Review the legacy bridge in
    `Config::load_from_base_config_with_overrides` and
    `Config::set_legacy_sandbox_policy`.
    This is where legacy `workspace-write` roots become runtime workspace
    roots, while Codex internal writable roots stay internal and do not
    appear as user-facing workspace roots.
    
    5. Then skim downstream call sites.
    The interesting pattern is raw-vs-effective access: state/proxy/bwrap
    paths keep the raw constrained profile, while execution, summaries, and
    user-visible status use the effective profile and workspace-root list.
    
    ## What Changed
    
    - added `[permissions.<id>.workspace_roots]` to the config model and
    schema
    - added runtime `workspace_roots` state to `Config`/`Permissions` and
    `ConfigOverrides`
    - made `Permissions` profile fields private and replaced direct mutation
    with accessors/setters
    - added `PermissionProfile` and `FileSystemSandboxPolicy` helpers for
    materializing `:workspace_roots` exact paths and deny-read globs across
    all roots
    - moved legacy additional writable roots into runtime workspace-root
    state instead of active profile modifications
    - removed `ActivePermissionProfileModification` and its app-server
    protocol/schema export
    - updated sandbox/status summary paths so internal writable roots are
    not reported as user workspace roots
    
    ## Verification Strategy
    
    The targeted tests cover the behavior at the layers where regressions
    are most likely:
    - `codex-rs/core/src/config/config_tests.rs` verifies config loading,
    legacy workspace-root seeding, effective profile materialization, and
    memory-root handling.
    - `codex-rs/core/src/config/permissions_tests.rs` verifies profile
    `workspace_roots` parsing and `:workspace_roots` scoped/glob
    compilation.
    - `codex-rs/protocol/src/permissions.rs` unit tests verify exact and
    glob materialization over multiple workspace roots.
    - `codex-rs/tui/src/status/tests.rs` and
    `codex-rs/utils/sandbox-summary/src/sandbox_summary.rs` verify the
    user-facing summaries show effective workspace roots and hide internal
    writes.
    
    I also ran `cargo check --tests` locally after the latest stack refresh
    to catch cross-crate API breakage from the private-field/accessor
    changes.
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22610).
    * #22612
    * #22611
    * #22683
    * __->__ #22610
  • Trim TUI legacy core helper usage (#22695)
    ## Why
    
    The TUI still had a few low-risk dependencies flowing through the
    transitional `legacy_core` namespace after the app-server migration.
    These helpers either already have clearer non-core owners or are
    presentation logic that does not belong in `codex-core`, so moving them
    out reduces the compatibility surface without changing product behavior.
    
    ## What changed
    
    This is a low-risk change, almost completely mechanical in nature.
    
    - Route TUI Codex-home lookup through `codex-utils-home-dir`, use
    `Config::log_dir` directly, and call
    `codex-sandboxing::system_bwrap_warning` without going through
    `legacy_core`.
    - Move shared `codex resume` hint formatting from `codex-core` into
    `codex-utils-cli`.
    - Update CLI and TUI call sites to use the shared CLI utility, and keep
    the resume-command behavior covered by tests in its new home.
    
    ## Verification
    
    - `cargo test -p codex-utils-cli`
    - `cargo test -p codex-utils-cli resume_command`
  • [codex] add plugin marketplace CLI commands (#21396)
    ## Why
    
    Plugin CLI installs should behave more like `apt-get install`:
    configured marketplaces are the only install sources, the local
    marketplace snapshot is the package index used at install time, and
    `plugins/cache` is only a cache of already-downloaded plugin bytes.
    
    That distinction matters once marketplaces and plugins have auth or
    availability state. A repo-local marketplace manifest or leftover cached
    plugin artifact should not silently become an install source unless the
    marketplace was explicitly configured and its readable snapshot still
    authorizes the plugin.
    
    ## What Changed
    
    - add CLI commands to list configured marketplaces and add, list, or
    remove marketplace plugins
    - accept stable `plugin@marketplace` ids for add/remove while preserving
    the explicit `--marketplace` form
    - restrict `codex plugin add` and `codex plugin list` to configured
    marketplaces instead of also discovering current-working-directory
    marketplace roots
    - fail `codex plugin add` and `codex plugin list` when a configured
    marketplace snapshot is missing or malformed instead of treating it as
    an empty source or a generic plugin miss
    - preserve marketplace snapshot semantics: a configured local/Git
    marketplace snapshot can authorize installs without consulting the
    original upstream source
    - allow `plugins/cache` reuse only after configured marketplace
    resolution succeeds
    - keep removal resilient after marketplace deletion or drift and ignore
    malformed marketplace config entries in listing
    
    ## Commands Added
    
    - `codex plugin add <plugin>@<marketplace>`
    - `codex plugin add <plugin> --marketplace <marketplace>`
    - `codex plugin list`
    - `codex plugin list --marketplace <marketplace>`
    - `codex plugin remove <plugin>@<marketplace>`
    - `codex plugin remove <plugin> --marketplace <marketplace>`
    - `codex plugin marketplace add <source>`
    - `codex plugin marketplace add <source> --ref <ref>`
    - `codex plugin marketplace add <source> --sparse <path>`
    - `codex plugin marketplace list`
    - `codex plugin marketplace upgrade`
    - `codex plugin marketplace upgrade <marketplace>`
    - `codex plugin marketplace remove <marketplace>`
    
    ## CLI Help Output
    
    <details>
    <summary><code>codex plugin --help</code></summary>
    
    ```text
    Manage Codex plugins
    
    Usage: codex plugin [OPTIONS] <COMMAND>
    
    Commands:
      add          Install a plugin from a configured marketplace snapshot
      list         List plugins available from configured marketplace snapshots
      marketplace  Add, list, upgrade, or remove configured plugin marketplaces
      remove       Remove an installed plugin from local config and cache
      help         Print this message or the help of the given subcommand(s)
    ```
    
    </details>
    
    <details>
    <summary><code>codex plugin add --help</code></summary>
    
    ```text
    Install a plugin from a configured marketplace snapshot.
    
    Pass either `PLUGIN@MARKETPLACE` or pass `PLUGIN` with `--marketplace MARKETPLACE`.
    
    Usage: codex plugin add [OPTIONS] <PLUGIN[@MARKETPLACE]>
    
    Arguments:
      <PLUGIN[@MARKETPLACE]>
              Plugin selector to install: either PLUGIN@MARKETPLACE or PLUGIN with --marketplace
    
    Options:
      -m, --marketplace <MARKETPLACE>
              Configured marketplace name to use when PLUGIN does not include @MARKETPLACE
    
    Examples:
      codex plugin add sample@debug
      codex plugin add sample --marketplace debug
    ```
    
    </details>
    
    <details>
    <summary><code>codex plugin list --help</code></summary>
    
    ```text
    List plugins available from configured marketplace snapshots
    
    Usage: codex plugin list [OPTIONS]
    
    Options:
      -m, --marketplace <MARKETPLACE>
              Only list plugins from this configured marketplace name
    
    Examples:
      codex plugin list
      codex plugin list --marketplace debug
    ```
    
    </details>
    
    <details>
    <summary><code>codex plugin remove --help</code></summary>
    
    ```text
    Remove an installed plugin from local config and cache.
    
    Pass either `PLUGIN@MARKETPLACE` or pass `PLUGIN` with `--marketplace MARKETPLACE`.
    
    Usage: codex plugin remove [OPTIONS] <PLUGIN[@MARKETPLACE]>
    
    Arguments:
      <PLUGIN[@MARKETPLACE]>
              Plugin selector to remove: either PLUGIN@MARKETPLACE or PLUGIN with --marketplace
    
    Options:
      -m, --marketplace <MARKETPLACE>
              Marketplace name to use when PLUGIN does not include @MARKETPLACE
    
    Examples:
      codex plugin remove sample@debug
      codex plugin remove sample --marketplace debug
    ```
    
    </details>
    
    <details>
    <summary><code>codex plugin marketplace --help</code></summary>
    
    ```text
    Add, list, upgrade, or remove configured plugin marketplaces
    
    Usage: codex plugin marketplace [OPTIONS] <COMMAND>
    
    Commands:
      add      Add a local or Git marketplace to the configured marketplace sources
      list     List configured marketplace names and their local snapshot roots
      upgrade  Refresh configured Git marketplace snapshots
      remove   Remove a configured marketplace source by name
    ```
    
    </details>
    
    <details>
    <summary><code>codex plugin marketplace add --help</code></summary>
    
    ```text
    Add a local or Git marketplace to the configured marketplace sources
    
    Usage: codex plugin marketplace add [OPTIONS] <SOURCE>
    
    Arguments:
      <SOURCE>
              Marketplace source: a local path, owner/repo[@ref], HTTPS Git URL, or SSH Git URL
    
    Options:
          --ref <REF>
              Git ref to fetch for Git marketplace sources
    
          --sparse <PATH>
              Sparse checkout path for Git marketplace sources. Can be repeated
    
    Examples:
      codex plugin marketplace add ./path/to/marketplace
      codex plugin marketplace add owner/repo --ref main
      codex plugin marketplace add https://github.com/owner/repo --sparse plugins/foo
    ```
    
    </details>
    
    <details>
    <summary><code>codex plugin marketplace list --help</code></summary>
    
    ```text
    List configured marketplace names and their local snapshot roots
    
    Usage: codex plugin marketplace list [OPTIONS]
    ```
    
    </details>
    
    <details>
    <summary><code>codex plugin marketplace upgrade --help</code></summary>
    
    ```text
    Refresh configured Git marketplace snapshots.
    
    Omit MARKETPLACE_NAME to upgrade all configured Git marketplaces.
    
    Usage: codex plugin marketplace upgrade [OPTIONS] [MARKETPLACE_NAME]
    
    Arguments:
      [MARKETPLACE_NAME]
              Optional configured marketplace name to upgrade. Omit to upgrade all Git marketplaces
    
    Examples:
      codex plugin marketplace upgrade
      codex plugin marketplace upgrade debug
    ```
    
    </details>
    
    <details>
    <summary><code>codex plugin marketplace remove --help</code></summary>
    
    ```text
    Remove a configured marketplace source by name
    
    Usage: codex plugin marketplace remove [OPTIONS] <MARKETPLACE_NAME>
    
    Arguments:
      <MARKETPLACE_NAME>
              Configured marketplace name to remove
    
    Example:
      codex plugin marketplace remove debug
    ```
    
    </details>
    
    ## Public Semantics
    
    - `codex plugin add <plugin>@<marketplace>` succeeds only when
    `<marketplace>` is configured and its local marketplace snapshot
    contains `<plugin>`
    - repo-local marketplaces are not install sources until the user runs
    `codex plugin marketplace add ...`
    - configured marketplace snapshots must be readable; missing or
    malformed snapshots fail the CLI operation rather than silently falling
    through to cache or empty results
    - cached plugin artifacts can satisfy reinstall only when the configured
    marketplace snapshot still authorizes that plugin
    - cached plugin artifacts alone never make a plugin installable
    
    ## Tests
    
    - `cargo test -p codex-cli --test plugin_cli`
    - `cargo clippy -p codex-cli --tests -- -D warnings`
    - `cargo test -p codex-cli`
    - `git diff --check`
    - `just bazel-lock-update`
    - `just bazel-lock-check`