codex-python 1.114.1__tar.gz → 1.122.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_python-1.114.1 → codex_python-1.122.0}/PKG-INFO +49 -2
- {codex_python-1.114.1 → codex_python-1.122.0}/README.md +48 -1
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/__init__.py +9 -1
- codex_python-1.122.0/codex/_config_types.py +108 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/_runtime.py +8 -3
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/__init__.py +4 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_async_client.py +15 -4
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_async_services.py +71 -3
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_async_threads.py +18 -8
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_sync_client.py +6 -2
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_sync_services.py +90 -3
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_sync_threads.py +19 -4
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/models.py +36 -30
- codex_python-1.122.0/codex/app_server/options.py +535 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/codex.py +39 -7
- codex_python-1.122.0/codex/dynamic_tools.py +325 -0
- codex_python-1.122.0/codex/options.py +266 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/protocol/types.py +5344 -4126
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/thread.py +11 -4
- {codex_python-1.114.1 → codex_python-1.122.0}/crates/codex_native/Cargo.lock +7 -7
- {codex_python-1.114.1 → codex_python-1.122.0}/crates/codex_native/Cargo.toml +1 -1
- codex_python-1.114.1/codex/_config_types.py +0 -6
- codex_python-1.114.1/codex/app_server/options.py +0 -246
- codex_python-1.114.1/codex/options.py +0 -127
- {codex_python-1.114.1 → codex_python-1.122.0}/LICENSE +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/_binary.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/_file_utils.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/_turn_options.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_payloads.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_protocol_helpers.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_session.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_sync_support.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/_types.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/errors.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/app_server/transports.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/errors.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/output_schema.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/output_schema_file.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/protocol/__init__.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/py.typed +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/codex/vendor/.gitkeep +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/crates/codex_native/codex/__init__.py +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/crates/codex_native/src/lib.rs +0 -0
- {codex_python-1.114.1 → codex_python-1.122.0}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.122.0
|
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -66,13 +66,29 @@ Use `AppServerClient` when you want a deeper integration:
|
|
|
66
66
|
## Quickstart: `Codex`
|
|
67
67
|
|
|
68
68
|
```python
|
|
69
|
-
from codex import Codex
|
|
69
|
+
from codex import Codex, ThreadStartOptions
|
|
70
70
|
|
|
71
71
|
client = Codex()
|
|
72
|
+
|
|
73
|
+
# Simplest one-shot call.
|
|
72
74
|
summary = client.run_text("Diagnose the failing tests and propose a fix")
|
|
73
75
|
print(summary)
|
|
76
|
+
|
|
77
|
+
# One-shot call with thread-scoped defaults for that run's fresh internal thread.
|
|
78
|
+
summary = client.run_text(
|
|
79
|
+
"Diagnose the failing tests in this repo",
|
|
80
|
+
thread_options=ThreadStartOptions(
|
|
81
|
+
cwd="/repo",
|
|
82
|
+
model="gpt-5",
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
print(summary)
|
|
74
86
|
```
|
|
75
87
|
|
|
88
|
+
Use `thread_options=` on `run()`, `run_text()`, `run_json()`, and `run_model()` when you want to
|
|
89
|
+
set defaults on the fresh internal thread created for that one-shot call. Use
|
|
90
|
+
`start_thread()` / `resume_thread()` when later runs should share context.
|
|
91
|
+
|
|
76
92
|
More `Codex` examples: [docs/exec_api.md](docs/exec_api.md)
|
|
77
93
|
|
|
78
94
|
## Quickstart: `AppServerClient`
|
|
@@ -97,6 +113,35 @@ with AppServerClient.connect_stdio(initialize_options=initialize_options) as cli
|
|
|
97
113
|
More app-server examples: [docs/app_server.md](docs/app_server.md)
|
|
98
114
|
For websocket transport, install the optional extra: `pip install "codex-python[websocket]"`.
|
|
99
115
|
|
|
116
|
+
## Dynamic tools
|
|
117
|
+
|
|
118
|
+
Decorator-driven dynamic tools are available on both SDK surfaces.
|
|
119
|
+
|
|
120
|
+
### `Codex`
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from codex import Codex, dynamic_tool
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dynamic_tool
|
|
127
|
+
def lookup_ticket(id: str) -> str:
|
|
128
|
+
"""Look up a support ticket by id."""
|
|
129
|
+
return f"Ticket {id}: Login requests time out in eu-west-1."
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
client = Codex()
|
|
133
|
+
summary = client.run_text(
|
|
134
|
+
"Use the lookup_ticket dynamic tool for ticket 123 and summarize the result.",
|
|
135
|
+
tools=[lookup_ticket],
|
|
136
|
+
)
|
|
137
|
+
print(summary)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `AppServerClient`
|
|
141
|
+
|
|
142
|
+
For a complete app-server example using the same decorator-driven flow, see
|
|
143
|
+
[examples/app_server_dynamic_tool.py](examples/app_server_dynamic_tool.py).
|
|
144
|
+
|
|
100
145
|
## Structured output
|
|
101
146
|
|
|
102
147
|
### `Codex`
|
|
@@ -186,10 +231,12 @@ Advanced app-server usage, including typed stable RPC domains such as `client.mo
|
|
|
186
231
|
## Examples
|
|
187
232
|
|
|
188
233
|
- [examples/basic_conversation.py](examples/basic_conversation.py): minimal `Codex` flow
|
|
234
|
+
- [examples/basic_dynamic_tool.py](examples/basic_dynamic_tool.py): high-level `Codex` dynamic tool flow
|
|
189
235
|
- [examples/app_server_conversation.py](examples/app_server_conversation.py): minimal app-server flow
|
|
190
236
|
- [examples/app_server_websocket_conversation.py](examples/app_server_websocket_conversation.py): minimal websocket app-server flow
|
|
191
237
|
- [examples/app_server_stream_events.py](examples/app_server_stream_events.py): protocol-native app-server streaming
|
|
192
238
|
- [examples/app_server_tool_handler.py](examples/app_server_tool_handler.py): typed app-server request handling
|
|
239
|
+
- [examples/app_server_dynamic_tool.py](examples/app_server_dynamic_tool.py): decorator-driven dynamic tool registration
|
|
193
240
|
|
|
194
241
|
## Bundled binary behavior
|
|
195
242
|
|
|
@@ -44,13 +44,29 @@ Use `AppServerClient` when you want a deeper integration:
|
|
|
44
44
|
## Quickstart: `Codex`
|
|
45
45
|
|
|
46
46
|
```python
|
|
47
|
-
from codex import Codex
|
|
47
|
+
from codex import Codex, ThreadStartOptions
|
|
48
48
|
|
|
49
49
|
client = Codex()
|
|
50
|
+
|
|
51
|
+
# Simplest one-shot call.
|
|
50
52
|
summary = client.run_text("Diagnose the failing tests and propose a fix")
|
|
51
53
|
print(summary)
|
|
54
|
+
|
|
55
|
+
# One-shot call with thread-scoped defaults for that run's fresh internal thread.
|
|
56
|
+
summary = client.run_text(
|
|
57
|
+
"Diagnose the failing tests in this repo",
|
|
58
|
+
thread_options=ThreadStartOptions(
|
|
59
|
+
cwd="/repo",
|
|
60
|
+
model="gpt-5",
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
print(summary)
|
|
52
64
|
```
|
|
53
65
|
|
|
66
|
+
Use `thread_options=` on `run()`, `run_text()`, `run_json()`, and `run_model()` when you want to
|
|
67
|
+
set defaults on the fresh internal thread created for that one-shot call. Use
|
|
68
|
+
`start_thread()` / `resume_thread()` when later runs should share context.
|
|
69
|
+
|
|
54
70
|
More `Codex` examples: [docs/exec_api.md](docs/exec_api.md)
|
|
55
71
|
|
|
56
72
|
## Quickstart: `AppServerClient`
|
|
@@ -75,6 +91,35 @@ with AppServerClient.connect_stdio(initialize_options=initialize_options) as cli
|
|
|
75
91
|
More app-server examples: [docs/app_server.md](docs/app_server.md)
|
|
76
92
|
For websocket transport, install the optional extra: `pip install "codex-python[websocket]"`.
|
|
77
93
|
|
|
94
|
+
## Dynamic tools
|
|
95
|
+
|
|
96
|
+
Decorator-driven dynamic tools are available on both SDK surfaces.
|
|
97
|
+
|
|
98
|
+
### `Codex`
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from codex import Codex, dynamic_tool
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@dynamic_tool
|
|
105
|
+
def lookup_ticket(id: str) -> str:
|
|
106
|
+
"""Look up a support ticket by id."""
|
|
107
|
+
return f"Ticket {id}: Login requests time out in eu-west-1."
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
client = Codex()
|
|
111
|
+
summary = client.run_text(
|
|
112
|
+
"Use the lookup_ticket dynamic tool for ticket 123 and summarize the result.",
|
|
113
|
+
tools=[lookup_ticket],
|
|
114
|
+
)
|
|
115
|
+
print(summary)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### `AppServerClient`
|
|
119
|
+
|
|
120
|
+
For a complete app-server example using the same decorator-driven flow, see
|
|
121
|
+
[examples/app_server_dynamic_tool.py](examples/app_server_dynamic_tool.py).
|
|
122
|
+
|
|
78
123
|
## Structured output
|
|
79
124
|
|
|
80
125
|
### `Codex`
|
|
@@ -164,10 +209,12 @@ Advanced app-server usage, including typed stable RPC domains such as `client.mo
|
|
|
164
209
|
## Examples
|
|
165
210
|
|
|
166
211
|
- [examples/basic_conversation.py](examples/basic_conversation.py): minimal `Codex` flow
|
|
212
|
+
- [examples/basic_dynamic_tool.py](examples/basic_dynamic_tool.py): high-level `Codex` dynamic tool flow
|
|
167
213
|
- [examples/app_server_conversation.py](examples/app_server_conversation.py): minimal app-server flow
|
|
168
214
|
- [examples/app_server_websocket_conversation.py](examples/app_server_websocket_conversation.py): minimal websocket app-server flow
|
|
169
215
|
- [examples/app_server_stream_events.py](examples/app_server_stream_events.py): protocol-native app-server streaming
|
|
170
216
|
- [examples/app_server_tool_handler.py](examples/app_server_tool_handler.py): typed app-server request handling
|
|
217
|
+
- [examples/app_server_dynamic_tool.py](examples/app_server_dynamic_tool.py): decorator-driven dynamic tool registration
|
|
171
218
|
|
|
172
219
|
## Bundled binary behavior
|
|
173
220
|
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from codex.codex import Codex
|
|
6
|
+
from codex.dynamic_tools import dynamic_tool
|
|
6
7
|
from codex.errors import CodexError, CodexExecError, CodexParseError, ThreadRunError
|
|
7
8
|
from codex.options import (
|
|
8
9
|
CancelSignal,
|
|
10
|
+
CodexConfig,
|
|
9
11
|
CodexConfigObject,
|
|
10
12
|
CodexConfigValue,
|
|
11
13
|
CodexOptions,
|
|
@@ -13,11 +15,16 @@ from codex.options import (
|
|
|
13
15
|
ThreadStartOptions,
|
|
14
16
|
TurnOptions,
|
|
15
17
|
)
|
|
18
|
+
from codex.thread import CodexTurnStream, Input, Thread
|
|
16
19
|
|
|
17
|
-
__version__ = "1.
|
|
20
|
+
__version__ = "1.122.0"
|
|
18
21
|
|
|
19
22
|
__all__ = [
|
|
20
23
|
"Codex",
|
|
24
|
+
"CodexTurnStream",
|
|
25
|
+
"Thread",
|
|
26
|
+
"Input",
|
|
27
|
+
"dynamic_tool",
|
|
21
28
|
"CodexError",
|
|
22
29
|
"CodexExecError",
|
|
23
30
|
"CodexParseError",
|
|
@@ -26,6 +33,7 @@ __all__ = [
|
|
|
26
33
|
"ThreadStartOptions",
|
|
27
34
|
"ThreadResumeOptions",
|
|
28
35
|
"TurnOptions",
|
|
36
|
+
"CodexConfig",
|
|
29
37
|
"CodexConfigValue",
|
|
30
38
|
"CodexConfigObject",
|
|
31
39
|
"CancelSignal",
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
from codex.protocol import types as protocol
|
|
8
|
+
|
|
9
|
+
type CodexConfigValue = (
|
|
10
|
+
str | int | float | bool | list["CodexConfigValue"] | dict[str, "CodexConfigValue"]
|
|
11
|
+
)
|
|
12
|
+
type CodexConfigObject = dict[str, CodexConfigValue]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CodexConfig(BaseModel):
|
|
16
|
+
"""Typed top-level Codex config shape with forward-compatible extra keys."""
|
|
17
|
+
|
|
18
|
+
model_config = ConfigDict(extra="allow")
|
|
19
|
+
__pydantic_extra__: dict[str, CodexConfigValue]
|
|
20
|
+
|
|
21
|
+
analytics: CodexConfigObject | None = Field(
|
|
22
|
+
default=None,
|
|
23
|
+
description="Open-ended analytics configuration subtree.",
|
|
24
|
+
)
|
|
25
|
+
approval_policy: protocol.AskForApproval | None = Field(
|
|
26
|
+
default=None,
|
|
27
|
+
description="Default approval policy for new turns and commands.",
|
|
28
|
+
)
|
|
29
|
+
compact_prompt: str | None = Field(
|
|
30
|
+
default=None,
|
|
31
|
+
description="Prompt text used when the app-server compacts thread history.",
|
|
32
|
+
)
|
|
33
|
+
developer_instructions: str | None = Field(
|
|
34
|
+
default=None,
|
|
35
|
+
description="Default developer instructions applied to new turns.",
|
|
36
|
+
)
|
|
37
|
+
forced_chatgpt_workspace_id: str | None = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
description="Workspace identifier to force for ChatGPT-managed auth flows.",
|
|
40
|
+
)
|
|
41
|
+
forced_login_method: Literal["chatgpt", "api"] | None = Field(
|
|
42
|
+
default=None,
|
|
43
|
+
description="Force Codex auth to ChatGPT or API-key mode.",
|
|
44
|
+
)
|
|
45
|
+
instructions: str | None = Field(
|
|
46
|
+
default=None,
|
|
47
|
+
description="Default user-visible instructions prepended to new conversations.",
|
|
48
|
+
)
|
|
49
|
+
model: str | None = Field(
|
|
50
|
+
default=None,
|
|
51
|
+
description="Default model id used when threads or turns do not override it.",
|
|
52
|
+
)
|
|
53
|
+
model_auto_compact_token_limit: int | None = Field(
|
|
54
|
+
default=None,
|
|
55
|
+
description="Token threshold that triggers automatic thread compaction.",
|
|
56
|
+
)
|
|
57
|
+
model_context_window: int | None = Field(
|
|
58
|
+
default=None,
|
|
59
|
+
description="Context window size override for the configured model.",
|
|
60
|
+
)
|
|
61
|
+
model_provider: str | None = Field(
|
|
62
|
+
default=None,
|
|
63
|
+
description="Default model provider name.",
|
|
64
|
+
)
|
|
65
|
+
model_reasoning_effort: protocol.ReasoningEffort | None = Field(
|
|
66
|
+
default=None,
|
|
67
|
+
description="Default reasoning effort for turns that do not override it.",
|
|
68
|
+
)
|
|
69
|
+
model_reasoning_summary: protocol.ReasoningSummary | None = Field(
|
|
70
|
+
default=None,
|
|
71
|
+
description="Default reasoning-summary behavior for supported models.",
|
|
72
|
+
)
|
|
73
|
+
model_verbosity: Literal["low", "medium", "high"] | None = Field(
|
|
74
|
+
default=None,
|
|
75
|
+
description="Default verbosity for supported models.",
|
|
76
|
+
)
|
|
77
|
+
profile: str | None = Field(
|
|
78
|
+
default=None,
|
|
79
|
+
description="Name of the active Codex profile.",
|
|
80
|
+
)
|
|
81
|
+
profiles: CodexConfigObject | None = Field(
|
|
82
|
+
default_factory=dict,
|
|
83
|
+
description="Open-ended profile definitions keyed by profile name.",
|
|
84
|
+
)
|
|
85
|
+
review_model: str | None = Field(
|
|
86
|
+
default=None,
|
|
87
|
+
description="Model override used for review flows.",
|
|
88
|
+
)
|
|
89
|
+
sandbox_mode: protocol.SandboxMode | None = Field(
|
|
90
|
+
default=None,
|
|
91
|
+
description="Default sandbox mode for new threads and turns.",
|
|
92
|
+
)
|
|
93
|
+
sandbox_workspace_write: CodexConfigObject | None = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
description="Open-ended workspace-write sandbox configuration subtree.",
|
|
96
|
+
)
|
|
97
|
+
service_tier: protocol.ServiceTier | None = Field(
|
|
98
|
+
default=None,
|
|
99
|
+
description="Default service tier for requests that do not override it.",
|
|
100
|
+
)
|
|
101
|
+
tools: CodexConfigObject | None = Field(
|
|
102
|
+
default=None,
|
|
103
|
+
description="Open-ended tool configuration subtree.",
|
|
104
|
+
)
|
|
105
|
+
web_search: Literal["disabled", "cached", "live"] | None = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
description="Default web-search mode.",
|
|
108
|
+
)
|
|
@@ -7,7 +7,7 @@ from collections.abc import Callable, Mapping
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
from codex._binary import BundledCodexNotFoundError
|
|
10
|
-
from codex._config_types import CodexConfigObject, CodexConfigValue
|
|
10
|
+
from codex._config_types import CodexConfig, CodexConfigObject, CodexConfigValue
|
|
11
11
|
|
|
12
12
|
INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE"
|
|
13
13
|
PYTHON_SDK_ORIGINATOR = "codex_sdk_py"
|
|
@@ -50,9 +50,14 @@ def resolve_codex_path(
|
|
|
50
50
|
return system_codex
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def serialize_config_overrides(config_overrides: CodexConfigObject) -> list[str]:
|
|
53
|
+
def serialize_config_overrides(config_overrides: CodexConfig | CodexConfigObject) -> list[str]:
|
|
54
54
|
overrides: list[str] = []
|
|
55
|
-
|
|
55
|
+
root = (
|
|
56
|
+
config_overrides.model_dump(mode="python", exclude_none=True, exclude_unset=True)
|
|
57
|
+
if isinstance(config_overrides, CodexConfig)
|
|
58
|
+
else config_overrides
|
|
59
|
+
)
|
|
60
|
+
_flatten_config_overrides(root, "", overrides)
|
|
56
61
|
return overrides
|
|
57
62
|
|
|
58
63
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from codex._config_types import CodexConfig
|
|
3
4
|
from codex.app_server._async_client import AsyncAppServerClient, AsyncRpcClient
|
|
4
5
|
from codex.app_server._async_threads import AsyncAppServerThread, AsyncTurnStream
|
|
5
6
|
from codex.app_server._sync_client import AppServerClient, RpcClient
|
|
@@ -23,6 +24,7 @@ from codex.app_server.options import (
|
|
|
23
24
|
AppServerTurnOptions,
|
|
24
25
|
AppServerWebSocketOptions,
|
|
25
26
|
)
|
|
27
|
+
from codex.dynamic_tools import dynamic_tool
|
|
26
28
|
|
|
27
29
|
__all__ = [
|
|
28
30
|
"AsyncAppServerClient",
|
|
@@ -30,6 +32,7 @@ __all__ = [
|
|
|
30
32
|
"AsyncRpcClient",
|
|
31
33
|
"AsyncTurnStream",
|
|
32
34
|
"AppServerClient",
|
|
35
|
+
"dynamic_tool",
|
|
33
36
|
"AppServerThread",
|
|
34
37
|
"RpcClient",
|
|
35
38
|
"TurnStream",
|
|
@@ -40,6 +43,7 @@ __all__ = [
|
|
|
40
43
|
"AppServerRpcError",
|
|
41
44
|
"AppServerTurnError",
|
|
42
45
|
"AppServerClientInfo",
|
|
46
|
+
"CodexConfig",
|
|
43
47
|
"AppServerInitializeOptions",
|
|
44
48
|
"AppServerProcessOptions",
|
|
45
49
|
"AppServerWebSocketOptions",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from collections.abc import Collection, Mapping
|
|
5
|
+
from collections.abc import Callable, Collection, Mapping
|
|
6
6
|
from typing import TypeVar, cast
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel
|
|
@@ -43,6 +43,7 @@ from codex.app_server.transports import (
|
|
|
43
43
|
AsyncStdioTransport,
|
|
44
44
|
AsyncWebSocketTransport,
|
|
45
45
|
)
|
|
46
|
+
from codex.dynamic_tools import _DynamicToolRuntime, merge_dynamic_tool_specs, resolve_dynamic_tools
|
|
46
47
|
from codex.protocol import types as protocol
|
|
47
48
|
|
|
48
49
|
_ModelT = TypeVar("_ModelT", bound=BaseModel)
|
|
@@ -60,8 +61,9 @@ __all__ = [
|
|
|
60
61
|
class AsyncRpcClient:
|
|
61
62
|
"""Low-level async JSON-RPC access to app-server methods."""
|
|
62
63
|
|
|
63
|
-
def __init__(self, session: _AsyncSession) -> None:
|
|
64
|
+
def __init__(self, session: _AsyncSession, dynamic_tools: _DynamicToolRuntime) -> None:
|
|
64
65
|
self._session = session
|
|
66
|
+
self._dynamic_tools = dynamic_tools
|
|
65
67
|
|
|
66
68
|
async def request(
|
|
67
69
|
self,
|
|
@@ -92,6 +94,7 @@ class AsyncRpcClient:
|
|
|
92
94
|
*,
|
|
93
95
|
request_model: type[_RequestT] | None = None,
|
|
94
96
|
) -> None:
|
|
97
|
+
self._dynamic_tools.check_manual_handler_registration(method)
|
|
95
98
|
self._session.on_request(method, handler, request_model=request_model)
|
|
96
99
|
|
|
97
100
|
|
|
@@ -114,7 +117,8 @@ class AsyncAppServerClient:
|
|
|
114
117
|
initialize_options: AppServerInitializeOptions | None = None,
|
|
115
118
|
) -> None:
|
|
116
119
|
self._session = _AsyncSession(transport, initialize_options)
|
|
117
|
-
self.
|
|
120
|
+
self._dynamic_tools = _DynamicToolRuntime(self._session.on_request)
|
|
121
|
+
self.rpc = AsyncRpcClient(self._session, self._dynamic_tools)
|
|
118
122
|
self.events = AsyncEventsClient(self._session)
|
|
119
123
|
self.models = AsyncModelsClient(self.rpc)
|
|
120
124
|
self.apps = AsyncAppsClient(self.rpc)
|
|
@@ -167,12 +171,19 @@ class AsyncAppServerClient:
|
|
|
167
171
|
async def start_thread(
|
|
168
172
|
self,
|
|
169
173
|
options: AppServerThreadStartOptions | None = None,
|
|
174
|
+
*,
|
|
175
|
+
tools: Collection[Callable[..., object]] | None = None,
|
|
170
176
|
) -> AsyncAppServerThread:
|
|
177
|
+
start_options = options or AppServerThreadStartOptions()
|
|
178
|
+
resolved_tools = resolve_dynamic_tools(list(tools or ()))
|
|
179
|
+
dynamic_tools = merge_dynamic_tool_specs(start_options.dynamic_tools, resolved_tools)
|
|
180
|
+
self._dynamic_tools.prepare_activation(resolved_tools)
|
|
171
181
|
result = await self.rpc.request_typed(
|
|
172
182
|
"thread/start",
|
|
173
|
-
(
|
|
183
|
+
start_options.model_copy(update={"dynamic_tools": dynamic_tools}).to_params(),
|
|
174
184
|
ThreadResult,
|
|
175
185
|
)
|
|
186
|
+
self._dynamic_tools.activate(result.thread.id, resolved_tools)
|
|
176
187
|
return AsyncAppServerThread(cast(_ThreadClient, self), result.thread)
|
|
177
188
|
|
|
178
189
|
async def resume_thread(
|
|
@@ -26,6 +26,7 @@ from codex.app_server.models import (
|
|
|
26
26
|
ModelInfo,
|
|
27
27
|
ModelListResult,
|
|
28
28
|
SkillsConfigWriteResult,
|
|
29
|
+
SkillsListEntry,
|
|
29
30
|
SkillsListResult,
|
|
30
31
|
WindowsSandboxSetupStartResult,
|
|
31
32
|
)
|
|
@@ -113,7 +114,7 @@ class AsyncSkillsClient(_AsyncServiceClient):
|
|
|
113
114
|
cwds: Sequence[str] | None = None,
|
|
114
115
|
force_reload: bool | None = None,
|
|
115
116
|
per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
|
|
116
|
-
) -> list[
|
|
117
|
+
) -> list[SkillsListEntry]:
|
|
117
118
|
return (
|
|
118
119
|
await self.list_page(
|
|
119
120
|
cwds=cwds,
|
|
@@ -139,7 +140,10 @@ class AsyncSkillsClient(_AsyncServiceClient):
|
|
|
139
140
|
return await self._rpc.request_typed("skills/list", params, SkillsListResult)
|
|
140
141
|
|
|
141
142
|
async def write_config(self, *, path: str, enabled: bool) -> SkillsConfigWriteResult:
|
|
142
|
-
params = protocol.SkillsConfigWriteParams(
|
|
143
|
+
params = protocol.SkillsConfigWriteParams(
|
|
144
|
+
path=protocol.AbsolutePathBuf(path),
|
|
145
|
+
enabled=enabled,
|
|
146
|
+
)
|
|
143
147
|
return await self._rpc.request_typed("skills/config/write", params, SkillsConfigWriteResult)
|
|
144
148
|
|
|
145
149
|
|
|
@@ -322,19 +326,80 @@ class AsyncCommandClient(_AsyncServiceClient):
|
|
|
322
326
|
*,
|
|
323
327
|
command: Sequence[str],
|
|
324
328
|
cwd: str | None = None,
|
|
329
|
+
disable_output_cap: bool | None = None,
|
|
330
|
+
disable_timeout: bool | None = None,
|
|
331
|
+
env: Mapping[str, object | None] | None = None,
|
|
332
|
+
output_bytes_cap: int | None = None,
|
|
333
|
+
process_id: str | None = None,
|
|
325
334
|
sandbox_policy: protocol.SandboxPolicy | None = None,
|
|
335
|
+
size: protocol.CommandExecTerminalSize | None = None,
|
|
336
|
+
stream_stdin: bool | None = None,
|
|
337
|
+
stream_stdout_stderr: bool | None = None,
|
|
326
338
|
timeout_ms: int | None = None,
|
|
339
|
+
tty: bool | None = None,
|
|
327
340
|
) -> CommandExecResult:
|
|
328
341
|
params = protocol.CommandExecParams(
|
|
329
342
|
command=list(command),
|
|
330
343
|
cwd=cwd,
|
|
344
|
+
disableOutputCap=disable_output_cap,
|
|
345
|
+
disableTimeout=disable_timeout,
|
|
346
|
+
env=dict(env) if env is not None else None,
|
|
347
|
+
outputBytesCap=output_bytes_cap,
|
|
348
|
+
processId=process_id,
|
|
331
349
|
sandboxPolicy=sandbox_policy,
|
|
350
|
+
size=size,
|
|
351
|
+
streamStdin=stream_stdin,
|
|
352
|
+
streamStdoutStderr=stream_stdout_stderr,
|
|
332
353
|
timeoutMs=timeout_ms,
|
|
354
|
+
tty=tty,
|
|
333
355
|
)
|
|
334
356
|
return await self._rpc.request_typed("command/exec", params, CommandExecResult)
|
|
335
357
|
|
|
336
358
|
exec = execute
|
|
337
359
|
|
|
360
|
+
async def write_stdin(
|
|
361
|
+
self,
|
|
362
|
+
*,
|
|
363
|
+
process_id: str,
|
|
364
|
+
close_stdin: bool | None = None,
|
|
365
|
+
delta_base64: str | None = None,
|
|
366
|
+
) -> EmptyResult:
|
|
367
|
+
"""Write stdin bytes to a running `command/exec` process or close stdin.
|
|
368
|
+
|
|
369
|
+
This wraps the app-server `command/exec/write` request. `delta_base64`
|
|
370
|
+
is optional base64-encoded stdin data; `close_stdin` closes the
|
|
371
|
+
process stdin after the optional write.
|
|
372
|
+
"""
|
|
373
|
+
params = protocol.CommandExecWriteParams(
|
|
374
|
+
closeStdin=close_stdin,
|
|
375
|
+
deltaBase64=delta_base64,
|
|
376
|
+
processId=process_id,
|
|
377
|
+
)
|
|
378
|
+
return await self._rpc.request_typed("command/exec/write", params, EmptyResult)
|
|
379
|
+
|
|
380
|
+
async def resize_terminal(
|
|
381
|
+
self,
|
|
382
|
+
*,
|
|
383
|
+
process_id: str,
|
|
384
|
+
size: protocol.CommandExecTerminalSize,
|
|
385
|
+
) -> EmptyResult:
|
|
386
|
+
"""Resize the terminal attached to a running `command/exec` process.
|
|
387
|
+
|
|
388
|
+
This wraps the app-server `command/exec/resize` request and sends the
|
|
389
|
+
new terminal dimensions as `cols` and `rows`.
|
|
390
|
+
"""
|
|
391
|
+
params = protocol.CommandExecResizeParams(processId=process_id, size=size)
|
|
392
|
+
return await self._rpc.request_typed("command/exec/resize", params, EmptyResult)
|
|
393
|
+
|
|
394
|
+
async def terminate_process(self, *, process_id: str) -> EmptyResult:
|
|
395
|
+
"""Terminate a running `command/exec` process.
|
|
396
|
+
|
|
397
|
+
This wraps the app-server `command/exec/terminate` request for the
|
|
398
|
+
client-supplied process id.
|
|
399
|
+
"""
|
|
400
|
+
params = protocol.CommandExecTerminateParams(processId=process_id)
|
|
401
|
+
return await self._rpc.request_typed("command/exec/terminate", params, EmptyResult)
|
|
402
|
+
|
|
338
403
|
|
|
339
404
|
class AsyncExternalAgentConfigClient(_AsyncServiceClient):
|
|
340
405
|
async def detect(
|
|
@@ -369,7 +434,10 @@ class AsyncWindowsSandboxClient(_AsyncServiceClient):
|
|
|
369
434
|
mode: protocol.WindowsSandboxSetupMode,
|
|
370
435
|
cwd: str | None = None,
|
|
371
436
|
) -> WindowsSandboxSetupStartResult:
|
|
372
|
-
params = protocol.WindowsSandboxSetupStartParams(
|
|
437
|
+
params = protocol.WindowsSandboxSetupStartParams(
|
|
438
|
+
cwd=protocol.AbsolutePathBuf(cwd) if cwd is not None else None,
|
|
439
|
+
mode=mode,
|
|
440
|
+
)
|
|
373
441
|
return await self._rpc.request_typed(
|
|
374
442
|
"windowsSandbox/setupStart",
|
|
375
443
|
params,
|
|
@@ -41,9 +41,13 @@ _TURN_STREAM_NOTIFICATION_METHODS = {
|
|
|
41
41
|
"turn/completed",
|
|
42
42
|
"turn/diff/updated",
|
|
43
43
|
"turn/plan/updated",
|
|
44
|
+
"hook/started",
|
|
45
|
+
"hook/completed",
|
|
44
46
|
"thread/tokenUsage/updated",
|
|
45
47
|
"item/started",
|
|
46
48
|
"item/completed",
|
|
49
|
+
"item/autoApprovalReview/started",
|
|
50
|
+
"item/autoApprovalReview/completed",
|
|
47
51
|
"item/agentMessage/delta",
|
|
48
52
|
"item/plan/delta",
|
|
49
53
|
"item/reasoning/summaryTextDelta",
|
|
@@ -235,15 +239,21 @@ class AsyncTurnStream:
|
|
|
235
239
|
self._require_terminal_turn()
|
|
236
240
|
return self._final_message
|
|
237
241
|
|
|
238
|
-
async def steer(
|
|
242
|
+
async def steer(
|
|
243
|
+
self,
|
|
244
|
+
input: TurnInput,
|
|
245
|
+
*,
|
|
246
|
+
responsesapi_client_metadata: Mapping[str, object] | None = None,
|
|
247
|
+
) -> TurnIdResult:
|
|
239
248
|
"""Append additional user input to the in-flight turn."""
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
249
|
+
payload: dict[str, object] = {
|
|
250
|
+
"threadId": self.thread_id,
|
|
251
|
+
"expectedTurnId": self.turn_id,
|
|
252
|
+
"input": normalize_turn_input(input),
|
|
253
|
+
}
|
|
254
|
+
if responsesapi_client_metadata is not None:
|
|
255
|
+
payload["responsesapiClientMetadata"] = dict(responsesapi_client_metadata)
|
|
256
|
+
params = protocol.TurnSteerParams.model_validate(payload)
|
|
247
257
|
return await self._thread._client.rpc.request_typed("turn/steer", params, TurnIdResult)
|
|
248
258
|
|
|
249
259
|
async def interrupt(self) -> EmptyResult:
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import concurrent.futures
|
|
7
7
|
import time
|
|
8
|
-
from collections.abc import Coroutine, Mapping
|
|
8
|
+
from collections.abc import Callable, Collection, Coroutine, Mapping
|
|
9
9
|
from contextlib import suppress
|
|
10
10
|
from threading import Thread
|
|
11
11
|
from typing import Any, TypeVar, cast
|
|
@@ -311,9 +311,13 @@ class AppServerClient(_SyncRunner):
|
|
|
311
311
|
def start_thread(
|
|
312
312
|
self,
|
|
313
313
|
options: AppServerThreadStartOptions | None = None,
|
|
314
|
+
*,
|
|
315
|
+
tools: Collection[Callable[..., object]] | None = None,
|
|
314
316
|
) -> AppServerThread:
|
|
315
317
|
return AppServerThread(
|
|
316
|
-
cast(
|
|
318
|
+
cast(
|
|
319
|
+
_AsyncThreadLike, self._run(self._async_client.start_thread(options, tools=tools))
|
|
320
|
+
),
|
|
317
321
|
self._run,
|
|
318
322
|
)
|
|
319
323
|
|