codex-python 1.122.0__tar.gz → 1.131.1__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 (41) hide show
  1. {codex_python-1.122.0 → codex_python-1.131.1}/PKG-INFO +1 -1
  2. {codex_python-1.122.0 → codex_python-1.131.1}/codex/__init__.py +1 -1
  3. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_async_client.py +30 -1
  4. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_async_services.py +0 -6
  5. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_async_threads.py +25 -0
  6. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_protocol_helpers.py +9 -3
  7. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_sync_services.py +0 -6
  8. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_sync_threads.py +18 -0
  9. {codex_python-1.122.0 → codex_python-1.131.1}/codex/protocol/types.py +2195 -570
  10. {codex_python-1.122.0 → codex_python-1.131.1}/crates/codex_native/Cargo.lock +3 -3
  11. {codex_python-1.122.0 → codex_python-1.131.1}/crates/codex_native/Cargo.toml +1 -1
  12. {codex_python-1.122.0 → codex_python-1.131.1}/LICENSE +0 -0
  13. {codex_python-1.122.0 → codex_python-1.131.1}/README.md +0 -0
  14. {codex_python-1.122.0 → codex_python-1.131.1}/codex/_binary.py +0 -0
  15. {codex_python-1.122.0 → codex_python-1.131.1}/codex/_config_types.py +0 -0
  16. {codex_python-1.122.0 → codex_python-1.131.1}/codex/_file_utils.py +0 -0
  17. {codex_python-1.122.0 → codex_python-1.131.1}/codex/_runtime.py +0 -0
  18. {codex_python-1.122.0 → codex_python-1.131.1}/codex/_turn_options.py +0 -0
  19. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/__init__.py +0 -0
  20. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_payloads.py +0 -0
  21. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_session.py +0 -0
  22. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_sync_client.py +0 -0
  23. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_sync_support.py +0 -0
  24. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/_types.py +0 -0
  25. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/errors.py +0 -0
  26. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/models.py +0 -0
  27. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/options.py +0 -0
  28. {codex_python-1.122.0 → codex_python-1.131.1}/codex/app_server/transports.py +0 -0
  29. {codex_python-1.122.0 → codex_python-1.131.1}/codex/codex.py +0 -0
  30. {codex_python-1.122.0 → codex_python-1.131.1}/codex/dynamic_tools.py +0 -0
  31. {codex_python-1.122.0 → codex_python-1.131.1}/codex/errors.py +0 -0
  32. {codex_python-1.122.0 → codex_python-1.131.1}/codex/options.py +0 -0
  33. {codex_python-1.122.0 → codex_python-1.131.1}/codex/output_schema.py +0 -0
  34. {codex_python-1.122.0 → codex_python-1.131.1}/codex/output_schema_file.py +0 -0
  35. {codex_python-1.122.0 → codex_python-1.131.1}/codex/protocol/__init__.py +0 -0
  36. {codex_python-1.122.0 → codex_python-1.131.1}/codex/py.typed +0 -0
  37. {codex_python-1.122.0 → codex_python-1.131.1}/codex/thread.py +0 -0
  38. {codex_python-1.122.0 → codex_python-1.131.1}/codex/vendor/.gitkeep +0 -0
  39. {codex_python-1.122.0 → codex_python-1.131.1}/crates/codex_native/codex/__init__.py +0 -0
  40. {codex_python-1.122.0 → codex_python-1.131.1}/crates/codex_native/src/lib.rs +0 -0
  41. {codex_python-1.122.0 → codex_python-1.131.1}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-python
3
- Version: 1.122.0
3
+ Version: 1.131.1
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3 :: Only
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -17,7 +17,7 @@ from codex.options import (
17
17
  )
18
18
  from codex.thread import CodexTurnStream, Input, Thread
19
19
 
20
- __version__ = "1.122.0"
20
+ __version__ = "1.131.1"
21
21
 
