codex-python 1.131.1__tar.gz → 1.140.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 (42) hide show
  1. {codex_python-1.131.1 → codex_python-1.140.0}/PKG-INFO +1 -1
  2. {codex_python-1.131.1 → codex_python-1.140.0}/codex/__init__.py +1 -1
  3. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_async_client.py +3 -1
  4. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_async_services.py +74 -0
  5. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_async_threads.py +16 -5
  6. codex_python-1.140.0/codex/app_server/_payloads.py +102 -0
  7. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_sync_client.py +2 -0
  8. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_sync_services.py +83 -0
  9. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_sync_threads.py +24 -5
  10. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/options.py +66 -15
  11. {codex_python-1.131.1 → codex_python-1.140.0}/codex/options.py +52 -4
  12. {codex_python-1.131.1 → codex_python-1.140.0}/codex/protocol/types.py +1505 -725
  13. {codex_python-1.131.1 → codex_python-1.140.0}/crates/codex_native/Cargo.lock +9 -9
  14. {codex_python-1.131.1 → codex_python-1.140.0}/crates/codex_native/Cargo.toml +1 -1
  15. codex_python-1.131.1/codex/app_server/_payloads.py +0 -59
  16. {codex_python-1.131.1 → codex_python-1.140.0}/LICENSE +0 -0
  17. {codex_python-1.131.1 → codex_python-1.140.0}/README.md +0 -0
  18. {codex_python-1.131.1 → codex_python-1.140.0}/codex/_binary.py +0 -0
  19. {codex_python-1.131.1 → codex_python-1.140.0}/codex/_config_types.py +0 -0
  20. {codex_python-1.131.1 → codex_python-1.140.0}/codex/_file_utils.py +0 -0
  21. {codex_python-1.131.1 → codex_python-1.140.0}/codex/_runtime.py +0 -0
  22. {codex_python-1.131.1 → codex_python-1.140.0}/codex/_turn_options.py +0 -0
  23. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/__init__.py +0 -0
  24. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_protocol_helpers.py +0 -0
  25. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_session.py +0 -0
  26. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_sync_support.py +0 -0
  27. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/_types.py +0 -0
  28. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/errors.py +0 -0
  29. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/models.py +0 -0
  30. {codex_python-1.131.1 → codex_python-1.140.0}/codex/app_server/transports.py +0 -0
  31. {codex_python-1.131.1 → codex_python-1.140.0}/codex/codex.py +0 -0
  32. {codex_python-1.131.1 → codex_python-1.140.0}/codex/dynamic_tools.py +0 -0
  33. {codex_python-1.131.1 → codex_python-1.140.0}/codex/errors.py +0 -0
  34. {codex_python-1.131.1 → codex_python-1.140.0}/codex/output_schema.py +0 -0
  35. {codex_python-1.131.1 → codex_python-1.140.0}/codex/output_schema_file.py +0 -0
  36. {codex_python-1.131.1 → codex_python-1.140.0}/codex/protocol/__init__.py +0 -0
  37. {codex_python-1.131.1 → codex_python-1.140.0}/codex/py.typed +0 -0
  38. {codex_python-1.131.1 → codex_python-1.140.0}/codex/thread.py +0 -0
  39. {codex_python-1.131.1 → codex_python-1.140.0}/codex/vendor/.gitkeep +0 -0
  40. {codex_python-1.131.1 → codex_python-1.140.0}/crates/codex_native/codex/__init__.py +0 -0
  41. {codex_python-1.131.1 → codex_python-1.140.0}/crates/codex_native/src/lib.rs +0 -0
  42. {codex_python-1.131.1 → codex_python-1.140.0}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-python
3
- Version: 1.131.1
3
+ Version: 1.140.0
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.131.1"
20
+ __version__ = "1.140.0"
21
21
 
