Commit Graph

12 Commits

  • [codex] Accept string input for Python turns (#23162)
    ## Summary
    - Allow thread.turn and turn.steer, including async variants, to accept
    RunInput so plain strings work alongside typed input objects.
    - Export RunInput and update the SDK artifact generator so regenerated
    turn methods keep the same signature and normalization.
    - Update docs, examples, notebook cells, and tests to use string
    shorthand for text-only turns while keeping typed inputs for multimodal
    input.
    
    ## Validation
    - uv run --extra dev ruff format .
    - uv run --extra dev ruff check --output-format=github .
    - python3 -m py_compile sdk/python/src/openai_codex/__init__.py
    sdk/python/src/openai_codex/api.py
    sdk/python/src/openai_codex/_inputs.py
    sdk/python/scripts/update_sdk_artifacts.py
    sdk/python/tests/test_public_api_signatures.py
    sdk/python/tests/test_app_server_streaming.py
    sdk/python/tests/test_app_server_turn_controls.py
    sdk/python/tests/test_real_app_server_integration.py
    - python3 -c "import json;
    json.load(open('sdk/python/notebooks/sdk_walkthrough.ipynb'))"
    - sdk/python/.venv/bin/python -c "import inspect, openai_codex; from
    openai_codex import Thread, AsyncThread, TurnHandle, AsyncTurnHandle,
    RunInput; funcs=[Thread.run, Thread.turn, AsyncThread.run,
    AsyncThread.turn, TurnHandle.steer, AsyncTurnHandle.steer]; assert
    all(inspect.signature(fn).parameters['input'].annotation == 'RunInput'
    for fn in funcs); assert RunInput is openai_codex.RunInput"
  • [codex] Return TurnResult from Python turn handles (#23151)
    ## Why
    
    `TurnHandle.run()` returned the raw app-server `Turn`, whose live
    start/completed payloads do not include loaded `items`, so users saw
    empty `items` after starting a turn. That made the handle-based path
    behave differently from `Thread.run(...)`, and pushed examples toward
    persisted-thread reads plus helper extraction.
    
    This PR makes the run APIs standalone: starting a turn and running it
    returns collected turn data directly, or fails visibly when required
    stream events are missing.
    
    ## What Changed
    
    - Replaces the public `RunResult` export with `TurnResult`.
    - Adds turn metadata to `TurnResult`: `id`, `status`, `error`,
    `started_at`, `completed_at`, and `duration_ms`, alongside
    `final_response`, `items`, and `usage`.
    - Changes `TurnHandle.run()` and `AsyncTurnHandle.run()` to consume
    stream events with the same collector used by `Thread.run(...)`.
    - Exports `TurnError` from `openai_codex.types` for the new result
    shape.
    - Updates tests, examples, docs, and the walkthrough notebook to use
    `result.final_response` and `result.items` directly.
    - Removes persisted-thread helper paths and placeholder/skipped control
    flows from the public examples and notebook.
    
    ## Verification
    
    - `python3 -m py_compile ...` over changed SDK, example, and test Python
    files.
    - `python3 -c "import json;
    json.load(open('sdk/python/notebooks/sdk_walkthrough.ipynb'))"`
    - `git diff --check`
    - `PYTHONPATH=sdk/python/src python3 -c ...` import/signature smoke for
    `TurnResult`, `TurnHandle.run`, and `AsyncTurnHandle.run`.
  • sdk/python: add first-class login support (#23093)
    ## Why
    
    The Python SDK can already create threads and run turns, but
    authentication still has to be arranged outside the SDK. App-server
    already exposes account login, account inspection, logout, and
    `account/login/completed` notifications, so SDK users currently have to
    work around a missing public client layer for a core setup step.
    
    This change makes authentication a normal SDK workflow while preserving
    the backend flow shape: API-key login completes immediately, and
    interactive ChatGPT flows return live handles that complete later
    through app-server notifications.
    
    ## What changed
    
    - Added public sync and async auth methods on `Codex` / `AsyncCodex`:
      - `login_api_key(...)`
      - `login_chatgpt()`
      - `login_chatgpt_device_code()`
      - `account(...)`
      - `logout()`
    - Added public browser-login and device-code handle types with
    attempt-local `wait()` and `cancel()` helpers. Cancellation stays on the
    handle instead of a root-level SDK method.
    - Extended the Python app-server client and notification router so login
    completion events are routed by `login_id` without consuming unrelated
    global notifications.
    - Kept login request/handle logic in a focused internal `_login.py`
    module so `api.py` remains the public facade instead of absorbing more
    auth plumbing.
    - Exported the new handle types plus curated account/login response
    types from the SDK surfaces.
    - Updated SDK docs, added sync/async login walkthrough examples, and
    added a notebook login walkthrough cell.
    
    ## Verification
    
    Added SDK coverage for:
    
    - API-key login, account readback, and logout through the app-server
    harness in both sync and async clients.
    - Browser login cancellation plus `handle.wait()` completion through the
    real app-server boundary used by the Python SDK harness.
    - Waiter routing that stays scoped across replaced interactive login
    attempts, plus async handle cancellation coverage.
    - Login notification demuxing, replay of early completion events, and
    async client delegation.
    - Public export/signature assertions.
    - Real integration-suite smoke coverage for the new examples and
    notebook login cell.
  • [codex] Refine Python SDK user-facing docs (#22941)
    ## Summary
    - Remove maintainer and release-process wording from the Python SDK
    README and docs.
    - Rewrite SDK-facing comments/docstrings so they read as standalone
    product documentation.
    - Add a real app-server integration smoke that follows the public
    quickstart-style `Codex() -> thread_start() -> run()` path.
    
    ## Integration coverage
    - Add `test_real_quickstart_style_flow_smoke` in the real app-server
    integration suite.
    
    ## Validation
    - Local tests were not run per repo guidance. CI should validate this
    branch once the PR is online.
  • [5/8] Rename Python SDK package to openai-codex (#21905)
    ## Why
    
    The SDK should publish under the reserved public distribution name
    `openai-codex`, and its import module should match that name in the
    Python style. Since package names can contain hyphens but import modules
    cannot, the public import path becomes `openai_codex`.
    
    Keeping the rename separate from the public API surface change makes the
    naming change easy to review and avoids mixing it with API curation.
    
    ## What
    
    - Rename the SDK distribution from `openai-codex-app-server-sdk` to
    `openai-codex`.
    - Rename the import package from `codex_app_server` to `openai_codex`.
    - Keep the runtime wheel as the separate `openai-codex-cli-bin`
    dependency.
    - Update docs, examples, notebooks, artifact scripts, lockfile metadata,
    and tests for the new distribution/module names.
    
    ## Stack
    
    1. #21891 `[1/8]` Pin Python SDK runtime dependency
    2. #21893 `[2/8]` Generate Python SDK types from pinned runtime
    3. #21895 `[3/8]` Run Python SDK tests in CI
    4. #21896 `[4/8]` Define Python SDK public API surface
    5. This PR `[5/8]` Rename Python SDK package to `openai-codex`
    6. #21910 `[6/8]` Add high-level Python SDK approval mode
    7. #22014 `[7/8]` Add Python SDK app-server integration harness
    8. #22021 `[8/8]` Add Python SDK Ruff formatting
    
    ## Verification
    
    - Updated package metadata and public API tests to assert the
    distribution and import names.
    
    Co-authored-by: Codex <noreply@openai.com>
  • [4/8] Define Python SDK public API surface (#21896)
    ## Why
    
    The SDK package root should be the ergonomic public client API, not a
    dump of every generated app-server schema type. Generated models still
    need a supported import path, but callers should be able to tell which
    names are high-level SDK entrypoints and which names are protocol value
    models.
    
    ## What
    
    - Define a curated root `__all__` for clients, handles, input helpers,
    retry helpers, config, and public errors.
    - Add a `types` module as the supported home for generated app-server
    response, event, enum, and helper models.
    - Update docs and examples to import protocol/value models from the type
    module.
    - Add tests that lock root exports, type-module exports, star-import
    behavior, and example import hygiene.
    
    ## Stack
    
    1. #21891 `[1/8]` Pin Python SDK runtime dependency
    2. #21893 `[2/8]` Generate Python SDK types from pinned runtime
    3. #21895 `[3/8]` Run Python SDK tests in CI
    4. This PR `[4/8]` Define Python SDK public API surface
    5. #21905 `[5/8]` Rename Python SDK package to `openai-codex`
    6. #21910 `[6/8]` Add high-level Python SDK approval mode
    7. #22014 `[7/8]` Add Python SDK app-server integration harness
    8. #22021 `[8/8]` Add Python SDK Ruff formatting
    
    ## Verification
    
    - Added public API signature tests for root exports, `types` exports,
    and example imports.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Route Python SDK turn notifications by ID (#21778)
    ## Why
    
    The Python SDK previously protected the stdio transport with a single
    active turn-consumer guard. That avoided competing reads from stdout,
    but it also meant one `Codex`/`AsyncCodex` client could not stream
    multiple active turns at the same time. Notifications could also arrive
    before the caller received a `TurnHandle` and registered for streaming,
    so the SDK needed an explicit routing layer instead of letting
    individual API calls read directly from the shared transport.
    
    ## What Changed
    
    - Added a private `MessageRouter` that owns per-request response queues,
    per-turn notification queues, pending turn-notification replay, and
    global notification delivery behind a single stdout reader thread.
    - Generated typed notification routing metadata so turn IDs come from
    known payload shapes instead of router-side attribute guessing, with
    explicit fallback handling for unknown notification payloads.
    - Updated sync and async turn streaming so `TurnHandle.stream()`/`run()`
    and `stream_text()` consume only notifications for their own turn ID,
    while `AsyncAppServerClient` no longer serializes all transport calls
    behind one async lock.
    - Cleared pending turn-notification buffers when unregistered turns
    complete so never-consumed turn handles do not leave stale queues
    behind.
    - Removed the internal stream-until helper now that turn completion
    waiting can register directly with routed turn notifications.
    - Updated Python SDK docs and focused tests for concurrent transport
    calls, interleaved turn routing, buffered early notifications, unknown
    notification routing, async delegation, and routed turn completion
    behavior.
    
    ## Validation
    
    - `uv run --extra dev ruff format scripts/update_sdk_artifacts.py
    src/codex_app_server/_message_router.py src/codex_app_server/client.py
    src/codex_app_server/generated/notification_registry.py
    tests/test_client_rpc_methods.py
    tests/test_public_api_runtime_behavior.py
    tests/test_async_client_behavior.py`
    - `uv run --extra dev ruff check scripts/update_sdk_artifacts.py
    src/codex_app_server/_message_router.py src/codex_app_server/client.py
    src/codex_app_server/generated/notification_registry.py
    tests/test_client_rpc_methods.py
    tests/test_public_api_runtime_behavior.py
    tests/test_async_client_behavior.py`
    - `uv run --extra dev pytest tests/test_client_rpc_methods.py
    tests/test_public_api_runtime_behavior.py
    tests/test_async_client_behavior.py`
    - `git diff --check`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Harden package-manager install policy (#19163)
    ## Summary
    
    This PR hardens package-manager usage across the repo to reduce
    dependency supply-chain risk. It also removes the stale `codex-cli`
    Docker path, which was already broken on `main`, instead of keeping a
    bitrotted container workflow alive.
    
    ## What changed
    
    - Updated pnpm package manager pins and workspace install settings.
    - Removed stale `codex-cli` Docker assets instead of trying to keep a
    broken local container path alive.
    - Added uv settings and lockfiles for the Python SDK packages.
    - Updated Python SDK setup docs to use `uv sync`.
    
    ## Why
    
    This is primarily a security hardening change. It reduces
    package-install and supply-chain risk by ensuring dependency installs go
    through pinned package managers, committed lockfiles, release-age
    settings, and reviewed build-script controls.
    
    For `codex-cli`, the right follow-up was to remove the local Docker path
    rather than keep patching it:
    
    - `codex-cli/Dockerfile` installed `codex.tgz` with `npm install -g`,
    which bypassed the repo lockfile and age-gated pnpm settings.
    - The local `codex-cli/scripts/build_container.sh` helper was already
    broken on `main`: it called `pnpm run build`, but
    `codex-cli/package.json` does not define a `build` script.
    - The container path itself had bitrotted enough that keeping it would
    require extra packaging-specific behavior that was not otherwise needed
    by the repo.
    
    ## Gaps addressed
    
    - Global npm installs bypassed the repo lockfile in Docker and CLI
    reinstall paths, including `codex-cli/Dockerfile` and
    `codex-cli/bin/codex.js`.
    - CI and Docker pnpm installs used `--frozen-lockfile`, but the repo was
    missing stricter pnpm workspace settings for dependency build scripts.
    - Python SDK projects had `pyproject.toml` metadata but no committed
    `uv.lock` coverage or uv age/index settings in `sdk/python` and
    `sdk/python-runtime`.
    - The secure devcontainer install path used npm/global install behavior
    without a local locked package-manager boundary.
    - The local `codex-cli` Docker helper was already broken on `main`, so
    this PR removes that stale Docker path instead of preserving a broken
    surface.
    - pnpm was already pinned, but not to the current repo-wide pnpm version
    target.
    
    ## Verification
    
    - `pnpm install --frozen-lockfile`
    - `.devcontainer/codex-install`: `pnpm install --prod --frozen-lockfile`
    - `.devcontainer/codex-install`: `./node_modules/.bin/codex --version`
    - `sdk/python`: `uv lock --check`, `uv sync --locked --all-extras
    --dry-run`, `uv build`
    - `sdk/python-runtime`: `uv lock --check`, `uv sync --locked --dry-run`,
    `uv build --wheel`
    - `pnpm -r --filter ./sdk/typescript run build`
    - `pnpm -r --filter ./sdk/typescript run lint`
    - `pnpm -r --filter ./sdk/typescript run test`
    - `node --check codex-cli/bin/codex.js`
    - `docker build -f .devcontainer/Dockerfile.secure -t codex-secure-test
    .`
    - `cargo build -p codex-cli`
    - repo-wide package-manager audit
  • Stage publishable Python runtime wheels (#18865)
    This is PR 2 of the Python SDK PyPI publishing split. [PR
    1](https://github.com/openai/codex/pull/18862) refreshed the generated
    SDK bindings; this PR makes the runtime package itself publishable, and
    PR 3 will wire the SDK package/version pinning to this runtime package.
    
    ## Summary
    - Rename the runtime distribution to `openai-codex-cli-bin` while
    keeping the import package as `codex_cli_bin`.
    - Make the runtime package wheel-only and build `py3-none-<platform>`
    wheels instead of interpreter-specific wheels.
    - Add `stage-runtime --codex-version` and `--platform-tag` so release
    staging can produce the platform wheel matrix from Codex release tags.
    - Add focused artifact workflow tests for version normalization,
    platform tag injection, and runtime wheel metadata.
    
    ## Why Rename
    There is already an unofficial PyPI package,
    [`codex-bin`](https://pypi.org/project/codex-bin/), distributing OpenAI
    Codex binaries. Publishing the official SDK runtime dependency as
    `openai-codex-cli-bin` makes the ownership clear, avoids confusing the
    SDK-pinned runtime wheel with that unowned wrapper, and keeps the import
    package unchanged as `codex_cli_bin`.
    
    ## Tests
    - `uv run --extra dev pytest
    tests/test_artifact_workflow_and_binaries.py` -> 21 passed
    - `uv run --extra dev python scripts/update_sdk_artifacts.py
    stage-runtime /tmp/codex-python-pr2-rebased/runtime-stage
    /tmp/codex-python-pr2-rebased/codex --codex-version
    rust-v0.116.0-alpha.1 --platform-tag macosx_11_0_arm64`
    - `uv run --with build --extra dev python -m build --wheel
    /tmp/codex-python-pr2-rebased/runtime-stage`
    - `uv run --with twine --extra dev twine check
    /tmp/codex-python-pr2-rebased/runtime-stage/dist/openai_codex_cli_bin-0.116.0a1-py3-none-macosx_11_0_arm64.whl`
    
    ## Note
    - Full `uv run --extra dev pytest` currently fails because regenerating
    from schemas already on `main` adds new DeviceKey Python types. I left
    that generated catch-up out of this runtime-only PR.
  • Add Python SDK thread.run convenience methods (#15088)
    ## TL;DR
    Add `thread.run(...)` / `async thread.run(...)` convenience methods to
    the Python SDK for the common case.
    
    - add `RunInput = Input | str` and `RunResult` with `final_response`,
    collected `items`, and optional `usage`
    - keep `thread.turn(...)` strict and lower-level for streaming,
    steering, interrupting, and raw generated `Turn` access
    - update Python SDK docs, quickstart examples, and tests for the sync
    and async convenience flows
    
    ## Validation
    - `python3 -m pytest sdk/python/tests/test_public_api_signatures.py
    sdk/python/tests/test_public_api_runtime_behavior.py`
    - `python3 -m pytest
    sdk/python/tests/test_real_app_server_integration.py -k
    'thread_run_convenience or async_thread_run_convenience'` (skipped in
    this environment)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add Python SDK public API and examples (#14446)
    ## TL;DR
    WIP esp the examples
    
    Thin the Python SDK public surface so the wrapper layer returns
    canonical app-server generated models directly.
    
    - keeps `Codex` / `AsyncCodex` / `Thread` / `Turn` and input helpers,
    but removes alias-only type layers and custom result models
    - `metadata` now returns `InitializeResponse` and `run()` returns the
    generated app-server `Turn`
    - updates docs, examples, notebook, and tests to use canonical generated
    types and regenerates `v2_all.py` against current schema
    - keeps the pinned runtime-package integration flow and real integration
    coverage
    
      ## Validation
      - `PYTHONPATH=sdk/python/src python3 -m pytest sdk/python/tests`
    - `GH_TOKEN="$(gh auth token)" RUN_REAL_CODEX_TESTS=1
    PYTHONPATH=sdk/python/src python3 -m pytest sdk/python/tests -rs`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add Python app-server SDK (#14435)
    ## TL;DR
    Bring the Python app-server SDK from `main-with-prs-13953-and-14232`
    onto current `main` as a standalone SDK-only PR.
    
    - adds the new `sdk/python` and `sdk/python-runtime` package trees
    - keeps the scope to the SDK payload only, without the unrelated
    branch-history or workflow changes from the source branch
    - regenerates `sdk/python/src/codex_app_server/generated/v2_all.py`
    against current `main` schema so the extracted SDK matches today's
    protocol definitions
    
    ## Validation
    - `PYTHONPATH=sdk/python/src python3 -m pytest sdk/python/tests`
    
    Co-authored-by: Codex <noreply@openai.com>