22
22
  __all__ = [
23
23
  "Codex",
@@ -22,7 +22,7 @@ from codex.app_server._async_services import (
22
22
  from codex.app_server._async_threads import AsyncAppServerThread as AsyncAppServerThread
23
23
  from codex.app_server._async_threads import AsyncTurnStream as AsyncTurnStream
24
24
  from codex.app_server._async_threads import _ThreadClient
25
- from codex.app_server._protocol_helpers import RequestHandler
25
+ from codex.app_server._protocol_helpers import Notification, RequestHandler
26
26
  from codex.app_server._session import _AsyncNotificationSubscription, _AsyncSession
27
27
  from codex.app_server.models import (
28
28
  InitializeResult,
@@ -107,6 +107,35 @@ class AsyncEventsClient:
107
107
  def subscribe(self, methods: Collection[str] | None = None) -> _AsyncNotificationSubscription:
108
108
  return self._session.subscribe_notifications(methods)
109
109
 
110
+ def subscribe_command_exec_output(self, process_id: str) -> _AsyncNotificationSubscription:
111
+ """Subscribe to `command/exec/outputDelta` notifications for one process id."""
112
+
113
+ def predicate(notification: Notification) -> bool:
114
+ return (
115
+ isinstance(notification, protocol.CommandExecOutputDeltaNotificationModel)
116
+ and notification.params.processId == process_id
117
+ )
118
+
119
+ return self._session.subscribe_notifications(
120
+ {"command/exec/outputDelta"},
121
+ predicate=predicate,
122
+ )
123
+
124
+ def subscribe_process_events(self, process_handle: str) -> _AsyncNotificationSubscription:
125
+ """Subscribe to `process/outputDelta` and `process/exited` for one process handle."""
126
+
127
+ def predicate(notification: Notification) -> bool:
128
+ if isinstance(notification, protocol.ProcessOutputDeltaNotificationModel):
129
+ return notification.params.processHandle == process_handle
130
+ if isinstance(notification, protocol.ProcessExitedNotificationModel):
131
+ return notification.params.processHandle == process_handle
132
+ return False
133
+
134
+ return self._session.subscribe_notifications(
135
+ {"process/outputDelta", "process/exited"},
136
+ predicate=predicate,
137
+ )
138
+
110
139
 
111
140
  class AsyncAppServerClient:
112
141
  """Async client for `codex app-server`."""
@@ -113,13 +113,11 @@ class AsyncSkillsClient(_AsyncServiceClient):
113
113
  *,
114
114
  cwds: Sequence[str] | None = None,
115
115
  force_reload: bool | None = None,
116
- per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
117
116
  ) -> list[SkillsListEntry]:
118
117
  return (
119
118
  await self.list_page(
120
119
  cwds=cwds,
121
120
  force_reload=force_reload,
122
- per_cwd_extra_user_roots=per_cwd_extra_user_roots,
123
121
  )
124
122
  ).data
125
123
 
@@ -128,14 +126,10 @@ class AsyncSkillsClient(_AsyncServiceClient):
128
126
  *,
129
127
  cwds: Sequence[str] | None = None,
130
128
  force_reload: bool | None = None,
131
- per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
132
129
  ) -> SkillsListResult:
133
130
  params = protocol.SkillsListParams(
134
131
  cwds=list(cwds) if cwds is not None else None,
135
132
  forceReload=force_reload,
136
- perCwdExtraUserRoots=(
137
- list(per_cwd_extra_user_roots) if per_cwd_extra_user_roots is not None else None
138
- ),
139
133
  )
140
134
  return await self._rpc.request_typed("skills/list", params, SkillsListResult)
141
135
 
@@ -37,6 +37,7 @@ _ModelT = TypeVar("_ModelT", bound=BaseModel)
37
37
  DEFAULT_REVIEW_DELIVERY = protocol.ReviewDelivery("inline")
38
38
 
39
39
  _TURN_STREAM_NOTIFICATION_METHODS = {
40
+ "error",
40
41
  "turn/started",
41
42
  "turn/completed",
42
43
  "turn/diff/updated",
@@ -54,6 +55,7 @@ _TURN_STREAM_NOTIFICATION_METHODS = {
54
55
  "item/reasoning/summaryPartAdded",
55
56
  "item/reasoning/textDelta",
56
57
  "item/commandExecution/outputDelta",
58
+ "item/commandExecution/terminalInteraction",
57
59
  "item/fileChange/outputDelta",
58
60
  "serverRequest/resolved",
59
61
  }
@@ -132,6 +134,7 @@ class AsyncTurnStream:
132
134
  self.usage: protocol.ThreadTokenUsage | None = None
133
135
  self._item_index: dict[str, int] = {}
134
136
  self._text_deltas: list[str] = []
137
+ self._retryable_error_notifications: list[protocol.ErrorNotificationModel] = []
135
138
  self._done = False
136
139
  self._closed = False
137
140
 
@@ -188,6 +191,16 @@ class AsyncTurnStream:
188
191
  raise StopAsyncIteration
189
192
  notification = await self._subscription.next()
190
193
  self._apply(notification)
194
+ if isinstance(notification, protocol.ErrorNotificationModel):
195
+ if not notification.params.willRetry:
196
+ self._done = True
197
+ await self.close()
198
+ error = notification.params.error
199
+ message = error.message
200
+ if error.additionalDetails is not None and error.additionalDetails != "":
201
+ message = f"{message}: {error.additionalDetails}"
202
+ raise AppServerTurnError(message)
203
+ self._retryable_error_notifications.append(notification)
191
204
  if isinstance(notification, protocol.TurnCompletedNotificationModel):
192
205
  self._done = True
193
206
  return notification
@@ -274,6 +287,18 @@ class AsyncTurnStream:
274
287
  """Return the streamed agent text deltas received so far."""
275
288
  return tuple(self._text_deltas)
276
289
 
290
+ @property
291
+ def retryable_error_notifications(self) -> tuple[protocol.ErrorNotificationModel, ...]:
292
+ """Return retryable turn error notifications received so far."""
293
+ return tuple(self._retryable_error_notifications)
294
+
295
+ @property
296
+ def retryable_errors(self) -> tuple[protocol.TurnError, ...]:
297
+ """Return retryable turn errors received so far."""
298
+ return tuple(
299
+ notification.params.error for notification in self._retryable_error_notifications
300
+ )
301
+
277
302
  def _apply(self, notification: Notification) -> None:
278
303
  self._apply_text_delta(notification)
279
304
  self._apply_token_usage(notification)
@@ -12,7 +12,8 @@ from codex.app_server.models import GenericNotification, GenericServerRequest
12
12
  from codex.protocol import types as protocol
13
13
 
14
14
  type RequestHandler[RequestT: BaseModel] = Callable[[RequestT], object | Awaitable[object]]
15
- Notification = BaseModel
15
+ type Notification = protocol.ServerNotificationValue | GenericNotification
16
+ type ServerRequest = protocol.ServerRequestValue | GenericServerRequest
16
17
 
17
18
 
18
19
  def method_name(message: BaseModel) -> str:
@@ -123,7 +124,7 @@ def parse_notification(message: JsonObject, *, strict: bool) -> Notification:
123
124
  raise AppServerProtocolError(_notification_error_message(message)) from exc
124
125
 
125
126
 
126
- def parse_server_request(message: JsonObject, *, strict: bool) -> BaseModel:
127
+ def parse_server_request(message: JsonObject, *, strict: bool) -> ServerRequest:
127
128
  method = message.get("method")
128
129
  try:
129
130
  return protocol.ServerRequest.model_validate(message).root
@@ -151,9 +152,10 @@ def _build_known_methods(*, root_model: type[BaseModel]) -> frozenset[str]:
151
152
  root_field = getattr(root_model, "model_fields", {}).get("root")
152
153
  if root_field is None:
153
154
  return frozenset()
155
+ annotation = _unwrap_type_alias(root_field.annotation)
154
156
  methods = {
155
157
  method
156
- for candidate in get_args(root_field.annotation)
158
+ for candidate in get_args(annotation)
157
159
  if isinstance(candidate, type) and issubclass(candidate, BaseModel)
158
160
  for method in [_candidate_method_literal(candidate)]
159
161
  if method is not None
@@ -161,6 +163,10 @@ def _build_known_methods(*, root_model: type[BaseModel]) -> frozenset[str]:
161
163
  return frozenset(methods)
162
164
 
163
165
 
166
+ def _unwrap_type_alias(annotation: object) -> object:
167
+ return getattr(annotation, "__value__", annotation)
168
+
169
+
164
170
  def _candidate_method_literal(candidate: type[BaseModel]) -> str | None:
165
171
  model_fields = getattr(candidate, "model_fields", None)
166
172
  if not isinstance(model_fields, dict) or "method" not in model_fields:
@@ -76,7 +76,6 @@ class _AsyncSkillsClientLike(Protocol):
76
76
  *,
77
77
  cwds: Sequence[str] | None = None,
78
78
  force_reload: bool | None = None,
79
- per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
80
79
  ) -> list[SkillsListEntry]: ...
81
80
 
82
81
  async def list_page(
@@ -84,7 +83,6 @@ class _AsyncSkillsClientLike(Protocol):
84
83
  *,
85
84
  cwds: Sequence[str] | None = None,
86
85
  force_reload: bool | None = None,
87
- per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
88
86
  ) -> SkillsListResult: ...
89
87
 
90
88
  async def write_config(self, *, path: str, enabled: bool) -> SkillsConfigWriteResult: ...
@@ -338,13 +336,11 @@ class _SkillsClient(_SyncRunner):
338
336
  *,
339
337
  cwds: Sequence[str] | None = None,
340
338
  force_reload: bool | None = None,
341
- per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
342
339
  ) -> list[SkillsListEntry]:
343
340
  return self._run(
344
341
  self._async_client.list(
345
342
  cwds=cwds,
346
343
  force_reload=force_reload,
347
- per_cwd_extra_user_roots=per_cwd_extra_user_roots,
348
344
  )
349
345
  )
350
346
 
@@ -353,13 +349,11 @@ class _SkillsClient(_SyncRunner):
353
349
  *,
354
350
  cwds: Sequence[str] | None = None,
355
351
  force_reload: bool | None = None,
356
- per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
357
352
  ) -> SkillsListResult:
358
353
  return self._run(
359
354
  self._async_client.list_page(
360
355
  cwds=cwds,
361
356
  force_reload=force_reload,
362
- per_cwd_extra_user_roots=per_cwd_extra_user_roots,
363
357
  )
364
358
  )
365
359
 
@@ -25,6 +25,10 @@ class _AsyncEventsClientLike(Protocol):
25
25
  methods: Collection[str] | None = None,
26
26
  ) -> _AsyncNotificationSubscription: ...
27
27
 
28
+ def subscribe_command_exec_output(self, process_id: str) -> _AsyncNotificationSubscription: ...
29
+
30
+ def subscribe_process_events(self, process_handle: str) -> _AsyncNotificationSubscription: ...
31
+
28
32
 
29
33
  class _AsyncTurnStreamLike(Protocol):
30
34
  initial_turn: protocol.Turn
@@ -157,6 +161,20 @@ class EventsClient(_SyncRunner):
157
161
  self._run,
158
162
  )
159
163
 
164
+ def subscribe_command_exec_output(self, process_id: str) -> NotificationSubscription:
165
+ """Subscribe to `command/exec/outputDelta` notifications for one process id."""
166
+ return NotificationSubscription(
167
+ self._async_events.subscribe_command_exec_output(process_id),
168
+ self._run,
169
+ )
170
+
171
+ def subscribe_process_events(self, process_handle: str) -> NotificationSubscription:
172
+ """Subscribe to `process/outputDelta` and `process/exited` for one process handle."""
173
+ return NotificationSubscription(
174
+ self._async_events.subscribe_process_events(process_handle),
175
+ self._run,
176
+ )
177
+
160
178
 
161
179
  class TurnStream(_SyncRunner):
162
180
  """Synchronous iterator over protocol-native notifications for a single turn."""