codex-python 0.2.16__tar.gz → 1.0.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.
Files changed (33) hide show
  1. codex_python-1.0.0/PKG-INFO +136 -0
  2. codex_python-1.0.0/README.md +117 -0
  3. codex_python-1.0.0/codex/__init__.py +70 -0
  4. codex_python-1.0.0/codex/_binary.py +42 -0
  5. codex_python-1.0.0/codex/codex.py +22 -0
  6. codex_python-1.0.0/codex/errors.py +17 -0
  7. codex_python-1.0.0/codex/events.py +66 -0
  8. codex_python-1.0.0/codex/exec.py +118 -0
  9. codex_python-1.0.0/codex/items.py +84 -0
  10. codex_python-1.0.0/codex/options.py +27 -0
  11. codex_python-1.0.0/codex/output_schema_file.py +34 -0
  12. codex_python-1.0.0/codex/thread.py +170 -0
  13. codex_python-1.0.0/codex/vendor/.gitkeep +0 -0
  14. codex_python-1.0.0/crates/codex_native/Cargo.lock +204 -0
  15. codex_python-1.0.0/crates/codex_native/Cargo.toml +17 -0
  16. codex_python-1.0.0/crates/codex_native/codex/__init__.py +8 -0
  17. codex_python-1.0.0/crates/codex_native/src/lib.rs +12 -0
  18. {codex_python-0.2.16 → codex_python-1.0.0}/pyproject.toml +6 -8
  19. codex_python-0.2.16/PKG-INFO +0 -141
  20. codex_python-0.2.16/README.md +0 -121
  21. codex_python-0.2.16/codex/__init__.py +0 -34
  22. codex_python-0.2.16/codex/api.py +0 -123
  23. codex_python-0.2.16/codex/config.py +0 -243
  24. codex_python-0.2.16/codex/event.py +0 -29
  25. codex_python-0.2.16/codex/native.py +0 -35
  26. codex_python-0.2.16/codex/protocol/_base_model.py +0 -8
  27. codex_python-0.2.16/codex/protocol/types.py +0 -1561
  28. codex_python-0.2.16/crates/codex_native/Cargo.lock +0 -3600
  29. codex_python-0.2.16/crates/codex_native/Cargo.toml +0 -48
  30. codex_python-0.2.16/crates/codex_native/src/bin/protocol_schema.rs +0 -170
  31. codex_python-0.2.16/crates/codex_native/src/lib.rs +0 -545
  32. {codex_python-0.2.16 → codex_python-1.0.0}/LICENSE +0 -0
  33. {codex_python-0.2.16 → codex_python-1.0.0}/codex/py.typed +0 -0
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: codex-python
3
+ Version: 1.0.0
4
+ Classifier: Programming Language :: Python :: 3
5
+ Classifier: Programming Language :: Python :: 3 :: Only
6
+ Classifier: Programming Language :: Python :: 3.13
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Typing :: Typed
9
+ Classifier: Operating System :: OS Independent
10
+ License-File: LICENSE
11
+ Summary: Python SDK for Codex CLI with bundled platform binaries
12
+ Keywords: codex,sdk,cli,automation
13
+ Requires-Python: >=3.12
14
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
15
+ Project-URL: Homepage, https://github.com/gersmann/codex-python
16
+ Project-URL: Issues, https://github.com/gersmann/codex-python/issues
17
+ Project-URL: Repository, https://github.com/gersmann/codex-python
18
+
19
+ # codex-python
20
+
21
+ Python SDK for Codex with bundled `codex` binaries inside platform wheels.
22
+
23
+ The SDK mirrors the TypeScript SDK behavior:
24
+ - Spawns `codex exec --experimental-json`
25
+ - Streams JSONL events
26
+ - Supports thread resume, structured output schemas, images, sandbox/model options
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install codex-python
32
+ ```
33
+
34
+ ## Quickstart
35
+
36
+ ```python
37
+ from codex import Codex
38
+
39
+ client = Codex()
40
+ thread = client.start_thread()
41
+
42
+ result = thread.run("Diagnose the failing tests and propose a fix")
43
+ print(result.final_response)
44
+ print(result.items)
45
+ ```
46
+
47
+ ## Streaming
48
+
49
+ ```python
50
+ from codex import Codex
51
+
52
+ client = Codex()
53
+ thread = client.start_thread()
54
+
55
+ stream = thread.run_streamed("Investigate this bug")
56
+ for event in stream.events:
57
+ if event["type"] == "item.completed":
58
+ print(event["item"])
59
+ elif event["type"] == "turn.completed":
60
+ print(event["usage"])
61
+ ```
62
+
63
+ ## Structured output
64
+
65
+ ```python
66
+ from codex import Codex, TurnOptions
67
+
68
+ schema = {
69
+ "type": "object",
70
+ "properties": {"summary": {"type": "string"}},
71
+ "required": ["summary"],
72
+ "additionalProperties": False,
73
+ }
74
+
75
+ client = Codex()
76
+ thread = client.start_thread()
77
+ result = thread.run("Summarize repository status", TurnOptions(output_schema=schema))
78
+ print(result.final_response)
79
+ ```
80
+
81
+ ## Input with local images
82
+
83
+ ```python
84
+ from codex import Codex
85
+
86
+ client = Codex()
87
+ thread = client.start_thread()
88
+ result = thread.run(
89
+ [
90
+ {"type": "text", "text": "Describe these screenshots"},
91
+ {"type": "local_image", "path": "./ui.png"},
92
+ {"type": "local_image", "path": "./diagram.jpg"},
93
+ ]
94
+ )
95
+ ```
96
+
97
+ ## Resume a thread
98
+
99
+ ```python
100
+ from codex import Codex
101
+
102
+ client = Codex()
103
+ thread = client.resume_thread("thread_123")
104
+ thread.run("Continue from previous context")
105
+ ```
106
+
107
+ ## Options
108
+
109
+ - `CodexOptions`: `codex_path_override`, `base_url`, `api_key`
110
+ - `ThreadOptions`: `model`, `sandbox_mode`, `working_directory`, `skip_git_repo_check`
111
+ - `TurnOptions`: `output_schema`
112
+
113
+ ## Bundled binary behavior
114
+
115
+ By default, the SDK resolves the bundled binary at:
116
+
117
+ `codex/vendor/<target-triple>/codex/{codex|codex.exe}`
118
+
119
+ If the bundled binary is not present (for example in a source checkout), the SDK falls back to
120
+ `codex` on `PATH`.
121
+
122
+ You can always override with `CodexOptions(codex_path_override=...)`.
123
+
124
+ ## Development
125
+
126
+ ```bash
127
+ make lint
128
+ make test
129
+ ```
130
+
131
+ If you want to test vendored-binary behavior locally, fetch binaries into `codex/vendor`:
132
+
133
+ ```bash
134
+ python scripts/fetch_codex_binary.py --target-triple x86_64-unknown-linux-musl
135
+ ```
136
+
@@ -0,0 +1,117 @@
1
+ # codex-python
2
+
3
+ Python SDK for Codex with bundled `codex` binaries inside platform wheels.
4
+
5
+ The SDK mirrors the TypeScript SDK behavior:
6
+ - Spawns `codex exec --experimental-json`
7
+ - Streams JSONL events
8
+ - Supports thread resume, structured output schemas, images, sandbox/model options
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install codex-python
14
+ ```
15
+
16
+ ## Quickstart
17
+
18
+ ```python
19
+ from codex import Codex
20
+
21
+ client = Codex()
22
+ thread = client.start_thread()
23
+
24
+ result = thread.run("Diagnose the failing tests and propose a fix")
25
+ print(result.final_response)
26
+ print(result.items)
27
+ ```
28
+
29
+ ## Streaming
30
+
31
+ ```python
32
+ from codex import Codex
33
+
34
+ client = Codex()
35
+ thread = client.start_thread()
36
+
37
+ stream = thread.run_streamed("Investigate this bug")
38
+ for event in stream.events:
39
+ if event["type"] == "item.completed":
40
+ print(event["item"])
41
+ elif event["type"] == "turn.completed":
42
+ print(event["usage"])
43
+ ```
44
+
45
+ ## Structured output
46
+
47
+ ```python
48
+ from codex import Codex, TurnOptions
49
+
50
+ schema = {
51
+ "type": "object",
52
+ "properties": {"summary": {"type": "string"}},
53
+ "required": ["summary"],
54
+ "additionalProperties": False,
55
+ }
56
+
57
+ client = Codex()
58
+ thread = client.start_thread()
59
+ result = thread.run("Summarize repository status", TurnOptions(output_schema=schema))
60
+ print(result.final_response)
61
+ ```
62
+
63
+ ## Input with local images
64
+
65
+ ```python
66
+ from codex import Codex
67
+
68
+ client = Codex()
69
+ thread = client.start_thread()
70
+ result = thread.run(
71
+ [
72
+ {"type": "text", "text": "Describe these screenshots"},
73
+ {"type": "local_image", "path": "./ui.png"},
74
+ {"type": "local_image", "path": "./diagram.jpg"},
75
+ ]
76
+ )
77
+ ```
78
+
79
+ ## Resume a thread
80
+
81
+ ```python
82
+ from codex import Codex
83
+
84
+ client = Codex()
85
+ thread = client.resume_thread("thread_123")
86
+ thread.run("Continue from previous context")
87
+ ```
88
+
89
+ ## Options
90
+
91
+ - `CodexOptions`: `codex_path_override`, `base_url`, `api_key`
92
+ - `ThreadOptions`: `model`, `sandbox_mode`, `working_directory`, `skip_git_repo_check`
93
+ - `TurnOptions`: `output_schema`
94
+
95
+ ## Bundled binary behavior
96
+
97
+ By default, the SDK resolves the bundled binary at:
98
+
99
+ `codex/vendor/<target-triple>/codex/{codex|codex.exe}`
100
+
101
+ If the bundled binary is not present (for example in a source checkout), the SDK falls back to
102
+ `codex` on `PATH`.
103
+
104
+ You can always override with `CodexOptions(codex_path_override=...)`.
105
+
106
+ ## Development
107
+
108
+ ```bash
109
+ make lint
110
+ make test
111
+ ```
112
+
113
+ If you want to test vendored-binary behavior locally, fetch binaries into `codex/vendor`:
114
+
115
+ ```bash
116
+ python scripts/fetch_codex_binary.py --target-triple x86_64-unknown-linux-musl
117
+ ```
@@ -0,0 +1,70 @@
1
+ """Python SDK for embedding Codex via the bundled CLI binary."""
2
+
3
+ from codex.codex import Codex
4
+ from codex.errors import CodexError, CodexExecError, CodexParseError, ThreadRunError
5
+ from codex.events import (
6
+ ItemCompletedEvent,
7
+ ItemStartedEvent,
8
+ ItemUpdatedEvent,
9
+ ThreadError,
10
+ ThreadErrorEvent,
11
+ ThreadEvent,
12
+ ThreadStartedEvent,
13
+ TurnCompletedEvent,
14
+ TurnFailedEvent,
15
+ TurnStartedEvent,
16
+ Usage,
17
+ )
18
+ from codex.items import (
19
+ AgentMessageItem,
20
+ CommandExecutionItem,
21
+ ErrorItem,
22
+ FileChangeItem,
23
+ McpToolCallItem,
24
+ ReasoningItem,
25
+ ThreadItem,
26
+ TodoListItem,
27
+ WebSearchItem,
28
+ )
29
+ from codex.options import ApprovalMode, CodexOptions, SandboxMode, ThreadOptions, TurnOptions
30
+ from codex.thread import Input, RunResult, RunStreamedResult, Thread, UserInput
31
+
32
+ __version__ = "1.0.0"
33
+
34
+ __all__ = [
35
+ "Codex",
36
+ "CodexError",
37
+ "CodexExecError",
38
+ "CodexParseError",
39
+ "ThreadRunError",
40
+ "Thread",
41
+ "RunResult",
42
+ "RunStreamedResult",
43
+ "Input",
44
+ "UserInput",
45
+ "CodexOptions",
46
+ "ThreadOptions",
47
+ "TurnOptions",
48
+ "ApprovalMode",
49
+ "SandboxMode",
50
+ "ThreadEvent",
51
+ "ThreadStartedEvent",
52
+ "TurnStartedEvent",
53
+ "TurnCompletedEvent",
54
+ "TurnFailedEvent",
55
+ "ItemStartedEvent",
56
+ "ItemUpdatedEvent",
57
+ "ItemCompletedEvent",
58
+ "ThreadError",
59
+ "ThreadErrorEvent",
60
+ "Usage",
61
+ "ThreadItem",
62
+ "AgentMessageItem",
63
+ "ReasoningItem",
64
+ "CommandExecutionItem",
65
+ "FileChangeItem",
66
+ "McpToolCallItem",
67
+ "WebSearchItem",
68
+ "TodoListItem",
69
+ "ErrorItem",
70
+ ]
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ import platform
4
+ from pathlib import Path
5
+
6
+ from codex.errors import CodexExecError
7
+
8
+
9
+ def resolve_target_triple(system_name: str | None = None, machine_name: str | None = None) -> str:
10
+ system = (system_name or platform.system()).lower()
11
+ machine = (machine_name or platform.machine()).lower()
12
+
13
+ if system in {"linux", "android"}:
14
+ if machine in {"x86_64", "amd64"}:
15
+ return "x86_64-unknown-linux-musl"
16
+ if machine in {"aarch64", "arm64"}:
17
+ return "aarch64-unknown-linux-musl"
18
+ elif system == "darwin":
19
+ if machine in {"x86_64", "amd64"}:
20
+ return "x86_64-apple-darwin"
21
+ if machine in {"aarch64", "arm64"}:
22
+ return "aarch64-apple-darwin"
23
+ elif system in {"windows", "win32"}:
24
+ if machine in {"x86_64", "amd64"}:
25
+ return "x86_64-pc-windows-msvc"
26
+ if machine in {"aarch64", "arm64"}:
27
+ return "aarch64-pc-windows-msvc"
28
+
29
+ raise CodexExecError(f"Unsupported platform: {system} ({machine})")
30
+
31
+
32
+ def bundled_codex_path(target_triple: str | None = None) -> Path:
33
+ triple = target_triple or resolve_target_triple()
34
+ package_root = Path(__file__).resolve().parent
35
+ binary_name = "codex.exe" if "windows" in triple else "codex"
36
+ binary_path = package_root / "vendor" / triple / "codex" / binary_name
37
+ if not binary_path.exists():
38
+ raise CodexExecError(
39
+ "Bundled codex binary not found at "
40
+ f"{binary_path}. Install a platform wheel or provide codex_path_override."
41
+ )
42
+ return binary_path
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from codex.exec import CodexExec
4
+ from codex.options import CodexOptions, ThreadOptions
5
+ from codex.thread import Thread
6
+
7
+
8
+ class Codex:
9
+ """Main entrypoint for interacting with Codex threads."""
10
+
11
+ def __init__(self, options: CodexOptions | None = None) -> None:
12
+ resolved = options or CodexOptions()
13
+ self._exec = CodexExec(resolved.codex_path_override)
14
+ self._options = resolved
15
+
16
+ def start_thread(self, options: ThreadOptions | None = None) -> Thread:
17
+ return Thread(self._exec, self._options, options or ThreadOptions())
18
+
19
+ def resume_thread(self, id: str, options: ThreadOptions | None = None) -> Thread:
20
+ if id == "":
21
+ raise ValueError("id must be non-empty")
22
+ return Thread(self._exec, self._options, options or ThreadOptions(), id)
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class CodexError(RuntimeError):
5
+ """Base error for the Python Codex SDK."""
6
+
7
+
8
+ class CodexExecError(CodexError):
9
+ """Raised when the Codex CLI process fails."""
10
+
11
+
12
+ class CodexParseError(CodexError):
13
+ """Raised when streaming JSONL events cannot be parsed."""
14
+
15
+
16
+ class ThreadRunError(CodexError):
17
+ """Raised when a run or stream fails before turn completion."""
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal, TypedDict
4
+
5
+ from codex.items import ThreadItem
6
+
7
+
8
+ class ThreadStartedEvent(TypedDict):
9
+ type: Literal["thread.started"]
10
+ thread_id: str
11
+
12
+
13
+ class TurnStartedEvent(TypedDict):
14
+ type: Literal["turn.started"]
15
+
16
+
17
+ class Usage(TypedDict):
18
+ input_tokens: int
19
+ cached_input_tokens: int
20
+ output_tokens: int
21
+
22
+
23
+ class TurnCompletedEvent(TypedDict):
24
+ type: Literal["turn.completed"]
25
+ usage: Usage
26
+
27
+
28
+ class ThreadError(TypedDict):
29
+ message: str
30
+
31
+
32
+ class TurnFailedEvent(TypedDict):
33
+ type: Literal["turn.failed"]
34
+ error: ThreadError
35
+
36
+
37
+ class ItemStartedEvent(TypedDict):
38
+ type: Literal["item.started"]
39
+ item: ThreadItem
40
+
41
+
42
+ class ItemUpdatedEvent(TypedDict):
43
+ type: Literal["item.updated"]
44
+ item: ThreadItem
45
+
46
+
47
+ class ItemCompletedEvent(TypedDict):
48
+ type: Literal["item.completed"]
49
+ item: ThreadItem
50
+
51
+
52
+ class ThreadErrorEvent(TypedDict):
53
+ type: Literal["error"]
54
+ message: str
55
+
56
+
57
+ ThreadEvent = (
58
+ ThreadStartedEvent
59
+ | TurnStartedEvent
60
+ | TurnCompletedEvent
61
+ | TurnFailedEvent
62
+ | ItemStartedEvent
63
+ | ItemUpdatedEvent
64
+ | ItemCompletedEvent
65
+ | ThreadErrorEvent
66
+ )
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import shutil
5
+ import subprocess
6
+ from collections.abc import Iterator
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+
10
+ from codex._binary import bundled_codex_path
11
+ from codex.errors import CodexExecError
12
+ from codex.options import SandboxMode
13
+
14
+ INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE"
15
+ PYTHON_SDK_ORIGINATOR = "codex_sdk_py"
16
+
17
+
18
+ @dataclass(slots=True, frozen=True)
19
+ class CodexExecArgs:
20
+ input: str
21
+ base_url: str | None = None
22
+ api_key: str | None = None
23
+ thread_id: str | None = None
24
+ images: list[str] | None = None
25
+ model: str | None = None
26
+ sandbox_mode: SandboxMode | None = None
27
+ working_directory: str | None = None
28
+ skip_git_repo_check: bool = False
29
+ output_schema_file: str | None = None
30
+
31
+
32
+ class CodexExec:
33
+ def __init__(self, executable_path: str | None = None) -> None:
34
+ if executable_path is not None:
35
+ path = Path(executable_path)
36
+ else:
37
+ try:
38
+ path = bundled_codex_path()
39
+ except CodexExecError as bundled_error:
40
+ system_codex = shutil.which("codex")
41
+ if system_codex is None:
42
+ raise CodexExecError(
43
+ f"{bundled_error} Also failed to find `codex` on PATH."
44
+ ) from bundled_error
45
+ path = Path(system_codex)
46
+ self.executable_path = str(path)
47
+
48
+ def run(self, args: CodexExecArgs) -> Iterator[str]:
49
+ command_args: list[str] = ["exec", "--experimental-json"]
50
+
51
+ if args.model is not None:
52
+ command_args.extend(["--model", args.model])
53
+ if args.sandbox_mode is not None:
54
+ command_args.extend(["--sandbox", args.sandbox_mode])
55
+ if args.working_directory is not None:
56
+ command_args.extend(["--cd", args.working_directory])
57
+ if args.skip_git_repo_check:
58
+ command_args.append("--skip-git-repo-check")
59
+ if args.output_schema_file is not None:
60
+ command_args.extend(["--output-schema", args.output_schema_file])
61
+ if args.images is not None:
62
+ for image in args.images:
63
+ command_args.extend(["--image", image])
64
+ if args.thread_id:
65
+ command_args.extend(["resume", args.thread_id])
66
+
67
+ env = os.environ.copy()
68
+ if INTERNAL_ORIGINATOR_ENV not in env:
69
+ env[INTERNAL_ORIGINATOR_ENV] = PYTHON_SDK_ORIGINATOR
70
+ if args.base_url is not None:
71
+ env["OPENAI_BASE_URL"] = args.base_url
72
+ if args.api_key is not None:
73
+ env["CODEX_API_KEY"] = args.api_key
74
+
75
+ try:
76
+ child = subprocess.Popen(
77
+ [self.executable_path, *command_args],
78
+ stdin=subprocess.PIPE,
79
+ stdout=subprocess.PIPE,
80
+ stderr=subprocess.PIPE,
81
+ text=True,
82
+ encoding="utf-8",
83
+ env=env,
84
+ )
85
+ except OSError as exc:
86
+ raise CodexExecError(
87
+ f"Failed to spawn codex executable at '{self.executable_path}': {exc}"
88
+ ) from exc
89
+
90
+ if child.stdin is None:
91
+ child.kill()
92
+ raise CodexExecError("Child process has no stdin")
93
+ if child.stdout is None:
94
+ child.kill()
95
+ raise CodexExecError("Child process has no stdout")
96
+ if child.stderr is None:
97
+ child.kill()
98
+ raise CodexExecError("Child process has no stderr")
99
+
100
+ try:
101
+ child.stdin.write(args.input)
102
+ child.stdin.close()
103
+ except OSError as exc:
104
+ child.kill()
105
+ raise CodexExecError(f"Failed to write input to codex process: {exc}") from exc
106
+
107
+ try:
108
+ for line in child.stdout:
109
+ yield line.rstrip("\r\n")
110
+ finally:
111
+ child.stdout.close()
112
+
113
+ exit_code = child.wait()
114
+ stderr = child.stderr.read()
115
+ child.stderr.close()
116
+
117
+ if exit_code != 0:
118
+ raise CodexExecError(f"Codex exec exited with code {exit_code}: {stderr}")
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal, NotRequired, TypedDict
4
+
5
+ CommandExecutionStatus = Literal["in_progress", "completed", "failed"]
6
+ PatchChangeKind = Literal["add", "delete", "update"]
7
+ PatchApplyStatus = Literal["completed", "failed"]
8
+ McpToolCallStatus = Literal["in_progress", "completed", "failed"]
9
+
10
+
11
+ class CommandExecutionItem(TypedDict):
12
+ id: str
13
+ type: Literal["command_execution"]
14
+ command: str
15
+ aggregated_output: str
16
+ status: CommandExecutionStatus
17
+ exit_code: NotRequired[int]
18
+
19
+
20
+ class FileUpdateChange(TypedDict):
21
+ path: str
22
+ kind: PatchChangeKind
23
+
24
+
25
+ class FileChangeItem(TypedDict):
26
+ id: str
27
+ type: Literal["file_change"]
28
+ changes: list[FileUpdateChange]
29
+ status: PatchApplyStatus
30
+
31
+
32
+ class McpToolCallItem(TypedDict):
33
+ id: str
34
+ type: Literal["mcp_tool_call"]
35
+ server: str
36
+ tool: str
37
+ status: McpToolCallStatus
38
+
39
+
40
+ class AgentMessageItem(TypedDict):
41
+ id: str
42
+ type: Literal["agent_message"]
43
+ text: str
44
+
45
+
46
+ class ReasoningItem(TypedDict):
47
+ id: str
48
+ type: Literal["reasoning"]
49
+ text: str
50
+
51
+
52
+ class WebSearchItem(TypedDict):
53
+ id: str
54
+ type: Literal["web_search"]
55
+ query: str
56
+
57
+
58
+ class ErrorItem(TypedDict):
59
+ id: str
60
+ type: Literal["error"]
61
+ message: str
62
+
63
+
64
+ class TodoItem(TypedDict):
65
+ text: str
66
+ completed: bool
67
+
68
+
69
+ class TodoListItem(TypedDict):
70
+ id: str
71
+ type: Literal["todo_list"]
72
+ items: list[TodoItem]
73
+
74
+
75
+ ThreadItem = (
76
+ AgentMessageItem
77
+ | ReasoningItem
78
+ | CommandExecutionItem
79
+ | FileChangeItem
80
+ | McpToolCallItem
81
+ | WebSearchItem
82
+ | TodoListItem
83
+ | ErrorItem
84
+ )
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Literal
5
+
6
+ ApprovalMode = Literal["never", "on-request", "on-failure", "untrusted"]
7
+ SandboxMode = Literal["read-only", "workspace-write", "danger-full-access"]
8
+
9
+
10
+ @dataclass(slots=True, frozen=True)
11
+ class CodexOptions:
12
+ codex_path_override: str | None = None
13
+ base_url: str | None = None
14
+ api_key: str | None = None
15
+
16
+
17
+ @dataclass(slots=True, frozen=True)
18
+ class ThreadOptions:
19
+ model: str | None = None
20
+ sandbox_mode: SandboxMode | None = None
21
+ working_directory: str | None = None
22
+ skip_git_repo_check: bool = False
23
+
24
+
25
+ @dataclass(slots=True, frozen=True)
26
+ class TurnOptions:
27
+ output_schema: dict[str, object] | None = None