Compare commits

..

92 Commits

Author SHA1 Message Date
Charles Cunningham
1357f36a25 Preserve full-history fork semantics
Co-authored-by: Codex <noreply@openai.com>
2026-03-23 13:54:03 -07:00
Charles Cunningham
5dac3c0f23 codex: route fork snapshot through core test support
Co-authored-by: Codex <noreply@openai.com>
2026-03-23 13:30:45 -07:00
Charles Cunningham
5f67c17144 codex: fix external fork snapshot test type
Co-authored-by: Codex <noreply@openai.com>
2026-03-23 13:22:25 -07:00
Charles Cunningham
7370d01767 codex: fix PR CI fallout on latest main
Co-authored-by: Codex <noreply@openai.com>
2026-03-23 13:14:32 -07:00
Charles Cunningham
f3d1732242 core: add fork snapshot compatibility
Co-authored-by: Codex <noreply@openai.com>
2026-03-23 13:13:54 -07:00
xl-openai
9a33e5c0a0 feat: support disable skills by name. (#15378)
Support disabling skills by name, primarily for plugin skills. We can’t
use the path, since plugin skill paths may change across versions.
2026-03-23 12:57:40 -07:00
Charley Cunningham
332edba78e Thread guardian Responses API errors into denial rationale (#15516)
## Summary
- capture the last guardian `EventMsg::Error` while waiting for review
completion
- reuse that error as the denial rationale when the review turn
completes without an assessment payload
- add a regression test for the `/responses` HTTP 400 path

## Testing
- `just fmt`
- `cargo test -p codex-core
guardian_review_surfaces_responses_api_errors_in_rejection_reason`
- `just argument-comment-lint -p codex-core`

## Notes
- `cargo test -p codex-core` still fails on the pre-existing unrelated
test
`tools::js_repl::tests::js_repl_imported_local_files_can_access_repl_globals`
in this environment (`mktemp ... Operation not permitted` while
downloading `dotslash`)

Co-authored-by: Codex <noreply@openai.com>
2026-03-23 12:46:49 -07:00
jif-oai
450dc289c3 chore: split sub-agent v2 implementation (#15540)
Just to make things cleaner
2026-03-23 19:41:53 +00:00
canvrno-oai
b5d0a5518d Plugins TUI install/uninstall (#15342)
- Add install/uninstall actions to the TUI plugins menu
- Wire plugin install/uninstall through both TUI and `tui_app_server`
- Refresh config/plugin state after changes so the UI updates
immediately
- Add a post-install app setup flow for plugins that require additional
app auth

<img width="1567" height="300" alt="Screenshot 2026-03-20 at 4 08 44 PM"
src="https://github.com/user-attachments/assets/366bd31b-2ffd-4e80-b4a3-3a9a9c674a5f"
/>
<img width="445" height="240" alt="Screenshot 2026-03-20 at 4 08 54 PM"
src="https://github.com/user-attachments/assets/613999ab-269a-4758-ab59-7c057a1742dc"
/>
<img width="797" height="219" alt="Screenshot 2026-03-20 at 4 09 07 PM"
src="https://github.com/user-attachments/assets/b9679e60-40f5-49bb-ade0-2e40449c3fbf"
/>
<img width="499" height="235" alt="Screenshot 2026-03-20 at 4 09 24 PM"
src="https://github.com/user-attachments/assets/261ce2fe-f356-4e99-8ac9-f29ed850bc75"
/>




Note/known issue: The /plugin install flow fails in `tui_app_server`
because after a successful install it tries to trigger a
ReloadUserConfig operation, but `tui_app_server` has not yet implemented
transport for that operation, so it falls through to the generic “Not
available in app-server TUI yet” stub.
2026-03-23 12:38:39 -07:00
Celia Chen
f55f5c258f Fix: proactive auth refresh to reload guarded disk state first (#15357)
## Summary

Fix a managed ChatGPT auth bug where a stale Codex process could
proactively refresh using an old in-memory refresh token even after
another process had already rotated auth on disk.

This changes the proactive `AuthManager::auth()` path to reuse the
existing guarded `refresh_token()` flow instead of calling the refresh
endpoint directly from cached auth state.

## Original Issue

Users reported repeated `codexd` log lines like:

```text
ERROR codex_core::auth: Failed to refresh token: error sending request for url (https://auth.openai.com/oauth/token)
```

In practice this showed up most often when multiple `codexd` processes
were left running. Killing the extra processes stopped the noise, which
suggested the issue was caused by stale auth state across processes
rather than invalid user credentials.

## Diagnosis

The bug was in the proactive refresh path used by `AuthManager::auth()`:

- Process A could refresh successfully, rotate refresh token `R0` to
`R1`, and persist the updated auth state plus `last_refresh` to disk.
- Process B could keep an older auth snapshot cached in memory, still
holding `R0` and the old `last_refresh`.
- Later, when Process B called `auth()`, it checked staleness from its
cached in-memory auth instead of first reloading from disk.
- Because that cached `last_refresh` was stale, Process B would
proactively call `/oauth/token` with stale refresh token `R0`.
- On failure, `auth()` logged the refresh error but kept returning the
same stale cached auth, so repeated `auth()` calls could keep retrying
with dead state.

This differed from the existing unauthorized-recovery flow, which
already did the safer thing: guarded reload from disk first, then
refresh only if the on-disk auth was unchanged.

## What Changed

- Switched proactive refresh in `AuthManager::auth()` to:
  - do a pure staleness check on cached auth
  - call `refresh_token()` when stale
- return the original cached auth on genuine refresh failure, preserving
existing outward behavior
- Removed the direct proactive refresh-from-cached-state path
- Added regression tests covering:
  - stale cached auth with newer same-account auth already on disk
- the same scenario even when the refresh endpoint would fail if called

## Why This Fix

`refresh_token()` already contains the right cross-process safety
behavior:

- guarded reload from disk
- same-account verification
- skip-refresh when another process already changed auth

Reusing that path makes proactive refresh consistent with unauthorized
recovery and prevents stale processes from trying to refresh
already-rotated tokens.

## Testing

Test shape:

- create a fresh temp `CODEX_HOME` from `~/.codex/auth.json`
- force `last_refresh` to an old timestamp so proactive refresh is
required
- start two long-lived helper processes against the same auth file
- start `B` first so it caches stale auth and sleeps
- start `A` second so it refreshes first
- point both at a local mock `/oauth/token` server
- inspect whether `B` makes a second refresh request with the stale
in-memory token, or reloads the rotated token from disk

### Before the fix

The repro showed the bug clearly: the mock server saw two refreshes with
the same stale token, `A` rotated to a new token, and `B` still returned
the stale token instead of reloading from disk.

```text
POST /oauth/token refresh_token=rt_j6s0...
POST /oauth/token refresh_token=rt_j6s0...

B:cached_before=rt_j6s0...
B:cached_after=rt_j6s0...
B:returned=rt_j6s0...

A:cached_before=rt_j6s0...
A:cached_after=rotated-refresh-token-logged-run-v2
A:returned=rotated-refresh-token-logged-run-v2
```

### After the fix

After the fix, the mock server saw only one refresh request. `A`
refreshed once, and `B` started with the stale token but reloaded and
returned the rotated token.

```text
POST /oauth/token refresh_token=rt_j6s0...

B:cached_before=rt_j6s0...
B:cached_after=rotated-refresh-token-fix-branch
B:returned=rotated-refresh-token-fix-branch

A:cached_before=rt_j6s0...
A:cached_after=rotated-refresh-token-fix-branch
A:returned=rotated-refresh-token-fix-branch
```

This shows the new behavior: `A` refreshes once, then `B` reuses the
updated auth from disk instead of making a second refresh request with
the stale token.
2026-03-23 12:07:59 -07:00
jif-oai
37ac0c093c feat: structured multi-agent output (#15515)
Send input now sends messages as assistant message and with this format:

```
author: /root/worker_a
recipient: /root/worker_a/tester
other_recipients: []
Content: bla bla bla. Actual content. Only text for now
```
2026-03-23 18:53:54 +00:00
Charley Cunningham
e838645fa2 tui: queue follow-ups during manual /compact (#15259)
## Summary
- queue input after the user submits `/compact` until that manual
compact turn ends
- mirror the same behavior in the app-server TUI
- add regressions for input queued before compact starts and while it is
running

Co-authored-by: Codex <noreply@openai.com>
2026-03-23 10:19:44 -07:00
canvrno-oai
54801634e1 Label plugins as plugins, and hide skills/apps for given plugin (#15279)
- Duplicate app mentions are now suppressed when they’re plugin-backed
with the same display name.
- Remaining connector mentions now label category as [Plugin] when
plugin metadata is present, otherwise [App].
- Mention result lists are now capped to 8 rows after filtering.
- Updates both tui and tui_app_server with the same changes.
2026-03-23 10:10:17 -07:00
jif-oai
2887f16cb9 fix: cargo deny (#15520) 2026-03-23 16:48:54 +00:00
Michael Bolin
d1088158b8 fix: fall back to vendored bubblewrap when system bwrap lacks --argv0 (#15338)
## Why

Fixes [#15283](https://github.com/openai/codex/issues/15283), where
sandboxed tool calls fail on older distro `bubblewrap` builds because
`/usr/bin/bwrap` does not understand `--argv0`. The upstream [bubblewrap
v0.9.0 release
notes](https://github.com/containers/bubblewrap/releases/tag/v0.9.0)
explicitly call out `Add --argv0`. Flipping `use_legacy_landlock`
globally works around that compatibility bug, but it also weakens the
default Linux sandbox and breaks proxy-routed and split-policy cases
called out in review.

The follow-up Linux CI failure was in the new launcher test rather than
the launcher logic: the fake `bwrap` helper stayed open for writing, so
Linux would not exec it. This update also closes the user-visibility gap
from review by surfacing the same startup warning when `/usr/bin/bwrap`
is present but too old for `--argv0`, not only when it is missing.

## What Changed

- keep `use_legacy_landlock` default-disabled
- teach `codex-rs/linux-sandbox/src/launcher.rs` to fall back to the
vendored bubblewrap build when `/usr/bin/bwrap` does not advertise
`--argv0` support
- add launcher tests for supported, unsupported, and missing system
`bwrap`
- write the fake `bwrap` test helper to a closed temp path so the
supported-path launcher test works on Linux too
- extend the startup warning path so Codex warns when `/usr/bin/bwrap`
is missing or too old to support `--argv0`
- mirror the warning/fallback wording across
`codex-rs/linux-sandbox/README.md` and `codex-rs/core/README.md`,
including that the fallback is the vendored bubblewrap compiled into the
binary
- cite the upstream `bubblewrap` release that introduced `--argv0`

## Verification

- `bazel test --config=remote --platforms=//:rbe
//codex-rs/linux-sandbox:linux-sandbox-unit-tests
--test_filter=launcher::tests::prefers_system_bwrap_when_help_lists_argv0
--test_output=errors`
- `cargo test -p codex-core system_bwrap_warning`
- `cargo check -p codex-exec -p codex-tui -p codex-tui-app-server -p
codex-app-server`
- `just argument-comment-lint`
2026-03-23 09:46:51 -07:00
jif-oai
d807d44ae7 nit: guard -> registry (#15317) 2026-03-23 10:02:11 +00:00
Charley Cunningham
5e3793def2 Use Shift+Left to edit queued messages in tmux (#15480)
## Summary
- use Shift+Left to edit the most recent queued message when running
under tmux
- mirror the same binding change in the app-server TUI
- add tmux-specific tests and snapshot coverage for the rendered
queued-message hint

## Testing
- just fmt
- cargo test -p codex-tui
- cargo test -p codex-tui-app-server
- just argument-comment-lint -p codex-tui -p codex-tui-app-server

Co-authored-by: Codex <noreply@openai.com>
2026-03-22 21:19:31 -07:00
Charley Cunningham
85065ea1b8 core: snapshot fork startup context injection (#15443)
## Summary
- add a snapshot-style core test for fork startup context injection
followed by first-turn diff injection
- capture the current duplicated startup-plus-turn context behavior
without changing runtime logic

## Testing
- not run locally; relying on CI
- just fmt

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-22 18:24:14 -07:00
Charley Cunningham
e830000e41 Remove smart_approvals alias migration (#15464)
Remove the legacy `smart_approvals` config migration from core config
loading.

This change:
- stops rewriting `smart_approvals` into `guardian_approval`
- stops backfilling `approvals_reviewer = "guardian_subagent"`
- replaces the migration tests with regression coverage that asserts the
deprecated key is ignored in root and profile scopes

Verification:
- `just fmt`
- `cargo test -p codex-core smart_approvals_alias_is_ignored`
- `cargo test -p codex-core approvals_reviewer_`
- `just argument-comment-lint`

Notes:
- `cargo test -p codex-core` still hits an unrelated existing failure in
`tools::js_repl::tests::js_repl_imported_local_files_can_access_repl_globals`;
the JS REPL kernel exits after `mktemp` fails under the current
environment.

Enhancement request: requested cleanup to delete the `smart_approvals`
alias migration; no public issue link is available.

Co-authored-by: Codex <noreply@openai.com>
2026-03-22 17:10:42 -07:00
Dylan Hurd
31728dd460 chore(exec_policy) ExecPolicyRequirementScenario tests (#15415)
## Summary
Consolidate exec_policy_tests on `ExecApprovalRequirementScenario` for
consistency.

## Testing
- [x] These are tests
2026-03-22 08:07:43 -07:00
Matthew Zeng
19702e190e [apps] Improve app tools loading for TUI. (#15376)
- [x] Remove the app tools copy in TUI and reference the core tools
instead, this reduces tools/list calls from 4 to just 1.
2026-03-22 00:17:48 -07:00
Eric Traut
cf0223887f Remove legacy auth and notification handling from tui_app_server (#15414)
## Summary
- remove `tui_app_server` handling for legacy app-server notifications
- drop the local ChatGPT auth refresh request path from `tui_app_server`
- remove the now-unused refresh response helper from local auth loading

Split out of #15106 so the `tui_app_server` cleanup can land separately
from the larger `codex-exec` app-server migration.
2026-03-21 15:06:10 -06:00
Channing Conger
c23566b3af Add JIT entitlement for macosx (#15409)
Without this entitlement, hardened mac os release binaries are unable to
allocate the executable memory for the JIT compiled JS.

Tested with local signing.  Without entitlement I reproduce the error:
```
#
# Fatal process out of memory: Failed to reserve virtual memory for CodeRange
#
==== C stack trace ===============================

    0   codex                               0x00000001075d1acc codex + 85760716
    1   codex                               0x00000001075d6a64 codex + 85781092
    2   codex                               0x00000001075c7100 codex + 85717248
    3   codex                               0x0000000107637394 codex + 86176660
    4   codex                               0x0000000107823cfc codex + 88194300
    5   codex                               0x000000010777c438 codex + 87508024
    6   codex                               0x000000010777d130 codex + 87511344
    7   codex                               0x0000000107c87a54 codex + 92797524
    8   codex                               0x0000000107641188 codex + 86217096
    9   codex                               0x00000001076412d8 codex + 86217432
    10  codex                               0x0000000107553908 codex + 85244168
    11  codex                               0x000000010465f124 codex + 36008228
    12  codex                               0x000000010466a0d0 codex + 36053200
    13  codex                               0x000000010466ce78 codex + 36064888
    14  codex                               0x000000010734edb0 codex + 83127728
    15  libsystem_pthread.dylib             0x00000001810d3c08 _pthread_start + 136
    16  libsystem_pthread.dylib             0x00000001810ceba8 thread_start + 8
zsh: trace trap  target/release/codex exec --enable code_mode_only --enable code_mode --
```

With the entitlement the exec succeeds.
2026-03-21 13:43:14 -07:00
Eric Traut
b0236501e2 Remove legacy app-server notification handling from tui_app_server (#15390)
As part of moving the TUI onto the app server, we added some temporary
handling of some legacy events. We've confirmed that these do not need
to be supported, so this PR removes this support from the
tui_app_server, allowing for additional simplifications in follow-on
PRs. These events are needed only for very old rollouts. None of the
other app server clients (IDE extension or app) support these either.

## Summary
- stop translating legacy `codex/event/*` notifications inside
`tui_app_server`
- remove the TUI-side legacy warning and rollback buffering/replay paths
that were only fed by those notifications
- keep the lower-level app-server and app-server-client legacy event
plumbing intact so PR #15106 can rebase on top and handle the remaining
exec/lower-layer migration separately
2026-03-21 12:29:33 -06:00
Dylan Hurd
0d9bb8ea58 chore(context) Include guardian approval context (#15366)
## Summary
Include the guardian context in the developer message for approvals

## Testing
- [x] Updated unit tests
2026-03-21 16:31:22 +00:00
Matthew Zeng
06e06ab173 [plugins] Fix plugin explicit mention context management. (#15372)
- [x] Fix plugin explicit mention context management.
2026-03-21 00:29:29 -07:00
Channing Conger
e4eedd6170 Code mode on v8 (#15276)
Moves Code Mode to a new crate with no dependencies on codex. This
create encodes the code mode semantics that we want for lifetime,
mounting, tool calling.

The model-facing surface is mostly unchanged. `exec` still runs raw
JavaScript, `wait` still resumes or terminates a `cell_id`, nested tools
are still available through `tools.*`, and helpers like `text`, `image`,
`store`, `load`, `notify`, `yield_control`, and `exit` still exist.

The major change is underneath that surface:

- Old code mode was an external Node runtime.
- New code mode is an in-process V8 runtime embedded directly in Rust.
- Old code mode managed cells inside a long-lived Node runner process.
- New code mode manages cells in Rust, with one V8 runtime thread per
active `exec`.
- Old code mode used JSON protocol messages over child stdin/stdout plus
Node worker-thread messages.
- New code mode uses Rust channels and direct V8 callbacks/events.

This PR also fixes the two migration regressions that fell out of that
substrate change:

- `wait { terminate: true }` now waits for the V8 runtime to actually
stop before reporting termination.
- synchronous top-level `exit()` now succeeds again instead of surfacing
as a script error.

---

- `core/src/tools/code_mode/*` is now mostly an adapter layer for the
public `exec` / `wait` tools.
- `code-mode/src/service.rs` owns cell sessions and async control flow
in Rust.
- `code-mode/src/runtime/*.rs` owns the embedded V8 isolate and
JavaScript execution.
- each `exec` spawns a dedicated runtime thread plus a Rust
session-control task.
- helper globals are installed directly into the V8 context instead of
being injected through a source prelude.
- helper modules like `tools.js` and `@openai/code_mode` are synthesized
through V8 module resolution callbacks in Rust.

---

Also added a benchmark for showing the speed of init and use of a code
mode env:
```
$ cargo bench -p codex-code-mode --bench exec_overhead -- --samples 30 --warm-iterations 25 --tool-counts 0,32,128
Finished [`bench` profile [optimized]](https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles) target(s) in 0.18s
     Running benches/exec_overhead.rs (target/release/deps/exec_overhead-008c440d800545ae)
exec_overhead: samples=30, warm_iterations=25, tool_counts=[0, 32, 128]
scenario       tools samples    warmups      iters      mean/exec       p95/exec       rssΔ p50       rssΔ max
cold_exec          0      30          0          1         1.13ms         1.20ms        8.05MiB        8.06MiB
warm_exec          0      30          1         25       473.43us       512.49us      912.00KiB        1.33MiB
cold_exec         32      30          0          1         1.03ms         1.15ms        8.08MiB        8.11MiB
warm_exec         32      30          1         25       509.73us       545.76us      960.00KiB        1.30MiB
cold_exec        128      30          0          1         1.14ms         1.19ms        8.30MiB        8.34MiB
warm_exec        128      30          1         25       575.08us       591.03us      736.00KiB      864.00KiB
memory uses a fresh-process max RSS delta for each scenario
```

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-20 23:36:58 -07:00
alexsong-oai
ec32866c37 Pass platform param to featured plugins (#15348) 2026-03-21 01:42:40 +00:00
Dylan Hurd
60c59a7799 fix(core) disable command_might_be_dangerous when unsandboxed (#15036)
## Summary
If we are in a mode that is already explicitly un-sandboxed, then
`ApprovalPolicy::Never` should not block dangerous commands.

## Testing
- [x] Existing unit test covers old behavior
- [x] Added a unit test for this new case
2026-03-21 01:28:25 +00:00
Dylan Hurd
7754dd1b89 chore(core) update prefix_rule guidance (#15231)
## Summary
Small tweaks to the prefix_rule guidance.

## Testing
- [x] in progress
2026-03-20 15:57:06 -07:00
Celia Chen
9eef2e91fc fix: allow restricted filesystem profiles to read helper executables (#15114)
## Summary

This PR fixes restricted filesystem permission profiles so Codex's
runtime-managed helper executables remain readable without requiring
explicit user configuration.

- add implicit readable roots for the configured `zsh` helper path and
the main execve wrapper
- allowlist the shared `$CODEX_HOME/tmp/arg0` root when the execve
wrapper lives there, so session-specific helper paths keep working
- dedupe injected paths and avoid adding duplicate read entries to the
sandbox policy
- add regression coverage for restricted read mode with helper
executable overrides

## Testing 
before this change: got this error when executing a shell command via
zsh fork:
```
"sandbox error: sandbox denied exec error, exit code: 127, stdout: , stderr: /etc/zprofile:11: operation not permitted: /usr/libexec/path_helper\nzsh:1: operation not permitted: .codex/skills/proxy-a/scripts/fetch_example.sh\n"
```

saw this change went away after this change, meaning the readable roots
and injected correctly.
2026-03-20 15:51:06 -07:00
canvrno-oai
10a936d127 Gate tui /plugins menu behind flag (#15285)
Gate /plugins menu behind `--enable plugins` flag
2026-03-20 15:49:04 -07:00
Ahmed Ibrahim
3431f01776 Add realtime transcript notification in v2 (#15344)
- emit a typed `thread/realtime/transcriptUpdated` notification from
live realtime transcript deltas
- expose that notification as flat `threadId`, `role`, and `text` fields
instead of a nested transcript array
- continue forwarding raw `handoff_request` items on
`thread/realtime/itemAdded`, including the accumulated
`active_transcript`
- update app-server docs, tests, and generated protocol schema artifacts
to match the delta-based payloads

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-20 15:30:48 -07:00
Dylan Hurd
ea8b07e680 chore(core) Remove Feature::PowershellUtf8 (#15128)
## Summary
This feature has been enabled for powershell for a while now, let's get
rid of the logic

## Testing
- [x] Unit tests
2026-03-20 22:03:31 +00:00
Matthew Zeng
dd88ed767b [apps] Use ARC for yolo mode. (#15273)
- [x] Use ARC for yolo mode.
2026-03-20 21:13:20 +00:00
Channing Conger
1350477150 Add v8-poc consumer of our new built v8 (#15203)
This adds a dummy v8-poc project that in Cargo links against our
prebuilt binaries and the ones provided by rusty_v8 for non musl
platforms. This demonstrates that we can successfully link and use v8 on
all platforms that we want to target.

In bazel things are slightly more complicated. Since the libraries as
published have libc++ linked in already we end up with a lot of double
linked symbols if we try to use them in bazel land. Instead we fall back
to building rusty_v8 and v8 from source (cached of course) on the
platforms we ship to.

There is likely some compatibility drift in the windows bazel builder
that we'll need to reconcile before we can re-enable them. I'm happy to
be on the hook to unwind that.
2026-03-20 12:08:25 -07:00
Channing Conger
a941d8439d Bump aws-lc-rs (#15337)
Bump our dep.

RUSTSEC-2026-0048
Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0048
2026-03-20 18:59:13 +00:00
Shaqayeq
9e31aeadce Pin Python SDK app-server stdio to UTF-8 on Windows (#15244)
## TL;DR
Pin the Python app-server SDK subprocess pipes to UTF-8 so Windows users
on non-UTF-8 locales do not hit `UnicodeDecodeError` when the `codex`
child emits UTF-8 text.

- add `encoding="utf-8"` to the `subprocess.Popen(...)` call in
`AppServerClient.start()`
- add a focused regression test that asserts the client launches the
subprocess with UTF-8 text I/O
- validates with `python -m pytest
sdk/python/tests/test_client_rpc_methods.py
sdk/python/tests/test_client_process_launch.py
sdk/python/tests/test_public_api_runtime_behavior.py`

Fixes #14311.
2026-03-20 18:26:24 +00:00
jif-oai
79ad7b247b feat: change multi-agent to use path-like system instead of uuids (#15313)
This PR add an URI-based system to reference agents within a tree. This
comes from a sync between research and engineering.

The main agent (the one manually spawned by a user) is always called
`/root`. Any sub-agent spawned by it will be `/root/agent_1` for example
where `agent_1` is chosen by the model.

Any agent can contact any agents using the path.

Paths can be used either in absolute or relative to the calling agents

Resume is not supported for now on this new path
2026-03-20 18:23:48 +00:00
pakrym-oai
4ddde54c19 Add remote test skill (#15324)
Teach codex to run remote tests.
2026-03-20 10:37:57 -07:00
jif-oai
b9fa08ec61 try to fix bazel (#15328)
Fix Bazel macOS CI failures caused by the llvm module's pinned macOS SDK
URL returning 403 Forbidden from Apple's CDN.

Bump llvm to 0.6.8, switch to the new osx.from_archive(...) /
osx.frameworks(...) API, and refresh MODULE.bazel.lock so Bazel uses the
updated SDK archive configuration.
2026-03-20 10:18:19 -07:00
Eric Traut
4f28b64abc Add temporary app-server originator fallback for codex-tui (#15218)
## Summary
- make app-server treat `clientInfo.name == "codex-tui"` as a legacy
compatibility case
- fall back to `DEFAULT_ORIGINATOR` instead of sending `codex-tui` as
the originator header
- add a TODO noting this is a temporary workaround that should be
removed later

## Testing
- Not run (not requested)
2026-03-20 10:51:21 -06:00
pakrym-oai
ba85a58039 Add remote env CI matrix and integration test (#14869)
`CODEX_TEST_REMOTE_ENV` will make `test_codex` start the executor
"remotely" (inside a docker container) turning any integration test into
remote test.
2026-03-20 08:02:50 -07:00
xl-openai
e5f4d1fef5 feat: prefer git for curated plugin sync (#15275)
start with git clone, fallback to http.
2026-03-20 00:06:24 -07:00
Won Park
461ba012fc Feat/restore image generation history (#15223)
Restore image generation items in resumed thread history
2026-03-19 22:57:16 -07:00
Charley Cunningham
b3a4da84da Add guardian follow-up reminder (#15262)
## Summary
- add a short guardian follow-up developer reminder before reused
reviews
- cache prior-review state on the guardian session instead of rescanning
full history on each request
- update guardian follow-up coverage and snapshot expectations

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 22:35:52 -07:00
xl-openai
b1570d6c23 feat: Add One-Time Startup Remote Plugin Sync (#15264)
For early users who have already enabled apps, we should enable plugins
as part of the initial setup.
2026-03-20 05:01:39 +00:00
Andrei Eternal
cc192763e1 Disable hooks on windows for now (#15252)
We'll verify a bit later that all of this works correctly and re-enable
2026-03-19 21:31:56 -07:00
canvrno-oai
f7201e5a9f Initial plugins TUI menu - list and read only. tui + tui_app_server (#15215)
### Preliminary /plugins TUI menu
- Adds a preliminary /plugins menu flow in both tui and tui_app_server.
- Fetches plugin list data asynchronously and shows loading/error/cached
states.
  - Limits this first pass to the curated ChatGPT marketplace.
  - Shows available plugins with installed/status metadata.
- Supports in-menu search over plugin display name, plugin id, plugin
name, and marketplace label.
- Opens a plugin detail view on selection, including summaries for
Skills, Apps, and MCP Servers, with back navigation.

### Testing
  - Launch codex-cli with plugins enabled (`--enable plugins`).
  - Run /plugins and verify:
      - loading state appears first
      - plugin list is shown
      - search filters results
- selecting a plugin opens detail view, with a list of
skills/connectors/MCP servers for the plugin
      - back action returns to the list.
- Verify disabled behavior by running /plugins without plugins enabled
(shows “Plugins are disabled” message).
- Launch with `--enable tui_app_server` (and plugins enabled) and repeat
the same /plugins flow; behavior should match.
2026-03-19 21:28:33 -07:00
Michael Bolin
fa2a2f0be9 Use released DotSlash package for argument-comment lint (#15199)
## Why
The argument-comment lint now has a packaged DotSlash artifact from
[#15198](https://github.com/openai/codex/pull/15198), so the normal repo
lint path should use that released payload instead of rebuilding the
lint from source every time.

That keeps `just clippy` and CI aligned with the shipped artifact while
preserving a separate source-build path for people actively hacking on
the lint crate.

The current alpha package also exposed two integration wrinkles that the
repo-side prebuilt wrapper needs to smooth over:
- the bundled Dylint library filename includes the host triple, for
example `@nightly-2025-09-18-aarch64-apple-darwin`, and Dylint derives
`RUSTUP_TOOLCHAIN` from that filename
- on Windows, Dylint's driver path also expects `RUSTUP_HOME` to be
present in the environment

Without those adjustments, the prebuilt CI jobs fail during `cargo
metadata` or driver setup. This change makes the checked-in prebuilt
wrapper normalize the packaged library name to the plain
`nightly-2025-09-18` channel before invoking `cargo-dylint`, and it
teaches both the wrapper and the packaged runner source to infer
`RUSTUP_HOME` from `rustup show home` when the environment does not
already provide it.

After the prebuilt Windows lint job started running successfully, it
also surfaced a handful of existing anonymous literal callsites in
`windows-sandbox-rs`. This PR now annotates those callsites so the new
cross-platform lint job is green on the current tree.

## What Changed
- checked in the current
`tools/argument-comment-lint/argument-comment-lint` DotSlash manifest
- kept `tools/argument-comment-lint/run.sh` as the source-build wrapper
for lint development
- added `tools/argument-comment-lint/run-prebuilt-linter.sh` as the
normal enforcement path, using the checked-in DotSlash package and
bundled `cargo-dylint`
- updated `just clippy` and `just argument-comment-lint` to use the
prebuilt wrapper
- split `.github/workflows/rust-ci.yml` so source-package checks live in
a dedicated `argument_comment_lint_package` job, while the released lint
runs in an `argument_comment_lint_prebuilt` matrix on Linux, macOS, and
Windows
- kept the pinned `nightly-2025-09-18` toolchain install in the prebuilt
CI matrix, since the prebuilt package still relies on rustup-provided
toolchain components
- updated `tools/argument-comment-lint/run-prebuilt-linter.sh` to
normalize host-qualified nightly library filenames, keep the `rustup`
shim directory ahead of direct toolchain `cargo` binaries, and export
`RUSTUP_HOME` when needed for Windows Dylint driver setup
- updated `tools/argument-comment-lint/src/bin/argument-comment-lint.rs`
so future published DotSlash artifacts apply the same nightly-filename
normalization and `RUSTUP_HOME` inference internally
- fixed the remaining Windows lint violations in
`codex-rs/windows-sandbox-rs` by adding the required `/*param*/`
comments at the reported callsites
- documented the checked-in DotSlash file, wrapper split, archive
layout, nightly prerequisite, and Windows `RUSTUP_HOME` requirement in
`tools/argument-comment-lint/README.md`
2026-03-20 03:19:22 +00:00
starr-openai
96a86710c3 Split exec process into local and remote implementations (#15233)
## Summary
- match the exec-process structure to filesystem PR #15232
- expose `ExecProcess` on `Environment`
- make `LocalProcess` the real implementation and `RemoteProcess` a thin
network proxy over `ExecServerClient`
- make `ProcessHandler` a thin RPC adapter delegating to `LocalProcess`
- add a shared local/remote process test

## Validation
- `just fmt`
- `CARGO_TARGET_DIR=~/.cache/cargo-target/codex cargo test -p
codex-exec-server`
- `just fix -p codex-exec-server`

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-20 03:13:08 +00:00
Ahmed Ibrahim
2e22885e79 Split features into codex-features crate (#15253)
- Split the feature system into a new `codex-features` crate.
- Cut `codex-core` and workspace consumers over to the new config and
warning APIs.

Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com>
Co-authored-by: Codex <noreply@openai.com>
2026-03-19 20:12:07 -07:00
xl-openai
35f8b87a5b fix: Distinguish missing and empty plugin products (#15263)
Treat [] as no product allowed, empty as all products allowed.
2026-03-19 20:02:40 -07:00
Michael Bolin
a3e59e9e85 core: add a full-buffer exec capture policy (#15254) 2026-03-20 02:38:12 +00:00
Matthew Zeng
0a344e4fab [plugins] Install MCPs when calling plugin/install (#15195)
- [x] Auth MCPs when installing plugins.
2026-03-19 19:36:58 -07:00
Ahmed Ibrahim
2aa4873802 Move auth code into login crate (#15150)
- Move the auth implementation and token data into codex-login.
- Keep codex-core re-exporting that surface from codex-login for
existing callers.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 18:58:17 -07:00
Channing Conger
ded7854f09 V8 Bazel Build (#15021)
Alternative approach, we use rusty_v8 for all platforms that its
predefined, but lets build from source a musl v8 version with bazel for
x86 and aarch64 only. We would need to release this on github and then
use the release.
2026-03-19 18:05:23 -07:00
pakrym-oai
403b397e4e Refactor ExecServer filesystem split between local and remote (#15232)
For each feature we have:
1. Trait exposed on environment
2. **Local Implementation** of the trait
3. Remote implementation that uses the client to proxy via network
4. Handler implementation that handles PRC requests and calls into
**Local Implementation**
2026-03-19 17:08:04 -07:00
Won Park
6b8175c734 changed save directory to codex_home (#15222)
saving image gen default save directory to
codex_home/imagegen/thread_id/
2026-03-19 15:16:26 -07:00
Owen Lin
9e695fe830 feat(app-server): add mcpServer/startupStatus/updated notification (#15220)
Exposes the legacy `codex/event/mcp_startup_update` event as an API v2
notification.

The legacy event has this shape:
```
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
pub struct McpStartupUpdateEvent {
    /// Server name being started.
    pub server: String,
    /// Current startup status.
    pub status: McpStartupStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
#[serde(rename_all = "snake_case", tag = "state")]
#[ts(rename_all = "snake_case", tag = "state")]
pub enum McpStartupStatus {
    Starting,
    Ready,
    Failed { error: String },
    Cancelled,
}
```
2026-03-19 15:09:59 -07:00
nicholasclark-openai
2bee37fe69 Plumb MCP turn metadata through _meta (#15190)
## Summary

Some background. We're looking to instrument GA turns end to end. Right
now a big gap is grouping mcp tool calls with their codex sessions. We
send session id and turn id headers to the responses call but not the
mcp/wham calls.

Ideally we could pass the args as headers like with responses, but given
the setup of the rmcp client, we can't send as headers without either
changing the rmcp package upstream to allow per request headers or
introducing a mutex which break concurrency. An earlier attempt made the
assumption that we had 1 client per thread, which allowed us to set
headers at the start of a turn. @pakrym mentioned that this assumption
might break in the near future.

So the solution now is to package the turn metadata/session id into the
_meta field in the post body and pull out in codex-backend.

- send turn metadata to MCP servers via `tools/call` `_meta` instead of
assuming per-thread request headers on shared clients
- preserve the existing `_codex_apps` metadata while adding
`x-codex-turn-metadata` for all MCP tool calls
- extend tests to cover both custom MCP servers and the codex apps
search flow

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 22:05:13 +00:00
xl-openai
2254ec4f30 feat: expose needs_auth for plugin/read. (#15217)
So UI can render it properly.
2026-03-19 15:02:45 -07:00
Won Park
27977d6716 adding full imagepath to tui (#15154)
adding full path to TUI so image is open-able in the TUI after being
generated. LImited to VSCode Terminal for now.
2026-03-19 21:29:22 +00:00
iceweasel-oai
69750a0b5a add specific tool guidance for Windows destructive commands (#15207)
updated Windows shell/unified_exec tool descriptions:

`exec_command`
```text
Runs a command in a PTY, returning output or a session ID for ongoing interaction.

Windows safety rules:
- Do not compose destructive filesystem commands across shells. Do not enumerate paths in PowerShell and then pass them to `cmd /c`, batch builtins, or another shell for deletion or moving. Use one shell end-to-end, prefer native PowerShell cmdlets such as `Remove-Item` / `Move-Item` with `-LiteralPath`, and avoid string-built shell commands for file operations.
- Before any recursive delete or move on Windows, verify the resolved absolute target paths stay within the intended workspace or explicitly named target directory. Never issue a recursive delete or move against a computed path if the final target has not been checked.
```

`shell`
```text
Runs a Powershell command (Windows) and returns its output. Arguments to `shell` will be passed to CreateProcessW(). Most commands should be prefixed with ["powershell.exe", "-Command"].

Examples of valid command strings:

- ls -a (show hidden): ["powershell.exe", "-Command", "Get-ChildItem -Force"]
- recursive find by name: ["powershell.exe", "-Command", "Get-ChildItem -Recurse -Filter *.py"]
- recursive grep: ["powershell.exe", "-Command", "Get-ChildItem -Path C:\\myrepo -Recurse | Select-String -Pattern 'TODO' -CaseSensitive"]
- ps aux | grep python: ["powershell.exe", "-Command", "Get-Process | Where-Object { $_.ProcessName -like '*python*' }"]
- setting an env var: ["powershell.exe", "-Command", "$env:FOO='bar'; echo $env:FOO"]
- running an inline Python script: ["powershell.exe", "-Command", "@'\nprint('Hello, world!')\n'@ | python -"]

Windows safety rules:
- Do not compose destructive filesystem commands across shells. Do not enumerate paths in PowerShell and then pass them to `cmd /c`, batch builtins, or another shell for deletion or moving. Use one shell end-to-end, prefer native PowerShell cmdlets such as `Remove-Item` / `Move-Item` with `-LiteralPath`, and avoid string-built shell commands for file operations.
- Before any recursive delete or move on Windows, verify the resolved absolute target paths stay within the intended workspace or explicitly named target directory. Never issue a recursive delete or move against a computed path if the final target has not been checked.
```

`shell_command`
```text
Runs a Powershell command (Windows) and returns its output.

Examples of valid command strings:

- ls -a (show hidden): "Get-ChildItem -Force"
- recursive find by name: "Get-ChildItem -Recurse -Filter *.py"
- recursive grep: "Get-ChildItem -Path C:\\myrepo -Recurse | Select-String -Pattern 'TODO' -CaseSensitive"
- ps aux | grep python: "Get-Process | Where-Object { $_.ProcessName -like '*python*' }"
- setting an env var: "$env:FOO='bar'; echo $env:FOO"
- running an inline Python script: "@'\nprint('Hello, world!')\n'@ | python -"

Windows safety rules:
- Do not compose destructive filesystem commands across shells. Do not enumerate paths in PowerShell and then pass them to `cmd /c`, batch builtins, or another shell for deletion or moving. Use one shell end-to-end, prefer native PowerShell cmdlets such as `Remove-Item` / `Move-Item` with `-LiteralPath`, and avoid string-built shell commands for file operations.
- Before any recursive delete or move on Windows, verify the resolved absolute target paths stay within the intended workspace or explicitly named target directory. Never issue a recursive delete or move against a computed path if the final target has not been checked.
```
2026-03-19 21:09:34 +00:00
Ahmed Ibrahim
7eb19e5319 Move terminal module to terminal-detection crate (#15216)
- Move core/src/terminal.rs and its tests into a standalone
terminal-detection workspace crate.
- Update direct consumers to depend on codex-terminal-detection and
import terminal APIs directly.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 14:08:04 -07:00
Owen Lin
668330acc1 feat(tracing): tag app-server turn spans with turn_id (#15206)
So we can find and filter spans by `turn.id`.

We do this for the `turn/start`, `turn/steer`, and `turn/interrupt`
APIs.
2026-03-19 13:07:19 -07:00
Yaroslav Volovich
60cd0cf75e feat(tui): add /title terminal title configuration (#12334)
## Problem

When multiple Codex sessions are open at once, terminal tabs and windows
are hard to distinguish from each other. The existing status line only
helps once the TUI is already focused, so it does not solve the "which
tab is this?" problem.

This PR adds a first-class `/title` command so the terminal window or
tab title can carry a short, configurable summary of the current
session.

## Screenshot

<img width="849" height="320" alt="image"
src="https://github.com/user-attachments/assets/8b112927-7890-45ed-bb1e-adf2f584663d"
/>

## Mental model

`/statusline` and `/title` are separate status surfaces with different
constraints. The status line is an in-app footer that can be denser and
more detailed. The terminal title is external terminal metadata, so it
needs short, stable segments that still make multiple sessions easy to
tell apart.

The `/title` configuration is an ordered list of compact items. By
default it renders `spinner,project`, so active sessions show
lightweight progress first while idle sessions still stay easy to
disambiguate. Each configured item is omitted when its value is not
currently available rather than forcing a placeholder.

## Non-goals

This does not merge `/title` into `/statusline`, and it does not add an
arbitrary free-form title string. The feature is intentionally limited
to a small set of structured items so the title stays short and
reviewable.

This also does not attempt to restore whatever title the terminal or
shell had before Codex started. When Codex clears the title, it clears
the title Codex last wrote.

## Tradeoffs

A separate `/title` command adds some conceptual overlap with
`/statusline`, but it keeps title-specific constraints explicit instead
of forcing the status line model to cover two different surfaces.

Title refresh can happen frequently, so the implementation now shares
parsing and git-branch orchestration between the status line and title
paths, and caches the derived project-root name by cwd. That keeps the
hot path cheap without introducing background polling.

## Architecture

The TUI gets a new `/title` slash command and a dedicated picker UI for
selecting and ordering terminal-title items. The chosen ids are
persisted in `tui.terminal_title`, with `spinner` and `project` as the
default when the config is unset. `status` remains available as a
separate text item, so configurations like `spinner,status` render
compact progress like `⠋ Working`.

`ChatWidget` now refreshes both status surfaces through a shared
`refresh_status_surfaces()` path. That shared path parses configured
items once, warns on invalid ids once, synchronizes shared cached state
such as git-branch lookup, then renders the footer status line and
terminal title from the same snapshot.

Low-level OSC title writes live in `codex-rs/tui/src/terminal_title.rs`,
which owns the terminal write path and last-mile sanitization before
emitting OSC 0.

## Security

Terminal-title text is treated as untrusted display content before Codex
emits it. The write path strips control characters, removes invisible
and bidi formatting characters that can make the title visually
misleading, normalizes whitespace, and caps the emitted length.

References used while implementing this:

- [xterm control
sequences](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
- [WezTerm escape sequences](https://wezterm.org/escape-sequences.html)
- [CWE-150: Improper Neutralization of Escape, Meta, or Control
Sequences](https://cwe.mitre.org/data/definitions/150.html)
- [CERT VU#999008 (Trojan Source)](https://kb.cert.org/vuls/id/999008)
- [Trojan Source disclosure site](https://trojansource.codes/)
- [Unicode Bidirectional Algorithm (UAX
#9)](https://www.unicode.org/reports/tr9/)
- [Unicode Security Considerations (UTR
#36)](https://www.unicode.org/reports/tr36/)

## Observability

Unknown configured title item ids are warned about once instead of
repeatedly spamming the transcript. Live preview applies immediately
while the `/title` picker is open, and cancel rolls the in-memory title
selection back to the pre-picker value.

If terminal title writes fail, the TUI emits debug logs around set and
clear attempts. The rendered status label intentionally collapses richer
internal states into compact title text such as `Starting...`, `Ready`,
`Thinking...`, `Working...`, `Waiting...`, and `Undoing...` when
`status` is configured.

## Tests

Ran:

- `just fmt`
- `cargo test -p codex-tui`

At the moment, the red Windows `rust-ci` failures are due to existing
`codex-core` `apply_patch_cli` stack-overflow tests that also reproduce
on `main`. The `/title`-specific `codex-tui` suite is green.
2026-03-19 19:26:36 +00:00
gabec-openai
fe287ac467 Log automated reviewer approval sources distinctly (#15201)
## Summary

- log guardian-reviewed tool approvals as `source=automated_reviewer` in
`codex.tool_decision`
- keep direct user approvals as `source=user` and config-driven
approvals as `source=config`

## Testing

-
`/Users/gabec/.codex/skills/codex-oss-fastdev/scripts/codex-rs-fmt-quiet.sh`
-
`/Users/gabec/.codex/skills/codex-oss-fastdev/scripts/codex-rs-test-quiet.sh
-p codex-otel` (fails in sandboxed loopback bind tests under
`otel/tests/suite/otlp_http_loopback.rs`)
- `cargo test -p codex-core guardian -- --nocapture` (original-tree run
reached Guardian tests and only hit sandbox-related listener/proxy
failures)

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 12:10:41 -07:00
starr-openai
1d210f639e Add exec-server exec RPC implementation (#15090)
Stacked PR 2/3, based on the stub PR.

Adds the exec RPC implementation and process/event flow in exec-server
only.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 19:00:36 +00:00
Michael Bolin
b87ba0a3cc Publish runnable DotSlash package for argument-comment lint (#15198)
## Why

To date, the argument-comment linter introduced in
https://github.com/openai/codex/pull/14651 had to be built from source
to run, which can be a bit slow (both for local dev and when it is run
in CI). Because of the potential slowness, I did not wire it up to run
as part of `just clippy` or anything like that. As a result, I have seen
a number of occasions where folks put up PRs that violate the lint, see
it fail in CI, and then have to put up their PR again.

The goal of this PR is to pre-build a runnable version of the linter and
then make it available via a DotSlash file. Once it is available, I will
update `just clippy` and other touchpoints to make it a natural part of
the dev cycle so lint violations should get flagged _before_ putting up
a PR for review.

To get things started, we will build the DotSlash file as part of an
alpha release. Though I don't expect the linter to change often, so I'll
probably change this to only build as part of mainline releases once we
have a working DotSlash file. (Ultimately, we should probably move the
linter into its own repo so it can have its own release cycle.)

## What Changed
- add a reusable `rust-release-argument-comment-lint.yml` workflow that
builds host-specific archives for macOS arm64, Linux arm64/x64, and
Windows x64
- wire `rust-release.yml` to publish the `argument-comment-lint`
DotSlash manifest on all releases for now, including alpha tags
- package a runnable layout instead of a bare library

The Unix archive layout is:

```text
argument-comment-lint/
  bin/
    argument-comment-lint
    cargo-dylint
  lib/
    libargument_comment_lint@nightly-2025-09-18-<target>.dylib|so
```

On Windows the same layout is published as a `.zip`, with `.exe` and
`.dll` filenames instead.

DotSlash resolves the package entrypoint to
`argument-comment-lint/bin/argument-comment-lint`. That runner finds the
sibling bundled `cargo-dylint` binary plus the single packaged Dylint
library under `lib/`, then invokes `cargo-dylint dylint --lib-path
<that-library>` with the repo's default lint settings.
2026-03-19 18:59:02 +00:00
pakrym-oai
1837038f4e Add experimental exec server URL handling (#15196)
Add a config and attempt to start the server.
2026-03-19 18:25:11 +00:00
Andrei Eternal
267499bed8 [hooks] use a user message > developer message for prompt continuation (#14867)
## Summary

Persist Stop-hook continuation prompts as `user` messages instead of
hidden `developer` messages + some requested integration tests

This is a followup to @pakrym 's comment in
https://github.com/openai/codex/pull/14532 to make sure stop-block
continuation prompts match training for turn loops

- Stop continuation now writes `<hook_prompt hook_run_id="...">stop
hook's user prompt<hook_prompt>`
- Introduces quick-xml dependency, though we already indirectly depended
on it anyway via syntect
- This PR only has about 500 lines of actual logic changes, the rest is
tests/schema

## Testing

Example run (with a sessionstart hook and 3 stop hooks) - this shows
context added by session start, then two stop hooks sending their own
additional prompts in a new turn. The model responds with a single
message addressing both. Then when that turn ends, the hooks detect that
they just ran using `stop_hook_active` and decide not to infinite loop

test files for this (unzip, move codex -> .codex):
[codex.zip](https://github.com/user-attachments/files/26075806/codex.zip)

```
› cats


• Running SessionStart hook: lighting the observatory

SessionStart hook (completed)
  warning: Hi, I'm a session start hook for wizard-tower (startup).
  hook context: A wimboltine stonpet is an exotic cuisine from hyperspace

• Cats are tiny zen wizards, my friend: equal parts nap, mystery, and chaos. If you want, we can talk
  cat facts, cat breeds, cat names, or build something cat-themed in this repo.

• Running Stop hook: checking the tower wards

• Running Stop hook: sacking the guards

• Running Stop hook: hiring the guards

Stop hook (completed)
  warning: Wizard Tower Stop hook reviewed the completed reply (177 chars).

Stop hook (blocked)
  warning: Wizard Tower Stop hook continuing conversation
  feedback: cook the stonpet

Stop hook (blocked)
  warning: Wizard Tower Stop hook continuing conversation
  feedback: eat the cooked stonpet

• Stonpet’s cooked, aloha style: flash-seared over a blue quasiflame, glazed with nebula salt, and
  rested until the hyperspace juices settle.

  Now we eat with gratitude, my friend. One mindful bite in, and the flavor is pure cosmic surf:
  smoky, bright, and totally out of this dimension.

• Running Stop hook: checking the tower wards

• Running Stop hook: sacking the guards

• Running Stop hook: hiring the guards

Stop hook (completed)
  warning: Wizard Tower Stop hook reviewed the completed reply (285 chars).

Stop hook (completed)
  warning: Wizard Tower Stop hook saw a second pass and stayed calm to avoid a loop.

Stop hook (completed)
  warning: Wizard Tower Stop hook saw a second pass and stayed calm to avoid a loop.
```
2026-03-19 10:53:08 -07:00
nicholasclark-openai
5ec121ba12 Revert "Forward session and turn headers to MCP HTTP requests" (#15185)
Reverts openai/codex#15011

Codex merged by mistake before feedback applied
2026-03-19 10:38:53 -07:00
jif-oai
859c58f07d chore: morpheus does not generate memories (#15175)
For obvious reasons
2026-03-19 15:48:28 +00:00
jif-oai
2cf4d5ef35 chore: add metrics for profile (#15180) 2026-03-19 15:48:02 +00:00
pakrym-oai
dee03da508 Move environment abstraction into exec server (#15125)
The idea is that codex-exec exposes an Environment struct with services
on it. Each of those is a trait.

Depending on construction parameters passed to Environment they are
either backed by local or remote server but core doesn't see these
differences.
2026-03-19 08:31:14 -07:00
jif-oai
32d2df5c1e fix: case where agent is already closed (#15163) 2026-03-19 12:12:50 +00:00
jif-oai
70cdb17703 feat: add graph representation of agent network (#15056)
Add a representation of the agent graph. This is now used for:
* Cascade close agents (when I close a parent, it close the kids)
* Cascade resume (oposite)

Later, this will also be used for post-compaction stuffing of the
context

Direct fix for: https://github.com/openai/codex/issues/14458
2026-03-19 10:21:25 +00:00
xl-openai
db5781a088 feat: support product-scoped plugins. (#15041)
1. Added SessionSource::Custom(String) and --session-source.
  2. Enforced plugin and skill products by session_source.
  3. Applied the same filtering to curated background refresh.
2026-03-19 00:46:15 -07:00
Eric Traut
01df50cf42 Add thread/shellCommand to app server API surface (#14988)
This PR adds a new `thread/shellCommand` app server API so clients can
implement `!` shell commands. These commands are executed within the
sandbox, and the command text and output are visible to the model.

The internal implementation mirrors the current TUI `!` behavior.
- persist shell command execution as `CommandExecution` thread items,
including source and formatted output metadata
- bridge live and replayed app-server command execution events back into
the existing `tui_app_server` exec rendering path

This PR also wires `tui_app_server` to submit `!` commands through the
new API.
2026-03-18 23:42:40 -06:00
canvrno-oai
10eb3ec7fc Simple directory mentions (#14970)
- Adds simple support for directory mentions in the TUI.
- Codex App/VS Code will require minor change to recognize a directory
mention as such and change the link behavior.
- Directory mentions have a trailing slash to differentiate from
extensionless files


<img width="972" height="382" alt="image"
src="https://github.com/user-attachments/assets/8035b1eb-0978-465b-8d7a-4db2e5feca39"
/>
<img width="978" height="228" alt="image"
src="https://github.com/user-attachments/assets/af22cf0b-dd10-4440-9bee-a09915f6ba52"
/>
2026-03-19 05:24:09 +00:00
Andrei Eternal
42e932d7bf [hooks] turn_id extension for Stop & UserPromptSubmit (#15118)
## Description

Adding an extension to the spec that exposes the turn_id to hook
scripts. This is a codex-specific mechanic that allows connecting the
hook runs with particular turns

## Testing

hooks config / sample hooks to use. Extract this, rename codex ->
.codex, and place this into a repo or your home folder. It includes:
config.toml that enables hooks, hooks.json, and sample python hooks:


[codex.zip](https://github.com/user-attachments/files/26102671/codex.zip)

example run (note the turn_ids change between turns):

```
› hello


• Running SessionStart hook: lighting the observatory

SessionStart hook (completed)
  warning: Hi, I'm a session start hook for wizard-tower (startup).
  hook context: Startup context: A wimboltine stonpet is an exotic cuisine from hyperspace

• Running UserPromptSubmit hook: lighting the observatory lanterns

UserPromptSubmit hook (completed)
  warning: wizard-tower UserPromptSubmit demo inspected: hello for turn: 019d036d-c7fa-72d2-b6fd-
78878bfe34e4
  hook context: Wizard Tower UserPromptSubmit demo fired. For this reply only, include the exact
phrase 'observatory lanterns lit' near the end.

• Aloha! Grateful to be here and ready to build with you. Show me what you want to tackle in wizard-
  tower, and we’ll surf the next wave together. observatory lanterns lit

• Running Stop hook: back to shore

Stop hook (completed)
  warning: Wizard Tower Stop hook reviewed the completed reply (170 chars) for turn: 019d036d-c7fa-
72d2-b6fd-78878bfe34e4


› what's a stonpet?


• Running UserPromptSubmit hook: lighting the observatory lanterns

UserPromptSubmit hook (completed)
  warning: wizard-tower UserPromptSubmit demo inspected: what's a stonpet? for turn: 019d036e-3164-
72c3-a170-98925564c4fc
  hook context: Wizard Tower UserPromptSubmit demo fired. For this reply only, include the exact
phrase 'observatory lanterns lit' near the end.

• A stonpet isn’t a standard real-world word, brah. In our shared context here, a wimboltine stonpet
  is an exotic cuisine from hyperspace, so “stonpet” sounds like the dish or food itself.

  If you want, we can totally invent the lore for it next. observatory lanterns lit

• Running Stop hook: back to shore

Stop hook (completed)
  warning: Wizard Tower Stop hook reviewed the completed reply (271 chars) for turn: 019d036e-3164-
72c3-a170-98925564c4fc
```
2026-03-18 21:48:31 -07:00
nicholasclark-openai
b14689df3b Forward session and turn headers to MCP HTTP requests (#15011)
## Summary
- forward request-scoped task headers through MCP tool metadata lookups
and tool calls
- apply those headers to streamable HTTP initialize, tools/list, and
tools/call requests
- update affected rmcp/core tests for the new request_headers plumbing

## Testing
- cargo test -p codex-rmcp-client
- cargo test -p codex-core (fails on pre-existing unrelated error in
core/src/auth_env_telemetry.rs: missing websocket_connect_timeout_ms in
ModelProviderInfo initializer)
- just fix -p codex-rmcp-client
- just fix -p codex-core (hits the same unrelated auth_env_telemetry.rs
error)
- just fmt

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-18 21:29:37 -07:00
Owen Lin
20f2a216df feat(core, tracing): create turn spans over websockets (#14632)
## Description

Dependent on:
- [responsesapi] https://github.com/openai/openai/pull/760991 
- [codex-backend] https://github.com/openai/openai/pull/760985

`codex app-server -> codex-backend -> responsesapi` now reuses a
persistent websocket connection across many turns. This PR updates
tracing when using websockets so that each `response.create` websocket
request propagates the current tracing context, so we can get a holistic
end-to-end trace for each turn.

Tracing is propagated via special keys (`ws_request_header_traceparent`,
`ws_request_header_tracestate`) set in the `client_metadata` param in
Responses API.

Currently tracing on websockets is a bit broken because we only set
tracing context on ws connection time, so it's detached from a
`turn/start` request.
2026-03-19 03:41:06 +00:00
pakrym-oai
903660edba Remove stdio transport from exec server (#15119)
Summary
- delete the deprecated stdio transport plumbing from the exec server
stack
- add a basic `exec_server()` harness plus test utilities to start a
server, send requests, and await events
- refresh exec-server dependencies, configs, and documentation to
reflect the new flow

Testing
- Not run (not requested)

---------

Co-authored-by: starr-openai <starr@openai.com>
Co-authored-by: Codex <noreply@openai.com>
2026-03-19 01:00:35 +00:00
Shaqayeq
4fd2774614 Add Python SDK thread.run convenience methods (#15088)
## TL;DR
Add `thread.run(...)` / `async thread.run(...)` convenience methods to
the Python SDK for the common case.

- add `RunInput = Input | str` and `RunResult` with `final_response`,
collected `items`, and optional `usage`
- keep `thread.turn(...)` strict and lower-level for streaming,
steering, interrupting, and raw generated `Turn` access
- update Python SDK docs, quickstart examples, and tests for the sync
and async convenience flows

## Validation
- `python3 -m pytest sdk/python/tests/test_public_api_signatures.py
sdk/python/tests/test_public_api_runtime_behavior.py`
- `python3 -m pytest
sdk/python/tests/test_real_app_server_integration.py -k
'thread_run_convenience or async_thread_run_convenience'` (skipped in
this environment)

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 00:57:48 +00:00
alexsong-oai
825d09373d Support featured plugins (#15042) 2026-03-18 17:45:30 -07:00
starr-openai
81996fcde6 Add exec-server stub server and protocol docs (#15089)
Stacked PR 1/3.

This is the initialize-only exec-server stub slice: binary/client
scaffolding and protocol docs, without exec/filesystem implementation.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 00:30:05 +00:00
xl-openai
dcd5e08269 fix: harden plugin feature gating (#15104)
Resubmit https://github.com/openai/codex/pull/15020 with correct
content.

1. Use requirement-resolved config.features as the plugin gate.
2. Guard plugin/list, plugin/read, and related flows behind that gate.
3. Skip bad marketplace.json files instead of failing the whole list.
4. Simplify plugin state and caching.
2026-03-19 00:03:37 +00:00
pakrym-oai
56d0c6bf67 Add apply_patch code mode result (#15100)
It's empty !
2026-03-18 16:11:10 -07:00
pakrym-oai
3590e181fa Add update_plan code mode result (#15103)
It's empty!
2026-03-18 16:10:51 -07:00
Ahmed Ibrahim
b306885bd8 don't add transcript for v2 realtime (#15111)
# External (non-OpenAI) Pull Request Requirements

Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md

If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.

Include a link to a bug report or enhancement request.
2026-03-18 15:54:13 -07:00
563 changed files with 39644 additions and 8609 deletions

View File

@@ -0,0 +1,16 @@
---
name: remote-tests
description: How to run tests using remote executor.
---
Some codex integration tests support a running against a remote executor.
This means that when CODEX_TEST_REMOTE_ENV environment variable is set they will attempt to start an executor process in a docker container CODEX_TEST_REMOTE_ENV points to and use it in tests.
Docker container is built and initialized via ./scripts/test-remote-env.sh
Currently running remote tests is only supported on Linux, so you need to use a devbox to run them
You can list devboxes via `applied_devbox ls`, pick the one with `codex` in the name.
Connect to devbox via `ssh <devbox_name>`.
Reuse the same checkout of codex in `~/code/codex`. Reset files if needed. Multiple checkouts take longer to build and take up more space.
Check whether the SHA and modified files are in sync between remote and local.

View File

@@ -132,9 +132,11 @@ runs:
keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}")
fi
entitlements_path="$GITHUB_ACTION_PATH/codex.entitlements.plist"
for binary in codex codex-responses-api-proxy; do
path="codex-rs/target/${TARGET}/release/${binary}"
codesign --force --options runtime --timestamp --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$path"
codesign --force --options runtime --timestamp --entitlements "$entitlements_path" --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$path"
done
- name: Notarize macOS binaries

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,24 @@
{
"outputs": {
"argument-comment-lint": {
"platforms": {
"macos-aarch64": {
"regex": "^argument-comment-lint-aarch64-apple-darwin\\.tar\\.gz$",
"path": "argument-comment-lint/bin/argument-comment-lint"
},
"linux-x86_64": {
"regex": "^argument-comment-lint-x86_64-unknown-linux-gnu\\.tar\\.gz$",
"path": "argument-comment-lint/bin/argument-comment-lint"
},
"linux-aarch64": {
"regex": "^argument-comment-lint-aarch64-unknown-linux-gnu\\.tar\\.gz$",
"path": "argument-comment-lint/bin/argument-comment-lint"
},
"windows-x86_64": {
"regex": "^argument-comment-lint-x86_64-pc-windows-msvc\\.zip$",
"path": "argument-comment-lint/bin/argument-comment-lint.exe"
}
}
}
}
}

287
.github/scripts/rusty_v8_bazel.py vendored Normal file
View File

@@ -0,0 +1,287 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import gzip
import re
import shutil
import subprocess
import sys
import tempfile
import tomllib
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
MUSL_RUNTIME_ARCHIVE_LABELS = [
"@llvm//runtimes/libcxx:libcxx.static",
"@llvm//runtimes/libcxx:libcxxabi.static",
]
LLVM_AR_LABEL = "@llvm//tools:llvm-ar"
LLVM_RANLIB_LABEL = "@llvm//tools:llvm-ranlib"
def bazel_execroot() -> Path:
result = subprocess.run(
["bazel", "info", "execution_root"],
cwd=ROOT,
check=True,
capture_output=True,
text=True,
)
return Path(result.stdout.strip())
def bazel_output_base() -> Path:
result = subprocess.run(
["bazel", "info", "output_base"],
cwd=ROOT,
check=True,
capture_output=True,
text=True,
)
return Path(result.stdout.strip())
def bazel_output_path(path: str) -> Path:
if path.startswith("external/"):
return bazel_output_base() / path
return bazel_execroot() / path
def bazel_output_files(
platform: str,
labels: list[str],
compilation_mode: str = "fastbuild",
) -> list[Path]:
expression = "set(" + " ".join(labels) + ")"
result = subprocess.run(
[
"bazel",
"cquery",
"-c",
compilation_mode,
f"--platforms=@llvm//platforms:{platform}",
"--output=files",
expression,
],
cwd=ROOT,
check=True,
capture_output=True,
text=True,
)
return [bazel_output_path(line.strip()) for line in result.stdout.splitlines() if line.strip()]
def bazel_build(
platform: str,
labels: list[str],
compilation_mode: str = "fastbuild",
) -> None:
subprocess.run(
[
"bazel",
"build",
"-c",
compilation_mode,
f"--platforms=@llvm//platforms:{platform}",
*labels,
],
cwd=ROOT,
check=True,
)
def ensure_bazel_output_files(
platform: str,
labels: list[str],
compilation_mode: str = "fastbuild",
) -> list[Path]:
outputs = bazel_output_files(platform, labels, compilation_mode)
if all(path.exists() for path in outputs):
return outputs
bazel_build(platform, labels, compilation_mode)
outputs = bazel_output_files(platform, labels, compilation_mode)
missing = [str(path) for path in outputs if not path.exists()]
if missing:
raise SystemExit(f"missing built outputs for {labels}: {missing}")
return outputs
def release_pair_label(target: str) -> str:
target_suffix = target.replace("-", "_")
return f"//third_party/v8:rusty_v8_release_pair_{target_suffix}"
def resolved_v8_crate_version() -> str:
cargo_lock = tomllib.loads((ROOT / "codex-rs" / "Cargo.lock").read_text())
versions = sorted(
{
package["version"]
for package in cargo_lock["package"]
if package["name"] == "v8"
}
)
if len(versions) == 1:
return versions[0]
if len(versions) > 1:
raise SystemExit(f"expected exactly one resolved v8 version, found: {versions}")
module_bazel = (ROOT / "MODULE.bazel").read_text()
matches = sorted(
set(
re.findall(
r'https://static\.crates\.io/crates/v8/v8-([0-9]+\.[0-9]+\.[0-9]+)\.crate',
module_bazel,
)
)
)
if len(matches) != 1:
raise SystemExit(
"expected exactly one pinned v8 crate version in MODULE.bazel, "
f"found: {matches}"
)
return matches[0]
def staged_archive_name(target: str, source_path: Path) -> str:
if source_path.suffix == ".lib":
return f"rusty_v8_release_{target}.lib.gz"
return f"librusty_v8_release_{target}.a.gz"
def is_musl_archive_target(target: str, source_path: Path) -> bool:
return target.endswith("-unknown-linux-musl") and source_path.suffix == ".a"
def single_bazel_output_file(
platform: str,
label: str,
compilation_mode: str = "fastbuild",
) -> Path:
outputs = ensure_bazel_output_files(platform, [label], compilation_mode)
if len(outputs) != 1:
raise SystemExit(f"expected exactly one output for {label}, found {outputs}")
return outputs[0]
def merged_musl_archive(
platform: str,
lib_path: Path,
compilation_mode: str = "fastbuild",
) -> Path:
llvm_ar = single_bazel_output_file(platform, LLVM_AR_LABEL, compilation_mode)
llvm_ranlib = single_bazel_output_file(platform, LLVM_RANLIB_LABEL, compilation_mode)
runtime_archives = [
single_bazel_output_file(platform, label, compilation_mode)
for label in MUSL_RUNTIME_ARCHIVE_LABELS
]
temp_dir = Path(tempfile.mkdtemp(prefix="rusty-v8-musl-stage-"))
merged_archive = temp_dir / lib_path.name
merge_commands = "\n".join(
[
f"create {merged_archive}",
f"addlib {lib_path}",
*[f"addlib {archive}" for archive in runtime_archives],
"save",
"end",
]
)
subprocess.run(
[str(llvm_ar), "-M"],
cwd=ROOT,
check=True,
input=merge_commands,
text=True,
)
subprocess.run([str(llvm_ranlib), str(merged_archive)], cwd=ROOT, check=True)
return merged_archive
def stage_release_pair(
platform: str,
target: str,
output_dir: Path,
compilation_mode: str = "fastbuild",
) -> None:
outputs = ensure_bazel_output_files(
platform,
[release_pair_label(target)],
compilation_mode,
)
try:
lib_path = next(path for path in outputs if path.suffix in {".a", ".lib"})
except StopIteration as exc:
raise SystemExit(f"missing static library output for {target}") from exc
try:
binding_path = next(path for path in outputs if path.suffix == ".rs")
except StopIteration as exc:
raise SystemExit(f"missing Rust binding output for {target}") from exc
output_dir.mkdir(parents=True, exist_ok=True)
staged_library = output_dir / staged_archive_name(target, lib_path)
staged_binding = output_dir / f"src_binding_release_{target}.rs"
source_archive = (
merged_musl_archive(platform, lib_path, compilation_mode)
if is_musl_archive_target(target, lib_path)
else lib_path
)
with source_archive.open("rb") as src, staged_library.open("wb") as dst:
with gzip.GzipFile(
filename="",
mode="wb",
fileobj=dst,
compresslevel=6,
mtime=0,
) as gz:
shutil.copyfileobj(src, gz)
shutil.copyfile(binding_path, staged_binding)
print(staged_library)
print(staged_binding)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command", required=True)
stage_release_pair_parser = subparsers.add_parser("stage-release-pair")
stage_release_pair_parser.add_argument("--platform", required=True)
stage_release_pair_parser.add_argument("--target", required=True)
stage_release_pair_parser.add_argument("--output-dir", required=True)
stage_release_pair_parser.add_argument(
"--compilation-mode",
default="fastbuild",
choices=["fastbuild", "opt", "dbg"],
)
subparsers.add_parser("resolved-v8-crate-version")
return parser.parse_args()
def main() -> int:
args = parse_args()
if args.command == "stage-release-pair":
stage_release_pair(
platform=args.platform,
target=args.target,
output_dir=Path(args.output_dir),
compilation_mode=args.compilation_mode,
)
return 0
if args.command == "resolved-v8-crate-version":
print(resolved_v8_crate_version())
return 0
raise SystemExit(f"unsupported command: {args.command}")
if __name__ == "__main__":
sys.exit(main())

View File

@@ -156,7 +156,6 @@ jobs:
bazel_args=(
test
//...
--test_verbose_timeout_warnings
--build_metadata=REPO_URL=https://github.com/openai/codex.git
--build_metadata=COMMIT_SHA=$(git rev-parse HEAD)
@@ -164,6 +163,13 @@ jobs:
--build_metadata=VISIBILITY=PUBLIC
)
bazel_targets=(
//...
# Keep V8 out of the ordinary Bazel CI path. Only the dedicated
# canary and release workflows should build `third_party/v8`.
-//third_party/v8:all
)
if [[ "${RUNNER_OS:-}" != "Windows" ]]; then
# Bazel test sandboxes on macOS may resolve an older Homebrew `node`
# before the `actions/setup-node` runtime on PATH.
@@ -183,6 +189,8 @@ jobs:
--bazelrc=.github/workflows/ci.bazelrc \
"${bazel_args[@]}" \
"--remote_header=x-buildbuddy-api-key=$BUILDBUDDY_API_KEY" \
-- \
"${bazel_targets[@]}" \
2>&1 | tee "$bazel_console_log"
bazel_status=${PIPESTATUS[0]}
set -e
@@ -210,6 +218,8 @@ jobs:
"${bazel_args[@]}" \
--remote_cache= \
--remote_executor= \
-- \
"${bazel_targets[@]}" \
2>&1 | tee "$bazel_console_log"
bazel_status=${PIPESTATUS[0]}
set -e

View File

@@ -91,17 +91,13 @@ jobs:
- name: cargo shear
run: cargo shear
argument_comment_lint:
name: Argument comment lint
argument_comment_lint_package:
name: Argument comment lint package
runs-on: ubuntu-24.04
needs: changed
if: ${{ needs.changed.outputs.argument_comment_lint == 'true' || needs.changed.outputs.workflows == 'true' || github.event_name == 'push' }}
if: ${{ needs.changed.outputs.argument_comment_lint_package == 'true' || github.event_name == 'push' }}
steps:
- uses: actions/checkout@v6
- name: Install Linux sandbox build dependencies
run: |
sudo DEBIAN_FRONTEND=noninteractive apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev
- uses: dtolnay/rust-toolchain@1.93.0
with:
toolchain: nightly-2025-09-18
@@ -120,14 +116,46 @@ jobs:
- name: Install cargo-dylint tooling
if: ${{ steps.cargo_dylint_cache.outputs.cache-hit != 'true' }}
run: cargo install --locked cargo-dylint dylint-link
- name: Check source wrapper syntax
run: bash -n tools/argument-comment-lint/run.sh
- name: Test argument comment lint package
if: ${{ needs.changed.outputs.argument_comment_lint_package == 'true' || github.event_name == 'push' }}
working-directory: tools/argument-comment-lint
run: cargo test
- name: Run argument comment lint on codex-rs
argument_comment_lint_prebuilt:
name: Argument comment lint - ${{ matrix.name }}
runs-on: ${{ matrix.runs_on || matrix.runner }}
needs: changed
if: ${{ needs.changed.outputs.argument_comment_lint == 'true' || needs.changed.outputs.workflows == 'true' || github.event_name == 'push' }}
strategy:
fail-fast: false
matrix:
include:
- name: Linux
runner: ubuntu-24.04
- name: macOS
runner: macos-15-xlarge
- name: Windows
runner: windows-x64
runs_on:
group: codex-runners
labels: codex-windows-x64
steps:
- uses: actions/checkout@v6
- name: Install Linux sandbox build dependencies
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
bash -n tools/argument-comment-lint/run.sh
./tools/argument-comment-lint/run.sh
sudo DEBIAN_FRONTEND=noninteractive apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev
- uses: dtolnay/rust-toolchain@1.93.0
with:
toolchain: nightly-2025-09-18
components: llvm-tools-preview, rustc-dev, rust-src
- uses: facebook/install-dotslash@v2
- name: Run argument comment lint on codex-rs
shell: bash
run: ./tools/argument-comment-lint/run-prebuilt-linter.sh
# --- CI to validate on different os/targets --------------------------------
lint_build:
@@ -141,8 +169,10 @@ jobs:
run:
working-directory: codex-rs
env:
# Speed up repeated builds across CI runs by caching compiled objects (non-Windows).
USE_SCCACHE: ${{ startsWith(matrix.runner, 'windows') && 'false' || 'true' }}
# Speed up repeated builds across CI runs by caching compiled objects, except on
# arm64 macOS runners cross-targeting x86_64 where ring/cc-rs can produce
# mixed-architecture archives under sccache.
USE_SCCACHE: ${{ (startsWith(matrix.runner, 'windows') || (matrix.runner == 'macos-15-xlarge' && matrix.target == 'x86_64-apple-darwin')) && 'false' || 'true' }}
CARGO_INCREMENTAL: "0"
SCCACHE_CACHE_SIZE: 10G
# In rust-ci, representative release-profile checks use thin LTO for faster feedback.
@@ -417,6 +447,24 @@ jobs:
echo "CFLAGS=${cflags}" >> "$GITHUB_ENV"
echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV"
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' }}
name: Configure musl rusty_v8 artifact overrides
env:
TARGET: ${{ matrix.target }}
shell: bash
run: |
set -euo pipefail
version="$(python3 "${GITHUB_WORKSPACE}/.github/scripts/rusty_v8_bazel.py" resolved-v8-crate-version)"
release_tag="rusty-v8-v${version}"
base_url="https://github.com/openai/codex/releases/download/${release_tag}"
archive="https://github.com/openai/codex/releases/download/rusty-v8-v${version}/librusty_v8_release_${TARGET}.a.gz"
binding_dir="${RUNNER_TEMP}/rusty_v8"
binding_path="${binding_dir}/src_binding_release_${TARGET}.rs"
mkdir -p "${binding_dir}"
curl -fsSL "${base_url}/src_binding_release_${TARGET}.rs" -o "${binding_path}"
echo "RUSTY_V8_ARCHIVE=${archive}" >> "$GITHUB_ENV"
echo "RUSTY_V8_SRC_BINDING_PATH=${binding_path}" >> "$GITHUB_ENV"
- name: Install cargo-chef
if: ${{ matrix.profile == 'release' }}
uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
@@ -497,7 +545,7 @@ jobs:
key: apt-${{ matrix.runner }}-${{ matrix.target }}-v1
tests:
name: Tests — ${{ matrix.runner }} - ${{ matrix.target }}
name: Tests — ${{ matrix.runner }} - ${{ matrix.target }}${{ matrix.remote_env == 'true' && ' (remote)' || '' }}
runs-on: ${{ matrix.runs_on || matrix.runner }}
timeout-minutes: 30
needs: changed
@@ -506,8 +554,10 @@ jobs:
run:
working-directory: codex-rs
env:
# Speed up repeated builds across CI runs by caching compiled objects (non-Windows).
USE_SCCACHE: ${{ startsWith(matrix.runner, 'windows') && 'false' || 'true' }}
# Speed up repeated builds across CI runs by caching compiled objects, except on
# arm64 macOS runners cross-targeting x86_64 where ring/cc-rs can produce
# mixed-architecture archives under sccache.
USE_SCCACHE: ${{ (startsWith(matrix.runner, 'windows') || (matrix.runner == 'macos-15-xlarge' && matrix.target == 'x86_64-apple-darwin')) && 'false' || 'true' }}
CARGO_INCREMENTAL: "0"
SCCACHE_CACHE_SIZE: 10G
@@ -521,6 +571,7 @@ jobs:
- runner: ubuntu-24.04
target: x86_64-unknown-linux-gnu
profile: dev
remote_env: "true"
runs_on:
group: codex-runners
labels: codex-linux-x64
@@ -558,6 +609,7 @@ jobs:
sudo apt-get update -y
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev
fi
# Some integration tests rely on DotSlash being installed.
# See https://github.com/openai/codex/pull/7617.
- name: Install DotSlash
@@ -642,6 +694,15 @@ jobs:
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
fi
- name: Set up remote test env (Docker)
if: ${{ runner.os == 'Linux' && matrix.remote_env == 'true' }}
shell: bash
run: |
set -euo pipefail
export CODEX_TEST_REMOTE_ENV_CONTAINER_NAME=codex-remote-test-env
source "${GITHUB_WORKSPACE}/scripts/test-remote-env.sh"
echo "CODEX_TEST_REMOTE_ENV=${CODEX_TEST_REMOTE_ENV}" >> "$GITHUB_ENV"
- name: tests
id: test
run: cargo nextest run --all-features --no-fail-fast --target ${{ matrix.target }} --cargo-profile ci-test --timings
@@ -694,6 +755,16 @@ jobs:
echo '```';
} >> "$GITHUB_STEP_SUMMARY"
- name: Tear down remote test env
if: ${{ always() && runner.os == 'Linux' && matrix.remote_env == 'true' }}
shell: bash
run: |
set +e
if [[ "${{ steps.test.outcome }}" != "success" ]]; then
docker logs codex-remote-test-env || true
fi
docker rm -f codex-remote-test-env >/dev/null 2>&1 || true
- name: verify tests passed
if: steps.test.outcome == 'failure'
run: |
@@ -704,14 +775,23 @@ jobs:
results:
name: CI results (required)
needs:
[changed, general, cargo_shear, argument_comment_lint, lint_build, tests]
[
changed,
general,
cargo_shear,
argument_comment_lint_package,
argument_comment_lint_prebuilt,
lint_build,
tests,
]
if: always()
runs-on: ubuntu-24.04
steps:
- name: Summarize
shell: bash
run: |
echo "arglint: ${{ needs.argument_comment_lint.result }}"
echo "argpkg : ${{ needs.argument_comment_lint_package.result }}"
echo "arglint: ${{ needs.argument_comment_lint_prebuilt.result }}"
echo "general: ${{ needs.general.result }}"
echo "shear : ${{ needs.cargo_shear.result }}"
echo "lint : ${{ needs.lint_build.result }}"
@@ -724,8 +804,12 @@ jobs:
exit 0
fi
if [[ '${{ needs.changed.outputs.argument_comment_lint_package }}' == 'true' || '${{ github.event_name }}' == 'push' ]]; then
[[ '${{ needs.argument_comment_lint_package.result }}' == 'success' ]] || { echo 'argument_comment_lint_package failed'; exit 1; }
fi
if [[ '${{ needs.changed.outputs.argument_comment_lint }}' == 'true' || '${{ needs.changed.outputs.workflows }}' == 'true' || '${{ github.event_name }}' == 'push' ]]; then
[[ '${{ needs.argument_comment_lint.result }}' == 'success' ]] || { echo 'argument_comment_lint failed'; exit 1; }
[[ '${{ needs.argument_comment_lint_prebuilt.result }}' == 'success' ]] || { echo 'argument_comment_lint_prebuilt failed'; exit 1; }
fi
if [[ '${{ needs.changed.outputs.codex }}' == 'true' || '${{ needs.changed.outputs.workflows }}' == 'true' || '${{ github.event_name }}' == 'push' ]]; then

View File

@@ -0,0 +1,103 @@
name: rust-release-argument-comment-lint
on:
workflow_call:
inputs:
publish:
required: true
type: boolean
jobs:
skip:
if: ${{ !inputs.publish }}
runs-on: ubuntu-latest
steps:
- run: echo "Skipping argument-comment-lint release assets for prerelease tag"
build:
if: ${{ inputs.publish }}
name: Build - ${{ matrix.runner }} - ${{ matrix.target }}
runs-on: ${{ matrix.runs_on || matrix.runner }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15-xlarge
target: aarch64-apple-darwin
archive_name: argument-comment-lint-aarch64-apple-darwin.tar.gz
lib_name: libargument_comment_lint@nightly-2025-09-18-aarch64-apple-darwin.dylib
runner_binary: argument-comment-lint
cargo_dylint_binary: cargo-dylint
- runner: ubuntu-24.04
target: x86_64-unknown-linux-gnu
archive_name: argument-comment-lint-x86_64-unknown-linux-gnu.tar.gz
lib_name: libargument_comment_lint@nightly-2025-09-18-x86_64-unknown-linux-gnu.so
runner_binary: argument-comment-lint
cargo_dylint_binary: cargo-dylint
- runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
archive_name: argument-comment-lint-aarch64-unknown-linux-gnu.tar.gz
lib_name: libargument_comment_lint@nightly-2025-09-18-aarch64-unknown-linux-gnu.so
runner_binary: argument-comment-lint
cargo_dylint_binary: cargo-dylint
- runner: windows-x64
target: x86_64-pc-windows-msvc
archive_name: argument-comment-lint-x86_64-pc-windows-msvc.zip
lib_name: argument_comment_lint@nightly-2025-09-18-x86_64-pc-windows-msvc.dll
runner_binary: argument-comment-lint.exe
cargo_dylint_binary: cargo-dylint.exe
runs_on:
group: codex-runners
labels: codex-windows-x64
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@1.93.0
with:
toolchain: nightly-2025-09-18
targets: ${{ matrix.target }}
components: llvm-tools-preview, rustc-dev, rust-src
- name: Install tooling
shell: bash
run: |
install_root="${RUNNER_TEMP}/argument-comment-lint-tools"
cargo install --locked cargo-dylint --root "$install_root"
cargo install --locked dylint-link
echo "INSTALL_ROOT=$install_root" >> "$GITHUB_ENV"
- name: Cargo build
working-directory: tools/argument-comment-lint
shell: bash
run: cargo build --release --target ${{ matrix.target }}
- name: Stage artifact
shell: bash
run: |
dest="dist/argument-comment-lint/${{ matrix.target }}"
mkdir -p "$dest"
package_root="${RUNNER_TEMP}/argument-comment-lint"
rm -rf "$package_root"
mkdir -p "$package_root/bin" "$package_root/lib"
cp "tools/argument-comment-lint/target/${{ matrix.target }}/release/${{ matrix.runner_binary }}" \
"$package_root/bin/${{ matrix.runner_binary }}"
cp "${INSTALL_ROOT}/bin/${{ matrix.cargo_dylint_binary }}" \
"$package_root/bin/${{ matrix.cargo_dylint_binary }}"
cp "tools/argument-comment-lint/target/${{ matrix.target }}/release/${{ matrix.lib_name }}" \
"$package_root/lib/${{ matrix.lib_name }}"
archive_path="$dest/${{ matrix.archive_name }}"
if [[ "${{ runner.os }}" == "Windows" ]]; then
(cd "${RUNNER_TEMP}" && 7z a "$GITHUB_WORKSPACE/$archive_path" argument-comment-lint >/dev/null)
else
(cd "${RUNNER_TEMP}" && tar -czf "$GITHUB_WORKSPACE/$archive_path" argument-comment-lint)
fi
- uses: actions/upload-artifact@v7
with:
name: argument-comment-lint-${{ matrix.target }}
path: dist/argument-comment-lint/${{ matrix.target }}/*

View File

@@ -210,6 +210,24 @@ jobs:
echo "CFLAGS=${cflags}" >> "$GITHUB_ENV"
echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV"
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' }}
name: Configure musl rusty_v8 artifact overrides
env:
TARGET: ${{ matrix.target }}
shell: bash
run: |
set -euo pipefail
version="$(python3 "${GITHUB_WORKSPACE}/.github/scripts/rusty_v8_bazel.py" resolved-v8-crate-version)"
release_tag="rusty-v8-v${version}"
base_url="https://github.com/openai/codex/releases/download/${release_tag}"
archive="https://github.com/openai/codex/releases/download/rusty-v8-v${version}/librusty_v8_release_${TARGET}.a.gz"
binding_dir="${RUNNER_TEMP}/rusty_v8"
binding_path="${binding_dir}/src_binding_release_${TARGET}.rs"
mkdir -p "${binding_dir}"
curl -fsSL "${base_url}/src_binding_release_${TARGET}.rs" -o "${binding_path}"
echo "RUSTY_V8_ARCHIVE=${archive}" >> "$GITHUB_ENV"
echo "RUSTY_V8_SRC_BINDING_PATH=${binding_path}" >> "$GITHUB_ENV"
- name: Cargo build
shell: bash
run: |
@@ -380,11 +398,19 @@ jobs:
publish: true
secrets: inherit
argument-comment-lint-release-assets:
name: argument-comment-lint release assets
needs: tag-check
uses: ./.github/workflows/rust-release-argument-comment-lint.yml
with:
publish: true
release:
needs:
- build
- build-windows
- shell-tool-mcp
- argument-comment-lint-release-assets
name: release
runs-on: ubuntu-latest
permissions:
@@ -521,6 +547,13 @@ jobs:
tag: ${{ github.ref_name }}
config: .github/dotslash-config.json
- uses: facebook/dotslash-publish-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag: ${{ github.ref_name }}
config: .github/dotslash-argument-comment-lint-config.json
- name: Trigger developers.openai.com deploy
# Only trigger the deploy if the release is not a pre-release.
# The deploy is used to update the developers.openai.com website with the new config schema json file.

188
.github/workflows/rusty-v8-release.yml vendored Normal file
View File

@@ -0,0 +1,188 @@
name: rusty-v8-release
on:
workflow_dispatch:
inputs:
release_tag:
description: Optional release tag. Defaults to rusty-v8-v<resolved_v8_version>.
required: false
type: string
publish:
description: Publish the staged musl artifacts to a GitHub release.
required: false
default: true
type: boolean
concurrency:
group: ${{ github.workflow }}::${{ inputs.release_tag || github.run_id }}
cancel-in-progress: false
jobs:
metadata:
runs-on: ubuntu-latest
outputs:
release_tag: ${{ steps.release_tag.outputs.release_tag }}
v8_version: ${{ steps.v8_version.outputs.version }}
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Resolve exact v8 crate version
id: v8_version
shell: bash
run: |
set -euo pipefail
version="$(python3 .github/scripts/rusty_v8_bazel.py resolved-v8-crate-version)"
echo "version=${version}" >> "$GITHUB_OUTPUT"
- name: Resolve release tag
id: release_tag
env:
RELEASE_TAG_INPUT: ${{ inputs.release_tag }}
V8_VERSION: ${{ steps.v8_version.outputs.version }}
shell: bash
run: |
set -euo pipefail
release_tag="${RELEASE_TAG_INPUT}"
if [[ -z "${release_tag}" ]]; then
release_tag="rusty-v8-v${V8_VERSION}"
fi
echo "release_tag=${release_tag}" >> "$GITHUB_OUTPUT"
build:
name: Build ${{ matrix.target }}
needs: metadata
runs-on: ${{ matrix.runner }}
permissions:
contents: read
actions: read
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
platform: linux_amd64_musl
target: x86_64-unknown-linux-musl
- runner: ubuntu-24.04-arm
platform: linux_arm64_musl
target: aarch64-unknown-linux-musl
steps:
- uses: actions/checkout@v6
- name: Set up Bazel
uses: bazelbuild/setup-bazelisk@v3
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Build Bazel V8 release pair
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
PLATFORM: ${{ matrix.platform }}
TARGET: ${{ matrix.target }}
shell: bash
run: |
set -euo pipefail
target_suffix="${TARGET//-/_}"
pair_target="//third_party/v8:rusty_v8_release_pair_${target_suffix}"
extra_targets=()
if [[ "${TARGET}" == *-unknown-linux-musl ]]; then
extra_targets=(
"@llvm//runtimes/libcxx:libcxx.static"
"@llvm//runtimes/libcxx:libcxxabi.static"
)
fi
bazel_args=(
build
-c
opt
"--platforms=@llvm//platforms:${PLATFORM}"
"${pair_target}"
"${extra_targets[@]}"
--build_metadata=COMMIT_SHA=$(git rev-parse HEAD)
)
bazel \
--noexperimental_remote_repo_contents_cache \
--bazelrc=.github/workflows/v8-ci.bazelrc \
"${bazel_args[@]}" \
"--remote_header=x-buildbuddy-api-key=${BUILDBUDDY_API_KEY}"
- name: Stage release pair
env:
PLATFORM: ${{ matrix.platform }}
TARGET: ${{ matrix.target }}
shell: bash
run: |
set -euo pipefail
python3 .github/scripts/rusty_v8_bazel.py stage-release-pair \
--platform "${PLATFORM}" \
--target "${TARGET}" \
--compilation-mode opt \
--output-dir "dist/${TARGET}"
- name: Upload staged musl artifacts
uses: actions/upload-artifact@v7
with:
name: rusty-v8-${{ needs.metadata.outputs.v8_version }}-${{ matrix.target }}
path: dist/${{ matrix.target }}/*
publish-release:
if: ${{ inputs.publish }}
needs:
- metadata
- build
runs-on: ubuntu-latest
permissions:
contents: write
actions: read
steps:
- name: Ensure publishing from default branch
if: ${{ github.ref_name != github.event.repository.default_branch }}
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
shell: bash
run: |
set -euo pipefail
echo "Publishing is only allowed from ${DEFAULT_BRANCH}; current ref is ${GITHUB_REF_NAME}." >&2
exit 1
- name: Ensure release tag is new
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.metadata.outputs.release_tag }}
shell: bash
run: |
set -euo pipefail
if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" > /dev/null 2>&1; then
echo "Release tag ${RELEASE_TAG} already exists; musl artifact tags are immutable." >&2
exit 1
fi
- uses: actions/download-artifact@v8
with:
path: dist
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.metadata.outputs.release_tag }}
name: ${{ needs.metadata.outputs.release_tag }}
files: dist/**
# Keep V8 artifact releases out of Codex's normal "latest release" channel.
prerelease: true

132
.github/workflows/v8-canary.yml vendored Normal file
View File

@@ -0,0 +1,132 @@
name: v8-canary
on:
pull_request:
paths:
- ".github/scripts/rusty_v8_bazel.py"
- ".github/workflows/rusty-v8-release.yml"
- ".github/workflows/v8-canary.yml"
- "MODULE.bazel"
- "MODULE.bazel.lock"
- "codex-rs/Cargo.toml"
- "patches/BUILD.bazel"
- "patches/v8_*.patch"
- "third_party/v8/**"
push:
branches:
- main
paths:
- ".github/scripts/rusty_v8_bazel.py"
- ".github/workflows/rusty-v8-release.yml"
- ".github/workflows/v8-canary.yml"
- "MODULE.bazel"
- "MODULE.bazel.lock"
- "codex-rs/Cargo.toml"
- "patches/BUILD.bazel"
- "patches/v8_*.patch"
- "third_party/v8/**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}::${{ github.event.pull_request.number > 0 && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
jobs:
metadata:
runs-on: ubuntu-latest
outputs:
v8_version: ${{ steps.v8_version.outputs.version }}
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Resolve exact v8 crate version
id: v8_version
shell: bash
run: |
set -euo pipefail
version="$(python3 .github/scripts/rusty_v8_bazel.py resolved-v8-crate-version)"
echo "version=${version}" >> "$GITHUB_OUTPUT"
build:
name: Build ${{ matrix.target }}
needs: metadata
runs-on: ${{ matrix.runner }}
permissions:
contents: read
actions: read
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
platform: linux_amd64_musl
target: x86_64-unknown-linux-musl
- runner: ubuntu-24.04-arm
platform: linux_arm64_musl
target: aarch64-unknown-linux-musl
steps:
- uses: actions/checkout@v6
- name: Set up Bazel
uses: bazelbuild/setup-bazelisk@v3
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Build Bazel V8 release pair
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
PLATFORM: ${{ matrix.platform }}
TARGET: ${{ matrix.target }}
shell: bash
run: |
set -euo pipefail
target_suffix="${TARGET//-/_}"
pair_target="//third_party/v8:rusty_v8_release_pair_${target_suffix}"
extra_targets=(
"@llvm//runtimes/libcxx:libcxx.static"
"@llvm//runtimes/libcxx:libcxxabi.static"
)
bazel_args=(
build
"--platforms=@llvm//platforms:${PLATFORM}"
"${pair_target}"
"${extra_targets[@]}"
--build_metadata=COMMIT_SHA=$(git rev-parse HEAD)
)
bazel \
--noexperimental_remote_repo_contents_cache \
--bazelrc=.github/workflows/v8-ci.bazelrc \
"${bazel_args[@]}" \
"--remote_header=x-buildbuddy-api-key=${BUILDBUDDY_API_KEY}"
- name: Stage release pair
env:
PLATFORM: ${{ matrix.platform }}
TARGET: ${{ matrix.target }}
shell: bash
run: |
set -euo pipefail
python3 .github/scripts/rusty_v8_bazel.py stage-release-pair \
--platform "${PLATFORM}" \
--target "${TARGET}" \
--output-dir "dist/${TARGET}"
- name: Upload staged musl artifacts
uses: actions/upload-artifact@v7
with:
name: v8-canary-${{ needs.metadata.outputs.v8_version }}-${{ matrix.target }}
path: dist/${{ matrix.target }}/*

5
.github/workflows/v8-ci.bazelrc vendored Normal file
View File

@@ -0,0 +1,5 @@
import %workspace%/.github/workflows/ci.bazelrc
common --build_metadata=REPO_URL=https://github.com/openai/codex.git
common --build_metadata=ROLE=CI
common --build_metadata=VISIBILITY=PUBLIC

View File

@@ -48,6 +48,8 @@ Run `just fmt` (in `codex-rs` directory) automatically after you have finished m
Before finalizing a large change to `codex-rs`, run `just fix -p <project>` (in `codex-rs` directory) to fix any linter issues in the code. Prefer scoping with `-p` to avoid slow workspacewide Clippy builds; only run `just fix` without `-p` if you changed shared crates. Do not re-run tests after running `fix` or `fmt`.
Also run `just argument-comment-lint` to ensure the codebase is clean of comment lint errors.
## TUI style conventions
See `codex-rs/tui/styles.md`.

View File

@@ -1,31 +1,42 @@
module(name = "codex")
bazel_dep(name = "bazel_skylib", version = "1.8.2")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "llvm", version = "0.6.7")
bazel_dep(name = "llvm", version = "0.6.8")
register_toolchains("@llvm//toolchain:all")
osx = use_extension("@llvm//extensions:osx.bzl", "osx")
osx.framework(name = "ApplicationServices")
osx.framework(name = "AppKit")
osx.framework(name = "ColorSync")
osx.framework(name = "CoreFoundation")
osx.framework(name = "CoreGraphics")
osx.framework(name = "CoreServices")
osx.framework(name = "CoreText")
osx.framework(name = "AudioToolbox")
osx.framework(name = "CFNetwork")
osx.framework(name = "FontServices")
osx.framework(name = "AudioUnit")
osx.framework(name = "CoreAudio")
osx.framework(name = "CoreAudioTypes")
osx.framework(name = "Foundation")
osx.framework(name = "ImageIO")
osx.framework(name = "IOKit")
osx.framework(name = "Kernel")
osx.framework(name = "OSLog")
osx.framework(name = "Security")
osx.framework(name = "SystemConfiguration")
osx.from_archive(
sha256 = "6a4922f89487a96d7054ec6ca5065bfddd9f1d017c74d82f1d79cecf7feb8228",
strip_prefix = "Payload/Library/Developer/CommandLineTools/SDKs/MacOSX26.2.sdk",
type = "pkg",
urls = [
"https://swcdn.apple.com/content/downloads/26/44/047-81934-A_28TPKM5SD1/ps6pk6dk4x02vgfa5qsctq6tgf23t5f0w2/CLTools_macOSNMOS_SDK.pkg",
],
)
osx.frameworks(names = [
"ApplicationServices",
"AppKit",
"ColorSync",
"CoreFoundation",
"CoreGraphics",
"CoreServices",
"CoreText",
"AudioToolbox",
"CFNetwork",
"FontServices",
"AudioUnit",
"CoreAudio",
"CoreAudioTypes",
"Foundation",
"ImageIO",
"IOKit",
"Kernel",
"OSLog",
"Security",
"SystemConfiguration",
])
use_repo(osx, "macos_sdk")
# Needed to disable xcode...
@@ -132,6 +143,35 @@ crate.annotation(
workspace_cargo_toml = "rust/runfiles/Cargo.toml",
)
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
new_local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")
new_local_repository(
name = "v8_targets",
build_file = "//third_party/v8:BUILD.bazel",
path = "third_party/v8",
)
crate.annotation(
build_script_data = [
"@v8_targets//:rusty_v8_archive_for_target",
"@v8_targets//:rusty_v8_binding_for_target",
],
build_script_env = {
"RUSTY_V8_ARCHIVE": "$(execpath @v8_targets//:rusty_v8_archive_for_target)",
"RUSTY_V8_SRC_BINDING_PATH": "$(execpath @v8_targets//:rusty_v8_binding_for_target)",
},
crate = "v8",
gen_build_script = "on",
patch_args = ["-p1"],
patches = [
"//patches:rusty_v8_prebuilt_out_dir.patch",
],
)
inject_repo(crate, "v8_targets")
llvm = use_extension("@llvm//extensions:llvm.bzl", "llvm")
use_repo(llvm, "llvm-project")
@@ -174,6 +214,109 @@ crate.annotation(
inject_repo(crate, "alsa_lib")
bazel_dep(name = "v8", version = "14.6.202.9")
archive_override(
module_name = "v8",
integrity = "sha256-JphDwLAzsd9KvgRZ7eQvNtPU6qGd3XjFt/a/1QITAJU=",
patch_strip = 3,
patches = [
"//patches:v8_module_deps.patch",
"//patches:v8_bazel_rules.patch",
"//patches:v8_source_portability.patch",
],
strip_prefix = "v8-14.6.202.9",
urls = ["https://github.com/v8/v8/archive/refs/tags/14.6.202.9.tar.gz"],
)
http_archive(
name = "v8_crate_146_4_0",
build_file = "//third_party/v8:v8_crate.BUILD.bazel",
sha256 = "d97bcac5cdc5a195a4813f1855a6bc658f240452aac36caa12fd6c6f16026ab1",
strip_prefix = "v8-146.4.0",
type = "tar.gz",
urls = ["https://static.crates.io/crates/v8/v8-146.4.0.crate"],
)
http_file(
name = "rusty_v8_146_4_0_aarch64_apple_darwin_archive",
downloaded_file_path = "librusty_v8_release_aarch64-apple-darwin.a.gz",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/librusty_v8_release_aarch64-apple-darwin.a.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_aarch64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_aarch64-unknown-linux-gnu.a.gz",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/librusty_v8_release_aarch64-unknown-linux-gnu.a.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_aarch64_pc_windows_msvc_archive",
downloaded_file_path = "rusty_v8_release_aarch64-pc-windows-msvc.lib.gz",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/rusty_v8_release_aarch64-pc-windows-msvc.lib.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_x86_64_apple_darwin_archive",
downloaded_file_path = "librusty_v8_release_x86_64-apple-darwin.a.gz",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/librusty_v8_release_x86_64-apple-darwin.a.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_x86_64_pc_windows_msvc_archive",
downloaded_file_path = "rusty_v8_release_x86_64-pc-windows-msvc.lib.gz",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/rusty_v8_release_x86_64-pc-windows-msvc.lib.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_aarch64_unknown_linux_musl_archive",
downloaded_file_path = "librusty_v8_release_aarch64-unknown-linux-musl.a.gz",
urls = [
"https://github.com/openai/codex/releases/download/rusty-v8-v146.4.0/librusty_v8_release_aarch64-unknown-linux-musl.a.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_aarch64_unknown_linux_musl_binding",
downloaded_file_path = "src_binding_release_aarch64-unknown-linux-musl.rs",
urls = [
"https://github.com/openai/codex/releases/download/rusty-v8-v146.4.0/src_binding_release_aarch64-unknown-linux-musl.rs",
],
)
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_musl_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-musl.a.gz",
urls = [
"https://github.com/openai/codex/releases/download/rusty-v8-v146.4.0/librusty_v8_release_x86_64-unknown-linux-musl.a.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_musl_binding",
downloaded_file_path = "src_binding_release_x86_64-unknown-linux-musl.rs",
urls = [
"https://github.com/openai/codex/releases/download/rusty-v8-v146.4.0/src_binding_release_x86_64-unknown-linux-musl.rs",
],
)
use_repo(crate, "crates")
bazel_dep(name = "libcap", version = "2.27.bcr.1")

36
MODULE.bazel.lock generated

File diff suppressed because one or more lines are too long

410
codex-rs/Cargo.lock generated
View File

@@ -380,7 +380,7 @@ version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -391,7 +391,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -409,6 +409,7 @@ dependencies = [
"chrono",
"codex-app-server-protocol",
"codex-core",
"codex-features",
"codex-protocol",
"codex-utils-cargo-bin",
"core_test_support",
@@ -799,9 +800,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "aws-lc-rs"
version = "1.15.4"
version = "1.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256"
checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
dependencies = [
"aws-lc-sys",
"untrusted 0.7.1",
@@ -810,9 +811,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.37.0"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a"
checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a"
dependencies = [
"cc",
"cmake",
@@ -948,6 +949,8 @@ dependencies = [
"cexpr",
"clang-sys",
"itertools 0.13.0",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
@@ -1151,6 +1154,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
[[package]]
name = "calendrical_calculations"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a0b39595c6ee54a8d0900204ba4c401d0ab4eb45adaf07178e8d017541529e7"
dependencies = [
"core_maths",
"displaydoc",
]
[[package]]
name = "cassowary"
version = "0.3.0"
@@ -1427,7 +1440,8 @@ dependencies = [
"codex-chatgpt",
"codex-cloud-requirements",
"codex-core",
"codex-environment",
"codex-exec-server",
"codex-features",
"codex-feedback",
"codex-file-search",
"codex-login",
@@ -1474,6 +1488,7 @@ dependencies = [
"codex-app-server-protocol",
"codex-arg0",
"codex-core",
"codex-features",
"codex-feedback",
"codex-protocol",
"futures",
@@ -1581,7 +1596,7 @@ dependencies = [
"thiserror 2.0.18",
"tokio",
"url",
"which",
"which 8.0.0",
"wiremock",
"zip",
]
@@ -1657,6 +1672,7 @@ dependencies = [
"codex-core",
"codex-exec",
"codex-execpolicy",
"codex-features",
"codex-login",
"codex-mcp-server",
"codex-protocol",
@@ -1664,6 +1680,7 @@ dependencies = [
"codex-rmcp-client",
"codex-state",
"codex-stdio-to-uds",
"codex-terminal-detection",
"codex-tui",
"codex-tui-app-server",
"codex-utils-cargo-bin",
@@ -1783,6 +1800,20 @@ dependencies = [
"thiserror 2.0.18",
]
[[package]]
name = "codex-code-mode"
version = "0.0.0"
dependencies = [
"async-trait",
"pretty_assertions",
"serde",
"serde_json",
"tokio",
"tokio-util",
"tracing",
"v8",
]
[[package]]
name = "codex-config"
version = "0.0.0"
@@ -1840,15 +1871,16 @@ dependencies = [
"codex-arg0",
"codex-artifacts",
"codex-async-utils",
"codex-client",
"codex-code-mode",
"codex-config",
"codex-connectors",
"codex-environment",
"codex-exec-server",
"codex-execpolicy",
"codex-features",
"codex-file-search",
"codex-git",
"codex-hooks",
"codex-keyring-store",
"codex-login",
"codex-network-proxy",
"codex-otel",
"codex-protocol",
@@ -1858,6 +1890,7 @@ dependencies = [
"codex-shell-escalation",
"codex-skills",
"codex-state",
"codex-terminal-detection",
"codex-test-macros",
"codex-utils-absolute-path",
"codex-utils-cache",
@@ -1884,7 +1917,6 @@ dependencies = [
"image",
"indexmap 2.13.0",
"insta",
"keyring",
"landlock",
"libc",
"maplit",
@@ -1893,7 +1925,6 @@ dependencies = [
"openssl-sys",
"opentelemetry",
"opentelemetry_sdk",
"os_info",
"predicates",
"pretty_assertions",
"rand 0.9.2",
@@ -1907,7 +1938,6 @@ dependencies = [
"serde_yaml",
"serial_test",
"sha1",
"sha2",
"shlex",
"similar",
"tempfile",
@@ -1927,7 +1957,7 @@ dependencies = [
"url",
"uuid",
"walkdir",
"which",
"which 8.0.0",
"wildmatch",
"windows-sys 0.52.0",
"wiremock",
@@ -1947,17 +1977,6 @@ dependencies = [
"serde_json",
]
[[package]]
name = "codex-environment"
version = "0.0.0"
dependencies = [
"async-trait",
"codex-utils-absolute-path",
"pretty_assertions",
"tempfile",
"tokio",
]
[[package]]
name = "codex-exec"
version = "0.0.0"
@@ -2003,6 +2022,30 @@ dependencies = [
"wiremock",
]
[[package]]
name = "codex-exec-server"
version = "0.0.0"
dependencies = [
"anyhow",
"async-trait",
"base64 0.22.1",
"clap",
"codex-app-server-protocol",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
"codex-utils-pty",
"futures",
"pretty_assertions",
"serde",
"serde_json",
"tempfile",
"test-case",
"thiserror 2.0.18",
"tokio",
"tokio-tungstenite",
"tracing",
]
[[package]]
name = "codex-execpolicy"
version = "0.0.0"
@@ -2049,6 +2092,20 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "codex-features"
version = "0.0.0"
dependencies = [
"codex-login",
"codex-otel",
"codex-protocol",
"pretty_assertions",
"schemars 0.8.22",
"serde",
"toml 0.9.11+spec-1.1.0",
"tracing",
]
[[package]]
name = "codex-feedback"
version = "0.0.0"
@@ -2149,7 +2206,7 @@ dependencies = [
"serde_json",
"tokio",
"tracing",
"which",
"which 8.0.0",
"wiremock",
]
@@ -2158,19 +2215,30 @@ name = "codex-login"
version = "0.0.0"
dependencies = [
"anyhow",
"async-trait",
"base64 0.22.1",
"chrono",
"codex-app-server-protocol",
"codex-client",
"codex-core",
"codex-config",
"codex-keyring-store",
"codex-protocol",
"codex-terminal-detection",
"core_test_support",
"keyring",
"once_cell",
"os_info",
"pretty_assertions",
"rand 0.9.2",
"regex-lite",
"reqwest",
"schemars 0.8.22",
"serde",
"serde_json",
"serial_test",
"sha2",
"tempfile",
"thiserror 2.0.18",
"tiny_http",
"tokio",
"tracing",
@@ -2187,6 +2255,7 @@ dependencies = [
"anyhow",
"codex-arg0",
"codex-core",
"codex-features",
"codex-protocol",
"codex-shell-command",
"codex-utils-cli",
@@ -2262,6 +2331,7 @@ version = "0.0.0"
dependencies = [
"chrono",
"codex-api",
"codex-app-server-protocol",
"codex-protocol",
"codex-utils-absolute-path",
"codex-utils-string",
@@ -2328,6 +2398,7 @@ dependencies = [
"icu_locale_core",
"icu_provider",
"pretty_assertions",
"quick-xml",
"schemars 0.8.22",
"serde",
"serde_json",
@@ -2388,7 +2459,7 @@ dependencies = [
"tracing",
"urlencoding",
"webbrowser",
"which",
"which 8.0.0",
]
[[package]]
@@ -2428,7 +2499,7 @@ dependencies = [
"tree-sitter",
"tree-sitter-bash",
"url",
"which",
"which 8.0.0",
]
[[package]]
@@ -2476,6 +2547,7 @@ dependencies = [
"serde",
"serde_json",
"sqlx",
"strum 0.27.2",
"tokio",
"tracing",
"tracing-subscriber",
@@ -2493,6 +2565,14 @@ dependencies = [
"uds_windows",
]
[[package]]
name = "codex-terminal-detection"
version = "0.0.0"
dependencies = [
"pretty_assertions",
"tracing",
]
[[package]]
name = "codex-test-macros"
version = "0.0.0"
@@ -2513,6 +2593,7 @@ dependencies = [
"chrono",
"clap",
"codex-ansi-escape",
"codex-app-server-client",
"codex-app-server-protocol",
"codex-arg0",
"codex-backend-client",
@@ -2521,6 +2602,7 @@ dependencies = [
"codex-client",
"codex-cloud-requirements",
"codex-core",
"codex-features",
"codex-feedback",
"codex-file-search",
"codex-login",
@@ -2528,6 +2610,7 @@ dependencies = [
"codex-protocol",
"codex-shell-command",
"codex-state",
"codex-terminal-detection",
"codex-tui-app-server",
"codex-utils-absolute-path",
"codex-utils-approval-presets",
@@ -2588,7 +2671,7 @@ dependencies = [
"uuid",
"vt100",
"webbrowser",
"which",
"which 8.0.0",
"windows-sys 0.52.0",
"winsplit",
]
@@ -2612,6 +2695,7 @@ dependencies = [
"codex-client",
"codex-cloud-requirements",
"codex-core",
"codex-features",
"codex-feedback",
"codex-file-search",
"codex-login",
@@ -2619,6 +2703,7 @@ dependencies = [
"codex-protocol",
"codex-shell-command",
"codex-state",
"codex-terminal-detection",
"codex-utils-absolute-path",
"codex-utils-approval-presets",
"codex-utils-cargo-bin",
@@ -2678,7 +2763,7 @@ dependencies = [
"uuid",
"vt100",
"webbrowser",
"which",
"which 8.0.0",
"windows-sys 0.52.0",
"winsplit",
]
@@ -2849,6 +2934,14 @@ dependencies = [
"regex-lite",
]
[[package]]
name = "codex-v8-poc"
version = "0.0.0"
dependencies = [
"pretty_assertions",
"v8",
]
[[package]]
name = "codex-windows-sandbox"
version = "0.0.0"
@@ -3054,6 +3147,15 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core_maths"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
dependencies = [
"libm",
]
[[package]]
name = "core_test_support"
version = "0.0.0"
@@ -3061,13 +3163,18 @@ dependencies = [
"anyhow",
"assert_cmd",
"base64 0.22.1",
"codex-arg0",
"codex-core",
"codex-exec-server",
"codex-features",
"codex-protocol",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
"ctor 0.6.3",
"futures",
"notify",
"opentelemetry",
"opentelemetry_sdk",
"pretty_assertions",
"regex-lite",
"reqwest",
@@ -3076,6 +3183,9 @@ dependencies = [
"tempfile",
"tokio",
"tokio-tungstenite",
"tracing",
"tracing-opentelemetry",
"tracing-subscriber",
"walkdir",
"wiremock",
"zstd",
@@ -3651,6 +3761,38 @@ dependencies = [
"subtle",
]
[[package]]
name = "diplomat"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9adb46b05e2f53dcf6a7dfc242e4ce9eb60c369b6b6eb10826a01e93167f59c6"
dependencies = [
"diplomat_core",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "diplomat-runtime"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0569bd3caaf13829da7ee4e83dbf9197a0e1ecd72772da6d08f0b4c9285c8d29"
[[package]]
name = "diplomat_core"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51731530ed7f2d4495019abc7df3744f53338e69e2863a6a64ae91821c763df1"
dependencies = [
"proc-macro2",
"quote",
"serde",
"smallvec",
"strck",
"syn 2.0.114",
]
[[package]]
name = "dirs"
version = "6.0.0"
@@ -3679,7 +3821,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -3924,7 +4066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -4257,6 +4399,16 @@ dependencies = [
"libc",
]
[[package]]
name = "fslock"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "futures"
version = "0.3.31"
@@ -4485,6 +4637,15 @@ dependencies = [
"regex-syntax 0.8.8",
]
[[package]]
name = "gzip-header"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2"
dependencies = [
"crc32fast",
]
[[package]]
name = "h2"
version = "0.4.13"
@@ -4942,6 +5103,28 @@ dependencies = [
"cc",
]
[[package]]
name = "icu_calendar"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f0e52e009b6b16ba9c0693578796f2dd4aaa59a7f8f920423706714a89ac4e"
dependencies = [
"calendrical_calculations",
"displaydoc",
"icu_calendar_data",
"icu_locale",
"icu_locale_core",
"icu_provider",
"tinystr",
"zerovec",
]
[[package]]
name = "icu_calendar_data"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527f04223b17edfe0bd43baf14a0cb1b017830db65f3950dc00224860a9a446d"
[[package]]
name = "icu_collections"
version = "2.1.1"
@@ -5328,7 +5511,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -5376,6 +5559,12 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "ixdtf"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84de9d95a6d2547d9b77ee3f25fa0ee32e3c3a6484d47a55adebc0439c077992"
[[package]]
name = "jiff"
version = "0.2.18"
@@ -5796,6 +5985,7 @@ dependencies = [
"anyhow",
"codex-core",
"codex-mcp-server",
"codex-terminal-detection",
"codex-utils-cargo-bin",
"core_test_support",
"os_info",
@@ -6084,7 +6274,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -6728,7 +6918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.45.0",
]
[[package]]
@@ -7100,6 +7290,16 @@ dependencies = [
"yansi",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn 2.0.114",
]
[[package]]
name = "proc-macro-crate"
version = "3.4.0"
@@ -7248,6 +7448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
"serde",
]
[[package]]
@@ -7932,6 +8133,16 @@ dependencies = [
"webpki-roots 1.0.5",
]
[[package]]
name = "resb"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a067ab3b5ca3b4dc307d0de9cf75f9f5e6ca9717b192b2f28a36c83e5de9e76"
dependencies = [
"potential_utf",
"serde_core",
]
[[package]]
name = "resolv-conf"
version = "0.7.6"
@@ -8129,7 +8340,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.11.0",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -8172,9 +8383,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.103.9"
version = "0.103.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
dependencies = [
"aws-lc-rs",
"ring",
@@ -9312,6 +9523,15 @@ dependencies = [
"serde_json",
]
[[package]]
name = "strck"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42316e70da376f3d113a68d138a60d8a9883c604fe97942721ec2068dab13a9f"
dependencies = [
"unicode-ident",
]
[[package]]
name = "streaming-iterator"
version = "0.1.9"
@@ -9367,6 +9587,9 @@ name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros 0.27.2",
]
[[package]]
name = "strum_macros"
@@ -9381,6 +9604,18 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "strum_macros"
version = "0.28.0"
@@ -9519,9 +9754,9 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]]
name = "tar"
version = "0.4.44"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
dependencies = [
"filetime",
"libc",
@@ -9538,7 +9773,40 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix 1.1.3",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
name = "temporal_capi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a151e402c2bdb6a3a2a2f3f225eddaead2e7ce7dd5d3fa2090deb11b17aa4ed8"
dependencies = [
"diplomat",
"diplomat-runtime",
"icu_calendar",
"icu_locale",
"num-traits",
"temporal_rs",
"timezone_provider",
"writeable",
"zoneinfo64",
]
[[package]]
name = "temporal_rs"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88afde3bd75d2fc68d77a914bece426aa08aa7649ffd0cdd4a11c3d4d33474d1"
dependencies = [
"core_maths",
"icu_calendar",
"icu_locale",
"ixdtf",
"num-traits",
"timezone_provider",
"tinystr",
"writeable",
]
[[package]]
@@ -9748,6 +10016,18 @@ dependencies = [
"time-core",
]
[[package]]
name = "timezone_provider"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9ba0000e9e73862f3e7ca1ff159e2ddf915c9d8bb11e38a7874760f445d993"
dependencies = [
"tinystr",
"zerotrie",
"zerovec",
"zoneinfo64",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
@@ -10547,6 +10827,23 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "v8"
version = "146.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d97bcac5cdc5a195a4813f1855a6bc658f240452aac36caa12fd6c6f16026ab1"
dependencies = [
"bindgen",
"bitflags 2.10.0",
"fslock",
"gzip-header",
"home",
"miniz_oxide",
"paste",
"temporal_capi",
"which 6.0.3",
]
[[package]]
name = "valuable"
version = "0.1.1"
@@ -10846,6 +11143,18 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "which"
version = "6.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
dependencies = [
"either",
"home",
"rustix 0.38.44",
"winsafe",
]
[[package]]
name = "which"
version = "8.0.0"
@@ -10910,7 +11219,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.48.0",
]
[[package]]
@@ -11849,6 +12158,19 @@ version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
[[package]]
name = "zoneinfo64"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2e5597efbe7c421da8a7fd396b20b571704e787c21a272eecf35dfe9d386f0"
dependencies = [
"calendrical_calculations",
"icu_locale_core",
"potential_utf",
"resb",
"serde",
]
[[package]]
name = "zopfli"
version = "0.8.3"

View File

@@ -11,7 +11,9 @@ members = [
"apply-patch",
"arg0",
"feedback",
"features",
"codex-backend-openapi-models",
"code-mode",
"cloud-requirements",
"cloud-tasks",
"cloud-tasks-client",
@@ -22,10 +24,10 @@ members = [
"shell-escalation",
"skills",
"core",
"environment",
"hooks",
"secrets",
"exec",
"exec-server",
"execpolicy",
"execpolicy-legacy",
"keyring-store",
@@ -44,6 +46,7 @@ members = [
"otel",
"tui",
"tui_app_server",
"v8-poc",
"utils/absolute-path",
"utils/cargo-bin",
"utils/git",
@@ -66,6 +69,7 @@ members = [
"codex-client",
"codex-api",
"state",
"terminal-detection",
"codex-experimental-api-macros",
"test-macros",
"package-manager",
@@ -88,6 +92,7 @@ app_test_support = { path = "app-server/tests/common" }
codex-ansi-escape = { path = "ansi-escape" }
codex-api = { path = "codex-api" }
codex-artifacts = { path = "artifacts" }
codex-code-mode = { path = "code-mode" }
codex-package-manager = { path = "package-manager" }
codex-app-server = { path = "app-server" }
codex-app-server-client = { path = "app-server-client" }
@@ -104,11 +109,12 @@ codex-cloud-requirements = { path = "cloud-requirements" }
codex-connectors = { path = "connectors" }
codex-config = { path = "config" }
codex-core = { path = "core" }
codex-environment = { path = "environment" }
codex-exec = { path = "exec" }
codex-exec-server = { path = "exec-server" }
codex-execpolicy = { path = "execpolicy" }
codex-experimental-api-macros = { path = "codex-experimental-api-macros" }
codex-feedback = { path = "feedback" }
codex-features = { path = "features" }
codex-file-search = { path = "file-search" }
codex-git = { path = "utils/git" }
codex-hooks = { path = "hooks" }
@@ -131,8 +137,10 @@ codex-skills = { path = "skills" }
codex-state = { path = "state" }
codex-stdio-to-uds = { path = "stdio-to-uds" }
codex-test-macros = { path = "test-macros" }
codex-terminal-detection = { path = "terminal-detection" }
codex-tui = { path = "tui" }
codex-tui-app-server = { path = "tui_app_server" }
codex-v8-poc = { path = "v8-poc" }
codex-utils-absolute-path = { path = "utils/absolute-path" }
codex-utils-approval-presets = { path = "utils/approval-presets" }
codex-utils-cache = { path = "utils/cache" }
@@ -232,6 +240,7 @@ portable-pty = "0.9.0"
predicates = "3"
pretty_assertions = "1.4.1"
pulldown-cmark = "0.10"
quick-xml = "0.38.4"
rand = "0.9"
ratatui = "0.29.0"
ratatui-macros = "0.6.0"
@@ -240,6 +249,7 @@ regex-lite = "0.1.8"
reqwest = "0.12"
rmcp = { version = "0.15.0", default-features = false }
runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" }
v8 = "=146.4.0"
rustls = { version = "0.23", default-features = false, features = [
"ring",
"std",
@@ -365,7 +375,8 @@ ignored = [
"icu_provider",
"openssl-sys",
"codex-utils-readiness",
"codex-secrets"
"codex-secrets",
"codex-v8-poc",
]
[profile.release]

View File

@@ -16,6 +16,7 @@ codex-app-server = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-arg0 = { workspace = true }
codex-core = { workspace = true }
codex-features = { workspace = true }
codex-feedback = { workspace = true }
codex-protocol = { workspace = true }
futures = { workspace = true }

View File

@@ -47,6 +47,7 @@ use codex_core::config::Config;
use codex_core::config_loader::CloudRequirementsLoader;
use codex_core::config_loader::LoaderOverrides;
use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig;
use codex_features::Feature;
use codex_feedback::CodexFeedback;
use codex_protocol::protocol::SessionSource;
use serde::de::DeserializeOwned;
@@ -215,7 +216,7 @@ impl InProcessClientStartArgs {
default_mode_request_user_input: self
.config
.features
.enabled(codex_core::features::Feature::DefaultModeRequestUserInput),
.enabled(Feature::DefaultModeRequestUserInput),
},
));
@@ -1484,7 +1485,7 @@ mod tests {
CollaborationModesConfig {
default_mode_request_user_input: config
.features
.enabled(codex_core::features::Feature::DefaultModeRequestUserInput),
.enabled(Feature::DefaultModeRequestUserInput),
},
));
event_tx

View File

@@ -2343,13 +2343,27 @@
"enabled": {
"type": "boolean"
},
"name": {
"description": "Name-based selector.",
"type": [
"string",
"null"
]
},
"path": {
"type": "string"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Path-based selector."
}
},
"required": [
"enabled",
"path"
"enabled"
],
"type": "object"
},
@@ -2881,6 +2895,22 @@
],
"type": "object"
},
"ThreadShellCommandParams": {
"properties": {
"command": {
"description": "Shell command string evaluated by the thread's configured shell. Unlike `command/exec`, this intentionally preserves shell syntax such as pipes, redirects, and quoting. This runs unsandboxed with full access rather than inheriting the thread sandbox policy.",
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"command",
"threadId"
],
"type": "object"
},
"ThreadSortKey": {
"enum": [
"created_at",
@@ -3586,6 +3616,30 @@
"title": "Thread/compact/startRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/shellCommand"
],
"title": "Thread/shellCommandRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadShellCommandParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/shellCommandRequest",
"type": "object"
},
{
"properties": {
"id": {

View File

@@ -1,6 +1,13 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"FuzzyFileSearchMatchType": {
"enum": [
"file",
"directory"
],
"type": "string"
},
"FuzzyFileSearchResult": {
"description": "Superset of [`codex_file_search::FileMatch`]",
"properties": {
@@ -18,6 +25,9 @@
"null"
]
},
"match_type": {
"$ref": "#/definitions/FuzzyFileSearchMatchType"
},
"path": {
"type": "string"
},
@@ -32,6 +42,7 @@
},
"required": [
"file_name",
"match_type",
"path",
"root",
"score"

View File

@@ -1,6 +1,13 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"FuzzyFileSearchMatchType": {
"enum": [
"file",
"directory"
],
"type": "string"
},
"FuzzyFileSearchResult": {
"description": "Superset of [`codex_file_search::FileMatch`]",
"properties": {
@@ -18,6 +25,9 @@
"null"
]
},
"match_type": {
"$ref": "#/definitions/FuzzyFileSearchMatchType"
},
"path": {
"type": "string"
},
@@ -32,6 +42,7 @@
},
"required": [
"file_name",
"match_type",
"path",
"root",
"score"

View File

@@ -83,6 +83,9 @@
],
"type": "object"
},
"AgentPath": {
"type": "string"
},
"AppBranding": {
"description": "EXPERIMENTAL - app metadata returned by app-list APIs.",
"properties": {
@@ -511,6 +514,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -745,6 +770,15 @@
],
"type": "object"
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -964,6 +998,13 @@
],
"type": "object"
},
"FuzzyFileSearchMatchType": {
"enum": [
"file",
"directory"
],
"type": "string"
},
"FuzzyFileSearchResult": {
"description": "Superset of [`codex_file_search::FileMatch`]",
"properties": {
@@ -981,6 +1022,9 @@
"null"
]
},
"match_type": {
"$ref": "#/definitions/FuzzyFileSearchMatchType"
},
"path": {
"type": "string"
},
@@ -995,6 +1039,7 @@
},
"required": [
"file_name",
"match_type",
"path",
"root",
"score"
@@ -1181,6 +1226,21 @@
],
"type": "string"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"HookRunStatus": {
"enum": [
"running",
@@ -1400,6 +1460,36 @@
],
"type": "object"
},
"McpServerStartupState": {
"enum": [
"starting",
"ready",
"failed",
"cancelled"
],
"type": "string"
},
"McpServerStatusUpdatedNotification": {
"properties": {
"error": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
},
"status": {
"$ref": "#/definitions/McpServerStartupState"
}
},
"required": [
"name",
"status"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -1555,6 +1645,13 @@
],
"type": "object"
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -1880,6 +1977,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -1921,6 +2031,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -2224,6 +2345,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -2379,6 +2527,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -2704,6 +2860,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},
@@ -2935,6 +3097,26 @@
],
"type": "object"
},
"ThreadRealtimeTranscriptUpdatedNotification": {
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
"properties": {
"role": {
"type": "string"
},
"text": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"role",
"text",
"threadId"
],
"type": "object"
},
"ThreadStartedNotification": {
"properties": {
"thread": {
@@ -4131,6 +4313,26 @@
"title": "McpServer/oauthLogin/completedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"mcpServer/startupStatus/updated"
],
"title": "McpServer/startupStatus/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/McpServerStatusUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "McpServer/startupStatus/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -4412,6 +4614,26 @@
"title": "Thread/realtime/itemAddedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/realtime/transcriptUpdated"
],
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadRealtimeTranscriptUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcriptUpdatedNotification",
"type": "object"
},
{
"properties": {
"method": {

View File

@@ -499,6 +499,30 @@
"title": "Thread/compact/startRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"thread/shellCommand"
],
"title": "Thread/shellCommandRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadShellCommandParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/shellCommandRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -2034,6 +2058,13 @@
"title": "FileChangeRequestApprovalResponse",
"type": "object"
},
"FuzzyFileSearchMatchType": {
"enum": [
"file",
"directory"
],
"type": "string"
},
"FuzzyFileSearchParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -2093,6 +2124,9 @@
"null"
]
},
"match_type": {
"$ref": "#/definitions/FuzzyFileSearchMatchType"
},
"path": {
"type": "string"
},
@@ -2107,6 +2141,7 @@
},
"required": [
"file_name",
"match_type",
"path",
"root",
"score"
@@ -3938,6 +3973,26 @@
"title": "McpServer/oauthLogin/completedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"mcpServer/startupStatus/updated"
],
"title": "McpServer/startupStatus/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/McpServerStatusUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "McpServer/startupStatus/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -4219,6 +4274,26 @@
"title": "Thread/realtime/itemAddedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/realtime/transcriptUpdated"
],
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadRealtimeTranscriptUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcriptUpdatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -4844,6 +4919,9 @@
"title": "AgentMessageDeltaNotification",
"type": "object"
},
"AgentPath": {
"type": "string"
},
"AnalyticsConfig": {
"additionalProperties": true,
"properties": {
@@ -5197,11 +5275,15 @@
},
"name": {
"type": "string"
},
"needsAuth": {
"type": "boolean"
}
},
"required": [
"id",
"name"
"name",
"needsAuth"
],
"type": "object"
},
@@ -5576,6 +5658,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/v2/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -6110,6 +6214,15 @@
"title": "CommandExecutionOutputDeltaNotification",
"type": "object"
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -7927,6 +8040,21 @@
],
"type": "string"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"HookRunStatus": {
"enum": [
"running",
@@ -8507,6 +8635,15 @@
"title": "McpServerRefreshResponse",
"type": "object"
},
"McpServerStartupState": {
"enum": [
"starting",
"ready",
"failed",
"cancelled"
],
"type": "string"
},
"McpServerStatus": {
"properties": {
"authStatus": {
@@ -8543,6 +8680,29 @@
],
"type": "object"
},
"McpServerStatusUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"error": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
},
"status": {
"$ref": "#/definitions/v2/McpServerStartupState"
}
},
"required": [
"name",
"status"
],
"title": "McpServerStatusUpdatedNotification",
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -8977,6 +9137,13 @@
},
"type": "object"
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"OverriddenMetadata": {
"properties": {
"effectiveValue": true,
@@ -9340,6 +9507,13 @@
"PluginListResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"featuredPluginIds": {
"default": [],
"items": {
"type": "string"
},
"type": "array"
},
"marketplaces": {
"items": {
"$ref": "#/definitions/v2/PluginMarketplaceEntry"
@@ -10984,6 +11158,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -11165,6 +11352,9 @@
"description": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"interface": {
"anyOf": [
{
@@ -11190,6 +11380,7 @@
},
"required": [
"description",
"enabled",
"name",
"path"
],
@@ -11246,13 +11437,27 @@
"enabled": {
"type": "boolean"
},
"name": {
"description": "Name-based selector.",
"type": [
"string",
"null"
]
},
"path": {
"type": "string"
"anyOf": [
{
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Path-based selector."
}
},
"required": [
"enabled",
"path"
"enabled"
],
"title": "SkillsConfigWriteParams",
"type": "object"
@@ -11380,6 +11585,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/v2/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -11890,6 +12106,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/v2/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -12045,6 +12288,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/v2/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/v2/CommandExecutionStatus"
},
@@ -12370,6 +12621,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},
@@ -12852,6 +13109,28 @@
"title": "ThreadRealtimeStartedNotification",
"type": "object"
},
"ThreadRealtimeTranscriptUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
"properties": {
"role": {
"type": "string"
},
"text": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"role",
"text",
"threadId"
],
"title": "ThreadRealtimeTranscriptUpdatedNotification",
"type": "object"
},
"ThreadResumeParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "There are three ways to resume a thread: 1. By thread_id: load the thread from disk by thread_id and resume it. 2. By history: instantiate the thread from memory and resume it. 3. By path: load the thread from disk by path and resume it.\n\nThe precedence is: history > path > thread_id. If using history or path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
@@ -13084,6 +13363,29 @@
"title": "ThreadSetNameResponse",
"type": "object"
},
"ThreadShellCommandParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"command": {
"description": "Shell command string evaluated by the thread's configured shell. Unlike `command/exec`, this intentionally preserves shell syntax such as pipes, redirects, and quoting. This runs unsandboxed with full access rather than inheriting the thread sandbox policy.",
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"command",
"threadId"
],
"title": "ThreadShellCommandParams",
"type": "object"
},
"ThreadShellCommandResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ThreadShellCommandResponse",
"type": "object"
},
"ThreadSortKey": {
"enum": [
"created_at",

View File

@@ -139,6 +139,9 @@
"title": "AgentMessageDeltaNotification",
"type": "object"
},
"AgentPath": {
"type": "string"
},
"AnalyticsConfig": {
"additionalProperties": true,
"properties": {
@@ -492,11 +495,15 @@
},
"name": {
"type": "string"
},
"needsAuth": {
"type": "boolean"
}
},
"required": [
"id",
"name"
"name",
"needsAuth"
],
"type": "object"
},
@@ -1026,6 +1033,30 @@
"title": "Thread/compact/startRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/shellCommand"
],
"title": "Thread/shellCommandRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadShellCommandParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/shellCommandRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -2220,6 +2251,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -2754,6 +2807,15 @@
"title": "CommandExecutionOutputDeltaNotification",
"type": "object"
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -4327,6 +4389,13 @@
}
]
},
"FuzzyFileSearchMatchType": {
"enum": [
"file",
"directory"
],
"type": "string"
},
"FuzzyFileSearchParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -4370,6 +4439,9 @@
"null"
]
},
"match_type": {
"$ref": "#/definitions/FuzzyFileSearchMatchType"
},
"path": {
"type": "string"
},
@@ -4384,6 +4456,7 @@
},
"required": [
"file_name",
"match_type",
"path",
"root",
"score"
@@ -4671,6 +4744,21 @@
],
"type": "string"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"HookRunStatus": {
"enum": [
"running",
@@ -5295,6 +5383,15 @@
"title": "McpServerRefreshResponse",
"type": "object"
},
"McpServerStartupState": {
"enum": [
"starting",
"ready",
"failed",
"cancelled"
],
"type": "string"
},
"McpServerStatus": {
"properties": {
"authStatus": {
@@ -5331,6 +5428,29 @@
],
"type": "object"
},
"McpServerStatusUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"error": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
},
"status": {
"$ref": "#/definitions/McpServerStartupState"
}
},
"required": [
"name",
"status"
],
"title": "McpServerStatusUpdatedNotification",
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -5765,6 +5885,13 @@
},
"type": "object"
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"OverriddenMetadata": {
"properties": {
"effectiveValue": true,
@@ -6128,6 +6255,13 @@
"PluginListResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"featuredPluginIds": {
"default": [],
"items": {
"type": "string"
},
"type": "array"
},
"marketplaces": {
"items": {
"$ref": "#/definitions/PluginMarketplaceEntry"
@@ -8303,6 +8437,26 @@
"title": "McpServer/oauthLogin/completedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"mcpServer/startupStatus/updated"
],
"title": "McpServer/startupStatus/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/McpServerStatusUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "McpServer/startupStatus/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -8584,6 +8738,26 @@
"title": "Thread/realtime/itemAddedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/realtime/transcriptUpdated"
],
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadRealtimeTranscriptUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcriptUpdatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -8744,6 +8918,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -8925,6 +9112,9 @@
"description": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"interface": {
"anyOf": [
{
@@ -8950,6 +9140,7 @@
},
"required": [
"description",
"enabled",
"name",
"path"
],
@@ -9006,13 +9197,27 @@
"enabled": {
"type": "boolean"
},
"name": {
"description": "Name-based selector.",
"type": [
"string",
"null"
]
},
"path": {
"type": "string"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Path-based selector."
}
},
"required": [
"enabled",
"path"
"enabled"
],
"title": "SkillsConfigWriteParams",
"type": "object"
@@ -9140,6 +9345,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -9650,6 +9866,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -9805,6 +10048,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -10130,6 +10381,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},
@@ -10612,6 +10869,28 @@
"title": "ThreadRealtimeStartedNotification",
"type": "object"
},
"ThreadRealtimeTranscriptUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
"properties": {
"role": {
"type": "string"
},
"text": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"role",
"text",
"threadId"
],
"title": "ThreadRealtimeTranscriptUpdatedNotification",
"type": "object"
},
"ThreadResumeParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "There are three ways to resume a thread: 1. By thread_id: load the thread from disk by thread_id and resume it. 2. By history: instantiate the thread from memory and resume it. 3. By path: load the thread from disk by path and resume it.\n\nThe precedence is: history > path > thread_id. If using history or path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
@@ -10844,6 +11123,29 @@
"title": "ThreadSetNameResponse",
"type": "object"
},
"ThreadShellCommandParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"command": {
"description": "Shell command string evaluated by the thread's configured shell. Unlike `command/exec`, this intentionally preserves shell syntax such as pipes, redirects, and quoting. This runs unsandboxed with full access rather than inheriting the thread sandbox policy.",
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"command",
"threadId"
],
"title": "ThreadShellCommandParams",
"type": "object"
},
"ThreadShellCommandResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ThreadShellCommandResponse",
"type": "object"
},
"ThreadSortKey": {
"enum": [
"created_at",

View File

@@ -112,9 +112,38 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"TurnError": {
"properties": {
"additionalDetails": {

View File

@@ -177,6 +177,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -257,6 +266,21 @@
],
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -487,6 +511,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -642,6 +693,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -967,6 +1026,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -177,6 +177,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -257,6 +266,21 @@
],
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -487,6 +511,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -642,6 +693,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -967,6 +1026,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -0,0 +1,34 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"McpServerStartupState": {
"enum": [
"starting",
"ready",
"failed",
"cancelled"
],
"type": "string"
}
},
"properties": {
"error": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
},
"status": {
"$ref": "#/definitions/McpServerStartupState"
}
},
"required": [
"name",
"status"
],
"title": "McpServerStatusUpdatedNotification",
"type": "object"
}

View File

@@ -21,11 +21,15 @@
},
"name": {
"type": "string"
},
"needsAuth": {
"type": "boolean"
}
},
"required": [
"id",
"name"
"name",
"needsAuth"
],
"type": "object"
},

View File

@@ -239,6 +239,13 @@
}
},
"properties": {
"featuredPluginIds": {
"default": [],
"items": {
"type": "string"
},
"type": "array"
},
"marketplaces": {
"items": {
"$ref": "#/definitions/PluginMarketplaceEntry"

View File

@@ -25,11 +25,15 @@
},
"name": {
"type": "string"
},
"needsAuth": {
"type": "boolean"
}
},
"required": [
"id",
"name"
"name",
"needsAuth"
],
"type": "object"
},
@@ -314,6 +318,9 @@
"description": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"interface": {
"anyOf": [
{
@@ -339,6 +346,7 @@
},
"required": [
"description",
"enabled",
"name",
"path"
],

View File

@@ -131,6 +131,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +313,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -371,6 +402,21 @@
],
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -470,6 +516,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -601,6 +654,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -756,6 +836,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1081,6 +1169,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -1,16 +1,36 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AbsolutePathBuf": {
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
}
},
"properties": {
"enabled": {
"type": "boolean"
},
"name": {
"description": "Name-based selector.",
"type": [
"string",
"null"
]
},
"path": {
"type": "string"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Path-based selector."
}
},
"required": [
"enabled",
"path"
"enabled"
],
"title": "SkillsConfigWriteParams",
"type": "object"

View File

@@ -5,6 +5,9 @@
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
},
"AgentPath": {
"type": "string"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `guardian_subagent` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request.",
"enum": [
@@ -193,6 +196,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -353,6 +378,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -456,6 +490,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -562,6 +611,13 @@
],
"type": "string"
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -826,6 +882,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -863,6 +932,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -1081,6 +1161,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -1236,6 +1343,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1561,6 +1676,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -1,6 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AgentPath": {
"type": "string"
},
"ByteRange": {
"properties": {
"end": {
@@ -131,6 +134,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +316,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -394,6 +428,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -493,6 +542,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -584,6 +640,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -621,6 +690,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -839,6 +919,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -994,6 +1101,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1319,6 +1434,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -1,6 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AgentPath": {
"type": "string"
},
"ByteRange": {
"properties": {
"end": {
@@ -131,6 +134,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +316,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -394,6 +428,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -493,6 +542,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -584,6 +640,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -621,6 +690,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -839,6 +919,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -994,6 +1101,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1319,6 +1434,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -1,6 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AgentPath": {
"type": "string"
},
"ByteRange": {
"properties": {
"end": {
@@ -131,6 +134,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +316,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -394,6 +428,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -493,6 +542,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -584,6 +640,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -621,6 +690,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -839,6 +919,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -994,6 +1101,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1319,6 +1434,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
"properties": {
"role": {
"type": "string"
},
"text": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"role",
"text",
"threadId"
],
"title": "ThreadRealtimeTranscriptUpdatedNotification",
"type": "object"
}

View File

@@ -5,6 +5,9 @@
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
},
"AgentPath": {
"type": "string"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `guardian_subagent` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request.",
"enum": [
@@ -193,6 +196,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -353,6 +378,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -456,6 +490,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -562,6 +611,13 @@
],
"type": "string"
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -826,6 +882,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -863,6 +932,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -1081,6 +1161,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -1236,6 +1343,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1561,6 +1676,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -1,6 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AgentPath": {
"type": "string"
},
"ByteRange": {
"properties": {
"end": {
@@ -131,6 +134,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +316,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -394,6 +428,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -493,6 +542,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -584,6 +640,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -621,6 +690,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -839,6 +919,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -994,6 +1101,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1319,6 +1434,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"command": {
"description": "Shell command string evaluated by the thread's configured shell. Unlike `command/exec`, this intentionally preserves shell syntax such as pipes, redirects, and quoting. This runs unsandboxed with full access rather than inheriting the thread sandbox policy.",
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"command",
"threadId"
],
"title": "ThreadShellCommandParams",
"type": "object"
}

View File

@@ -0,0 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ThreadShellCommandResponse",
"type": "object"
}

View File

@@ -5,6 +5,9 @@
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
},
"AgentPath": {
"type": "string"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `guardian_subagent` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request.",
"enum": [
@@ -193,6 +196,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -353,6 +378,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -456,6 +490,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -562,6 +611,13 @@
],
"type": "string"
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -826,6 +882,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -863,6 +932,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -1081,6 +1161,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -1236,6 +1343,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1561,6 +1676,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -1,6 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AgentPath": {
"type": "string"
},
"ByteRange": {
"properties": {
"end": {
@@ -131,6 +134,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +316,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -394,6 +428,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -493,6 +542,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -584,6 +640,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -621,6 +690,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -839,6 +919,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -994,6 +1101,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1319,6 +1434,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -1,6 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AgentPath": {
"type": "string"
},
"ByteRange": {
"properties": {
"end": {
@@ -131,6 +134,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +316,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -394,6 +428,21 @@
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -493,6 +542,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -584,6 +640,19 @@
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
@@ -621,6 +690,17 @@
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
@@ -839,6 +919,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -994,6 +1101,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1319,6 +1434,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -131,6 +131,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +313,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -371,6 +402,21 @@
],
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -470,6 +516,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -601,6 +654,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -756,6 +836,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1081,6 +1169,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -131,6 +131,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +313,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -371,6 +402,21 @@
],
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -470,6 +516,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -601,6 +654,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -756,6 +836,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1081,6 +1169,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -131,6 +131,28 @@
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
@@ -291,6 +313,15 @@
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
@@ -371,6 +402,21 @@
],
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
@@ -470,6 +516,13 @@
}
]
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
@@ -601,6 +654,33 @@
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
@@ -756,6 +836,14 @@
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
@@ -1081,6 +1169,12 @@
"null"
]
},
"savedPath": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string"
},

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type AgentPath = string;

View File

@@ -49,6 +49,7 @@ import type { ThreadReadParams } from "./v2/ThreadReadParams";
import type { ThreadResumeParams } from "./v2/ThreadResumeParams";
import type { ThreadRollbackParams } from "./v2/ThreadRollbackParams";
import type { ThreadSetNameParams } from "./v2/ThreadSetNameParams";
import type { ThreadShellCommandParams } from "./v2/ThreadShellCommandParams";
import type { ThreadStartParams } from "./v2/ThreadStartParams";
import type { ThreadUnarchiveParams } from "./v2/ThreadUnarchiveParams";
import type { ThreadUnsubscribeParams } from "./v2/ThreadUnsubscribeParams";
@@ -60,4 +61,4 @@ import type { WindowsSandboxSetupStartParams } from "./v2/WindowsSandboxSetupSta
/**
* Request from the client to the server.
*/
export type ClientRequest ={ "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/unsubscribe", id: RequestId, params: ThreadUnsubscribeParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/metadata/update", id: RequestId, params: ThreadMetadataUpdateParams, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/compact/start", id: RequestId, params: ThreadCompactStartParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "plugin/list", id: RequestId, params: PluginListParams, } | { "method": "plugin/read", id: RequestId, params: PluginReadParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "fs/readFile", id: RequestId, params: FsReadFileParams, } | { "method": "fs/writeFile", id: RequestId, params: FsWriteFileParams, } | { "method": "fs/createDirectory", id: RequestId, params: FsCreateDirectoryParams, } | { "method": "fs/getMetadata", id: RequestId, params: FsGetMetadataParams, } | { "method": "fs/readDirectory", id: RequestId, params: FsReadDirectoryParams, } | { "method": "fs/remove", id: RequestId, params: FsRemoveParams, } | { "method": "fs/copy", id: RequestId, params: FsCopyParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "plugin/install", id: RequestId, params: PluginInstallParams, } | { "method": "plugin/uninstall", id: RequestId, params: PluginUninstallParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/steer", id: RequestId, params: TurnSteerParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "experimentalFeature/list", id: RequestId, params: ExperimentalFeatureListParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "windowsSandbox/setupStart", id: RequestId, params: WindowsSandboxSetupStartParams, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "command/exec/write", id: RequestId, params: CommandExecWriteParams, } | { "method": "command/exec/terminate", id: RequestId, params: CommandExecTerminateParams, } | { "method": "command/exec/resize", id: RequestId, params: CommandExecResizeParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "externalAgentConfig/detect", id: RequestId, params: ExternalAgentConfigDetectParams, } | { "method": "externalAgentConfig/import", id: RequestId, params: ExternalAgentConfigImportParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, };
export type ClientRequest ={ "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/unsubscribe", id: RequestId, params: ThreadUnsubscribeParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/metadata/update", id: RequestId, params: ThreadMetadataUpdateParams, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/compact/start", id: RequestId, params: ThreadCompactStartParams, } | { "method": "thread/shellCommand", id: RequestId, params: ThreadShellCommandParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "plugin/list", id: RequestId, params: PluginListParams, } | { "method": "plugin/read", id: RequestId, params: PluginReadParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "fs/readFile", id: RequestId, params: FsReadFileParams, } | { "method": "fs/writeFile", id: RequestId, params: FsWriteFileParams, } | { "method": "fs/createDirectory", id: RequestId, params: FsCreateDirectoryParams, } | { "method": "fs/getMetadata", id: RequestId, params: FsGetMetadataParams, } | { "method": "fs/readDirectory", id: RequestId, params: FsReadDirectoryParams, } | { "method": "fs/remove", id: RequestId, params: FsRemoveParams, } | { "method": "fs/copy", id: RequestId, params: FsCopyParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "plugin/install", id: RequestId, params: PluginInstallParams, } | { "method": "plugin/uninstall", id: RequestId, params: PluginUninstallParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/steer", id: RequestId, params: TurnSteerParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "experimentalFeature/list", id: RequestId, params: ExperimentalFeatureListParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "windowsSandbox/setupStart", id: RequestId, params: WindowsSandboxSetupStartParams, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "command/exec/write", id: RequestId, params: CommandExecWriteParams, } | { "method": "command/exec/terminate", id: RequestId, params: CommandExecTerminateParams, } | { "method": "command/exec/resize", id: RequestId, params: CommandExecResizeParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "externalAgentConfig/detect", id: RequestId, params: ExternalAgentConfigDetectParams, } | { "method": "externalAgentConfig/import", id: RequestId, params: ExternalAgentConfigImportParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, };

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type FuzzyFileSearchMatchType = "file" | "directory";

View File

@@ -1,8 +1,9 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { FuzzyFileSearchMatchType } from "./FuzzyFileSearchMatchType";
/**
* Superset of [`codex_file_search::FileMatch`]
*/
export type FuzzyFileSearchResult = { root: string, path: string, file_name: string, score: number, indices: Array<number> | null, };
export type FuzzyFileSearchResult = { root: string, path: string, match_type: FuzzyFileSearchMatchType, file_name: string, score: number, indices: Array<number> | null, };

View File

@@ -22,6 +22,7 @@ import type { ItemGuardianApprovalReviewCompletedNotification } from "./v2/ItemG
import type { ItemGuardianApprovalReviewStartedNotification } from "./v2/ItemGuardianApprovalReviewStartedNotification";
import type { ItemStartedNotification } from "./v2/ItemStartedNotification";
import type { McpServerOauthLoginCompletedNotification } from "./v2/McpServerOauthLoginCompletedNotification";
import type { McpServerStatusUpdatedNotification } from "./v2/McpServerStatusUpdatedNotification";
import type { McpToolCallProgressNotification } from "./v2/McpToolCallProgressNotification";
import type { ModelReroutedNotification } from "./v2/ModelReroutedNotification";
import type { PlanDeltaNotification } from "./v2/PlanDeltaNotification";
@@ -40,6 +41,7 @@ import type { ThreadRealtimeErrorNotification } from "./v2/ThreadRealtimeErrorNo
import type { ThreadRealtimeItemAddedNotification } from "./v2/ThreadRealtimeItemAddedNotification";
import type { ThreadRealtimeOutputAudioDeltaNotification } from "./v2/ThreadRealtimeOutputAudioDeltaNotification";
import type { ThreadRealtimeStartedNotification } from "./v2/ThreadRealtimeStartedNotification";
import type { ThreadRealtimeTranscriptUpdatedNotification } from "./v2/ThreadRealtimeTranscriptUpdatedNotification";
import type { ThreadStartedNotification } from "./v2/ThreadStartedNotification";
import type { ThreadStatusChangedNotification } from "./v2/ThreadStatusChangedNotification";
import type { ThreadTokenUsageUpdatedNotification } from "./v2/ThreadTokenUsageUpdatedNotification";
@@ -54,4 +56,4 @@ import type { WindowsWorldWritableWarningNotification } from "./v2/WindowsWorldW
/**
* Notification sent from the server to the client.
*/
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification };
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "mcpServer/startupStatus/updated", "params": McpServerStatusUpdatedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/transcriptUpdated", "params": ThreadRealtimeTranscriptUpdatedNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification };

View File

@@ -3,4 +3,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SubAgentSource } from "./SubAgentSource";
export type SessionSource = "cli" | "vscode" | "exec" | "mcp" | { "subagent": SubAgentSource } | "unknown";
export type SessionSource = "cli" | "vscode" | "exec" | "mcp" | { "custom": string } | { "subagent": SubAgentSource } | "unknown";

View File

@@ -1,6 +1,7 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AgentPath } from "./AgentPath";
import type { ThreadId } from "./ThreadId";
export type SubAgentSource = "review" | "compact" | { "thread_spawn": { parent_thread_id: ThreadId, depth: number, agent_nickname: string | null, agent_role: string | null, } } | "memory_consolidation" | { "other": string };
export type SubAgentSource = "review" | "compact" | { "thread_spawn": { parent_thread_id: ThreadId, depth: number, agent_path: AgentPath | null, agent_nickname: string | null, agent_role: string | null, } } | "memory_consolidation" | { "other": string };

View File

@@ -1,6 +1,7 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
export type { AbsolutePathBuf } from "./AbsolutePathBuf";
export type { AgentPath } from "./AgentPath";
export type { ApplyPatchApprovalParams } from "./ApplyPatchApprovalParams";
export type { ApplyPatchApprovalResponse } from "./ApplyPatchApprovalResponse";
export type { AuthMode } from "./AuthMode";
@@ -18,6 +19,7 @@ export type { FileChange } from "./FileChange";
export type { ForcedLoginMethod } from "./ForcedLoginMethod";
export type { FunctionCallOutputBody } from "./FunctionCallOutputBody";
export type { FunctionCallOutputContentItem } from "./FunctionCallOutputContentItem";
export type { FuzzyFileSearchMatchType } from "./FuzzyFileSearchMatchType";
export type { FuzzyFileSearchParams } from "./FuzzyFileSearchParams";
export type { FuzzyFileSearchResponse } from "./FuzzyFileSearchResponse";
export type { FuzzyFileSearchResult } from "./FuzzyFileSearchResult";

View File

@@ -5,4 +5,4 @@
/**
* EXPERIMENTAL - app metadata summary for plugin responses.
*/
export type AppSummary = { id: string, name: string, description: string | null, installUrl: string | null, };
export type AppSummary = { id: string, name: string, description: string | null, installUrl: string | null, needsAuth: boolean, };

View File

@@ -1,6 +1,7 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { NonSteerableTurnKind } from "./NonSteerableTurnKind";
/**
* This translation layer make sure that we expose codex error code in camel case.
@@ -8,4 +9,4 @@
* When an upstream HTTP status is available (for example, from the Responses API or a provider),
* it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.
*/
export type CodexErrorInfo = "contextWindowExceeded" | "usageLimitExceeded" | "serverOverloaded" | { "httpConnectionFailed": { httpStatusCode: number | null, } } | { "responseStreamConnectionFailed": { httpStatusCode: number | null, } } | "internalServerError" | "unauthorized" | "badRequest" | "threadRollbackFailed" | "sandboxError" | { "responseStreamDisconnected": { httpStatusCode: number | null, } } | { "responseTooManyFailedAttempts": { httpStatusCode: number | null, } } | "other";
export type CodexErrorInfo = "contextWindowExceeded" | "usageLimitExceeded" | "serverOverloaded" | { "httpConnectionFailed": { httpStatusCode: number | null, } } | { "responseStreamConnectionFailed": { httpStatusCode: number | null, } } | "internalServerError" | "unauthorized" | "badRequest" | "threadRollbackFailed" | "sandboxError" | { "responseStreamDisconnected": { httpStatusCode: number | null, } } | { "responseTooManyFailedAttempts": { httpStatusCode: number | null, } } | { "activeTurnNotSteerable": { turnKind: NonSteerableTurnKind, } } | "other";

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type CommandExecutionSource = "agent" | "userShell" | "unifiedExecStartup" | "unifiedExecInteraction";

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type HookPromptFragment = { text: string, hookRunId: string, };

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type McpServerStartupState = "starting" | "ready" | "failed" | "cancelled";

View File

@@ -0,0 +1,6 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { McpServerStartupState } from "./McpServerStartupState";
export type McpServerStatusUpdatedNotification = { name: string, status: McpServerStartupState, error: string | null, };

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type NonSteerableTurnKind = "review" | "compact";

View File

@@ -3,4 +3,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { PluginMarketplaceEntry } from "./PluginMarketplaceEntry";
export type PluginListResponse = { marketplaces: Array<PluginMarketplaceEntry>, remoteSyncError: string | null, };
export type PluginListResponse = { marketplaces: Array<PluginMarketplaceEntry>, remoteSyncError: string | null, featuredPluginIds: Array<string>, };

View File

@@ -3,4 +3,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SubAgentSource } from "../SubAgentSource";
export type SessionSource = "cli" | "vscode" | "exec" | "appServer" | { "subAgent": SubAgentSource } | "unknown";
export type SessionSource = "cli" | "vscode" | "exec" | "appServer" | { "custom": string } | { "subAgent": SubAgentSource } | "unknown";

View File

@@ -3,4 +3,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SkillInterface } from "./SkillInterface";
export type SkillSummary = { name: string, description: string, shortDescription: string | null, interface: SkillInterface | null, path: string, };
export type SkillSummary = { name: string, description: string, shortDescription: string | null, interface: SkillInterface | null, path: string, enabled: boolean, };

View File

@@ -1,5 +1,14 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
export type SkillsConfigWriteParams = { path: string, enabled: boolean, };
export type SkillsConfigWriteParams = {
/**
* Path-based selector.
*/
path?: AbsolutePathBuf | null,
/**
* Name-based selector.
*/
name?: string | null, enabled: boolean, };

View File

@@ -8,10 +8,12 @@ import type { CollabAgentState } from "./CollabAgentState";
import type { CollabAgentTool } from "./CollabAgentTool";
import type { CollabAgentToolCallStatus } from "./CollabAgentToolCallStatus";
import type { CommandAction } from "./CommandAction";
import type { CommandExecutionSource } from "./CommandExecutionSource";
import type { CommandExecutionStatus } from "./CommandExecutionStatus";
import type { DynamicToolCallOutputContentItem } from "./DynamicToolCallOutputContentItem";
import type { DynamicToolCallStatus } from "./DynamicToolCallStatus";
import type { FileUpdateChange } from "./FileUpdateChange";
import type { HookPromptFragment } from "./HookPromptFragment";
import type { McpToolCallError } from "./McpToolCallError";
import type { McpToolCallResult } from "./McpToolCallResult";
import type { McpToolCallStatus } from "./McpToolCallStatus";
@@ -20,7 +22,7 @@ import type { PatchApplyStatus } from "./PatchApplyStatus";
import type { UserInput } from "./UserInput";
import type { WebSearchAction } from "./WebSearchAction";
export type ThreadItem = { "type": "userMessage", id: string, content: Array<UserInput>, } | { "type": "agentMessage", id: string, text: string, phase: MessagePhase | null, memoryCitation: MemoryCitation | null, } | { "type": "plan", id: string, text: string, } | { "type": "reasoning", id: string, summary: Array<string>, content: Array<string>, } | { "type": "commandExecution", id: string,
export type ThreadItem = { "type": "userMessage", id: string, content: Array<UserInput>, } | { "type": "hookPrompt", id: string, fragments: Array<HookPromptFragment>, } | { "type": "agentMessage", id: string, text: string, phase: MessagePhase | null, memoryCitation: MemoryCitation | null, } | { "type": "plan", id: string, text: string, } | { "type": "reasoning", id: string, summary: Array<string>, content: Array<string>, } | { "type": "commandExecution", id: string,
/**
* The command to be executed.
*/
@@ -32,7 +34,7 @@ cwd: string,
/**
* Identifier for the underlying PTY process (when available).
*/
processId: string | null, status: CommandExecutionStatus,
processId: string | null, source: CommandExecutionSource, status: CommandExecutionStatus,
/**
* A best-effort parsing of the command to understand the action(s) it will perform.
* This returns a list of CommandAction objects because a single shell command may
@@ -95,4 +97,4 @@ reasoningEffort: ReasoningEffort | null,
/**
* Last known status of the target agents, when available.
*/
agentsStates: { [key in string]?: CollabAgentState }, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, } | { "type": "imageView", id: string, path: string, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, };
agentsStates: { [key in string]?: CollabAgentState }, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, } | { "type": "imageView", id: string, path: string, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, savedPath?: string, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, };

View File

@@ -0,0 +1,9 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* EXPERIMENTAL - flat transcript delta emitted whenever realtime
* transcript text changes.
*/
export type ThreadRealtimeTranscriptUpdatedNotification = { threadId: string, role: string, text: string, };

View File

@@ -0,0 +1,12 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ThreadShellCommandParams = { threadId: string,
/**
* Shell command string evaluated by the thread's configured shell.
* Unlike `command/exec`, this intentionally preserves shell syntax
* such as pipes, redirects, and quoting. This runs unsandboxed with full
* access rather than inheriting the thread sandbox policy.
*/
command: string, };

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ThreadShellCommandResponse = Record<string, never>;

View File

@@ -55,6 +55,7 @@ export type { CommandExecutionOutputDeltaNotification } from "./CommandExecution
export type { CommandExecutionRequestApprovalParams } from "./CommandExecutionRequestApprovalParams";
export type { CommandExecutionRequestApprovalResponse } from "./CommandExecutionRequestApprovalResponse";
export type { CommandExecutionRequestApprovalSkillMetadata } from "./CommandExecutionRequestApprovalSkillMetadata";
export type { CommandExecutionSource } from "./CommandExecutionSource";
export type { CommandExecutionStatus } from "./CommandExecutionStatus";
export type { Config } from "./Config";
export type { ConfigBatchWriteParams } from "./ConfigBatchWriteParams";
@@ -125,6 +126,7 @@ export type { HookExecutionMode } from "./HookExecutionMode";
export type { HookHandlerType } from "./HookHandlerType";
export type { HookOutputEntry } from "./HookOutputEntry";
export type { HookOutputEntryKind } from "./HookOutputEntryKind";
export type { HookPromptFragment } from "./HookPromptFragment";
export type { HookRunStatus } from "./HookRunStatus";
export type { HookRunSummary } from "./HookRunSummary";
export type { HookScope } from "./HookScope";
@@ -169,7 +171,9 @@ export type { McpServerOauthLoginCompletedNotification } from "./McpServerOauthL
export type { McpServerOauthLoginParams } from "./McpServerOauthLoginParams";
export type { McpServerOauthLoginResponse } from "./McpServerOauthLoginResponse";
export type { McpServerRefreshResponse } from "./McpServerRefreshResponse";
export type { McpServerStartupState } from "./McpServerStartupState";
export type { McpServerStatus } from "./McpServerStatus";
export type { McpServerStatusUpdatedNotification } from "./McpServerStatusUpdatedNotification";
export type { McpToolCallError } from "./McpToolCallError";
export type { McpToolCallProgressNotification } from "./McpToolCallProgressNotification";
export type { McpToolCallResult } from "./McpToolCallResult";
@@ -190,6 +194,7 @@ export type { NetworkApprovalProtocol } from "./NetworkApprovalProtocol";
export type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment";
export type { NetworkPolicyRuleAction } from "./NetworkPolicyRuleAction";
export type { NetworkRequirements } from "./NetworkRequirements";
export type { NonSteerableTurnKind } from "./NonSteerableTurnKind";
export type { OverriddenMetadata } from "./OverriddenMetadata";
export type { PatchApplyStatus } from "./PatchApplyStatus";
export type { PatchChangeKind } from "./PatchChangeKind";
@@ -277,12 +282,15 @@ export type { ThreadRealtimeErrorNotification } from "./ThreadRealtimeErrorNotif
export type { ThreadRealtimeItemAddedNotification } from "./ThreadRealtimeItemAddedNotification";
export type { ThreadRealtimeOutputAudioDeltaNotification } from "./ThreadRealtimeOutputAudioDeltaNotification";
export type { ThreadRealtimeStartedNotification } from "./ThreadRealtimeStartedNotification";
export type { ThreadRealtimeTranscriptUpdatedNotification } from "./ThreadRealtimeTranscriptUpdatedNotification";
export type { ThreadResumeParams } from "./ThreadResumeParams";
export type { ThreadResumeResponse } from "./ThreadResumeResponse";
export type { ThreadRollbackParams } from "./ThreadRollbackParams";
export type { ThreadRollbackResponse } from "./ThreadRollbackResponse";
export type { ThreadSetNameParams } from "./ThreadSetNameParams";
export type { ThreadSetNameResponse } from "./ThreadSetNameResponse";
export type { ThreadShellCommandParams } from "./ThreadShellCommandParams";
export type { ThreadShellCommandResponse } from "./ThreadShellCommandResponse";
export type { ThreadSortKey } from "./ThreadSortKey";
export type { ThreadSourceKind } from "./ThreadSourceKind";
export type { ThreadStartParams } from "./ThreadStartParams";

View File

@@ -267,6 +267,10 @@ client_request_definitions! {
params: v2::ThreadCompactStartParams,
response: v2::ThreadCompactStartResponse,
},
ThreadShellCommand => "thread/shellCommand" {
params: v2::ThreadShellCommandParams,
response: v2::ThreadShellCommandResponse,
},
#[experimental("thread/backgroundTerminals/clean")]
ThreadBackgroundTerminalsClean => "thread/backgroundTerminals/clean" {
params: v2::ThreadBackgroundTerminalsCleanParams,
@@ -800,11 +804,20 @@ pub struct FuzzyFileSearchParams {
pub struct FuzzyFileSearchResult {
pub root: String,
pub path: String,
pub match_type: FuzzyFileSearchMatchType,
pub file_name: String,
pub score: u32,
pub indices: Option<Vec<u32>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
pub enum FuzzyFileSearchMatchType {
File,
Directory,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
pub struct FuzzyFileSearchResponse {
pub files: Vec<FuzzyFileSearchResult>,
@@ -892,6 +905,7 @@ server_notification_definitions! {
ServerRequestResolved => "serverRequest/resolved" (v2::ServerRequestResolvedNotification),
McpToolCallProgress => "item/mcpToolCall/progress" (v2::McpToolCallProgressNotification),
McpServerOauthLoginCompleted => "mcpServer/oauthLogin/completed" (v2::McpServerOauthLoginCompletedNotification),
McpServerStatusUpdated => "mcpServer/startupStatus/updated" (v2::McpServerStatusUpdatedNotification),
AccountUpdated => "account/updated" (v2::AccountUpdatedNotification),
AccountRateLimitsUpdated => "account/rateLimits/updated" (v2::AccountRateLimitsUpdatedNotification),
AppListUpdated => "app/list/updated" (v2::AppListUpdatedNotification),
@@ -909,6 +923,8 @@ server_notification_definitions! {
ThreadRealtimeStarted => "thread/realtime/started" (v2::ThreadRealtimeStartedNotification),
#[experimental("thread/realtime/itemAdded")]
ThreadRealtimeItemAdded => "thread/realtime/itemAdded" (v2::ThreadRealtimeItemAddedNotification),
#[experimental("thread/realtime/transcriptUpdated")]
ThreadRealtimeTranscriptUpdated => "thread/realtime/transcriptUpdated" (v2::ThreadRealtimeTranscriptUpdatedNotification),
#[experimental("thread/realtime/outputAudio/delta")]
ThreadRealtimeOutputAudioDelta => "thread/realtime/outputAudio/delta" (v2::ThreadRealtimeOutputAudioDeltaNotification),
#[experimental("thread/realtime/error")]

View File

@@ -18,6 +18,7 @@ use crate::protocol::v2::TurnError;
use crate::protocol::v2::TurnStatus;
use crate::protocol::v2::UserInput;
use crate::protocol::v2::WebSearchAction;
use codex_protocol::items::parse_hook_prompt_message;
use codex_protocol::models::MessagePhase;
use codex_protocol::protocol::AgentReasoningEvent;
use codex_protocol::protocol::AgentReasoningRawContentEvent;
@@ -184,12 +185,37 @@ impl ThreadHistoryBuilder {
match item {
RolloutItem::EventMsg(event) => self.handle_event(event),
RolloutItem::Compacted(payload) => self.handle_compacted(payload),
RolloutItem::TurnContext(_)
| RolloutItem::SessionMeta(_)
| RolloutItem::ResponseItem(_) => {}
RolloutItem::ResponseItem(item) => self.handle_response_item(item),
RolloutItem::TurnContext(_) | RolloutItem::SessionMeta(_) => {}
}
}
fn handle_response_item(&mut self, item: &codex_protocol::models::ResponseItem) {
let codex_protocol::models::ResponseItem::Message {
role, content, id, ..
} = item
else {
return;
};
if role != "user" {
return;
}
let Some(hook_prompt) = parse_hook_prompt_message(id.as_ref(), content) else {
return;
};
self.ensure_turn().items.push(ThreadItem::HookPrompt {
id: hook_prompt.id,
fragments: hook_prompt
.fragments
.into_iter()
.map(crate::protocol::v2::HookPromptFragment::from)
.collect(),
});
}
fn handle_user_message(&mut self, payload: &UserMessageEvent) {
// User messages should stay in explicitly opened turns. For backward
// compatibility with older streams that did not open turns explicitly,
@@ -281,6 +307,7 @@ impl ThreadHistoryBuilder {
);
}
codex_protocol::items::TurnItem::UserMessage(_)
| codex_protocol::items::TurnItem::HookPrompt(_)
| codex_protocol::items::TurnItem::AgentMessage(_)
| codex_protocol::items::TurnItem::Reasoning(_)
| codex_protocol::items::TurnItem::WebSearch(_)
@@ -301,6 +328,7 @@ impl ThreadHistoryBuilder {
);
}
codex_protocol::items::TurnItem::UserMessage(_)
| codex_protocol::items::TurnItem::HookPrompt(_)
| codex_protocol::items::TurnItem::AgentMessage(_)
| codex_protocol::items::TurnItem::Reasoning(_)
| codex_protocol::items::TurnItem::WebSearch(_)
@@ -341,6 +369,7 @@ impl ThreadHistoryBuilder {
command,
cwd: payload.cwd.clone(),
process_id: payload.process_id.clone(),
source: payload.source.into(),
status: CommandExecutionStatus::InProgress,
command_actions,
aggregated_output: None,
@@ -371,6 +400,7 @@ impl ThreadHistoryBuilder {
command,
cwd: payload.cwd.clone(),
process_id: payload.process_id.clone(),
source: payload.source.into(),
status,
command_actions,
aggregated_output,
@@ -539,6 +569,7 @@ impl ThreadHistoryBuilder {
status: String::new(),
revised_prompt: None,
result: String::new(),
saved_path: None,
};
self.upsert_item_in_current_turn(item);
}
@@ -549,6 +580,7 @@ impl ThreadHistoryBuilder {
status: payload.status.clone(),
revised_prompt: payload.revised_prompt.clone(),
result: payload.result.clone(),
saved_path: payload.saved_path.clone(),
};
self.upsert_item_in_current_turn(item);
}
@@ -1144,10 +1176,13 @@ impl From<&PendingTurn> for Turn {
#[cfg(test)]
mod tests {
use super::*;
use crate::protocol::v2::CommandExecutionSource;
use codex_protocol::ThreadId;
use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem as CoreDynamicToolCallOutputContentItem;
use codex_protocol::items::HookPromptFragment as CoreHookPromptFragment;
use codex_protocol::items::TurnItem as CoreTurnItem;
use codex_protocol::items::UserMessageItem as CoreUserMessageItem;
use codex_protocol::items::build_hook_prompt_message;
use codex_protocol::models::MessagePhase as CoreMessagePhase;
use codex_protocol::models::WebSearchAction as CoreWebSearchAction;
use codex_protocol::parse_command::ParsedCommand;
@@ -1352,6 +1387,61 @@ mod tests {
);
}
#[test]
fn replays_image_generation_end_events_into_turn_history() {
let items = vec![
RolloutItem::EventMsg(EventMsg::TurnStarted(TurnStartedEvent {
turn_id: "turn-image".into(),
model_context_window: None,
collaboration_mode_kind: Default::default(),
})),
RolloutItem::EventMsg(EventMsg::UserMessage(UserMessageEvent {
message: "generate an image".into(),
images: None,
text_elements: Vec::new(),
local_images: Vec::new(),
})),
RolloutItem::EventMsg(EventMsg::ImageGenerationEnd(ImageGenerationEndEvent {
call_id: "ig_123".into(),
status: "completed".into(),
revised_prompt: Some("final prompt".into()),
result: "Zm9v".into(),
saved_path: Some("/tmp/ig_123.png".into()),
})),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: "turn-image".into(),
last_agent_message: None,
})),
];
let turns = build_turns_from_rollout_items(&items);
assert_eq!(turns.len(), 1);
assert_eq!(
turns[0],
Turn {
id: "turn-image".into(),
status: TurnStatus::Completed,
error: None,
items: vec![
ThreadItem::UserMessage {
id: "item-1".into(),
content: vec![UserInput::Text {
text: "generate an image".into(),
text_elements: Vec::new(),
}],
},
ThreadItem::ImageGeneration {
id: "ig_123".into(),
status: "completed".into(),
revised_prompt: Some("final prompt".into()),
result: "Zm9v".into(),
saved_path: Some("/tmp/ig_123.png".into()),
},
],
}
);
}
#[test]
fn splits_reasoning_when_interleaved() {
let events = vec![
@@ -1745,6 +1835,7 @@ mod tests {
command: "echo 'hello world'".into(),
cwd: PathBuf::from("/tmp"),
process_id: Some("pid-1".into()),
source: CommandExecutionSource::Agent,
status: CommandExecutionStatus::Completed,
command_actions: vec![CommandAction::Unknown {
command: "echo hello world".into(),
@@ -1893,6 +1984,7 @@ mod tests {
command: "ls".into(),
cwd: PathBuf::from("/tmp"),
process_id: Some("pid-2".into()),
source: CommandExecutionSource::Agent,
status: CommandExecutionStatus::Declined,
command_actions: vec![CommandAction::Unknown {
command: "ls".into(),
@@ -1987,6 +2079,7 @@ mod tests {
command: "echo done".into(),
cwd: PathBuf::from("/tmp"),
process_id: Some("pid-42".into()),
source: CommandExecutionSource::Agent,
status: CommandExecutionStatus::Completed,
command_actions: vec![CommandAction::Unknown {
command: "echo done".into(),
@@ -2639,4 +2732,80 @@ mod tests {
})
);
}
#[test]
fn rebuilds_hook_prompt_items_from_rollout_response_items() {
let hook_prompt = build_hook_prompt_message(&[
CoreHookPromptFragment::from_single_hook("Retry with tests.", "hook-run-1"),
CoreHookPromptFragment::from_single_hook("Then summarize cleanly.", "hook-run-2"),
])
.expect("hook prompt message");
let items = vec![
RolloutItem::EventMsg(EventMsg::TurnStarted(TurnStartedEvent {
turn_id: "turn-a".into(),
model_context_window: None,
collaboration_mode_kind: Default::default(),
})),
RolloutItem::EventMsg(EventMsg::UserMessage(UserMessageEvent {
message: "hello".into(),
images: None,
text_elements: Vec::new(),
local_images: Vec::new(),
})),
RolloutItem::ResponseItem(hook_prompt),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: "turn-a".into(),
last_agent_message: None,
})),
];
let turns = build_turns_from_rollout_items(&items);
assert_eq!(turns.len(), 1);
assert_eq!(turns[0].items.len(), 2);
assert_eq!(
turns[0].items[1],
ThreadItem::HookPrompt {
id: turns[0].items[1].id().to_string(),
fragments: vec![
crate::protocol::v2::HookPromptFragment {
text: "Retry with tests.".into(),
hook_run_id: "hook-run-1".into(),
},
crate::protocol::v2::HookPromptFragment {
text: "Then summarize cleanly.".into(),
hook_run_id: "hook-run-2".into(),
},
],
}
);
}
#[test]
fn ignores_plain_user_response_items_in_rollout_replay() {
let items = vec![
RolloutItem::EventMsg(EventMsg::TurnStarted(TurnStartedEvent {
turn_id: "turn-a".into(),
model_context_window: None,
collaboration_mode_kind: Default::default(),
})),
RolloutItem::ResponseItem(codex_protocol::models::ResponseItem::Message {
id: Some("msg-1".into()),
role: "user".into(),
content: vec![codex_protocol::models::ContentItem::InputText {
text: "plain text".into(),
}],
end_turn: None,
phase: None,
}),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: "turn-a".into(),
last_agent_message: None,
})),
];
let turns = build_turns_from_rollout_items(&items);
assert_eq!(turns.len(), 1);
assert!(turns[0].items.is_empty());
}
}

View File

@@ -52,6 +52,7 @@ use codex_protocol::protocol::AgentStatus as CoreAgentStatus;
use codex_protocol::protocol::AskForApproval as CoreAskForApproval;
use codex_protocol::protocol::CodexErrorInfo as CoreCodexErrorInfo;
use codex_protocol::protocol::CreditsSnapshot as CoreCreditsSnapshot;
use codex_protocol::protocol::ExecCommandSource as CoreExecCommandSource;
use codex_protocol::protocol::ExecCommandStatus as CoreExecCommandStatus;
use codex_protocol::protocol::GranularApprovalConfig as CoreGranularApprovalConfig;
use codex_protocol::protocol::GuardianRiskLevel as CoreGuardianRiskLevel;
@@ -65,6 +66,7 @@ use codex_protocol::protocol::HookRunSummary as CoreHookRunSummary;
use codex_protocol::protocol::HookScope as CoreHookScope;
use codex_protocol::protocol::ModelRerouteReason as CoreModelRerouteReason;
use codex_protocol::protocol::NetworkAccess as CoreNetworkAccess;
use codex_protocol::protocol::NonSteerableTurnKind as CoreNonSteerableTurnKind;
use codex_protocol::protocol::PatchApplyStatus as CorePatchApplyStatus;
use codex_protocol::protocol::RateLimitSnapshot as CoreRateLimitSnapshot;
use codex_protocol::protocol::RateLimitWindow as CoreRateLimitWindow;
@@ -92,6 +94,7 @@ use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value as JsonValue;
use serde_with::serde_as;
use thiserror::Error;
use ts_rs::TS;
@@ -126,6 +129,14 @@ macro_rules! v2_enum_from_core {
};
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub enum NonSteerableTurnKind {
Review,
Compact,
}
/// This translation layer make sure that we expose codex error code in camel case.
///
/// When an upstream HTTP status is available (for example, from the Responses API or a provider),
@@ -165,6 +176,13 @@ pub enum CodexErrorInfo {
#[ts(rename = "httpStatusCode")]
http_status_code: Option<u16>,
},
/// Returned when `turn/start` or `turn/steer` is submitted while the current active turn
/// cannot accept same-turn steering, for example `/review` or manual `/compact`.
ActiveTurnNotSteerable {
#[serde(rename = "turnKind")]
#[ts(rename = "turnKind")]
turn_kind: NonSteerableTurnKind,
},
Other,
}
@@ -191,11 +209,25 @@ impl From<CoreCodexErrorInfo> for CodexErrorInfo {
CoreCodexErrorInfo::ResponseTooManyFailedAttempts { http_status_code } => {
CodexErrorInfo::ResponseTooManyFailedAttempts { http_status_code }
}
CoreCodexErrorInfo::ActiveTurnNotSteerable { turn_kind } => {
CodexErrorInfo::ActiveTurnNotSteerable {
turn_kind: turn_kind.into(),
}
}
CoreCodexErrorInfo::Other => CodexErrorInfo::Other,
}
}
}
impl From<CoreNonSteerableTurnKind> for NonSteerableTurnKind {
fn from(value: CoreNonSteerableTurnKind) -> Self {
match value {
CoreNonSteerableTurnKind::Review => Self::Review,
CoreNonSteerableTurnKind::Compact => Self::Compact,
}
}
}
#[derive(
Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS, ExperimentalApi,
)]
@@ -1467,6 +1499,7 @@ pub enum SessionSource {
VsCode,
Exec,
AppServer,
Custom(String),
SubAgent(CoreSubAgentSource),
#[serde(other)]
Unknown,
@@ -1479,6 +1512,7 @@ impl From<CoreSessionSource> for SessionSource {
CoreSessionSource::VSCode => SessionSource::VsCode,
CoreSessionSource::Exec => SessionSource::Exec,
CoreSessionSource::Mcp => SessionSource::AppServer,
CoreSessionSource::Custom(source) => SessionSource::Custom(source),
CoreSessionSource::SubAgent(sub) => SessionSource::SubAgent(sub),
CoreSessionSource::Unknown => SessionSource::Unknown,
}
@@ -1492,6 +1526,7 @@ impl From<SessionSource> for CoreSessionSource {
SessionSource::VsCode => CoreSessionSource::VSCode,
SessionSource::Exec => CoreSessionSource::Exec,
SessionSource::AppServer => CoreSessionSource::Mcp,
SessionSource::Custom(source) => CoreSessionSource::Custom(source),
SessionSource::SubAgent(sub) => CoreSessionSource::SubAgent(sub),
SessionSource::Unknown => CoreSessionSource::Unknown,
}
@@ -2030,6 +2065,7 @@ pub struct AppSummary {
pub name: String,
pub description: Option<String>,
pub install_url: Option<String>,
pub needs_auth: bool,
}
impl From<AppInfo> for AppSummary {
@@ -2039,6 +2075,7 @@ impl From<AppInfo> for AppSummary {
name: value.name,
description: value.description,
install_url: value.install_url,
needs_auth: false,
}
}
}
@@ -2871,6 +2908,23 @@ pub struct ThreadCompactStartParams {
#[ts(export_to = "v2/")]
pub struct ThreadCompactStartResponse {}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadShellCommandParams {
pub thread_id: String,
/// Shell command string evaluated by the thread's configured shell.
/// Unlike `command/exec`, this intentionally preserves shell syntax
/// such as pipes, redirects, and quoting. This runs unsandboxed with full
/// access rather than inheriting the thread sandbox policy.
pub command: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadShellCommandResponse {}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -3093,6 +3147,8 @@ pub struct PluginListParams {
pub struct PluginListResponse {
pub marketplaces: Vec<PluginMarketplaceEntry>,
pub remote_sync_error: Option<String>,
#[serde(default)]
pub featured_plugin_ids: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -3284,6 +3340,7 @@ pub struct SkillSummary {
pub short_description: Option<String>,
pub interface: Option<SkillInterface>,
pub path: PathBuf,
pub enabled: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -3322,7 +3379,12 @@ pub enum PluginSource {
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct SkillsConfigWriteParams {
pub path: PathBuf,
/// Path-based selector.
#[ts(optional = nullable)]
pub path: Option<AbsolutePathBuf>,
/// Name-based selector.
#[ts(optional = nullable)]
pub name: Option<String>,
pub enabled: bool,
}
@@ -3761,6 +3823,17 @@ pub struct ThreadRealtimeItemAddedNotification {
pub item: JsonValue,
}
/// EXPERIMENTAL - flat transcript delta emitted whenever realtime
/// transcript text changes.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadRealtimeTranscriptUpdatedNotification {
pub thread_id: String,
pub role: String,
pub text: String,
}
/// EXPERIMENTAL - streamed output audio emitted by thread realtime.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
@@ -4103,6 +4176,12 @@ pub enum ThreadItem {
UserMessage { id: String, content: Vec<UserInput> },
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
HookPrompt {
id: String,
fragments: Vec<HookPromptFragment>,
},
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
AgentMessage {
id: String,
text: String,
@@ -4135,6 +4214,8 @@ pub enum ThreadItem {
cwd: PathBuf,
/// Identifier for the underlying PTY process (when available).
process_id: Option<String>,
#[serde(default)]
source: CommandExecutionSource,
status: CommandExecutionStatus,
/// A best-effort parsing of the command to understand the action(s) it will perform.
/// This returns a list of CommandAction objects because a single shell command may
@@ -4222,6 +4303,9 @@ pub enum ThreadItem {
status: String,
revised_prompt: Option<String>,
result: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
saved_path: Option<String>,
},
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
@@ -4234,10 +4318,19 @@ pub enum ThreadItem {
ContextCompaction { id: String },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase", export_to = "v2/")]
pub struct HookPromptFragment {
pub text: String,
pub hook_run_id: String,
}
impl ThreadItem {
pub fn id(&self) -> &str {
match self {
ThreadItem::UserMessage { id, .. }
| ThreadItem::HookPrompt { id, .. }
| ThreadItem::AgentMessage { id, .. }
| ThreadItem::Plan { id, .. }
| ThreadItem::Reasoning { id, .. }
@@ -4347,6 +4440,14 @@ impl From<CoreTurnItem> for ThreadItem {
id: user.id,
content: user.content.into_iter().map(UserInput::from).collect(),
},
CoreTurnItem::HookPrompt(hook_prompt) => ThreadItem::HookPrompt {
id: hook_prompt.id,
fragments: hook_prompt
.fragments
.into_iter()
.map(HookPromptFragment::from)
.collect(),
},
CoreTurnItem::AgentMessage(agent) => {
let text = agent
.content
@@ -4381,6 +4482,7 @@ impl From<CoreTurnItem> for ThreadItem {
status: image.status,
revised_prompt: image.revised_prompt,
result: image.result,
saved_path: image.saved_path,
},
CoreTurnItem::ContextCompaction(compaction) => {
ThreadItem::ContextCompaction { id: compaction.id }
@@ -4389,6 +4491,15 @@ impl From<CoreTurnItem> for ThreadItem {
}
}
impl From<codex_protocol::items::HookPromptFragment> for HookPromptFragment {
fn from(value: codex_protocol::items::HookPromptFragment) -> Self {
Self {
text: value.text,
hook_run_id: value.hook_run_id,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -4415,6 +4526,17 @@ impl From<&CoreExecCommandStatus> for CommandExecutionStatus {
}
}
v2_enum_from_core! {
#[derive(Default)]
pub enum CommandExecutionSource from CoreExecCommandSource {
#[default]
Agent,
UserShell,
UnifiedExecStartup,
UnifiedExecInteraction,
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -4861,6 +4983,7 @@ pub struct TerminalInteractionNotification {
pub stdin: String,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -4930,6 +5053,25 @@ pub struct McpServerOauthLoginCompletedNotification {
pub error: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub enum McpServerStartupState {
Starting,
Ready,
Failed,
Cancelled,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct McpServerStatusUpdatedNotification {
pub name: String,
pub status: McpServerStartupState,
pub error: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -6311,6 +6453,40 @@ mod tests {
assert_eq!(decoded, params);
}
#[test]
fn thread_shell_command_params_round_trip() {
let params = ThreadShellCommandParams {
thread_id: "thr_123".to_string(),
command: "printf 'hello world\\n'".to_string(),
};
let value = serde_json::to_value(&params).expect("serialize thread/shellCommand params");
assert_eq!(
value,
json!({
"threadId": "thr_123",
"command": "printf 'hello world\\n'",
})
);
let decoded = serde_json::from_value::<ThreadShellCommandParams>(value)
.expect("deserialize thread/shellCommand params");
assert_eq!(decoded, params);
}
#[test]
fn thread_shell_command_response_round_trip() {
let response = ThreadShellCommandResponse {};
let value =
serde_json::to_value(&response).expect("serialize thread/shellCommand response");
assert_eq!(value, json!({}));
let decoded = serde_json::from_value::<ThreadShellCommandResponse>(value)
.expect("deserialize thread/shellCommand response");
assert_eq!(decoded, response);
}
#[test]
fn command_exec_params_default_optional_streaming_flags() {
let params = serde_json::from_value::<CommandExecParams>(json!({
@@ -6605,6 +6781,32 @@ mod tests {
assert_eq!(decoded, notification);
}
#[test]
fn command_execution_output_delta_round_trips() {
let notification = CommandExecutionOutputDeltaNotification {
thread_id: "thread-1".to_string(),
turn_id: "turn-1".to_string(),
item_id: "item-1".to_string(),
delta: "\u{fffd}a\n".to_string(),
};
let value = serde_json::to_value(&notification)
.expect("serialize item/commandExecution/outputDelta notification");
assert_eq!(
value,
json!({
"threadId": "thread-1",
"turnId": "turn-1",
"itemId": "item-1",
"delta": "\u{fffd}a\n",
})
);
let decoded = serde_json::from_value::<CommandExecutionOutputDeltaNotification>(value)
.expect("deserialize round-trip");
assert_eq!(decoded, notification);
}
#[test]
fn sandbox_policy_round_trips_external_sandbox_network_access() {
let v2_policy = SandboxPolicy::ExternalSandbox {
@@ -7666,6 +7868,22 @@ mod tests {
);
}
#[test]
fn codex_error_info_serializes_active_turn_not_steerable_turn_kind_in_camel_case() {
let value = CodexErrorInfo::ActiveTurnNotSteerable {
turn_kind: NonSteerableTurnKind::Review,
};
assert_eq!(
serde_json::to_value(value).unwrap(),
json!({
"activeTurnNotSteerable": {
"turnKind": "review"
}
})
);
}
#[test]
fn dynamic_tool_response_serializes_content_items() {
let value = serde_json::to_value(DynamicToolCallResponse {

View File

@@ -32,7 +32,8 @@ axum = { workspace = true, default-features = false, features = [
codex-arg0 = { workspace = true }
codex-cloud-requirements = { workspace = true }
codex-core = { workspace = true }
codex-environment = { workspace = true }
codex-exec-server = { workspace = true }
codex-features = { workspace = true }
codex-otel = { workspace = true }
codex-shell-command = { workspace = true }
codex-utils-cli = { workspace = true }

View File

@@ -136,10 +136,11 @@ Example with notification opt-out:
- `thread/name/set` — set or update a threads user-facing name for either a loaded thread or a persisted rollout; returns `{}` on success and emits `thread/name/updated` to initialized, opted-in clients. Thread names are not required to be unique; name lookups resolve to the most recently updated thread.
- `thread/unarchive` — move an archived rollout file back into the sessions directory; returns the restored `thread` on success and emits `thread/unarchived`.
- `thread/compact/start` — trigger conversation history compaction for a thread; returns `{}` immediately while progress streams through standard turn/item notifications.
- `thread/shellCommand` — run a user-initiated `!` shell command against a thread; this runs unsandboxed with full access rather than inheriting the thread sandbox policy. Returns `{}` immediately while progress streams through standard turn/item notifications and any active turn receives the formatted output in its message stream.
- `thread/backgroundTerminals/clean` — terminate all running background terminals for a thread (experimental; requires `capabilities.experimentalApi`); returns `{}` when the cleanup request is accepted.
- `thread/rollback` — drop the last N turns from the agents in-memory context and persist a rollback marker in the rollout so future resumes see the pruned history; returns the updated `thread` (with `turns` populated) on success.
- `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications. For `collaborationMode`, `settings.developer_instructions: null` means "use built-in instructions for the selected mode".
- `turn/steer` — add user input to an already in-flight turn without starting a new turn; returns the active `turnId` that accepted the input.
- `turn/steer` — add user input to an already in-flight regular turn without starting a new turn; returns the active `turnId` that accepted the input. Review and manual compaction turns reject `turn/steer`.
- `turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`.
- `thread/realtime/start` — start a thread-scoped realtime session (experimental); returns `{}` and streams `thread/realtime/*` notifications.
- `thread/realtime/appendAudio` — append an input audio chunk to the active realtime session (experimental); returns `{}`.
@@ -162,12 +163,12 @@ Example with notification opt-out:
- `experimentalFeature/list` — list feature flags with stage metadata (`beta`, `underDevelopment`, `stable`, etc.), enabled/default-enabled state, and cursor pagination. For non-beta flags, `displayName`/`description`/`announcement` are `null`.
- `collaborationMode/list` — list available collaboration mode presets (experimental, no pagination). This response omits built-in developer instructions; clients should either pass `settings.developer_instructions: null` when setting a mode to use Codex's built-in instructions, or provide their own instructions explicitly.
- `skills/list` — list skills for one or more `cwd` values (optional `forceReload`).
- `plugin/list` — list discovered plugin marketplaces and plugin state, including effective marketplace install/auth policy metadata. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category. Pass `forceRemoteSync: true` to refresh curated plugin state before listing (**under development; do not call from production clients yet**).
- `plugin/read` — read one plugin by `marketplacePath` plus `pluginName`, returning marketplace info, a list-style `summary`, manifest descriptions/interface metadata, and bundled skills/apps/MCP server names (**under development; do not call from production clients yet**).
- `plugin/list` — list discovered plugin marketplaces and plugin state, including effective marketplace install/auth policy metadata and best-effort `featuredPluginIds` for the official curated marketplace. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category. Pass `forceRemoteSync: true` to refresh curated plugin state before listing (**under development; do not call from production clients yet**).
- `plugin/read` — read one plugin by `marketplacePath` plus `pluginName`, returning marketplace info, a list-style `summary`, manifest descriptions/interface metadata, and bundled skills/apps/MCP server names. Returned plugin skills include their current `enabled` state after local config filtering. Plugin app summaries also include `needsAuth` when the server can determine connector accessibility (**under development; do not call from production clients yet**).
- `skills/changed` — notification emitted when watched local skill files change.
- `app/list` — list available apps.
- `skills/config/write` — write user-level skill config by path.
- `plugin/install` — install a plugin from a discovered marketplace entry, rejecting marketplace entries marked unavailable for install, and return the effective plugin auth policy plus any apps that still need auth (**under development; do not call from production clients yet**).
- `skills/config/write` — write user-level skill config by name or absolute path.
- `plugin/install` — install a plugin from a discovered marketplace entry, rejecting marketplace entries marked unavailable for install, install MCPs if any, and return the effective plugin auth policy plus any apps that still need auth (**under development; do not call from production clients yet**).
- `plugin/uninstall` — uninstall a plugin by id by removing its cached files and clearing its user-level config entry (**under development; do not call from production clients yet**).
- `mcpServer/oauth/login` — start an OAuth login for a configured MCP server; returns an `authorization_url` and later emits `mcpServer/oauthLogin/completed` once the browser flow finishes.
- `tool/requestUserInput` — prompt the user with 13 short questions for a tool call and return their answers (experimental).
@@ -415,6 +416,31 @@ While compaction is running, the thread is effectively in a turn so clients shou
{ "id": 25, "result": {} }
```
### Example: Run a thread shell command
Use `thread/shellCommand` for the TUI `!` workflow. The request returns immediately with `{}`.
This API runs unsandboxed with full access; it does not inherit the thread
sandbox policy.
If the thread already has an active turn, the command runs as an auxiliary action on that turn. In that case, progress is emitted as standard `item/*` notifications on the existing turn and the formatted output is injected into the turns message stream:
- `item/started` with `item: { "type": "commandExecution", "source": "userShell", ... }`
- zero or more `item/commandExecution/outputDelta`
- `item/completed` with the same `commandExecution` item id
If the thread does not already have an active turn, the server starts a standalone turn for the shell command. In that case clients should expect:
- `turn/started`
- `item/started` with `item: { "type": "commandExecution", "source": "userShell", ... }`
- zero or more `item/commandExecution/outputDelta`
- `item/completed` with the same `commandExecution` item id
- `turn/completed`
```json
{ "method": "thread/shellCommand", "id": 26, "params": { "threadId": "thr_b", "command": "git status --short" } }
{ "id": 26, "result": {} }
```
### Example: Start a turn (send user input)
Turns attach user input (text or images) to a thread and trigger Codex generation. The `input` field is a list of discriminated unions:
@@ -549,8 +575,8 @@ Use `thread/backgroundTerminals/clean` to terminate all running background termi
### Example: Steer an active turn
Use `turn/steer` to append additional user input to the currently active turn. This does not emit
`turn/started` and does not accept turn context overrides.
Use `turn/steer` to append additional user input to the currently active regular turn. This does
not emit `turn/started` and does not accept turn context overrides.
```json
{ "method": "turn/steer", "id": 32, "params": {
@@ -561,7 +587,9 @@ Use `turn/steer` to append additional user input to the currently active turn. T
{ "id": 32, "result": { "turnId": "turn_456" } }
```
`expectedTurnId` is required. If there is no active turn (or `expectedTurnId` does not match the active turn), the request fails with an `invalid request` error.
`expectedTurnId` is required. If there is no active turn, `expectedTurnId` does not match the
active turn, or the active turn kind does not accept same-turn steering (for example review or
manual compaction), the request fails with an `invalid request` error.
### Example: Request a code review
@@ -799,7 +827,8 @@ The fuzzy file search session API emits per-query notifications:
The thread realtime API emits thread-scoped notifications for session lifecycle and streaming media:
- `thread/realtime/started``{ threadId, sessionId }` once realtime starts for the thread (experimental).
- `thread/realtime/itemAdded``{ threadId, item }` for non-audio realtime items (experimental). `item` is forwarded as raw JSON while the upstream websocket item schema remains unstable.
- `thread/realtime/itemAdded``{ threadId, item }` for raw non-audio realtime items that do not have a dedicated typed app-server notification, including `handoff_request` (experimental). `item` is forwarded as raw JSON while the upstream websocket item schema remains unstable.
- `thread/realtime/transcriptUpdated``{ threadId, role, text }` whenever realtime transcript text changes (experimental). This forwards the live transcript delta from that realtime event, not the full accumulated transcript.
- `thread/realtime/outputAudio/delta``{ threadId, audio }` for streamed output audio chunks (experimental). `audio` uses camelCase fields (`data`, `sampleRate`, `numChannels`, `samplesPerChannel`).
- `thread/realtime/error``{ threadId, message }` when realtime encounters a transport or backend error (experimental).
- `thread/realtime/closed``{ threadId, reason }` when the realtime transport closes (experimental).
@@ -810,6 +839,10 @@ Because audio is intentionally separate from `ThreadItem`, clients can opt out o
- `windowsSandbox/setupCompleted``{ mode, success, error }` after a `windowsSandbox/setupStart` request finishes.
### MCP server startup events
- `mcpServer/startupStatus/updated``{ name, status, error }` when app-server observes an MCP server startup transition. `status` is one of `starting`, `ready`, `failed`, or `cancelled`. `error` is `null` except for `failed`.
### Turn events
The app-server streams JSON-RPC notifications while a turn is running. Each turn emits `turn/started` when it begins running and ends with `turn/completed` (final `turn` status). Token usage events stream separately via `thread/tokenUsage/updated`. Clients subscribe to the events they care about, rendering each item incrementally as updates arrive. The per-item lifecycle is always: `item/started` → zero or more item-specific deltas → `item/completed`.
@@ -887,6 +920,8 @@ There are additional item-specific events:
- `ResponseStreamConnectionFailed { httpStatusCode? }`: failure to connect to the response SSE stream
- `ResponseStreamDisconnected { httpStatusCode? }`: disconnect of the response SSE stream in the middle of a turn before completion
- `ResponseTooManyFailedAttempts { httpStatusCode? }`
- `ActiveTurnNotSteerable { turnKind }`: `turn/start` or `turn/steer` was submitted while the
current active turn was not steerable, for example `/review` or manual `/compact`
- `BadRequest`
- `Unauthorized`
- `SandboxError`
@@ -1110,14 +1145,29 @@ The server also emits `skills/changed` notifications when watched local skill fi
}
```
To enable or disable a skill by path:
To enable or disable a skill by absolute path:
```json
{
"method": "skills/config/write",
"id": 26,
"params": {
"path": "/Users/me/.codex/skills/skill-creator/SKILL.md",
"path": "/Users/alice/.codex/skills/skill-creator/SKILL.md",
"name": null,
"enabled": false
}
}
```
To enable or disable a skill by name:
```json
{
"method": "skills/config/write",
"id": 27,
"params": {
"path": null,
"name": "github:yeet",
"enabled": false
}
}
@@ -1232,6 +1282,7 @@ Codex supports these authentication modes. The current mode is surfaced in `acco
- `account/rateLimits/read` — fetch ChatGPT rate limits; updates arrive via `account/rateLimits/updated` (notify).
- `account/rateLimits/updated` (notify) — emitted whenever a user's ChatGPT rate limits change.
- `mcpServer/oauthLogin/completed` (notify) — emitted after a `mcpServer/oauth/login` flow finishes for a server; payload includes `{ name, success, error? }`.
- `mcpServer/startupStatus/updated` (notify) — emitted when a configured MCP server's startup status changes for a loaded thread; payload includes `{ name, status, error }` where `status` is `starting`, `ready`, `failed`, or `cancelled`.
### 1) Check auth state

View File

@@ -107,6 +107,7 @@ fn app_server_request_span_template(
app_server.api_version = "v2",
app_server.client_name = field::Empty,
app_server.client_version = field::Empty,
turn.id = field::Empty,
)
}

View File

@@ -27,6 +27,7 @@ use codex_app_server_protocol::CommandExecutionOutputDeltaNotification;
use codex_app_server_protocol::CommandExecutionRequestApprovalParams;
use codex_app_server_protocol::CommandExecutionRequestApprovalResponse;
use codex_app_server_protocol::CommandExecutionRequestApprovalSkillMetadata;
use codex_app_server_protocol::CommandExecutionSource;
use codex_app_server_protocol::CommandExecutionStatus;
use codex_app_server_protocol::ContextCompactedNotification;
use codex_app_server_protocol::DeprecationNoticeNotification;
@@ -56,6 +57,8 @@ use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::McpServerElicitationAction;
use codex_app_server_protocol::McpServerElicitationRequestParams;
use codex_app_server_protocol::McpServerElicitationRequestResponse;
use codex_app_server_protocol::McpServerStartupState;
use codex_app_server_protocol::McpServerStatusUpdatedNotification;
use codex_app_server_protocol::McpToolCallError;
use codex_app_server_protocol::McpToolCallResult;
use codex_app_server_protocol::McpToolCallStatus;
@@ -83,6 +86,7 @@ use codex_app_server_protocol::ThreadRealtimeErrorNotification;
use codex_app_server_protocol::ThreadRealtimeItemAddedNotification;
use codex_app_server_protocol::ThreadRealtimeOutputAudioDeltaNotification;
use codex_app_server_protocol::ThreadRealtimeStartedNotification;
use codex_app_server_protocol::ThreadRealtimeTranscriptUpdatedNotification;
use codex_app_server_protocol::ThreadRollbackResponse;
use codex_app_server_protocol::ThreadTokenUsage;
use codex_app_server_protocol::ThreadTokenUsageUpdatedNotification;
@@ -110,6 +114,7 @@ use codex_core::sandboxing::intersect_permission_profiles;
use codex_protocol::ThreadId;
use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem as CoreDynamicToolCallOutputContentItem;
use codex_protocol::dynamic_tools::DynamicToolResponse as CoreDynamicToolResponse;
use codex_protocol::items::parse_hook_prompt_message;
use codex_protocol::plan_tool::UpdatePlanArgs;
use codex_protocol::protocol::ApplyPatchApprovalRequestEvent;
use codex_protocol::protocol::CodexErrorInfo as CoreCodexErrorInfo;
@@ -308,6 +313,34 @@ pub(crate) async fn apply_bespoke_event_handling(
.await;
}
}
EventMsg::McpStartupUpdate(update) => {
if let ApiVersion::V2 = api_version {
let (status, error) = match update.status {
codex_protocol::protocol::McpStartupStatus::Starting => {
(McpServerStartupState::Starting, None)
}
codex_protocol::protocol::McpStartupStatus::Ready => {
(McpServerStartupState::Ready, None)
}
codex_protocol::protocol::McpStartupStatus::Failed { error } => {
(McpServerStartupState::Failed, Some(error))
}
codex_protocol::protocol::McpStartupStatus::Cancelled => {
(McpServerStartupState::Cancelled, None)
}
};
let notification = McpServerStatusUpdatedNotification {
name: update.server,
status,
error,
};
outgoing
.send_server_notification(ServerNotification::McpServerStatusUpdated(
notification,
))
.await;
}
}
EventMsg::Warning(_warning_event) => {}
EventMsg::GuardianAssessment(assessment) => {
if let ApiVersion::V2 = api_version {
@@ -365,8 +398,30 @@ pub(crate) async fn apply_bespoke_event_handling(
))
.await;
}
RealtimeEvent::InputTranscriptDelta(_) => {}
RealtimeEvent::OutputTranscriptDelta(_) => {}
RealtimeEvent::InputTranscriptDelta(event) => {
let notification = ThreadRealtimeTranscriptUpdatedNotification {
thread_id: conversation_id.to_string(),
role: "user".to_string(),
text: event.delta,
};
outgoing
.send_server_notification(
ServerNotification::ThreadRealtimeTranscriptUpdated(notification),
)
.await;
}
RealtimeEvent::OutputTranscriptDelta(event) => {
let notification = ThreadRealtimeTranscriptUpdatedNotification {
thread_id: conversation_id.to_string(),
role: "assistant".to_string(),
text: event.delta,
};
outgoing
.send_server_notification(
ServerNotification::ThreadRealtimeTranscriptUpdated(notification),
)
.await;
}
RealtimeEvent::AudioOut(audio) => {
let notification = ThreadRealtimeOutputAudioDeltaNotification {
thread_id: conversation_id.to_string(),
@@ -1483,6 +1538,14 @@ pub(crate) async fn apply_bespoke_event_handling(
.await;
}
EventMsg::RawResponseItem(raw_response_item_event) => {
maybe_emit_hook_prompt_item_completed(
api_version,
conversation_id,
&event_turn_id,
&raw_response_item_event.item,
&outgoing,
)
.await;
maybe_emit_raw_response_item_completed(
api_version,
conversation_id,
@@ -1563,6 +1626,7 @@ pub(crate) async fn apply_bespoke_event_handling(
command,
cwd,
process_id,
source: exec_command_begin_event.source.into(),
status: CommandExecutionStatus::InProgress,
command_actions,
aggregated_output: None,
@@ -1580,7 +1644,6 @@ pub(crate) async fn apply_bespoke_event_handling(
}
EventMsg::ExecCommandOutputDelta(exec_command_output_delta_event) => {
let item_id = exec_command_output_delta_event.call_id.clone();
let delta = String::from_utf8_lossy(&exec_command_output_delta_event.chunk).to_string();
// The underlying EventMsg::ExecCommandOutputDelta is used for shell, unified_exec,
// and apply_patch tool calls. We represent apply_patch with the FileChange item, and
// everything else with the CommandExecution item.
@@ -1592,6 +1655,8 @@ pub(crate) async fn apply_bespoke_event_handling(
state.turn_summary.file_change_started.contains(&item_id)
};
if is_file_change {
let delta =
String::from_utf8_lossy(&exec_command_output_delta_event.chunk).to_string();
let notification = FileChangeOutputDeltaNotification {
thread_id: conversation_id.to_string(),
turn_id: event_turn_id.clone(),
@@ -1608,7 +1673,8 @@ pub(crate) async fn apply_bespoke_event_handling(
thread_id: conversation_id.to_string(),
turn_id: event_turn_id.clone(),
item_id,
delta,
delta: String::from_utf8_lossy(&exec_command_output_delta_event.chunk)
.to_string(),
};
outgoing
.send_server_notification(ServerNotification::CommandExecutionOutputDelta(
@@ -1641,6 +1707,7 @@ pub(crate) async fn apply_bespoke_event_handling(
aggregated_output,
exit_code,
duration,
source,
status,
..
} = exec_command_end_event;
@@ -1672,6 +1739,7 @@ pub(crate) async fn apply_bespoke_event_handling(
command: shlex_join(&command),
cwd,
process_id,
source: source.into(),
status,
command_actions,
aggregated_output,
@@ -1935,6 +2003,7 @@ async fn complete_command_execution_item(
command: String,
cwd: PathBuf,
process_id: Option<String>,
source: CommandExecutionSource,
command_actions: Vec<V2ParsedCommand>,
status: CommandExecutionStatus,
outgoing: &ThreadScopedOutgoingMessageSender,
@@ -1944,6 +2013,7 @@ async fn complete_command_execution_item(
command,
cwd,
process_id,
source,
status,
command_actions,
aggregated_output: None,
@@ -1981,6 +2051,49 @@ async fn maybe_emit_raw_response_item_completed(
.await;
}
async fn maybe_emit_hook_prompt_item_completed(
api_version: ApiVersion,
conversation_id: ThreadId,
turn_id: &str,
item: &codex_protocol::models::ResponseItem,
outgoing: &ThreadScopedOutgoingMessageSender,
) {
let ApiVersion::V2 = api_version else {
return;
};
let codex_protocol::models::ResponseItem::Message {
role, content, id, ..
} = item
else {
return;
};
if role != "user" {
return;
}
let Some(hook_prompt) = parse_hook_prompt_message(id.as_ref(), content) else {
return;
};
let notification = ItemCompletedNotification {
thread_id: conversation_id.to_string(),
turn_id: turn_id.to_string(),
item: ThreadItem::HookPrompt {
id: hook_prompt.id,
fragments: hook_prompt
.fragments
.into_iter()
.map(codex_app_server_protocol::HookPromptFragment::from)
.collect(),
},
};
outgoing
.send_server_notification(ServerNotification::ItemCompleted(notification))
.await;
}
async fn find_and_remove_turn_summary(
_conversation_id: ThreadId,
thread_state: &Arc<Mutex<ThreadState>>,
@@ -2607,6 +2720,7 @@ async fn on_command_execution_request_approval_response(
completion_item.command,
completion_item.cwd,
/*process_id*/ None,
CommandExecutionSource::Agent,
completion_item.command_actions,
status,
&outgoing,
@@ -2751,6 +2865,8 @@ mod tests {
use codex_app_server_protocol::GuardianApprovalReviewStatus;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::TurnPlanStepStatus;
use codex_protocol::items::HookPromptFragment;
use codex_protocol::items::build_hook_prompt_message;
use codex_protocol::mcp::CallToolResult;
use codex_protocol::models::FileSystemPermissions as CoreFileSystemPermissions;
use codex_protocol::models::NetworkPermissions as CoreNetworkPermissions;
@@ -3785,4 +3901,59 @@ mod tests {
assert!(rx.try_recv().is_err(), "no messages expected");
Ok(())
}
#[tokio::test]
async fn test_hook_prompt_raw_response_emits_item_completed() -> Result<()> {
let (tx, mut rx) = mpsc::channel(CHANNEL_CAPACITY);
let outgoing = Arc::new(OutgoingMessageSender::new(tx));
let conversation_id = ThreadId::new();
let outgoing = ThreadScopedOutgoingMessageSender::new(
outgoing,
vec![ConnectionId(1)],
conversation_id,
);
let item = build_hook_prompt_message(&[
HookPromptFragment::from_single_hook("Retry with tests.", "hook-run-1"),
HookPromptFragment::from_single_hook("Then summarize cleanly.", "hook-run-2"),
])
.expect("hook prompt message");
maybe_emit_hook_prompt_item_completed(
ApiVersion::V2,
conversation_id,
"turn-1",
&item,
&outgoing,
)
.await;
let msg = recv_broadcast_message(&mut rx).await?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::ItemCompleted(
notification,
)) => {
assert_eq!(notification.thread_id, conversation_id.to_string());
assert_eq!(notification.turn_id, "turn-1");
assert_eq!(
notification.item,
ThreadItem::HookPrompt {
id: notification.item.id().to_string(),
fragments: vec![
codex_app_server_protocol::HookPromptFragment {
text: "Retry with tests.".into(),
hook_run_id: "hook-run-1".into(),
},
codex_app_server_protocol::HookPromptFragment {
text: "Then summarize cleanly.".into(),
hook_run_id: "hook-run-2".into(),
},
],
}
);
}
other => bail!("unexpected message: {other:?}"),
}
assert!(rx.try_recv().is_err(), "no extra messages expected");
Ok(())
}
}

View File

@@ -32,6 +32,7 @@ use codex_app_server_protocol::CancelLoginAccountParams;
use codex_app_server_protocol::CancelLoginAccountResponse;
use codex_app_server_protocol::CancelLoginAccountStatus;
use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::CodexErrorInfo as AppServerCodexErrorInfo;
use codex_app_server_protocol::CollaborationModeListParams;
use codex_app_server_protocol::CollaborationModeListResponse;
use codex_app_server_protocol::CommandExecParams;
@@ -146,6 +147,8 @@ use codex_app_server_protocol::ThreadResumeResponse;
use codex_app_server_protocol::ThreadRollbackParams;
use codex_app_server_protocol::ThreadSetNameParams;
use codex_app_server_protocol::ThreadSetNameResponse;
use codex_app_server_protocol::ThreadShellCommandParams;
use codex_app_server_protocol::ThreadShellCommandResponse;
use codex_app_server_protocol::ThreadSortKey;
use codex_app_server_protocol::ThreadSourceKind;
use codex_app_server_protocol::ThreadStartParams;
@@ -159,6 +162,7 @@ use codex_app_server_protocol::ThreadUnsubscribeParams;
use codex_app_server_protocol::ThreadUnsubscribeResponse;
use codex_app_server_protocol::ThreadUnsubscribeStatus;
use codex_app_server_protocol::Turn;
use codex_app_server_protocol::TurnError;
use codex_app_server_protocol::TurnInterruptParams;
use codex_app_server_protocol::TurnStartParams;
use codex_app_server_protocol::TurnStartResponse;
@@ -179,6 +183,7 @@ use codex_core::AuthManager;
use codex_core::CodexAuth;
use codex_core::CodexThread;
use codex_core::Cursor as RolloutCursor;
use codex_core::ForkSnapshot;
use codex_core::NewThread;
use codex_core::RolloutRecorder;
use codex_core::SessionMeta;
@@ -189,7 +194,6 @@ use codex_core::ThreadSortKey as CoreThreadSortKey;
use codex_core::auth::AuthMode as CoreAuthMode;
use codex_core::auth::CLIENT_ID;
use codex_core::auth::login_with_api_key;
use codex_core::auth::login_with_chatgpt_auth_tokens;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use codex_core::config::NetworkProxyAuditMetadata;
@@ -202,12 +206,10 @@ use codex_core::config_loader::CloudRequirementsLoader;
use codex_core::default_client::set_default_client_residency_requirement;
use codex_core::error::CodexErr;
use codex_core::error::Result as CodexResult;
use codex_core::exec::ExecCapturePolicy;
use codex_core::exec::ExecExpiration;
use codex_core::exec::ExecParams;
use codex_core::exec_env::create_env;
use codex_core::features::FEATURES;
use codex_core::features::Feature;
use codex_core::features::Stage;
use codex_core::find_archived_thread_path_by_id_str;
use codex_core::find_thread_name_by_id;
use codex_core::find_thread_names_by_ids;
@@ -221,11 +223,13 @@ use codex_core::models_manager::collaboration_mode_presets::CollaborationModesCo
use codex_core::parse_cursor;
use codex_core::plugins::MarketplaceError;
use codex_core::plugins::MarketplacePluginSource;
use codex_core::plugins::OPENAI_CURATED_MARKETPLACE_NAME;
use codex_core::plugins::PluginInstallError as CorePluginInstallError;
use codex_core::plugins::PluginInstallRequest;
use codex_core::plugins::PluginReadRequest;
use codex_core::plugins::PluginUninstallError as CorePluginUninstallError;
use codex_core::plugins::load_plugin_apps;
use codex_core::plugins::load_plugin_mcp_servers;
use codex_core::read_head_for_summary;
use codex_core::read_session_meta_line;
use codex_core::rollout_date_parts;
@@ -236,9 +240,13 @@ use codex_core::state_db::reconcile_rollout;
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
use codex_core::windows_sandbox::WindowsSandboxSetupMode as CoreWindowsSandboxSetupMode;
use codex_core::windows_sandbox::WindowsSandboxSetupRequest;
use codex_features::FEATURES;
use codex_features::Feature;
use codex_features::Stage;
use codex_feedback::CodexFeedback;
use codex_login::ServerOptions as LoginServerOptions;
use codex_login::ShutdownHandle;
use codex_login::auth::login_with_chatgpt_auth_tokens;
use codex_login::run_login_server;
use codex_protocol::ThreadId;
use codex_protocol::config_types::CollaborationMode;
@@ -308,6 +316,7 @@ use codex_app_server_protocol::ServerRequest;
mod apps_list_helpers;
mod plugin_app_helpers;
mod plugin_mcp_oauth;
use crate::filters::compute_source_filters;
use crate::filters::source_kind_matches;
@@ -419,13 +428,16 @@ impl CodexMessageProcessor {
self.thread_manager.skills_manager().clear_cache();
}
pub(crate) async fn maybe_start_curated_repo_sync_for_latest_config(&self) {
pub(crate) async fn maybe_start_plugin_startup_tasks_for_latest_config(&self) {
match self.load_latest_config(/*fallback_cwd*/ None).await {
Ok(config) => self
.thread_manager
.plugins_manager()
.maybe_start_curated_repo_sync_for_config(&config),
Err(err) => warn!("failed to load latest config for curated plugin sync: {err:?}"),
.maybe_start_plugin_startup_tasks_for_config(
&config,
self.thread_manager.auth_manager(),
),
Err(err) => warn!("failed to load latest config for plugin startup tasks: {err:?}"),
}
}
@@ -691,6 +703,10 @@ impl CodexMessageProcessor {
self.thread_read(to_connection_request_id(request_id), params)
.await;
}
ClientRequest::ThreadShellCommand { request_id, params } => {
self.thread_shell_command(to_connection_request_id(request_id), params)
.await;
}
ClientRequest::SkillsList { request_id, params } => {
self.skills_list(to_connection_request_id(request_id), params)
.await;
@@ -1401,7 +1417,7 @@ impl CodexMessageProcessor {
let account = match self.auth_manager.auth_cached() {
Some(auth) => match auth.auth_mode() {
CoreAuthMode::ApiKey => Some(Account::ApiKey {}),
CoreAuthMode::Chatgpt => {
CoreAuthMode::Chatgpt | CoreAuthMode::ChatgptAuthTokens => {
let email = auth.get_account_email();
let plan_type = auth.account_plan_type();
@@ -1662,11 +1678,17 @@ impl CodexMessageProcessor {
None => ExecExpiration::DefaultTimeout,
}
};
let capture_policy = if disable_output_cap {
ExecCapturePolicy::FullBuffer
} else {
ExecCapturePolicy::ShellTool
};
let sandbox_cwd = self.config.cwd.clone();
let exec_params = ExecParams {
command,
cwd: cwd.clone(),
expiration,
capture_policy,
env,
network: started_network_proxy
.as_ref()
@@ -2970,6 +2992,58 @@ impl CodexMessageProcessor {
}
}
async fn thread_shell_command(
&self,
request_id: ConnectionRequestId,
params: ThreadShellCommandParams,
) {
let ThreadShellCommandParams { thread_id, command } = params;
let command = command.trim().to_string();
if command.is_empty() {
self.outgoing
.send_error(
request_id,
JSONRPCErrorError {
code: INVALID_REQUEST_ERROR_CODE,
message: "command must not be empty".to_string(),
data: None,
},
)
.await;
return;
}
let (_, thread) = match self.load_thread(&thread_id).await {
Ok(v) => v,
Err(error) => {
self.outgoing.send_error(request_id, error).await;
return;
}
};
match self
.submit_core_op(
&request_id,
thread.as_ref(),
Op::RunUserShellCommand { command },
)
.await
{
Ok(_) => {
self.outgoing
.send_response(request_id, ThreadShellCommandResponse {})
.await;
}
Err(err) => {
self.send_internal_error(
request_id,
format!("failed to start shell command: {err}"),
)
.await;
}
}
}
async fn thread_list(&self, request_id: ConnectionRequestId, params: ThreadListParams) {
let ThreadListParams {
cursor,
@@ -3966,7 +4040,7 @@ impl CodexMessageProcessor {
} = match self
.thread_manager
.fork_thread(
usize::MAX,
ForkSnapshot::FullHistory,
config,
rollout_path.clone(),
persist_extended_history,
@@ -4525,20 +4599,28 @@ impl CodexMessageProcessor {
}
};
let configured_servers = self
.thread_manager
.mcp_manager()
.configured_servers(&config);
if let Err(error) = self.queue_mcp_server_refresh_for_config(&config).await {
self.outgoing.send_error(request_id, error).await;
return;
}
let response = McpServerRefreshResponse {};
self.outgoing.send_response(request_id, response).await;
}
async fn queue_mcp_server_refresh_for_config(
&self,
config: &Config,
) -> Result<(), JSONRPCErrorError> {
let configured_servers = self.thread_manager.mcp_manager().configured_servers(config);
let mcp_servers = match serde_json::to_value(configured_servers) {
Ok(value) => value,
Err(err) => {
let error = JSONRPCErrorError {
return Err(JSONRPCErrorError {
code: INTERNAL_ERROR_CODE,
message: format!("failed to serialize MCP servers: {err}"),
data: None,
};
self.outgoing.send_error(request_id, error).await;
return;
});
}
};
@@ -4546,15 +4628,13 @@ impl CodexMessageProcessor {
match serde_json::to_value(config.mcp_oauth_credentials_store_mode) {
Ok(value) => value,
Err(err) => {
let error = JSONRPCErrorError {
return Err(JSONRPCErrorError {
code: INTERNAL_ERROR_CODE,
message: format!(
"failed to serialize MCP OAuth credentials store mode: {err}"
),
data: None,
};
self.outgoing.send_error(request_id, error).await;
return;
});
}
};
@@ -4567,8 +4647,7 @@ impl CodexMessageProcessor {
// active turn to avoid work for threads that never resume.
let thread_manager = Arc::clone(&self.thread_manager);
thread_manager.refresh_mcp_servers(refresh_config).await;
let response = McpServerRefreshResponse {};
self.outgoing.send_response(request_id, response).await;
Ok(())
}
async fn mcp_server_oauth_login(
@@ -4842,6 +4921,7 @@ impl CodexMessageProcessor {
MarketplaceError::InvalidMarketplaceFile { .. }
| MarketplaceError::PluginNotFound { .. }
| MarketplaceError::PluginNotAvailable { .. }
| MarketplaceError::PluginsDisabled
| MarketplaceError::InvalidPlugin(_) => {
self.send_invalid_request_error(request_id, err.to_string())
.await;
@@ -5363,6 +5443,13 @@ impl CodexMessageProcessor {
.extend(valid_extra_roots);
}
let config = match self.load_latest_config(/*fallback_cwd*/ None).await {
Ok(config) => config,
Err(error) => {
self.outgoing.send_error(request_id, error).await;
return;
}
};
let skills_manager = self.thread_manager.skills_manager();
let mut data = Vec::new();
for cwd in cwds {
@@ -5370,7 +5457,7 @@ impl CodexMessageProcessor {
.get(&cwd)
.map_or(&[][..], std::vec::Vec::as_slice);
let outcome = skills_manager
.skills_for_cwd_with_extra_user_roots(&cwd, force_reload, extra_roots)
.skills_for_cwd_with_extra_user_roots(&cwd, &config, force_reload, extra_roots)
.await;
let errors = errors_to_info(&outcome.errors);
let skills = skills_to_info(&outcome.skills, &outcome.disabled_paths);
@@ -5401,11 +5488,11 @@ impl CodexMessageProcessor {
}
};
let mut remote_sync_error = None;
let auth = self.auth_manager.auth().await;
if force_remote_sync {
let auth = self.auth_manager.auth().await;
match plugins_manager
.sync_plugins_from_remote(&config, auth.as_ref())
.sync_plugins_from_remote(&config, auth.as_ref(), /*additive_only*/ false)
.await
{
Ok(sync_result) => {
@@ -5435,8 +5522,11 @@ impl CodexMessageProcessor {
};
}
let config_for_marketplace_listing = config.clone();
let plugins_manager_for_marketplace_listing = plugins_manager.clone();
let data = match tokio::task::spawn_blocking(move || {
let marketplaces = plugins_manager.list_marketplaces_for_config(&config, &roots)?;
let marketplaces = plugins_manager_for_marketplace_listing
.list_marketplaces_for_config(&config_for_marketplace_listing, &roots)?;
Ok::<Vec<PluginMarketplaceEntry>, MarketplaceError>(
marketplaces
.into_iter()
@@ -5482,12 +5572,34 @@ impl CodexMessageProcessor {
}
};
let featured_plugin_ids = if data
.iter()
.any(|marketplace| marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME)
{
match plugins_manager
.featured_plugin_ids_for_config(&config, auth.as_ref())
.await
{
Ok(featured_plugin_ids) => featured_plugin_ids,
Err(err) => {
warn!(
error = %err,
"plugin/list featured plugin fetch failed; returning empty featured ids"
);
Vec::new()
}
}
} else {
Vec::new()
};
self.outgoing
.send_response(
request_id,
PluginListResponse {
marketplaces: data,
remote_sync_error,
featured_plugin_ids,
},
)
.await;
@@ -5536,6 +5648,17 @@ impl CodexMessageProcessor {
};
let app_summaries =
plugin_app_helpers::load_plugin_app_summaries(&config, &outcome.plugin.apps).await;
let visible_skills = outcome
.plugin
.skills
.iter()
.filter(|skill| {
skill.matches_product_restriction_for_product(
self.thread_manager.session_source().restriction_product(),
)
})
.cloned()
.collect::<Vec<_>>();
let plugin = PluginDetail {
marketplace_name: outcome.marketplace_name,
marketplace_path: outcome.marketplace_path,
@@ -5550,7 +5673,7 @@ impl CodexMessageProcessor {
interface: outcome.plugin.interface.map(plugin_interface_to_info),
},
description: outcome.plugin.description,
skills: plugin_skills_to_info(&outcome.plugin.skills),
skills: plugin_skills_to_info(&visible_skills, &outcome.plugin.disabled_skill_paths),
apps: app_summaries,
mcp_servers: outcome.plugin.mcp_server_names,
};
@@ -5565,8 +5688,30 @@ impl CodexMessageProcessor {
request_id: ConnectionRequestId,
params: SkillsConfigWriteParams,
) {
let SkillsConfigWriteParams { path, enabled } = params;
let edits = vec![ConfigEdit::SetSkillConfig { path, enabled }];
let SkillsConfigWriteParams {
path,
name,
enabled,
} = params;
let edit = match (path, name) {
(Some(path), None) => ConfigEdit::SetSkillConfig {
path: path.into_path_buf(),
enabled,
},
(None, Some(name)) if !name.trim().is_empty() => {
ConfigEdit::SetSkillConfigByName { name, enabled }
}
_ => {
let error = JSONRPCErrorError {
code: INVALID_PARAMS_ERROR_CODE,
message: "skills/config/write requires exactly one of path or name".to_string(),
data: None,
};
self.outgoing.send_error(request_id, error).await;
return;
}
};
let edits = vec![edit];
let result = ConfigEditsBuilder::new(&self.config.codex_home)
.with_edits(edits)
.apply()
@@ -5574,6 +5719,7 @@ impl CodexMessageProcessor {
match result {
Ok(()) => {
self.thread_manager.plugins_manager().clear_cache();
self.thread_manager.skills_manager().clear_cache();
self.outgoing
.send_response(
@@ -5636,6 +5782,22 @@ impl CodexMessageProcessor {
self.config.as_ref().clone()
}
};
self.clear_plugin_related_caches();
let plugin_mcp_servers = load_plugin_mcp_servers(result.installed_path.as_path());
if !plugin_mcp_servers.is_empty() {
if let Err(err) = self.queue_mcp_server_refresh_for_config(&config).await {
warn!(
plugin = result.plugin_id.as_key(),
"failed to queue MCP refresh after plugin install: {err:?}"
);
}
self.start_plugin_mcp_oauth_logins(&config, plugin_mcp_servers)
.await;
}
let plugin_apps = load_plugin_apps(result.installed_path.as_path());
let apps_needing_auth = if plugin_apps.is_empty()
|| !config.features.apps_enabled(Some(&self.auth_manager)).await
@@ -5696,7 +5858,6 @@ impl CodexMessageProcessor {
)
};
self.clear_plugin_related_caches();
self.outgoing
.send_response(
request_id,
@@ -5918,6 +6079,9 @@ impl CodexMessageProcessor {
match turn_id {
Ok(turn_id) => {
self.outgoing
.record_request_turn_id(&request_id, &turn_id)
.await;
let turn = Turn {
id: turn_id.clone(),
items: vec![],
@@ -5970,6 +6134,9 @@ impl CodexMessageProcessor {
.await;
return;
}
self.outgoing
.record_request_turn_id(&request_id, &params.expected_turn_id)
.await;
if let Err(error) = Self::validate_v2_input_limit(&params.input) {
self.outgoing.send_error(request_id, error).await;
return;
@@ -5990,24 +6157,57 @@ impl CodexMessageProcessor {
self.outgoing.send_response(request_id, response).await;
}
Err(err) => {
let (code, message) = match err {
let (code, message, data) = match err {
SteerInputError::NoActiveTurn(_) => (
INVALID_REQUEST_ERROR_CODE,
"no active turn to steer".to_string(),
None,
),
SteerInputError::ExpectedTurnMismatch { expected, actual } => (
INVALID_REQUEST_ERROR_CODE,
format!("expected active turn id `{expected}` but found `{actual}`"),
None,
),
SteerInputError::ActiveTurnNotSteerable { turn_kind } => {
let message = match turn_kind {
codex_protocol::protocol::NonSteerableTurnKind::Review => {
"cannot steer a review turn".to_string()
}
codex_protocol::protocol::NonSteerableTurnKind::Compact => {
"cannot steer a compact turn".to_string()
}
};
let error = TurnError {
message: message.clone(),
codex_error_info: Some(
AppServerCodexErrorInfo::ActiveTurnNotSteerable {
turn_kind: turn_kind.into(),
},
),
additional_details: None,
};
let data = match serde_json::to_value(error) {
Ok(data) => Some(data),
Err(error) => {
tracing::error!(
?error,
"failed to serialize active-turn-not-steerable turn error"
);
None
}
};
(INVALID_REQUEST_ERROR_CODE, message, data)
}
SteerInputError::EmptyInput => (
INVALID_REQUEST_ERROR_CODE,
"input must not be empty".to_string(),
None,
),
};
let error = JSONRPCErrorError {
code,
message,
data: None,
data,
};
self.outgoing.send_error(request_id, error).await;
}
@@ -6309,7 +6509,7 @@ impl CodexMessageProcessor {
} = self
.thread_manager
.fork_thread(
usize::MAX,
ForkSnapshot::FullHistory,
config,
rollout_path,
/*persist_extended_history*/ false,
@@ -6450,7 +6650,10 @@ impl CodexMessageProcessor {
request_id: ConnectionRequestId,
params: TurnInterruptParams,
) {
let TurnInterruptParams { thread_id, .. } = params;
let TurnInterruptParams { thread_id, turn_id } = params;
self.outgoing
.record_request_turn_id(&request_id, &turn_id)
.await;
let (thread_uuid, thread) = match self.load_thread(&thread_id).await {
Ok(v) => v,
@@ -7490,7 +7693,10 @@ fn skills_to_info(
.collect()
}
fn plugin_skills_to_info(skills: &[codex_core::skills::SkillMetadata]) -> Vec<SkillSummary> {
fn plugin_skills_to_info(
skills: &[codex_core::skills::SkillMetadata],
disabled_skill_paths: &std::collections::HashSet<PathBuf>,
) -> Vec<SkillSummary> {
skills
.iter()
.map(|skill| SkillSummary {
@@ -7508,6 +7714,7 @@ fn plugin_skills_to_info(skills: &[codex_core::skills::SkillMetadata]) -> Vec<Sk
}
}),
path: skill.path_to_skills_md.clone(),
enabled: !disabled_skill_paths.contains(&skill.path_to_skills_md),
})
.collect()
}
@@ -8096,6 +8303,7 @@ fn with_thread_spawn_agent_metadata(
codex_protocol::protocol::SubAgentSource::ThreadSpawn {
parent_thread_id,
depth,
agent_path,
agent_nickname: existing_agent_nickname,
agent_role: existing_agent_role,
},
@@ -8103,6 +8311,7 @@ fn with_thread_spawn_agent_metadata(
codex_protocol::protocol::SubAgentSource::ThreadSpawn {
parent_thread_id,
depth,
agent_path,
agent_nickname: agent_nickname.or(existing_agent_nickname),
agent_role: agent_role.or(existing_agent_role),
},
@@ -8649,6 +8858,7 @@ mod tests {
source: SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
parent_thread_id,
depth: 1,
agent_path: None,
agent_nickname: None,
agent_role: None,
}),
@@ -8741,6 +8951,7 @@ mod tests {
serde_json::to_string(&SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
parent_thread_id: ThreadId::from_string("ad7f0408-99b8-4f6e-a46f-bd0eec433370")?,
depth: 1,
agent_path: None,
agent_nickname: None,
agent_role: None,
}))?;

View File

@@ -26,9 +26,47 @@ pub(super) async fn load_plugin_app_summaries(
}
};
connectors::connectors_for_plugin_apps(connectors, plugin_apps)
let plugin_connectors = connectors::connectors_for_plugin_apps(connectors, plugin_apps);
let accessible_connectors =
match connectors::list_accessible_connectors_from_mcp_tools_with_options_and_status(
config, /*force_refetch*/ false,
)
.await
{
Ok(status) if status.codex_apps_ready => status.connectors,
Ok(_) => {
return plugin_connectors
.into_iter()
.map(AppSummary::from)
.collect();
}
Err(err) => {
warn!("failed to load app auth state for plugin/read: {err:#}");
return plugin_connectors
.into_iter()
.map(AppSummary::from)
.collect();
}
};
let accessible_ids = accessible_connectors
.iter()
.map(|connector| connector.id.as_str())
.collect::<HashSet<_>>();
plugin_connectors
.into_iter()
.map(AppSummary::from)
.map(|connector| {
let needs_auth = !accessible_ids.contains(connector.id.as_str());
AppSummary {
id: connector.id,
name: connector.name,
description: connector.description,
install_url: connector.install_url,
needs_auth,
}
})
.collect()
}
@@ -58,7 +96,13 @@ pub(super) fn plugin_apps_needing_auth(
&& !accessible_ids.contains(connector.id.as_str())
})
.cloned()
.map(AppSummary::from)
.map(|connector| AppSummary {
id: connector.id,
name: connector.name,
description: connector.description,
install_url: connector.install_url,
needs_auth: true,
})
.collect()
}

View File

@@ -0,0 +1,95 @@
use std::collections::HashMap;
use std::sync::Arc;
use codex_app_server_protocol::McpServerOauthLoginCompletedNotification;
use codex_app_server_protocol::ServerNotification;
use codex_core::config::Config;
use codex_core::config::types::McpServerConfig;
use codex_core::mcp::auth::McpOAuthLoginSupport;
use codex_core::mcp::auth::oauth_login_support;
use codex_core::mcp::auth::resolve_oauth_scopes;
use codex_core::mcp::auth::should_retry_without_scopes;
use codex_rmcp_client::perform_oauth_login;
use tracing::warn;
use super::CodexMessageProcessor;
impl CodexMessageProcessor {
pub(super) async fn start_plugin_mcp_oauth_logins(
&self,
config: &Config,
plugin_mcp_servers: HashMap<String, McpServerConfig>,
) {
for (name, server) in plugin_mcp_servers {
let oauth_config = match oauth_login_support(&server.transport).await {
McpOAuthLoginSupport::Supported(config) => config,
McpOAuthLoginSupport::Unsupported => continue,
McpOAuthLoginSupport::Unknown(err) => {
warn!(
"MCP server may or may not require login for plugin install {name}: {err}"
);
continue;
}
};
let resolved_scopes = resolve_oauth_scopes(
/*explicit_scopes*/ None,
server.scopes.clone(),
oauth_config.discovered_scopes.clone(),
);
let store_mode = config.mcp_oauth_credentials_store_mode;
let callback_port = config.mcp_oauth_callback_port;
let callback_url = config.mcp_oauth_callback_url.clone();
let outgoing = Arc::clone(&self.outgoing);
let notification_name = name.clone();
tokio::spawn(async move {
let first_attempt = perform_oauth_login(
&name,
&oauth_config.url,
store_mode,
oauth_config.http_headers.clone(),
oauth_config.env_http_headers.clone(),
&resolved_scopes.scopes,
server.oauth_resource.as_deref(),
callback_port,
callback_url.as_deref(),
)
.await;
let final_result = match first_attempt {
Err(err) if should_retry_without_scopes(&resolved_scopes, &err) => {
perform_oauth_login(
&name,
&oauth_config.url,
store_mode,
oauth_config.http_headers,
oauth_config.env_http_headers,
&[],
server.oauth_resource.as_deref(),
callback_port,
callback_url.as_deref(),
)
.await
}
result => result,
};
let (success, error) = match final_result {
Ok(()) => (true, None),
Err(err) => (false, Some(err.to_string())),
};
let notification = ServerNotification::McpServerOauthLoginCompleted(
McpServerOauthLoginCompletedNotification {
name: notification_name,
success,
error,
},
);
outgoing.send_server_notification(notification).await;
});
}
}
}

View File

@@ -733,6 +733,7 @@ mod tests {
env: HashMap::new(),
network: None,
expiration: ExecExpiration::DefaultTimeout,
capture_policy: codex_core::exec::ExecCapturePolicy::ShellTool,
sandbox: SandboxType::WindowsRestrictedToken,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
@@ -845,6 +846,7 @@ mod tests {
env: HashMap::new(),
network: None,
expiration: ExecExpiration::Cancellation(CancellationToken::new()),
capture_policy: codex_core::exec::ExecCapturePolicy::ShellTool,
sandbox: SandboxType::None,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,

View File

@@ -133,6 +133,7 @@ mod tests {
let spawn = CoreSessionSource::SubAgent(CoreSubAgentSource::ThreadSpawn {
parent_thread_id,
depth: 1,
agent_path: None,
agent_nickname: None,
agent_role: None,
});

View File

@@ -18,11 +18,11 @@ use codex_app_server_protocol::FsRemoveResponse;
use codex_app_server_protocol::FsWriteFileParams;
use codex_app_server_protocol::FsWriteFileResponse;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_environment::CopyOptions;
use codex_environment::CreateDirectoryOptions;
use codex_environment::Environment;
use codex_environment::ExecutorFileSystem;
use codex_environment::RemoveOptions;
use codex_exec_server::CopyOptions;
use codex_exec_server::CreateDirectoryOptions;
use codex_exec_server::Environment;
use codex_exec_server::ExecutorFileSystem;
use codex_exec_server::RemoveOptions;
use std::io;
use std::sync::Arc;
@@ -34,7 +34,7 @@ pub(crate) struct FsApi {
impl Default for FsApi {
fn default() -> Self {
Self {
file_system: Arc::new(Environment.get_filesystem()),
file_system: Environment::default().get_filesystem(),
}
}
}

View File

@@ -5,6 +5,7 @@ use std::sync::Mutex;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use codex_app_server_protocol::FuzzyFileSearchMatchType;
use codex_app_server_protocol::FuzzyFileSearchResult;
use codex_app_server_protocol::FuzzyFileSearchSessionCompletedNotification;
use codex_app_server_protocol::FuzzyFileSearchSessionUpdatedNotification;
@@ -60,6 +61,10 @@ pub(crate) async fn run_fuzzy_file_search(
FuzzyFileSearchResult {
root: m.root.to_string_lossy().to_string(),
path: m.path.to_string_lossy().to_string(),
match_type: match m.match_type {
file_search::MatchType::File => FuzzyFileSearchMatchType::File,
file_search::MatchType::Directory => FuzzyFileSearchMatchType::Directory,
},
file_name: file_name.to_string_lossy().to_string(),
score: m.score,
indices: m.indices,
@@ -231,6 +236,10 @@ fn collect_files(snapshot: &file_search::FileSearchSnapshot) -> Vec<FuzzyFileSea
FuzzyFileSearchResult {
root: m.root.to_string_lossy().to_string(),
path: m.path.to_string_lossy().to_string(),
match_type: match m.match_type {
file_search::MatchType::File => FuzzyFileSearchMatchType::File,
file_search::MatchType::Directory => FuzzyFileSearchMatchType::Directory,
},
file_name: file_name.to_string_lossy().to_string(),
score: m.score,
indices: m.indices.clone(),

View File

@@ -336,6 +336,7 @@ pub async fn run_main(
loader_overrides,
default_analytics_enabled,
AppServerTransport::Stdio,
SessionSource::VSCode,
)
.await
}
@@ -346,6 +347,7 @@ pub async fn run_main_with_transport(
loader_overrides: LoaderOverrides,
default_analytics_enabled: bool,
transport: AppServerTransport,
session_source: SessionSource,
) -> IoResult<()> {
let (transport_event_tx, mut transport_event_rx) =
mpsc::channel::<TransportEvent>(CHANNEL_CAPACITY);
@@ -477,7 +479,7 @@ pub async fn run_main_with_transport(
range: None,
});
}
if let Some(warning) = codex_core::config::missing_system_bwrap_warning() {
if let Some(warning) = codex_core::config::system_bwrap_warning() {
config_warnings.push(ConfigWarningNotification {
summary: warning,
details: None,
@@ -621,7 +623,7 @@ pub async fn run_main_with_transport(
feedback: feedback.clone(),
log_db,
config_warnings,
session_source: SessionSource::VSCode,
session_source,
enable_codex_api_key_env: false,
});
let mut thread_created_rx = processor.thread_created_receiver();

View File

@@ -4,6 +4,7 @@ use codex_app_server::run_main_with_transport;
use codex_arg0::Arg0DispatchPaths;
use codex_arg0::arg0_dispatch_or_else;
use codex_core::config_loader::LoaderOverrides;
use codex_protocol::protocol::SessionSource;
use codex_utils_cli::CliConfigOverrides;
use std::path::PathBuf;
@@ -21,6 +22,15 @@ struct AppServerArgs {
default_value = AppServerTransport::DEFAULT_LISTEN_URL
)]
listen: AppServerTransport,
/// Session source used to derive product restrictions and metadata.
#[arg(
long = "session-source",
value_name = "SOURCE",
default_value = "vscode",
value_parser = SessionSource::from_startup_arg
)]
session_source: SessionSource,
}
fn main() -> anyhow::Result<()> {
@@ -32,6 +42,7 @@ fn main() -> anyhow::Result<()> {
..Default::default()
};
let transport = args.listen;
let session_source = args.session_source;
run_main_with_transport(
arg0_paths,
@@ -39,6 +50,7 @@ fn main() -> anyhow::Result<()> {
loader_overrides,
/*default_analytics_enabled*/ false,
transport,
session_source,
)
.await?;
Ok(())

View File

@@ -50,20 +50,22 @@ use codex_arg0::Arg0DispatchPaths;
use codex_core::AnalyticsEventsClient;
use codex_core::AuthManager;
use codex_core::ThreadManager;
use codex_core::auth::ExternalAuthRefreshContext;
use codex_core::auth::ExternalAuthRefreshReason;
use codex_core::auth::ExternalAuthRefresher;
use codex_core::auth::ExternalAuthTokens;
use codex_core::config::Config;
use codex_core::config_loader::CloudRequirementsLoader;
use codex_core::config_loader::LoaderOverrides;
use codex_core::default_client::DEFAULT_ORIGINATOR;
use codex_core::default_client::SetOriginatorError;
use codex_core::default_client::USER_AGENT_SUFFIX;
use codex_core::default_client::get_codex_user_agent;
use codex_core::default_client::set_default_client_residency_requirement;
use codex_core::default_client::set_default_originator;
use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig;
use codex_features::Feature;
use codex_feedback::CodexFeedback;
use codex_login::auth::ExternalAuthRefreshContext;
use codex_login::auth::ExternalAuthRefreshReason;
use codex_login::auth::ExternalAuthRefresher;
use codex_login::auth::ExternalAuthTokens;
use codex_protocol::ThreadId;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::W3cTraceContext;
@@ -77,6 +79,7 @@ use toml::Value as TomlValue;
use tracing::Instrument;
const EXTERNAL_AUTH_REFRESH_TIMEOUT: Duration = Duration::from_secs(10);
const TUI_APP_SERVER_CLIENT_NAME: &str = "codex-tui";
#[derive(Clone)]
struct ExternalAuthRefreshBridge {
@@ -212,7 +215,7 @@ impl MessageProcessor {
CollaborationModesConfig {
default_mode_request_user_input: config
.features
.enabled(codex_core::features::Feature::DefaultModeRequestUserInput),
.enabled(Feature::DefaultModeRequestUserInput),
},
));
(auth_manager, thread_manager)
@@ -228,10 +231,7 @@ impl MessageProcessor {
thread_manager
.plugins_manager()
.set_analytics_events_client(analytics_events_client.clone());
// TODO(xl): Move into PluginManager once this no longer depends on config feature gating.
thread_manager
.plugins_manager()
.maybe_start_curated_repo_sync_for_config(&config);
let cloud_requirements = Arc::new(RwLock::new(cloud_requirements));
let codex_message_processor = CodexMessageProcessor::new(CodexMessageProcessorArgs {
auth_manager: auth_manager.clone(),
@@ -244,6 +244,11 @@ impl MessageProcessor {
feedback,
log_db,
});
// Keep plugin startup warmups aligned at app-server startup.
// TODO(xl): Move into PluginManager once this no longer depends on config feature gating.
thread_manager
.plugins_manager()
.maybe_start_plugin_startup_tasks_for_config(&config, auth_manager.clone());
let config_api = ConfigApi::new(
config.codex_home.clone(),
cli_overrides,
@@ -548,7 +553,14 @@ impl MessageProcessor {
} = params.client_info;
session.app_server_client_name = Some(name.clone());
session.client_version = Some(version.clone());
if let Err(error) = set_default_originator(name.clone()) {
let originator = if name == TUI_APP_SERVER_CLIENT_NAME {
// TODO: Remove this temporary workaround once app-server clients no longer
// need to retain the legacy TUI `codex_cli_rs` originator behavior.
DEFAULT_ORIGINATOR.to_string()
} else {
name.clone()
};
if let Err(error) = set_default_originator(originator) {
match error {
SetOriginatorError::InvalidHeaderValue => {
let error = JSONRPCErrorError {
@@ -787,7 +799,7 @@ impl MessageProcessor {
Ok(response) => {
self.codex_message_processor.clear_plugin_related_caches();
self.codex_message_processor
.maybe_start_curated_repo_sync_for_latest_config()
.maybe_start_plugin_startup_tasks_for_latest_config()
.await;
self.outgoing.send_response(request_id, response).await;
}
@@ -804,7 +816,7 @@ impl MessageProcessor {
Ok(response) => {
self.codex_message_processor.clear_plugin_related_caches();
self.codex_message_processor
.maybe_start_curated_repo_sync_for_latest_config()
.maybe_start_plugin_startup_tasks_for_latest_config()
.await;
self.outgoing.send_response(request_id, response).await;
}

View File

@@ -580,7 +580,7 @@ async fn turn_start_jsonrpc_span_parents_core_turn_spans() -> Result<()> {
parent_span_id: remote_parent_span_id,
context: remote_trace,
} = RemoteTrace::new("00000000000000000000000000000077", "0000000000000088");
let _: TurnStartResponse = harness
let turn_start_response: TurnStartResponse = harness
.request(
ClientRequest::TurnStart {
request_id: RequestId::Integer(3),
@@ -628,6 +628,10 @@ async fn turn_start_jsonrpc_span_parents_core_turn_spans() -> Result<()> {
assert_eq!(server_request_span.parent_span_id, remote_parent_span_id);
assert!(server_request_span.parent_span_is_remote);
assert_eq!(server_request_span.span_context.trace_id(), remote_trace_id);
assert_eq!(
span_attr(server_request_span, "turn.id"),
Some(turn_start_response.turn.id.as_str())
);
assert_span_descends_from(&spans, core_turn_span, server_request_span);
harness.shutdown().await;

View File

@@ -75,6 +75,10 @@ impl RequestContext {
pub(crate) fn span(&self) -> Span {
self.span.clone()
}
fn record_turn_id(&self, turn_id: &str) {
self.span.record("turn.id", turn_id);
}
}
#[derive(Debug, Clone)]
@@ -217,6 +221,17 @@ impl OutgoingMessageSender {
.and_then(RequestContext::request_trace)
}
pub(crate) async fn record_request_turn_id(
&self,
request_id: &ConnectionRequestId,
turn_id: &str,
) {
let request_contexts = self.request_contexts.lock().await;
if let Some(request_context) = request_contexts.get(request_id) {
request_context.record_turn_id(turn_id);
}
}
async fn take_request_context(
&self,
request_id: &ConnectionRequestId,

View File

@@ -13,6 +13,7 @@ base64 = { workspace = true }
chrono = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-core = { workspace = true }
codex-features = { workspace = true }
codex-protocol = { workspace = true }
codex-utils-cargo-bin = { workspace = true }
serde = { workspace = true }

View File

@@ -1,5 +1,5 @@
use codex_core::features::FEATURES;
use codex_core::features::Feature;
use codex_features::FEATURES;
use codex_features::Feature;
use std::collections::BTreeMap;
use std::path::Path;

View File

@@ -68,6 +68,7 @@ use codex_app_server_protocol::ThreadRealtimeStopParams;
use codex_app_server_protocol::ThreadResumeParams;
use codex_app_server_protocol::ThreadRollbackParams;
use codex_app_server_protocol::ThreadSetNameParams;
use codex_app_server_protocol::ThreadShellCommandParams;
use codex_app_server_protocol::ThreadStartParams;
use codex_app_server_protocol::ThreadUnarchiveParams;
use codex_app_server_protocol::ThreadUnsubscribeParams;
@@ -95,7 +96,11 @@ pub const DEFAULT_CLIENT_NAME: &str = "codex-app-server-tests";
impl McpProcess {
pub async fn new(codex_home: &Path) -> anyhow::Result<Self> {
Self::new_with_env(codex_home, &[]).await
Self::new_with_env_and_args(codex_home, &[], &[]).await
}
pub async fn new_with_args(codex_home: &Path, args: &[&str]) -> anyhow::Result<Self> {
Self::new_with_env_and_args(codex_home, &[], args).await
}
/// Creates a new MCP process, allowing tests to override or remove
@@ -106,6 +111,14 @@ impl McpProcess {
pub async fn new_with_env(
codex_home: &Path,
env_overrides: &[(&str, Option<&str>)],
) -> anyhow::Result<Self> {
Self::new_with_env_and_args(codex_home, env_overrides, &[]).await
}
async fn new_with_env_and_args(
codex_home: &Path,
env_overrides: &[(&str, Option<&str>)],
args: &[&str],
) -> anyhow::Result<Self> {
let program = codex_utils_cargo_bin::cargo_bin("codex-app-server")
.context("should find binary for codex-app-server")?;
@@ -118,6 +131,7 @@ impl McpProcess {
cmd.env("CODEX_HOME", codex_home);
cmd.env("RUST_LOG", "info");
cmd.env_remove(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR);
cmd.args(args);
for (k, v) in env_overrides {
match v {
@@ -386,6 +400,15 @@ impl McpProcess {
self.send_request("thread/compact/start", params).await
}
/// Send a `thread/shellCommand` JSON-RPC request.
pub async fn send_thread_shell_command_request(
&mut self,
params: ThreadShellCommandParams,
) -> anyhow::Result<i64> {
let params = Some(serde_json::to_value(params)?);
self.send_request("thread/shellCommand", params).await
}
/// Send a `thread/rollback` JSON-RPC request.
pub async fn send_thread_rollback_request(
&mut self,

View File

@@ -79,6 +79,7 @@ pub fn create_fake_rollout_with_source(
originator: "codex".to_string(),
cli_version: "0.0.0".to_string(),
source,
agent_path: None,
agent_nickname: None,
agent_role: None,
model_provider: model_provider.map(str::to_string),
@@ -161,6 +162,7 @@ pub fn create_fake_rollout_with_text_elements(
originator: "codex".to_string(),
cli_version: "0.0.0".to_string(),
source: SessionSource::Cli,
agent_path: None,
agent_nickname: None,
agent_role: None,
model_provider: model_provider.map(str::to_string),

View File

@@ -257,6 +257,7 @@ async fn test_fuzzy_file_search_sorts_and_includes_indices() -> Result<()> {
{
"root": root_path.clone(),
"path": "abexy",
"match_type": "file",
"file_name": "abexy",
"score": 84,
"indices": [0, 1, 2],
@@ -264,6 +265,7 @@ async fn test_fuzzy_file_search_sorts_and_includes_indices() -> Result<()> {
{
"root": root_path.clone(),
"path": sub_abce_rel,
"match_type": "file",
"file_name": "abce",
"score": expected_score,
"indices": [4, 5, 7],
@@ -271,6 +273,7 @@ async fn test_fuzzy_file_search_sorts_and_includes_indices() -> Result<()> {
{
"root": root_path.clone(),
"path": "abcde",
"match_type": "file",
"file_name": "abcde",
"score": 71,
"indices": [0, 1, 4],

View File

@@ -10,8 +10,8 @@ use codex_app_server_protocol::ExperimentalFeatureStage;
use codex_app_server_protocol::JSONRPCResponse;
use codex_app_server_protocol::RequestId;
use codex_core::config::ConfigBuilder;
use codex_core::features::FEATURES;
use codex_core::features::Stage;
use codex_features::FEATURES;
use codex_features::Stage;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
use tokio::time::timeout;

View File

@@ -38,6 +38,7 @@ mod thread_name_websocket;
mod thread_read;
mod thread_resume;
mod thread_rollback;
mod thread_shell_command;
mod thread_start;
mod thread_status;
mod thread_unarchive;

View File

@@ -18,8 +18,8 @@ use codex_app_server_protocol::TurnStartParams;
use codex_app_server_protocol::TurnStartResponse;
use codex_app_server_protocol::TurnStatus;
use codex_app_server_protocol::UserInput as V2UserInput;
use codex_core::features::FEATURES;
use codex_core::features::Feature;
use codex_features::FEATURES;
use codex_features::Feature;
use codex_protocol::config_types::CollaborationMode;
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::Settings;

Some files were not shown because too many files have changed in this diff Show More