Implementation:Openai Openai agents python Codex Same Thread Pattern
| Knowledge Sources | |
|---|---|
| Domains | Tool_Integration, Agent_Context, Codex |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Demonstrates reusing the same Codex CLI thread across multiple Runner.run() calls by enabling use_run_context_thread_id=True on the codex tool, persisting thread identity through a shared context object.
Description
This implementation shows how to configure a Codex tool so that successive agent runs share the same underlying Codex thread. The key mechanism is the use_run_context_thread_id flag on the codex_tool() factory function. When set to True, the SDK automatically stores and retrieves the Codex thread ID on the run context object, keyed by a name derived from the tool name (e.g., codex_thread_id_engineer for a tool named codex_engineer).
The context object can be either a Pydantic BaseModel with an appropriately named field or a plain dict. After the first Runner.run() call, the thread ID is written into the context. On subsequent runs with the same context, the SDK detects the existing thread ID and reuses it, allowing the Codex backend to maintain conversational continuity.
The example also demonstrates streaming event callbacks via on_stream, which receives CodexToolStreamEvent payloads including ThreadStartedEvent, TurnStartedEvent, TurnCompletedEvent, TurnFailedEvent, and ThreadErrorEvent. The ThreadOptions dataclass controls Codex-specific parameters such as model, reasoning effort, network access, and approval policy.
Usage
Use this pattern when you need to build multi-turn interactions with Codex where later prompts should have access to the full conversation history and workspace state from earlier turns. This is particularly useful for iterative coding workflows where a follow-up request (e.g., "now rewrite that in TypeScript") depends on knowing what was produced in a prior turn.
Code Reference
Source Location
- Repository: Openai_Openai_agents_python
- File: examples/tools/codex_same_thread.py
- Lines: 1-125
Signature
codex_tool(
name="codex_engineer",
sandbox_mode="workspace-write",
default_thread_options=ThreadOptions(
model="gpt-5.2-codex",
model_reasoning_effort="low",
network_access_enabled=True,
web_search_enabled=False,
approval_policy="never",
),
on_stream=on_codex_stream,
use_run_context_thread_id=True,
)
Import
from agents import Agent, ModelSettings, Runner, gen_trace_id, trace
from agents.extensions.experimental.codex import (
CodexToolStreamEvent,
ThreadErrorEvent,
ThreadOptions,
ThreadStartedEvent,
TurnCompletedEvent,
TurnFailedEvent,
TurnStartedEvent,
codex_tool,
)
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| name | str |
Yes | Unique name for the Codex tool instance; used to derive the thread ID key on the context. |
| sandbox_mode | str |
No | Codex sandbox mode, e.g., "workspace-write".
|
| default_thread_options | ThreadOptions |
No | Configuration for the Codex thread including model, reasoning effort, network access, web search, and approval policy. |
| on_stream | Callable[[CodexToolStreamEvent], Awaitable[None]] |
No | Async callback invoked for each streaming event from the Codex thread. |
| use_run_context_thread_id | bool |
No | When True, persists and reuses the thread ID via the run context object.
|
| context | dict | Yes (at Runner.run) | Passed to Runner.run(); stores the thread ID between runs.
|
Outputs
| Name | Type | Description |
|---|---|---|
| result.final_output | str |
The final text output from the agent after the Codex tool completes its turn. |
| context[thread_id_key] | None | The Codex thread ID, persisted on the context object after each run. |
Usage Examples
import asyncio
from pydantic import BaseModel
from agents import Agent, ModelSettings, Runner, gen_trace_id, trace
from agents.extensions.experimental.codex import codex_tool, ThreadOptions
class MyContext(BaseModel):
codex_thread_id_engineer: str | None = None
agent = Agent(
name="Codex Agent (same thread)",
instructions="Always use the Codex tool to answer the user's question.",
tools=[
codex_tool(
name="codex_engineer",
sandbox_mode="workspace-write",
default_thread_options=ThreadOptions(
model="gpt-5.2-codex",
model_reasoning_effort="low",
),
use_run_context_thread_id=True,
)
],
model_settings=ModelSettings(tool_choice="required"),
)
async def main():
context = MyContext()
with trace("Codex same thread example"):
# Turn 1
first_result = await Runner.run(agent, "Write a Python web server.", context=context)
print(first_result.final_output)
# Turn 2 reuses the same Codex thread
second_result = await Runner.run(agent, "Now add logging to it.", context=context)
print(second_result.final_output)
asyncio.run(main())
Using a Dict Context Instead of Pydantic
context: dict[str, str] = {}
first_result = await Runner.run(agent, "Write a hello world script.", context=context)
# context now contains {"codex_thread_id_engineer": "<thread-id>"}
second_result = await Runner.run(agent, "Add error handling.", context=context)
# Same thread is reused automatically