From 8748ac15ca14966af74cdf6762e82ab7e401ff3d Mon Sep 17 00:00:00 2001 From: Shaqayeq Date: Sun, 12 Apr 2026 23:17:46 -0700 Subject: [PATCH] python-sdk: prepare openai-codex package --- sdk/python/README.md | 41 +- sdk/python/docs/faq.md | 12 +- sdk/python/docs/getting-started.md | 2 +- sdk/python/examples/README.md | 12 +- sdk/python/pyproject.toml | 4 +- sdk/python/src/codex_app_server/api.py | 4 + .../generated/notification_registry.py | 2 + .../src/codex_app_server/generated/v2_all.py | 645 ++++++++++++++++-- .../tests/test_public_api_signatures.py | 2 + 9 files changed, 618 insertions(+), 106 deletions(-) diff --git a/sdk/python/README.md b/sdk/python/README.md index 97068afe31..4d90b673ac 100644 --- a/sdk/python/README.md +++ b/sdk/python/README.md @@ -11,10 +11,16 @@ cd sdk/python python -m pip install -e . ``` -Published SDK builds pin an exact `codex-cli-bin` runtime dependency. For local -repo development, either pass `AppServerConfig(codex_bin=...)` to point at a -local build explicitly, or use the repo examples/notebook bootstrap which -installs the pinned runtime package automatically. +Published SDK builds pin an exact `openai-codex-cli-bin` runtime dependency. +For local repo development, either pass `AppServerConfig(codex_bin=...)` to +point at a local build explicitly, or use the repo examples/notebook bootstrap +which installs the pinned runtime package automatically. + +When published, normal installs should use: + +```bash +python -m pip install openai-codex +``` ## Quickstart @@ -53,9 +59,9 @@ python examples/01_quickstart_constructor/async.py The repo no longer checks `codex` binaries into `sdk/python`. -Published SDK builds are pinned to an exact `codex-cli-bin` package version, -and that runtime package carries the platform-specific binary for the target -wheel. +Published SDK builds are pinned to an exact `openai-codex-cli-bin` package +version, and that runtime package carries the platform-specific binary bundle +for the target wheel. For local repo development, the checked-in `sdk/python-runtime` package is only a template for staged release artifacts. Editable installs should use an @@ -69,30 +75,33 @@ cd sdk/python python scripts/update_sdk_artifacts.py generate-types python scripts/update_sdk_artifacts.py \ stage-sdk \ - /tmp/codex-python-release/codex-app-server-sdk \ + /tmp/codex-python-release/openai-codex \ --runtime-version 1.2.3 python scripts/update_sdk_artifacts.py \ stage-runtime \ - /tmp/codex-python-release/codex-cli-bin \ - /path/to/codex \ + /tmp/codex-python-release/openai-codex-cli-bin \ + /path/to/runtime-bundle-dir \ --runtime-version 1.2.3 ``` This supports the CI release flow: - run `generate-types` before packaging -- stage `codex-app-server-sdk` once with an exact `codex-cli-bin==...` dependency -- stage `codex-cli-bin` on each supported platform runner with the same pinned runtime version -- build and publish `codex-cli-bin` as platform wheels only; do not publish an sdist +- stage `openai-codex` once with an exact `openai-codex-cli-bin==...` dependency +- stage `openai-codex-cli-bin` on each supported platform runner with the same pinned runtime version +- build and publish `openai-codex-cli-bin` as platform wheels only; do not publish an sdist ## Compatibility and versioning -- Package: `codex-app-server-sdk` -- Runtime package: `codex-cli-bin` +- Package: `openai-codex` +- Runtime package: `openai-codex-cli-bin` - Current SDK version in this repo: `0.2.0` - Python: `>=3.10` - Target protocol: Codex `app-server` JSON-RPC v2 -- Recommendation: keep SDK and `codex` CLI reasonably up to date together +- Release tags map to Python package versions as follows: `rust-v1.2.3` -> + `1.2.3`, `rust-v1.2.3-alpha.4` -> `1.2.3a4`, and + `rust-v1.2.3-beta.5` -> `1.2.3b5`. +- Recommendation: keep SDK and `codex` CLI at the exact same published version. ## Notes diff --git a/sdk/python/docs/faq.md b/sdk/python/docs/faq.md index b2c9cf3b1f..73f9dc9122 100644 --- a/sdk/python/docs/faq.md +++ b/sdk/python/docs/faq.md @@ -54,26 +54,26 @@ This avoids duplicate ways to do the same operation and keeps behavior explicit. Common causes: -- published runtime package (`codex-cli-bin`) is not installed +- published runtime package (`openai-codex-cli-bin`) is not installed - local `codex_bin` override points to a missing file - local auth/session is missing - incompatible/old app-server Maintainers stage releases by building the SDK once and the runtime once per -platform with the same pinned runtime version. Publish `codex-cli-bin` as -platform wheels only; do not publish an sdist: +platform with the same pinned runtime version. Publish `openai-codex-cli-bin` +as platform wheels only; do not publish an sdist: ```bash cd sdk/python python scripts/update_sdk_artifacts.py generate-types python scripts/update_sdk_artifacts.py \ stage-sdk \ - /tmp/codex-python-release/codex-app-server-sdk \ + /tmp/codex-python-release/openai-codex \ --runtime-version 1.2.3 python scripts/update_sdk_artifacts.py \ stage-runtime \ - /tmp/codex-python-release/codex-cli-bin \ - /path/to/codex \ + /tmp/codex-python-release/openai-codex-cli-bin \ + /path/to/runtime-bundle-dir \ --runtime-version 1.2.3 ``` diff --git a/sdk/python/docs/getting-started.md b/sdk/python/docs/getting-started.md index 76034d72ee..70a193a3d5 100644 --- a/sdk/python/docs/getting-started.md +++ b/sdk/python/docs/getting-started.md @@ -16,7 +16,7 @@ python -m pip install -e . Requirements: - Python `>=3.10` -- installed `codex-cli-bin` runtime package, or an explicit `codex_bin` override +- installed `openai-codex-cli-bin` runtime package, or an explicit `codex_bin` override - local Codex auth/session configured ## 2) Run your first turn (sync) diff --git a/sdk/python/examples/README.md b/sdk/python/examples/README.md index 5edf2badbd..1c8a6262fb 100644 --- a/sdk/python/examples/README.md +++ b/sdk/python/examples/README.md @@ -23,12 +23,12 @@ python -m pip install -e . When running examples from this repo checkout, the SDK source uses the local tree and does not bundle a runtime binary. The helper in `examples/_bootstrap.py` -uses the installed `codex-cli-bin` runtime package. +uses the installed `openai-codex-cli-bin` runtime package. -If the pinned `codex-cli-bin` runtime is not already installed, the bootstrap +If the pinned `openai-codex-cli-bin` runtime is not already installed, the bootstrap will download the matching GitHub release artifact, stage a temporary local -`codex-cli-bin` package, install it into your active interpreter, and clean up -the temporary files afterward. +`openai-codex-cli-bin` package, install it into your active interpreter, and +clean up the temporary files afterward. Current pinned runtime version: `0.116.0-alpha.1` @@ -43,8 +43,8 @@ python examples//async.py The examples bootstrap local imports from `sdk/python/src` automatically, so no SDK wheel install is required. You only need the Python dependencies for your -active interpreter and an installed `codex-cli-bin` runtime package (either -already present or automatically provisioned by the bootstrap). +active interpreter and an installed `openai-codex-cli-bin` runtime package +(either already present or automatically provisioned by the bootstrap). ## Recommended first run diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml index f5129cbf93..203641d500 100644 --- a/sdk/python/pyproject.toml +++ b/sdk/python/pyproject.toml @@ -3,13 +3,13 @@ requires = ["hatchling>=1.24.0"] build-backend = "hatchling.build" [project] -name = "codex-app-server-sdk" +name = "openai-codex" version = "0.2.0" description = "Python SDK for Codex app-server v2" readme = "README.md" requires-python = ">=3.10" license = { text = "Apache-2.0" } -authors = [{ name = "OpenClaw Assistant" }] +authors = [{ name = "OpenAI" }] keywords = ["codex", "json-rpc", "sdk", "llm", "app-server"] classifiers = [ "Development Status :: 4 - Beta", diff --git a/sdk/python/src/codex_app_server/api.py b/sdk/python/src/codex_app_server/api.py index 5009d9bbf5..6cfdd286db 100644 --- a/sdk/python/src/codex_app_server/api.py +++ b/sdk/python/src/codex_app_server/api.py @@ -146,6 +146,7 @@ class Codex: sandbox: SandboxMode | None = None, service_name: str | None = None, service_tier: ServiceTier | None = None, + session_start_source: ThreadStartSource | None = None, ) -> Thread: params = ThreadStartParams( approval_policy=approval_policy, @@ -161,6 +162,7 @@ class Codex: sandbox=sandbox, service_name=service_name, service_tier=service_tier, + session_start_source=session_start_source, ) started = self._client.thread_start(params) return Thread(self._client, started.thread.id) @@ -336,6 +338,7 @@ class AsyncCodex: sandbox: SandboxMode | None = None, service_name: str | None = None, service_tier: ServiceTier | None = None, + session_start_source: ThreadStartSource | None = None, ) -> AsyncThread: await self._ensure_initialized() params = ThreadStartParams( @@ -352,6 +355,7 @@ class AsyncCodex: sandbox=sandbox, service_name=service_name, service_tier=service_tier, + session_start_source=session_start_source, ) started = await self._client.thread_start(params) return AsyncThread(self, started.thread.id) diff --git a/sdk/python/src/codex_app_server/generated/notification_registry.py b/sdk/python/src/codex_app_server/generated/notification_registry.py index ca885d92de..506a684762 100644 --- a/sdk/python/src/codex_app_server/generated/notification_registry.py +++ b/sdk/python/src/codex_app_server/generated/notification_registry.py @@ -44,6 +44,7 @@ from .v2_all import ThreadRealtimeClosedNotification from .v2_all import ThreadRealtimeErrorNotification from .v2_all import ThreadRealtimeItemAddedNotification from .v2_all import ThreadRealtimeOutputAudioDeltaNotification +from .v2_all import ThreadRealtimeSdpNotification from .v2_all import ThreadRealtimeStartedNotification from .v2_all import ThreadRealtimeTranscriptUpdatedNotification from .v2_all import ThreadStartedNotification @@ -97,6 +98,7 @@ NOTIFICATION_MODELS: dict[str, type[BaseModel]] = { "thread/realtime/error": ThreadRealtimeErrorNotification, "thread/realtime/itemAdded": ThreadRealtimeItemAddedNotification, "thread/realtime/outputAudio/delta": ThreadRealtimeOutputAudioDeltaNotification, + "thread/realtime/sdp": ThreadRealtimeSdpNotification, "thread/realtime/started": ThreadRealtimeStartedNotification, "thread/realtime/transcriptUpdated": ThreadRealtimeTranscriptUpdatedNotification, "thread/started": ThreadStartedNotification, diff --git a/sdk/python/src/codex_app_server/generated/v2_all.py b/sdk/python/src/codex_app_server/generated/v2_all.py index 21d4968c33..357e9248c0 100644 --- a/sdk/python/src/codex_app_server/generated/v2_all.py +++ b/sdk/python/src/codex_app_server/generated/v2_all.py @@ -211,6 +211,18 @@ class AuthMode(Enum): chatgpt_auth_tokens = "chatgptAuthTokens" +class AutoReviewDecisionSource(RootModel[Literal["agent"]]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + Literal["agent"], + Field( + description="[UNSTABLE] Source that produced a terminal guardian approval review decision." + ), + ] + + class ByteRange(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -832,6 +844,7 @@ class FeedbackUploadParams(BaseModel): extra_log_files: Annotated[list[str] | None, Field(alias="extraLogFiles")] = None include_logs: Annotated[bool, Field(alias="includeLogs")] reason: str | None = None + tags: dict[str, Any] | None = None thread_id: Annotated[str | None, Field(alias="threadId")] = None @@ -870,7 +883,10 @@ class FsChangedNotification(BaseModel): ] watch_id: Annotated[ str, - Field(alias="watchId", description="Watch identifier returned by `fs/watch`."), + Field( + alias="watchId", + description="Watch identifier previously provided to `fs/watch`.", + ), ] @@ -1054,7 +1070,10 @@ class FsUnwatchParams(BaseModel): ) watch_id: Annotated[ str, - Field(alias="watchId", description="Watch identifier returned by `fs/watch`."), + Field( + alias="watchId", + description="Watch identifier previously provided to `fs/watch`.", + ), ] @@ -1072,6 +1091,13 @@ class FsWatchParams(BaseModel): path: Annotated[ AbsolutePathBuf, Field(description="Absolute file or directory path to watch.") ] + watch_id: Annotated[ + str, + Field( + alias="watchId", + description="Connection-scoped watch identifier used for `fs/unwatch` and `fs/changed`.", + ), + ] class FsWatchResponse(BaseModel): @@ -1082,13 +1108,6 @@ class FsWatchResponse(BaseModel): AbsolutePathBuf, Field(description="Canonicalized path associated with the watch."), ] - watch_id: Annotated[ - str, - Field( - alias="watchId", - description="Connection-scoped watch identifier used for `fs/unwatch` and `fs/changed`.", - ), - ] class FsWriteFileParams(BaseModel): @@ -1199,17 +1218,57 @@ class GitInfo(BaseModel): sha: str | None = None +class ApplyPatchGuardianApprovalReviewAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwd: str + files: list[str] + type: Annotated[ + Literal["applyPatch"], Field(title="ApplyPatchGuardianApprovalReviewActionType") + ] + + +class McpToolCallGuardianApprovalReviewAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + connector_id: Annotated[str | None, Field(alias="connectorId")] = None + connector_name: Annotated[str | None, Field(alias="connectorName")] = None + server: str + tool_name: Annotated[str, Field(alias="toolName")] + tool_title: Annotated[str | None, Field(alias="toolTitle")] = None + type: Annotated[ + Literal["mcpToolCall"], + Field(title="McpToolCallGuardianApprovalReviewActionType"), + ] + + class GuardianApprovalReviewStatus(Enum): in_progress = "inProgress" approved = "approved" denied = "denied" + timed_out = "timedOut" aborted = "aborted" +class GuardianCommandSource(Enum): + shell = "shell" + unified_exec = "unifiedExec" + + class GuardianRiskLevel(Enum): low = "low" medium = "medium" high = "high" + critical = "critical" + + +class GuardianUserAuthorization(Enum): + unknown = "unknown" + low = "low" + medium = "medium" + high = "high" class HookEventName(Enum): @@ -1300,22 +1359,6 @@ class InputModality(Enum): image = "image" -class ListMcpServerStatusParams(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - cursor: Annotated[ - str | None, - Field(description="Opaque pagination cursor returned by a previous call."), - ] = None - limit: Annotated[ - int | None, - Field( - description="Optional page size; defaults to a server-defined value.", ge=0 - ), - ] = None - - class ExecLocalShellAction(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -1358,6 +1401,16 @@ class ChatgptLoginAccountParams(BaseModel): ] +class ChatgptDeviceCodeLoginAccountParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[ + Literal["chatgptDeviceCode"], + Field(title="ChatgptDeviceCodev2::LoginAccountParamsType"), + ] + + class ChatgptAuthTokensLoginAccountParams(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -1393,6 +1446,7 @@ class LoginAccountParams( RootModel[ ApiKeyLoginAccountParams | ChatgptLoginAccountParams + | ChatgptDeviceCodeLoginAccountParams | ChatgptAuthTokensLoginAccountParams ] ): @@ -1402,6 +1456,7 @@ class LoginAccountParams( root: Annotated[ ApiKeyLoginAccountParams | ChatgptLoginAccountParams + | ChatgptDeviceCodeLoginAccountParams | ChatgptAuthTokensLoginAccountParams, Field(title="LoginAccountParams"), ] @@ -1433,6 +1488,31 @@ class ChatgptLoginAccountResponse(BaseModel): ] +class ChatgptDeviceCodeLoginAccountResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + login_id: Annotated[str, Field(alias="loginId")] + type: Annotated[ + Literal["chatgptDeviceCode"], + Field(title="ChatgptDeviceCodev2::LoginAccountResponseType"), + ] + user_code: Annotated[ + str, + Field( + alias="userCode", + description="One-time code the user must enter after signing in.", + ), + ] + verification_url: Annotated[ + str, + Field( + alias="verificationUrl", + description="URL the client should open in a browser to complete device code authorization.", + ), + ] + + class ChatgptAuthTokensLoginAccountResponse(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -1447,6 +1527,7 @@ class LoginAccountResponse( RootModel[ ApiKeyLoginAccountResponse | ChatgptLoginAccountResponse + | ChatgptDeviceCodeLoginAccountResponse | ChatgptAuthTokensLoginAccountResponse ] ): @@ -1456,6 +1537,7 @@ class LoginAccountResponse( root: Annotated[ ApiKeyLoginAccountResponse | ChatgptLoginAccountResponse + | ChatgptDeviceCodeLoginAccountResponse | ChatgptAuthTokensLoginAccountResponse, Field(title="LoginAccountResponse"), ] @@ -1490,6 +1572,15 @@ class McpAuthStatus(Enum): o_auth = "oAuth" +class McpResourceReadParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + server: str + thread_id: Annotated[str, Field(alias="threadId")] + uri: str + + class McpServerOauthLoginCompletedNotification(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -1529,6 +1620,11 @@ class McpServerStartupState(Enum): cancelled = "cancelled" +class McpServerStatusDetail(Enum): + full = "full" + tools_and_auth_only = "toolsAndAuthOnly" + + class McpServerStatusUpdatedNotification(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -1538,6 +1634,27 @@ class McpServerStatusUpdatedNotification(BaseModel): status: McpServerStartupState +class McpServerToolCallParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + field_meta: Annotated[Any | None, Field(alias="_meta")] = None + arguments: Any | None = None + server: str + thread_id: Annotated[str, Field(alias="threadId")] + tool: str + + +class McpServerToolCallResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + field_meta: Annotated[Any | None, Field(alias="_meta")] = None + content: list + is_error: Annotated[bool | None, Field(alias="isError")] = None + structured_content: Annotated[Any | None, Field(alias="structuredContent")] = None + + class McpToolCallError(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -1559,6 +1676,7 @@ class McpToolCallResult(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + field_meta: Annotated[Any | None, Field(alias="_meta")] = None content: list structured_content: Annotated[Any | None, Field(alias="structuredContent")] = None @@ -1658,28 +1776,84 @@ class NetworkAccess(Enum): enabled = "enabled" +class NetworkApprovalProtocol(Enum): + http = "http" + https = "https" + socks5_tcp = "socks5Tcp" + socks5_udp = "socks5Udp" + + +class NetworkDomainPermission(Enum): + allow = "allow" + deny = "deny" + + class NetworkRequirements(BaseModel): model_config = ConfigDict( populate_by_name=True, ) allow_local_binding: Annotated[bool | None, Field(alias="allowLocalBinding")] = None - allow_unix_sockets: Annotated[list[str] | None, Field(alias="allowUnixSockets")] = ( - None - ) + allow_unix_sockets: Annotated[ + list[str] | None, + Field( + alias="allowUnixSockets", + description="Legacy compatibility view derived from `unix_sockets`.", + ), + ] = None allow_upstream_proxy: Annotated[bool | None, Field(alias="allowUpstreamProxy")] = ( None ) - allowed_domains: Annotated[list[str] | None, Field(alias="allowedDomains")] = None + allowed_domains: Annotated[ + list[str] | None, + Field( + alias="allowedDomains", + description="Legacy compatibility view derived from `domains`.", + ), + ] = None + danger_full_access_denylist_only: Annotated[ + bool | None, Field(alias="dangerFullAccessDenylistOnly") + ] = None dangerously_allow_all_unix_sockets: Annotated[ bool | None, Field(alias="dangerouslyAllowAllUnixSockets") ] = None dangerously_allow_non_loopback_proxy: Annotated[ bool | None, Field(alias="dangerouslyAllowNonLoopbackProxy") ] = None - denied_domains: Annotated[list[str] | None, Field(alias="deniedDomains")] = None + denied_domains: Annotated[ + list[str] | None, + Field( + alias="deniedDomains", + description="Legacy compatibility view derived from `domains`.", + ), + ] = None + domains: Annotated[ + dict[str, Any] | None, + Field( + description="Canonical network permission map for `experimental_network`." + ), + ] = None enabled: bool | None = None http_port: Annotated[int | None, Field(alias="httpPort", ge=0)] = None + managed_allowed_domains_only: Annotated[ + bool | None, + Field( + alias="managedAllowedDomainsOnly", + description="When true, only managed allowlist entries are respected while managed network enforcement is active.", + ), + ] = None socks_port: Annotated[int | None, Field(alias="socksPort", ge=0)] = None + unix_sockets: Annotated[ + dict[str, Any] | None, + Field( + alias="unixSockets", + description="Canonical unix socket permission map for `experimental_network`.", + ), + ] = None + + +class NetworkUnixSocketPermission(Enum): + allow = "allow" + none = "none" class NonSteerableTurnKind(Enum): @@ -1746,6 +1920,7 @@ class PlanType(Enum): go = "go" plus = "plus" pro = "pro" + prolite = "prolite" team = "team" self_serve_business_usage_based = "self_serve_business_usage_based" business = "business" @@ -1935,6 +2110,38 @@ class RealtimeConversationVersion(Enum): v2 = "v2" +class RealtimeVoice(Enum): + alloy = "alloy" + arbor = "arbor" + ash = "ash" + ballad = "ballad" + breeze = "breeze" + cedar = "cedar" + coral = "coral" + cove = "cove" + echo = "echo" + ember = "ember" + juniper = "juniper" + maple = "maple" + marin = "marin" + sage = "sage" + shimmer = "shimmer" + sol = "sol" + spruce = "spruce" + vale = "vale" + verse = "verse" + + +class RealtimeVoicesList(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + default_v1: Annotated[RealtimeVoice, Field(alias="defaultV1")] + default_v2: Annotated[RealtimeVoice, Field(alias="defaultV2")] + v1: list[RealtimeVoice] + v2: list[RealtimeVoice] + + class ReasoningEffort(Enum): none = "none" minimal = "minimal" @@ -2078,6 +2285,38 @@ class Resource(BaseModel): uri: str +class ResourceContent1(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + field_meta: Annotated[Any | None, Field(alias="_meta")] = None + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + text: str + uri: Annotated[str, Field(description="The URI of this resource.")] + + +class ResourceContent2(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + field_meta: Annotated[Any | None, Field(alias="_meta")] = None + blob: str + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + uri: Annotated[str, Field(description="The URI of this resource.")] + + +class ResourceContent(RootModel[ResourceContent1 | ResourceContent2]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + ResourceContent1 | ResourceContent2, + Field( + description="Contents returned when reading a resource from an MCP server." + ), + ] + + class ResourceTemplate(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -3222,6 +3461,52 @@ class ThreadRealtimeOutputAudioDeltaNotification(BaseModel): thread_id: Annotated[str, Field(alias="threadId")] +class ThreadRealtimeSdpNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + sdp: str + thread_id: Annotated[str, Field(alias="threadId")] + + +class WebsocketThreadRealtimeStartTransport(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[ + Literal["websocket"], Field(title="WebsocketThreadRealtimeStartTransportType") + ] + + +class WebrtcThreadRealtimeStartTransport(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + sdp: Annotated[ + str, + Field( + description="SDP offer generated by a WebRTC RTCPeerConnection after configuring audio and the realtime events data channel." + ), + ] + type: Annotated[ + Literal["webrtc"], Field(title="WebrtcThreadRealtimeStartTransportType") + ] + + +class ThreadRealtimeStartTransport( + RootModel[ + WebsocketThreadRealtimeStartTransport | WebrtcThreadRealtimeStartTransport + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + WebsocketThreadRealtimeStartTransport | WebrtcThreadRealtimeStartTransport, + Field(description="EXPERIMENTAL - transport used by thread realtime."), + ] + + class ThreadRealtimeStartedNotification(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -3339,33 +3624,9 @@ class ThreadSourceKind(Enum): unknown = "unknown" -class ThreadStartParams(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - approval_policy: Annotated[AskForApproval | None, Field(alias="approvalPolicy")] = ( - None - ) - approvals_reviewer: Annotated[ - ApprovalsReviewer | None, - Field( - alias="approvalsReviewer", - description="Override where approval requests are routed for review on this thread and subsequent turns.", - ), - ] = None - base_instructions: Annotated[str | None, Field(alias="baseInstructions")] = None - config: dict[str, Any] | None = None - cwd: str | None = None - developer_instructions: Annotated[ - str | None, Field(alias="developerInstructions") - ] = None - ephemeral: bool | None = None - model: str | None = None - model_provider: Annotated[str | None, Field(alias="modelProvider")] = None - personality: Personality | None = None - sandbox: SandboxMode | None = None - service_name: Annotated[str | None, Field(alias="serviceName")] = None - service_tier: Annotated[ServiceTier | None, Field(alias="serviceTier")] = None +class ThreadStartSource(Enum): + startup = "startup" + clear = "clear" class NotLoadedThreadStatus(BaseModel): @@ -3795,15 +4056,6 @@ class InitializeRequest(BaseModel): params: InitializeParams -class ThreadStartRequest(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - id: RequestId - method: Annotated[Literal["thread/start"], Field(title="Thread/startRequestMethod")] - params: ThreadStartParams - - class ThreadResumeRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -4158,16 +4410,27 @@ class ConfigMcpServerReloadRequest(BaseModel): params: None = None -class McpServerStatusListRequest(BaseModel): +class McpServerResourceReadRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, ) id: RequestId method: Annotated[ - Literal["mcpServerStatus/list"], - Field(title="McpServerStatus/listRequestMethod"), + Literal["mcpServer/resource/read"], + Field(title="McpServer/resource/readRequestMethod"), ] - params: ListMcpServerStatusParams + params: McpResourceReadParams + + +class McpServerToolCallRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["mcpServer/tool/call"], Field(title="McpServer/tool/callRequestMethod") + ] + params: McpServerToolCallParams class WindowsSandboxSetupStartRequest(BaseModel): @@ -4738,8 +5001,70 @@ class GuardianApprovalReview(BaseModel): ) rationale: str | None = None risk_level: Annotated[GuardianRiskLevel | None, Field(alias="riskLevel")] = None - risk_score: Annotated[int | None, Field(alias="riskScore", ge=0)] = None status: GuardianApprovalReviewStatus + user_authorization: Annotated[ + GuardianUserAuthorization | None, Field(alias="userAuthorization") + ] = None + + +class CommandGuardianApprovalReviewAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: str + cwd: str + source: GuardianCommandSource + type: Annotated[ + Literal["command"], Field(title="CommandGuardianApprovalReviewActionType") + ] + + +class ExecveGuardianApprovalReviewAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + argv: list[str] + cwd: str + program: str + source: GuardianCommandSource + type: Annotated[ + Literal["execve"], Field(title="ExecveGuardianApprovalReviewActionType") + ] + + +class NetworkAccessGuardianApprovalReviewAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + host: str + port: Annotated[int, Field(ge=0)] + protocol: NetworkApprovalProtocol + target: str + type: Annotated[ + Literal["networkAccess"], + Field(title="NetworkAccessGuardianApprovalReviewActionType"), + ] + + +class GuardianApprovalReviewAction( + RootModel[ + CommandGuardianApprovalReviewAction + | ExecveGuardianApprovalReviewAction + | ApplyPatchGuardianApprovalReviewAction + | NetworkAccessGuardianApprovalReviewAction + | McpToolCallGuardianApprovalReviewAction + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ( + CommandGuardianApprovalReviewAction + | ExecveGuardianApprovalReviewAction + | ApplyPatchGuardianApprovalReviewAction + | NetworkAccessGuardianApprovalReviewAction + | McpToolCallGuardianApprovalReviewAction + ) class HookOutputEntry(BaseModel): @@ -4782,9 +5107,19 @@ class ItemGuardianApprovalReviewCompletedNotification(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - action: Any | None = None + action: GuardianApprovalReviewAction + decision_source: Annotated[AutoReviewDecisionSource, Field(alias="decisionSource")] review: GuardianApprovalReview - target_item_id: Annotated[str, Field(alias="targetItemId")] + review_id: Annotated[ + str, Field(alias="reviewId", description="Stable identifier for this review.") + ] + target_item_id: Annotated[ + str | None, + Field( + alias="targetItemId", + description="Identifier for the reviewed item or tool call when one exists.\n\nIn most cases, one review maps to one target item. The exceptions are - execve reviews, where a single command may contain multiple execve calls to review (only possible when using the shell_zsh_fork feature) - network policy reviews, where there is no target item\n\nA network call is triggered by a CommandExecution item, so having a target_item_id set to the CommandExecution item would be misleading because the review is about the network call, not the command execution. Therefore, target_item_id is set to None for network policy reviews.", + ), + ] = None thread_id: Annotated[str, Field(alias="threadId")] turn_id: Annotated[str, Field(alias="turnId")] @@ -4793,13 +5128,51 @@ class ItemGuardianApprovalReviewStartedNotification(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - action: Any | None = None + action: GuardianApprovalReviewAction review: GuardianApprovalReview - target_item_id: Annotated[str, Field(alias="targetItemId")] + review_id: Annotated[ + str, Field(alias="reviewId", description="Stable identifier for this review.") + ] + target_item_id: Annotated[ + str | None, + Field( + alias="targetItemId", + description="Identifier for the reviewed item or tool call when one exists.\n\nIn most cases, one review maps to one target item. The exceptions are - execve reviews, where a single command may contain multiple execve calls to review (only possible when using the shell_zsh_fork feature) - network policy reviews, where there is no target item\n\nA network call is triggered by a CommandExecution item, so having a target_item_id set to the CommandExecution item would be misleading because the review is about the network call, not the command execution. Therefore, target_item_id is set to None for network policy reviews.", + ), + ] = None thread_id: Annotated[str, Field(alias="threadId")] turn_id: Annotated[str, Field(alias="turnId")] +class ListMcpServerStatusParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cursor: Annotated[ + str | None, + Field(description="Opaque pagination cursor returned by a previous call."), + ] = None + detail: Annotated[ + McpServerStatusDetail | None, + Field( + description="Controls how much MCP inventory data to fetch for each server. Defaults to `Full` when omitted." + ), + ] = None + limit: Annotated[ + int | None, + Field( + description="Optional page size; defaults to a server-defined value.", ge=0 + ), + ] = None + + +class McpResourceReadResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + contents: list[ResourceContent] + + class McpServerStatus(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -4825,6 +5198,9 @@ class Model(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + additional_speed_tiers: Annotated[ + list[str] | None, Field(alias="additionalSpeedTiers") + ] = [] availability_nux: Annotated[ ModelAvailabilityNux | None, Field(alias="availabilityNux") ] = None @@ -5142,6 +5518,17 @@ class ThreadRealtimeOutputAudioDeltaServerNotification(BaseModel): params: ThreadRealtimeOutputAudioDeltaNotification +class ThreadRealtimeSdpServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/realtime/sdp"], + Field(title="Thread/realtime/sdpNotificationMethod"), + ] + params: ThreadRealtimeSdpNotification + + class ThreadRealtimeErrorServerNotification(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -5444,6 +5831,38 @@ class ThreadListParams(BaseModel): ] = None +class ThreadStartParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_policy: Annotated[AskForApproval | None, Field(alias="approvalPolicy")] = ( + None + ) + approvals_reviewer: Annotated[ + ApprovalsReviewer | None, + Field( + alias="approvalsReviewer", + description="Override where approval requests are routed for review on this thread and subsequent turns.", + ), + ] = None + base_instructions: Annotated[str | None, Field(alias="baseInstructions")] = None + config: dict[str, Any] | None = None + cwd: str | None = None + developer_instructions: Annotated[ + str | None, Field(alias="developerInstructions") + ] = None + ephemeral: bool | None = None + model: str | None = None + model_provider: Annotated[str | None, Field(alias="modelProvider")] = None + personality: Personality | None = None + sandbox: SandboxMode | None = None + service_name: Annotated[str | None, Field(alias="serviceName")] = None + service_tier: Annotated[ServiceTier | None, Field(alias="serviceTier")] = None + session_start_source: Annotated[ + ThreadStartSource | None, Field(alias="sessionStartSource") + ] = None + + class ThreadTokenUsage(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -5660,6 +6079,15 @@ class AppsListResponse(BaseModel): ] = None +class ThreadStartRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/start"], Field(title="Thread/startRequestMethod")] + params: ThreadStartParams + + class ThreadListRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -5696,6 +6124,18 @@ class ReviewStartRequest(BaseModel): params: ReviewStartParams +class McpServerStatusListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["mcpServerStatus/list"], + Field(title="McpServerStatus/listRequestMethod"), + ] + params: ListMcpServerStatusParams + + class CommandExecRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -6074,6 +6514,20 @@ class Turn(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + completed_at: Annotated[ + int | None, + Field( + alias="completedAt", + description="Unix timestamp (in seconds) when the turn completed.", + ), + ] = None + duration_ms: Annotated[ + int | None, + Field( + alias="durationMs", + description="Duration between turn start and completion in milliseconds, if known.", + ), + ] = None error: Annotated[ TurnError | None, Field(description="Only populated when the Turn's status is failed."), @@ -6085,6 +6539,13 @@ class Turn(BaseModel): description="Only populated on a `thread/resume` or `thread/fork` response. For all other responses and notifications returning a Turn, the items field will be an empty list." ), ] + started_at: Annotated[ + int | None, + Field( + alias="startedAt", + description="Unix timestamp (in seconds) when the turn started.", + ), + ] = None status: TurnStatus @@ -6177,6 +6638,8 @@ class ClientRequest( | McpServerOauthLoginRequest | ConfigMcpServerReloadRequest | McpServerStatusListRequest + | McpServerResourceReadRequest + | McpServerToolCallRequest | WindowsSandboxSetupStartRequest | AccountLoginStartRequest | AccountLoginCancelRequest @@ -6242,6 +6705,8 @@ class ClientRequest( | McpServerOauthLoginRequest | ConfigMcpServerReloadRequest | McpServerStatusListRequest + | McpServerResourceReadRequest + | McpServerToolCallRequest | WindowsSandboxSetupStartRequest | AccountLoginStartRequest | AccountLoginCancelRequest @@ -6392,6 +6857,13 @@ class Thread(BaseModel): description="Whether the thread is ephemeral and should not be materialized on disk." ), ] + forked_from_id: Annotated[ + str | None, + Field( + alias="forkedFromId", + description="Source thread id when this thread was created by forking another thread.", + ), + ] = None git_info: Annotated[ GitInfo | None, Field( @@ -6456,6 +6928,13 @@ class ThreadForkResponse(BaseModel): ), ] cwd: str + instruction_sources: Annotated[ + list[str] | None, + Field( + alias="instructionSources", + description="Instruction source files currently loaded for this thread.", + ), + ] = [] model: str model_provider: Annotated[str, Field(alias="modelProvider")] reasoning_effort: Annotated[ @@ -6507,6 +6986,13 @@ class ThreadResumeResponse(BaseModel): ), ] cwd: str + instruction_sources: Annotated[ + list[str] | None, + Field( + alias="instructionSources", + description="Instruction source files currently loaded for this thread.", + ), + ] = [] model: str model_provider: Annotated[str, Field(alias="modelProvider")] reasoning_effort: Annotated[ @@ -6542,6 +7028,13 @@ class ThreadStartResponse(BaseModel): ), ] cwd: str + instruction_sources: Annotated[ + list[str] | None, + Field( + alias="instructionSources", + description="Instruction source files currently loaded for this thread.", + ), + ] = [] model: str model_provider: Annotated[str, Field(alias="modelProvider")] reasoning_effort: Annotated[ @@ -6624,6 +7117,7 @@ class ServerNotification( | ThreadRealtimeItemAddedServerNotification | ThreadRealtimeTranscriptUpdatedServerNotification | ThreadRealtimeOutputAudioDeltaServerNotification + | ThreadRealtimeSdpServerNotification | ThreadRealtimeErrorServerNotification | ThreadRealtimeClosedServerNotification | WindowsWorldWritableWarningServerNotification @@ -6681,6 +7175,7 @@ class ServerNotification( | ThreadRealtimeItemAddedServerNotification | ThreadRealtimeTranscriptUpdatedServerNotification | ThreadRealtimeOutputAudioDeltaServerNotification + | ThreadRealtimeSdpServerNotification | ThreadRealtimeErrorServerNotification | ThreadRealtimeClosedServerNotification | WindowsWorldWritableWarningServerNotification diff --git a/sdk/python/tests/test_public_api_signatures.py b/sdk/python/tests/test_public_api_signatures.py index ce1b847253..79ed9b4a9a 100644 --- a/sdk/python/tests/test_public_api_signatures.py +++ b/sdk/python/tests/test_public_api_signatures.py @@ -56,6 +56,7 @@ def test_generated_public_signatures_are_snake_case_and_typed() -> None: "sandbox", "service_name", "service_tier", + "session_start_source", ], Codex.thread_list: [ "archived", @@ -131,6 +132,7 @@ def test_generated_public_signatures_are_snake_case_and_typed() -> None: "sandbox", "service_name", "service_tier", + "session_start_source", ], AsyncCodex.thread_list: [ "archived",