22
22
  __all__ = [
23
23
  "Codex",
@@ -14,6 +14,7 @@ from codex.app_server._async_services import (
14
14
  AsyncConfigClient,
15
15
  AsyncExternalAgentConfigClient,
16
16
  AsyncFeedbackClient,
17
+ AsyncFsClient,
17
18
  AsyncMcpServersClient,
18
19
  AsyncModelsClient,
19
20
  AsyncSkillsClient,
@@ -151,7 +152,8 @@ class AsyncAppServerClient:
151
152
  self.events = AsyncEventsClient(self._session)
152
153
  self.models = AsyncModelsClient(self.rpc)
153
154
  self.apps = AsyncAppsClient(self.rpc)
154
- self.skills = AsyncSkillsClient(self.rpc)
155
+ self.fs = AsyncFsClient(self.rpc)
156
+ self.skills = AsyncSkillsClient(self.rpc, self.fs)
155
157
  self.account = AsyncAccountClient(self.rpc)
156
158
  self.config = AsyncConfigClient(self.rpc)
157
159
  self.mcp_servers = AsyncMcpServersClient(self.rpc)
@@ -1,10 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import base64
3
4
  from collections.abc import Mapping, Sequence
4
5
  from typing import Any, Protocol, TypeVar
5
6
 
6
7
  from pydantic import BaseModel
7
8
 
9
+ from codex.app_server._payloads import skill_input
8
10
  from codex.app_server.models import (
9
11
  AccountCancelLoginResult,
10
12
  AccountRateLimitsResult,
@@ -49,6 +51,16 @@ class _AsyncServiceClient:
49
51
  self._rpc = rpc
50
52
 
51
53
 
54
+ def _skill_markdown_path(directory: str) -> str:
55
+ if directory == "":
56
+ raise ValueError("Skill directory cannot be empty.")
57
+ if directory.endswith("/") or directory.endswith("\\"):
58
+ return f"{directory}SKILL.md"
59
+ if "\\" in directory and "/" not in directory:
60
+ return f"{directory}\\SKILL.md"
61
+ return f"{directory}/SKILL.md"
62
+
63
+
52
64
  class AsyncModelsClient(_AsyncServiceClient):
53
65
  async def list(
54
66
  self,
@@ -107,7 +119,50 @@ class AsyncAppsClient(_AsyncServiceClient):
107
119
  return await self._rpc.request_typed("app/list", params, AppListResult)
108
120
 
109
121
 
122
+ class AsyncFsClient(_AsyncServiceClient):
123
+ async def create_directory(
124
+ self,
125
+ *,
126
+ path: str,
127
+ recursive: bool | None = True,
128
+ ) -> protocol.FsCreateDirectoryResponse:
129
+ params = protocol.FsCreateDirectoryParams(
130
+ path=protocol.AbsolutePathBuf(path),
131
+ recursive=recursive,
132
+ )
133
+ return await self._rpc.request_typed(
134
+ "fs/createDirectory",
135
+ params,
136
+ protocol.FsCreateDirectoryResponse,
137
+ )
138
+
139
+ async def write_file(
140
+ self,
141
+ *,
142
+ path: str,
143
+ data: str | bytes,
144
+ encoding: str = "utf-8",
145
+ ) -> protocol.FsWriteFileResponse:
146
+ raw_data = data.encode(encoding) if isinstance(data, str) else data
147
+ params = protocol.FsWriteFileParams(
148
+ path=protocol.AbsolutePathBuf(path),
149
+ dataBase64=base64.b64encode(raw_data).decode("ascii"),
150
+ )
151
+ return await self._rpc.request_typed(
152
+ "fs/writeFile",
153
+ params,
154
+ protocol.FsWriteFileResponse,
155
+ )
156
+
157
+
110
158
  class AsyncSkillsClient(_AsyncServiceClient):
159
+ def __init__(self, rpc: _TypedRpcClient, fs: AsyncFsClient) -> None:
160
+ super().__init__(rpc)
161
+ self._fs = fs
162
+
163
+ def input(self, *, name: str, path: str) -> protocol.SkillUserInput:
164
+ return skill_input(name=name, path=path)
165
+
111
166
  async def list(
112
167
  self,
113
168
  *,
@@ -133,6 +188,9 @@ class AsyncSkillsClient(_AsyncServiceClient):
133
188
  )
134
189
  return await self._rpc.request_typed("skills/list", params, SkillsListResult)
135
190
 
191
+ async def reload(self, *, cwds: Sequence[str] | None = None) -> Sequence[SkillsListEntry]:
192
+ return await self.list(cwds=cwds, force_reload=True)
193
+
136
194
  async def write_config(self, *, path: str, enabled: bool) -> SkillsConfigWriteResult:
137
195
  params = protocol.SkillsConfigWriteParams(
138
196
  path=protocol.AbsolutePathBuf(path),
@@ -140,6 +198,20 @@ class AsyncSkillsClient(_AsyncServiceClient):
140
198
  )
141
199
  return await self._rpc.request_typed("skills/config/write", params, SkillsConfigWriteResult)
142
200
 
201
+ async def write_skill(
202
+ self,
203
+ *,
204
+ name: str,
205
+ directory: str,
206
+ instructions: str | bytes,
207
+ reload_cwds: Sequence[str] | None = None,
208
+ ) -> protocol.SkillUserInput:
209
+ skill_path = _skill_markdown_path(directory)
210
+ await self._fs.create_directory(path=directory, recursive=True)
211
+ await self._fs.write_file(path=skill_path, data=instructions)
212
+ await self.reload(cwds=reload_cwds)
213
+ return self.input(name=name, path=skill_path)
214
+
143
215
 
144
216
  class AsyncAccountClient(_AsyncServiceClient):
145
217
  async def read(self, *, refresh_token: bool | None = None) -> AccountReadResult:
@@ -324,6 +396,7 @@ class AsyncCommandClient(_AsyncServiceClient):
324
396
  disable_timeout: bool | None = None,
325
397
  env: Mapping[str, object | None] | None = None,
326
398
  output_bytes_cap: int | None = None,
399
+ permission_profile: str | None = None,
327
400
  process_id: str | None = None,
328
401
  sandbox_policy: protocol.SandboxPolicy | None = None,
329
402
  size: protocol.CommandExecTerminalSize | None = None,
@@ -339,6 +412,7 @@ class AsyncCommandClient(_AsyncServiceClient):
339
412
  disableTimeout=disable_timeout,
340
413
  env=dict(env) if env is not None else None,
341
414
  outputBytesCap=output_bytes_cap,
415
+ permissionProfile=permission_profile,
342
416
  processId=process_id,
343
417
  sandboxPolicy=sandbox_policy,
344
418
  size=size,
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
- from collections.abc import Callable, Collection, Mapping
6
+ from collections.abc import Callable, Collection, Mapping, Sequence
7
7
  from dataclasses import dataclass
8
8
  from typing import Protocol, TypeVar, cast
9
9
 
@@ -41,6 +41,7 @@ _TURN_STREAM_NOTIFICATION_METHODS = {
41
41
  "turn/started",
42
42
  "turn/completed",
43
43
  "turn/diff/updated",
44
+ "turn/moderationMetadata",
44
45
  "turn/plan/updated",
45
46
  "hook/started",
46
47
  "hook/completed",
@@ -257,12 +258,13 @@ class AsyncTurnStream:
257
258
  input: TurnInput,
258
259
  *,
259
260
  responsesapi_client_metadata: Mapping[str, object] | None = None,
261
+ skills: Sequence[protocol.SkillUserInput] | None = None,
260
262
  ) -> TurnIdResult:
261
263
  """Append additional user input to the in-flight turn."""
262
264
  payload: dict[str, object] = {
263
265
  "threadId": self.thread_id,
264
266
  "expectedTurnId": self.turn_id,
265
- "input": normalize_turn_input(input),
267
+ "input": normalize_turn_input(input, skills=skills),
266
268
  }
267
269
  if responsesapi_client_metadata is not None:
268
270
  payload["responsesapiClientMetadata"] = dict(responsesapi_client_metadata)
@@ -416,11 +418,13 @@ class AsyncAppServerThread:
416
418
  self,
417
419
  input: TurnInput,
418
420
  options: AppServerTurnOptions | None = None,
421
+ *,
422
+ skills: Sequence[protocol.SkillUserInput] | None = None,
419
423
  ) -> AsyncTurnStream:
420
424
  """Start a turn and return the protocol-native notification stream."""
421
425
  payload = (options or AppServerTurnOptions()).to_params(
422
426
  thread_id=self.id,
423
- input=normalize_turn_input(input),
427
+ input=normalize_turn_input(input, skills=skills),
424
428
  )
425
429
  return await AsyncTurnStream.start(self, payload)
426
430
 
@@ -428,8 +432,10 @@ class AsyncAppServerThread:
428
432
  self,
429
433
  input: TurnInput,
430
434
  options: AppServerTurnOptions | None = None,
435
+ *,
436
+ skills: Sequence[protocol.SkillUserInput] | None = None,
431
437
  ) -> str:
432
- stream = await self.run(input, options)
438
+ stream = await self.run(input, options, skills=skills)
433
439
  await stream.wait()
434
440
  stream.raise_for_terminal_status()
435
441
  return stream.final_text
@@ -438,8 +444,10 @@ class AsyncAppServerThread:
438
444
  self,
439
445
  input: TurnInput,
440
446
  options: AppServerTurnOptions | None = None,
447
+ *,
448
+ skills: Sequence[protocol.SkillUserInput] | None = None,
441
449
  ) -> object:
442
- stream = await self.run(input, options)
450
+ stream = await self.run(input, options, skills=skills)
443
451
  await stream.wait()
444
452
  stream.raise_for_terminal_status()
445
453
  return stream.final_json()
@@ -449,6 +457,8 @@ class AsyncAppServerThread:
449
457
  input: TurnInput,
450
458
  model_type: type[_ModelT],
451
459
  options: AppServerTurnOptions | None = None,
460
+ *,
461
+ skills: Sequence[protocol.SkillUserInput] | None = None,
452
462
  ) -> _ModelT:
