diff --git a/sdk/python/README.md b/sdk/python/README.md index 72290f38e0..d3b74b02cf 100644 --- a/sdk/python/README.md +++ b/sdk/python/README.md @@ -4,8 +4,8 @@ Experimental Python SDK for `codex app-server` JSON-RPC v2 over stdio, with a sm The generated wire-model layer is sourced from the pinned `openai-codex-cli-bin` runtime package and exposed as Pydantic models with snake_case Python fields -that serialize back to the app-server’s camelCase wire format. -The package root exports the ergonomic client API; public app-server value and +that serialize back to the protocol's camelCase wire format. +The package root exports the ergonomic client API; public Codex protocol value and event types live in `openai_codex.types`. ## Install @@ -17,7 +17,7 @@ source .venv/bin/activate ``` Published SDK builds pin an exact `openai-codex-cli-bin` runtime dependency -with the same version as the SDK. Pass `AppServerConfig(codex_bin=...)` only +with the same version as the SDK. Pass `CodexConfig(codex_bin=...)` only when you intentionally want to run against a specific local app-server binary. ## Quickstart @@ -26,7 +26,7 @@ when you intentionally want to run against a specific local app-server binary. from openai_codex import Codex, Sandbox with Codex() as codex: - # Call login_api_key(...) first when this app-server session is not + # Call login_api_key(...) first when this Codex session is not # already authenticated. thread = codex.thread_start(model="gpt-5", sandbox=Sandbox.workspace_write) result = thread.run("Say hello in one sentence.") @@ -57,7 +57,7 @@ Available presets: - `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. -When `sandbox=` is omitted, app-server uses its configured default. A sandbox +When `sandbox=` is omitted, Codex uses its configured default. A sandbox passed to `run(...)` or `turn(...)` applies to that turn and subsequent turns on the thread. @@ -87,7 +87,7 @@ with Codex() as codex: Use `login_chatgpt_device_code()` for device-code auth, `handle.cancel()` to stop an in-progress interactive login, and `logout()` to clear the active -app-server account session. +Codex account session. ## Docs map diff --git a/sdk/python/docs/api-reference.md b/sdk/python/docs/api-reference.md index a353d32fa2..f56234540b 100644 --- a/sdk/python/docs/api-reference.md +++ b/sdk/python/docs/api-reference.md @@ -1,6 +1,6 @@ # OpenAI Codex SDK — API Reference -Public surface of `openai_codex` for app-server v2. +Public surface of `openai_codex` for Codex workflows. This SDK surface is experimental. Turn streams are routed by turn ID so one client can consume multiple active turns concurrently. Thread starts default to `ApprovalMode.auto_review`; turn starts accept an optional `approval_mode` override. @@ -11,6 +11,7 @@ Thread starts default to `ApprovalMode.auto_review`; turn starts accept an optio from openai_codex import ( Codex, AsyncCodex, + CodexConfig, ApprovalMode, Sandbox, ChatgptLoginHandle, @@ -47,12 +48,12 @@ from openai_codex.types import ( - Version: `openai_codex.__version__` - Requires Python >= 3.10 -- Public app-server value and event types live in `openai_codex.types` +- Public Codex protocol value and event types live in `openai_codex.types` ## Codex (sync) ```python -Codex(config: AppServerConfig | None = None) +Codex(config: CodexConfig | None = None) ``` Properties/methods: @@ -82,7 +83,7 @@ with Codex() as codex: ## AsyncCodex (async parity) ```python -AsyncCodex(config: AppServerConfig | None = None) +AsyncCodex(config: CodexConfig | None = None) ``` Preferred usage: @@ -201,7 +202,7 @@ Presets: - `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. -When `sandbox=` is omitted, app-server uses its configured default. A sandbox +When `sandbox=` is omitted, Codex uses its configured default. A sandbox passed to `run(...)` or `turn(...)` applies to that turn and subsequent turns. ## TurnHandle / AsyncTurnHandle @@ -249,7 +250,7 @@ Use a plain `str` as shorthand for `TextInput(...)` anywhere a turn input is acc ## Public Types -The SDK wrappers return and accept public app-server models wherever possible: +The SDK wrappers return and accept public Codex protocol models wherever possible: ```python from openai_codex.types import ( diff --git a/sdk/python/docs/faq.md b/sdk/python/docs/faq.md index 42f2cb1f6a..b0c496579b 100644 --- a/sdk/python/docs/faq.md +++ b/sdk/python/docs/faq.md @@ -66,7 +66,7 @@ The presets are: - `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. -When `sandbox=` is omitted, app-server uses its configured default. A turn +When `sandbox=` is omitted, Codex uses its configured default. A turn sandbox override applies to that turn and subsequent turns. ## Why only `thread_start(...)` and `thread_resume(...)`? @@ -86,7 +86,7 @@ Common causes: - published runtime package (`openai-codex-cli-bin`) is not installed - local `codex_bin` override points to a missing file -- app-server version older than the SDK schema +- installed Codex runtime version older than the SDK schema ## Why does a turn "hang"? diff --git a/sdk/python/docs/getting-started.md b/sdk/python/docs/getting-started.md index 6113b9050a..45271ed64d 100644 --- a/sdk/python/docs/getting-started.md +++ b/sdk/python/docs/getting-started.md @@ -99,7 +99,7 @@ Available presets: - `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. -When `sandbox=` is omitted, app-server uses its configured default. A turn +When `sandbox=` is omitted, Codex uses its configured default. A turn override also becomes the sandbox for subsequent turns on that thread. ## 5) Continue the same thread (multi-turn) @@ -150,9 +150,9 @@ with Codex() as codex: print(result.final_response) ``` -## 8) Public app-server types +## 8) Public Codex protocol types -The convenience wrappers live at the package root. Public app-server value and +The convenience wrappers live at the package root. Public Codex protocol value and event types live under: ```python diff --git a/sdk/python/examples/_bootstrap.py b/sdk/python/examples/_bootstrap.py index 92fe18e26a..fde7a32c00 100644 --- a/sdk/python/examples/_bootstrap.py +++ b/sdk/python/examples/_bootstrap.py @@ -47,11 +47,11 @@ def ensure_local_sdk_src() -> Path: def runtime_config(): - """Return an example-friendly AppServerConfig for repo-source SDK usage.""" - from openai_codex import AppServerConfig + """Return an example-friendly CodexConfig for repo-source SDK usage.""" + from openai_codex import CodexConfig ensure_runtime_package_installed(sys.executable, _SDK_PYTHON_DIR) - return AppServerConfig() + return CodexConfig() def _png_chunk(chunk_type: bytes, data: bytes) -> bytes: diff --git a/sdk/python/src/openai_codex/__init__.py b/sdk/python/src/openai_codex/__init__.py index 9f1ca770c5..3453388498 100644 --- a/sdk/python/src/openai_codex/__init__.py +++ b/sdk/python/src/openai_codex/__init__.py @@ -22,10 +22,10 @@ from .api import ( TurnHandle, TurnResult, ) -from .client import AppServerConfig +from .client import CodexConfig from .errors import ( - AppServerError, - AppServerRpcError, + CodexError, + CodexRpcError, InternalRpcError, InvalidParamsError, InvalidRequestError, @@ -41,7 +41,7 @@ from .retry import retry_on_overload __all__ = [ "__version__", - "AppServerConfig", + "CodexConfig", "Codex", "AsyncCodex", "ApprovalMode", @@ -64,10 +64,10 @@ __all__ = [ "SkillInput", "MentionInput", "retry_on_overload", - "AppServerError", + "CodexError", "TransportClosedError", "JsonRpcError", - "AppServerRpcError", + "CodexRpcError", "ParseError", "InvalidRequestError", "MethodNotFoundError", diff --git a/sdk/python/src/openai_codex/_login.py b/sdk/python/src/openai_codex/_login.py index cdfdda41da..377c9489ec 100644 --- a/sdk/python/src/openai_codex/_login.py +++ b/sdk/python/src/openai_codex/_login.py @@ -3,8 +3,8 @@ from __future__ import annotations from dataclasses import dataclass from typing import Protocol -from .async_client import AsyncAppServerClient -from .client import AppServerClient +from .async_client import AsyncCodexClient +from .client import CodexClient from .generated.v2_all import ( AccountLoginCompletedNotification, CancelLoginAccountResponse, @@ -19,14 +19,14 @@ from .generated.v2_all import ( class _AsyncLoginOwner(Protocol): """Subset of AsyncCodex needed by async login handles.""" - _client: AsyncAppServerClient + _client: AsyncCodexClient async def _ensure_initialized(self) -> None: - """Ensure the owning SDK client has a live app-server connection.""" + """Ensure the owning SDK client has a live Codex connection.""" ... -def start_chatgpt_login(client: AppServerClient) -> ChatgptLoginHandle: +def start_chatgpt_login(client: CodexClient) -> ChatgptLoginHandle: """Start browser ChatGPT login and return the handle for that attempt.""" response = client.account_login_start( LoginAccountParams( @@ -60,7 +60,7 @@ async def async_start_chatgpt_login(owner: _AsyncLoginOwner) -> AsyncChatgptLogi ) -def start_device_code_login(client: AppServerClient) -> DeviceCodeLoginHandle: +def start_device_code_login(client: CodexClient) -> DeviceCodeLoginHandle: """Start device-code ChatGPT login and return the handle for that attempt.""" response = client.account_login_start( LoginAccountParams( @@ -102,7 +102,7 @@ async def async_start_device_code_login( class ChatgptLoginHandle: """Live browser-login attempt returned by `Codex.login_chatgpt()`.""" - _client: AppServerClient + _client: CodexClient login_id: str auth_url: str @@ -119,7 +119,7 @@ class ChatgptLoginHandle: class DeviceCodeLoginHandle: """Live device-code login attempt returned by `Codex.login_chatgpt_device_code()`.""" - _client: AppServerClient + _client: CodexClient login_id: str verification_url: str user_code: str diff --git a/sdk/python/src/openai_codex/_message_router.py b/sdk/python/src/openai_codex/_message_router.py index 14a01336a4..b500f16c56 100644 --- a/sdk/python/src/openai_codex/_message_router.py +++ b/sdk/python/src/openai_codex/_message_router.py @@ -4,7 +4,7 @@ import queue import threading from collections import deque -from .errors import AppServerError, map_jsonrpc_error +from .errors import CodexError, map_jsonrpc_error from .generated.notification_registry import notification_turn_id from .generated.v2_all import AccountLoginCompletedNotification from .models import JsonValue, Notification, UnknownNotification @@ -136,7 +136,7 @@ class MessageRouter: ) ) else: - waiter.put(AppServerError("Malformed JSON-RPC error response")) + waiter.put(CodexError("Malformed JSON-RPC error response")) return waiter.put(msg.get("result")) diff --git a/sdk/python/src/openai_codex/_run.py b/sdk/python/src/openai_codex/_run.py index ac9fa5ed38..f5c72d5109 100644 --- a/sdk/python/src/openai_codex/_run.py +++ b/sdk/python/src/openai_codex/_run.py @@ -10,7 +10,7 @@ from .generated.v2_all import ( ThreadItem, ThreadTokenUsage, ThreadTokenUsageUpdatedNotification, - Turn as AppServerTurn, + Turn, TurnCompletedNotification, TurnError, TurnStatus, @@ -55,7 +55,7 @@ def _final_assistant_response_from_items(items: list[ThreadItem]) -> str | None: return last_unknown_phase_response -def _raise_for_failed_turn(turn: AppServerTurn) -> None: +def _raise_for_failed_turn(turn: Turn) -> None: if turn.status != TurnStatus.failed: return if turn.error is not None and turn.error.message: diff --git a/sdk/python/src/openai_codex/api.py b/sdk/python/src/openai_codex/api.py index 6b6ddf617e..f27de49ebc 100644 --- a/sdk/python/src/openai_codex/api.py +++ b/sdk/python/src/openai_codex/api.py @@ -38,8 +38,8 @@ from ._run import ( _collect_turn_result, ) from ._sandbox import Sandbox as Sandbox, _sandbox_mode, _sandbox_policy -from .async_client import AsyncAppServerClient -from .client import AppServerClient, AppServerConfig +from .async_client import AsyncCodexClient +from .client import CodexClient, CodexConfig from .generated.v2_all import ( ApiKeyLoginAccountParams, GetAccountParams, @@ -73,10 +73,10 @@ from .models import InitializeResponse, JsonObject, Notification class Codex: - """Typed Python client for app-server v2 workflows.""" + """Typed Python client for Codex workflows.""" - def __init__(self, config: AppServerConfig | None = None) -> None: - self._client = AppServerClient(config=config) + def __init__(self, config: CodexConfig | None = None) -> None: + self._client = CodexClient(config=config) try: self._client.start() self._init = validate_initialize_metadata(self._client.initialize()) @@ -98,7 +98,7 @@ class Codex: self._client.close() def login_api_key(self, api_key: str) -> None: - """Authenticate app-server with an API key.""" + """Authenticate Codex with an API key.""" self._client.account_login_start( LoginAccountParams( root=ApiKeyLoginAccountParams( @@ -117,11 +117,11 @@ class Codex: return start_device_code_login(self._client) def account(self, *, refresh_token: bool = False) -> GetAccountResponse: - """Read the current app-server account state.""" + """Read the current Codex account state.""" return self._client.account_read(GetAccountParams(refresh_token=refresh_token)) def logout(self) -> None: - """Clear the current app-server account session.""" + """Clear the current Codex account session.""" self._client.account_logout() # BEGIN GENERATED: Codex.flat_methods @@ -281,8 +281,8 @@ class AsyncCodex: or first awaited API use. """ - def __init__(self, config: AppServerConfig | None = None) -> None: - self._client = AsyncAppServerClient(config=config) + def __init__(self, config: CodexConfig | None = None) -> None: + self._client = AsyncCodexClient(config=config) self._init: InitializeResponse | None = None self._initialized = False self._init_lock = asyncio.Lock() @@ -326,7 +326,7 @@ class AsyncCodex: self._initialized = False async def login_api_key(self, api_key: str) -> None: - """Authenticate app-server with an API key.""" + """Authenticate Codex with an API key.""" await self._ensure_initialized() await self._client.account_login_start( LoginAccountParams( @@ -348,12 +348,12 @@ class AsyncCodex: return await async_start_device_code_login(self) async def account(self, *, refresh_token: bool = False) -> GetAccountResponse: - """Read the current app-server account state.""" + """Read the current Codex account state.""" await self._ensure_initialized() return await self._client.account_read(GetAccountParams(refresh_token=refresh_token)) async def logout(self) -> None: - """Clear the current app-server account session.""" + """Clear the current Codex account session.""" await self._ensure_initialized() await self._client.account_logout() @@ -515,7 +515,7 @@ class AsyncCodex: @dataclass(slots=True) class Thread: - _client: AppServerClient + _client: CodexClient id: str def run( @@ -689,7 +689,7 @@ class AsyncThread: @dataclass(slots=True) class TurnHandle: - _client: AppServerClient + _client: CodexClient thread_id: str id: str diff --git a/sdk/python/src/openai_codex/async_client.py b/sdk/python/src/openai_codex/async_client.py index 282154b0cc..f0b12b99ed 100644 --- a/sdk/python/src/openai_codex/async_client.py +++ b/sdk/python/src/openai_codex/async_client.py @@ -6,7 +6,7 @@ from typing import AsyncIterator, Callable, ParamSpec, TypeVar from pydantic import BaseModel -from .client import AppServerClient, AppServerConfig +from .client import CodexClient, CodexConfig from .generated.v2_all import ( AccountLoginCompletedNotification, AgentMessageDeltaNotification, @@ -43,20 +43,20 @@ ParamsT = ParamSpec("ParamsT") ReturnT = TypeVar("ReturnT") -class AsyncAppServerClient: - """Async wrapper around AppServerClient using thread offloading.""" +class AsyncCodexClient: + """Async wrapper around CodexClient using thread offloading.""" - def __init__(self, config: AppServerConfig | None = None) -> None: + def __init__(self, config: CodexConfig | None = None) -> None: """Create the wrapped sync client that owns the transport process.""" - self._sync = AppServerClient(config=config) + self._sync = CodexClient(config=config) - async def __aenter__(self) -> "AsyncAppServerClient": - """Start the app-server process when entering an async context.""" + async def __aenter__(self) -> "AsyncCodexClient": + """Start the Codex process when entering an async context.""" await self.start() return self async def __aexit__(self, _exc_type, _exc, _tb) -> None: - """Close the app-server process when leaving an async context.""" + """Close the Codex process when leaving an async context.""" await self.close() async def _call_sync( @@ -88,7 +88,7 @@ class AsyncAppServerClient: await self._call_sync(self._sync.close) async def initialize(self) -> InitializeResponse: - """Initialize the app-server session.""" + """Initialize the Codex session.""" return await self._call_sync(self._sync.initialize) def register_turn_notifications(self, turn_id: str) -> None: diff --git a/sdk/python/src/openai_codex/client.py b/sdk/python/src/openai_codex/client.py index 794411d2a1..a97e12f658 100644 --- a/sdk/python/src/openai_codex/client.py +++ b/sdk/python/src/openai_codex/client.py @@ -12,7 +12,7 @@ from pydantic import BaseModel from ._message_router import MessageRouter from ._version import __version__ as SDK_VERSION -from .errors import AppServerError, TransportClosedError +from .errors import CodexError, TransportClosedError from .generated.notification_registry import NOTIFICATION_MODELS from .generated.v2_all import ( AccountLoginCompletedNotification, @@ -94,7 +94,7 @@ def _installed_codex_path() -> Path: except ImportError as exc: raise FileNotFoundError( "Unable to locate the pinned Codex runtime. Install the published SDK build " - f"with its {RUNTIME_PKG_NAME} dependency, or set AppServerConfig.codex_bin " + f"with its {RUNTIME_PKG_NAME} dependency, or set CodexConfig.codex_bin " "explicitly." ) from exc @@ -153,12 +153,12 @@ def _default_codex_bin_resolver_ops() -> CodexBinResolverOps: ) -def resolve_codex_bin(config: "AppServerConfig", ops: CodexBinResolverOps) -> Path: +def resolve_codex_bin(config: "CodexConfig", ops: CodexBinResolverOps) -> Path: if config.codex_bin is not None: codex_bin = Path(config.codex_bin) if not ops.path_exists(codex_bin): raise FileNotFoundError( - f"Codex binary not found at {codex_bin}. Set AppServerConfig.codex_bin " + f"Codex binary not found at {codex_bin}. Set CodexConfig.codex_bin " "to a valid binary path." ) return codex_bin @@ -166,12 +166,12 @@ def resolve_codex_bin(config: "AppServerConfig", ops: CodexBinResolverOps) -> Pa return ops.installed_codex_path() -def _resolve_codex_bin(config: "AppServerConfig") -> Path: +def _resolve_codex_bin(config: "CodexConfig") -> Path: return resolve_codex_bin(config, _default_codex_bin_resolver_ops()) @dataclass(slots=True) -class AppServerConfig: +class CodexConfig: codex_bin: str | None = None launch_args_override: tuple[str, ...] | None = None config_overrides: tuple[str, ...] = () @@ -183,15 +183,15 @@ class AppServerConfig: experimental_api: bool = True -class AppServerClient: +class CodexClient: """Synchronous typed JSON-RPC client for `codex app-server` over stdio.""" def __init__( self, - config: AppServerConfig | None = None, + config: CodexConfig | None = None, approval_handler: ApprovalHandler | None = None, ) -> None: - self.config = config or AppServerConfig() + self.config = config or CodexConfig() self._approval_handler = approval_handler or self._default_approval_handler self._proc: subprocess.Popen[str] | None = None self._lock = threading.Lock() @@ -200,7 +200,7 @@ class AppServerClient: self._stderr_thread: threading.Thread | None = None self._reader_thread: threading.Thread | None = None - def __enter__(self) -> "AppServerClient": + def __enter__(self) -> "CodexClient": self.start() return self @@ -289,7 +289,7 @@ class AppServerClient: ) -> ModelT: result = self._request_raw(method, params) if not isinstance(result, dict): - raise AppServerError(f"{method} response must be a JSON object") + raise CodexError(f"{method} response must be a JSON object") return response_model.model_validate(result) def _request_raw(self, method: str, params: JsonObject | None = None) -> JsonValue: @@ -659,28 +659,28 @@ class AppServerClient: def _write_message(self, payload: JsonObject) -> None: if self._proc is None or self._proc.stdin is None: - raise TransportClosedError("app-server is not running") + raise TransportClosedError("Codex process is not running") with self._lock: self._proc.stdin.write(json.dumps(payload) + "\n") self._proc.stdin.flush() def _read_message(self) -> dict[str, JsonValue]: if self._proc is None or self._proc.stdout is None: - raise TransportClosedError("app-server is not running") + raise TransportClosedError("Codex process is not running") line = self._proc.stdout.readline() if not line: raise TransportClosedError( - f"app-server closed stdout. stderr_tail={self._stderr_tail()[:2000]}" + f"Codex process closed stdout. stderr_tail={self._stderr_tail()[:2000]}" ) try: message = json.loads(line) except json.JSONDecodeError as exc: - raise AppServerError(f"Invalid JSON-RPC line: {line!r}") from exc + raise CodexError(f"Invalid JSON-RPC line: {line!r}") from exc if not isinstance(message, dict): - raise AppServerError(f"Invalid JSON-RPC payload: {message!r}") + raise CodexError(f"Invalid JSON-RPC payload: {message!r}") return message diff --git a/sdk/python/src/openai_codex/errors.py b/sdk/python/src/openai_codex/errors.py index 104e35f2e9..63ff7d2d2a 100644 --- a/sdk/python/src/openai_codex/errors.py +++ b/sdk/python/src/openai_codex/errors.py @@ -3,11 +3,11 @@ from __future__ import annotations from typing import Any -class AppServerError(Exception): +class CodexError(Exception): """Base exception for SDK errors.""" -class JsonRpcError(AppServerError): +class JsonRpcError(CodexError): """Raw JSON-RPC error wrapper from the server.""" def __init__(self, code: int, message: str, data: Any = None): @@ -17,35 +17,35 @@ class JsonRpcError(AppServerError): self.data = data -class TransportClosedError(AppServerError): - """Raised when the app-server transport closes unexpectedly.""" +class TransportClosedError(CodexError): + """Raised when the Codex transport closes unexpectedly.""" -class AppServerRpcError(JsonRpcError): +class CodexRpcError(JsonRpcError): """Base typed error for JSON-RPC failures.""" -class ParseError(AppServerRpcError): +class ParseError(CodexRpcError): pass -class InvalidRequestError(AppServerRpcError): +class InvalidRequestError(CodexRpcError): pass -class MethodNotFoundError(AppServerRpcError): +class MethodNotFoundError(CodexRpcError): pass -class InvalidParamsError(AppServerRpcError): +class InvalidParamsError(CodexRpcError): pass -class InternalRpcError(AppServerRpcError): +class InternalRpcError(CodexRpcError): pass -class ServerBusyError(AppServerRpcError): +class ServerBusyError(CodexRpcError): """Server is overloaded / unavailable and caller should retry.""" @@ -104,7 +104,7 @@ def map_jsonrpc_error(code: int, message: str, data: Any = None) -> JsonRpcError return ServerBusyError(code, message, data) if _contains_retry_limit_text(message): return RetryLimitExceededError(code, message, data) - return AppServerRpcError(code, message, data) + return CodexRpcError(code, message, data) return JsonRpcError(code, message, data) diff --git a/sdk/python/src/openai_codex/types.py b/sdk/python/src/openai_codex/types.py index f5bbbc86c3..ae4b769db4 100644 --- a/sdk/python/src/openai_codex/types.py +++ b/sdk/python/src/openai_codex/types.py @@ -1,4 +1,4 @@ -"""Public app-server model exports for type annotations and matching.""" +"""Public Codex protocol model exports for type annotations and matching.""" from __future__ import annotations diff --git a/sdk/python/tests/app_server_harness.py b/sdk/python/tests/app_server_harness.py index 63de28d3af..cf0c847394 100644 --- a/sdk/python/tests/app_server_harness.py +++ b/sdk/python/tests/app_server_harness.py @@ -10,7 +10,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from typing import Any -from openai_codex import AppServerConfig +from openai_codex import CodexConfig Json = dict[str, Any] @@ -225,9 +225,9 @@ class AppServerHarness: shutil.rmtree(self.codex_home, ignore_errors=True) shutil.rmtree(self.workspace, ignore_errors=True) - def app_server_config(self) -> AppServerConfig: + def app_server_config(self) -> CodexConfig: """Build SDK config for an isolated pinned-runtime app-server process.""" - return AppServerConfig( + return CodexConfig( cwd=str(self.workspace), env={ "CODEX_HOME": str(self.codex_home), diff --git a/sdk/python/tests/test_app_server_login.py b/sdk/python/tests/test_app_server_login.py index 088310e5e8..3437449403 100644 --- a/sdk/python/tests/test_app_server_login.py +++ b/sdk/python/tests/test_app_server_login.py @@ -5,14 +5,14 @@ import json from app_server_harness import AppServerHarness -from openai_codex import AppServerConfig, Codex +from openai_codex import Codex, CodexConfig from openai_codex.generated.v2_all import ( ChatgptAuthTokensLoginAccountParams, LoginAccountParams, ) -def _app_server_config(harness: AppServerHarness) -> AppServerConfig: +def _app_server_config(harness: AppServerHarness) -> CodexConfig: """Build an isolated login config without inheriting ambient API-key auth.""" config = harness.app_server_config() config.env = {**(config.env or {}), "OPENAI_API_KEY": ""} diff --git a/sdk/python/tests/test_app_server_streaming.py b/sdk/python/tests/test_app_server_streaming.py index 3b79d9def3..ff69c69da3 100644 --- a/sdk/python/tests/test_app_server_streaming.py +++ b/sdk/python/tests/test_app_server_streaming.py @@ -114,7 +114,7 @@ def test_async_stream_routes_text_deltas_and_completion(tmp_path) -> None: def test_low_level_sync_stream_text_uses_real_turn_routing(tmp_path) -> None: - """AppServerClient.stream_text should stream through a real app-server turn.""" + """CodexClient.stream_text should stream through a real app-server turn.""" with AppServerHarness(tmp_path) as harness: harness.responses.enqueue_sse( streaming_response("low-sync-stream", "msg-low-sync-stream", ["fir", "st"]) diff --git a/sdk/python/tests/test_artifact_workflow_and_binaries.py b/sdk/python/tests/test_artifact_workflow_and_binaries.py index 44459d9f76..c7b5713ba1 100644 --- a/sdk/python/tests/test_artifact_workflow_and_binaries.py +++ b/sdk/python/tests/test_artifact_workflow_and_binaries.py @@ -669,7 +669,7 @@ def test_default_runtime_is_resolved_from_installed_runtime_package( path_exists=lambda path: path == fake_binary, ) - config = client_module.AppServerConfig() + config = client_module.CodexConfig() assert config.codex_bin is None assert client_module.resolve_codex_bin(config, ops) == fake_binary @@ -717,7 +717,7 @@ def test_explicit_codex_bin_override_takes_priority(tmp_path: Path) -> None: path_exists=lambda path: path == explicit_binary, ) - config = client_module.AppServerConfig(codex_bin=str(explicit_binary)) + config = client_module.CodexConfig(codex_bin=str(explicit_binary)) assert client_module.resolve_codex_bin(config, ops) == explicit_binary @@ -732,7 +732,7 @@ def test_missing_runtime_package_requires_explicit_codex_bin() -> None: ) with pytest.raises(FileNotFoundError, match="missing packaged runtime"): - client_module.resolve_codex_bin(client_module.AppServerConfig(), ops) + client_module.resolve_codex_bin(client_module.CodexConfig(), ops) def test_broken_runtime_package_does_not_fall_back() -> None: @@ -746,6 +746,6 @@ def test_broken_runtime_package_does_not_fall_back() -> None: ) with pytest.raises(FileNotFoundError) as exc_info: - client_module.resolve_codex_bin(client_module.AppServerConfig(), ops) + client_module.resolve_codex_bin(client_module.CodexConfig(), ops) assert str(exc_info.value) == ("missing packaged binary") diff --git a/sdk/python/tests/test_async_client_behavior.py b/sdk/python/tests/test_async_client_behavior.py index 662d0a2716..59d44db716 100644 --- a/sdk/python/tests/test_async_client_behavior.py +++ b/sdk/python/tests/test_async_client_behavior.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio import time -from openai_codex.async_client import AsyncAppServerClient +from openai_codex.async_client import AsyncCodexClient from openai_codex.generated.v2_all import ( TurnCompletedNotification, ) @@ -15,7 +15,7 @@ def test_async_client_allows_concurrent_transport_calls() -> None: async def scenario() -> int: """Run two blocking sync calls and report peak overlap.""" - client = AsyncAppServerClient() + client = AsyncCodexClient() active = 0 max_active = 0 @@ -40,7 +40,7 @@ def test_async_client_turn_notification_methods_delegate_to_sync_client() -> Non async def scenario() -> tuple[list[tuple[str, str]], Notification, str]: """Record the sync-client calls made by async turn notification wrappers.""" - client = AsyncAppServerClient() + client = AsyncCodexClient() event = Notification( method="unknown/direct", payload=UnknownNotification(params={"turnId": "turn-1"}), diff --git a/sdk/python/tests/test_client_rpc_methods.py b/sdk/python/tests/test_client_rpc_methods.py index 0a11774baa..73fdd452ff 100644 --- a/sdk/python/tests/test_client_rpc_methods.py +++ b/sdk/python/tests/test_client_rpc_methods.py @@ -2,7 +2,7 @@ from __future__ import annotations from pathlib import Path -from openai_codex.client import AppServerClient, _params_dict +from openai_codex.client import CodexClient, _params_dict from openai_codex.generated.notification_registry import notification_turn_id from openai_codex.generated.v2_all import ( AgentMessageDeltaNotification, @@ -63,7 +63,7 @@ def test_thread_resume_response_accepts_auto_review_reviewer() -> None: def test_notifications_are_typed_with_canonical_v2_methods() -> None: - client = AppServerClient() + client = CodexClient() event = client._coerce_notification( "thread/tokenUsage/updated", { @@ -94,7 +94,7 @@ def test_notifications_are_typed_with_canonical_v2_methods() -> None: def test_unknown_notifications_fall_back_to_unknown_payloads() -> None: - client = AppServerClient() + client = CodexClient() event = client._coerce_notification( "unknown/notification", { @@ -110,7 +110,7 @@ def test_unknown_notifications_fall_back_to_unknown_payloads() -> None: def test_invalid_notification_payload_falls_back_to_unknown() -> None: - client = AppServerClient() + client = CodexClient() event = client._coerce_notification("thread/tokenUsage/updated", {"threadId": "missing"}) assert event.method == "thread/tokenUsage/updated" @@ -144,7 +144,7 @@ def test_generated_notification_turn_id_handles_known_payload_shapes() -> None: def test_turn_notification_router_demuxes_registered_turns() -> None: """The router should deliver out-of-order turn events to the matching queues.""" - client = AppServerClient() + client = CodexClient() client.register_turn_notifications("turn-1") client.register_turn_notifications("turn-2") @@ -187,7 +187,7 @@ def test_turn_notification_router_demuxes_registered_turns() -> None: def test_client_reader_routes_interleaved_turn_notifications_by_turn_id() -> None: """Reader-loop routing should preserve order within each interleaved turn stream.""" - client = AppServerClient() + client = CodexClient() client.register_turn_notifications("turn-1") client.register_turn_notifications("turn-2") @@ -266,7 +266,7 @@ def test_client_reader_routes_interleaved_turn_notifications_by_turn_id() -> Non def test_turn_notification_router_buffers_events_before_registration() -> None: """Early turn events should be replayed once their TurnHandle registers.""" - client = AppServerClient() + client = CodexClient() client._router.route_notification( client._coerce_notification( "item/agentMessage/delta", @@ -291,7 +291,7 @@ def test_turn_notification_router_buffers_events_before_registration() -> None: def test_turn_notification_router_clears_unregistered_turn_when_completed() -> None: """A completed unregistered turn should not leave a pending queue behind.""" - client = AppServerClient() + client = CodexClient() client._router.route_notification( client._coerce_notification( "item/agentMessage/delta", @@ -318,7 +318,7 @@ def test_turn_notification_router_clears_unregistered_turn_when_completed() -> N def test_turn_notification_router_routes_unknown_turn_notifications() -> None: """Unknown notifications should still route when their raw params carry a turn id.""" - client = AppServerClient() + client = CodexClient() client.register_turn_notifications("turn-1") client.register_turn_notifications("turn-2") diff --git a/sdk/python/tests/test_public_api_runtime_behavior.py b/sdk/python/tests/test_public_api_runtime_behavior.py index f6d83a965e..4cee695fee 100644 --- a/sdk/python/tests/test_public_api_runtime_behavior.py +++ b/sdk/python/tests/test_public_api_runtime_behavior.py @@ -52,7 +52,7 @@ def test_codex_init_failure_closes_client(monkeypatch: pytest.MonkeyPatch) -> No self._closed = True closed.append(True) - monkeypatch.setattr(public_api_module, "AppServerClient", FakeClient) + monkeypatch.setattr(public_api_module, "CodexClient", FakeClient) with pytest.raises(RuntimeError, match="missing required metadata"): Codex() diff --git a/sdk/python/tests/test_public_api_signatures.py b/sdk/python/tests/test_public_api_signatures.py index b4dc49acf1..59e25dda24 100644 --- a/sdk/python/tests/test_public_api_signatures.py +++ b/sdk/python/tests/test_public_api_signatures.py @@ -11,11 +11,11 @@ import openai_codex import openai_codex.types as public_types from openai_codex import ( ApprovalMode, - AppServerConfig, AsyncCodex, AsyncThread, AsyncTurnHandle, Codex, + CodexConfig, Sandbox, Thread, TurnHandle, @@ -26,7 +26,7 @@ from openai_codex.types import InitializeResponse EXPECTED_ROOT_EXPORTS = [ "__version__", - "AppServerConfig", + "CodexConfig", "Codex", "AsyncCodex", "ApprovalMode", @@ -49,10 +49,10 @@ EXPECTED_ROOT_EXPORTS = [ "SkillInput", "MentionInput", "retry_on_overload", - "AppServerError", + "CodexError", "TransportClosedError", "JsonRpcError", - "AppServerRpcError", + "CodexRpcError", "ParseError", "InvalidRequestError", "MethodNotFoundError", @@ -129,9 +129,9 @@ def _assert_no_any_annotations(fn: object) -> None: raise AssertionError(f"{fn} has public return annotation typed as Any") -def test_root_exports_app_server_config() -> None: +def test_root_exports_codex_config() -> None: """The root package should expose the process configuration object.""" - assert AppServerConfig.__name__ == "AppServerConfig" + assert CodexConfig.__name__ == "CodexConfig" def test_root_exports_turn_result() -> None: @@ -208,7 +208,7 @@ def test_package_and_default_client_versions_follow_project_version() -> None: pyproject = tomllib.loads(pyproject_path.read_text()) assert openai_codex.__version__ == pyproject["project"]["version"] - assert AppServerConfig().client_version == openai_codex.__version__ + assert CodexConfig().client_version == openai_codex.__version__ def test_package_includes_py_typed_marker() -> None: @@ -224,16 +224,16 @@ def test_package_root_exports_only_public_api() -> None: EXPECTED_ROOT_EXPORTS, True ) assert { - "AppServerClient": hasattr(openai_codex, "AppServerClient"), - "AsyncAppServerClient": hasattr(openai_codex, "AsyncAppServerClient"), + "CodexClient": hasattr(openai_codex, "CodexClient"), + "AsyncCodexClient": hasattr(openai_codex, "AsyncCodexClient"), "InitializeResponse": hasattr(openai_codex, "InitializeResponse"), "ThreadStartParams": hasattr(openai_codex, "ThreadStartParams"), "TurnStartParams": hasattr(openai_codex, "TurnStartParams"), "TurnCompletedNotification": hasattr(openai_codex, "TurnCompletedNotification"), "TurnStatus": hasattr(openai_codex, "TurnStatus"), } == { - "AppServerClient": False, - "AsyncAppServerClient": False, + "CodexClient": False, + "AsyncCodexClient": False, "InitializeResponse": False, "ThreadStartParams": False, "TurnStartParams": False, @@ -252,7 +252,7 @@ def test_package_star_import_matches_public_api() -> None: def test_types_module_exports_curated_public_types() -> None: - """The public type module should be the supported place for app-server models.""" + """The public type module should expose Codex protocol models.""" assert public_types.__all__ == EXPECTED_TYPES_EXPORTS assert {name: hasattr(public_types, name) for name in EXPECTED_TYPES_EXPORTS} == dict.fromkeys( EXPECTED_TYPES_EXPORTS, True