codex-sdk-python 0.87.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codex_sdk_python-0.87.0/PKG-INFO +889 -0
- codex_sdk_python-0.87.0/README.md +853 -0
- codex_sdk_python-0.87.0/pyproject.toml +130 -0
- codex_sdk_python-0.87.0/src/codex_sdk/__init__.py +142 -0
- codex_sdk_python-0.87.0/src/codex_sdk/abort.py +40 -0
- codex_sdk_python-0.87.0/src/codex_sdk/app_server.py +956 -0
- codex_sdk_python-0.87.0/src/codex_sdk/codex.py +147 -0
- codex_sdk_python-0.87.0/src/codex_sdk/config_overrides.py +70 -0
- codex_sdk_python-0.87.0/src/codex_sdk/events.py +112 -0
- codex_sdk_python-0.87.0/src/codex_sdk/exceptions.py +55 -0
- codex_sdk_python-0.87.0/src/codex_sdk/exec.py +447 -0
- codex_sdk_python-0.87.0/src/codex_sdk/hooks.py +74 -0
- codex_sdk_python-0.87.0/src/codex_sdk/integrations/__init__.py +1 -0
- codex_sdk_python-0.87.0/src/codex_sdk/integrations/pydantic_ai.py +172 -0
- codex_sdk_python-0.87.0/src/codex_sdk/integrations/pydantic_ai_model.py +491 -0
- codex_sdk_python-0.87.0/src/codex_sdk/items.py +173 -0
- codex_sdk_python-0.87.0/src/codex_sdk/options.py +150 -0
- codex_sdk_python-0.87.0/src/codex_sdk/telemetry.py +36 -0
- codex_sdk_python-0.87.0/src/codex_sdk/thread.py +607 -0
|
@@ -0,0 +1,889 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: codex-sdk-python
|
|
3
|
+
Version: 0.87.0
|
|
4
|
+
Summary: Python SDK for the Codex CLI agent with async threads, streaming events, and structured outputs
|
|
5
|
+
Keywords: codex,sdk,python,api,cli,agent,async,streaming
|
|
6
|
+
Author: Vectorfy Co
|
|
7
|
+
Author-email: Vectorfy Co <git@vectorfy.co>
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
21
|
+
Requires-Dist: logfire ; extra == 'logfire'
|
|
22
|
+
Requires-Dist: pydantic>=2 ; extra == 'pydantic'
|
|
23
|
+
Requires-Dist: pydantic-ai ; python_full_version >= '3.10' and extra == 'pydantic-ai'
|
|
24
|
+
Maintainer: Vectorfy Co
|
|
25
|
+
Maintainer-email: Vectorfy Co <git@vectorfy.co>
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Project-URL: Homepage, https://vectorfy.co
|
|
28
|
+
Project-URL: Repository, https://github.com/vectorfy-co/codex-sdk-python
|
|
29
|
+
Project-URL: Documentation, https://github.com/vectorfy-co/codex-sdk-python#readme
|
|
30
|
+
Project-URL: Issues, https://github.com/vectorfy-co/codex-sdk-python/issues
|
|
31
|
+
Project-URL: Changelog, https://github.com/vectorfy-co/codex-sdk-python/blob/main/CHANGELOG_SDK.md
|
|
32
|
+
Provides-Extra: logfire
|
|
33
|
+
Provides-Extra: pydantic
|
|
34
|
+
Provides-Extra: pydantic-ai
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# 
|
|
38
|
+
|
|
39
|
+
Embed the Codex agent in Python workflows. This SDK wraps the bundled `codex` CLI, streams JSONL events over stdin/stdout, and exposes structured, typed results.
|
|
40
|
+
|
|
41
|
+
<div align="left">
|
|
42
|
+
<table>
|
|
43
|
+
<tr>
|
|
44
|
+
<td><strong>Lifecycle</strong></td>
|
|
45
|
+
<td>
|
|
46
|
+
<a href="#ci-cd"><img src="https://img.shields.io/badge/CI%2FCD-Active-16a34a?style=flat&logo=githubactions&logoColor=white" alt="CI/CD badge" /></a>
|
|
47
|
+
<img src="https://img.shields.io/badge/Release-0.86.0-6b7280?style=flat&logo=pypi&logoColor=white" alt="Release badge" />
|
|
48
|
+
<a href="#license"><img src="https://img.shields.io/badge/License-Apache--2.0-0f766e?style=flat&logo=apache&logoColor=white" alt="License badge" /></a>
|
|
49
|
+
</td>
|
|
50
|
+
</tr>
|
|
51
|
+
<tr>
|
|
52
|
+
<td><strong>Core Stack</strong></td>
|
|
53
|
+
<td>
|
|
54
|
+
<img src="https://img.shields.io/badge/Python-3.8%2B-3776AB?style=flat&logo=python&logoColor=white" alt="Python badge" />
|
|
55
|
+
<img src="https://img.shields.io/badge/Codex-CLI-111827?style=flat&logo=gnubash&logoColor=white" alt="Codex CLI badge" />
|
|
56
|
+
<img src="https://img.shields.io/badge/JSONL-Events-0ea5e9?style=flat&logo=json&logoColor=white" alt="JSONL badge" />
|
|
57
|
+
<img src="https://img.shields.io/badge/Pydantic-v2-0b3b2e?style=flat&logo=pydantic&logoColor=white" alt="Pydantic badge" />
|
|
58
|
+
<img src="https://img.shields.io/badge/PydanticAI-Integrations-0b3b2e?style=flat&logo=pydantic&logoColor=white" alt="PydanticAI badge" />
|
|
59
|
+
</td>
|
|
60
|
+
</tr>
|
|
61
|
+
<tr>
|
|
62
|
+
<td><strong>Navigation</strong></td>
|
|
63
|
+
<td>
|
|
64
|
+
<a href="#quick-start"><img src="https://img.shields.io/badge/Local%20Setup-Quick%20Start-059669?style=flat&logo=serverless&logoColor=white" alt="Quick start" /></a>
|
|
65
|
+
<a href="#features"><img src="https://img.shields.io/badge/Overview-Features-7c3aed?style=flat&logo=simpleicons&logoColor=white" alt="Features" /></a>
|
|
66
|
+
<a href="#configuration"><img src="https://img.shields.io/badge/Config-Options%20%26%20Env-0ea5e9?style=flat&logo=json&logoColor=white" alt="Config" /></a>
|
|
67
|
+
<a href="#pydantic-ai"><img src="https://img.shields.io/badge/Integrations-PydanticAI-0b3b2e?style=flat&logo=pydantic&logoColor=white" alt="PydanticAI" /></a>
|
|
68
|
+
<a href="#architecture"><img src="https://img.shields.io/badge/Design-Architecture-1f2937?style=flat&logo=serverless&logoColor=white" alt="Architecture" /></a>
|
|
69
|
+
<a href="#testing"><img src="https://img.shields.io/badge/Quality-Testing-2563eb?style=flat&logo=pytest&logoColor=white" alt="Testing" /></a>
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
72
|
+
</table>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
- Runtime dependency-free: uses only the Python standard library.
|
|
76
|
+
- Codex CLI binaries are downloaded separately; use `scripts/setup_binary.py` from the repo or install the Codex CLI and set `codex_path_override`.
|
|
77
|
+
- Async-first API with sync helpers, streaming events, and structured output.
|
|
78
|
+
- Python 3.8/3.9 support is deprecated and will be removed in a future release; use Python 3.10+.
|
|
79
|
+
|
|
80
|
+
<a id="quick-start"></a>
|
|
81
|
+
## 
|
|
82
|
+
|
|
83
|
+
1. Install the SDK:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv add codex-sdk-python
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
2. Ensure a `codex` binary is available (required for local runs):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# From the repo source (downloads vendor binaries)
|
|
93
|
+
python scripts/setup_binary.py
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If you installed from PyPI, install the Codex CLI separately and either add it to your PATH
|
|
97
|
+
or pass `CodexOptions.codex_path_override`.
|
|
98
|
+
|
|
99
|
+
3. Authenticate with Codex:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
codex login
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Or export an API key:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
export CODEX_API_KEY="<your-api-key>"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
4. Run a first turn:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import asyncio
|
|
115
|
+
from codex_sdk import Codex
|
|
116
|
+
|
|
117
|
+
async def main() -> None:
|
|
118
|
+
codex = Codex()
|
|
119
|
+
thread = codex.start_thread()
|
|
120
|
+
turn = await thread.run("Diagnose the test failure and propose a fix")
|
|
121
|
+
print(turn.final_response)
|
|
122
|
+
print(turn.items)
|
|
123
|
+
|
|
124
|
+
if __name__ == "__main__":
|
|
125
|
+
asyncio.run(main())
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
For single-turn sessions with approval handling, use the turn session wrapper:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import asyncio
|
|
132
|
+
from codex_sdk import AppServerClient, AppServerOptions, ApprovalDecisions
|
|
133
|
+
|
|
134
|
+
async def main() -> None:
|
|
135
|
+
async with AppServerClient(AppServerOptions()) as app:
|
|
136
|
+
thread = await app.thread_start(model="gpt-5-codex-high", cwd=".")
|
|
137
|
+
thread_id = thread["thread"]["id"]
|
|
138
|
+
session = await app.turn_session(
|
|
139
|
+
thread_id,
|
|
140
|
+
"Run tests and summarize failures.",
|
|
141
|
+
approvals=ApprovalDecisions(command_execution="accept"),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
async for notification in session.notifications():
|
|
145
|
+
print(notification.method)
|
|
146
|
+
|
|
147
|
+
final_turn = await session.wait()
|
|
148
|
+
print(final_turn)
|
|
149
|
+
|
|
150
|
+
if __name__ == "__main__":
|
|
151
|
+
asyncio.run(main())
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Examples
|
|
155
|
+
|
|
156
|
+
Try the examples under `examples/`:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
python examples/basic_usage.py
|
|
160
|
+
python examples/streaming_example.py
|
|
161
|
+
python examples/thread_resume.py
|
|
162
|
+
python examples/app_server_basic.py
|
|
163
|
+
python examples/app_server_fork.py
|
|
164
|
+
python examples/app_server_requirements.py
|
|
165
|
+
python examples/app_server_skill_input.py
|
|
166
|
+
python examples/app_server_approvals.py
|
|
167
|
+
python examples/app_server_turn_session.py
|
|
168
|
+
python examples/config_overrides.py
|
|
169
|
+
python examples/hooks_streaming.py
|
|
170
|
+
python examples/notify_hook.py
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
<a id="features"></a>
|
|
174
|
+
## 
|
|
175
|
+
|
|
176
|
+
| Feature Badge | Details |
|
|
177
|
+
| ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
|
|
178
|
+
|  | Each `Thread` keeps context; resume by thread id or last session. |
|
|
179
|
+
|  | `run_streamed()` yields structured events as they happen. |
|
|
180
|
+
|  | `ThreadHooks` lets you react to streamed events inline. |
|
|
181
|
+
|  | `run_json()` validates JSON output against a schema. |
|
|
182
|
+
|  | `run_pydantic()` derives schema and validates with Pydantic v2. |
|
|
183
|
+
|  | Thread options map to Codex CLI sandbox and approval policies. |
|
|
184
|
+
|  | Codex can act as a PydanticAI model or as a delegated tool. |
|
|
185
|
+
|  | Cancel running turns via `AbortController` and `AbortSignal`. |
|
|
186
|
+
|  | Optional spans if Logfire is installed and initialized. |
|
|
187
|
+
|
|
188
|
+
<a id="configuration"></a>
|
|
189
|
+
## 
|
|
190
|
+
|
|
191
|
+
### Installation extras
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
uv add "codex-sdk-python[pydantic]" # Pydantic v2 schema helpers
|
|
195
|
+
uv add "codex-sdk-python[pydantic-ai]" # PydanticAI integrations
|
|
196
|
+
uv add "codex-sdk-python[logfire]" # Optional tracing
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Environment variables
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
CODEX_API_KEY=<api-key>
|
|
203
|
+
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
204
|
+
CODEX_HOME=~/.codex
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Notes:
|
|
208
|
+
- `CODEX_API_KEY` is forwarded to the `codex` process; `CodexOptions.api_key` overrides the environment.
|
|
209
|
+
- `OPENAI_BASE_URL` is set when `CodexOptions.base_url` is provided.
|
|
210
|
+
- `CODEX_HOME` controls where sessions are stored and where `resume_last_thread()` looks.
|
|
211
|
+
|
|
212
|
+
### CodexOptions (client)
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from codex_sdk import Codex, CodexOptions
|
|
216
|
+
|
|
217
|
+
codex = Codex(
|
|
218
|
+
CodexOptions(
|
|
219
|
+
codex_path_override="/path/to/codex",
|
|
220
|
+
base_url="https://api.openai.com/v1",
|
|
221
|
+
api_key="<key>",
|
|
222
|
+
env={"CUSTOM_ENV": "custom"},
|
|
223
|
+
config_overrides={
|
|
224
|
+
"analytics.enabled": True,
|
|
225
|
+
"notify": ["python3", "/path/to/notify.py"],
|
|
226
|
+
},
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
- `codex_path_override`: use a custom CLI binary path.
|
|
232
|
+
- `base_url`: sets `OPENAI_BASE_URL` for the child process.
|
|
233
|
+
- `api_key`: sets `CODEX_API_KEY` for the child process.
|
|
234
|
+
- `env`: when set, replaces inherited environment variables; the SDK still injects required values.
|
|
235
|
+
|
|
236
|
+
### ThreadOptions (per thread)
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
from codex_sdk import ThreadOptions
|
|
240
|
+
|
|
241
|
+
ThreadOptions(
|
|
242
|
+
model="gpt-5-codex-high",
|
|
243
|
+
sandbox_mode="workspace-write",
|
|
244
|
+
working_directory="/path/to/project",
|
|
245
|
+
skip_git_repo_check=True,
|
|
246
|
+
model_reasoning_effort="high",
|
|
247
|
+
network_access_enabled=True,
|
|
248
|
+
web_search_mode="cached",
|
|
249
|
+
shell_snapshot_enabled=True,
|
|
250
|
+
background_terminals_enabled=True,
|
|
251
|
+
apply_patch_freeform_enabled=False,
|
|
252
|
+
exec_policy_enabled=True,
|
|
253
|
+
remote_models_enabled=False,
|
|
254
|
+
request_compression_enabled=True,
|
|
255
|
+
approval_policy="on-request",
|
|
256
|
+
additional_directories=["../shared"],
|
|
257
|
+
config_overrides={"analytics.enabled": True},
|
|
258
|
+
)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Important mappings to the Codex CLI:
|
|
262
|
+
- `sandbox_mode` maps to `--sandbox` (`read-only`, `workspace-write`, `danger-full-access`).
|
|
263
|
+
- `working_directory` maps to `--cd`.
|
|
264
|
+
- `additional_directories` maps to repeated `--add-dir`.
|
|
265
|
+
- `skip_git_repo_check` maps to `--skip-git-repo-check`.
|
|
266
|
+
- `model_reasoning_effort` maps to `--config model_reasoning_effort=...`.
|
|
267
|
+
- `network_access_enabled` maps to `--config sandbox_workspace_write.network_access=...`.
|
|
268
|
+
- `web_search_mode` maps to `--config web_search="disabled|cached|live"`.
|
|
269
|
+
- `web_search_enabled`/`web_search_cached_enabled` map to `--config web_search=...` for legacy
|
|
270
|
+
compatibility.
|
|
271
|
+
- `shell_snapshot_enabled` maps to `--config features.shell_snapshot=...`.
|
|
272
|
+
- `background_terminals_enabled` maps to `--config features.unified_exec=...`.
|
|
273
|
+
- `apply_patch_freeform_enabled` maps to `--config features.apply_patch_freeform=...`.
|
|
274
|
+
- `exec_policy_enabled` maps to `--config features.exec_policy=...`.
|
|
275
|
+
- `remote_models_enabled` maps to `--config features.remote_models=...`.
|
|
276
|
+
- `request_compression_enabled` maps to `--config features.enable_request_compression=...`.
|
|
277
|
+
- `feature_overrides` maps to `--config features.<key>=...` (explicit options take precedence).
|
|
278
|
+
- `approval_policy` maps to `--config approval_policy=...`.
|
|
279
|
+
- `config_overrides` maps to repeated `--config key=value` entries.
|
|
280
|
+
|
|
281
|
+
Note: `skills_enabled` is deprecated in Codex 0.80+ (skills are always enabled).
|
|
282
|
+
|
|
283
|
+
Feature overrides example:
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
ThreadOptions(
|
|
287
|
+
feature_overrides={
|
|
288
|
+
"web_search_cached": True,
|
|
289
|
+
"powershell_utf8": True,
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### App server (JSON-RPC)
|
|
295
|
+
|
|
296
|
+
For richer integrations (thread fork, requirements, explicit skill input), use the app-server
|
|
297
|
+
protocol. The client handles the initialize/initialized handshake and gives you access to
|
|
298
|
+
JSON-RPC notifications.
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
import asyncio
|
|
302
|
+
from codex_sdk import AppServerClient, AppServerOptions
|
|
303
|
+
|
|
304
|
+
async def main() -> None:
|
|
305
|
+
async with AppServerClient(AppServerOptions()) as app:
|
|
306
|
+
thread = await app.thread_start(model="gpt-5-codex-high", cwd=".")
|
|
307
|
+
thread_id = thread["thread"]["id"]
|
|
308
|
+
await app.turn_start(
|
|
309
|
+
thread_id,
|
|
310
|
+
[
|
|
311
|
+
{"type": "text", "text": "Use $my-skill and summarize."},
|
|
312
|
+
{"type": "skill", "name": "my-skill", "path": "/path/to/SKILL.md"},
|
|
313
|
+
],
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
async for notification in app.notifications():
|
|
317
|
+
print(notification.method, notification.params)
|
|
318
|
+
|
|
319
|
+
if __name__ == "__main__":
|
|
320
|
+
asyncio.run(main())
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Text inputs may include `textElements` with `byteRange` to preserve UI annotations in history.
|
|
324
|
+
The SDK also accepts `text_elements`/`byte_range` and normalizes them to camelCase.
|
|
325
|
+
|
|
326
|
+
Codex 0.86.0+ supports optional `SKILL.toml` metadata alongside `SKILL.md`. When present,
|
|
327
|
+
`skills_list` responses include an `interface` object (display name, icons, brand color,
|
|
328
|
+
default prompt) for richer UI integrations.
|
|
329
|
+
|
|
330
|
+
#### App-server convenience methods
|
|
331
|
+
|
|
332
|
+
The SDK also exposes helpers for most app-server endpoints:
|
|
333
|
+
|
|
334
|
+
- Threads: `thread_list`, `thread_archive`, `thread_rollback`, `thread_loaded_list`
|
|
335
|
+
- Config: `config_read`, `config_value_write`, `config_batch_write`, `config_requirements_read`
|
|
336
|
+
- Skills: `skills_list`
|
|
337
|
+
- Turns/review: `turn_start`, `turn_interrupt`, `review_start`, `turn_session`
|
|
338
|
+
- Models: `model_list`
|
|
339
|
+
- One-off commands: `command_exec`
|
|
340
|
+
- MCP auth/status: `mcp_server_oauth_login`, `mcp_server_refresh`, `mcp_server_status_list`
|
|
341
|
+
- Account: `account_login_start`, `account_login_cancel`, `account_logout`,
|
|
342
|
+
`account_rate_limits_read`, `account_read`
|
|
343
|
+
- Feedback: `feedback_upload`
|
|
344
|
+
|
|
345
|
+
These map 1:1 to the Codex app-server protocol; see `codex/codex-rs/app-server/README.md`
|
|
346
|
+
for payload shapes and event semantics.
|
|
347
|
+
|
|
348
|
+
### Observability (OTEL) and notify
|
|
349
|
+
|
|
350
|
+
Codex emits OTEL traces/logs/metrics when configured in `~/.codex/config.toml`.
|
|
351
|
+
For headless runs (`codex exec`), set `analytics.enabled=true` and provide OTEL exporters
|
|
352
|
+
in the config file. You can also pass overrides with `config_overrides`.
|
|
353
|
+
|
|
354
|
+
```python
|
|
355
|
+
CodexOptions(
|
|
356
|
+
config_overrides={
|
|
357
|
+
"analytics.enabled": True,
|
|
358
|
+
"notify": ["python3", "/path/to/notify.py"],
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
See `examples/notify_hook.py` for a ready-to-use notify script.
|
|
364
|
+
|
|
365
|
+
### TurnOptions (per turn)
|
|
366
|
+
|
|
367
|
+
```python
|
|
368
|
+
from codex_sdk import TurnOptions
|
|
369
|
+
|
|
370
|
+
TurnOptions(
|
|
371
|
+
output_schema={"type": "object", "properties": {"ok": {"type": "boolean"}}},
|
|
372
|
+
signal=controller.signal,
|
|
373
|
+
)
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
- `output_schema` must be a JSON object (mapping). The SDK writes it to a temp file and passes `--output-schema`.
|
|
377
|
+
- `signal` is an `AbortSignal` for canceling an in-flight turn.
|
|
378
|
+
|
|
379
|
+
### Bundled CLI binary and platform support
|
|
380
|
+
|
|
381
|
+
The SDK resolves a platform-specific Codex CLI binary under `src/codex_sdk/vendor/<target>/codex/`.
|
|
382
|
+
It selects the target triple based on OS and CPU and ensures the binary is executable on POSIX.
|
|
383
|
+
|
|
384
|
+
Supported target triples:
|
|
385
|
+
- Linux: `x86_64-unknown-linux-musl`, `aarch64-unknown-linux-musl`
|
|
386
|
+
- macOS: `x86_64-apple-darwin`, `aarch64-apple-darwin`
|
|
387
|
+
- Windows: `x86_64-pc-windows-msvc`, `aarch64-pc-windows-msvc`
|
|
388
|
+
|
|
389
|
+
If you are working from source and the vendor directory is missing, run `python scripts/setup_binary.py`
|
|
390
|
+
or follow `SETUP.md` to download the official npm package and copy the `vendor/` directory.
|
|
391
|
+
|
|
392
|
+
<a id="auth"></a>
|
|
393
|
+
## 
|
|
394
|
+
|
|
395
|
+
The SDK delegates authentication to the Codex CLI:
|
|
396
|
+
- Run `codex login` to create local credentials (stored under `~/.codex/` by the CLI).
|
|
397
|
+
- Or set `CODEX_API_KEY` (or pass `CodexOptions.api_key`) for headless use.
|
|
398
|
+
- `CodexOptions.base_url` sets `OPENAI_BASE_URL` to target an OpenAI-compatible endpoint.
|
|
399
|
+
|
|
400
|
+
<a id="usage"></a>
|
|
401
|
+
## 
|
|
402
|
+
|
|
403
|
+
### Basic run
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
from codex_sdk import Codex
|
|
407
|
+
|
|
408
|
+
codex = Codex()
|
|
409
|
+
thread = codex.start_thread()
|
|
410
|
+
turn = await thread.run("Summarize the repository")
|
|
411
|
+
print(turn.final_response)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Sync helpers (non-async)
|
|
415
|
+
|
|
416
|
+
```python
|
|
417
|
+
from pydantic import BaseModel
|
|
418
|
+
|
|
419
|
+
class RepoStatus(BaseModel):
|
|
420
|
+
summary: str
|
|
421
|
+
|
|
422
|
+
turn = thread.run_sync("Summarize the repository")
|
|
423
|
+
parsed = thread.run_json_sync("Summarize", output_schema={"type": "object"})
|
|
424
|
+
validated = thread.run_pydantic_sync("Summarize", output_model=RepoStatus)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Note: sync helpers raise `CodexError` if called from an active event loop.
|
|
428
|
+
|
|
429
|
+
### Streaming events
|
|
430
|
+
|
|
431
|
+
```python
|
|
432
|
+
result = await thread.run_streamed("Diagnose the test failure")
|
|
433
|
+
async for event in result.events:
|
|
434
|
+
if event.type == "item.completed":
|
|
435
|
+
print(event.item.type)
|
|
436
|
+
elif event.type == "turn.completed":
|
|
437
|
+
print(event.usage)
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
To iterate directly without the wrapper:
|
|
441
|
+
|
|
442
|
+
```python
|
|
443
|
+
async for event in thread.run_streamed_events("Diagnose the test failure"):
|
|
444
|
+
print(event.type)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Hooks for streamed events
|
|
448
|
+
|
|
449
|
+
Use `ThreadHooks` to react to events without manually wiring an event loop.
|
|
450
|
+
|
|
451
|
+
```python
|
|
452
|
+
from codex_sdk import ThreadHooks
|
|
453
|
+
|
|
454
|
+
hooks = ThreadHooks(
|
|
455
|
+
on_event=lambda event: print("event", event.type),
|
|
456
|
+
on_item_type={
|
|
457
|
+
"command_execution": lambda item: print("command", item.command),
|
|
458
|
+
},
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
turn = await thread.run_with_hooks("Run the tests and summarize failures.", hooks=hooks)
|
|
462
|
+
print(turn.final_response)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Event types (ThreadEvent)
|
|
466
|
+
|
|
467
|
+
- `thread.started`
|
|
468
|
+
- `turn.started`
|
|
469
|
+
- `turn.completed` (includes token usage)
|
|
470
|
+
- `turn.failed`
|
|
471
|
+
- `item.started`
|
|
472
|
+
- `item.updated`
|
|
473
|
+
- `item.completed`
|
|
474
|
+
- `error`
|
|
475
|
+
|
|
476
|
+
### Item types (ThreadItem)
|
|
477
|
+
|
|
478
|
+
- `agent_message`
|
|
479
|
+
- `reasoning`
|
|
480
|
+
- `command_execution`
|
|
481
|
+
- `file_change`
|
|
482
|
+
- `mcp_tool_call`
|
|
483
|
+
- `web_search`
|
|
484
|
+
- `todo_list`
|
|
485
|
+
- `error`
|
|
486
|
+
|
|
487
|
+
### Structured output (JSON schema)
|
|
488
|
+
|
|
489
|
+
```python
|
|
490
|
+
schema = {
|
|
491
|
+
"type": "object",
|
|
492
|
+
"properties": {
|
|
493
|
+
"summary": {"type": "string"},
|
|
494
|
+
"status": {"type": "string", "enum": ["ok", "action_required"]},
|
|
495
|
+
},
|
|
496
|
+
"required": ["summary", "status"],
|
|
497
|
+
"additionalProperties": False,
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
result = await thread.run_json("Summarize repository status", output_schema=schema)
|
|
501
|
+
print(result.output)
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Pydantic output validation
|
|
505
|
+
|
|
506
|
+
```python
|
|
507
|
+
from pydantic import BaseModel
|
|
508
|
+
|
|
509
|
+
class RepoStatus(BaseModel):
|
|
510
|
+
summary: str
|
|
511
|
+
status: str
|
|
512
|
+
|
|
513
|
+
result = await thread.run_pydantic("Summarize repository status", output_model=RepoStatus)
|
|
514
|
+
print(result.output)
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Images + text
|
|
518
|
+
|
|
519
|
+
```python
|
|
520
|
+
turn = await thread.run(
|
|
521
|
+
[
|
|
522
|
+
{"type": "text", "text": "Describe these screenshots"},
|
|
523
|
+
{"type": "local_image", "path": "./ui.png"},
|
|
524
|
+
{"type": "text", "text": "Focus on failures"},
|
|
525
|
+
{"type": "local_image", "path": "./diagram.jpg"},
|
|
526
|
+
]
|
|
527
|
+
)
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Abort a running turn
|
|
531
|
+
|
|
532
|
+
```python
|
|
533
|
+
import asyncio
|
|
534
|
+
from codex_sdk import AbortController, TurnOptions
|
|
535
|
+
|
|
536
|
+
controller = AbortController()
|
|
537
|
+
options = TurnOptions(signal=controller.signal)
|
|
538
|
+
|
|
539
|
+
task = asyncio.create_task(thread.run("Long task", options))
|
|
540
|
+
controller.abort("user requested cancel")
|
|
541
|
+
await task
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Thread resume helpers
|
|
545
|
+
|
|
546
|
+
```python
|
|
547
|
+
from codex_sdk import Codex
|
|
548
|
+
|
|
549
|
+
codex = Codex()
|
|
550
|
+
thread = codex.resume_thread("<thread-id>")
|
|
551
|
+
|
|
552
|
+
# Or resume the most recent session (uses CODEX_HOME or ~/.codex)
|
|
553
|
+
last_thread = codex.resume_last_thread()
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Turn helpers
|
|
557
|
+
|
|
558
|
+
Each `Turn` provides convenience filters: `agent_messages()`, `reasoning()`, `commands()`,
|
|
559
|
+
`file_changes()`, `mcp_tool_calls()`, `web_searches()`, `todo_lists()`, and `errors()`.
|
|
560
|
+
|
|
561
|
+
<a id="api"></a>
|
|
562
|
+
## 
|
|
563
|
+
|
|
564
|
+
Core classes:
|
|
565
|
+
- `Codex`: `start_thread()`, `resume_thread()`, `resume_last_thread()`.
|
|
566
|
+
- `Thread`: `run()`, `run_streamed()`, `run_streamed_events()`, `run_json()`, `run_pydantic()`,
|
|
567
|
+
plus `run_sync()`, `run_json_sync()`, `run_pydantic_sync()`.
|
|
568
|
+
- `Turn`: `items`, `final_response`, `usage`, and helper filters.
|
|
569
|
+
- `AppServerClient`, `AppServerTurnSession`, `ApprovalDecisions` for app-server integrations.
|
|
570
|
+
- `ThreadHooks` for event callbacks.
|
|
571
|
+
- `CodexOptions`, `ThreadOptions`, `TurnOptions`.
|
|
572
|
+
- `AbortController`, `AbortSignal`.
|
|
573
|
+
|
|
574
|
+
Exceptions:
|
|
575
|
+
- `CodexError`, `CodexCLIError`, `CodexParseError`, `CodexAbortError`, `TurnFailedError`.
|
|
576
|
+
|
|
577
|
+
Typed events and items:
|
|
578
|
+
- `ThreadEvent` union of `thread.*`, `turn.*`, `item.*`, and `error` events.
|
|
579
|
+
- `ThreadItem` union of `agent_message`, `reasoning`, `command_execution`, `file_change`,
|
|
580
|
+
`mcp_tool_call`, `web_search`, `todo_list`, `error`.
|
|
581
|
+
|
|
582
|
+
<a id="examples"></a>
|
|
583
|
+
## 
|
|
584
|
+
|
|
585
|
+
Example scripts under `examples/`:
|
|
586
|
+
|
|
587
|
+
- `basic_usage.py`: minimal `Codex` + `Thread` usage.
|
|
588
|
+
- `streaming_example.py`: live event streaming.
|
|
589
|
+
- `structured_output.py`: JSON schema output parsing.
|
|
590
|
+
- `thread_resume.py`: resume with `CODEX_THREAD_ID`.
|
|
591
|
+
- `permission_levels_example.py`: sandbox modes and working directory.
|
|
592
|
+
- `model_configuration_example.py`: model selection and endpoint config.
|
|
593
|
+
- `app_server_turn_session.py`: approval-handled turns over app-server.
|
|
594
|
+
- `hooks_streaming.py`: event hooks for streaming runs.
|
|
595
|
+
- `notify_hook.py`: notify script for CLI callbacks.
|
|
596
|
+
- `pydantic_ai_model_provider.py`: Codex as a PydanticAI model provider.
|
|
597
|
+
- `pydantic_ai_handoff.py`: Codex as a PydanticAI tool.
|
|
598
|
+
|
|
599
|
+
<a id="sandbox"></a>
|
|
600
|
+
## 
|
|
601
|
+
|
|
602
|
+
The SDK forwards sandbox and approval controls directly to `codex exec`.
|
|
603
|
+
|
|
604
|
+
- `read-only`: can read files and run safe commands, no writes.
|
|
605
|
+
- `workspace-write`: can write inside the working directory and added directories.
|
|
606
|
+
- `danger-full-access`: unrestricted (use with caution).
|
|
607
|
+
|
|
608
|
+
Additional controls:
|
|
609
|
+
- `working_directory`: restricts where the CLI starts and what it can access.
|
|
610
|
+
- `additional_directories`: allowlist extra folders when using `workspace-write`.
|
|
611
|
+
- `approval_policy`: `never`, `on-request`, `on-failure`, `untrusted`.
|
|
612
|
+
- `network_access_enabled`: toggles network access in workspace-write sandbox.
|
|
613
|
+
- `web_search_mode`: toggles web search (`disabled`, `cached`, `live`).
|
|
614
|
+
|
|
615
|
+
<a id="pydantic-ai"></a>
|
|
616
|
+
## 
|
|
617
|
+
|
|
618
|
+
This SDK offers two ways to integrate with PydanticAI:
|
|
619
|
+
|
|
620
|
+
### 1) Codex as a PydanticAI model provider
|
|
621
|
+
|
|
622
|
+
Use `CodexModel` to delegate tool-call planning and text generation to Codex, while PydanticAI executes tools and validates outputs.
|
|
623
|
+
|
|
624
|
+
```python
|
|
625
|
+
from pydantic_ai import Agent, Tool
|
|
626
|
+
|
|
627
|
+
from codex_sdk.integrations.pydantic_ai_model import CodexModel
|
|
628
|
+
from codex_sdk.options import ThreadOptions
|
|
629
|
+
|
|
630
|
+
def add(a: int, b: int) -> int:
|
|
631
|
+
return a + b
|
|
632
|
+
|
|
633
|
+
model = CodexModel(
|
|
634
|
+
thread_options=ThreadOptions(
|
|
635
|
+
model="gpt-5",
|
|
636
|
+
sandbox_mode="read-only",
|
|
637
|
+
skip_git_repo_check=True,
|
|
638
|
+
)
|
|
639
|
+
)
|
|
640
|
+
agent = Agent(model, tools=[Tool(add)])
|
|
641
|
+
|
|
642
|
+
result = agent.run_sync("What's 19 + 23? Use the add tool.")
|
|
643
|
+
print(result.output)
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
How it works:
|
|
647
|
+
- `CodexModel` builds a JSON schema envelope with `tool_calls` and `final`.
|
|
648
|
+
- Codex emits tool calls as JSON strings; PydanticAI runs them.
|
|
649
|
+
- If `allow_text_output` is true, Codex can place final text in `final`.
|
|
650
|
+
- Streaming APIs (`Agent.run_stream_events()`, `Agent.run_stream_sync()`) are supported; Codex
|
|
651
|
+
emits streamed responses as a single chunk once the turn completes.
|
|
652
|
+
|
|
653
|
+
Safety defaults (you can override with your own `ThreadOptions`):
|
|
654
|
+
- `sandbox_mode="read-only"`
|
|
655
|
+
- `skip_git_repo_check=True`
|
|
656
|
+
- `approval_policy="never"`
|
|
657
|
+
- `web_search_mode="disabled"`
|
|
658
|
+
- `network_access_enabled=False`
|
|
659
|
+
|
|
660
|
+
### 2) Codex as a PydanticAI tool (handoff)
|
|
661
|
+
|
|
662
|
+
Register Codex as a tool and let a PydanticAI agent decide when to delegate tasks.
|
|
663
|
+
|
|
664
|
+
```python
|
|
665
|
+
from pydantic_ai import Agent
|
|
666
|
+
|
|
667
|
+
from codex_sdk import ThreadOptions
|
|
668
|
+
from codex_sdk.integrations.pydantic_ai import codex_handoff_tool
|
|
669
|
+
|
|
670
|
+
tool = codex_handoff_tool(
|
|
671
|
+
thread_options=ThreadOptions(
|
|
672
|
+
sandbox_mode="workspace-write",
|
|
673
|
+
skip_git_repo_check=True,
|
|
674
|
+
working_directory=".",
|
|
675
|
+
),
|
|
676
|
+
include_items=True,
|
|
677
|
+
items_limit=20,
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
agent = Agent(
|
|
681
|
+
"openai:gpt-5",
|
|
682
|
+
tools=[tool],
|
|
683
|
+
system_prompt=(
|
|
684
|
+
"You can delegate implementation details to the codex_handoff tool. "
|
|
685
|
+
"Use it for repository-aware edits, command execution, or patches."
|
|
686
|
+
),
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
result = await agent.run(
|
|
690
|
+
"Use the codex_handoff tool to scan this repository and suggest one small DX improvement."
|
|
691
|
+
)
|
|
692
|
+
print(result.output)
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
Handoff options:
|
|
696
|
+
- `persist_thread`: keep a single Codex thread across tool calls (default true).
|
|
697
|
+
- `include_items`: include a summarized item list in tool output.
|
|
698
|
+
- `items_limit`: cap the number of items returned.
|
|
699
|
+
- `include_usage`: include token usage.
|
|
700
|
+
- `timeout_seconds`: wrap the run in `asyncio.wait_for`.
|
|
701
|
+
|
|
702
|
+
<a id="telemetry"></a>
|
|
703
|
+
## 
|
|
704
|
+
|
|
705
|
+
If `logfire` is installed and initialized, the SDK emits spans:
|
|
706
|
+
- `codex_sdk.exec`
|
|
707
|
+
- `codex_sdk.thread.turn`
|
|
708
|
+
- `codex_sdk.pydantic_ai.model_request`
|
|
709
|
+
- `codex_sdk.pydantic_ai.handoff`
|
|
710
|
+
|
|
711
|
+
If Logfire is missing or not initialized, the span context manager is a no-op.
|
|
712
|
+
|
|
713
|
+
<a id="architecture"></a>
|
|
714
|
+
<a id="acheature"></a>
|
|
715
|
+
## 
|
|
716
|
+
|
|
717
|
+
### System components
|
|
718
|
+
|
|
719
|
+
```mermaid
|
|
720
|
+
flowchart LR
|
|
721
|
+
subgraph App[Your Python App]
|
|
722
|
+
U[User Code]
|
|
723
|
+
T[Thread API]
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
subgraph SDK[Codex SDK]
|
|
727
|
+
C[Codex]
|
|
728
|
+
E[CodexExec]
|
|
729
|
+
P[Event Parser]
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
subgraph CLI[Bundled Codex CLI]
|
|
733
|
+
X["codex exec --experimental-json"]
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
FS[(Filesystem)]
|
|
737
|
+
NET[(Network)]
|
|
738
|
+
|
|
739
|
+
U --> T --> C --> E --> X
|
|
740
|
+
X -->|JSONL events| P --> T
|
|
741
|
+
X --> FS
|
|
742
|
+
X --> NET
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### Streaming event lifecycle
|
|
746
|
+
|
|
747
|
+
```mermaid
|
|
748
|
+
sequenceDiagram
|
|
749
|
+
participant Dev as Developer
|
|
750
|
+
participant Thread as Thread.run_streamed()
|
|
751
|
+
participant Exec as CodexExec
|
|
752
|
+
participant CLI as codex exec
|
|
753
|
+
|
|
754
|
+
Dev->>Thread: run_streamed(prompt)
|
|
755
|
+
Thread->>Exec: spawn CLI with flags
|
|
756
|
+
Exec->>CLI: stdin prompt
|
|
757
|
+
CLI-->>Exec: JSONL line
|
|
758
|
+
Exec-->>Thread: raw line
|
|
759
|
+
Thread-->>Dev: ThreadEvent
|
|
760
|
+
CLI-->>Exec: JSONL line
|
|
761
|
+
Exec-->>Thread: raw line
|
|
762
|
+
Thread-->>Dev: ThreadEvent
|
|
763
|
+
CLI-->>Exec: exit code
|
|
764
|
+
Exec-->>Thread: completion
|
|
765
|
+
Thread-->>Dev: turn.completed / turn.failed
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### PydanticAI model-provider loop
|
|
769
|
+
|
|
770
|
+
```mermaid
|
|
771
|
+
sequenceDiagram
|
|
772
|
+
participant Agent as PydanticAI Agent
|
|
773
|
+
participant Model as CodexModel
|
|
774
|
+
participant SDK as Codex SDK
|
|
775
|
+
participant CLI as codex exec
|
|
776
|
+
participant Tools as User Tools
|
|
777
|
+
|
|
778
|
+
Agent->>Model: request(messages, tools)
|
|
779
|
+
Model->>SDK: start_thread + run_json(prompt, output_schema)
|
|
780
|
+
SDK->>CLI: codex exec --output-schema
|
|
781
|
+
CLI-->>SDK: JSON envelope {tool_calls, final}
|
|
782
|
+
SDK-->>Model: ParsedTurn
|
|
783
|
+
alt tool_calls present
|
|
784
|
+
Model-->>Agent: ToolCallPart(s)
|
|
785
|
+
Agent->>Tools: execute tool(s)
|
|
786
|
+
Tools-->>Agent: results
|
|
787
|
+
else final text allowed
|
|
788
|
+
Model-->>Agent: TextPart(final)
|
|
789
|
+
end
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
### PydanticAI handoff tool
|
|
793
|
+
|
|
794
|
+
```mermaid
|
|
795
|
+
flowchart LR
|
|
796
|
+
Agent[PydanticAI Agent] --> Tool[codex_handoff_tool]
|
|
797
|
+
Tool --> SDK[Codex SDK Thread]
|
|
798
|
+
SDK --> CLI[Codex CLI]
|
|
799
|
+
CLI --> SDK
|
|
800
|
+
SDK --> Tool
|
|
801
|
+
Tool --> Agent
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
<a id="testing"></a>
|
|
805
|
+
## 
|
|
806
|
+
|
|
807
|
+
This repo uses unit tests with mocked CLI processes to keep the test suite fast and deterministic.
|
|
808
|
+
|
|
809
|
+
Test focus areas:
|
|
810
|
+
- `tests/test_exec.py`: CLI invocation, environment handling, config flags, abort behavior.
|
|
811
|
+
- `tests/test_thread.py`: parsing, streaming, JSON schema, Pydantic validation, input normalization.
|
|
812
|
+
- `tests/test_codex.py`: resume helpers and option wiring.
|
|
813
|
+
- `tests/test_abort.py`: abort signal semantics.
|
|
814
|
+
- `tests/test_telemetry.py`: Logfire span behavior.
|
|
815
|
+
- `tests/test_pydantic_ai_*`: PydanticAI model provider and handoff integration.
|
|
816
|
+
|
|
817
|
+
### Run tests
|
|
818
|
+
|
|
819
|
+
```bash
|
|
820
|
+
uv sync
|
|
821
|
+
uv run pytest
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
Note: PydanticAI tests are skipped unless `pydantic-ai` is installed.
|
|
825
|
+
|
|
826
|
+
### Coverage
|
|
827
|
+
|
|
828
|
+
```bash
|
|
829
|
+
uv run pytest --cov=codex_sdk
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
Coverage is configured in `pyproject.toml` with `fail_under = 95`.
|
|
833
|
+
|
|
834
|
+
### Upgrade checklist
|
|
835
|
+
|
|
836
|
+
For SDK release updates, follow `UPGRADE_CHECKLIST.md`.
|
|
837
|
+
|
|
838
|
+
### Format and lint
|
|
839
|
+
|
|
840
|
+
```bash
|
|
841
|
+
uv run black src tests
|
|
842
|
+
uv run isort src tests
|
|
843
|
+
uv run flake8 src tests
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
### Type checking
|
|
847
|
+
|
|
848
|
+
```bash
|
|
849
|
+
uv run mypy src
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
<a id="ci-cd"></a>
|
|
853
|
+
## 
|
|
854
|
+
|
|
855
|
+
This repository includes GitHub Actions workflows under `.github/workflows/`.
|
|
856
|
+
The CI pipeline runs linting, type checks, and `pytest --cov=codex_sdk`.
|
|
857
|
+
Release automation creates GitHub releases from `CHANGELOG_SDK.md` when you push a
|
|
858
|
+
`vX.Y.Z` tag or manually dispatch the workflow, then the publish workflow uploads
|
|
859
|
+
the package to PyPI on release publish.
|
|
860
|
+
|
|
861
|
+
<a id="operations"></a>
|
|
862
|
+
## 
|
|
863
|
+
|
|
864
|
+
- Sessions are stored under `~/.codex/sessions` (or `CODEX_HOME`).
|
|
865
|
+
- Use `resume_thread(thread_id)` to continue a known session.
|
|
866
|
+
- Use `resume_last_thread()` to pick the most recent session automatically.
|
|
867
|
+
- Clean up stale sessions by removing old `rollout-*.jsonl` files if needed.
|
|
868
|
+
|
|
869
|
+
<a id="troubleshooting"></a>
|
|
870
|
+
## 
|
|
871
|
+
|
|
872
|
+
- **Codex CLI exited non-zero**: Catch `CodexCLIError` and inspect `.stderr`.
|
|
873
|
+
- **Unknown event type**: `CodexParseError` means the CLI emitted an unexpected JSONL entry.
|
|
874
|
+
- **Turn failed**: `TurnFailedError` indicates a `turn.failed` event.
|
|
875
|
+
- **Run canceled**: `CodexAbortError` indicates a triggered `AbortSignal`.
|
|
876
|
+
- **No thread id**: Ensure a `thread.started` event is emitted before resuming.
|
|
877
|
+
|
|
878
|
+
<a id="production"></a>
|
|
879
|
+
## 
|
|
880
|
+
|
|
881
|
+
- Prefer `read-only` or `workspace-write` sandboxes in production.
|
|
882
|
+
- Set `working_directory` to a repo root and keep `skip_git_repo_check=False` where possible.
|
|
883
|
+
- Configure `approval_policy` for any tool execution requiring user consent.
|
|
884
|
+
- Disable `web_search_mode` and `network_access_enabled` unless explicitly needed.
|
|
885
|
+
|
|
886
|
+
<a id="license"></a>
|
|
887
|
+
## 
|
|
888
|
+
|
|
889
|
+
Apache-2.0
|