Commit Graph

13 Commits

  • [sdk/python] Stop advertising HTTP image URLs (#29464)
    ## Summary
    
    - use generated image data URLs in the Python SDK examples and notebook
    - document HTTP and HTTPS image URLs as deprecated and recommend
    `LocalImageInput`
    - replace the remote-URL integration test with data-URL coverage
    
    `ImageInput` remains available for data URLs. The SDK does not duplicate
    app-server URL validation.
    
    ## Testing
    
    - `uv run --frozen --no-sync ruff check --output-format=full .`
    - `uv run --frozen --no-sync ruff format --check .`
    - full Python SDK test suite with an isolated writable
    `CODEX_SQLITE_HOME` (119 passed, 38 skipped)
  • [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] 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] 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] 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.
  • [6/8] Add high-level Python SDK approval mode (#21910)
    ## Why
    
    The high-level SDK should expose the approval behavior it actually
    supports instead of leaking generated app-server routing fields. New
    work should have two clear choices: default auto review, or explicitly
    deny escalated permission requests. Existing threads and subsequent
    turns should preserve their current approval behavior unless the caller
    passes an override.
    
    ## What
    
    - Add the public `ApprovalMode` enum with `auto_review` and `deny_all`.
    - Default new thread creation to `ApprovalMode.auto_review`.
    - Preserve existing approval settings by default for resume, fork, run,
    and turn helpers.
    - Remove raw `approval_policy` / `approvals_reviewer` kwargs from
    high-level SDK wrappers.
    - Update generated wrapper output, docs, examples, notebooks, and tests
    for the high-level approval mode API.
    
    ## 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. #21905 `[5/8]` Rename Python SDK package to `openai-codex`
    6. This PR `[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 approval-mode mapping/default tests for new threads, existing
    threads, forks, resumes, and subsequent turns.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [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>
  • 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>