7919 Commits

  • TUI: Unified mentions tweaks + polish mentions rendering (#23363)
    This change keeps unified @mentions behind the mentions_v2 gate, moves
    the flag to under-development, and polishes mention rendering/history
    behavior.
    
    It also adds a few small improvements to the mentions feature around
    mention rendering and history round-tripping for plugin/tool mentions in
    message edit scenarios. Plugin selections now insert `@` mentions with
    better casing, and saved history preserves the visible sigil so recalled
    messages look the same as what the user typed.
    
    - Preserves `@` sigils when encoding/decoding mention history for
    tool/plugin paths.
    - Improves plugin mention insertion so display names/casing are
    reflected more cleanly in the composer.
    - Update composer to render user-entered plugin mentions in the same
    color as the mentions menu. ALso applies to recalled/edited messages.
    - Left/right arrows no longer switch unified-mention search modes after
    an @mention has already been accepted (Ex: arrowing left through a
    composed message that contains @mentions).
    - Keeps bound mentions stable around punctuation, so accepted `@`
    mentions do not reopen the popup and punctuated `$` mentions still
    persist to cross-session history.
    
    **Steps to test**
    - Ensure mentions_v2 is enabled through configuration or `--enable
    mentions_v2`
    - Type `@` in the TUI composer and verify filesystem/plugin/skill
    results are displayed in the unified mentions menu.
    - Select a plugin mention from the `@` popup and confirm the inserted
    text is an `@...` mention with casing, then recall/edit the message and
    confirm it still renders as `@...`.
    - Mention a skill and verify that skills still insert as `$skill`
    mentions rather than `@` mentions.
    - Verify punctuated mentions such as `@plugin.` and `($skill)` keep
    their bound mention behavior across editing and history recall.
  • chore: add GPT-5.5 to the Amazon Bedrock catalog (#24701)
    ## Summary
    
    Amazon Bedrock should expose GPT-5.5 alongside GPT-5.4, and the Bedrock
    GPT entries should stay aligned with the canonical bundled OpenAI model
    metadata instead of carrying a separate hand-written copy that can drift
    over time. This change will be merged when the model is online.
    
    This change:
    
    - Adds the Bedrock Mantle model id for `openai.gpt-5.5`.
    - Builds the Bedrock GPT-5.5 and GPT-5.4 catalog entries from the
    bundled OpenAI model catalog, then overrides the Bedrock-facing slug,
    explicit priority, and Bedrock-specific context windows.
    - Hardcodes both `context_window` and `max_context_window` to `272000`
    for Bedrock GPT-5.5 and GPT-5.4.
    - Keeps `openai.gpt-5.5` as the default Bedrock model ahead of
    `openai.gpt-5.4` and the Bedrock OSS models.
  • Fix extension turn item emitter test event ordering (#24936)
    ## Why
    
    PR #24813 added extension `TurnItemEmitter` coverage and introduced a
    test that records a conversation history item before asserting
    extension-emitted turn item events.
    
    `record_conversation_items()` also emits a `RawResponseItem` event to
    observers. The test was reading from the same event receiver and
    expected the next event to be `ItemStarted`, so the test failed reliably
    once the setup history item was present.
    
    ## What Changed
    
    Update
    `passes_turn_fields_and_scoped_turn_item_emitter_to_extension_call` to
    consume and assert the expected setup `RawResponseItem` before checking
    the extension `ItemStarted`, `WebSearchBegin`, `ItemCompleted`, and
    `WebSearchEnd` events.
    
    This is test-only and does not change extension runtime behavior.
    
    ## Verification
    
    - `cargo nextest run --no-fail-fast -p codex-core
    tools::handlers::extension_tools::tests::passes_turn_fields_and_scoped_turn_item_emitter_to_extension_call`
  • Reap stale multi-agent slots (#24903)
    ## Summary
    
    - Let `close_agent` clean up an agent that is still registered in
    `AgentRegistry` even when its underlying thread is already missing.
    - Preserve the explicit-close boundary: for known stale thread-spawn
    agents, mark the persisted spawn edge `Closed`, then treat
    `ThreadNotFound` / `InternalAgentDied` as a successful close so the
    registry slot can be released.
    - Add a regression for MultiAgentV2 task-name targets where
    `close_agent("worker")` succeeds after the worker thread has already
    disappeared.
    
    ## Motivation
    
    A worker can disappear from `ThreadManager` while its metadata still
    exists in the root `AgentRegistry`. Before this change, the close tool
    failed while trying to subscribe to the missing thread status, so it
    never reached the cleanup path that releases the registered agent slot.
    With `agents.max_threads = 1`, an explicit close of that stale task-name
    agent could fail and leave the session unable to spawn a replacement.
    
    ## Scope
    
    This PR intentionally does not add automatic stale-agent reaping to
    `spawn_agent`, `resume_agent`, or `list_agents`. A thread being missing
    from `ThreadManager` is not the same as an explicit close: persisted
    open spawn edges are still the durable source of truth for resume and
    task-name ownership until `close_agent` is called.
    
    ## Validation
    
    - `just test -p codex-core -E
    'test(multi_agent_v2_close_agent_reaps_stale_task_name_target) |
    test(resume_agent_from_rollout_reopens_open_descendants_after_manager_shutdown)'`
    - `just fix -p codex-core`
  • Expose MCP server info as part of server status (#24698)
    # Summary
    
    Expose MCP server info via App Server (when available) so apps can
    render a richer MCP experience
  • feat(app-server): include turns page on thread resume (#23534)
    ## Summary
    
    The client currently calls `thread/resume` to establish live updates and
    immediately follows it with `thread/turns/list` to hydrate recent turns.
    This lets `thread/resume` return that page directly, eliminating a round
    trip and the ordering/deduplication gap between the two calls.
    
    Experimental clients opt in with `initialTurnsPage: { limit,
    sortDirection, itemsView }`. The response returns `initialTurnsPage` as
    a `TurnsPage`, including cursors for paging further back in history.
    Keeping the controls in a nested opt-in object provides the useful
    `thread/turns/list` knobs without spreading page-specific parameters
    across `thread/resume`.
    
    ## Verification
    
    - `just fmt`
    - `just write-app-server-schema --experimental`
    - `just write-app-server-schema`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server
    thread_resume_initial_turns_page_matches_requested_turns_list_page
    --tests`
    - `cargo test -p codex-app-server
    thread_resume_rejoins_running_thread_even_with_override_mismatch
    --tests`
    - `just fix -p codex-app-server-protocol -p codex-app-server`
  • extension-api: add TurnItemEmitter to tool calls (#24813)
    ## Why
    Extension-contributed tools need to emit visible turn items through
    Codex's normal event and persistence pipeline.
    
    ## What
    - Add `TurnItemEmitter` to extension `ToolCall`s and route the core
    implementation through `Session::emit_turn_item_*`.
    - Hold weak session and turn references so retained tool calls cannot
    keep host state alive.
    - Provide a no-op emitter for extension test callers.
    
    ## Test Plan
    - `just test -p codex-core -E
    'test(passes_turn_fields_and_scoped_turn_item_emitter_to_extension_call)'`
    
    ---------
    
    Co-authored-by: jif-oai <jif@openai.com>
  • Remove libubsan CI workaround (#24782)
    It seems that this was added to allow rustc to load proc macros that had
    been compiled with UBSan enabled, which zig does for debug and
    `ReleaseSafe` builds. When zig drives the link of the final binary it
    knows to include the ubsan runtime, but our zig-built artifacts are
    being linked into a binary whose linking rustc drives. This removes the
    libubsan workaround we have and replaces it with
    `-fno-sanitize=undefined` passed to zig.
    
    The new argument is passed at the end of zig's args so should take
    precedence over any earlier arguments from the script's caller.
  • Gate goal tools by thread eligibility (#24925)
    ## Why
    
    Goal tools create and update goal state for a persistent thread. The
    extension was only checking whether goals were enabled before
    advertising those tools, which meant they could be surfaced in contexts
    that should not receive thread goal controls: ephemeral threads without
    persistent thread state and review subagents.
    
    Those sessions can still run the goal extension lifecycle, but the
    thread tools should only be visible when the current thread can safely
    use them.
    
    ## What changed
    
    - Adds a `GoalRuntimeConfig` that separates goal enablement from whether
    goal tools are available for the current thread.
    - Computes tool eligibility on thread start from
    `persistent_thread_state_available` and `SessionSource`, hiding tools
    for review subagents.
    - Uses `GoalRuntimeHandle::tools_visible()` when contributing thread
    tools so enabled runtime state does not automatically imply tool
    exposure.
    - Adds backend coverage for hiding goal tools on ephemeral threads and
    review subagents.
    
    ## Testing
    
    - Added `goal_tools_hidden_for_ephemeral_threads`.
    - Added `goal_tools_hidden_for_review_subagents`.
  • 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
  • [codex] Update OpenAI Docs skill (#24914)
    ## Summary
    - update the bundled `openai-docs` system skill to match the latest
    `openai-docs-plus` content from `skills-internal`
    - add the cached Codex manual fetch helper and expand the skill routing
    for Codex self-knowledge
    - keep the stable local skill identity and labels as `openai-docs`
    
    ## Why
    The built-in OpenAI Docs skill needed to reflect the current upstream
    guidance from `skills-internal` while preserving the local system-skill
    name used by Codex.
    
    ## Impact
    Codex now ships the newer OpenAI Docs skill behavior for Codex
    self-knowledge and manual-first documentation lookups.
    
    ## Validation
    - `just test -p codex-skills`
    - exact directory diff against transformed `skills-internal`
    `origin/main` was clean
  • Add turn error lifecycle contributor (#24916)
    Summary
    - Add TurnErrorInput and TurnLifecycleContributor::on_turn_error to the
    extension API.
    - Emit the turn-error lifecycle from core turn error paths, including
    usage limit failures.
    - Add direct lifecycle coverage for the emitted error facts and stores.
    
    Tests
    - just fmt
    - git diff --check
    - Not run: full tests or clippy (per instructions)
  • Add thread start contributor facts (#24915)
    Summary: add session source and persistent-state availability to
    ThreadStartInput; populate them from session init; update existing goal
    test harness constructors. Tests: just fmt; git diff --check. No full
    tests or clippy run per request.
  • [codex-cli] Refresh near-expiry ChatGPT access tokens before requests (#23546)
    ## Summary
    
    - refresh managed ChatGPT auth during auth resolution when its access
    token is inside ChatGPT web's five-minute near-expiry window
    - cover refresh-window decisions while preserving the existing
    expired-token refresh path
    
    ## Why
    
    Codex already resolves managed ChatGPT auth before outbound requests and
    refreshes expired access tokens there. This change adjusts the existing
    predicate to refresh a still-valid access token once it is within the
    same five-minute refresh window used by ChatGPT web, avoiding a request
    with a token about to expire.
    
    A cross-process serialization follow-up was explored in #24663 and
    closed for now; we do not currently suspect cross-process refresh races
    are a root cause of the refresh errors under investigation.
    
    External-token, API-key, and Agent Identity auth modes remain unchanged.
    
    ## Validation
    
    - `bazel test //codex-rs/login:login-all-test`
    - `just fmt` runs Rust formatting successfully, then its Python SDK Ruff
    step cannot install `openai-codex-cli-bin==0.131.0a4` on this Linux
    environment because no compatible wheel is published.
  • Add Guardian review metrics (#24897)
    ## Why
    
    Guardian reviews already emit analytics events, but we do not expose
    aggregate OpenTelemetry metrics for review volume, latency, token usage,
    or terminal outcomes. That makes it harder to monitor Guardian behavior
    during rollouts and to compare review outcomes by source, action type,
    session kind, model, and failure mode.
    
    ## What Changed
    
    - Added Guardian review metric names for count, total duration, time to
    first token, and token usage in `codex-rs/otel`.
    - Added `core/src/guardian/metrics.rs` to convert
    `GuardianReviewAnalyticsResult` into sanitized metric tags covering
    decision, terminal status, failure reason, approval request source,
    reviewed action, session kind, risk/outcome, model, reasoning effort,
    and context/truncation state.
    - Emitted the new metrics from `track_guardian_review` for each terminal
    Guardian review result.
    
    ## Testing
    
    - Added
    `guardian_review_metrics_record_counts_durations_and_token_usage`, which
    verifies the emitted count, duration, TTFT, token usage histograms, and
    tag set through the in-memory metrics exporter.
  • Fix memories namespace for Responses API tools (#24898)
    ## Why
    
    Dedicated memories tools are exposed through a Responses API namespace
    tool. The namespace itself has to be a valid tool identifier, so
    `memories/` can fail validation before the model ever gets a chance to
    call the memory tools.
    
    ## What changed
    
    - Changed `MEMORY_TOOLS_NAMESPACE` from `memories/` to `memories`.
    - Added `memory_tool_namespace_matches_responses_api_identifier` so the
    namespace stays non-empty and limited to Responses-safe identifier
    characters.
    
    ## Verification
    
    - Added unit coverage for the namespace identifier shape in
    `codex-rs/ext/memories/src/tests.rs`.
  • [codex] Fix Guardian argument comment lint (#24902)
    ## Summary
    - Add the required `/*parent_thread_id*/` argument comment at the
    Guardian review session test callsite flagged by CI.
    
    ## Validation
    - `just fmt`
    - Not run: clippy/tests, per request; CI will cover them.
  • Use stable Guardian prompt cache keys (#24803)
    ## Why
    
    Guardian review sessions are reusable across forks when their
    `GuardianReviewSessionReuseKey` is unchanged, but the underlying
    Responses request was still using the child thread ID as
    `prompt_cache_key`. That meant forked Guardian reviews that should share
    cache context produced different cache keys, reducing prompt cache reuse
    and weakening the reuse invariant.
    
    ## What Changed
    
    - Adds a `ModelClient` prompt cache key override and uses it for
    `ResponsesApiRequest.prompt_cache_key`.
    - Computes Guardian review cache keys as
    `guardian:<sha1(parent_thread_id:reuse_key)>`, scoped to the parent
    thread plus the reuse-sensitive Guardian config.
    - Wires session construction to apply that override only for Guardian
    sub-agent sessions.
    
    ## Testing
    
    - Added coverage that Guardian cache keys are stable for the same
    parent/reuse key, change when either the parent thread or reuse key
    changes, fit within the Responses API length limit, and are absent for
    non-Guardian sessions.
    - Extended the parallel review test to assert forked Guardian reviews
    send the same `prompt_cache_key`.
  • Thread Guardian cache key through session (#24895)
    Split from the Guardian prompt cache key change. This PR only updates
    codex-rs/core/src/session/session.rs. Validation was not run per
    request; this branch is expected to rely on the companion split PRs.
  • Assert Guardian prompt cache key reuse (#24894)
    Split from the Guardian prompt cache key change. This PR only updates
    codex-rs/core/src/guardian/tests.rs. Validation was not run per request;
    this branch is expected to rely on the companion split PRs.
  • Add Guardian review prompt cache key (#24893)
    Split from the Guardian prompt cache key change. This PR only updates
    codex-rs/core/src/guardian/review_session.rs. Validation was not run per
    request; this branch is expected to rely on the companion split PRs.
  • Export Guardian prompt cache key helper (#24892)
    Split from the Guardian prompt cache key change. This PR only updates
    codex-rs/core/src/guardian/mod.rs. Validation was not run per request;
    this branch is expected to rely on the companion split PRs.
  • Stabilize Guardian client cache key handling (#24891)
    Split from the Guardian prompt cache key change. This PR only updates
    codex-rs/core/src/client.rs. Validation was not run per request; this
    branch is expected to rely on the companion split PRs.
  • Move memories root setup out of core config (#24758)
    ## Why
    
    Config loading should not create or write-authorize the memories root
    just because memory support exists. Memory startup is the code path that
    actually materializes that tree.
    
    ## What
    
    - Stop creating the memories root during Config load and remove it from
    legacy workspace-write projections.
    - Grant the memories root read access only when the memories feature and
    use_memories are enabled.
    - Create the memories root inside memories startup before seeding
    extension instructions.
    - Update config and startup tests around the ownership boundary.
    
    ## Tests
    
    - just fmt
    - just fix -p codex-core
    - just fix -p codex-memories-write
    - just test -p codex-core
    memory_tool_makes_memories_root_readable_without_creating_or_widening_writes
    workspace_write_includes_configured_writable_root_once_without_memories_root
    permission_profile_override_keeps_memories_root_out_of_legacy_projection
    permissions_profiles_allow_direct_write_roots_outside_workspace_root
    default_permissions_profile_populates_runtime_sandbox_policy
    - just test -p codex-memories-write memories_startup_creates_memory_root
    
    Note: a broader just test -p codex-core run is not clean in this
    sandbox; it hit missing test_stdio_server plus seatbelt, realtime, and
    environment-sensitive failures. The changed config tests above pass.
  • [codex] Stage Python SDK beta versions from release tags (#24872)
    ## Summary
    - Treat `sdk/python` as a development template with source version
    `0.0.0-dev`, matching the existing Python runtime packaging pattern.
    - Have `python-v*` tags supply the published SDK beta version through
    the existing `stage-sdk --sdk-version` path.
    - Remove the workflow check requiring a source version bump for each
    beta release and remove its now-unused host Python setup step.
    - Keep the reviewed runtime dependency pin at
    `openai-codex-cli-bin==0.132.0`.
    - Remove beta-number-specific documentation so it does not need editing
    for each publish.
    
    ## Why
    The package staging script already writes the release version into the
    artifact. Requiring the checked-in SDK template version to match every
    tag adds release-only source churn without changing the package users
    receive.
    
    ## Validation
    - Not run locally; relying on online CI for this workflow and metadata
    change.
    
    ## Release
    After this PR lands, publish the next beta by pushing tag
    `python-v0.1.0b2` from merged `main`.
  • [codex] Remove Python SDK beta warning note (#24870)
    ## Summary
    - Remove the beta warning callout from the PyPI-facing Python SDK
    README.
    - Keep the existing Beta title and install/usage guidance unchanged.
    
    ## Validation
    - Not run locally; relying on online CI for this documentation-only
    change.
    
    ## Release
    - Land this change before publishing the next Python SDK beta.
  • [codex] Remove Python SDK language classifiers (#24868)
    ## Summary
    - Remove the Python language classifiers from the Python SDK package
    metadata.
    - Keep `requires-python = ">=3.10"` as the package's interpreter
    compatibility constraint.
    - Avoid presenting a curated version-support list in PyPI metadata.
    
    ## Validation
    - Not run locally; relying on online CI for this metadata-only change.
    
    ## Release
    - Land this change before publishing the next Python SDK beta.
  • [codex] Simplify Python SDK install guidance (#24866)
    ## Summary
    - Remove the exact-version install snippet from the PyPI-facing Python
    SDK README.
    - Remove the release-selection explanation so the install section
    presents the standard `pip install openai-codex` path directly.
    
    ## Validation
    - Not run locally; relying on online CI for this documentation-only
    change.
  • Treat refresh_token_reused 400s as relogin-required (#24830)
    ## Summary
    - classify known refresh-token terminal failures from `/oauth/token` as
    permanent even when the backend returns `400`
    - preserve the existing relogin-required message for
    `refresh_token_reused` instead of retrying and collapsing into a generic
    cloud requirements error
    - add regression coverage for `400 refresh_token_reused`
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-login`
  • [codex] Prepare Python SDK beta documentation and package metadata (#24836)
    ## Why
    
    The initial public `openai-codex` beta should read and install like a
    normal published Python package before a release tag is created. This
    follows merged PR #24828, which establishes the independent SDK beta
    release plumbing and exact runtime dependency.
    
    ## What changed
    
    - Rewrote `sdk/python/README.md` as a compact PyPI-facing beta package
    page: published installation, one quickstart, short login examples,
    built-in help, and links to deeper guides.
    - Updated the getting-started guide, API reference, FAQ, and examples
    index to present the published beta consistently without repeating
    onboarding in the package landing page or reference page.
    - Made `pip install openai-codex` the primary install path while beta
    releases are the only published SDK releases, with `--pre` documented
    for opting into prereleases after a stable release exists.
    - Added curated `help()` / `pydoc` docstrings across the public API and
    generated public convenience methods through
    `scripts/update_sdk_artifacts.py`.
    - Declared the repository `Apache-2.0` license expression and
    Documentation URL in package metadata, without introducing a duplicated
    SDK-local license file.
    - Kept the source distribution focused on installable package material
    (`src/openai_codex`, `README.md`, and `pyproject.toml`); the repository
    docs and runnable examples remain linked from the PyPI README.
    - Built release artifacts in an Alpine container on the Ubuntu runner,
    matching Python SDK CI and allowing type generation to install the
    published `musllinux` runtime wheel.
    - Added `twine check --strict` to the release workflow so malformed PyPI
    metadata or rendered README content fails before publishing.
    - Added focused SDK assertions for beta metadata, the exact runtime pin,
    source distribution contents, and the built-in Python documentation
    surface.
    
    ## Validation
    
    - Ran `uv run --frozen --extra dev ruff check
    scripts/update_sdk_artifacts.py src/openai_codex
    tests/test_public_api_signatures.py
    tests/test_artifact_workflow_and_binaries.py` before the final
    README-only reductions and review-fix follow-ups.
    - Built `openai_codex-0.1.0b1-py3-none-any.whl` and
    `openai_codex-0.1.0b1.tar.gz` before the final README-only reductions
    and review-fix follow-ups.
    - Ran `python -m twine check --strict` on both built artifacts before
    the final README-only reductions and review-fix follow-ups.
    - Verified artifact metadata reports `Apache-2.0` without a duplicated
    SDK-local license file.
    - Verified `inspect.getdoc(...)` resolves documentation for the package,
    `Codex`, `CodexConfig`, and key generated thread methods.
    - Rebased the documentation/readiness change onto merged PR #24828
    without changing the intended SDK or workflow file contents.
    - Final verification is delegated to online CI for this PR.
  • [codex] Add independent beta release for the Python SDK (#24828)
    ## Why
    
    `openai-codex` needs a beta release lifecycle without requiring beta
    releases of its pinned runtime package. Previously, SDK staging rewrote
    its runtime dependency to the SDK version, which made an SDK-only beta
    impossible.
    
    ## What changed
    
    - Set the initial SDK beta version to `0.1.0b1` and pin it to published
    stable `openai-codex-cli-bin==0.132.0`.
    - Decoupled SDK release staging from runtime versioning so it preserves
    the reviewed exact runtime pin.
    - Added a `python-v*` tag workflow that builds and publishes only
    `openai-codex` through PyPI trusted publishing.
    - Removed the Beta classifier from runtime package metadata for future
    runtime publications.
    - Regenerated protocol-derived SDK models from the selected stable
    runtime package.
    
    `0.132.0` is the newest stable runtime admitted by the checked-in
    dependency date fence and retains the Linux wheel family currently used
    by SDK CI.
    
    ## Release setup
    
    Before pushing `python-v0.1.0b1`, configure PyPI trusted publishing for
    the `openai-codex` project with workflow `python-sdk-release.yml`,
    environment `pypi`, and job `publish-python-sdk`.
    
    ## Validation
    
    - `uv run --frozen --extra dev ruff check src/openai_codex scripts
    examples tests`
    - Parsed `.github/workflows/python-sdk-release.yml` with PyYAML.
    - Built staged release artifacts locally:
    `openai_codex-0.1.0b1-py3-none-any.whl` and
    `openai_codex-0.1.0b1.tar.gz`.
    - Verified wheel metadata pins `openai-codex-cli-bin==0.132.0`.
    - Tests are deferred to online CI for this PR.
  • [codex] Remove redundant SQLite dynamic tool storage (#24819)
    ## Why
    
    Dynamic tools are defined at thread start and already stored in rollout
    `SessionMeta`, which restores resumed and forked sessions. Persisting
    the same tools through SQLite creates a second runtime persistence path
    that is unnecessary prework for the explicit namespace refactor.
    
    ## What changed
    
    - Restore missing thread-start dynamic tools directly from rollout
    history, including when SQLite is enabled.
    - Remove SQLite dynamic-tool reads, writes, backfill, and thread
    metadata patch plumbing.
    - Add SQLite-enabled resume integration coverage that verifies a
    rollout-defined dynamic tool is still sent after resume.
    
    ## Compatibility
    
    The existing `thread_dynamic_tools` table is intentionally not dropped
    even though it's now unused. Older Codex binaries are allowed to open
    databases migrated by newer binaries and still reference this table;
    dropping it would break that mixed-version path. See
    [here](https://github.com/openai/codex/blob/main/codex-rs/state/src/migrations.rs#L10-L11).
    
    ## Verification
    
    - `just test -p codex-state -p codex-rollout -p codex-thread-store`
    - `just test -p codex-core --test all
    resume_restores_dynamic_tools_from_rollout_with_sqlite_enabled`
  • [codex] Rename Python SDK AppServerConfig to CodexConfig (#24800)
    ## Why
    
    `AppServerConfig` is exported as part of the ergonomic Python SDK
    surface and passed to `Codex(...)` and `AsyncCodex(...)`. That name
    exposes the underlying app-server transport at the same layer where
    users are configuring the Codex client. `CodexConfig` makes the common
    callsite read naturally and names the object it configures.
    
    ## What changed
    
    - Renamed the public configuration dataclass from `AppServerConfig` to
    `CodexConfig`.
    - Updated `Codex`, `AsyncCodex`, and the transport clients to accept
    `CodexConfig`.
    - Updated binary-resolution messages, package exports, docs, examples,
    and related coverage to use the new public name.
    
    ## API impact
    
    ```python
    from openai_codex import Codex, CodexConfig
    
    with Codex(config=CodexConfig(codex_bin="/path/to/codex")) as codex:
        ...
    ```
    
    Callers should now import and construct `CodexConfig`; `AppServerConfig`
    is no longer part of the Python SDK surface.
    
    ## Validation
    
    - `uv run --frozen --extra dev ruff check src/openai_codex scripts
    examples tests`
    - Tests are deferred to online CI for this PR.
  • [codex] Fix hyperlink-aware key-value table rendering (#24825)
    ## Why
    
    The key/value markdown table renderer added in #24636 still operates on
    `Line` values, while table cells and rendered table output now carry
    `HyperlinkLine`. That mismatch breaks `codex-tui` compilation on `main`
    and would risk losing semantic web-link annotations if corrected by
    flattening the values.
    
    ## What changed
    
    - Make key/value record rendering wrap and emit `HyperlinkLine` values
    consistently with the existing grid renderer.
    - Remap wrapped hyperlink ranges and shift them when value content is
    prefixed by record-mode indentation or labels.
    - Add focused coverage verifying key/value fallback output preserves
    web-link destinations.
    
    ## Verification
    
    - `just test -p codex-tui -E
    'test(key_value_table_keeps_web_annotations) |
    test(/table_renders_(key_value_records_when_compact_fragmentation_is_systemic_snapshot|stacked_key_value_records_when_path_column_becomes_too_narrow_snapshot|records_when_multiple_prose_columns_are_starved_snapshot)/)'`
  • 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`
  • feat(tui): render cramped markdown tables as key-value records [2 of 2] (#24636)
    ## Stack
    
    - **Base: #24489 [1 of 2]** - render markdown tables in app style.
    - **Current: #24636 [2 of 2]** - render cramped markdown tables as
    key/value records.
    
    Review this PR against `fcoury/app-style-markdown-tables`; it contains
    only the fallback behavior for cramped tables.
    
    ## Why
    
    The row-separated markdown table rendering in #24489 remains readable
    while columns have usable room. Once long links or multiple prose-heavy
    columns are compressed into narrow allocations, however, the grid can
    turn words and paths into tall vertical strips that are difficult to
    scan. In those cases the content matters more than preserving the grid
    shape.
    
    ## What Changed
    
    <table>
    <tr><td>
    <p align="center"><b>
    Normal
    </b></p>
    <img width="1722" height="619" alt="CleanShot 2026-05-27 at 14 32 57"
    src="https://github.com/user-attachments/assets/d04f5fbd-6064-4acd-91bd-072d19b983df"
    />
    </td></tr>
    <tr><td>
    <p align="center"><b>
    Narrow
    </b></p>
    <img width="863" height="1013" alt="CleanShot 2026-05-27 at 14 33 12"
    src="https://github.com/user-attachments/assets/6a7d2968-0a68-48fd-ab5d-209b3dbaf03e"
    />
    </td></tr>
    <tr><td>
    <p align="center"><b>
    Very narrow
    </b></p>
    <img width="435" height="746" alt="CleanShot 2026-05-27 at 14 33 47"
    src="https://github.com/user-attachments/assets/f6a59e30-b1d2-4063-9c05-43933abc77d6"
    />
    </td></tr>
    </table>
    
    - Detect tables whose grid allocation causes systemic token
    fragmentation or starves multiple prose-heavy columns.
    - Render those tables as repeated key/value records instead of retaining
    an unreadable grid.
    - Use aligned label/value records when there is useful horizontal room,
    and switch to a stacked narrow-record layout where each label is
    followed by a full-width value when width is especially constrained.
    - Preserve the themed label color, rich inline formatting, links, and
    the existing grid presentation for tables that remain readable.
    - Add snapshot coverage for path-heavy narrow tables, prose-heavy issue
    tables, systemic compact fragmentation, and a control case that should
    continue to render as a grid.
    
    ## How to Test
    
    1. Start Codex from this branch and render a normal multi-column
    markdown table at a comfortable terminal width. Confirm it still appears
    as the styled row-separated grid from #24489.
    2. Render a table containing a long linked record identifier or
    file-like value, then narrow the terminal until the grid would split the
    value into vertical fragments. Confirm it switches to key/value records,
    with labels above values at very narrow widths.
    3. Render a table with multiple prose-heavy columns, such as an issue
    summary table with `Issue`, `Activity`, `Complexity`, and `Why start`.
    Confirm a cramped width switches to records rather than wrapping several
    columns into hard-to-read strips.
    4. Render a compact table where only one value wraps mildly. Confirm it
    stays in grid form rather than switching prematurely.
    
    ## Validation
    
    - Ran `just test -p codex-tui` while developing the fallback and
    reviewed/accepted the intended new markdown-render snapshots. The
    command still reports two unrelated existing guardian feature-flag test
    failures outside this diff.
    - Ran `just fix -p codex-tui` and `just fmt` after the Rust changes were
    complete.
    - `just argument-comment-lint` cannot reach source linting locally
    because Bazel fails while resolving LLVM sanitizer headers; touched
    positional literal callsites were inspected manually and annotated where
    needed.
  • feat(tui): add OSC 8 web links to rich content (#24472)
    ## Why
    
    Wrapped URLs in rich TUI output, especially URLs rendered inside
    Markdown tables, are split across terminal rows. In terminals that
    support OSC 8 hyperlinks, treating each visible fragment as part of the
    complete destination enables reliable open-link and copy-link actions
    even after table layout wraps the URL.
    
    This addresses the semantic-link portion of #12200 and the behavior
    described in
    https://github.com/openai/codex/issues/12200#issuecomment-4535452980. It
    does not change ordinary drag-selection across bordered table rows.
    
    ## What Changed
    
    - Added shared TUI OSC 8 support that validates `http://` and `https://`
    destinations, sanitizes terminal payloads, and applies metadata
    separately from visible line width/layout.
    - Added semantic web-link annotations to assistant and proposed-plan
    Markdown, including explicit web links and bare web URLs in prose and
    table cells while excluding code and non-web Markdown destinations.
    - Preserved complete URL targets through table wrapping, narrow pipe
    fallback, streaming, transcript overlay rendering, history insertion,
    and resize replay.
    - Routed intentional Codex-owned links in notices,
    status/setup/app-link, feedback, onboarding, MCP/plugin help, memories,
    and update surfaces through the shared hyperlink handling.
    
    ## How to Test
    
    1. Run Codex in a terminal with OSC 8 link support, such as Ghostty, and
    request an assistant response containing a Markdown table whose last
    column contains a long `https://` URL.
    2. Make the terminal narrow enough for the URL to wrap across multiple
    bordered table rows.
    3. Use the terminal's open-link or copy-link action on more than one
    wrapped URL fragment and confirm each fragment resolves to the complete
    original URL.
    4. Resize the terminal after the table is rendered and repeat the link
    action to confirm the destination survives scrollback replay.
    5. Open the transcript overlay while rich output is present and confirm
    web links remain interactive there.
    6. As a regression check, render inline/fenced code containing URL text
    and a Markdown link such as
    `[https://example.com](mailto:support@example.com)`; confirm these do
    not acquire a web OSC 8 destination.
    
    Targeted automated coverage exercised Markdown links and exclusions,
    wrapped and pipe-fallback tables, streaming/transcript overlay
    propagation, status-link truncation, and rendered word-wrapping cell
    alignment. `just test -p codex-tui` was also run; it passed the
    hyperlink coverage and reproduced two unrelated existing guardian
    feature-flag test failures.
  • fix(linux-sandbox): preserve shell cleanup on interruption (#22729)
    ## Why
    Interrupted `shell_command` calls can race with the outer tool-dispatch
    cancellation path. When that happens, the runtime future may be dropped
    before the spawned process gets a chance to run `SIGTERM` cleanup. For
    bwrapd-backed Linux sandbox commands, that can leave synthetic
    protected-path mount bookkeeping such as `.git/.codex` registrations
    under `/tmp` behind after a TUI interruption.
    
    The relevant cancellation points are the outer dispatch race in
    [`core/src/tools/parallel.rs`](https://github.com/openai/codex/blob/bd184ba84703cc924921ed883f0cf17d3dba60ff/codex-rs/core/src/tools/parallel.rs#L91-L132)
    and the process shutdown logic in
    [`core/src/exec.rs`](https://github.com/openai/codex/blob/bd184ba84703cc924921ed883f0cf17d3dba60ff/codex-rs/core/src/exec.rs#L1367-L1393).
    
    ## What changed
    - Keep `shell_command` dispatch alive long enough for the runtime to
    finish cancellation cleanup instead of immediately returning the
    synthetic aborted response.
    - Fold shell-turn cancellation into the existing `ExecExpiration` path
    in
    [`core/src/tools/runtimes/shell.rs`](https://github.com/openai/codex/blob/bd184ba84703cc924921ed883f0cf17d3dba60ff/codex-rs/core/src/tools/runtimes/shell.rs#L267-L274),
    so cancellation and timeout behavior stay centralized.
    - On cancellation, send `SIGTERM` first, wait briefly for cleanup to
    run, then hard-kill any remaining descendants in the original process
    group.
    - Treat `ESRCH` as an already-gone process-group cleanup case in
    `codex-utils-pty`, which keeps best-effort teardown from surfacing a
    stale-process race as an error.
    
    ## Verification
    - `cargo test -p codex-core cancellation`
    - Added regression coverage for:
      - `shell_tool_cancellation_waits_for_runtime_cleanup`
      - `process_exec_tool_call_cancellation_allows_sigterm_cleanup`
  • chore: enable namespace tools for Bedrock (#24713)
    Client-side namespace tools are now supported by bedrock. Enable
    `namespace_tools` for the Amazon Bedrock provider while continuing to
    disable unsupported hosted tools such as image generation and web
    search.
  • feat(tui): render markdown tables in app style [1 of 2] (#24489)
    ## Stack
    
    - **Current: #24489 [1 of 2]** - render markdown tables in app style.
    - **Stacked follow-up: #24636 [2 of 2]** - render cramped markdown
    tables as key/value records.
    
    ## Why
    
    Markdown tables currently render as boxed terminal grids, which gives
    ordinary assistant output a heavier visual treatment than surrounding
    rich text. This row-separated layout is the best match for how the App
    renders tables, while accented headers remain distinguishable even when
    a terminal font renders bold subtly.
    
    <table>
    <tr><td>
    <p align="center">Codex CLI - Before</p>
    <img width="1722" height="742" alt="CleanShot 2026-05-25 at 18 46 17"
    src="https://github.com/user-attachments/assets/f673d92a-ebd8-46e2-b414-3d985e41b6a4"
    />
    </td></tr>
    <tr><td>
    <p align="center">Codex CLI - After</p>
    <img width="1720" height="957" alt="image"
    src="https://github.com/user-attachments/assets/36a3d331-bea1-439b-b5be-e97b0731bd6f"
    />
    </td></tr>
    <tr><td>
    <p align="center">Codex App</p>
    <img width="979" height="1293" alt="CleanShot 2026-05-25 at 18 45 04"
    src="https://github.com/user-attachments/assets/7d97cae0-9256-4f6e-a4b3-8b8f22b0d901"
    />
    </td></tr>
    </table>
    
    ## What Changed
    
    - Render markdown tables as padded, aligned rows without an enclosing
    box.
    - Style table headers with the active syntax-theme accent plus bold
    text, while keeping separators low contrast and theme-aware.
    - Use a segmented heavy header rule and thin body-row rules, preserving
    wrapping, narrow-width fallback, streaming parity, and rich-history
    rendering.
    - Update focused assertions and snapshots for the final table layout.
    
    ## How to Test
    
    1. Render a markdown table in the TUI with several rows and columns.
    2. Confirm the header uses the active theme accent, rows use
    one-character interior padding, and the table has no enclosing box.
    3. Confirm the header is followed by segmented `━` rules and multiple
    body rows are separated by muted segmented `─` rules.
    4. Render the same table while streaming and in history/raw-mode
    toggles; the final rich layout should remain stable.
    5. Render a narrow table with long content and verify wrapping or pipe
    fallback does not overflow.
    
    ## Validation
    
    - `just test -p codex-tui table`
    - `just test -p codex-tui streaming::controller::tests`
    - `just argument-comment-lint-from-source -p codex-tui -- --all-targets`
    - `just fix -p codex-tui`
    - `just fmt`
    
    `just test -p codex-tui` was also run after accepting the snapshots; it
    fails only in the unrelated existing guardian app tests
    `update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
    and
    `update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`.
  • feat(tui): make turn interruption keybind configurable (#24766)
    ## Why
    
    Interrupting an active turn is currently fixed to `Esc`, which is easy
    to hit accidentally and cannot be customized through `/keymap`. This
    gives users a less accidental binding while preserving the existing
    default.
    
    ## What Changed
    
    - Adds `tui.keymap.chat.interrupt_turn` to `/keymap`, defaulting to
    `esc` and supporting remapping or unbinding.
    - Uses the configured interrupt binding for running-turn status, queued
    steer interruption, and `request_user_input`, including the visible
    hints.
    - Preserves local `Esc` behavior for popups, Vim insert mode, and
    `/agent` editing while validating conflicts with fixed/backtrack and
    request-input navigation bindings.
    - Adds behavior and snapshot coverage for remapped interruption paths.
    
    ## How to Test
    
    1. Run Codex and open `/keymap`, then set **Interrupt Turn** to `f12`.
    2. Start a turn and confirm `Esc` no longer interrupts it while `f12`
    does; the running hint should display `f12 to interrupt`.
    3. Queue a steer while a turn is running and confirm the preview
    displays `f12`; pressing it should interrupt and submit the steer
    immediately.
    4. Trigger a `request_user_input` prompt and confirm its footer uses
    `f12`; with notes open, `Esc` should still clear notes while `f12`
    interrupts the turn.
    5. Clear the Interrupt Turn binding and confirm the key-specific
    interrupt hint is removed while `Ctrl+C` remains available.
    
    Targeted validation:
    
    - `just write-config-schema`
    - `just fix -p codex-config`
    - `just fix -p codex-tui`
    - `just fmt`
    - `just argument-comment-lint-from-source -p codex-config -p codex-tui`
    - `just test -p codex-config`
    - `cargo insta pending-snapshots --manifest-path tui/Cargo.toml`
    - `just test -p codex-tui keymap_setup::tests`
    - `just test -p codex-tui` (fails in two pre-existing guardian
    feature-flag tests unrelated to this diff; the intentional picker
    snapshot updates were reviewed and accepted)
  • feat(tui): add vim text object bindings (#24382)
    ## Why
    
    Vim mode currently supports some normal-mode operators and motions, but
    common text-object combinations like `ciw`, `daw`, `di(`, and
    quote/bracket variants are still missing. That makes the composer feel
    incomplete for users who expect operator + text object editing to work
    inside prompts.
    
    Closes #21383.
    
    ## What Changed
    
    - Add Vim pending-state support for operator/text-object sequences.
    - Add `c` as a normal-mode operator for text objects, so combinations
    like `ciw` delete the object and enter insert mode.
    - Support word, WORD, delimiter, and quote text objects:
      - `iw`, `aw`, `iW`, `aW`
      - `i(`, `a(`, `i)`, `a)`, `ib`, `ab`
      - `i[`, `a[`, `i]`, `a]`
      - `i{`, `a{`, `i}`, `a}`, `iB`, `aB`
      - `i"`, `a"`, `i'`, `a'`, `i\``, `a\``
    - Add configurable keymap entries and keymap picker coverage for the new
    Vim text-object context.
    - Regenerate the config schema and update keymap picker snapshots.
    
    ## How to Test
    
    Manual smoke test:
    
    1. Start Codex with Vim composer mode enabled.
    2. Type a draft such as:
       ```text
       alpha beta gamma
       call(foo[bar], {"x": "hello world"})
       say "one \"two\" three" now
       ```
    3. Put the cursor on `beta`, press `ciw`, and confirm `beta` is removed
    and the composer enters insert mode.
    4. Escape back to normal mode, put the cursor on `gamma`, press `daw`,
    and confirm `gamma` plus surrounding whitespace is removed.
    5. Put the cursor inside `foo[bar]`, press `di[`, and confirm only `bar`
    is removed.
    6. Put the cursor inside `call(...)`, press `da(`, and confirm the whole
    parenthesized section is removed.
    7. Put the cursor inside the quoted text, press `ci"`, and confirm the
    quote contents are removed and insert mode starts.
    8. Verify cancellation does not edit text: press `d` then `Esc`, and
    press `d` then `i` then `Esc`.
    
    Targeted tests:
    
    - `cargo test -p codex-tui --lib vim_`
    - `cargo nextest run -p codex-tui keymap_setup::tests`
    
    Additional local checks:
    
    - `just write-config-schema`
    - `just fmt`
    - `just fix -p codex-tui`
    - `git diff --check`
    - `cargo insta pending-snapshots --manifest-path tui/Cargo.toml`
    
    Local full-suite note: `just test -p codex-tui` ran to completion. The
    keymap snapshot failures were expected and accepted. Two unrelated
    guardian feature-flag tests still fail locally:
    -
    `app::tests::update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
    -
    `app::tests::update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`
    
    `just argument-comment-lint` is currently blocked locally by Bazel
    analysis before the lint runs because `compiler-rt` has an empty
    `include/sanitizer/*.h` glob in the local Bazel cache. The touched Rust
    diff was manually inspected for opaque positional literals.
  • [codex] Add friendly Python SDK sandbox presets (#24772)
    ## Why
    
    The Python SDK currently exposes sandbox selection differently depending
    on where it is used: thread lifecycle methods accept `SandboxMode`,
    while turns accept the lower-level `SandboxPolicy` shape. For the common
    case of choosing an access level, that leaks app-server wire details
    into otherwise straightforward SDK usage.
    
    This makes the common path explicit and discoverable: callers choose a
    named sandbox preset once, using the same keyword on threads and turns.
    The preset name `workspace_write` also makes the granted capability
    clear at the callsite.
    
    ## What changed
    
    - Added a root-level `Sandbox` enum with documented presets:
      - `Sandbox.read_only`: read files without allowing writes.
    - `Sandbox.workspace_write`: the normal default for projects with a
    recorded trust decision; read files and write inside the workspace and
    configured writable roots.
      - `Sandbox.full_access`: run without filesystem access restrictions.
    - Documented that omitting `sandbox=` delegates to app-server's
    configured default, while explicit turn overrides remain sticky for
    subsequent turns.
    - Updated sync and async thread lifecycle and turn APIs to consistently
    accept `sandbox=Sandbox...`, translating to the existing app-server
    thread and turn representations internally.
    - Updated the public API artifact generator so regenerated SDK wrappers
    retain the friendly enum shape.
    - Replaced low-level policy construction in Python docs, examples, and
    the walkthrough notebook with the preset API.
    - Added focused coverage for root exports, method signatures,
    preset-to-wire mapping, and rejection of raw string sandbox inputs.
    
    ## API impact
    
    High-level turn calls now use `sandbox=` instead of `sandbox_policy=`:
    
    ```python
    from openai_codex import Codex, Sandbox
    
    with Codex() as codex:
        thread = codex.thread_start(sandbox=Sandbox.workspace_write)
        result = thread.run("Review the diff only.", sandbox=Sandbox.read_only)
    ```
    
    `thread_start(...)` already defaults to `ApprovalMode.auto_review`, so
    normal writable usage is concise:
    
    ```python
    with Codex() as codex:
        thread = codex.thread_start(sandbox=Sandbox.workspace_write)
        thread.run("Update the files in this workspace.")
    ```
    
    With that combination, edits inside `cwd` and configured writable roots
    run within the workspace-write sandbox. Operations that require
    approval, such as edits outside those roots, are routed through auto
    review. When `sandbox=` is omitted, app-server resolves its configured
    default. A sandbox supplied to `run(...)` or `turn(...)` applies to that
    turn and subsequent turns.
    
    ## Test coverage
    
    - `sdk/python/tests/test_public_api_signatures.py` covers the public
    export and parameter names, including the default approval mode.
    - `sdk/python/tests/test_public_api_runtime_behavior.py` covers preset
    mappings to the existing wire types and raw string rejection.
  • [codex] add compaction metadata to turn headers (#24368)
    ## Summary
    - Add `request_kind` values for foreground turn, startup prewarm,
    compaction, and detached memory model requests.
    - Attach compaction dispatch metadata to local Responses, legacy
    `/v1/responses/compact`, and remote v2 compact requests.
    - Add the existing logical context-window identifier as `window_id` on
    turn-owned model request metadata.
    - Keep identity fields optional for detached memory requests, while
    still emitting `request_kind="memory"` in non-git/no-sandbox workspaces.
    
    ## Root Cause
    `x-codex-turn-metadata` has more than one producer. Foreground turns and
    compaction requests own a real turn and should carry that turn identity.
    Detached memory stage-one requests do not own a foreground turn, so
    absent identity fields are valid rather than missing data. Startup
    websocket prewarm is also a model request, but it has `generate=false`
    and must not be counted as a foreground turn.
    
    `thread_source` or session source identifies where a thread came from
    (for example review, guardian, or another subagent). `request_kind`
    identifies what the current outbound model request is doing (`turn`,
    `prewarm`, `compaction`, or `memory`). A review or guardian thread can
    issue either a normal turn request or a compaction request, so source
    cannot replace request kind.
    
    ## Behavior / Impact
    - Ordinary foreground requests send `request_kind="turn"`, their real
    identity fields, and `window_id="<thread_id>:<window_generation>"`.
    - Startup websocket warmup requests send `request_kind="prewarm"` so
    they are not counted as foreground turns.
    - Compaction requests send `request_kind="compaction"`, their real
    owning turn identity, the existing `window_id`, and
    `compaction.{trigger,reason,implementation,phase,strategy}`.
    - Detached memory stage-one requests send `request_kind="memory"`
    without `session_id`, `thread_id`, `turn_id`, or `window_id`; when no
    workspace metadata exists, the kind-only header is still emitted.
    - `session_id`, `thread_id`, `turn_id`, and `window_id` remain optional
    in the header schema because detached memory requests do not own a
    foreground turn or context window.
    - `window_id` is not a new ID system: it is copied from the already-sent
    `x-codex-window-id` / WS client metadata value at model-request dispatch
    time.
    - Existing `x-codex-window-id` HTTP/WS emission, value format,
    generation advancement, resume behavior, and fork reset behavior are
    unchanged.
    - `request_kind`, `window_id`, and upstream turn-owned identity fields
    remain schema-owned; input `responsesapi_client_metadata` cannot replace
    their canonical values.
    - No table, DAG, export, app-server API, or MCP `_meta` schema changes
    are included.
    
    A compaction attempt stopped by a pre-compact hook issues no model
    request and therefore has no request header; its outcome remains in
    analytics events. Status, error, duration, and token deltas also remain
    analytics fields rather than request-header fields.
    
    Future detached-memory attribution using a real initiating turn ID as
    `trigger_turn_id` is intentionally not part of this PR.
    
    ## Sync With Main
    - Final pushed head `716342e79` is rebased onto `origin/main@0d37db4b2`.
    - The metadata conflict came from upstream `#24160`, which added
    `forked_from_thread_id` on the same `turn_metadata` surface. Resolution
    preserves that field and its protection from client metadata override
    alongside this PR's request-kind, compaction, and window-id fields.
    - While resolving the overlapping commits, I removed an accidental
    recursive model-request overlay and a duplicate detached-memory header
    builder before completing the rebase.
    
    ## Latency / User Experience Boundary
    - Foreground turns perform no new filesystem, git, or network work. New
    fields are inserted into metadata already serialized for outgoing
    requests.
    - Compaction issues the same model/HTTP requests with the same prompt,
    model, service tier, and sampling settings; only metadata bytes change.
    - Startup prewarm already sent metadata; it is now correctly classified
    as `prewarm`.
    - Non-git detached memory now sends a small kind-only metadata header
    rather than no header.
    - This client diff adds no user-visible latency mechanism beyond
    negligible serialization and header bytes on already-existing requests.
    
    ## Validation
    On conflict-resolved head `1d35c2cfb` based on `origin/main@487521733`:
    - `just fmt` (passed)
    - `just fix -p codex-core` (passed)
    - `git diff --check origin/main...HEAD` (passed)
    - `just test -p codex-core -E 'test(turn_metadata) |
    test(websocket_first_turn_uses_startup_prewarm_and_create) |
    test(responses_stream_includes_turn_metadata_header_for_git_workspace_e2e)
    |
    test(responses_websocket_forwards_turn_metadata_on_initial_and_incremental_create)
    | test(remote_compact_v2_retries_failures_with_stream_retry_budget) |
    test(window_id_advances_after_compact_persists_on_resume_and_resets_on_fork)'`
    (`23 passed`; `bench-smoke` passed)
    - `just test -p codex-app-server -E
    'test(turn_start_forwards_client_metadata_to_responses_request_v2) |
    test(turn_start_forwards_client_metadata_to_responses_websocket_request_body_v2)
    | test(auto_compaction_remote_emits_started_and_completed_items)'` (`3
    passed`; `bench-smoke` passed)
    - `just test -p codex-memories-write` (`29 passed`; `bench-smoke`
    passed)
  • [codex] Remove stale composer narrative doc references (#24641)
    ## Context
    
    `docs/tui-chat-composer.md` was removed by #20896 as part of removing
    local-only docs/specs from the repository. I checked the #20896 file
    list and the merge commit: the composer doc was deleted, not moved or
    copied, and current `main` does not contain a replacement composer
    narrative doc.
    
    Current guidance should keep contributors and agents focused on the docs
    that still exist: the module docs in `chat_composer.rs` and
    `paste_burst.rs`.
    
    ## Summary
    
    - Removes the scoped TUI bottom-pane AGENTS.md requirement to update
    `docs/tui-chat-composer.md`.
    - Removes stale module-doc references to that deleted narrative doc from
    `chat_composer.rs` and `paste_burst.rs`.
    
    ## Validation
    
    - Checked #20896 and the merge commit with rename/copy detection to
    confirm `docs/tui-chat-composer.md` was deleted rather than moved.
    - Searched current `main` for a replacement composer narrative doc.
    - Not run; documentation-only change.
  • fix: Preserve draft text when completing argument-taking slash commands (#23950)
    This adds slash command completion behavior for argument-taking
    commands, where text after the partially typed command becomes inline
    arguments instead of being discarded. This addresses the workflow of
    drafting text first, moving to the start, and completing a slash command
    around that existing draft. Before this change, this workflow would
    remove all user-input text aside from the slash command, which can be
    frustrating if the user had just typed out a long and well thought out
    goal.
    
    - Preserves the draft tail for inline-argument slash commands like
    `/goal` and `/review` when completing with `Tab` or `Enter`.
    - Keeps popup filtering focused on the command fragment under the cursor
    rather than the full draft text.
    - Leaves slash commands that do not support inline arguments unchanged,
    so completion still replaces the existing draft tail for those commands.
    - Adds focused TUI tests under slash input covering preserved arguments,
    cursor edge cases, and the negative case for a command without inline
    args.
      
    Follow-up simplification and test relocation from #24683 folded into
    this PR.
    
    ---------
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • make vercel webhook url an env secret (#24778)
    move `DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL` to a repo environment secret.
    
    to keep scope of use of that env secret small, move the vercel website
    redeploy to its own post-release job.