453
463
  """Run a turn and validate the final assistant text with `model_type`."""
454
464
  stream = await self.run(
@@ -458,6 +468,7 @@ class AsyncAppServerThread:
458
468
  model_type,
459
469
  owner="AppServerThread.run_model()",
460
470
  ),
471
+ skills=skills,
461
472
  )
462
473
  await stream.wait()
463
474
  stream.raise_for_terminal_status()
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping, Sequence
4
+ from typing import Any, cast
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from codex.app_server._types import JsonObject
9
+ from codex.protocol import types as protocol
10
+
11
+ InputItem = (
12
+ str
13
+ | Mapping[str, Any]
14
+ | protocol.UserInput
15
+ | protocol.TextUserInput
16
+ | protocol.ImageUserInput
17
+ | protocol.LocalImageUserInput
18
+ | protocol.SkillUserInput
19
+ | protocol.MentionUserInput
20
+ )
21
+ TurnInput = InputItem | Sequence[InputItem]
22
+
23
+ type ParamsModel = BaseModel
24
+
25
+
26
+ def serialize_value(value: object) -> object:
27
+ if isinstance(value, BaseModel):
28
+ return value.model_dump(
29
+ mode="json",
30
+ by_alias=True,
31
+ exclude_none=True,
32
+ exclude_unset=True,
33
+ )
34
+ if isinstance(value, type) and issubclass(value, BaseModel):
35
+ return value.model_json_schema()
36
+ if isinstance(value, list):
37
+ return [serialize_value(item) for item in value]
38
+ if isinstance(value, tuple):
39
+ return [serialize_value(item) for item in value]
40
+ if isinstance(value, Mapping):
41
+ return {key: serialize_value(item) for key, item in value.items()}
42
+ return value
43
+
44
+
45
+ def normalize_input_item(item: InputItem) -> JsonObject:
46
+ if isinstance(item, str):
47
+ return {"type": "text", "text": item}
48
+ serialized = serialize_value(item)
49
+ if not isinstance(serialized, dict):
50
+ raise TypeError(f"Input item must serialize to an object, got {type(serialized).__name__}")
51
+ return cast(JsonObject, serialized)
52
+
53
+
54
+ def skill_input(*, name: str, path: str) -> protocol.SkillUserInput:
55
+ if name == "":
56
+ raise ValueError("Skill name cannot be empty.")
57
+ if name.startswith("$"):
58
+ raise ValueError("Skill name should not include the '$' activation marker.")
59
+ if any(character.isspace() for character in name):
60
+ raise ValueError("Skill name cannot contain whitespace.")
61
+ if "/" in name or "\\" in name:
62
+ raise ValueError("Skill name cannot contain path separators.")
63
+ if path == "":
64
+ raise ValueError("Skill path cannot be empty.")
65
+ return protocol.SkillUserInput(
66
+ type=protocol.SkillUserInputType("skill"),
67
+ name=name,
68
+ path=path,
69
+ )
70
+
71
+
72
+ def _add_skill_markers(
73
+ items: list[JsonObject],
74
+ skills: Sequence[protocol.SkillUserInput],
75
+ ) -> list[JsonObject]:
76
+ if not skills:
77
+ return items
78
+ markers = " ".join(f"${skill.name}" for skill in skills)
79
+ for item in items:
80
+ if item.get("type") != "text":
81
+ continue
82
+ text = item.get("text")
83
+ if isinstance(text, str):
84
+ item["text"] = f"{markers}\n\n{text}"
85
+ return items
86
+ return [{"type": "text", "text": markers}, *items]
87
+
88
+
89
+ def normalize_turn_input(
90
+ value: TurnInput,
91
+ *,
92
+ skills: Sequence[protocol.SkillUserInput] | None = None,
93
+ ) -> list[JsonObject]:
94
+ if isinstance(value, str):
95
+ items = [{"type": "text", "text": value}]
96
+ elif isinstance(value, Sequence):
97
+ items = [normalize_input_item(item) for item in value]
98
+ else:
99
+ items = [normalize_input_item(value)]
100
+ if skills is None:
101
+ return items
102
+ return _add_skill_markers(items, skills) + [normalize_input_item(skill) for skill in skills]
@@ -21,6 +21,7 @@ from codex.app_server._sync_services import (
21
21
  _ConfigClient,
22
22
  _ExternalAgentConfigClient,
23
23
  _FeedbackClient,
24
+ _FsClient,
24
25
  _McpServersClient,
25
26
  _ModelsClient,
26
27
  _SkillsClient,
@@ -240,6 +241,7 @@ class AppServerClient(_SyncRunner):
240
241
  self.mcp_servers = _McpServersClient(async_client.mcp_servers, self._run)
241
242
  self.feedback = _FeedbackClient(async_client.feedback, self._run)
242
243
  self.command = _CommandClient(async_client.command, self._run)
244
+ self.fs = _FsClient(async_client.fs, self._run)
243
245
  self.external_agent_config = _ExternalAgentConfigClient(
244
246
  async_client.external_agent_config,
245
247
  self._run,
@@ -71,6 +71,8 @@ class _AsyncAppsClientLike(Protocol):
71
71
 
72
72
 
73
73
  class _AsyncSkillsClientLike(Protocol):
74
+ def input(self, *, name: str, path: str) -> protocol.SkillUserInput: ...
75
+
74
76
  async def list(
75
77
  self,
76
78
  *,
@@ -85,8 +87,36 @@ class _AsyncSkillsClientLike(Protocol):
85
87
  force_reload: bool | None = None,
86
88
  ) -> SkillsListResult: ...
87
89
 
90
+ async def reload(self, *, cwds: Sequence[str] | None = None) -> Sequence[SkillsListEntry]: ...
91
+
88
92
  async def write_config(self, *, path: str, enabled: bool) -> SkillsConfigWriteResult: ...
89
93
 
94
+ async def write_skill(
95
+ self,
96
+ *,
97
+ name: str,
98
+ directory: str,
99
+ instructions: str | bytes,
100
+ reload_cwds: Sequence[str] | None = None,
101
+ ) -> protocol.SkillUserInput: ...
102
+
103
+
104
+ class _AsyncFsClientLike(Protocol):
105
+ async def create_directory(
106
+ self,
107
+ *,
108
+ path: str,
109
+ recursive: bool | None = True,
110
+ ) -> protocol.FsCreateDirectoryResponse: ...
111
+
112
+ async def write_file(
113
+ self,
114
+ *,
115
+ path: str,
116
+ data: str | bytes,
117
+ encoding: str = "utf-8",
118
+ ) -> protocol.FsWriteFileResponse: ...
119
+
90
120
 
91
121
  class _AsyncAccountClientLike(Protocol):
92
122
  async def read(self, *, refresh_token: bool | None = None) -> AccountReadResult: ...
@@ -187,6 +217,7 @@ class _AsyncCommandClientLike(Protocol):
187
217
  disable_timeout: bool | None = None,
188
218
  env: Mapping[str, object | None] | None = None,
189
219
  output_bytes_cap: int | None = None,
220
+ permission_profile: str | None = None,
190
221
  process_id: str | None = None,
191
222
  sandbox_policy: protocol.SandboxPolicy | None = None,
192
223
  size: protocol.CommandExecTerminalSize | None = None,
@@ -331,6 +362,9 @@ class _SkillsClient(_SyncRunner):
331
362
  super().__init__(runner)
332
363
  self._async_client = async_client
333
364
 
365
+ def input(self, *, name: str, path: str) -> protocol.SkillUserInput:
366
+ return self._async_client.input(name=name, path=path)
367
+
334
368
  def list(
335
369
  self,
336
370
  *,
@@ -357,9 +391,56 @@ class _SkillsClient(_SyncRunner):
357
391
  )
358
392
  )
359
393
 
394
+ def reload(self, *, cwds: Sequence[str] | None = None) -> Sequence[SkillsListEntry]:
395
+ return self._run(self._async_client.reload(cwds=cwds))
396
+
360
397
  def write_config(self, *, path: str, enabled: bool) -> SkillsConfigWriteResult:
361
398
  return self._run(self._async_client.write_config(path=path, enabled=enabled))
362
399
 
400
+ def write_skill(
401
+ self,
402
+ *,
403
+ name: str,
404
+ directory: str,
405
+ instructions: str | bytes,
406
+ reload_cwds: Sequence[str] | None = None,
407
+ ) -> protocol.SkillUserInput:
408
+ return self._run(
409
+ self._async_client.write_skill(
410
+ name=name,
411
+ directory=directory,
412
+ instructions=instructions,
413
+ reload_cwds=reload_cwds,
414
+ )
415
+ )
416
+
417
+
418
+ class _FsClient(_SyncRunner):
419
+ def __init__(
420
+ self,
421
+ async_client: _AsyncFsClientLike,
422
+ runner: Callable[[Coroutine[Any, Any, Any]], Any],
423
+ ) -> None:
424
+ super().__init__(runner)
425
+ self._async_client = async_client
426
+
427
+ def create_directory(
428
+ self,
429
+ *,
430
+ path: str,
431
+ recursive: bool | None = True,
432
+ ) -> protocol.FsCreateDirectoryResponse:
433
+ return self._run(self._async_client.create_directory(path=path, recursive=recursive))
434
+
435
+ def write_file(
436
+ self,
437
+ *,
438
+ path: str,
439
+ data: str | bytes,
440
+ encoding: str = "utf-8",
441
+ ) -> protocol.FsWriteFileResponse:
442
+ return self._run(self._async_client.write_file(path=path, data=data, encoding=encoding))
443
+
363
444
 
364
445
  class _AccountClient(_SyncRunner):
365
446
  def __init__(
@@ -554,6 +635,7 @@ class _CommandClient(_SyncRunner):
554
635
  disable_timeout: bool | None = None,
555
636
  env: Mapping[str, object | None] | None = None,
556
637
  output_bytes_cap: int | None = None,
638
+ permission_profile: str | None = None,
557
639
  process_id: str | None = None,
558
640
  sandbox_policy: protocol.SandboxPolicy | None = None,
559
641
  size: protocol.CommandExecTerminalSize | None = None,
@@ -570,6 +652,7 @@ class _CommandClient(_SyncRunner):
570
652
  disable_timeout=disable_timeout,
571
653
  env=env,
572
654
  output_bytes_cap=output_bytes_cap,
655
+ permission_profile=permission_profile,
573
656
  process_id=process_id,
574
657
  sandbox_policy=sandbox_policy,
575
658
  size=size,
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from collections.abc import Callable, Collection, Coroutine, Mapping
5
+ from collections.abc import Callable, Collection, Coroutine, Mapping, Sequence
6
6
  from typing import Any, Protocol, TypeVar
7
7
 
8
8
  from pydantic import BaseModel
@@ -54,6 +54,7 @@ class _AsyncTurnStreamLike(Protocol):
54
54
  input: TurnInput,
55
55
  *,
56
56
  responsesapi_client_metadata: Mapping[str, object] | None = None,
57
+ skills: Sequence[protocol.SkillUserInput] | None = None,
57
58
  ) -> TurnIdResult: ...
58
59
 
59
60
  async def interrupt(self) -> EmptyResult: ...
@@ -74,18 +75,24 @@ class _AsyncThreadLike(Protocol):
74
75
  self,
75
76
  input: TurnInput,
76
77
  options: AppServerTurnOptions | None = None,
78
+ *,
79
+ skills: Sequence[protocol.SkillUserInput] | None = None,
77
80
  ) -> _AsyncTurnStreamLike: ...
