8 Commits

  • Separate local and remote plugin analytics IDs (#29495)
    ## Why
    
    Plugin analytics overloaded `plugin_id`: most events used the Codex
    `<plugin>@<marketplace>` identity, while remote install events used the
    backend plugin ID. That makes the same field change meaning across event
    types and complicates downstream identity resolution.
    
    This change makes the contract unambiguous:
    
    - `plugin_id`: the local Codex `<plugin>@<marketplace>` identity, when
    resolved
    - `remote_plugin_id`: the backend plugin identity, when available
    
    For a remote install failure that happens before plugin details resolve,
    `plugin_id` is `null` and `remote_plugin_id` remains populated.
    
    ## What changed
    
    All six plugin analytics events use the same identity contract:
    
    - `codex_plugin_installed`
    - `codex_plugin_install_failed`
    - `codex_plugin_uninstalled`
    - `codex_plugin_enabled`
    - `codex_plugin_disabled`
    - `codex_plugin_used`
    
    Remote identity is resolved from the current installed-plugin snapshot
    first, with persisted install metadata as fallback. The telemetry
    metadata type keeps local identity optional for failures that occur
    before remote details are available.
    
    The app-server test client's manual analytics smokes now find remote
    mutation events through `remote_plugin_id` and validate that `plugin_id`
    remains local.
    
    ## Remote uninstall
    
    Resolve and capture telemetry metadata before removing the local plugin
    cache, then emit `codex_plugin_uninstalled` after the backend confirms
    success. The event is also emitted when backend uninstall succeeds but
    local cache cleanup reports `CacheRemove`.
    
    If a concurrent remote-cache refresh removes the local bundle before
    telemetry capture, the already-fetched remote plugin detail supplies
    fallback capability metadata.
    
    ## Validation
    
    - `just test -p codex-analytics` — 82 passed
    - `just test -p codex-core-plugins` — 271 passed
    - `just test -p codex-app-server-test-client` — 5 passed
    - `just test -p codex-plugin` — 3 passed
    - `just test -p codex-app-server plugin_install` — 37 passed
    - `just test -p codex-app-server plugin_uninstall` — 10 passed
    
    The production app-server install/uninstall flow was also exercised
    against `plugins~Plugin_f1b845ac33888191ac156169c58733c2`
    (`build-ios-apps@openai-curated-remote`), and the plugin's original
    uninstalled state was restored.
  • [codex] handle request_user_input in app-server test client (#29476)
    ## Why
    
    `codex-app-server-test-client` previously treated
    `item/tool/requestUserInput` as an unsupported server request and
    terminated the connection. That made it impossible to use the client for
    end-to-end testing of interactive turns: an operator could observe the
    request, but could not answer it and confirm that the same turn resumed.
    
    ## What changed
    
    - Handle `ToolRequestUserInput` server requests in the test client's
    central request dispatcher.
    - Render numbered terminal choices, accept exact option labels, support
    free-form `Other` and text-only questions, and collect multiple answers.
    - Send a protocol-native `ToolRequestUserInputResponse` and continue
    streaming the active turn.
    - Fail clearly when interactive input is requested without a terminal.
    - Document the interactive behavior and add focused tests for option
    selection, free-form answers, multiple questions, and invalid-selection
    retries.
    
    ## Testing
    
    - `just test -p codex-app-server-test-client`
    - `just bazel-lock-check`
    - Manually exercised the app-server flow, selected `TUI`, observed
    `serverRequest/resolved`, and verified that the same turn completed with
    the selected answer.
  • [codex-app-server-test-client] Plugin Install/Uninstall Analytics Smoke Test (#27100)
    ## This PR
    
    The original [combined remote plugin analytics PR
    #26281](https://github.com/openai/codex/pull/26281) mixed reusable
    analytics test infrastructure, two manual smoke workflows, a metadata
    refactor, and the final identity behavior. This PR adds the
    account-mutating validation workflow separately so its cleanup and
    recovery guarantees can be reviewed without the final analytics behavior
    change.
    
    - Add a manually invoked remote plugin install/uninstall smoke workflow.
    - Require explicit account-mutation confirmation and an initially
    uninstalled plugin.
    - Validate the current `codex_plugin_installed` contract, where
    `plugin_id` is the backend ID.
    - Restore and verify the original uninstalled state, with a dedicated
    recovery command.
    
    This baseline intentionally does not require `codex_plugin_uninstalled`,
    because production does not emit that event yet. The final PR will
    update this smoke to require local `plugin_id`, `remote_plugin_id`, and
    uninstall emission. Review this PR as the net diff against #27099.
    
    ## Testing
    
    - `just test -p codex-app-server-test-client` (3 focused
    capture/validation tests passed)
    - The live workflow was previously exercised on the green combined
    reference branch, and the original uninstalled account state was
    restored.
    - CI is green across the required platform matrix.
    
    ## Split Overview
    
    ```text
    main
    ├── #27093  Debug analytics capture
    │   └── #27099  Non-mutating plugin smoke
    │       └── #27100  Remote install/uninstall smoke  ← you are here
    └── #27102  Plugin telemetry metadata refactor
    
    After #27093, #27099, #27100, and #27102 merge:
    └── Final PR: add remote_plugin_id to plugin analytics
    ```
    
    Review order and dependencies:
    
    1. [#27093 Add debug-only analytics event
    capture](https://github.com/openai/codex/pull/27093) (based on `main`)
    2. [#27099 Add a plugin analytics smoke
    workflow](https://github.com/openai/codex/pull/27099) (stacked on
    #27093)
    3. [#27100 Add a remote plugin analytics mutation smoke
    workflow](https://github.com/openai/codex/pull/27100) **(this PR,
    stacked on #27099)**
    4. [#27102 Centralize plugin telemetry metadata
    construction](https://github.com/openai/codex/pull/27102) (independent,
    based on `main`)
    5. Final remote-ID behavior PR (created after PRs 1-4 merge)
    
    The original [#26281](https://github.com/openai/codex/pull/26281)
    remains open as the green aggregate reference until the final PR is
    published.
  • [codex-app-server-test-client & codex-app-server] Plugin Usage Analytics Smoke Test (#27099)
    ## This PR
    
    The original [combined remote plugin analytics PR
    #26281](https://github.com/openai/codex/pull/26281) mixed reusable
    analytics test infrastructure, two manual smoke workflows, a metadata
    refactor, and the final identity behavior. This PR establishes a
    non-mutating end-to-end plugin smoke workflow before any analytics
    identity semantics change.
    
    - Add `plugin-analytics-smoke` to the existing app-server test client.
    - Exercise plugin disable, enable, and use through production app-server
    RPC paths.
    - Isolate config writes in a temporary file and use a loopback Responses
    API server.
    - Capture analytics without sending them to the production analytics
    backend.
    - Validate the current local `plugin_id`, names, capability metadata,
    thread, turn, and model fields.
    
    This is intentionally a baseline smoke workflow. It does not assert
    `remote_plugin_id`; the final PR will update it when that field exists.
    Review this PR as the net diff against #27093.
    
    ## Testing
    
    - The test-client target compiles successfully.
    - The combined reference branch exercised the manual smoke against the
    live remote plugin service.
    - CI is green across the required platform matrix.
    
    ## Split Overview
    
    ```text
    main
    ├── #27093  Debug analytics capture
    │   └── #27099  Non-mutating plugin smoke           ← you are here
    │       └── #27100  Remote install/uninstall smoke
    └── #27102  Plugin telemetry metadata refactor
    
    After #27093, #27099, #27100, and #27102 merge:
    └── Final PR: add remote_plugin_id to plugin analytics
    ```
    
    Review order and dependencies:
    
    1. [#27093 Add debug-only analytics event
    capture](https://github.com/openai/codex/pull/27093) (based on `main`)
    2. [#27099 Add a plugin analytics smoke
    workflow](https://github.com/openai/codex/pull/27099) **(this PR,
    stacked on #27093)**
    3. [#27100 Add a remote plugin analytics mutation smoke
    workflow](https://github.com/openai/codex/pull/27100) (stacked on this
    PR)
    4. [#27102 Centralize plugin telemetry metadata
    construction](https://github.com/openai/codex/pull/27102) (independent,
    based on `main`)
    5. Final remote-ID behavior PR (created after PRs 1-4 merge)
    
    The original [#26281](https://github.com/openai/codex/pull/26281)
    remains open as the green aggregate reference until the final PR is
    published.
  • app-server: Add an ability to watch events in the test client (#13080)
    Add a `watch` subcommand to `codex-app-server-test-client` binary to
    help in manual testing of events flow.
  • codex-rs: fix thread resume rejoin semantics (#11756)
    ## Summary
    - always rejoin an in-memory running thread on `thread/resume`, even
    when overrides are present
    - reject `thread/resume` when `history` is provided for a running thread
    - reject `thread/resume` when `path` mismatches the running thread
    rollout path
    - warn (but do not fail) on override mismatches for running threads
    - add more `thread_resume` integration tests and fixes; including
    restart-based resume-with-overrides coverage
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-app-server --test all thread_resume`
    - manual test with app-server-test-client
    https://github.com/openai/codex/pull/11755
    - manual test both stdio and websocket in app
  • app-server-test-client websocket client and thread tools (#11755)
    - add websocket endpoint mode with default ws://127.0.0.1:4222 while
    keeping stdio codex-bin path compatibility
    - add thread-resume (follow stream) and thread-list commands for manual
    thread lifecycle testing
    - quickstart docs
  • feat: add app-server-test-client crate for internal use (#5391)
    For app-server development it's been helpful to be able to trigger some
    test flows end-to-end and print the JSON-RPC messages sent between
    client and server.