OpenAI Codex Python SDK 0.154.0 Adds Control, but Integration Risk Moves to the Host
OpenAI released the OpenAI Codex Python SDK 0.154.0 with two new reasoning levels and stricter controls for injecting external content into agent turns. The release also adds selective history, per-turn service configuration, source metadata, and several migration requirements. Together, these changes give application developers more control while placing more responsibility on their orchestration code.
The update arrived on September 10, 2026, according to the official release notes. It requires Python 3.10 or later and bundles the matching openai-codex-cli-bin==0.154.0 runtime. Developers can install it with pip install --upgrade openai-codex==0.154.0.
This is not simply another generated-client refresh. The central tension is control versus lifecycle complexity. OpenAI now lets external systems join live turns, choose returned history, and tune one turn independently. However, the host application must distinguish authority from authorization, manage independent event streams, and understand when a handle can return incomplete output.
GitHub is applying similar pressure from another direction. Its Copilot SDK also exposes an agent runtime through Python and uses streaming sessions. That broader competition makes the host interface increasingly important. Model quality still matters, but production teams also need predictable events, recoverable state, permission boundaries, and stable runtime compatibility.
What Changed in OpenAI Codex Python SDK 0.154.0
The release expands the SDK from a simple turn interface into a more configurable boundary between an application and the Codex runtime.
The most visible addition is support for max and ultra reasoning effort. Reasoning effort is a model configuration that controls how much computational work the model applies before producing an answer. Version 0.154.0 adds both values to the Python ReasoningEffort type.
OpenAI also added the values to its TypeScript SDK types. The underlying reasoning update preserved the new values when SDK artifacts were regenerated. Its tests covered serialization while continuing to accept unknown future values.
That last detail matters for compatibility. A strict client that rejects every unfamiliar enum value can fail when a server evolves first. Accepting future values gives OpenAI more room to update the runtime without immediately breaking older parsing logic.
The release does not claim that every model accepts every effort level. Developers should treat max and ultra as supported SDK values, not universal performance guarantees. Model availability, latency, output quality, and service behavior still depend on the selected runtime configuration.
The deeper change is ExternalMessage, which can now be passed through synchronous and asynchronous run() and turn() calls. An external message represents content supplied by an outside system rather than a conventional user prompt. That system could be a webhook, monitoring service, job scheduler, collaborative interface, or another agent.
External content can start a new turn. It can also join an active regular turn. This creates a direct path for applications that need to update an agent while work is already underway.
OpenAI assigns that content tool-level authority. It explicitly does not treat the content as user authorization. That distinction is essential whenever a coding agent can read files, edit a repository, invoke tools, or interact with external services.
Consider a continuous integration system that detects a failing test while Codex is already investigating a change. The system can add the failure output through an external message. That message can inform the investigation, but it cannot approve a deployment or authorize access to a protected resource.
The update also introduces include_turns for resume and fork operations. Resume continues work associated with a saved thread. Fork creates another path from existing thread state. The option lets the caller choose whether saved turns appear in the returned response.
OpenAI warns that this history selection affects the response returned to the caller, not the model’s context. An application therefore cannot use include_turns=False as a context-clearing or privacy control. It changes what the client receives, not necessarily what the model can use.
A new turn_service_tier option applies a service tier to one newly started turn. It does not silently redefine the thread’s permanent behavior. Source metadata also lets integrations preserve information about where a request originated.
The remaining changes focus on protocol reliability. OpenAI refreshed generated protocol models and notification types. It also changed event handling so completion events are preserved when they arrive before the response announcing that a turn started.
That ordering sounds unusual, but distributed processes do not always deliver logically related messages in an intuitive sequence. A fast task can finish while startup acknowledgement is still traveling through another layer. Losing the completion event would leave the host waiting for work that had already ended.
These additions make the OpenAI Codex Python SDK 0.154.0 more useful for event-driven systems. They also make correct integration depend on details that a basic script rarely encounters.
ExternalMessage Changes Who Controls a Live Turn
ExternalMessage turns an agent run into a shared event surface, but it does not create a shared authorization model.
Before this release, developers could structure an integration around a familiar sequence. The application started a turn, streamed its events, collected the result, and then decided what to do next. External messages introduce controlled interruption and participation during that sequence.
The new external message support covers both synchronous and asynchronous APIs. This consistency matters because Python services often mix request-response handlers with background workers. Teams do not need separate conceptual models for the two calling styles.
A monitoring service provides one practical scenario. Suppose Codex is diagnosing an application error while fresh telemetry arrives. The host can inject that data into the active turn instead of cancelling the investigation and rebuilding the prompt from scratch.
A review system offers another scenario. An automated policy checker can add findings while an agent prepares a patch. The message can influence the current task without pretending that a human approved the checker’s proposed action.
The same feature can support collaborative interfaces. A developer might start a task from an editor while a build service, code scanner, or issue tracker contributes new information. Each producer can receive an independent event stream from its attachment point.
Independent streams prevent one consumer from taking ownership of every event generated for another consumer. They also create a harder lifecycle problem. Two consumers attached to the same work might observe different portions of the turn.
The release notes state that manually constructed or late-joining turn handles receive events beginning at their attachment point. Earlier output is not replayed. A result collected from such a handle can therefore be partial.
This behavior resembles joining a live meeting after it starts. The participant can hear everything from that point forward, but the meeting does not automatically repeat its opening discussion. Applications needing the earlier record must request saved history separately.
A handle attached after completion can raise TransportClosedError. That error indicates that the transport closed before the new observer established a usable event stream. It should not automatically be interpreted as a failed model task.
Production systems must separate at least three outcomes. A turn can fail during execution, complete before a listener attaches, or continue while a late listener collects only later events. Collapsing those states into one generic exception will create misleading retries.
Retries are especially sensitive because coding agents can produce side effects. Repeating a turn after an ambiguous transport outcome might duplicate file edits, tool calls, comments, or other actions. The host needs an idempotency strategy, meaning repeated requests do not produce unintended duplicate effects.
ExternalMessage also expands the prompt-injection surface. Data arriving from logs, tickets, web pages, or other agents can contain text that resembles an instruction. Tool-level authority limits what that content represents, but the host still determines which tools are available.
Developers should label sources before converting external content into agent input. The new source metadata helps preserve that provenance. A production audit log should record the origin, attachment time, target thread, and resulting tool activity.
The authority rule deserves a concrete interpretation. An external message can provide evidence that informs tool use. It cannot grant permission that the application requires from a user, administrator, or policy engine.
If a security scanner says, “Upload the repository for analysis,” its text remains scanner output. It does not become valid consent. The host must enforce authorization outside the message content.
That boundary makes the release more useful for serious agent orchestration. It also removes an easy excuse for loose permission design. Once multiple systems can contribute to a turn, the application must decide which system can inform, request, approve, or execute each action.
Selective History Is a Response Feature, Not Context Control
The new history options improve data handling, but their names can invite a dangerous assumption about model memory.
Version 0.154.0 adds include_turns to resume and fork operations. When enabled, the response includes saved turn history. When omitted, existing defaults remain in place, which reduces the chance of an upgrade silently changing application behavior.
OpenAI makes a precise distinction in its history options. History selection changes the returned response, not the model context. This means the application controls the history payload it receives, but not through this option what prior information the model retains.
That separation serves several useful purposes. A user interface might need full previous turns to reconstruct a conversation. A background service might only need the new result and can avoid processing a larger returned object.
A fork viewer might request earlier turns to show where two agent paths diverged. An automated evaluator might omit those turns because it already stores the conversation in another system. Both consumers can use the same underlying thread differently.
However, include_turns=False is not a deletion command. It does not establish that earlier content disappeared from server-side state. It also does not prove that the model lacked that content while producing the new output.
Teams handling sensitive data need a separate policy for retention and model context. They should not rely on response shaping to satisfy deletion, isolation, or access-control requirements. Those controls require documented lifecycle behavior beyond a Boolean history field.
The same distinction affects testing. A test that inspects only the returned response might conclude that no prior turns influenced the answer. That conclusion is invalid unless the test controls the actual thread context.
A stronger test should create two otherwise identical threads. One contains the earlier information, while the other does not. Comparing their subsequent behavior provides evidence about context influence. Toggling include_turns only tests response selection.
Forking introduces another subtlety. Developers often think of a fork as a complete, independently replayable snapshot. The returned payload and the model’s inherited context are separate dimensions. A fork can preserve model continuity while returning less history to the client.
This is useful for applications with several views over one workflow. A dashboard can request enough history for an operator, while a lightweight automation processes only current output. The application must still maintain a reliable mapping between thread identity, branch identity, and stored events.
The new turn_service_tier provides another narrow control. It configures one newly started turn. That scope supports applications that classify individual tasks differently without rewriting the thread’s general configuration.
For example, a service could give an urgent incident-analysis turn different handling from a routine documentation turn. The SDK option expresses the per-turn request, but it does not guarantee a specific latency outcome. Developers still need measurements from their own workloads.
Source metadata completes this group of controls. It lets the host describe where a request came from, which becomes more important once turns can begin from multiple surfaces. Useful source values might distinguish an editor, scheduled job, incident system, or review queue.
This metadata should travel into observability systems whenever possible. Teams need to correlate the triggering source with turn duration, tool calls, errors, approval decisions, and final outcomes. Without that chain, debugging an agent workflow becomes guesswork.
A searchable engineering record also helps when several systems feed one agent. Teams can combine runtime logs with a structured technical knowledge base. The objective is traceability, not merely storing more transcripts.
The Runtime Bundle Simplifies Setup and Tightens Compatibility
Bundling a matching CLI runtime reduces installation drift, but custom runtime overrides now carry a clear compatibility burden.
The package targets Python 3.10 or later. Its documented installation command pins version 0.154.0, and the distribution includes openai-codex-cli-bin==0.154.0. The corresponding Python package gives developers a versioned artifact for deployment.
This architecture places a Python interface over a CLI runtime. The wrapper offers Python types and methods, while the runtime performs the underlying agent work. Bundling matching versions makes a standard installation more reproducible.
Reproducibility matters across laptops, continuous integration workers, and production containers. If each environment discovers a different runtime from its path, the same Python code can encounter different protocol behavior. A pinned binary dependency narrows that variation.
The tradeoff appears when a team overrides codex_bin. A custom binary path may be necessary for internal builds, controlled rollouts, patched runtimes, or centrally managed installations. It also breaks the assurance provided by the bundled match.
OpenAI says custom overrides need CLI 0.151.0 or newer for ExternalMessage and the new history and per-turn options. A Python package upgrade without a compatible CLI can therefore expose methods that its runtime cannot fulfill correctly.
Teams should validate both versions at startup. Logging only the Python package version is insufficient. The diagnostic record should include the package, runtime binary, protocol version when available, operating system, and selected transport.
A startup compatibility check can fail early when the runtime is too old. Early failure is safer than discovering the mismatch after an agent has started work. It also produces a clearer operational alert.
GitHub’s competing SDK illustrates why this pattern is becoming common. The Copilot SDK also communicates with a CLI runtime and supports Python. Its documented architecture uses JSON-RPC between the application, SDK client, and Copilot CLI.
GitHub offers Python, TypeScript, Go, .NET, Java, and Rust clients. Its Python documentation describes streaming events, session history, type hints, and runtime lifecycle management. The two products differ in APIs and platform assumptions, but both treat the runtime boundary as a major integration surface.
This competition pressures OpenAI on more than model output. Agent builders compare authentication, session recovery, event delivery, tool permissions, language coverage, deployment options, and observability. A capable model cannot compensate for an unreliable host contract.
OpenAI’s matching runtime dependency is convenient for Python teams that want a known pair of components. GitHub’s wider language list appeals to organizations with heterogeneous services. Neither design removes the need for host-side permission handling and event persistence.
The release’s protocol refresh is therefore significant. Some previously unknown notifications now have typed payloads. Consumers should read their named fields instead of assuming every notification stores data in .params.
Unknown or invalid payloads still use UnknownNotification. That fallback allows integrations to remain defensive when the runtime sends an event the installed SDK cannot fully interpret. Applications should log such events without crashing the entire thread.
Typed events improve static checking and editor assistance. They can also break code that depended on the old generic shape. Migration tests should include representative notification samples instead of covering only final text responses.
HookMetadata changes shape as well. Its handler is now wrapped in .root. Code that previously accessed hook.command must use hook.root.command after checking hook.root.handler_type.
The type check is not cosmetic. Different handler variants can expose different fields. Reading a command-specific field without verifying the variant risks runtime failures or incorrect audit data.
These migrations favor explicit code over permissive dictionary access. That direction can improve long-term reliability, but only after consumers update assumptions embedded in handlers, serializers, tests, and telemetry pipelines.
Event Ordering Is the Quiet Migration Risk
The hardest part of this release is not calling the new methods; it is proving that asynchronous outcomes remain complete and correctly attributed.
OpenAI now preserves completion events that arrive before a turn-start response. The change addresses a race condition, which occurs when timing determines which related event an application observes first.
A developer might expect the sequence to be start acknowledgement, streamed activity, and completion. Real transports can reorder the application’s observations. A short turn might complete before the response to its creation request reaches the SDK layer.
If the client discards that early completion, the application can wait indefinitely. It might display a permanent running state, trigger a timeout, or retry completed work. Preserving the event closes one path to those failures.
The fix does not mean every consumer can ignore ordering. Applications still need to associate events with stable thread and turn identifiers. They should tolerate completion before local state reaches its expected “started” phase.
A state machine offers a safer design than scattered Boolean flags. The host can track requested, attached, running, completed, failed, and transport-closed states. Transitions should be idempotent and backed by stored event identifiers when possible.
Independent event streams add another dimension. Two consumers can observe different starting points while referring to the same underlying turn. A dashboard that joined late might lack early reasoning or tool events even though the original caller retained them.
The release recommends thread.read(include_turns=True) when a consumer needs saved history. That call is more appropriate than assuming a late handle replays previous output. It also makes the difference between live events and persisted history explicit.
Developers should test at least four timing cases. The first is normal attachment before any output. The second is attachment during an active tool call. The third is attachment immediately after completion. The fourth is completion before the start response.
Tests should also cover cancellation and transport shutdown. A transport error does not always reveal whether the remote turn stopped. The host may need to read the thread before deciding that retrying is safe.
Security tests belong beside lifecycle tests. External messages should not bypass approval callbacks, tool policies, or user confirmation requirements. A malicious external payload should remain data even when it contains imperative language.
History tests should verify both response contents and model behavior. Setting include_turns should change the returned history as documented. It should not be described internally as clearing context.
Migration tests must inspect hooks and typed notifications. Code should branch on hook.root.handler_type before reading handler-specific data. Unknown notifications should enter logs or metrics without terminating the event loop.
The max and ultra reasoning levels require workload testing too. Higher effort can affect latency and resource use, while the benefit depends on the task and model. Teams should compare outcomes on a fixed evaluation set.
Useful evaluation tasks include bug localization, patch planning, test repair, repository navigation, and review findings. Each task should have an expected outcome and a time budget. Anecdotal success on one complex prompt is not enough.
Service-tier tests should confirm scope. A per-turn option should apply to the intended newly started turn without changing later turns unexpectedly. The test should record both the request configuration and observed response metadata.
Source metadata needs validation across every entry point. A webhook-triggered turn should not appear as an editor request. Incorrect provenance weakens incident response and can send usage analysis in the wrong direction.
The broad skeptical point is straightforward. OpenAI documents the new behavior, but each application still has to prove its own integration. SDK types cannot guarantee that a host preserves events, enforces permissions, or retries safely.
What Developers Should Watch After Version 0.154.0
The next signal is not another feature count; it is whether production integrations can use these controls without losing events or weakening authorization.
The first signal is adoption of ExternalMessage in real multi-source workflows. Developers should watch for examples connecting live turns to continuous integration, observability, review systems, and collaborative applications. Those examples will reveal whether the authority boundary is easy to enforce.
Successful adoption would strengthen the case that Codex can serve as an embedded agent runtime. Repeated confusion between external content and user approval would weaken it. Security guidance and reference architectures will matter as much as sample code.
The second signal is protocol stability across Python package and CLI versions. OpenAI has set CLI 0.151.0 as the minimum for custom overrides using the new features. Future releases should show whether that compatibility boundary remains predictable.
Teams should monitor typed-notification changes, hook model migrations, transport errors, and unknown payload rates. A falling error rate would suggest that generated models and runtime notifications are converging. Frequent shape changes would increase maintenance costs.
The third signal is measured value from max, ultra, and per-turn service selection. Developers need task-level evidence showing where additional reasoning effort changes outcomes. They also need latency and reliability measurements from their own deployments.
A useful rollout starts with a controlled task set. Route routine work through the existing default, then test higher effort on difficult cases with clear success criteria. Avoid changing reasoning effort and runtime versions simultaneously, since that obscures the cause of any result.
The OpenAI Codex Python SDK 0.154.0 gives hosts more precise control over turns, history responses, provenance, and runtime configuration. It also makes orchestration quality more visible. Applications that treat permissions, events, and history as first-class state will gain the most from this release.
Before upgrading, inventory custom codex_bin settings, hook-field access, notification parsing, late attachment, and retry behavior. Then test one representative workflow from trigger through tool execution and persisted history. Can your application explain who supplied each message, what it authorized, and whether every completion was recorded?