78
81
 
79
82
  async def run_text(
80
83
  self,
81
84
  input: TurnInput,
82
85
  options: AppServerTurnOptions | None = None,
86
+ *,
87
+ skills: Sequence[protocol.SkillUserInput] | None = None,
83
88
  ) -> str: ...
84
89
 
85
90
  async def run_json(
86
91
  self,
87
92
  input: TurnInput,
88
93
  options: AppServerTurnOptions | None = None,
94
+ *,
95
+ skills: Sequence[protocol.SkillUserInput] | None = None,
89
96
  ) -> object: ...
90
97
 
91
98
  async def run_model(
@@ -93,6 +100,8 @@ class _AsyncThreadLike(Protocol):
93
100
  input: TurnInput,
94
101
  model_type: type[_ModelT],
95
102
  options: AppServerTurnOptions | None = None,
103
+ *,
104
+ skills: Sequence[protocol.SkillUserInput] | None = None,
96
105
  ) -> _ModelT: ...
97
106
 
98
107
  async def review(
@@ -245,11 +254,13 @@ class TurnStream(_SyncRunner):
245
254
  input: TurnInput,
246
255
  *,
247
256
  responsesapi_client_metadata: Mapping[str, object] | None = None,
257
+ skills: Sequence[protocol.SkillUserInput] | None = None,
248
258
  ) -> TurnIdResult:
