[codex] Add independent beta release for the Python SDK (#24828)

## Why

`openai-codex` needs a beta release lifecycle without requiring beta
releases of its pinned runtime package. Previously, SDK staging rewrote
its runtime dependency to the SDK version, which made an SDK-only beta
impossible.

## What changed

- Set the initial SDK beta version to `0.1.0b1` and pin it to published
stable `openai-codex-cli-bin==0.132.0`.
- Decoupled SDK release staging from runtime versioning so it preserves
the reviewed exact runtime pin.
- Added a `python-v*` tag workflow that builds and publishes only
`openai-codex` through PyPI trusted publishing.
- Removed the Beta classifier from runtime package metadata for future
runtime publications.
- Regenerated protocol-derived SDK models from the selected stable
runtime package.

`0.132.0` is the newest stable runtime admitted by the checked-in
dependency date fence and retains the Linux wheel family currently used
by SDK CI.

## Release setup

Before pushing `python-v0.1.0b1`, configure PyPI trusted publishing for
the `openai-codex` project with workflow `python-sdk-release.yml`,
environment `pypi`, and job `publish-python-sdk`.

## Validation

- `uv run --frozen --extra dev ruff check src/openai_codex scripts
examples tests`
- Parsed `.github/workflows/python-sdk-release.yml` with PyYAML.
- Built staged release artifacts locally:
`openai_codex-0.1.0b1-py3-none-any.whl` and
`openai_codex-0.1.0b1.tar.gz`.
- Verified wheel metadata pins `openai-codex-cli-bin==0.132.0`.
- Tests are deferred to online CI for this PR.
This commit is contained in:
Ahmed Ibrahim
2026-05-27 17:57:51 -07:00
committed by GitHub
Unverified
parent 304d15cab0
commit 4d0c4cd058
9 changed files with 335 additions and 292 deletions
+149 -157
View File
@@ -49,26 +49,24 @@ class AccountLoginCompletedNotification(BaseModel):
success: bool
class AdditionalWritableRootActivePermissionProfileModification(BaseModel):
class ActivePermissionProfile(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
path: AbsolutePathBuf
type: Annotated[
Literal["additionalWritableRoot"],
Field(title="AdditionalWritableRootActivePermissionProfileModificationType"),
extends: Annotated[
str | None,
Field(
description="Parent profile identifier once permissions profiles support inheritance. This is currently always `null`."
),
] = None
id: Annotated[
str,
Field(
description="Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile."
),
]
class ActivePermissionProfileModification(
RootModel[AdditionalWritableRootActivePermissionProfileModification]
):
model_config = ConfigDict(
populate_by_name=True,
)
root: AdditionalWritableRootActivePermissionProfileModification
class AddCreditsNudgeCreditType(Enum):
credits = "credits"
usage_limit = "usage_limit"
@@ -597,6 +595,12 @@ class UserConfigLayerSource(BaseModel):
description="This is the path to the user's config.toml file, though it is not guaranteed to exist."
),
]
profile: Annotated[
str | None,
Field(
description="Name of the selected profile-v2 config layered on top of the base user config, when this layer represents one."
),
] = None
type: Annotated[Literal["user"], Field(title="UserConfigLayerSourceType")]
@@ -680,6 +684,7 @@ class CommandConfiguredHookHandler(BaseModel):
)
async_: Annotated[bool, Field(alias="async")]
command: str
command_windows: Annotated[str | None, Field(alias="commandWindows")] = None
status_message: Annotated[str | None, Field(alias="statusMessage")] = None
timeout_sec: Annotated[int | None, Field(alias="timeoutSec", ge=0)] = None
type: Annotated[Literal["command"], Field(title="CommandConfiguredHookHandlerType")]
@@ -842,6 +847,13 @@ class ExperimentalFeatureListParams(BaseModel):
int | None,
Field(description="Optional page size; defaults to a reasonable server-side value.", ge=0),
] = None
thread_id: Annotated[
str | None,
Field(
alias="threadId",
description="Optional loaded thread id. Pass this when showing feature state for an existing thread so enablement is computed from that thread's refreshed config, including project-local config for the thread's cwd.",
),
] = None
class ExperimentalFeatureStage(Enum):
@@ -1014,6 +1026,18 @@ class FileSystemSpecialPath(
)
class ForcedChatgptWorkspaceIds(RootModel[str | list[str]]):
model_config = ConfigDict(
populate_by_name=True,
)
root: Annotated[
str | list[str],
Field(
description="Backward-compatible API shape for ChatGPT workspace login restrictions."
),
]
class ForcedLoginMethod(Enum):
chatgpt = "chatgpt"
api = "api"
@@ -1482,8 +1506,6 @@ class HooksListParams(BaseModel):
class ImageDetail(Enum):
auto = "auto"
low = "low"
high = "high"
original = "original"
@@ -2166,66 +2188,6 @@ class PatchChangeKind(
root: AddPatchChangeKind | DeletePatchChangeKind | UpdatePatchChangeKind
class DisabledPermissionProfile(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
type: Annotated[Literal["disabled"], Field(title="DisabledPermissionProfileType")]
class UnrestrictedPermissionProfileFileSystemPermissions(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
type: Annotated[
Literal["unrestricted"],
Field(title="UnrestrictedPermissionProfileFileSystemPermissionsType"),
]
class AdditionalWritableRootPermissionProfileModificationParams(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
path: AbsolutePathBuf
type: Annotated[
Literal["additionalWritableRoot"],
Field(title="AdditionalWritableRootPermissionProfileModificationParamsType"),
]
class PermissionProfileModificationParams(
RootModel[AdditionalWritableRootPermissionProfileModificationParams]
):
model_config = ConfigDict(
populate_by_name=True,
)
root: AdditionalWritableRootPermissionProfileModificationParams
class PermissionProfileNetworkPermissions(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
enabled: bool
class ProfilePermissionProfileSelectionParams(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
id: str
modifications: list[PermissionProfileModificationParams] | None = None
type: Annotated[Literal["profile"], Field(title="ProfilePermissionProfileSelectionParamsType")]
class PermissionProfileSelectionParams(RootModel[ProfilePermissionProfileSelectionParams]):
model_config = ConfigDict(
populate_by_name=True,
)
root: ProfilePermissionProfileSelectionParams
class Personality(Enum):
none = "none"
friendly = "friendly"
@@ -2298,6 +2260,23 @@ class PluginInstallResponse(BaseModel):
auth_policy: Annotated[PluginAuthPolicy, Field(alias="authPolicy")]
class PluginInstalledParams(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
cwds: Annotated[
list[AbsolutePathBuf] | None,
Field(description="Optional working directories used to discover repo marketplaces."),
] = None
install_suggestion_plugin_names: Annotated[
list[str] | None,
Field(
alias="installSuggestionPluginNames",
description="Additional uninstalled plugin names that should be returned when present locally. This is used by mention surfaces that intentionally expose install entrypoints.",
),
] = None
class PluginInterface(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -2385,6 +2364,26 @@ class PluginReadParams(BaseModel):
remote_marketplace_name: Annotated[str | None, Field(alias="remoteMarketplaceName")] = None
class PluginShareCheckoutParams(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
remote_plugin_id: Annotated[str, Field(alias="remotePluginId")]
class PluginShareCheckoutResponse(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
marketplace_name: Annotated[str, Field(alias="marketplaceName")]
marketplace_path: Annotated[AbsolutePathBuf, Field(alias="marketplacePath")]
plugin_id: Annotated[str, Field(alias="pluginId")]
plugin_name: Annotated[str, Field(alias="pluginName")]
plugin_path: Annotated[AbsolutePathBuf, Field(alias="pluginPath")]
remote_plugin_id: Annotated[str, Field(alias="remotePluginId")]
remote_version: Annotated[str | None, Field(alias="remoteVersion")] = None
class PluginShareDeleteParams(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -2745,6 +2744,7 @@ class RemoteControlStatusChangedNotification(BaseModel):
)
environment_id: Annotated[str | None, Field(alias="environmentId")] = None
installation_id: Annotated[str, Field(alias="installationId")]
server_name: Annotated[str, Field(alias="serverName")]
status: RemoteControlConnectionStatus
@@ -2911,6 +2911,13 @@ class CompactionResponseItem(BaseModel):
type: Annotated[Literal["compaction"], Field(title="CompactionResponseItemType")]
class CompactionTriggerResponseItem(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
type: Annotated[Literal["compaction_trigger"], Field(title="CompactionTriggerResponseItemType")]
class ContextCompactionResponseItem(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -3627,6 +3634,8 @@ class ThreadGoalClearedNotification(BaseModel):
class ThreadGoalStatus(Enum):
active = "active"
paused = "paused"
blocked = "blocked"
usage_limited = "usageLimited"
budget_limited = "budgetLimited"
complete = "complete"
@@ -4308,6 +4317,7 @@ class ImageUserInput(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
detail: ImageDetail | None = None
type: Annotated[Literal["image"], Field(title="ImageUserInputType")]
url: str
@@ -4316,6 +4326,7 @@ class LocalImageUserInput(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
detail: ImageDetail | None = None
path: str
type: Annotated[Literal["localImage"], Field(title="LocalImageUserInputType")]
@@ -4525,30 +4536,6 @@ class AccountUpdatedNotification(BaseModel):
plan_type: Annotated[PlanType | None, Field(alias="planType")] = None
class ActivePermissionProfile(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
extends: Annotated[
str | None,
Field(
description="Parent profile identifier once permissions profiles support inheritance. This is currently always `null`."
),
] = None
id: Annotated[
str,
Field(
description="Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile."
),
]
modifications: Annotated[
list[ActivePermissionProfileModification] | None,
Field(
description="Bounded user-requested modifications applied on top of the named profile, if any."
),
] = []
class AppConfig(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -4790,6 +4777,15 @@ class PluginListRequest(BaseModel):
params: PluginListParams
class PluginInstalledRequest(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
id: RequestId
method: Annotated[Literal["plugin/installed"], Field(title="Plugin/installedRequestMethod")]
params: PluginInstalledParams
class PluginReadRequest(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -4817,6 +4813,17 @@ class PluginShareListRequest(BaseModel):
params: PluginShareListParams
class PluginShareCheckoutRequest(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
id: RequestId
method: Annotated[
Literal["plugin/share/checkout"], Field(title="Plugin/share/checkoutRequestMethod")
]
params: PluginShareCheckoutParams
class PluginShareDeleteRequest(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -5424,6 +5431,7 @@ class ConfigRequirements(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
allow_managed_hooks_only: Annotated[bool | None, Field(alias="allowManagedHooksOnly")] = None
allowed_approval_policies: Annotated[
list[AskForApproval] | None, Field(alias="allowedApprovalPolicies")
] = None
@@ -5851,40 +5859,6 @@ class OverriddenMetadata(BaseModel):
overriding_layer: Annotated[ConfigLayerMetadata, Field(alias="overridingLayer")]
class ExternalPermissionProfile(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
network: PermissionProfileNetworkPermissions
type: Annotated[Literal["external"], Field(title="ExternalPermissionProfileType")]
class RestrictedPermissionProfileFileSystemPermissions(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
entries: list[FileSystemSandboxEntry]
glob_scan_max_depth: Annotated[int | None, Field(alias="globScanMaxDepth", ge=1)] = None
type: Annotated[
Literal["restricted"], Field(title="RestrictedPermissionProfileFileSystemPermissionsType")
]
class PermissionProfileFileSystemPermissions(
RootModel[
RestrictedPermissionProfileFileSystemPermissions
| UnrestrictedPermissionProfileFileSystemPermissions
]
):
model_config = ConfigDict(
populate_by_name=True,
)
root: (
RestrictedPermissionProfileFileSystemPermissions
| UnrestrictedPermissionProfileFileSystemPermissions
)
class PluginSharePrincipal(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -6626,7 +6600,6 @@ class ToolsV2(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
view_image: bool | None = None
web_search: WebSearchToolConfig | None = None
@@ -7072,24 +7045,6 @@ class ListMcpServerStatusResponse(BaseModel):
] = None
class ManagedPermissionProfile(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
file_system: Annotated[PermissionProfileFileSystemPermissions, Field(alias="fileSystem")]
network: PermissionProfileNetworkPermissions
type: Annotated[Literal["managed"], Field(title="ManagedPermissionProfileType")]
class PermissionProfile(
RootModel[ManagedPermissionProfile | DisabledPermissionProfile | ExternalPermissionProfile]
):
model_config = ConfigDict(
populate_by_name=True,
)
root: ManagedPermissionProfile | DisabledPermissionProfile | ExternalPermissionProfile
class PluginShareContext(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -7098,6 +7053,13 @@ class PluginShareContext(BaseModel):
creator_name: Annotated[str | None, Field(alias="creatorName")] = None
discoverability: PluginShareDiscoverability | None = None
remote_plugin_id: Annotated[str, Field(alias="remotePluginId")]
remote_version: Annotated[
str | None,
Field(
alias="remoteVersion",
description="Version of the remote shared plugin release when available.",
),
] = None
share_principals: Annotated[
list[PluginSharePrincipal] | None, Field(alias="sharePrincipals")
] = None
@@ -7129,7 +7091,20 @@ class PluginSummary(BaseModel):
installed: bool
interface: PluginInterface | None = None
keywords: list[str] | None = []
local_version: Annotated[
str | None,
Field(
alias="localVersion",
description="Version of the locally materialized plugin package when available.",
),
] = None
name: str
remote_plugin_id: Annotated[
str | None,
Field(
alias="remotePluginId", description="Backend remote plugin identifier when available."
),
] = None
share_context: Annotated[
PluginShareContext | None,
Field(
@@ -7209,6 +7184,7 @@ class ResponseItem(
| WebSearchCallResponseItem
| ImageGenerationCallResponseItem
| CompactionResponseItem
| CompactionTriggerResponseItem
| ContextCompactionResponseItem
| OtherResponseItem
]
@@ -7229,6 +7205,7 @@ class ResponseItem(
| WebSearchCallResponseItem
| ImageGenerationCallResponseItem
| CompactionResponseItem
| CompactionTriggerResponseItem
| ContextCompactionResponseItem
| OtherResponseItem
)
@@ -7448,8 +7425,9 @@ class Config(BaseModel):
),
] = None
compact_prompt: str | None = None
desktop: dict[str, Any] | None = None
developer_instructions: str | None = None
forced_chatgpt_workspace_id: str | None = None
forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | None = None
forced_login_method: ForcedLoginMethod | None = None
instructions: str | None = None
model: str | None = None
@@ -7827,7 +7805,7 @@ class ThreadForkResponse(BaseModel):
sandbox: Annotated[
SandboxPolicy,
Field(
description="Legacy sandbox policy retained for compatibility. Experimental clients should prefer `permissionProfile` when they need exact runtime permissions."
description="Legacy sandbox policy retained for compatibility. Experimental clients should prefer `activePermissionProfile` for profile provenance."
),
]
service_tier: Annotated[str | None, Field(alias="serviceTier")] = None
@@ -7895,7 +7873,7 @@ class ThreadResumeResponse(BaseModel):
sandbox: Annotated[
SandboxPolicy,
Field(
description="Legacy sandbox policy retained for compatibility. Experimental clients should prefer `permissionProfile` when they need exact runtime permissions."
description="Legacy sandbox policy retained for compatibility. Experimental clients should prefer `activePermissionProfile` for profile provenance."
),
]
service_tier: Annotated[str | None, Field(alias="serviceTier")] = None
@@ -7940,7 +7918,7 @@ class ThreadStartResponse(BaseModel):
sandbox: Annotated[
SandboxPolicy,
Field(
description="Legacy sandbox policy retained for compatibility. Experimental clients should prefer `permissionProfile` when they need exact runtime permissions."
description="Legacy sandbox policy retained for compatibility. Experimental clients should prefer `activePermissionProfile` for profile provenance."
),
]
service_tier: Annotated[str | None, Field(alias="serviceTier")] = None
@@ -7998,11 +7976,13 @@ class ClientRequest(
| MarketplaceRemoveRequest
| MarketplaceUpgradeRequest
| PluginListRequest
| PluginInstalledRequest
| PluginReadRequest
| PluginSkillReadRequest
| PluginShareSaveRequest
| PluginShareUpdateTargetsRequest
| PluginShareListRequest
| PluginShareCheckoutRequest
| PluginShareDeleteRequest
| AppListRequest
| FsReadFileRequest
@@ -8079,11 +8059,13 @@ class ClientRequest(
| MarketplaceRemoveRequest
| MarketplaceUpgradeRequest
| PluginListRequest
| PluginInstalledRequest
| PluginReadRequest
| PluginSkillReadRequest
| PluginShareSaveRequest
| PluginShareUpdateTargetsRequest
| PluginShareListRequest
| PluginShareCheckoutRequest
| PluginShareDeleteRequest
| AppListRequest
| FsReadFileRequest
@@ -8135,6 +8117,16 @@ class ClientRequest(
]
class PluginInstalledResponse(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
marketplace_load_errors: Annotated[
list[MarketplaceLoadErrorInfo] | None, Field(alias="marketplaceLoadErrors")
] = []
marketplaces: list[PluginMarketplaceEntry]
class PluginListResponse(BaseModel):
model_config = ConfigDict(
populate_by_name=True,