Skip to content

Commit 99466fc

Browse files
authored
chore(app-server): update Codex protocol to 0.122 (#11)
1 parent 44615bd commit 99466fc

18 files changed

Lines changed: 6418 additions & 4243 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
- name: Fetch bundled codex binary for integration test
7070
if: ${{ steps.integration.outputs.enabled == 'true' }}
7171
env:
72-
CODEX_BINARY_RELEASE_TAG: ${{ vars.CODEX_BINARY_RELEASE_TAG || 'latest' }}
72+
CODEX_BINARY_RELEASE_TAG: ${{ vars.CODEX_BINARY_RELEASE_TAG || 'rust-v0.122.0' }}
7373
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
7474
run: |
7575
set -euo pipefail

.github/workflows/release-published.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ jobs:
243243
- name: Fetch bundled codex binaries
244244
shell: bash
245245
env:
246-
CODEX_BINARY_RELEASE_TAG: ${{ vars.CODEX_BINARY_RELEASE_TAG || 'latest' }}
246+
CODEX_BINARY_RELEASE_TAG: ${{ vars.CODEX_BINARY_RELEASE_TAG || 'rust-v0.122.0' }}
247247
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
248248
run: |
249249
set -euo pipefail

codex/app_server/_async_services.py

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
ModelInfo,
2727
ModelListResult,
2828
SkillsConfigWriteResult,
29+
SkillsListEntry,
2930
SkillsListResult,
3031
WindowsSandboxSetupStartResult,
3132
)
@@ -113,7 +114,7 @@ async def list(
113114
cwds: Sequence[str] | None = None,
114115
force_reload: bool | None = None,
115116
per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
116-
) -> list[protocol.SkillsListEntry]:
117+
) -> list[SkillsListEntry]:
117118
return (
118119
await self.list_page(
119120
cwds=cwds,
@@ -139,7 +140,10 @@ async def list_page(
139140
return await self._rpc.request_typed("skills/list", params, SkillsListResult)
140141

141142
async def write_config(self, *, path: str, enabled: bool) -> SkillsConfigWriteResult:
142-
params = protocol.SkillsConfigWriteParams(path=path, enabled=enabled)
143+
params = protocol.SkillsConfigWriteParams(
144+
path=protocol.AbsolutePathBuf(path),
145+
enabled=enabled,
146+
)
143147
return await self._rpc.request_typed("skills/config/write", params, SkillsConfigWriteResult)
144148

145149

@@ -322,19 +326,80 @@ async def execute(
322326
*,
323327
command: Sequence[str],
324328
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,
325334
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,
326338
timeout_ms: int | None = None,
339+
tty: bool | None = None,
327340
) -> CommandExecResult:
328341
params = protocol.CommandExecParams(
329342
command=list(command),
330343
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,
331349
sandboxPolicy=sandbox_policy,
350+
size=size,
351+
streamStdin=stream_stdin,
352+
streamStdoutStderr=stream_stdout_stderr,
332353
timeoutMs=timeout_ms,
354+
tty=tty,
333355
)
334356
return await self._rpc.request_typed("command/exec", params, CommandExecResult)
335357

336358
exec = execute
337359

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+
338403

339404
class AsyncExternalAgentConfigClient(_AsyncServiceClient):
340405
async def detect(
@@ -369,7 +434,10 @@ async def setup_start(
369434
mode: protocol.WindowsSandboxSetupMode,
370435
cwd: str | None = None,
371436
) -> WindowsSandboxSetupStartResult:
372-
params = protocol.WindowsSandboxSetupStartParams(cwd=cwd, mode=mode)
437+
params = protocol.WindowsSandboxSetupStartParams(
438+
cwd=protocol.AbsolutePathBuf(cwd) if cwd is not None else None,
439+
mode=mode,
440+
)
373441
return await self._rpc.request_typed(
374442
"windowsSandbox/setupStart",
375443
params,

codex/app_server/_async_threads.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,13 @@
4141
"turn/completed",
4242
"turn/diff/updated",
4343
"turn/plan/updated",
44+
"hook/started",
45+
"hook/completed",
4446
"thread/tokenUsage/updated",
4547
"item/started",
4648
"item/completed",
49+
"item/autoApprovalReview/started",
50+
"item/autoApprovalReview/completed",
4751
"item/agentMessage/delta",
4852
"item/plan/delta",
4953
"item/reasoning/summaryTextDelta",
@@ -235,15 +239,21 @@ def final_message(self) -> protocol.AgentMessageThreadItem | None:
235239
self._require_terminal_turn()
236240
return self._final_message
237241

238-
async def steer(self, input: TurnInput) -> TurnIdResult:
242+
async def steer(
243+
self,
244+
input: TurnInput,
245+
*,
246+
responsesapi_client_metadata: Mapping[str, object] | None = None,
247+
) -> TurnIdResult:
239248
"""Append additional user input to the in-flight turn."""
240-
params = protocol.TurnSteerParams.model_validate(
241-
{
242-
"threadId": self.thread_id,
243-
"expectedTurnId": self.turn_id,
244-
"input": normalize_turn_input(input),
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)
247257
return await self._thread._client.rpc.request_typed("turn/steer", params, TurnIdResult)
248258

249259
async def interrupt(self) -> EmptyResult:

codex/app_server/_sync_services.py

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from collections.abc import Callable, Coroutine, Sequence
3+
from collections.abc import Callable, Coroutine, Mapping, Sequence
44
from typing import Any, Protocol
55

66
from codex.app_server._sync_support import _SyncRunner
@@ -25,6 +25,7 @@
2525
ModelInfo,
2626
ModelListResult,
2727
SkillsConfigWriteResult,
28+
SkillsListEntry,
2829
SkillsListResult,
2930
WindowsSandboxSetupStartResult,
3031
)
@@ -76,7 +77,7 @@ async def list(
7677
cwds: Sequence[str] | None = None,
7778
force_reload: bool | None = None,
7879
per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
79-
) -> list[protocol.SkillsListEntry]: ...
80+
) -> list[SkillsListEntry]: ...
8081

8182
async def list_page(
8283
self,
@@ -184,10 +185,36 @@ async def execute(
184185
*,
185186
command: Sequence[str],
186187
cwd: str | None = None,
188+
disable_output_cap: bool | None = None,
189+
disable_timeout: bool | None = None,
190+
env: Mapping[str, object | None] | None = None,
191+
output_bytes_cap: int | None = None,
192+
process_id: str | None = None,
187193
sandbox_policy: protocol.SandboxPolicy | None = None,
194+
size: protocol.CommandExecTerminalSize | None = None,
195+
stream_stdin: bool | None = None,
196+
stream_stdout_stderr: bool | None = None,
188197
timeout_ms: int | None = None,
198+
tty: bool | None = None,
189199
) -> CommandExecResult: ...
190200

201+
async def write_stdin(
202+
self,
203+
*,
204+
process_id: str,
205+
close_stdin: bool | None = None,
206+
delta_base64: str | None = None,
207+
) -> EmptyResult: ...
208+
209+
async def resize_terminal(
210+
self,
211+
*,
212+
process_id: str,
213+
size: protocol.CommandExecTerminalSize,
214+
) -> EmptyResult: ...
215+
216+
async def terminate_process(self, *, process_id: str) -> EmptyResult: ...
217+
191218

192219
class _AsyncExternalAgentConfigClientLike(Protocol):
193220
async def detect(
@@ -312,7 +339,7 @@ def list(
312339
cwds: Sequence[str] | None = None,
313340
force_reload: bool | None = None,
314341
per_cwd_extra_user_roots: Sequence[protocol.SkillsListExtraRootsForCwd] | None = None,
315-
) -> list[protocol.SkillsListEntry]:
342+
) -> list[SkillsListEntry]:
316343
return self._run(
317344
self._async_client.list(
318345
cwds=cwds,
@@ -529,20 +556,80 @@ def execute(
529556
*,
530557
command: Sequence[str],
531558
cwd: str | None = None,
559+
disable_output_cap: bool | None = None,
560+
disable_timeout: bool | None = None,
561+
env: Mapping[str, object | None] | None = None,
562+
output_bytes_cap: int | None = None,
563+
process_id: str | None = None,
532564
sandbox_policy: protocol.SandboxPolicy | None = None,
565+
size: protocol.CommandExecTerminalSize | None = None,
566+
stream_stdin: bool | None = None,
567+
stream_stdout_stderr: bool | None = None,
533568
timeout_ms: int | None = None,
569+
tty: bool | None = None,
534570
) -> CommandExecResult:
535571
return self._run(
536572
self._async_client.execute(
537573
command=command,
538574
cwd=cwd,
575+
disable_output_cap=disable_output_cap,
576+
disable_timeout=disable_timeout,
577+
env=env,
578+
output_bytes_cap=output_bytes_cap,
579+
process_id=process_id,
539580
sandbox_policy=sandbox_policy,
581+
size=size,
582+
stream_stdin=stream_stdin,
583+
stream_stdout_stderr=stream_stdout_stderr,
540584
timeout_ms=timeout_ms,
585+
tty=tty,
541586
)
542587
)
543588

544589
exec = execute
545590

591+
def write_stdin(
592+
self,
593+
*,
594+
process_id: str,
595+
close_stdin: bool | None = None,
596+
delta_base64: str | None = None,
597+
) -> EmptyResult:
598+
"""Write stdin bytes to a running `command/exec` process or close stdin.
599+
600+
This wraps the app-server `command/exec/write` request. `delta_base64`
601+
is optional base64-encoded stdin data; `close_stdin` closes the
602+
process stdin after the optional write.
603+
"""
604+
return self._run(
605+
self._async_client.write_stdin(
606+
process_id=process_id,
607+
close_stdin=close_stdin,
608+
delta_base64=delta_base64,
609+
)
610+
)
611+
612+
def resize_terminal(
613+
self,
614+
*,
615+
process_id: str,
616+
size: protocol.CommandExecTerminalSize,
617+
) -> EmptyResult:
618+
"""Resize the terminal attached to a running `command/exec` process.
619+
620+
This wraps the app-server `command/exec/resize` request and sends the
621+
new terminal dimensions as `cols` and `rows`.
622+
"""
623+
return self._run(self._async_client.resize_terminal(process_id=process_id, size=size))
624+
625+
def terminate_process(self, *, process_id: str) -> EmptyResult:
626+
"""Terminate a running `command/exec` process.
627+
628+
This wraps the app-server `command/exec/terminate` request for the
629+
client-supplied process id.
630+
"""
631+
return self._run(self._async_client.terminate_process(process_id=process_id))
632+
546633

547634
class _ExternalAgentConfigClient(_SyncRunner):
548635
def __init__(

codex/app_server/_sync_threads.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from collections.abc import Callable, Collection, Coroutine
5+
from collections.abc import Callable, Collection, Coroutine, Mapping
66
from typing import Any, Protocol, TypeVar
77

88
from pydantic import BaseModel
@@ -45,7 +45,12 @@ def final_model(self, model_type: type[_ModelT]) -> _ModelT: ...
4545

4646
def raise_for_terminal_status(self) -> None: ...
4747

48-
async def steer(self, input: TurnInput) -> TurnIdResult: ...
48+
async def steer(
49+
self,
50+
input: TurnInput,
51+
*,
52+
responsesapi_client_metadata: Mapping[str, object] | None = None,
53+
) -> TurnIdResult: ...
4954

5055
async def interrupt(self) -> EmptyResult: ...
5156

@@ -217,8 +222,18 @@ def collect(self) -> TurnStream:
217222
def raise_for_terminal_status(self) -> None:
218223
self._async_stream.raise_for_terminal_status()
219224

220-
def steer(self, input: TurnInput) -> TurnIdResult:
221-
return self._run(self._async_stream.steer(input))
225+
def steer(
226+
self,
227+
input: TurnInput,
228+
*,
229+
responsesapi_client_metadata: Mapping[str, object] | None = None,
230+
) -> TurnIdResult:
231+
return self._run(
232+
self._async_stream.steer(
233+
input,
234+
responsesapi_client_metadata=responsesapi_client_metadata,
235+
)
236+
)
222237

223238
def interrupt(self) -> EmptyResult:
224239
return self._run(self._async_stream.interrupt())

0 commit comments

Comments
 (0)