249
259
  return self._run(
250
260
  self._async_stream.steer(
251
261
  input,
252
262
  responsesapi_client_metadata=responsesapi_client_metadata,
263
+ skills=skills,
253
264
  )
254
265
  )
255
266
 
@@ -286,9 +297,11 @@ class AppServerThread(_SyncRunner):
286
297
  self,
287
298
  input: TurnInput,
288
299
  options: AppServerTurnOptions | None = None,
300
+ *,
301
+ skills: Sequence[protocol.SkillUserInput] | None = None,
289
302
  ) -> TurnStream:
290
303
  return TurnStream(
291
- self._run(self._async_thread.run(input, options)),
304
+ self._run(self._async_thread.run(input, options, skills=skills)),
292
305
  self._run,
293
306
  )
294
307
 
@@ -296,23 +309,29 @@ class AppServerThread(_SyncRunner):
296
309
  self,
297
310
  input: TurnInput,
298
311
  options: AppServerTurnOptions | None = None,
312
+ *,
313
+ skills: Sequence[protocol.SkillUserInput] | None = None,
299
314
  ) -> str:
300
- return self._run(self._async_thread.run_text(input, options))
315
+ return self._run(self._async_thread.run_text(input, options, skills=skills))
301
316
 
302
317
  def run_json(
303
318
  self,
304
319
  input: TurnInput,
305
320
  options: AppServerTurnOptions | None = None,
321
+ *,
322
+ skills: Sequence[protocol.SkillUserInput] | None = None,
306
323
  ) -> object:
307
- return self._run(self._async_thread.run_json(input, options))
324
+ return self._run(self._async_thread.run_json(input, options, skills=skills))
308
325
 
309
326
  def run_model(
310
327
  self,
311
328
  input: TurnInput,
312
329
  model_type: type[_ModelT],
313
330
  options: AppServerTurnOptions | None = None,
331
+ *,
332
+ skills: Sequence[protocol.SkillUserInput] | None = None,
314
333
  ) -> _ModelT:
315
- return self._run(self._async_thread.run_model(input, model_type, options))
334
+ return self._run(self._async_thread.run_model(input, model_type, options, skills=skills))
316
335
 
317
336
  def review(
318
337
  self,