Compare commits

..

1 Commits

Author SHA1 Message Date
Eric Horacek
5620d7431a Add optional tool context to request_user_input
Add optional tool/arguments fields to request_user_input events and app-server tool/requestUserInput params, then wire MCP approval flows to populate and forward them.
2026-02-13 15:08:12 -08:00
387 changed files with 5387 additions and 27647 deletions

View File

@@ -15,8 +15,8 @@ common --experimental_platform_in_output_dir
common --noenable_runfiles
common --enable_platform_specific_config
common:linux --host_platform=//:local_linux
common:windows --host_platform=//:local_windows
# TODO(zbarsky): We need to untangle these libc constraints to get linux remote builds working.
common:linux --host_platform=//:local
common --@rules_cc//cc/toolchains/args/archiver_flags:use_libtool_on_macos=False
common --@toolchains_llvm_bootstrapped//config:experimental_stub_libgcc_s
@@ -28,14 +28,7 @@ common:windows --@rules_rust//rust/settings:experimental_use_sh_toolchain_for_bo
common --incompatible_strict_action_env
# Not ideal, but We need to allow dotslash to be found
common:linux --test_env=PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
common:macos --test_env=PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
# Pass through some env vars Windows needs to use powershell?
common:windows --test_env=PATH
common:windows --test_env=SYSTEMROOT
common:windows --test_env=COMSPEC
common:windows --test_env=WINDIR
common --test_env=PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
common --test_output=errors
common --bes_results_url=https://app.buildbuddy.io/invocation/

View File

@@ -77,12 +77,6 @@ for arg in "\$@"; do
fi
continue
;;
-Wp,-U_FORTIFY_SOURCE)
# aws-lc-sys emits this GCC preprocessor forwarding form in debug
# builds, but zig cc expects the define flag directly.
args+=("-U_FORTIFY_SOURCE")
continue
;;
esac
args+=("\${arg}")
done
@@ -102,23 +96,15 @@ for arg in "\$@"; do
fi
case "\${arg}" in
--target)
# Drop explicit --target and its value: we always pass zig's -target below.
skip_next=1
continue
;;
--target=*|-target=*|-target)
# Zig expects -target and rejects Rust triples like *-unknown-linux-musl.
if [[ "\${arg}" == "-target" ]]; then
skip_next=1
fi
continue
;;
-Wp,-U_FORTIFY_SOURCE)
# aws-lc-sys emits this GCC forwarding form in debug builds; zig c++
# expects the define flag directly.
args+=("-U_FORTIFY_SOURCE")
continue
;;
esac
args+=("\${arg}")
done

View File

@@ -1,13 +1,6 @@
common --remote_download_minimal
common --nobuild_runfile_links
common --keep_going
common --verbose_failures
# Disable disk cache since we have remote one and aren't using persistent workers.
common --disk_cache=
# Rearrange caches on Windows so they're on the same volume as the checkout.
common:windows --repo_contents_cache=D:/a/.cache/bazel-repo-contents-cache
common:windows --repository_cache=D:/a/.cache/bazel-repo-cache
# We prefer to run the build actions entirely remotely so we can dial up the concurrency.
# We have platform-specific tests, so we want to execute the tests on all platforms using the strongest sandboxing available on each platform.
@@ -23,5 +16,5 @@ common:macos --config=remote
common:macos --strategy=remote
common:macos --strategy=TestRunner=darwin-sandbox,local
# On windows we cannot cross-build the tests but run them locally due to what appears to be a Bazel bug
# (windows vs unix path confusion)
common:windows --strategy=TestRunner=local

View File

@@ -45,6 +45,15 @@ jobs:
echo "✅ Tag and Cargo.toml agree (${tag_ver})"
echo "::endgroup::"
- name: Verify config schema fixture
shell: bash
working-directory: codex-rs
run: |
set -euo pipefail
echo "If this fails, run: just write-config-schema to overwrite fixture with intentional changes."
cargo run -p codex-core --bin codex-write-config-schema
git diff --exit-code core/config.schema.json
build:
needs: tag-check
name: Build - ${{ matrix.runner }} - ${{ matrix.target }}

View File

@@ -15,14 +15,13 @@ In the codex-rs folder where the rust code lives:
- When writing tests, prefer comparing the equality of entire objects over fields one by one.
- When making a change that adds or changes an API, ensure that the documentation in the `docs/` folder is up to date if applicable.
- If you change `ConfigToml` or nested config types, run `just write-config-schema` to update `codex-rs/core/config.schema.json`.
- Do not create small helper methods that are referenced only once.
Run `just fmt` (in `codex-rs` directory) automatically after you have finished making Rust code changes; do not ask for approval to run it. Additionally, run the tests:
1. Run the test for the specific project that was changed. For example, if changes were made in `codex-rs/tui`, run `cargo test -p codex-tui`.
2. Once those pass, if any changes were made in common, core, or protocol, run the complete test suite with `cargo test --all-features`. project-specific or individual tests can be run without asking the user, but do ask the user before running the complete test suite.
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`.
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.
## TUI style conventions
@@ -129,11 +128,15 @@ These guidelines apply to app-server protocol work in `codex-rs`, especially:
`*Params` for request payloads, `*Response` for responses, and `*Notification` for notifications.
- Expose RPC methods as `<resource>/<method>` and keep `<resource>` singular (for example, `thread/read`, `app/list`).
- Always expose fields as camelCase on the wire with `#[serde(rename_all = "camelCase")]` unless a tagged union or explicit compatibility requirement needs a targeted rename.
- Exception: config RPC payloads are expected to use snake_case to mirror config.toml keys (see the config read/write/list APIs in `app-server-protocol/src/protocol/v2.rs`).
- Always set `#[ts(export_to = "v2/")]` on v2 request/response/notification types so generated TypeScript lands in the correct namespace.
- Never use `#[serde(skip_serializing_if = "Option::is_none")]` for v2 API payload fields.
Exception: client->server requests that intentionally have no params may use:
`params: #[ts(type = "undefined")] #[serde(skip_serializing_if = "Option::is_none")] Option<()>`.
- For client->server JSON-RPC request payloads (`*Params`) only, every optional field must be annotated with `#[ts(optional = nullable)]`. Do not use `#[ts(optional = nullable)]` outside client->server request payloads (`*Params`).
- For client->server JSON-RPC request payloads only, and you want to express a boolean field where omission means `false`, use `#[serde(default, skip_serializing_if = "std::ops::Not::not")] pub field: bool` over `Option<bool>`.
- For new list methods, implement cursor pagination by default:
request fields `pub cursor: Option<String>` and `pub limit: Option<u32>`,
response fields `pub data: Vec<...>` and `pub next_cursor: Option<String>`.
- Keep Rust and TS wire renames aligned. If a field or variant uses `#[serde(rename = "...")]`, add matching `#[ts(rename = "...")]`.
- For discriminated unions, use explicit tagging in both serializers:
`#[serde(tag = "type", ...)]` and `#[ts(tag = "type", ...)]`.
@@ -142,15 +145,6 @@ These guidelines apply to app-server protocol work in `codex-rs`, especially:
- For experimental API surface area:
use `#[experimental("method/or/field")]`, derive `ExperimentalApi` when field-level gating is needed, and use `inspect_params: true` in `common.rs` when only some fields of a method are experimental.
### Client->server request payloads (`*Params`)
- Every optional field must be annotated with `#[ts(optional = nullable)]`. Do not use `#[ts(optional = nullable)]` outside client->server request payloads (`*Params`).
- Optional collection fields (for example `Vec`, `HashMap`) must use `Option<...>` + `#[ts(optional = nullable)]`. Do not use `#[serde(default)]` to model optional collections, and do not use `skip_serializing_if` on v2 payload fields.
- When you want omission to mean `false` for boolean fields, use `#[serde(default, skip_serializing_if = "std::ops::Not::not")] pub field: bool` over `Option<bool>`.
- For new list methods, implement cursor pagination by default:
request fields `pub cursor: Option<String>` and `pub limit: Option<u32>`,
response fields `pub data: Vec<...>` and `pub next_cursor: Option<String>`.
### Development Workflow
- Update docs/examples when API behavior changes (at minimum `app-server/README.md`).

View File

@@ -6,21 +6,13 @@ xcode_config(name = "disable_xcode")
# TODO(zbarsky): Upstream a better libc constraint into rules_rust.
# We only enable this on linux though for sanity, and because it breaks remote execution.
platform(
name = "local_linux",
name = "local",
constraint_values = [
# We mark the local platform as glibc-compatible because musl-built rust cannot dlopen proc macros.
"@toolchains_llvm_bootstrapped//constraints/libc:gnu.2.28",
],
parents = ["@platforms//host"],
)
platform(
name = "local_windows",
constraint_values = [
# We just need to pick one of the ABIs. Do the same one we target.
"@rules_rs//rs/experimental/platforms/constraints:windows_gnullvm",
parents = [
"@platforms//host",
],
parents = ["@platforms//host"],
)
alias(

View File

@@ -1,14 +1,13 @@
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "toolchains_llvm_bootstrapped", version = "0.5.3")
single_version_override(
bazel_dep(name = "toolchains_llvm_bootstrapped", version = "0.3.1")
archive_override(
module_name = "toolchains_llvm_bootstrapped",
patch_strip = 1,
patches = [
"//patches:toolchains_llvm_bootstrapped_resource_dir.patch",
],
integrity = "sha256-4/2h4tYSUSptxFVI9G50yJxWGOwHSeTeOGBlaLQBV8g=",
strip_prefix = "toolchains_llvm_bootstrapped-d20baf67e04d8e2887e3779022890d1dc5e6b948",
urls = ["https://github.com/cerisier/toolchains_llvm_bootstrapped/archive/d20baf67e04d8e2887e3779022890d1dc5e6b948.tar.gz"],
)
osx = use_extension("@toolchains_llvm_bootstrapped//extensions:osx.bzl", "osx")
osx = use_extension("@toolchains_llvm_bootstrapped//toolchain/extension:osx.bzl", "osx")
osx.framework(name = "ApplicationServices")
osx.framework(name = "AppKit")
osx.framework(name = "ColorSync")
@@ -17,7 +16,6 @@ osx.framework(name = "CoreGraphics")
osx.framework(name = "CoreServices")
osx.framework(name = "CoreText")
osx.framework(name = "CFNetwork")
osx.framework(name = "FontServices")
osx.framework(name = "Foundation")
osx.framework(name = "ImageIO")
osx.framework(name = "Kernel")
@@ -33,92 +31,48 @@ register_toolchains(
bazel_dep(name = "apple_support", version = "2.1.0")
bazel_dep(name = "rules_cc", version = "0.2.16")
bazel_dep(name = "rules_platform", version = "0.1.0")
bazel_dep(name = "rules_rs", version = "0.0.23")
# Special toolchains branch
archive_override(
module_name = "rules_rs",
integrity = "sha256-YbDRjZos4UmfIPY98znK1BgBWRQ1/ui3CtL6RqxE30I=",
strip_prefix = "rules_rs-6cf3d940fdc48baf3ebd6c37daf8e0be8fc73ecb",
url = "https://github.com/dzbarsky/rules_rs/archive/6cf3d940fdc48baf3ebd6c37daf8e0be8fc73ecb.tar.gz",
bazel_dep(name = "rules_rust", version = "0.68.1")
single_version_override(
module_name = "rules_rust",
patch_strip = 1,
patches = [
"//patches:rules_rust.patch",
"//patches:rules_rust_windows_gnu.patch",
"//patches:rules_rust_musl.patch",
],
)
rules_rust = use_extension("@rules_rs//rs/experimental:rules_rust.bzl", "rules_rust")
use_repo(rules_rust, "rules_rust")
RUST_TRIPLES = [
"aarch64-unknown-linux-musl",
"aarch64-apple-darwin",
"aarch64-pc-windows-gnullvm",
"x86_64-unknown-linux-musl",
"x86_64-apple-darwin",
"x86_64-pc-windows-gnullvm",
]
toolchains = use_extension("@rules_rs//rs/experimental/toolchains:module_extension.bzl", "toolchains")
toolchains.toolchain(
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
edition = "2024",
version = "1.93.0",
)
use_repo(
toolchains,
"experimental_rust_toolchains_1_93_0",
"rust_toolchain_artifacts_macos_aarch64_1_93_0",
extra_target_triples = RUST_TRIPLES,
versions = ["1.93.0"],
)
use_repo(rust, "rust_toolchains")
register_toolchains("@experimental_rust_toolchains_1_93_0//:all")
register_toolchains("@rust_toolchains//:all")
bazel_dep(name = "rules_rs", version = "0.0.23")
crate = use_extension("@rules_rs//rs:extensions.bzl", "crate")
crate.from_cargo(
cargo_lock = "//codex-rs:Cargo.lock",
cargo_toml = "//codex-rs:Cargo.toml",
platform_triples = [
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"aarch64-apple-darwin",
"aarch64-pc-windows-gnullvm",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"x86_64-apple-darwin",
"x86_64-pc-windows-gnullvm",
],
)
bazel_dep(name = "zstd", version = "1.5.7")
crate.annotation(
crate = "zstd-sys",
gen_build_script = "off",
deps = ["@zstd"],
platform_triples = RUST_TRIPLES,
)
crate.annotation(
build_script_env = {
"AWS_LC_SYS_NO_JITTER_ENTROPY": "1",
},
crate = "aws-lc-sys",
patch_args = ["-p1"],
patches = [
"//patches:aws-lc-sys_memcmp_check.patch",
],
)
inject_repo(crate, "zstd")
bazel_dep(name = "bzip2", version = "1.0.8.bcr.3")
crate.annotation(
crate = "bzip2-sys",
gen_build_script = "off",
deps = ["@bzip2//:bz2"],
)
inject_repo(crate, "bzip2")
bazel_dep(name = "zlib", version = "1.3.1.bcr.8")
crate.annotation(
crate = "libz-sys",
gen_build_script = "off",
deps = ["@zlib"],
)
inject_repo(crate, "zlib")
# TODO(zbarsky): Enable annotation after fixing windows arm64 builds.
crate.annotation(
crate = "lzma-sys",
gen_build_script = "on",
crate = "nucleo-matcher",
strip_prefix = "matcher",
version = "0.3.1",
)
bazel_dep(name = "openssl", version = "3.5.4.bcr.0")

209
MODULE.bazel.lock generated

File diff suppressed because one or more lines are too long

View File

@@ -15,9 +15,3 @@ target_app = "cli"
content = "This is a test announcement"
version_regex = "^0\\.0\\.0$"
to_date = "2026-05-10"
[[announcements]]
content = "**BREAKING NEWS**: `gpt-5.3-codex` is out! Upgrade to `0.98.0` for a faster, smarter, more steerable agent."
from_date = "2026-02-01"
to_date = "2026-02-16"
version_regex = "^0\\.(?:[0-9]|[1-8][0-9]|9[0-7])\\."

532
codex-rs/Cargo.lock generated
View File

@@ -458,45 +458,6 @@ dependencies = [
"term",
]
[[package]]
name = "asn1-rs"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60"
dependencies = [
"asn1-rs-derive",
"asn1-rs-impl",
"displaydoc",
"nom 7.1.3",
"num-traits",
"rusticata-macros",
"thiserror 2.0.18",
"time",
]
[[package]]
name = "asn1-rs-derive"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"synstructure",
]
[[package]]
name = "asn1-rs-impl"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "assert-json-diff"
version = "2.0.2"
@@ -723,29 +684,6 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "aws-lc-rs"
version = "1.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256"
dependencies = [
"aws-lc-sys",
"untrusted 0.7.1",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
]
[[package]]
name = "axum"
version = "0.8.8"
@@ -754,7 +692,6 @@ checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [
"axum-core",
"bytes",
"form_urlencoded",
"futures-util",
"http 1.4.0",
"http-body",
@@ -770,13 +707,11 @@ dependencies = [
"serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@@ -795,7 +730,6 @@ dependencies = [
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@@ -852,6 +786,24 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "bindgen"
version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
"bitflags 2.10.0",
"cexpr",
"clang-sys",
"itertools 0.13.0",
"proc-macro2",
"quote",
"regex",
"rustc-hash 2.1.1",
"shlex",
"syn 2.0.114",
]
[[package]]
name = "bit-set"
version = "0.5.3"
@@ -922,20 +874,6 @@ dependencies = [
"piper",
]
[[package]]
name = "bm25"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cbd8ffdfb7b4c2ff038726178a780a94f90525ed0ad264c0afaa75dd8c18a64"
dependencies = [
"cached",
"deunicode",
"fxhash",
"rust-stemmers",
"stop-words",
"unicode-segmentation",
]
[[package]]
name = "borsh"
version = "1.6.0"
@@ -1014,39 +952,6 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "cached"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801927ee168e17809ab8901d9f01f700cd7d8d6a6527997fee44e4b0327a253c"
dependencies = [
"ahash",
"cached_proc_macro",
"cached_proc_macro_types",
"hashbrown 0.15.5",
"once_cell",
"thiserror 2.0.18",
"web-time",
]
[[package]]
name = "cached_proc_macro"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9225bdcf4e4a9a4c08bf16607908eb2fbf746828d5e0b5e019726dbf6571f201"
dependencies = [
"darling 0.20.11",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "cached_proc_macro_types"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
[[package]]
name = "cassowary"
version = "0.3.0"
@@ -1089,6 +994,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom 7.1.3",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
@@ -1173,6 +1087,17 @@ dependencies = [
"zeroize",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "4.5.56"
@@ -1272,7 +1197,6 @@ dependencies = [
"pretty_assertions",
"regex-lite",
"reqwest",
"rustls",
"serde",
"serde_json",
"thiserror 2.0.18",
@@ -1281,7 +1205,6 @@ dependencies = [
"tokio-tungstenite",
"tokio-util",
"tracing",
"tungstenite",
"url",
"wiremock",
]
@@ -1296,7 +1219,6 @@ dependencies = [
"axum",
"base64 0.22.1",
"chrono",
"clap",
"codex-app-server-protocol",
"codex-arg0",
"codex-backend-client",
@@ -1311,12 +1233,9 @@ dependencies = [
"codex-protocol",
"codex-rmcp-client",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
"codex-utils-json-to-toml",
"core_test_support",
"futures",
"os_info",
"owo-colors",
"pretty_assertions",
"rmcp",
"serde",
@@ -1326,7 +1245,6 @@ dependencies = [
"tempfile",
"time",
"tokio",
"tokio-tungstenite",
"toml 0.9.11+spec-1.1.0",
"tracing",
"tracing-subscriber",
@@ -1602,7 +1520,6 @@ dependencies = [
"async-channel",
"async-trait",
"base64 0.22.1",
"bm25",
"chardetng",
"chrono",
"clap",
@@ -1617,7 +1534,6 @@ dependencies = [
"codex-file-search",
"codex-git",
"codex-keyring-store",
"codex-network-proxy",
"codex-otel",
"codex-protocol",
"codex-rmcp-client",
@@ -1691,7 +1607,6 @@ dependencies = [
"walkdir",
"which",
"wildmatch",
"windows-sys 0.52.0",
"wiremock",
"zip",
"zstd",
@@ -1955,6 +1870,8 @@ dependencies = [
"anyhow",
"async-trait",
"clap",
"codex-app-server-protocol",
"codex-core",
"codex-utils-absolute-path",
"globset",
"pretty_assertions",
@@ -1964,15 +1881,15 @@ dependencies = [
"rama-net",
"rama-socks5",
"rama-tcp",
"rama-tls-rustls",
"rama-tls-boring",
"rama-unix",
"serde",
"serde_json",
"tempfile",
"thiserror 2.0.18",
"time",
"tokio",
"tracing",
"tracing-subscriber",
"url",
]
@@ -2000,9 +1917,9 @@ version = "0.0.0"
dependencies = [
"chrono",
"codex-api",
"codex-app-server-protocol",
"codex-protocol",
"codex-utils-absolute-path",
"codex-utils-string",
"eventsource-stream",
"http 1.4.0",
"opentelemetry",
@@ -2010,7 +1927,6 @@ dependencies = [
"opentelemetry-otlp",
"opentelemetry-semantic-conventions",
"opentelemetry_sdk",
"os_info",
"pretty_assertions",
"reqwest",
"serde",
@@ -2085,7 +2001,6 @@ dependencies = [
"codex-protocol",
"codex-utils-cargo-bin",
"codex-utils-home-dir",
"codex-utils-pty",
"futures",
"keyring",
"oauth2",
@@ -2329,9 +2244,6 @@ dependencies = [
[[package]]
name = "codex-utils-string"
version = "0.0.0"
dependencies = [
"pretty_assertions",
]
[[package]]
name = "codex-windows-sandbox"
@@ -2382,12 +2294,6 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.4"
@@ -2758,16 +2664,6 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core 0.20.11",
"darling_macro 0.20.11",
]
[[package]]
name = "darling"
version = "0.21.3"
@@ -2788,20 +2684,6 @@ dependencies = [
"darling_macro 0.23.0",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.11.1",
"syn 2.0.114",
]
[[package]]
name = "darling_core"
version = "0.21.3"
@@ -2829,17 +2711,6 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core 0.20.11",
"quote",
"syn 2.0.114",
]
[[package]]
name = "darling_macro"
version = "0.21.3"
@@ -2953,20 +2824,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "der-parser"
version = "10.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
dependencies = [
"asn1-rs",
"displaydoc",
"nom 7.1.3",
"num-bigint",
"num-traits",
"rusticata-macros",
]
[[package]]
name = "deranged"
version = "0.5.5"
@@ -3044,12 +2901,6 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "deunicode"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
[[package]]
name = "diff"
version = "0.1.13"
@@ -3553,7 +3404,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
dependencies = [
"crc32fast",
"libz-sys",
"miniz_oxide",
]
@@ -3657,7 +3507,28 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
"foreign-types-shared 0.1.1",
]
[[package]]
name = "foreign-types"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared 0.3.1",
]
[[package]]
name = "foreign-types-macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
@@ -3666,6 +3537,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@@ -3690,6 +3567,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"
@@ -3883,22 +3770,18 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gif"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "gimli"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "globset"
version = "0.4.18"
@@ -3989,30 +3872,6 @@ dependencies = [
"hashbrown 0.15.5",
]
[[package]]
name = "headers"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb"
dependencies = [
"base64 0.22.1",
"bytes",
"headers-core",
"http 1.4.0",
"httpdate",
"mime",
"sha1",
]
[[package]]
name = "headers-core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
dependencies = [
"http 1.4.0",
]
[[package]]
name = "heck"
version = "0.5.0"
@@ -4540,9 +4399,6 @@ checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
dependencies = [
"bytemuck",
"byteorder-lite",
"color_quant",
"gif",
"image-webp",
"moxcms",
"num-traits",
"png",
@@ -4551,16 +4407,6 @@ dependencies = [
"zune-jpeg 0.5.12",
]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]]
name = "impl-more"
version = "0.1.9"
@@ -4973,6 +4819,16 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "libloading"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link",
]
[[package]]
name = "libm"
version = "0.2.16"
@@ -5001,17 +4857,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "linux-keyutils"
version = "0.2.4"
@@ -5794,15 +5639,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "oid-registry"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
dependencies = [
"asn1-rs",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -5833,7 +5669,7 @@ checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
dependencies = [
"bitflags 2.10.0",
"cfg-if",
"foreign-types",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
@@ -6111,16 +5947,6 @@ dependencies = [
"hmac",
]
[[package]]
name = "pem"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
dependencies = [
"base64 0.22.1",
"serde_core",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
@@ -6622,6 +6448,42 @@ dependencies = [
"nibble_vec",
]
[[package]]
name = "rama-boring"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84f7f862c81618f9aef40bd32e73986321109a24272c79e040377c5ac29491e8"
dependencies = [
"bitflags 2.10.0",
"foreign-types 0.5.0",
"libc",
"openssl-macros",
"rama-boring-sys",
]
[[package]]
name = "rama-boring-sys"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5bfe3e86d71e9b91dae7561d5ceeaceb37a7d4fc078ab241afd7aab777f606f"
dependencies = [
"bindgen",
"cmake",
"fs_extra",
"fslock",
]
[[package]]
name = "rama-boring-tokio"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d71fab2ce4408cc40f819865501dbc63272ddab0e77dd3500ff77f1a0f883"
dependencies = [
"rama-boring",
"rama-boring-sys",
"tokio",
]
[[package]]
name = "rama-core"
version = "0.3.0-alpha.4"
@@ -6870,24 +6732,25 @@ dependencies = [
]
[[package]]
name = "rama-tls-rustls"
name = "rama-tls-boring"
version = "0.3.0-alpha.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "536d47f6b269fb20dffd45e4c04aa8b340698b3509326e3c36e444b4f33ce0d6"
checksum = "def3d5d06d3ca3a2d2e4376cf93de0555cd9c7960f085bf77be9562f5c9ace8f"
dependencies = [
"ahash",
"flume 0.12.0",
"itertools 0.14.0",
"moka",
"parking_lot",
"pin-project-lite",
"rama-boring",
"rama-boring-tokio",
"rama-core",
"rama-http-types",
"rama-net",
"rama-utils",
"rcgen",
"rustls",
"rustls-native-certs",
"rustls-pki-types",
"schannel",
"tokio",
"tokio-rustls",
"webpki-roots 1.0.5",
"x509-parser",
]
[[package]]
@@ -7049,20 +6912,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rcgen"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e"
dependencies = [
"aws-lc-rs",
"pem",
"rustls-pki-types",
"time",
"x509-parser",
"yasna",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -7229,18 +7078,17 @@ dependencies = [
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted 0.9.0",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rmcp"
version = "0.14.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a621b37a548ff6ab6292d57841eb25785a7f146d89391a19c9f199414bd13da"
checksum = "528d42f8176e6e5e71ea69182b17d1d0a19a6b3b894b564678b74cd7cab13cfa"
dependencies = [
"async-trait",
"axum",
"base64 0.22.1",
"bytes",
"chrono",
@@ -7271,9 +7119,9 @@ dependencies = [
[[package]]
name = "rmcp-macros"
version = "0.14.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b79ed92303f9262db79575aa8c3652581668e9d136be6fd0b9ededa78954c95"
checksum = "e3f81daaa494eb8e985c9462f7d6ce1ab05e5299f48aafd76cdd3d8b060e6f59"
dependencies = [
"darling 0.23.0",
"proc-macro2",
@@ -7341,16 +7189,6 @@ dependencies = [
"walkdir",
]
[[package]]
name = "rust-stemmers"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54"
dependencies = [
"serde",
"serde_derive",
]
[[package]]
name = "rustc-demangle"
version = "0.1.27"
@@ -7378,15 +7216,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rusticata-macros"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
"nom 7.1.3",
]
[[package]]
name = "rustix"
version = "0.38.44"
@@ -7419,7 +7248,6 @@ version = "0.23.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"ring",
@@ -7457,10 +7285,9 @@ version = "0.103.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted 0.9.0",
"untrusted",
]
[[package]]
@@ -8584,15 +8411,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stop-words"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645a3d441ccf4bf47f2e4b7681461986681a6eeea9937d4c3bc9febd61d17c71"
dependencies = [
"serde_json",
]
[[package]]
name = "streaming-iterator"
version = "0.1.9"
@@ -8966,9 +8784,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.47"
version = "0.3.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5"
dependencies = [
"deranged",
"itoa",
@@ -8989,9 +8807,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
version = "0.2.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4"
dependencies = [
"num-conv",
"time-core",
@@ -9131,7 +8949,7 @@ dependencies = [
[[package]]
name = "tokio-tungstenite"
version = "0.28.0"
source = "git+https://github.com/openai-oss-forks/tokio-tungstenite?rev=132f5b39c862e3a970f731d709608b3e6276d5f6#132f5b39c862e3a970f731d709608b3e6276d5f6"
source = "git+https://github.com/JakkuSakura/tokio-tungstenite?rev=2ae536b0de793f3ddf31fc2f22d445bf1ef2023d#2ae536b0de793f3ddf31fc2f22d445bf1ef2023d"
dependencies = [
"futures-util",
"log",
@@ -9525,13 +9343,11 @@ dependencies = [
[[package]]
name = "tungstenite"
version = "0.27.0"
source = "git+https://github.com/openai-oss-forks/tungstenite-rs?rev=9200079d3b54a1ff51072e24d81fd354f085156f#9200079d3b54a1ff51072e24d81fd354f085156f"
version = "0.28.0"
source = "git+https://github.com/JakkuSakura/tungstenite-rs?rev=f514de8644821113e5d18a027d6d28a5c8cc0a6e#f514de8644821113e5d18a027d6d28a5c8cc0a6e"
dependencies = [
"bytes",
"data-encoding",
"flate2",
"headers",
"http 1.4.0",
"httparse",
"log",
@@ -9693,12 +9509,6 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -10769,25 +10579,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "x509-parser"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
dependencies = [
"asn1-rs",
"aws-lc-rs",
"data-encoding",
"der-parser",
"lazy_static",
"nom 7.1.3",
"oid-registry",
"ring",
"rusticata-macros",
"thiserror 2.0.18",
"time",
]
[[package]]
name = "xdg-home"
version = "1.3.0"
@@ -10813,15 +10604,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yasna"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
dependencies = [
"time",
]
[[package]]
name = "yoke"
version = "0.8.1"

View File

@@ -93,7 +93,6 @@ codex-linux-sandbox = { path = "linux-sandbox" }
codex-lmstudio = { path = "lmstudio" }
codex-login = { path = "login" }
codex-mcp-server = { path = "mcp-server" }
codex-network-proxy = { path = "network-proxy" }
codex-ollama = { path = "ollama" }
codex-otel = { path = "otel" }
codex-process-hardening = { path = "process-hardening" }
@@ -130,7 +129,6 @@ async-stream = "0.3.6"
async-trait = "0.1.89"
axum = { version = "0.8", default-features = false }
base64 = "0.22.1"
bm25 = "2.3.2"
bytes = "1.10.1"
chardetng = "0.1.17"
chrono = "0.4.43"
@@ -196,8 +194,7 @@ ratatui-macros = "0.6.0"
regex = "1.12.2"
regex-lite = "0.1.8"
reqwest = "0.12"
rmcp = { version = "0.14.0", default-features = false }
rustls = { version = "0.23", default-features = false, features = ["ring", "std"] }
rmcp = { version = "0.12.0", default-features = false }
runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" }
schemars = "0.8.22"
seccompiler = "0.5.0"
@@ -224,13 +221,12 @@ tempfile = "3.23.0"
test-log = "0.2.19"
textwrap = "0.16.2"
thiserror = "2.0.17"
time = "0.3.47"
time = "0.3"
tiny_http = "0.12"
tokio = "1"
tokio-stream = "0.1.18"
tokio-test = "0.4"
tokio-tungstenite = { version = "0.28.0", features = ["proxy", "rustls-tls-native-roots"] }
tungstenite = { version = "0.27.0", features = ["deflate", "proxy"] }
tokio-util = "0.7.18"
toml = "0.9.5"
toml_edit = "0.24.0"
@@ -321,11 +317,10 @@ opt-level = 0
# ratatui = { path = "../../ratatui" }
crossterm = { git = "https://github.com/nornagon/crossterm", branch = "nornagon/color-query" }
ratatui = { git = "https://github.com/nornagon/ratatui", branch = "nornagon-v0.29.0-patch" }
tokio-tungstenite = { git = "https://github.com/openai-oss-forks/tokio-tungstenite", rev = "132f5b39c862e3a970f731d709608b3e6276d5f6" }
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "9200079d3b54a1ff51072e24d81fd354f085156f" }
tokio-tungstenite = { git = "https://github.com/JakkuSakura/tokio-tungstenite", rev = "2ae536b0de793f3ddf31fc2f22d445bf1ef2023d" }
# Uncomment to debug local changes.
# rmcp = { path = "../../rust-sdk/crates/rmcp" }
[patch."ssh://git@github.com/openai-oss-forks/tungstenite-rs.git"]
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "9200079d3b54a1ff51072e24d81fd354f085156f" }
[patch."ssh://git@github.com/JakkuSakura/tungstenite-rs.git"]
tungstenite = { git = "https://github.com/JakkuSakura/tungstenite-rs", rev = "f514de8644821113e5d18a027d6d28a5c8cc0a6e" }

View File

@@ -51,7 +51,6 @@ You can enable notifications by configuring a script that is run whenever the ag
### `codex exec` to run Codex programmatically/non-interactively
To run Codex non-interactively, run `codex exec PROMPT` (you can also pass the prompt via `stdin`) and Codex will work on your task until it decides that it is done and exits. Output is printed to the terminal directly. You can set the `RUST_LOG` environment variable to see more about what's going on.
Use `codex exec --ephemeral ...` to run without persisting session rollout files to disk.
### Experimenting with the Codex Sandbox

View File

@@ -21,7 +21,6 @@
"type": "object"
},
"AppsListParams": {
"description": "EXPERIMENTAL - list available apps/connectors.",
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
@@ -30,10 +29,6 @@
"null"
]
},
"forceRefetch": {
"description": "When true, bypass app caches and fetch the latest data from sources.",
"type": "boolean"
},
"limit": {
"description": "Optional page size; defaults to a reasonable server-side value.",
"format": "uint32",
@@ -427,27 +422,6 @@
],
"type": "object"
},
"ExperimentalFeatureListParams": {
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Optional page size; defaults to a reasonable server-side value.",
"format": "uint32",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
},
"FeedbackUploadParams": {
"properties": {
"classification": {
@@ -1069,23 +1043,11 @@
"type": "string"
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
@@ -2210,24 +2172,6 @@
],
"type": "object"
},
"SkillsListExtraRootsForCwd": {
"properties": {
"cwd": {
"type": "string"
},
"extraUserRoots": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"cwd",
"extraUserRoots"
],
"type": "object"
},
"SkillsListParams": {
"properties": {
"cwds": {
@@ -2240,17 +2184,6 @@
"forceReload": {
"description": "When true, bypass the skills cache and re-scan skills from disk.",
"type": "boolean"
},
"perCwdExtraUserRoots": {
"default": null,
"description": "Optional per-cwd extra roots to scan as user-scoped skills.",
"items": {
"$ref": "#/definitions/SkillsListExtraRootsForCwd"
},
"type": [
"array",
"null"
]
}
},
"type": "object"
@@ -2369,6 +2302,13 @@
"null"
]
},
"path": {
"description": "[UNSTABLE] Specify the rollout path to fork from. If specified, the thread_id param will be ignored.",
"type": [
"string",
"null"
]
},
"sandbox": {
"anyOf": [
{
@@ -2525,6 +2465,16 @@
"null"
]
},
"history": {
"description": "[UNSTABLE] FOR CODEX CLOUD - DO NOT USE. If specified, the thread will be resumed with the provided history instead of loaded from disk.",
"items": {
"$ref": "#/definitions/ResponseItem"
},
"type": [
"array",
"null"
]
},
"model": {
"description": "Configuration overrides for the resumed thread, if any.",
"type": [
@@ -2538,6 +2488,13 @@
"null"
]
},
"path": {
"description": "[UNSTABLE] Specify the rollout path to resume from. If specified, the thread_id param will be ignored.",
"type": [
"string",
"null"
]
},
"personality": {
"anyOf": [
{
@@ -2665,6 +2622,11 @@
"null"
]
},
"experimentalRawEvents": {
"default": false,
"description": "If true, opt into emitting raw response items on the event stream.\n\nThis is for internal use only (e.g. Codex Cloud). (TODO): Figure out a better way to categorize internal / experimental events & protocols.",
"type": "boolean"
},
"model": {
"type": [
"string",
@@ -2739,6 +2701,17 @@
],
"description": "Override the approval policy for this turn and subsequent turns."
},
"collaborationMode": {
"anyOf": [
{
"$ref": "#/definitions/CollaborationMode"
},
{
"type": "null"
}
],
"description": "EXPERIMENTAL - set a pre-set collaboration mode. Takes precedence over model, reasoning_effort, and developer instructions if set."
},
"cwd": {
"description": "Override the working directory for this turn and subsequent turns.",
"type": [
@@ -2816,29 +2789,6 @@
],
"type": "object"
},
"TurnSteerParams": {
"properties": {
"expectedTurnId": {
"description": "Required active turn id precondition. The request fails when it does not match the currently active turn.",
"type": "string"
},
"input": {
"items": {
"$ref": "#/definitions/UserInput"
},
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"expectedTurnId",
"input",
"threadId"
],
"type": "object"
},
"UserInput": {
"oneOf": [
{
@@ -3540,30 +3490,6 @@
"title": "Turn/startRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"turn/steer"
],
"title": "Turn/steerRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/TurnSteerParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Turn/steerRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -3636,30 +3562,6 @@
"title": "Model/listRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"experimentalFeature/list"
],
"title": "ExperimentalFeature/listRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ExperimentalFeatureListParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "ExperimentalFeature/listRequest",
"type": "object"
},
{
"properties": {
"id": {

View File

@@ -1486,6 +1486,7 @@
},
{
"properties": {
"arguments": true,
"call_id": {
"description": "Responses API call id for the associated tool call, if available.",
"type": "string"
@@ -1496,6 +1497,12 @@
},
"type": "array"
},
"tool": {
"type": [
"string",
"null"
]
},
"turn_id": {
"default": "",
"description": "Turn ID that this request belongs to. Uses `#[serde(default)]` for backwards compatibility.",
@@ -2775,95 +2782,6 @@
],
"title": "CollabCloseEndEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume begin.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"type": {
"enum": [
"collab_resume_begin"
],
"title": "CollabResumeBeginEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"type"
],
"title": "CollabResumeBeginEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume end.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"status": {
"allOf": [
{
"$ref": "#/definitions/AgentStatus"
}
],
"description": "Last known status of the receiver agent reported to the sender agent after resume."
},
"type": {
"enum": [
"collab_resume_end"
],
"title": "CollabResumeEndEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"status",
"type"
],
"title": "CollabResumeEndEventMsg",
"type": "object"
}
]
},
@@ -3258,23 +3176,11 @@
]
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
@@ -4786,7 +4692,6 @@
"type": "object"
},
{
"description": "Assistant-authored message payload used in turn-item streams.\n\n`phase` is optional because not all providers/models emit it. Consumers should use it when present, but retain legacy completion semantics when it is `None`.",
"properties": {
"content": {
"items": {
@@ -4797,17 +4702,6 @@
"id": {
"type": "string"
},
"phase": {
"anyOf": [
{
"$ref": "#/definitions/MessagePhase"
},
{
"type": "null"
}
],
"description": "Optional phase metadata carried through from `ResponseItem::Message`.\n\nThis is currently used by TUI rendering to distinguish mid-turn commentary from a final answer and avoid status-indicator jitter."
},
"type": {
"enum": [
"AgentMessage"
@@ -6199,6 +6093,7 @@
},
{
"properties": {
"arguments": true,
"call_id": {
"description": "Responses API call id for the associated tool call, if available.",
"type": "string"
@@ -6209,6 +6104,12 @@
},
"type": "array"
},
"tool": {
"type": [
"string",
"null"
]
},
"turn_id": {
"default": "",
"description": "Turn ID that this request belongs to. Uses `#[serde(default)]` for backwards compatibility.",
@@ -7488,96 +7389,7 @@
],
"title": "CollabCloseEndEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume begin.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"type": {
"enum": [
"collab_resume_begin"
],
"title": "CollabResumeBeginEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"type"
],
"title": "CollabResumeBeginEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume end.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"status": {
"allOf": [
{
"$ref": "#/definitions/AgentStatus"
}
],
"description": "Last known status of the receiver agent reported to the sender agent after resume."
},
"type": {
"enum": [
"collab_resume_end"
],
"title": "CollabResumeEndEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"status",
"type"
],
"title": "CollabResumeEndEventMsg",
"type": "object"
}
],
"title": "EventMsg"
}
}

View File

@@ -165,71 +165,6 @@
}
]
},
"AppInfo": {
"description": "EXPERIMENTAL - app metadata returned by app-list APIs.",
"properties": {
"description": {
"type": [
"string",
"null"
]
},
"distributionChannel": {
"type": [
"string",
"null"
]
},
"id": {
"type": "string"
},
"installUrl": {
"type": [
"string",
"null"
]
},
"isAccessible": {
"default": false,
"type": "boolean"
},
"logoUrl": {
"type": [
"string",
"null"
]
},
"logoUrlDark": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
},
"AppListUpdatedNotification": {
"description": "EXPERIMENTAL - notification emitted when the app list changes.",
"properties": {
"data": {
"items": {
"$ref": "#/definitions/AppInfo"
},
"type": "array"
}
},
"required": [
"data"
],
"type": "object"
},
"AskForApproval": {
"description": "Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
"oneOf": [
@@ -682,7 +617,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],
@@ -2130,6 +2064,7 @@
},
{
"properties": {
"arguments": true,
"call_id": {
"description": "Responses API call id for the associated tool call, if available.",
"type": "string"
@@ -2140,6 +2075,12 @@
},
"type": "array"
},
"tool": {
"type": [
"string",
"null"
]
},
"turn_id": {
"default": "",
"description": "Turn ID that this request belongs to. Uses `#[serde(default)]` for backwards compatibility.",
@@ -3419,95 +3360,6 @@
],
"title": "CollabCloseEndEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume begin.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"type": {
"enum": [
"collab_resume_begin"
],
"title": "CollabResumeBeginEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"type"
],
"title": "CollabResumeBeginEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume end.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"status": {
"allOf": [
{
"$ref": "#/definitions/AgentStatus"
}
],
"description": "Last known status of the receiver agent reported to the sender agent after resume."
},
"type": {
"enum": [
"collab_resume_end"
],
"title": "CollabResumeEndEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"status",
"type"
],
"title": "CollabResumeEndEventMsg",
"type": "object"
}
]
},
@@ -4103,23 +3955,11 @@
"type": "string"
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
@@ -6853,7 +6693,6 @@
"type": "object"
},
{
"description": "Assistant-authored message payload used in turn-item streams.\n\n`phase` is optional because not all providers/models emit it. Consumers should use it when present, but retain legacy completion semantics when it is `None`.",
"properties": {
"content": {
"items": {
@@ -6864,17 +6703,6 @@
"id": {
"type": "string"
},
"phase": {
"anyOf": [
{
"$ref": "#/definitions/MessagePhase"
},
{
"type": "null"
}
],
"description": "Optional phase metadata carried through from `ResponseItem::Message`.\n\nThis is currently used by TUI rendering to distinguish mid-turn commentary from a final answer and avoid status-indicator jitter."
},
"type": {
"enum": [
"AgentMessage"
@@ -7951,26 +7779,6 @@
"title": "Account/rateLimits/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"app/list/updated"
],
"title": "App/list/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/AppListUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "App/list/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -8197,4 +8005,4 @@
}
],
"title": "ServerNotification"
}
}

View File

@@ -549,6 +549,7 @@
"ToolRequestUserInputParams": {
"description": "EXPERIMENTAL. Params sent with a request_user_input event.",
"properties": {
"arguments": true,
"itemId": {
"type": "string"
},
@@ -561,6 +562,12 @@
"threadId": {
"type": "string"
},
"tool": {
"type": [
"string",
"null"
]
},
"turnId": {
"type": "string"
}

View File

@@ -57,6 +57,7 @@
},
"description": "EXPERIMENTAL. Params sent with a request_user_input event.",
"properties": {
"arguments": true,
"itemId": {
"type": "string"
},
@@ -69,6 +70,12 @@
"threadId": {
"type": "string"
},
"tool": {
"type": [
"string",
"null"
]
},
"turnId": {
"type": "string"
}

View File

@@ -862,30 +862,6 @@
"title": "Turn/startRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"turn/steer"
],
"title": "Turn/steerRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/TurnSteerParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Turn/steerRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -958,30 +934,6 @@
"title": "Model/listRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"experimentalFeature/list"
],
"title": "ExperimentalFeature/listRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ExperimentalFeatureListParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "ExperimentalFeature/listRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -3493,6 +3445,7 @@
},
{
"properties": {
"arguments": true,
"call_id": {
"description": "Responses API call id for the associated tool call, if available.",
"type": "string"
@@ -3503,6 +3456,12 @@
},
"type": "array"
},
"tool": {
"type": [
"string",
"null"
]
},
"turn_id": {
"default": "",
"description": "Turn ID that this request belongs to. Uses `#[serde(default)]` for backwards compatibility.",
@@ -4782,95 +4741,6 @@
],
"title": "CollabCloseEndEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume begin.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/v2/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/v2/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"type": {
"enum": [
"collab_resume_begin"
],
"title": "CollabResumeBeginEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"type"
],
"title": "CollabResumeBeginEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume end.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/v2/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/v2/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"status": {
"allOf": [
{
"$ref": "#/definitions/AgentStatus"
}
],
"description": "Last known status of the receiver agent reported to the sender agent after resume."
},
"type": {
"enum": [
"collab_resume_end"
],
"title": "CollabResumeEndEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"status",
"type"
],
"title": "CollabResumeEndEventMsg",
"type": "object"
}
],
"title": "EventMsg"
@@ -6153,23 +6023,11 @@
]
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
@@ -8192,26 +8050,6 @@
"title": "Account/rateLimits/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"app/list/updated"
],
"title": "App/list/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/AppListUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "App/list/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -9133,6 +8971,7 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL. Params sent with a request_user_input event.",
"properties": {
"arguments": true,
"itemId": {
"type": "string"
},
@@ -9145,6 +8984,12 @@
"threadId": {
"type": "string"
},
"tool": {
"type": [
"string",
"null"
]
},
"turnId": {
"type": "string"
}
@@ -9267,7 +9112,6 @@
"type": "object"
},
{
"description": "Assistant-authored message payload used in turn-item streams.\n\n`phase` is optional because not all providers/models emit it. Consumers should use it when present, but retain legacy completion semantics when it is `None`.",
"properties": {
"content": {
"items": {
@@ -9278,17 +9122,6 @@
"id": {
"type": "string"
},
"phase": {
"anyOf": [
{
"$ref": "#/definitions/MessagePhase"
},
{
"type": "null"
}
],
"description": "Optional phase metadata carried through from `ResponseItem::Message`.\n\nThis is currently used by TUI rendering to distinguish mid-turn commentary from a final answer and avoid status-indicator jitter."
},
"type": {
"enum": [
"AgentMessage"
@@ -9958,34 +9791,7 @@
},
"type": "object"
},
"AppConfig": {
"properties": {
"disabled_reason": {
"anyOf": [
{
"$ref": "#/definitions/v2/AppDisabledReason"
},
{
"type": "null"
}
]
},
"enabled": {
"default": true,
"type": "boolean"
}
},
"type": "object"
},
"AppDisabledReason": {
"enum": [
"unknown",
"user"
],
"type": "string"
},
"AppInfo": {
"description": "EXPERIMENTAL - app metadata returned by app-list APIs.",
"properties": {
"description": {
"type": [
@@ -10034,29 +9840,8 @@
],
"type": "object"
},
"AppListUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - notification emitted when the app list changes.",
"properties": {
"data": {
"items": {
"$ref": "#/definitions/v2/AppInfo"
},
"type": "array"
}
},
"required": [
"data"
],
"title": "AppListUpdatedNotification",
"type": "object"
},
"AppsConfig": {
"type": "object"
},
"AppsListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - list available apps/connectors.",
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
@@ -10065,10 +9850,6 @@
"null"
]
},
"forceRefetch": {
"description": "When true, bypass app caches and fetch the latest data from sources.",
"type": "boolean"
},
"limit": {
"description": "Optional page size; defaults to a reasonable server-side value.",
"format": "uint32",
@@ -10084,7 +9865,6 @@
},
"AppsListResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - app list response.",
"properties": {
"data": {
"items": {
@@ -10367,7 +10147,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],
@@ -11135,15 +10914,6 @@
"null"
]
},
"allowedWebSearchModes": {
"items": {
"$ref": "#/definitions/v2/WebSearchMode"
},
"type": [
"array",
"null"
]
},
"enforceResidency": {
"anyOf": [
{
@@ -11448,143 +11218,6 @@
"title": "ErrorNotification",
"type": "object"
},
"ExperimentalFeature": {
"properties": {
"announcement": {
"description": "Announcement copy shown to users when the feature is introduced. Null when this feature is not in beta.",
"type": [
"string",
"null"
]
},
"defaultEnabled": {
"description": "Whether this feature is enabled by default.",
"type": "boolean"
},
"description": {
"description": "Short summary describing what the feature does. Null when this feature is not in beta.",
"type": [
"string",
"null"
]
},
"displayName": {
"description": "User-facing display name shown in the experimental features UI. Null when this feature is not in beta.",
"type": [
"string",
"null"
]
},
"enabled": {
"description": "Whether this feature is currently enabled in the loaded config.",
"type": "boolean"
},
"name": {
"description": "Stable key used in config.toml and CLI flag toggles.",
"type": "string"
},
"stage": {
"allOf": [
{
"$ref": "#/definitions/v2/ExperimentalFeatureStage"
}
],
"description": "Lifecycle stage of this feature flag."
}
},
"required": [
"defaultEnabled",
"enabled",
"name",
"stage"
],
"type": "object"
},
"ExperimentalFeatureListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Optional page size; defaults to a reasonable server-side value.",
"format": "uint32",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"title": "ExperimentalFeatureListParams",
"type": "object"
},
"ExperimentalFeatureListResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"data": {
"items": {
"$ref": "#/definitions/v2/ExperimentalFeature"
},
"type": "array"
},
"nextCursor": {
"description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.",
"type": [
"string",
"null"
]
}
},
"required": [
"data"
],
"title": "ExperimentalFeatureListResponse",
"type": "object"
},
"ExperimentalFeatureStage": {
"oneOf": [
{
"description": "Feature is available for user testing and feedback.",
"enum": [
"beta"
],
"type": "string"
},
{
"description": "Feature is still being built and not ready for broad use.",
"enum": [
"underDevelopment"
],
"type": "string"
},
{
"description": "Feature is production-ready.",
"enum": [
"stable"
],
"type": "string"
},
{
"description": "Feature is deprecated and should be avoided.",
"enum": [
"deprecated"
],
"type": "string"
},
{
"description": "Feature flag is retained only for backwards compatibility.",
"enum": [
"removed"
],
"type": "string"
}
]
},
"FeedbackUploadParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -12345,23 +11978,11 @@
"type": "string"
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
@@ -12482,84 +12103,6 @@
],
"type": "string"
},
"NetworkRequirements": {
"properties": {
"allowLocalBinding": {
"type": [
"boolean",
"null"
]
},
"allowUnixSockets": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"allowUpstreamProxy": {
"type": [
"boolean",
"null"
]
},
"allowedDomains": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"dangerouslyAllowNonLoopbackAdmin": {
"type": [
"boolean",
"null"
]
},
"dangerouslyAllowNonLoopbackProxy": {
"type": [
"boolean",
"null"
]
},
"deniedDomains": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"enabled": {
"type": [
"boolean",
"null"
]
},
"httpPort": {
"format": "uint16",
"minimum": 0.0,
"type": [
"integer",
"null"
]
},
"socksPort": {
"format": "uint16",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
},
"OverriddenMetadata": {
"properties": {
"effectiveValue": true,
@@ -14079,24 +13622,6 @@
],
"type": "object"
},
"SkillsListExtraRootsForCwd": {
"properties": {
"cwd": {
"type": "string"
},
"extraUserRoots": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"cwd",
"extraUserRoots"
],
"type": "object"
},
"SkillsListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -14110,17 +13635,6 @@
"forceReload": {
"description": "When true, bypass the skills cache and re-scan skills from disk.",
"type": "boolean"
},
"perCwdExtraUserRoots": {
"default": null,
"description": "Optional per-cwd extra roots to scan as user-scoped skills.",
"items": {
"$ref": "#/definitions/v2/SkillsListExtraRootsForCwd"
},
"type": [
"array",
"null"
]
}
},
"title": "SkillsListParams",
@@ -14505,6 +14019,13 @@
"null"
]
},
"path": {
"description": "[UNSTABLE] Specify the rollout path to fork from. If specified, the thread_id param will be ignored.",
"type": [
"string",
"null"
]
},
"sandbox": {
"anyOf": [
{
@@ -15263,6 +14784,16 @@
"null"
]
},
"history": {
"description": "[UNSTABLE] FOR CODEX CLOUD - DO NOT USE. If specified, the thread will be resumed with the provided history instead of loaded from disk.",
"items": {
"$ref": "#/definitions/v2/ResponseItem"
},
"type": [
"array",
"null"
]
},
"model": {
"description": "Configuration overrides for the resumed thread, if any.",
"type": [
@@ -15276,6 +14807,13 @@
"null"
]
},
"path": {
"description": "[UNSTABLE] Specify the rollout path to resume from. If specified, the thread_id param will be ignored.",
"type": [
"string",
"null"
]
},
"personality": {
"anyOf": [
{
@@ -15475,6 +15013,11 @@
"null"
]
},
"experimentalRawEvents": {
"default": false,
"description": "If true, opt into emitting raw response items on the event stream.\n\nThis is for internal use only (e.g. Codex Cloud). (TODO): Figure out a better way to categorize internal / experimental events & protocols.",
"type": "boolean"
},
"model": {
"type": [
"string",
@@ -15911,6 +15454,17 @@
],
"description": "Override the approval policy for this turn and subsequent turns."
},
"collaborationMode": {
"anyOf": [
{
"$ref": "#/definitions/v2/CollaborationMode"
},
{
"type": "null"
}
],
"description": "EXPERIMENTAL - set a pre-set collaboration mode. Takes precedence over model, reasoning_effort, and developer instructions if set."
},
"cwd": {
"description": "Override the working directory for this turn and subsequent turns.",
"type": [
@@ -16028,44 +15582,6 @@
],
"type": "string"
},
"TurnSteerParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"expectedTurnId": {
"description": "Required active turn id precondition. The request fails when it does not match the currently active turn.",
"type": "string"
},
"input": {
"items": {
"$ref": "#/definitions/v2/UserInput"
},
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"expectedTurnId",
"input",
"threadId"
],
"title": "TurnSteerParams",
"type": "object"
},
"TurnSteerResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"turnId": {
"type": "string"
}
},
"required": [
"turnId"
],
"title": "TurnSteerResponse",
"type": "object"
},
"UserInput": {
"oneOf": [
{
@@ -16341,4 +15857,4 @@
},
"title": "CodexAppServerProtocol",
"type": "object"
}
}

View File

@@ -1486,6 +1486,7 @@
},
{
"properties": {
"arguments": true,
"call_id": {
"description": "Responses API call id for the associated tool call, if available.",
"type": "string"
@@ -1496,6 +1497,12 @@
},
"type": "array"
},
"tool": {
"type": [
"string",
"null"
]
},
"turn_id": {
"default": "",
"description": "Turn ID that this request belongs to. Uses `#[serde(default)]` for backwards compatibility.",
@@ -2775,95 +2782,6 @@
],
"title": "CollabCloseEndEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume begin.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"type": {
"enum": [
"collab_resume_begin"
],
"title": "CollabResumeBeginEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"type"
],
"title": "CollabResumeBeginEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume end.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"status": {
"allOf": [
{
"$ref": "#/definitions/AgentStatus"
}
],
"description": "Last known status of the receiver agent reported to the sender agent after resume."
},
"type": {
"enum": [
"collab_resume_end"
],
"title": "CollabResumeEndEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"status",
"type"
],
"title": "CollabResumeEndEventMsg",
"type": "object"
}
]
},
@@ -3258,23 +3176,11 @@
]
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
@@ -4786,7 +4692,6 @@
"type": "object"
},
{
"description": "Assistant-authored message payload used in turn-item streams.\n\n`phase` is optional because not all providers/models emit it. Consumers should use it when present, but retain legacy completion semantics when it is `None`.",
"properties": {
"content": {
"items": {
@@ -4797,17 +4702,6 @@
"id": {
"type": "string"
},
"phase": {
"anyOf": [
{
"$ref": "#/definitions/MessagePhase"
},
{
"type": "null"
}
],
"description": "Optional phase metadata carried through from `ResponseItem::Message`.\n\nThis is currently used by TUI rendering to distinguish mid-turn commentary from a final answer and avoid status-indicator jitter."
},
"type": {
"enum": [
"AgentMessage"
@@ -5186,4 +5080,4 @@
],
"title": "ForkConversationResponse",
"type": "object"
}
}

View File

@@ -271,23 +271,11 @@
"type": "string"
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"NewConversationParams": {
"properties": {

View File

@@ -1486,6 +1486,7 @@
},
{
"properties": {
"arguments": true,
"call_id": {
"description": "Responses API call id for the associated tool call, if available.",
"type": "string"
@@ -1496,6 +1497,12 @@
},
"type": "array"
},
"tool": {
"type": [
"string",
"null"
]
},
"turn_id": {
"default": "",
"description": "Turn ID that this request belongs to. Uses `#[serde(default)]` for backwards compatibility.",
@@ -2775,95 +2782,6 @@
],
"title": "CollabCloseEndEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume begin.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"type": {
"enum": [
"collab_resume_begin"
],
"title": "CollabResumeBeginEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"type"
],
"title": "CollabResumeBeginEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume end.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"status": {
"allOf": [
{
"$ref": "#/definitions/AgentStatus"
}
],
"description": "Last known status of the receiver agent reported to the sender agent after resume."
},
"type": {
"enum": [
"collab_resume_end"
],
"title": "CollabResumeEndEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"status",
"type"
],
"title": "CollabResumeEndEventMsg",
"type": "object"
}
]
},
@@ -3258,23 +3176,11 @@
]
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
@@ -4786,7 +4692,6 @@
"type": "object"
},
{
"description": "Assistant-authored message payload used in turn-item streams.\n\n`phase` is optional because not all providers/models emit it. Consumers should use it when present, but retain legacy completion semantics when it is `None`.",
"properties": {
"content": {
"items": {
@@ -4797,17 +4702,6 @@
"id": {
"type": "string"
},
"phase": {
"anyOf": [
{
"$ref": "#/definitions/MessagePhase"
},
{
"type": "null"
}
],
"description": "Optional phase metadata carried through from `ResponseItem::Message`.\n\nThis is currently used by TUI rendering to distinguish mid-turn commentary from a final answer and avoid status-indicator jitter."
},
"type": {
"enum": [
"AgentMessage"
@@ -5186,4 +5080,4 @@
],
"title": "ResumeConversationResponse",
"type": "object"
}
}

View File

@@ -1486,6 +1486,7 @@
},
{
"properties": {
"arguments": true,
"call_id": {
"description": "Responses API call id for the associated tool call, if available.",
"type": "string"
@@ -1496,6 +1497,12 @@
},
"type": "array"
},
"tool": {
"type": [
"string",
"null"
]
},
"turn_id": {
"default": "",
"description": "Turn ID that this request belongs to. Uses `#[serde(default)]` for backwards compatibility.",
@@ -2775,95 +2782,6 @@
],
"title": "CollabCloseEndEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume begin.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"type": {
"enum": [
"collab_resume_begin"
],
"title": "CollabResumeBeginEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"type"
],
"title": "CollabResumeBeginEventMsg",
"type": "object"
},
{
"description": "Collab interaction: resume end.",
"properties": {
"call_id": {
"description": "Identifier for the collab tool call.",
"type": "string"
},
"receiver_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the receiver."
},
"sender_thread_id": {
"allOf": [
{
"$ref": "#/definitions/ThreadId"
}
],
"description": "Thread ID of the sender."
},
"status": {
"allOf": [
{
"$ref": "#/definitions/AgentStatus"
}
],
"description": "Last known status of the receiver agent reported to the sender agent after resume."
},
"type": {
"enum": [
"collab_resume_end"
],
"title": "CollabResumeEndEventMsgType",
"type": "string"
}
},
"required": [
"call_id",
"receiver_thread_id",
"sender_thread_id",
"status",
"type"
],
"title": "CollabResumeEndEventMsg",
"type": "object"
}
]
},
@@ -3258,23 +3176,11 @@
]
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
@@ -4786,7 +4692,6 @@
"type": "object"
},
{
"description": "Assistant-authored message payload used in turn-item streams.\n\n`phase` is optional because not all providers/models emit it. Consumers should use it when present, but retain legacy completion semantics when it is `None`.",
"properties": {
"content": {
"items": {
@@ -4797,17 +4702,6 @@
"id": {
"type": "string"
},
"phase": {
"anyOf": [
{
"$ref": "#/definitions/MessagePhase"
},
{
"type": "null"
}
],
"description": "Optional phase metadata carried through from `ResponseItem::Message`.\n\nThis is currently used by TUI rendering to distinguish mid-turn commentary from a final answer and avoid status-indicator jitter."
},
"type": {
"enum": [
"AgentMessage"
@@ -5208,4 +5102,4 @@
],
"title": "SessionConfiguredNotification",
"type": "object"
}
}

View File

@@ -1,69 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AppInfo": {
"description": "EXPERIMENTAL - app metadata returned by app-list APIs.",
"properties": {
"description": {
"type": [
"string",
"null"
]
},
"distributionChannel": {
"type": [
"string",
"null"
]
},
"id": {
"type": "string"
},
"installUrl": {
"type": [
"string",
"null"
]
},
"isAccessible": {
"default": false,
"type": "boolean"
},
"logoUrl": {
"type": [
"string",
"null"
]
},
"logoUrlDark": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
}
},
"description": "EXPERIMENTAL - notification emitted when the app list changes.",
"properties": {
"data": {
"items": {
"$ref": "#/definitions/AppInfo"
},
"type": "array"
}
},
"required": [
"data"
],
"title": "AppListUpdatedNotification",
"type": "object"
}

View File

@@ -1,6 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - list available apps/connectors.",
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
@@ -9,10 +8,6 @@
"null"
]
},
"forceRefetch": {
"description": "When true, bypass app caches and fetch the latest data from sources.",
"type": "boolean"
},
"limit": {
"description": "Optional page size; defaults to a reasonable server-side value.",
"format": "uint32",

View File

@@ -2,7 +2,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AppInfo": {
"description": "EXPERIMENTAL - app metadata returned by app-list APIs.",
"properties": {
"description": {
"type": [
@@ -52,7 +51,6 @@
"type": "object"
}
},
"description": "EXPERIMENTAL - app list response.",
"properties": {
"data": {
"items": {

View File

@@ -17,35 +17,6 @@
},
"type": "object"
},
"AppConfig": {
"properties": {
"disabled_reason": {
"anyOf": [
{
"$ref": "#/definitions/AppDisabledReason"
},
{
"type": "null"
}
]
},
"enabled": {
"default": true,
"type": "boolean"
}
},
"type": "object"
},
"AppDisabledReason": {
"enum": [
"unknown",
"user"
],
"type": "string"
},
"AppsConfig": {
"type": "object"
},
"AskForApproval": {
"enum": [
"untrusted",

View File

@@ -30,15 +30,6 @@
"null"
]
},
"allowedWebSearchModes": {
"items": {
"$ref": "#/definitions/WebSearchMode"
},
"type": [
"array",
"null"
]
},
"enforceResidency": {
"anyOf": [
{
@@ -52,84 +43,6 @@
},
"type": "object"
},
"NetworkRequirements": {
"properties": {
"allowLocalBinding": {
"type": [
"boolean",
"null"
]
},
"allowUnixSockets": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"allowUpstreamProxy": {
"type": [
"boolean",
"null"
]
},
"allowedDomains": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"dangerouslyAllowNonLoopbackAdmin": {
"type": [
"boolean",
"null"
]
},
"dangerouslyAllowNonLoopbackProxy": {
"type": [
"boolean",
"null"
]
},
"deniedDomains": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"enabled": {
"type": [
"boolean",
"null"
]
},
"httpPort": {
"format": "uint16",
"minimum": 0.0,
"type": [
"integer",
"null"
]
},
"socksPort": {
"format": "uint16",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
},
"ResidencyRequirement": {
"enum": [
"us"
@@ -143,14 +56,6 @@
"danger-full-access"
],
"type": "string"
},
"WebSearchMode": {
"enum": [
"disabled",
"cached",
"live"
],
"type": "string"
}
},
"properties": {

View File

@@ -1,23 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Optional page size; defaults to a reasonable server-side value.",
"format": "uint32",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"title": "ExperimentalFeatureListParams",
"type": "object"
}

View File

@@ -1,116 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ExperimentalFeature": {
"properties": {
"announcement": {
"description": "Announcement copy shown to users when the feature is introduced. Null when this feature is not in beta.",
"type": [
"string",
"null"
]
},
"defaultEnabled": {
"description": "Whether this feature is enabled by default.",
"type": "boolean"
},
"description": {
"description": "Short summary describing what the feature does. Null when this feature is not in beta.",
"type": [
"string",
"null"
]
},
"displayName": {
"description": "User-facing display name shown in the experimental features UI. Null when this feature is not in beta.",
"type": [
"string",
"null"
]
},
"enabled": {
"description": "Whether this feature is currently enabled in the loaded config.",
"type": "boolean"
},
"name": {
"description": "Stable key used in config.toml and CLI flag toggles.",
"type": "string"
},
"stage": {
"allOf": [
{
"$ref": "#/definitions/ExperimentalFeatureStage"
}
],
"description": "Lifecycle stage of this feature flag."
}
},
"required": [
"defaultEnabled",
"enabled",
"name",
"stage"
],
"type": "object"
},
"ExperimentalFeatureStage": {
"oneOf": [
{
"description": "Feature is available for user testing and feedback.",
"enum": [
"beta"
],
"type": "string"
},
{
"description": "Feature is still being built and not ready for broad use.",
"enum": [
"underDevelopment"
],
"type": "string"
},
{
"description": "Feature is production-ready.",
"enum": [
"stable"
],
"type": "string"
},
{
"description": "Feature is deprecated and should be avoided.",
"enum": [
"deprecated"
],
"type": "string"
},
{
"description": "Feature flag is retained only for backwards compatibility.",
"enum": [
"removed"
],
"type": "string"
}
]
}
},
"properties": {
"data": {
"items": {
"$ref": "#/definitions/ExperimentalFeature"
},
"type": "array"
},
"nextCursor": {
"description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.",
"type": [
"string",
"null"
]
}
},
"required": [
"data"
],
"title": "ExperimentalFeatureListResponse",
"type": "object"
}

View File

@@ -52,7 +52,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -52,7 +52,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -238,23 +238,11 @@
"type": "string"
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"ReasoningItemContent": {
"oneOf": [

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -1,25 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"SkillsListExtraRootsForCwd": {
"properties": {
"cwd": {
"type": "string"
},
"extraUserRoots": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"cwd",
"extraUserRoots"
],
"type": "object"
}
},
"properties": {
"cwds": {
"description": "When empty, defaults to the current session working directory.",
@@ -31,17 +11,6 @@
"forceReload": {
"description": "When true, bypass the skills cache and re-scan skills from disk.",
"type": "boolean"
},
"perCwdExtraUserRoots": {
"default": null,
"description": "Optional per-cwd extra roots to scan as user-scoped skills.",
"items": {
"$ref": "#/definitions/SkillsListExtraRootsForCwd"
},
"type": [
"array",
"null"
]
}
},
"title": "SkillsListParams",

View File

@@ -69,6 +69,13 @@
"null"
]
},
"path": {
"description": "[UNSTABLE] Specify the rollout path to fork from. If specified, the thread_id param will be ignored.",
"type": [
"string",
"null"
]
},
"sandbox": {
"anyOf": [
{

View File

@@ -207,7 +207,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -247,23 +247,11 @@
"type": "string"
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
"enum": [
"commentary",
"final_answer"
],
"type": "string"
},
"Personality": {
"enum": [
@@ -844,6 +832,16 @@
"null"
]
},
"history": {
"description": "[UNSTABLE] FOR CODEX CLOUD - DO NOT USE. If specified, the thread will be resumed with the provided history instead of loaded from disk.",
"items": {
"$ref": "#/definitions/ResponseItem"
},
"type": [
"array",
"null"
]
},
"model": {
"description": "Configuration overrides for the resumed thread, if any.",
"type": [
@@ -857,6 +855,13 @@
"null"
]
},
"path": {
"description": "[UNSTABLE] Specify the rollout path to resume from. If specified, the thread_id param will be ignored.",
"type": [
"string",
"null"
]
},
"personality": {
"anyOf": [
{

View File

@@ -207,7 +207,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -86,6 +86,11 @@
"null"
]
},
"experimentalRawEvents": {
"default": false,
"description": "If true, opt into emitting raw response items on the event stream.\n\nThis is for internal use only (e.g. Codex Cloud). (TODO): Figure out a better way to categorize internal / experimental events & protocols.",
"type": "boolean"
},
"model": {
"type": [
"string",

View File

@@ -207,7 +207,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -383,6 +383,17 @@
],
"description": "Override the approval policy for this turn and subsequent turns."
},
"collaborationMode": {
"anyOf": [
{
"$ref": "#/definitions/CollaborationMode"
},
{
"type": "null"
}
],
"description": "EXPERIMENTAL - set a pre-set collaboration mode. Takes precedence over model, reasoning_effort, and developer instructions if set."
},
"cwd": {
"description": "Override the working directory for this turn and subsequent turns.",
"type": [

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -194,7 +194,6 @@
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],

View File

@@ -1,189 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ByteRange": {
"properties": {
"end": {
"format": "uint",
"minimum": 0.0,
"type": "integer"
},
"start": {
"format": "uint",
"minimum": 0.0,
"type": "integer"
}
},
"required": [
"end",
"start"
],
"type": "object"
},
"TextElement": {
"properties": {
"byteRange": {
"allOf": [
{
"$ref": "#/definitions/ByteRange"
}
],
"description": "Byte range in the parent `text` buffer that this element occupies."
},
"placeholder": {
"description": "Optional human-readable placeholder for the element, displayed in the UI.",
"type": [
"string",
"null"
]
}
},
"required": [
"byteRange"
],
"type": "object"
},
"UserInput": {
"oneOf": [
{
"properties": {
"text": {
"type": "string"
},
"text_elements": {
"default": [],
"description": "UI-defined spans within `text` used to render or persist special elements.",
"items": {
"$ref": "#/definitions/TextElement"
},
"type": "array"
},
"type": {
"enum": [
"text"
],
"title": "TextUserInputType",
"type": "string"
}
},
"required": [
"text",
"type"
],
"title": "TextUserInput",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"image"
],
"title": "ImageUserInputType",
"type": "string"
},
"url": {
"type": "string"
}
},
"required": [
"type",
"url"
],
"title": "ImageUserInput",
"type": "object"
},
{
"properties": {
"path": {
"type": "string"
},
"type": {
"enum": [
"localImage"
],
"title": "LocalImageUserInputType",
"type": "string"
}
},
"required": [
"path",
"type"
],
"title": "LocalImageUserInput",
"type": "object"
},
{
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"type": {
"enum": [
"skill"
],
"title": "SkillUserInputType",
"type": "string"
}
},
"required": [
"name",
"path",
"type"
],
"title": "SkillUserInput",
"type": "object"
},
{
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"type": {
"enum": [
"mention"
],
"title": "MentionUserInputType",
"type": "string"
}
},
"required": [
"name",
"path",
"type"
],
"title": "MentionUserInput",
"type": "object"
}
]
}
},
"properties": {
"expectedTurnId": {
"description": "Required active turn id precondition. The request fails when it does not match the currently active turn.",
"type": "string"
},
"input": {
"items": {
"$ref": "#/definitions/UserInput"
},
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"expectedTurnId",
"input",
"threadId"
],
"title": "TurnSteerParams",
"type": "object"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"turnId": {
"type": "string"
}
},
"required": [
"turnId"
],
"title": "TurnSteerResponse",
"type": "object"
}

View File

@@ -2,20 +2,5 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AgentMessageContent } from "./AgentMessageContent";
import type { MessagePhase } from "./MessagePhase";
/**
* Assistant-authored message payload used in turn-item streams.
*
* `phase` is optional because not all providers/models emit it. Consumers
* should use it when present, but retain legacy completion semantics when it
* is `None`.
*/
export type AgentMessageItem = { id: string, content: Array<AgentMessageContent>,
/**
* Optional phase metadata carried through from `ResponseItem::Message`.
*
* This is currently used by TUI rendering to distinguish mid-turn
* commentary from a final answer and avoid status-indicator jitter.
*/
phase?: MessagePhase, };
export type AgentMessageItem = { id: string, content: Array<AgentMessageContent>, };

View File

@@ -27,7 +27,6 @@ import type { CommandExecParams } from "./v2/CommandExecParams";
import type { ConfigBatchWriteParams } from "./v2/ConfigBatchWriteParams";
import type { ConfigReadParams } from "./v2/ConfigReadParams";
import type { ConfigValueWriteParams } from "./v2/ConfigValueWriteParams";
import type { ExperimentalFeatureListParams } from "./v2/ExperimentalFeatureListParams";
import type { FeedbackUploadParams } from "./v2/FeedbackUploadParams";
import type { GetAccountParams } from "./v2/GetAccountParams";
import type { ListMcpServerStatusParams } from "./v2/ListMcpServerStatusParams";
@@ -52,9 +51,8 @@ import type { ThreadStartParams } from "./v2/ThreadStartParams";
import type { ThreadUnarchiveParams } from "./v2/ThreadUnarchiveParams";
import type { TurnInterruptParams } from "./v2/TurnInterruptParams";
import type { TurnStartParams } from "./v2/TurnStartParams";
import type { TurnSteerParams } from "./v2/TurnSteerParams";
/**
* 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/name/set", id: RequestId, params: ThreadSetNameParams, } | { "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": "skills/remote/read", id: RequestId, params: SkillsRemoteReadParams, } | { "method": "skills/remote/write", id: RequestId, params: SkillsRemoteWriteParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "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": "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": "config/read", id: RequestId, params: ConfigReadParams, } | { "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": "newConversation", id: RequestId, params: NewConversationParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "listConversations", id: RequestId, params: ListConversationsParams, } | { "method": "resumeConversation", id: RequestId, params: ResumeConversationParams, } | { "method": "forkConversation", id: RequestId, params: ForkConversationParams, } | { "method": "archiveConversation", id: RequestId, params: ArchiveConversationParams, } | { "method": "sendUserMessage", id: RequestId, params: SendUserMessageParams, } | { "method": "sendUserTurn", id: RequestId, params: SendUserTurnParams, } | { "method": "interruptConversation", id: RequestId, params: InterruptConversationParams, } | { "method": "addConversationListener", id: RequestId, params: AddConversationListenerParams, } | { "method": "removeConversationListener", id: RequestId, params: RemoveConversationListenerParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "loginApiKey", id: RequestId, params: LoginApiKeyParams, } | { "method": "loginChatGpt", id: RequestId, params: undefined, } | { "method": "cancelLoginChatGpt", id: RequestId, params: CancelLoginChatGptParams, } | { "method": "logoutChatGpt", id: RequestId, params: undefined, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "getUserSavedConfig", id: RequestId, params: undefined, } | { "method": "setDefaultModel", id: RequestId, params: SetDefaultModelParams, } | { "method": "getUserAgent", id: RequestId, params: undefined, } | { "method": "userInfo", id: RequestId, params: undefined, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, } | { "method": "execOneOffCommand", id: RequestId, params: ExecOneOffCommandParams, };
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/name/set", id: RequestId, params: ThreadSetNameParams, } | { "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": "skills/remote/read", id: RequestId, params: SkillsRemoteReadParams, } | { "method": "skills/remote/write", id: RequestId, params: SkillsRemoteWriteParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "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": "config/read", id: RequestId, params: ConfigReadParams, } | { "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": "newConversation", id: RequestId, params: NewConversationParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "listConversations", id: RequestId, params: ListConversationsParams, } | { "method": "resumeConversation", id: RequestId, params: ResumeConversationParams, } | { "method": "forkConversation", id: RequestId, params: ForkConversationParams, } | { "method": "archiveConversation", id: RequestId, params: ArchiveConversationParams, } | { "method": "sendUserMessage", id: RequestId, params: SendUserMessageParams, } | { "method": "sendUserTurn", id: RequestId, params: SendUserTurnParams, } | { "method": "interruptConversation", id: RequestId, params: InterruptConversationParams, } | { "method": "addConversationListener", id: RequestId, params: AddConversationListenerParams, } | { "method": "removeConversationListener", id: RequestId, params: RemoveConversationListenerParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "loginApiKey", id: RequestId, params: LoginApiKeyParams, } | { "method": "loginChatGpt", id: RequestId, params: undefined, } | { "method": "cancelLoginChatGpt", id: RequestId, params: CancelLoginChatGptParams, } | { "method": "logoutChatGpt", id: RequestId, params: undefined, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "getUserSavedConfig", id: RequestId, params: undefined, } | { "method": "setDefaultModel", id: RequestId, params: SetDefaultModelParams, } | { "method": "getUserAgent", id: RequestId, params: undefined, } | { "method": "userInfo", id: RequestId, params: undefined, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, } | { "method": "execOneOffCommand", id: RequestId, params: ExecOneOffCommandParams, };

View File

@@ -1,18 +0,0 @@
// 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 { ThreadId } from "./ThreadId";
export type CollabResumeBeginEvent = {
/**
* Identifier for the collab tool call.
*/
call_id: string,
/**
* Thread ID of the sender.
*/
sender_thread_id: ThreadId,
/**
* Thread ID of the receiver.
*/
receiver_thread_id: ThreadId, };

View File

@@ -1,24 +0,0 @@
// 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 { AgentStatus } from "./AgentStatus";
import type { ThreadId } from "./ThreadId";
export type CollabResumeEndEvent = {
/**
* Identifier for the collab tool call.
*/
call_id: string,
/**
* Thread ID of the sender.
*/
sender_thread_id: ThreadId,
/**
* Thread ID of the receiver.
*/
receiver_thread_id: ThreadId,
/**
* Last known status of the receiver agent reported to the sender agent after
* resume.
*/
status: AgentStatus, };

View File

@@ -17,8 +17,6 @@ import type { CollabAgentSpawnBeginEvent } from "./CollabAgentSpawnBeginEvent";
import type { CollabAgentSpawnEndEvent } from "./CollabAgentSpawnEndEvent";
import type { CollabCloseBeginEvent } from "./CollabCloseBeginEvent";
import type { CollabCloseEndEvent } from "./CollabCloseEndEvent";
import type { CollabResumeBeginEvent } from "./CollabResumeBeginEvent";
import type { CollabResumeEndEvent } from "./CollabResumeEndEvent";
import type { CollabWaitingBeginEvent } from "./CollabWaitingBeginEvent";
import type { CollabWaitingEndEvent } from "./CollabWaitingEndEvent";
import type { ContextCompactedEvent } from "./ContextCompactedEvent";
@@ -74,4 +72,4 @@ import type { WebSearchEndEvent } from "./WebSearchEndEvent";
* Response event from the agent
* NOTE: Make sure none of these values have optional types, as it will mess up the extension code-gen.
*/
export type EventMsg = { "type": "error" } & ErrorEvent | { "type": "warning" } & WarningEvent | { "type": "context_compacted" } & ContextCompactedEvent | { "type": "thread_rolled_back" } & ThreadRolledBackEvent | { "type": "task_started" } & TurnStartedEvent | { "type": "task_complete" } & TurnCompleteEvent | { "type": "token_count" } & TokenCountEvent | { "type": "agent_message" } & AgentMessageEvent | { "type": "user_message" } & UserMessageEvent | { "type": "agent_message_delta" } & AgentMessageDeltaEvent | { "type": "agent_reasoning" } & AgentReasoningEvent | { "type": "agent_reasoning_delta" } & AgentReasoningDeltaEvent | { "type": "agent_reasoning_raw_content" } & AgentReasoningRawContentEvent | { "type": "agent_reasoning_raw_content_delta" } & AgentReasoningRawContentDeltaEvent | { "type": "agent_reasoning_section_break" } & AgentReasoningSectionBreakEvent | { "type": "session_configured" } & SessionConfiguredEvent | { "type": "thread_name_updated" } & ThreadNameUpdatedEvent | { "type": "mcp_startup_update" } & McpStartupUpdateEvent | { "type": "mcp_startup_complete" } & McpStartupCompleteEvent | { "type": "mcp_tool_call_begin" } & McpToolCallBeginEvent | { "type": "mcp_tool_call_end" } & McpToolCallEndEvent | { "type": "web_search_begin" } & WebSearchBeginEvent | { "type": "web_search_end" } & WebSearchEndEvent | { "type": "exec_command_begin" } & ExecCommandBeginEvent | { "type": "exec_command_output_delta" } & ExecCommandOutputDeltaEvent | { "type": "terminal_interaction" } & TerminalInteractionEvent | { "type": "exec_command_end" } & ExecCommandEndEvent | { "type": "view_image_tool_call" } & ViewImageToolCallEvent | { "type": "exec_approval_request" } & ExecApprovalRequestEvent | { "type": "request_user_input" } & RequestUserInputEvent | { "type": "dynamic_tool_call_request" } & DynamicToolCallRequest | { "type": "elicitation_request" } & ElicitationRequestEvent | { "type": "apply_patch_approval_request" } & ApplyPatchApprovalRequestEvent | { "type": "deprecation_notice" } & DeprecationNoticeEvent | { "type": "background_event" } & BackgroundEventEvent | { "type": "undo_started" } & UndoStartedEvent | { "type": "undo_completed" } & UndoCompletedEvent | { "type": "stream_error" } & StreamErrorEvent | { "type": "patch_apply_begin" } & PatchApplyBeginEvent | { "type": "patch_apply_end" } & PatchApplyEndEvent | { "type": "turn_diff" } & TurnDiffEvent | { "type": "get_history_entry_response" } & GetHistoryEntryResponseEvent | { "type": "mcp_list_tools_response" } & McpListToolsResponseEvent | { "type": "list_custom_prompts_response" } & ListCustomPromptsResponseEvent | { "type": "list_skills_response" } & ListSkillsResponseEvent | { "type": "list_remote_skills_response" } & ListRemoteSkillsResponseEvent | { "type": "remote_skill_downloaded" } & RemoteSkillDownloadedEvent | { "type": "skills_update_available" } | { "type": "plan_update" } & UpdatePlanArgs | { "type": "turn_aborted" } & TurnAbortedEvent | { "type": "shutdown_complete" } | { "type": "entered_review_mode" } & ReviewRequest | { "type": "exited_review_mode" } & ExitedReviewModeEvent | { "type": "raw_response_item" } & RawResponseItemEvent | { "type": "item_started" } & ItemStartedEvent | { "type": "item_completed" } & ItemCompletedEvent | { "type": "agent_message_content_delta" } & AgentMessageContentDeltaEvent | { "type": "plan_delta" } & PlanDeltaEvent | { "type": "reasoning_content_delta" } & ReasoningContentDeltaEvent | { "type": "reasoning_raw_content_delta" } & ReasoningRawContentDeltaEvent | { "type": "collab_agent_spawn_begin" } & CollabAgentSpawnBeginEvent | { "type": "collab_agent_spawn_end" } & CollabAgentSpawnEndEvent | { "type": "collab_agent_interaction_begin" } & CollabAgentInteractionBeginEvent | { "type": "collab_agent_interaction_end" } & CollabAgentInteractionEndEvent | { "type": "collab_waiting_begin" } & CollabWaitingBeginEvent | { "type": "collab_waiting_end" } & CollabWaitingEndEvent | { "type": "collab_close_begin" } & CollabCloseBeginEvent | { "type": "collab_close_end" } & CollabCloseEndEvent | { "type": "collab_resume_begin" } & CollabResumeBeginEvent | { "type": "collab_resume_end" } & CollabResumeEndEvent;
export type EventMsg = { "type": "error" } & ErrorEvent | { "type": "warning" } & WarningEvent | { "type": "context_compacted" } & ContextCompactedEvent | { "type": "thread_rolled_back" } & ThreadRolledBackEvent | { "type": "task_started" } & TurnStartedEvent | { "type": "task_complete" } & TurnCompleteEvent | { "type": "token_count" } & TokenCountEvent | { "type": "agent_message" } & AgentMessageEvent | { "type": "user_message" } & UserMessageEvent | { "type": "agent_message_delta" } & AgentMessageDeltaEvent | { "type": "agent_reasoning" } & AgentReasoningEvent | { "type": "agent_reasoning_delta" } & AgentReasoningDeltaEvent | { "type": "agent_reasoning_raw_content" } & AgentReasoningRawContentEvent | { "type": "agent_reasoning_raw_content_delta" } & AgentReasoningRawContentDeltaEvent | { "type": "agent_reasoning_section_break" } & AgentReasoningSectionBreakEvent | { "type": "session_configured" } & SessionConfiguredEvent | { "type": "thread_name_updated" } & ThreadNameUpdatedEvent | { "type": "mcp_startup_update" } & McpStartupUpdateEvent | { "type": "mcp_startup_complete" } & McpStartupCompleteEvent | { "type": "mcp_tool_call_begin" } & McpToolCallBeginEvent | { "type": "mcp_tool_call_end" } & McpToolCallEndEvent | { "type": "web_search_begin" } & WebSearchBeginEvent | { "type": "web_search_end" } & WebSearchEndEvent | { "type": "exec_command_begin" } & ExecCommandBeginEvent | { "type": "exec_command_output_delta" } & ExecCommandOutputDeltaEvent | { "type": "terminal_interaction" } & TerminalInteractionEvent | { "type": "exec_command_end" } & ExecCommandEndEvent | { "type": "view_image_tool_call" } & ViewImageToolCallEvent | { "type": "exec_approval_request" } & ExecApprovalRequestEvent | { "type": "request_user_input" } & RequestUserInputEvent | { "type": "dynamic_tool_call_request" } & DynamicToolCallRequest | { "type": "elicitation_request" } & ElicitationRequestEvent | { "type": "apply_patch_approval_request" } & ApplyPatchApprovalRequestEvent | { "type": "deprecation_notice" } & DeprecationNoticeEvent | { "type": "background_event" } & BackgroundEventEvent | { "type": "undo_started" } & UndoStartedEvent | { "type": "undo_completed" } & UndoCompletedEvent | { "type": "stream_error" } & StreamErrorEvent | { "type": "patch_apply_begin" } & PatchApplyBeginEvent | { "type": "patch_apply_end" } & PatchApplyEndEvent | { "type": "turn_diff" } & TurnDiffEvent | { "type": "get_history_entry_response" } & GetHistoryEntryResponseEvent | { "type": "mcp_list_tools_response" } & McpListToolsResponseEvent | { "type": "list_custom_prompts_response" } & ListCustomPromptsResponseEvent | { "type": "list_skills_response" } & ListSkillsResponseEvent | { "type": "list_remote_skills_response" } & ListRemoteSkillsResponseEvent | { "type": "remote_skill_downloaded" } & RemoteSkillDownloadedEvent | { "type": "skills_update_available" } | { "type": "plan_update" } & UpdatePlanArgs | { "type": "turn_aborted" } & TurnAbortedEvent | { "type": "shutdown_complete" } | { "type": "entered_review_mode" } & ReviewRequest | { "type": "exited_review_mode" } & ExitedReviewModeEvent | { "type": "raw_response_item" } & RawResponseItemEvent | { "type": "item_started" } & ItemStartedEvent | { "type": "item_completed" } & ItemCompletedEvent | { "type": "agent_message_content_delta" } & AgentMessageContentDeltaEvent | { "type": "plan_delta" } & PlanDeltaEvent | { "type": "reasoning_content_delta" } & ReasoningContentDeltaEvent | { "type": "reasoning_raw_content_delta" } & ReasoningRawContentDeltaEvent | { "type": "collab_agent_spawn_begin" } & CollabAgentSpawnBeginEvent | { "type": "collab_agent_spawn_end" } & CollabAgentSpawnEndEvent | { "type": "collab_agent_interaction_begin" } & CollabAgentInteractionBeginEvent | { "type": "collab_agent_interaction_end" } & CollabAgentInteractionEndEvent | { "type": "collab_waiting_begin" } & CollabWaitingBeginEvent | { "type": "collab_waiting_end" } & CollabWaitingEndEvent | { "type": "collab_close_begin" } & CollabCloseBeginEvent | { "type": "collab_close_end" } & CollabCloseEndEvent;

View File

@@ -2,10 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Classifies an assistant message as interim commentary or final answer text.
*
* Providers do not emit this consistently, so callers must treat `None` as
* "phase unknown" and keep compatibility behavior for legacy models.
*/
export type MessagePhase = "commentary" | "final_answer";

View File

@@ -2,6 +2,7 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { RequestUserInputQuestion } from "./RequestUserInputQuestion";
import type { JsonValue } from "./serde_json/JsonValue";
export type RequestUserInputEvent = {
/**
@@ -12,4 +13,4 @@ call_id: string,
* Turn ID that this request belongs to.
* Uses `#[serde(default)]` for backwards compatibility.
*/
turn_id: string, questions: Array<RequestUserInputQuestion>, };
turn_id: string, tool: string | null, arguments: JsonValue | null, questions: Array<RequestUserInputQuestion>, };

View File

@@ -8,7 +8,6 @@ import type { AccountLoginCompletedNotification } from "./v2/AccountLoginComplet
import type { AccountRateLimitsUpdatedNotification } from "./v2/AccountRateLimitsUpdatedNotification";
import type { AccountUpdatedNotification } from "./v2/AccountUpdatedNotification";
import type { AgentMessageDeltaNotification } from "./v2/AgentMessageDeltaNotification";
import type { AppListUpdatedNotification } from "./v2/AppListUpdatedNotification";
import type { CommandExecutionOutputDeltaNotification } from "./v2/CommandExecutionOutputDeltaNotification";
import type { ConfigWarningNotification } from "./v2/ConfigWarningNotification";
import type { ContextCompactedNotification } from "./v2/ContextCompactedNotification";
@@ -37,4 +36,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/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "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": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification } | { "method": "authStatusChange", "params": AuthStatusChangeNotification } | { "method": "loginChatGptComplete", "params": LoginChatGptCompleteNotification } | { "method": "sessionConfigured", "params": SessionConfiguredNotification };
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification } | { "method": "authStatusChange", "params": AuthStatusChangeNotification } | { "method": "loginChatGptComplete", "params": LoginChatGptCompleteNotification } | { "method": "sessionConfigured", "params": SessionConfiguredNotification };

View File

@@ -37,8 +37,6 @@ export type { CollabAgentSpawnBeginEvent } from "./CollabAgentSpawnBeginEvent";
export type { CollabAgentSpawnEndEvent } from "./CollabAgentSpawnEndEvent";
export type { CollabCloseBeginEvent } from "./CollabCloseBeginEvent";
export type { CollabCloseEndEvent } from "./CollabCloseEndEvent";
export type { CollabResumeBeginEvent } from "./CollabResumeBeginEvent";
export type { CollabResumeEndEvent } from "./CollabResumeEndEvent";
export type { CollabWaitingBeginEvent } from "./CollabWaitingBeginEvent";
export type { CollabWaitingEndEvent } from "./CollabWaitingEndEvent";
export type { CollaborationMode } from "./CollaborationMode";

View File

@@ -1,5 +0,0 @@
// 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 AppDisabledReason = "unknown" | "user";

View File

@@ -2,7 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* EXPERIMENTAL - app metadata returned by app-list APIs.
*/
export type AppInfo = { id: string, name: string, description: string | null, logoUrl: string | null, logoUrlDark: string | null, distributionChannel: string | null, installUrl: string | null, isAccessible: boolean, };

View File

@@ -1,9 +0,0 @@
// 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 { AppInfo } from "./AppInfo";
/**
* EXPERIMENTAL - notification emitted when the app list changes.
*/
export type AppListUpdatedNotification = { data: Array<AppInfo>, };

View File

@@ -1,6 +0,0 @@
// 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 { AppDisabledReason } from "./AppDisabledReason";
export type AppsConfig = { [key in string]?: { enabled: boolean, disabled_reason: AppDisabledReason | null, } };

View File

@@ -2,9 +2,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* EXPERIMENTAL - list available apps/connectors.
*/
export type AppsListParams = {
/**
* Opaque pagination cursor returned by a previous call.
@@ -13,8 +10,4 @@ cursor?: string | null,
/**
* Optional page size; defaults to a reasonable server-side value.
*/
limit?: number | null,
/**
* When true, bypass app caches and fetch the latest data from sources.
*/
forceRefetch?: boolean, };
limit?: number | null, };

View File

@@ -3,9 +3,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AppInfo } from "./AppInfo";
/**
* EXPERIMENTAL - app list response.
*/
export type AppsListResponse = { data: Array<AppInfo>,
/**
* Opaque cursor to pass to the next call to continue after the last item.

View File

@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type CollabAgentTool = "spawnAgent" | "sendInput" | "resumeAgent" | "wait" | "closeAgent";
export type CollabAgentTool = "spawnAgent" | "sendInput" | "wait" | "closeAgent";

View File

@@ -14,4 +14,4 @@ import type { SandboxMode } from "./SandboxMode";
import type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite";
import type { ToolsV2 } from "./ToolsV2";
export type Config = {model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, analytics: AnalyticsConfig | null} & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
export type Config = { model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, analytics: AnalyticsConfig | null, } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });

View File

@@ -1,9 +1,8 @@
// 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 { WebSearchMode } from "../WebSearchMode";
import type { AskForApproval } from "./AskForApproval";
import type { ResidencyRequirement } from "./ResidencyRequirement";
import type { SandboxMode } from "./SandboxMode";
export type ConfigRequirements = {allowedApprovalPolicies: Array<AskForApproval> | null, allowedSandboxModes: Array<SandboxMode> | null, allowedWebSearchModes: Array<WebSearchMode> | null, enforceResidency: ResidencyRequirement | null};
export type ConfigRequirements = { allowedApprovalPolicies: Array<AskForApproval> | null, allowedSandboxModes: Array<SandboxMode> | null, enforceResidency: ResidencyRequirement | null, };

View File

@@ -1,37 +0,0 @@
// 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 { ExperimentalFeatureStage } from "./ExperimentalFeatureStage";
export type ExperimentalFeature = {
/**
* Stable key used in config.toml and CLI flag toggles.
*/
name: string,
/**
* Lifecycle stage of this feature flag.
*/
stage: ExperimentalFeatureStage,
/**
* User-facing display name shown in the experimental features UI.
* Null when this feature is not in beta.
*/
displayName: string | null,
/**
* Short summary describing what the feature does.
* Null when this feature is not in beta.
*/
description: string | null,
/**
* Announcement copy shown to users when the feature is introduced.
* Null when this feature is not in beta.
*/
announcement: string | null,
/**
* Whether this feature is currently enabled in the loaded config.
*/
enabled: boolean,
/**
* Whether this feature is enabled by default.
*/
defaultEnabled: boolean, };

View File

@@ -1,13 +0,0 @@
// 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 ExperimentalFeatureListParams = {
/**
* Opaque pagination cursor returned by a previous call.
*/
cursor?: string | null,
/**
* Optional page size; defaults to a reasonable server-side value.
*/
limit?: number | null, };

View File

@@ -1,11 +0,0 @@
// 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 { ExperimentalFeature } from "./ExperimentalFeature";
export type ExperimentalFeatureListResponse = { data: Array<ExperimentalFeature>,
/**
* Opaque cursor to pass to the next call to continue after the last item.
* If None, there are no more items to return.
*/
nextCursor: string | null, };

View File

@@ -1,5 +0,0 @@
// 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 ExperimentalFeatureStage = "beta" | "underDevelopment" | "stable" | "deprecated" | "removed";

View File

@@ -1,5 +0,0 @@
// 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 NetworkRequirements = { enabled: boolean | null, httpPort: number | null, socksPort: number | null, allowUpstreamProxy: boolean | null, dangerouslyAllowNonLoopbackProxy: boolean | null, dangerouslyAllowNonLoopbackAdmin: boolean | null, allowedDomains: Array<string> | null, deniedDomains: Array<string> | null, allowUnixSockets: Array<string> | null, allowLocalBinding: boolean | null, };

View File

@@ -1,5 +0,0 @@
// 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 SkillsListExtraRootsForCwd = { cwd: string, extraUserRoots: Array<string>, };

View File

@@ -1,7 +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 { SkillsListExtraRootsForCwd } from "./SkillsListExtraRootsForCwd";
export type SkillsListParams = {
/**
@@ -11,8 +10,4 @@ cwds?: Array<string>,
/**
* When true, bypass the skills cache and re-scan skills from disk.
*/
forceReload?: boolean,
/**
* Optional per-cwd extra roots to scan as user-scoped skills.
*/
perCwdExtraUserRoots?: Array<SkillsListExtraRootsForCwd> | null, };
forceReload?: boolean, };

View File

@@ -14,11 +14,13 @@ import type { SandboxMode } from "./SandboxMode";
*
* Prefer using thread_id whenever possible.
*/
export type ThreadForkParams = {threadId: string, /**
export type ThreadForkParams = { threadId: string,
/**
* [UNSTABLE] Specify the rollout path to fork from.
* If specified, the thread_id param will be ignored.
*/
path?: string | null, /**
path?: string | null,
/**
* Configuration overrides for the forked thread, if any.
*/
model?: string | null, modelProvider?: string | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null};
model?: string | null, modelProvider?: string | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, };

View File

@@ -18,16 +18,19 @@ import type { SandboxMode } from "./SandboxMode";
*
* Prefer using thread_id whenever possible.
*/
export type ThreadResumeParams = {threadId: string, /**
export type ThreadResumeParams = { threadId: string,
/**
* [UNSTABLE] FOR CODEX CLOUD - DO NOT USE.
* If specified, the thread will be resumed with the provided history
* instead of loaded from disk.
*/
history?: Array<ResponseItem> | null, /**
history?: Array<ResponseItem> | null,
/**
* [UNSTABLE] Specify the rollout path to resume from.
* If specified, the thread_id param will be ignored.
*/
path?: string | null, /**
path?: string | null,
/**
* Configuration overrides for the resumed thread, if any.
*/
model?: string | null, modelProvider?: string | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null};
model?: string | null, modelProvider?: string | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, };

View File

@@ -7,7 +7,9 @@ import type { AskForApproval } from "./AskForApproval";
import type { SandboxMode } from "./SandboxMode";
export type ThreadStartParams = {model?: string | null, modelProvider?: string | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, /**
* If true, opt into emitting raw Responses API items on the event stream.
* If true, opt into emitting raw response items on the event stream.
*
* This is for internal use only (e.g. Codex Cloud).
* (TODO): Figure out a better way to categorize internal / experimental events & protocols.
*/
experimentalRawEvents: boolean};

View File

@@ -1,9 +1,10 @@
// 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 { JsonValue } from "../serde_json/JsonValue";
import type { ToolRequestUserInputQuestion } from "./ToolRequestUserInputQuestion";
/**
* EXPERIMENTAL. Params sent with a request_user_input event.
*/
export type ToolRequestUserInputParams = { threadId: string, turnId: string, itemId: string, questions: Array<ToolRequestUserInputQuestion>, };
export type ToolRequestUserInputParams = { threadId: string, turnId: string, itemId: string, tool: string | null, arguments: JsonValue | null, questions: Array<ToolRequestUserInputQuestion>, };

View File

@@ -10,35 +10,41 @@ import type { AskForApproval } from "./AskForApproval";
import type { SandboxPolicy } from "./SandboxPolicy";
import type { UserInput } from "./UserInput";
export type TurnStartParams = {threadId: string, input: Array<UserInput>, /**
export type TurnStartParams = { threadId: string, input: Array<UserInput>,
/**
* Override the working directory for this turn and subsequent turns.
*/
cwd?: string | null, /**
cwd?: string | null,
/**
* Override the approval policy for this turn and subsequent turns.
*/
approvalPolicy?: AskForApproval | null, /**
approvalPolicy?: AskForApproval | null,
/**
* Override the sandbox policy for this turn and subsequent turns.
*/
sandboxPolicy?: SandboxPolicy | null, /**
sandboxPolicy?: SandboxPolicy | null,
/**
* Override the model for this turn and subsequent turns.
*/
model?: string | null, /**
model?: string | null,
/**
* Override the reasoning effort for this turn and subsequent turns.
*/
effort?: ReasoningEffort | null, /**
effort?: ReasoningEffort | null,
/**
* Override the reasoning summary for this turn and subsequent turns.
*/
summary?: ReasoningSummary | null, /**
summary?: ReasoningSummary | null,
/**
* Override the personality for this turn and subsequent turns.
*/
personality?: Personality | null, /**
personality?: Personality | null,
/**
* Optional JSON Schema used to constrain the final assistant message for this turn.
*/
outputSchema?: JsonValue | null, /**
* EXPERIMENTAL - Set a pre-set collaboration mode.
outputSchema?: JsonValue | null,
/**
* EXPERIMENTAL - set a pre-set collaboration mode.
* Takes precedence over model, reasoning_effort, and developer instructions if set.
*
* For `collaboration_mode.settings.developer_instructions`, `null` means
* "use the built-in instructions for the selected mode".
*/
collaborationMode?: CollaborationMode | null};
collaborationMode?: CollaborationMode | null, };

View File

@@ -1,11 +0,0 @@
// 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 { UserInput } from "./UserInput";
export type TurnSteerParams = { threadId: string, input: Array<UserInput>,
/**
* Required active turn id precondition. The request fails when it does not
* match the currently active turn.
*/
expectedTurnId: string, };

View File

@@ -1,5 +0,0 @@
// 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 TurnSteerResponse = { turnId: string, };

View File

@@ -6,10 +6,7 @@ export type { AccountRateLimitsUpdatedNotification } from "./AccountRateLimitsUp
export type { AccountUpdatedNotification } from "./AccountUpdatedNotification";
export type { AgentMessageDeltaNotification } from "./AgentMessageDeltaNotification";
export type { AnalyticsConfig } from "./AnalyticsConfig";
export type { AppDisabledReason } from "./AppDisabledReason";
export type { AppInfo } from "./AppInfo";
export type { AppListUpdatedNotification } from "./AppListUpdatedNotification";
export type { AppsConfig } from "./AppsConfig";
export type { AppsListParams } from "./AppsListParams";
export type { AppsListResponse } from "./AppsListResponse";
export type { AskForApproval } from "./AskForApproval";
@@ -55,10 +52,6 @@ export type { DynamicToolCallResponse } from "./DynamicToolCallResponse";
export type { DynamicToolSpec } from "./DynamicToolSpec";
export type { ErrorNotification } from "./ErrorNotification";
export type { ExecPolicyAmendment } from "./ExecPolicyAmendment";
export type { ExperimentalFeature } from "./ExperimentalFeature";
export type { ExperimentalFeatureListParams } from "./ExperimentalFeatureListParams";
export type { ExperimentalFeatureListResponse } from "./ExperimentalFeatureListResponse";
export type { ExperimentalFeatureStage } from "./ExperimentalFeatureStage";
export type { FeedbackUploadParams } from "./FeedbackUploadParams";
export type { FeedbackUploadResponse } from "./FeedbackUploadResponse";
export type { FileChangeApprovalDecision } from "./FileChangeApprovalDecision";
@@ -92,7 +85,6 @@ export type { Model } from "./Model";
export type { ModelListParams } from "./ModelListParams";
export type { ModelListResponse } from "./ModelListResponse";
export type { NetworkAccess } from "./NetworkAccess";
export type { NetworkRequirements } from "./NetworkRequirements";
export type { OverriddenMetadata } from "./OverriddenMetadata";
export type { PatchApplyStatus } from "./PatchApplyStatus";
export type { PatchChangeKind } from "./PatchChangeKind";
@@ -124,7 +116,6 @@ export type { SkillToolDependency } from "./SkillToolDependency";
export type { SkillsConfigWriteParams } from "./SkillsConfigWriteParams";
export type { SkillsConfigWriteResponse } from "./SkillsConfigWriteResponse";
export type { SkillsListEntry } from "./SkillsListEntry";
export type { SkillsListExtraRootsForCwd } from "./SkillsListExtraRootsForCwd";
export type { SkillsListParams } from "./SkillsListParams";
export type { SkillsListResponse } from "./SkillsListResponse";
export type { SkillsRemoteReadParams } from "./SkillsRemoteReadParams";
@@ -185,8 +176,6 @@ export type { TurnStartParams } from "./TurnStartParams";
export type { TurnStartResponse } from "./TurnStartResponse";
export type { TurnStartedNotification } from "./TurnStartedNotification";
export type { TurnStatus } from "./TurnStatus";
export type { TurnSteerParams } from "./TurnSteerParams";
export type { TurnSteerResponse } from "./TurnSteerResponse";
export type { UserInput } from "./UserInput";
export type { WebSearchAction } from "./WebSearchAction";
export type { WindowsWorldWritableWarningNotification } from "./WindowsWorldWritableWarningNotification";

View File

@@ -238,10 +238,7 @@ fn filter_client_request_ts(out_dir: &Path, experimental_methods: &[&str]) -> Re
.collect();
let new_body = filtered_arms.join(" | ");
content = format!("{prefix}{new_body}{suffix}");
let import_usage_scope = split_type_alias(&content)
.map(|(_, body, _)| body)
.unwrap_or_else(|| new_body.clone());
content = prune_unused_type_imports(content, &import_usage_scope);
content = prune_unused_type_imports(content, &new_body);
fs::write(&path, content).with_context(|| format!("Failed to write {}", path.display()))?;
Ok(())
@@ -299,10 +296,7 @@ fn filter_experimental_fields_in_ts_file(
let prefix = &content[..open_brace + 1];
let suffix = &content[close_brace..];
content = format!("{prefix}{new_inner}{suffix}");
let import_usage_scope = split_type_alias(&content)
.map(|(_, body, _)| body)
.unwrap_or_else(|| new_inner.clone());
content = prune_unused_type_imports(content, &import_usage_scope);
content = prune_unused_type_imports(content, &new_inner);
fs::write(path, content).with_context(|| format!("Failed to write {}", path.display()))?;
Ok(())
}
@@ -1751,50 +1745,6 @@ mod tests {
Ok(())
}
#[test]
fn experimental_type_fields_ts_filter_keeps_imports_used_in_intersection_suffix() -> Result<()>
{
let output_dir = std::env::temp_dir().join(format!("codex_ts_filter_{}", Uuid::now_v7()));
fs::create_dir_all(&output_dir)?;
struct TempDirGuard(PathBuf);
impl Drop for TempDirGuard {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
let _guard = TempDirGuard(output_dir.clone());
let path = output_dir.join("Config.ts");
let content = r#"import type { JsonValue } from "../serde_json/JsonValue";
import type { Keep } from "./Keep";
export type Config = { stableField: Keep, unstableField: string | null } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
"#;
fs::write(&path, content)?;
static CUSTOM_FIELD: crate::experimental_api::ExperimentalField =
crate::experimental_api::ExperimentalField {
type_name: "Config",
field_name: "unstableField",
reason: "custom/unstableField",
};
filter_experimental_type_fields_ts(&output_dir, &[&CUSTOM_FIELD])?;
let filtered = fs::read_to_string(&path)?;
assert_eq!(filtered.contains("unstableField"), false);
assert_eq!(
filtered.contains(r#"import type { JsonValue } from "../serde_json/JsonValue";"#),
true
);
assert_eq!(
filtered.contains(r#"import type { Keep } from "./Keep";"#),
true
);
Ok(())
}
#[test]
fn stable_schema_filter_removes_mock_experimental_method() -> Result<()> {
let output_dir = std::env::temp_dir().join(format!("codex_schema_{}", Uuid::now_v7()));

View File

@@ -190,12 +190,10 @@ client_request_definitions! {
},
ThreadResume => "thread/resume" {
params: v2::ThreadResumeParams,
inspect_params: true,
response: v2::ThreadResumeResponse,
},
ThreadFork => "thread/fork" {
params: v2::ThreadForkParams,
inspect_params: true,
response: v2::ThreadForkResponse,
},
ThreadArchive => "thread/archive" {
@@ -214,11 +212,6 @@ client_request_definitions! {
params: v2::ThreadCompactStartParams,
response: v2::ThreadCompactStartResponse,
},
#[experimental("thread/backgroundTerminals/clean")]
ThreadBackgroundTerminalsClean => "thread/backgroundTerminals/clean" {
params: v2::ThreadBackgroundTerminalsCleanParams,
response: v2::ThreadBackgroundTerminalsCleanResponse,
},
ThreadRollback => "thread/rollback" {
params: v2::ThreadRollbackParams,
response: v2::ThreadRollbackResponse,
@@ -257,13 +250,8 @@ client_request_definitions! {
},
TurnStart => "turn/start" {
params: v2::TurnStartParams,
inspect_params: true,
response: v2::TurnStartResponse,
},
TurnSteer => "turn/steer" {
params: v2::TurnSteerParams,
response: v2::TurnSteerResponse,
},
TurnInterrupt => "turn/interrupt" {
params: v2::TurnInterruptParams,
response: v2::TurnInterruptResponse,
@@ -277,10 +265,6 @@ client_request_definitions! {
params: v2::ModelListParams,
response: v2::ModelListResponse,
},
ExperimentalFeatureList => "experimentalFeature/list" {
params: v2::ExperimentalFeatureListParams,
response: v2::ExperimentalFeatureListResponse,
},
#[experimental("collaborationMode/list")]
/// Lists collaboration mode presets.
CollaborationModeList => "collaborationMode/list" {
@@ -311,7 +295,6 @@ client_request_definitions! {
LoginAccount => "account/login/start" {
params: v2::LoginAccountParams,
inspect_params: true,
response: v2::LoginAccountResponse,
},
@@ -726,7 +709,6 @@ server_notification_definitions! {
McpServerOauthLoginCompleted => "mcpServer/oauthLogin/completed" (v2::McpServerOauthLoginCompletedNotification),
AccountUpdated => "account/updated" (v2::AccountUpdatedNotification),
AccountRateLimitsUpdated => "account/rateLimits/updated" (v2::AccountRateLimitsUpdatedNotification),
AppListUpdated => "app/list/updated" (v2::AppListUpdatedNotification),
ReasoningSummaryTextDelta => "item/reasoning/summaryTextDelta" (v2::ReasoningSummaryTextDeltaNotification),
ReasoningSummaryPartAdded => "item/reasoning/summaryPartAdded" (v2::ReasoningSummaryPartAddedNotification),
ReasoningTextDelta => "item/reasoning/textDelta" (v2::ReasoningTextDeltaNotification),
@@ -1105,47 +1087,6 @@ mod tests {
Ok(())
}
#[test]
fn serialize_list_experimental_features() -> Result<()> {
let request = ClientRequest::ExperimentalFeatureList {
request_id: RequestId::Integer(8),
params: v2::ExperimentalFeatureListParams::default(),
};
assert_eq!(
json!({
"method": "experimentalFeature/list",
"id": 8,
"params": {
"cursor": null,
"limit": null
}
}),
serde_json::to_value(&request)?,
);
Ok(())
}
#[test]
fn serialize_thread_background_terminals_clean() -> Result<()> {
let request = ClientRequest::ThreadBackgroundTerminalsClean {
request_id: RequestId::Integer(8),
params: v2::ThreadBackgroundTerminalsCleanParams {
thread_id: "thr_123".to_string(),
},
};
assert_eq!(
json!({
"method": "thread/backgroundTerminals/clean",
"id": 8,
"params": {
"threadId": "thr_123"
}
}),
serde_json::to_value(&request)?,
);
Ok(())
}
#[test]
fn mock_experimental_method_is_marked_experimental() {
let request = ClientRequest::MockExperimentalMethod {

View File

@@ -377,36 +377,6 @@ pub struct AnalyticsConfig {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
pub enum AppDisabledReason {
Unknown,
User,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
pub struct AppConfig {
#[serde(default = "default_enabled")]
pub enabled: bool,
pub disabled_reason: Option<AppDisabledReason>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
pub struct AppsConfig {
#[serde(default, flatten)]
#[schemars(with = "HashMap<String, AppConfig>")]
pub apps: HashMap<String, AppConfig>,
}
const fn default_enabled() -> bool {
true
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
pub struct Config {
pub model: Option<String>,
pub review_model: Option<String>,
@@ -430,9 +400,6 @@ pub struct Config {
pub model_reasoning_summary: Option<ReasoningSummary>,
pub model_verbosity: Option<Verbosity>,
pub analytics: Option<AnalyticsConfig>,
#[experimental("config/read.apps")]
#[serde(default)]
pub apps: Option<AppsConfig>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
@@ -527,32 +494,13 @@ pub struct ConfigReadResponse {
pub layers: Option<Vec<ConfigLayer>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ConfigRequirements {
pub allowed_approval_policies: Option<Vec<AskForApproval>>,
pub allowed_sandbox_modes: Option<Vec<SandboxMode>>,
pub allowed_web_search_modes: Option<Vec<WebSearchMode>>,
pub enforce_residency: Option<ResidencyRequirement>,
#[experimental("configRequirements/read.network")]
pub network: Option<NetworkRequirements>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct NetworkRequirements {
pub enabled: Option<bool>,
pub http_port: Option<u16>,
pub socks_port: Option<u16>,
pub allow_upstream_proxy: Option<bool>,
pub dangerously_allow_non_loopback_proxy: Option<bool>,
pub dangerously_allow_non_loopback_admin: Option<bool>,
pub allowed_domains: Option<Vec<String>>,
pub denied_domains: Option<Vec<String>>,
pub allow_unix_sockets: Option<Vec<String>>,
pub allow_local_binding: Option<bool>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
@@ -887,7 +835,7 @@ pub enum Account {
Chatgpt { email: String, plan_type: PlanType },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(tag = "type")]
#[ts(tag = "type")]
#[ts(export_to = "v2/")]
@@ -904,7 +852,6 @@ pub enum LoginAccountParams {
Chatgpt,
/// [UNSTABLE] FOR OPENAI INTERNAL USE ONLY - DO NOT USE.
/// The access token must contain the same scopes that Codex-managed ChatGPT auth tokens have.
#[experimental("account/login/start.chatgptAuthTokens")]
#[serde(rename = "chatgptAuthTokens")]
#[ts(rename = "chatgptAuthTokens")]
ChatgptAuthTokens {
@@ -1096,67 +1043,6 @@ pub struct CollaborationModeListResponse {
pub data: Vec<CollaborationModeMask>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ExperimentalFeatureListParams {
/// Opaque pagination cursor returned by a previous call.
#[ts(optional = nullable)]
pub cursor: Option<String>,
/// Optional page size; defaults to a reasonable server-side value.
#[ts(optional = nullable)]
pub limit: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub enum ExperimentalFeatureStage {
/// Feature is available for user testing and feedback.
Beta,
/// Feature is still being built and not ready for broad use.
UnderDevelopment,
/// Feature is production-ready.
Stable,
/// Feature is deprecated and should be avoided.
Deprecated,
/// Feature flag is retained only for backwards compatibility.
Removed,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ExperimentalFeature {
/// Stable key used in config.toml and CLI flag toggles.
pub name: String,
/// Lifecycle stage of this feature flag.
pub stage: ExperimentalFeatureStage,
/// User-facing display name shown in the experimental features UI.
/// Null when this feature is not in beta.
pub display_name: Option<String>,
/// Short summary describing what the feature does.
/// Null when this feature is not in beta.
pub description: Option<String>,
/// Announcement copy shown to users when the feature is introduced.
/// Null when this feature is not in beta.
pub announcement: Option<String>,
/// Whether this feature is currently enabled in the loaded config.
pub enabled: bool,
/// Whether this feature is enabled by default.
pub default_enabled: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ExperimentalFeatureListResponse {
pub data: Vec<ExperimentalFeature>,
/// Opaque cursor to pass to the next call to continue after the last item.
/// If None, there are no more items to return.
pub next_cursor: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -1193,7 +1079,6 @@ pub struct ListMcpServerStatusResponse {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
/// EXPERIMENTAL - list available apps/connectors.
pub struct AppsListParams {
/// Opaque pagination cursor returned by a previous call.
#[ts(optional = nullable)]
@@ -1201,15 +1086,11 @@ pub struct AppsListParams {
/// Optional page size; defaults to a reasonable server-side value.
#[ts(optional = nullable)]
pub limit: Option<u32>,
/// When true, bypass app caches and fetch the latest data from sources.
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub force_refetch: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
/// EXPERIMENTAL - app metadata returned by app-list APIs.
pub struct AppInfo {
pub id: String,
pub name: String,
@@ -1225,7 +1106,6 @@ pub struct AppInfo {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
/// EXPERIMENTAL - app list response.
pub struct AppsListResponse {
pub data: Vec<AppInfo>,
/// Opaque cursor to pass to the next call to continue after the last item.
@@ -1233,14 +1113,6 @@ pub struct AppsListResponse {
pub next_cursor: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
/// EXPERIMENTAL - notification emitted when the app list changes.
pub struct AppListUpdatedNotification {
pub data: Vec<AppInfo>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -1349,9 +1221,10 @@ pub struct ThreadStartParams {
#[experimental("thread/start.mockExperimentalField")]
#[ts(optional = nullable)]
pub mock_experimental_field: Option<String>,
/// If true, opt into emitting raw Responses API items on the event stream.
/// If true, opt into emitting raw response items on the event stream.
///
/// This is for internal use only (e.g. Codex Cloud).
#[experimental("thread/start.experimentalRawEvents")]
/// (TODO): Figure out a better way to categorize internal / experimental events & protocols.
#[serde(default)]
pub experimental_raw_events: bool,
}
@@ -1386,9 +1259,7 @@ pub struct ThreadStartResponse {
pub reasoning_effort: Option<ReasoningEffort>,
}
#[derive(
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
)]
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
/// There are three ways to resume a thread:
@@ -1406,13 +1277,11 @@ pub struct ThreadResumeParams {
/// [UNSTABLE] FOR CODEX CLOUD - DO NOT USE.
/// If specified, the thread will be resumed with the provided history
/// instead of loaded from disk.
#[experimental("thread/resume.history")]
#[ts(optional = nullable)]
pub history: Option<Vec<ResponseItem>>,
/// [UNSTABLE] Specify the rollout path to resume from.
/// If specified, the thread_id param will be ignored.
#[experimental("thread/resume.path")]
#[ts(optional = nullable)]
pub path: Option<PathBuf>,
@@ -1450,9 +1319,7 @@ pub struct ThreadResumeResponse {
pub reasoning_effort: Option<ReasoningEffort>,
}
#[derive(
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
)]
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
/// There are two ways to fork a thread:
@@ -1467,7 +1334,6 @@ pub struct ThreadForkParams {
/// [UNSTABLE] Specify the rollout path to fork from.
/// If specified, the thread_id param will be ignored.
#[experimental("thread/fork.path")]
#[ts(optional = nullable)]
pub path: Option<PathBuf>,
@@ -1554,18 +1420,6 @@ 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 ThreadBackgroundTerminalsCleanParams {
pub thread_id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadBackgroundTerminalsCleanResponse {}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -1704,19 +1558,6 @@ pub struct SkillsListParams {
/// When true, bypass the skills cache and re-scan skills from disk.
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub force_reload: bool,
/// Optional per-cwd extra roots to scan as user-scoped skills.
#[serde(default)]
#[ts(optional = nullable)]
pub per_cwd_extra_user_roots: Option<Vec<SkillsListExtraRootsForCwd>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct SkillsListExtraRootsForCwd {
pub cwd: PathBuf,
pub extra_user_roots: Vec<PathBuf>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -2093,9 +1934,7 @@ pub enum TurnStatus {
}
// Turn APIs
#[derive(
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
)]
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct TurnStartParams {
@@ -2126,12 +1965,8 @@ pub struct TurnStartParams {
#[ts(optional = nullable)]
pub output_schema: Option<JsonValue>,
/// EXPERIMENTAL - Set a pre-set collaboration mode.
/// EXPERIMENTAL - set a pre-set collaboration mode.
/// Takes precedence over model, reasoning_effort, and developer instructions if set.
///
/// For `collaboration_mode.settings.developer_instructions`, `null` means
/// "use the built-in instructions for the selected mode".
#[experimental("turn/start.collaborationMode")]
#[ts(optional = nullable)]
pub collaboration_mode: Option<CollaborationMode>,
}
@@ -2196,24 +2031,6 @@ pub struct TurnStartResponse {
pub turn: Turn,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct TurnSteerParams {
pub thread_id: String,
pub input: Vec<UserInput>,
/// Required active turn id precondition. The request fails when it does not
/// match the currently active turn.
pub expected_turn_id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct TurnSteerResponse {
pub turn_id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -2560,7 +2377,6 @@ pub enum CommandExecutionStatus {
pub enum CollabAgentTool {
SpawnAgent,
SendInput,
ResumeAgent,
Wait,
CloseAgent,
}
@@ -3070,6 +2886,8 @@ pub struct ToolRequestUserInputParams {
pub thread_id: String,
pub turn_id: String,
pub item_id: String,
pub tool: Option<String>,
pub arguments: Option<JsonValue>,
pub questions: Vec<ToolRequestUserInputQuestion>,
}
@@ -3311,7 +3129,6 @@ mod tests {
text: "world".to_string(),
},
],
phase: None,
});
assert_eq!(
@@ -3365,36 +3182,20 @@ mod tests {
serde_json::to_value(SkillsListParams {
cwds: Vec::new(),
force_reload: false,
per_cwd_extra_user_roots: None,
})
.unwrap(),
json!({
"perCwdExtraUserRoots": null,
}),
json!({}),
);
assert_eq!(
serde_json::to_value(SkillsListParams {
cwds: vec![PathBuf::from("/repo")],
force_reload: true,
per_cwd_extra_user_roots: Some(vec![SkillsListExtraRootsForCwd {
cwd: PathBuf::from("/repo"),
extra_user_roots: vec![
PathBuf::from("/shared/skills"),
PathBuf::from("/tmp/x")
],
}]),
})
.unwrap(),
json!({
"cwds": ["/repo"],
"forceReload": true,
"perCwdExtraUserRoots": [
{
"cwd": "/repo",
"extraUserRoots": ["/shared/skills", "/tmp/x"],
}
],
}),
);
}

View File

@@ -33,9 +33,6 @@ codex-rmcp-client = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-json-to-toml = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive"] }
futures = { workspace = true }
owo-colors = { workspace = true, features = ["supports-colors"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tempfile = { workspace = true }
@@ -48,7 +45,6 @@ tokio = { workspace = true, features = [
"rt-multi-thread",
"signal",
] }
tokio-tungstenite = { workspace = true }
tracing = { workspace = true, features = ["log"] }
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
uuid = { workspace = true, features = ["serde", "v7"] }
@@ -63,7 +59,6 @@ axum = { workspace = true, default-features = false, features = [
base64 = { workspace = true }
codex-execpolicy = { workspace = true }
core_test_support = { workspace = true }
codex-utils-cargo-bin = { workspace = true }
os_info = { workspace = true }
pretty_assertions = { workspace = true }
rmcp = { workspace = true, default-features = false, features = [
@@ -71,6 +66,5 @@ rmcp = { workspace = true, default-features = false, features = [
"transport-streamable-http-server",
] }
serial_test = { workspace = true }
tokio-tungstenite = { workspace = true }
wiremock = { workspace = true }
shlex = { workspace = true }

View File

@@ -19,14 +19,7 @@
## Protocol
Similar to [MCP](https://modelcontextprotocol.io/), `codex app-server` supports bidirectional communication using JSON-RPC 2.0 messages (with the `"jsonrpc":"2.0"` header omitted on the wire).
Supported transports:
- stdio (`--listen stdio://`, default): newline-delimited JSON (JSONL)
- websocket (`--listen ws://IP:PORT`): one JSON-RPC message per websocket text frame (**experimental / unsupported**)
Websocket transport is currently experimental and unsupported. Do not rely on it for production workloads.
Similar to [MCP](https://modelcontextprotocol.io/), `codex app-server` supports bidirectional communication, streaming JSONL over stdio. The protocol is JSON-RPC 2.0, though the `"jsonrpc":"2.0"` header is omitted.
## Message Schema
@@ -49,7 +42,7 @@ Use the thread APIs to create, list, or archive conversations. Drive a conversat
## Lifecycle Overview
- Initialize once per connection: Immediately after opening a transport connection, send an `initialize` request with your client metadata, then emit an `initialized` notification. Any other request on that connection before this handshake gets rejected.
- Initialize once: Immediately after launching the codex app-server process, send an `initialize` request with your client metadata, then emit an `initialized` notification. Any other request before this handshake gets rejected.
- Start (or resume) a thread: Call `thread/start` to open a fresh conversation. The response returns the thread object and youll also get a `thread/started` notification. If youre continuing an existing conversation, call `thread/resume` with its ID instead. If you want to branch from an existing conversation, call `thread/fork` to create a new thread id with copied history.
- Begin a turn: To send user input, call `turn/start` with the target `threadId` and the user's input. Optional fields let you override model, cwd, sandbox policy, etc. This immediately returns the new turn object and triggers a `turn/started` notification.
- Stream events: After `turn/start`, keep reading JSON-RPC notifications on stdout. Youll see `item/started`, `item/completed`, deltas like `item/agentMessage/delta`, tool progress, etc. These represent streaming model output plus any side effects (commands, tool calls, reasoning notes).
@@ -57,7 +50,7 @@ Use the thread APIs to create, list, or archive conversations. Drive a conversat
## Initialization
Clients must send a single `initialize` request per transport connection before invoking any other method on that connection, then acknowledge with an `initialized` notification. The server returns the user agent string it will present to upstream services; subsequent requests issued before initialization receive a `"Not initialized"` error, and repeated `initialize` calls on the same connection receive an `"Already initialized"` error.
Clients must send a single `initialize` request before invoking any other method, then acknowledge with an `initialized` notification. The server returns the user agent string it will present to upstream services; subsequent requests issued before initialization receive a `"Not initialized"` error, and repeated `initialize` calls receive an `"Already initialized"` error.
Applications building on top of `codex app-server` should identify themselves via the `clientInfo` parameter.
@@ -93,15 +86,12 @@ Example (from OpenAI's official VSCode extension):
- `thread/name/set` — set or update a threads user-facing name; returns `{}` on success. 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.
- `thread/compact/start` — trigger conversation history compaction for a thread; returns `{}` immediately while progress streams through standard turn/item notifications.
- `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/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.
- `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"`.
- `review/start` — kick off Codexs automated reviewer for a thread; responds like `turn/start` and emits `item/started`/`item/completed` notifications with `enteredReviewMode` and `exitedReviewMode` items, plus a final assistant `agentMessage` containing the review.
- `command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation).
- `model/list` — list available models (with reasoning effort options and optional `upgrade` model ids).
- `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).
- `skills/list` — list skills for one or more `cwd` values (optional `forceReload`).
- `skills/remote/read` — list public remote skills (**under development; do not call from production clients yet**).
@@ -109,7 +99,7 @@ Example (from OpenAI's official VSCode extension):
- `app/list` — list available apps.
- `skills/config/write` — write user-level skill config by path.
- `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).
- `tool/requestUserInput` — prompt the user with 13 short questions for a tool call and return their answers (experimental; may include optional `tool` and JSON `arguments` context when available).
- `config/mcpServer/reload` — reload MCP server config from disk and queue a refresh for loaded threads (applied on each thread's next active turn); returns `{}`. Use this after editing `config.toml` without restarting the server.
- `mcpServerStatus/list` — enumerate configured MCP servers with their tools, resources, resource templates, and auth status; supports cursor+limit pagination.
- `feedback/upload` — submit a feedback report (classification + optional reason/logs and conversation_id); returns the tracking thread id.
@@ -117,7 +107,7 @@ Example (from OpenAI's official VSCode extension):
- `config/read` — fetch the effective config on disk after resolving config layering.
- `config/value/write` — write a single config key/value to the user's config.toml on disk.
- `config/batchWrite` — apply multiple config edits atomically to the user's config.toml on disk.
- `configRequirements/read` — fetch loaded requirements constraints from `requirements.toml` and/or MDM (or `null` if none are configured), including allow-lists (`allowedApprovalPolicies`, `allowedSandboxModes`, `allowedWebSearchModes`), `enforceResidency`, and `network` constraints.
- `configRequirements/read` — fetch the loaded requirements allow-lists and `enforceResidency` from `requirements.toml` and/or MDM (or `null` if none are configured).
### Example: Start or resume a thread
@@ -365,33 +355,6 @@ You can cancel a running Turn with `turn/interrupt`.
The server requests cancellations for running subprocesses, then emits a `turn/completed` event with `status: "interrupted"`. Rely on the `turn/completed` to know when Codex-side cleanup is done.
### Example: Clean background terminals
Use `thread/backgroundTerminals/clean` to terminate all running background terminals associated with a thread. This method is experimental and requires `capabilities.experimentalApi = true`.
```json
{ "method": "thread/backgroundTerminals/clean", "id": 35, "params": {
"threadId": "thr_123"
} }
{ "id": 35, "result": {} }
```
### 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.
```json
{ "method": "turn/steer", "id": 32, "params": {
"threadId": "thr_123",
"input": [ { "type": "text", "text": "Actually focus on failing tests first." } ],
"expectedTurnId": "turn_456"
} }
{ "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.
### Example: Request a code review
Use `review/start` to run Codexs reviewer on the currently checked-out project. The request takes the thread id plus a `target` describing what should be reviewed:
@@ -509,7 +472,7 @@ Today both notifications carry an empty `items` array even when item events were
- `commandExecution``{id, command, cwd, status, commandActions, aggregatedOutput?, exitCode?, durationMs?}` for sandboxed commands; `status` is `inProgress`, `completed`, `failed`, or `declined`.
- `fileChange``{id, changes, status}` describing proposed edits; `changes` list `{path, kind, diff}` and `status` is `inProgress`, `completed`, `failed`, or `declined`.
- `mcpToolCall``{id, server, tool, status, arguments, result?, error?}` describing MCP calls; `status` is `inProgress`, `completed`, or `failed`.
- `collabToolCall``{id, tool, status, senderThreadId, receiverThreadId?, newThreadId?, prompt?, agentStatus?}` describing collab tool calls (`spawn_agent`, `send_input`, `resume_agent`, `wait`, `close_agent`); `status` is `inProgress`, `completed`, or `failed`.
- `collabToolCall``{id, tool, status, senderThreadId, receiverThreadId?, newThreadId?, prompt?, agentStatus?}` describing collab tool calls (`spawn_agent`, `send_input`, `wait`, `close_agent`); `status` is `inProgress`, `completed`, or `failed`.
- `webSearch``{id, query, action?}` for a web search request issued by the agent; `action` mirrors the Responses API web_search action payload (`search`, `open_page`, `find_in_page`) and may be omitted until completion.
- `imageView``{id, path}` emitted when the agent invokes the image viewer tool.
- `enteredReviewMode``{id, review}` sent when the reviewer starts; `review` is a short user-facing label such as `"current changes"` or the requested target description.
@@ -663,20 +626,11 @@ $skill-creator Add a new skill for triaging flaky CI and include step-by-step us
```
Use `skills/list` to fetch the available skills (optionally scoped by `cwds`, with `forceReload`).
You can also add `perCwdExtraUserRoots` to scan additional absolute paths as `user` scope for specific `cwd` entries.
Entries whose `cwd` is not present in `cwds` are ignored.
`skills/list` might reuse a cached skills result per `cwd`; setting `forceReload` to `true` refreshes the result from disk.
```json
{ "method": "skills/list", "id": 25, "params": {
"cwds": ["/Users/me/project", "/Users/me/other-project"],
"forceReload": true,
"perCwdExtraUserRoots": [
{
"cwd": "/Users/me/project",
"extraUserRoots": ["/Users/me/shared-skills"]
}
]
"cwds": ["/Users/me/project"],
"forceReload": false
} }
{ "id": 25, "result": {
"data": [{
@@ -721,8 +675,7 @@ Use `app/list` to fetch available apps (connectors). Each entry includes metadat
```json
{ "method": "app/list", "id": 50, "params": {
"cursor": null,
"limit": 50,
"forceRefetch": false
"limit": 50
} }
{ "id": 50, "result": {
"data": [
@@ -741,30 +694,6 @@ Use `app/list` to fetch available apps (connectors). Each entry includes metadat
} }
```
`app/list` returns after both accessible apps and directory apps are loaded. Set `forceRefetch: true` to bypass app caches and fetch fresh data from sources. Cache entries are only replaced when those refetches succeed.
The server also emits `app/list/updated` notifications whenever either source (accessible apps or directory apps) finishes loading. Each notification includes the latest merged app list.
```json
{
"method": "app/list/updated",
"params": {
"data": [
{
"id": "demo-app",
"name": "Demo App",
"description": "Example connector for documentation.",
"logoUrl": "https://example.com/demo-app.png",
"logoUrlDark": null,
"distributionChannel": null,
"installUrl": "https://chatgpt.com/apps/demo-app/demo-app",
"isAccessible": true
}
]
}
}
```
Invoke an app by inserting `$<app-slug>` in the text input. The slug is derived from the app name and lowercased with non-alphanumeric characters replaced by `-` (for example, "Demo App" becomes `$demo-app`). Add a `mention` input item (recommended) so the server uses the exact `app://<connector-id>` path rather than guessing by name.
Example:
@@ -959,7 +888,6 @@ Examples of descriptor strings:
- `thread/start.mockExperimentalField` (field-level gate)
### For maintainers: Adding experimental fields and methods
Use this checklist when introducing a field/method that should only be available when the client opts into experimental APIs.
At runtime, clients must send `initialize` with `capabilities.experimentalApi = true` to use experimental methods or fields.
@@ -980,7 +908,7 @@ At runtime, clients must send `initialize` with `capabilities.experimentalApi =
# Include experimental API fields/methods in fixtures.
just write-app-server-schema --experimental
```
5. Verify the protocol crate:
```bash

View File

@@ -299,6 +299,8 @@ pub(crate) async fn apply_bespoke_event_handling(
thread_id: conversation_id.to_string(),
turn_id: request.turn_id,
item_id: request.call_id,
tool: request.tool,
arguments: request.arguments,
questions,
};
let rx = outgoing
@@ -596,28 +598,6 @@ pub(crate) async fn apply_bespoke_event_handling(
.send_server_notification(ServerNotification::ItemCompleted(notification))
.await;
}
EventMsg::CollabResumeBegin(begin_event) => {
let item = collab_resume_begin_item(begin_event);
let notification = ItemStartedNotification {
thread_id: conversation_id.to_string(),
turn_id: event_turn_id.clone(),
item,
};
outgoing
.send_server_notification(ServerNotification::ItemStarted(notification))
.await;
}
EventMsg::CollabResumeEnd(end_event) => {
let item = collab_resume_end_item(end_event);
let notification = ItemCompletedNotification {
thread_id: conversation_id.to_string(),
turn_id: event_turn_id.clone(),
item,
};
outgoing
.send_server_notification(ServerNotification::ItemCompleted(notification))
.await;
}
EventMsg::AgentMessageContentDelta(event) => {
let codex_protocol::protocol::AgentMessageContentDeltaEvent { item_id, delta, .. } =
event;
@@ -1115,7 +1095,7 @@ pub(crate) async fn apply_bespoke_event_handling(
),
data: None,
};
outgoing.send_error(request_id.clone(), error).await;
outgoing.send_error(request_id, error).await;
return;
}
}
@@ -1129,7 +1109,7 @@ pub(crate) async fn apply_bespoke_event_handling(
),
data: None,
};
outgoing.send_error(request_id.clone(), error).await;
outgoing.send_error(request_id, error).await;
return;
}
};
@@ -1780,44 +1760,6 @@ async fn on_command_execution_request_approval_response(
}
}
fn collab_resume_begin_item(
begin_event: codex_core::protocol::CollabResumeBeginEvent,
) -> ThreadItem {
ThreadItem::CollabAgentToolCall {
id: begin_event.call_id,
tool: CollabAgentTool::ResumeAgent,
status: V2CollabToolCallStatus::InProgress,
sender_thread_id: begin_event.sender_thread_id.to_string(),
receiver_thread_ids: vec![begin_event.receiver_thread_id.to_string()],
prompt: None,
agents_states: HashMap::new(),
}
}
fn collab_resume_end_item(end_event: codex_core::protocol::CollabResumeEndEvent) -> ThreadItem {
let status = match &end_event.status {
codex_protocol::protocol::AgentStatus::Errored(_)
| codex_protocol::protocol::AgentStatus::NotFound => V2CollabToolCallStatus::Failed,
_ => V2CollabToolCallStatus::Completed,
};
let receiver_id = end_event.receiver_thread_id.to_string();
let agents_states = [(
receiver_id.clone(),
V2CollabAgentStatus::from(end_event.status),
)]
.into_iter()
.collect();
ThreadItem::CollabAgentToolCall {
id: end_event.call_id,
tool: CollabAgentTool::ResumeAgent,
status,
sender_thread_id: end_event.sender_thread_id.to_string(),
receiver_thread_ids: vec![receiver_id],
prompt: None,
agents_states,
}
}
/// similar to handle_mcp_tool_call_begin in exec
async fn construct_mcp_tool_call_notification(
begin_event: McpToolCallBeginEvent,
@@ -1891,15 +1833,12 @@ async fn construct_mcp_tool_call_end_notification(
mod tests {
use super::*;
use crate::CHANNEL_CAPACITY;
use crate::outgoing_message::OutgoingEnvelope;
use crate::outgoing_message::OutgoingMessage;
use crate::outgoing_message::OutgoingMessageSender;
use anyhow::Result;
use anyhow::anyhow;
use anyhow::bail;
use codex_app_server_protocol::TurnPlanStepStatus;
use codex_core::protocol::CollabResumeBeginEvent;
use codex_core::protocol::CollabResumeEndEvent;
use codex_core::protocol::CreditsSnapshot;
use codex_core::protocol::McpInvocation;
use codex_core::protocol::RateLimitSnapshot;
@@ -1921,21 +1860,6 @@ mod tests {
Arc::new(Mutex::new(HashMap::new()))
}
async fn recv_broadcast_message(
rx: &mut mpsc::Receiver<OutgoingEnvelope>,
) -> Result<OutgoingMessage> {
let envelope = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send one message"))?;
match envelope {
OutgoingEnvelope::Broadcast { message } => Ok(message),
OutgoingEnvelope::ToConnection { connection_id, .. } => {
bail!("unexpected targeted message for connection {connection_id:?}")
}
}
}
#[test]
fn file_change_accept_for_session_maps_to_approved_for_session() {
let (decision, completion_status) =
@@ -1944,55 +1868,6 @@ mod tests {
assert_eq!(completion_status, None);
}
#[test]
fn collab_resume_begin_maps_to_item_started_resume_agent() {
let event = CollabResumeBeginEvent {
call_id: "call-1".to_string(),
sender_thread_id: ThreadId::new(),
receiver_thread_id: ThreadId::new(),
};
let item = collab_resume_begin_item(event.clone());
let expected = ThreadItem::CollabAgentToolCall {
id: event.call_id,
tool: CollabAgentTool::ResumeAgent,
status: V2CollabToolCallStatus::InProgress,
sender_thread_id: event.sender_thread_id.to_string(),
receiver_thread_ids: vec![event.receiver_thread_id.to_string()],
prompt: None,
agents_states: HashMap::new(),
};
assert_eq!(item, expected);
}
#[test]
fn collab_resume_end_maps_to_item_completed_resume_agent() {
let event = CollabResumeEndEvent {
call_id: "call-2".to_string(),
sender_thread_id: ThreadId::new(),
receiver_thread_id: ThreadId::new(),
status: codex_protocol::protocol::AgentStatus::NotFound,
};
let item = collab_resume_end_item(event.clone());
let receiver_id = event.receiver_thread_id.to_string();
let expected = ThreadItem::CollabAgentToolCall {
id: event.call_id,
tool: CollabAgentTool::ResumeAgent,
status: V2CollabToolCallStatus::Failed,
sender_thread_id: event.sender_thread_id.to_string(),
receiver_thread_ids: vec![receiver_id.clone()],
prompt: None,
agents_states: [(
receiver_id,
V2CollabAgentStatus::from(codex_protocol::protocol::AgentStatus::NotFound),
)]
.into_iter()
.collect(),
};
assert_eq!(item, expected);
}
#[tokio::test]
async fn test_handle_error_records_message() -> Result<()> {
let conversation_id = ThreadId::new();
@@ -2037,7 +1912,10 @@ mod tests {
)
.await;
let msg = recv_broadcast_message(&mut rx).await?;
let msg = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send one notification"))?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => {
assert_eq!(n.turn.id, event_turn_id);
@@ -2076,7 +1954,10 @@ mod tests {
)
.await;
let msg = recv_broadcast_message(&mut rx).await?;
let msg = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send one notification"))?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => {
assert_eq!(n.turn.id, event_turn_id);
@@ -2115,7 +1996,10 @@ mod tests {
)
.await;
let msg = recv_broadcast_message(&mut rx).await?;
let msg = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send one notification"))?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => {
assert_eq!(n.turn.id, event_turn_id);
@@ -2164,7 +2048,10 @@ mod tests {
)
.await;
let msg = recv_broadcast_message(&mut rx).await?;
let msg = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send one notification"))?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::TurnPlanUpdated(n)) => {
assert_eq!(n.thread_id, conversation_id.to_string());
@@ -2232,7 +2119,10 @@ mod tests {
)
.await;
let first = recv_broadcast_message(&mut rx).await?;
let first = rx
.recv()
.await
.ok_or_else(|| anyhow!("expected usage notification"))?;
match first {
OutgoingMessage::AppServerNotification(
ServerNotification::ThreadTokenUsageUpdated(payload),
@@ -2248,7 +2138,10 @@ mod tests {
other => bail!("unexpected notification: {other:?}"),
}
let second = recv_broadcast_message(&mut rx).await?;
let second = rx
.recv()
.await
.ok_or_else(|| anyhow!("expected rate limit notification"))?;
match second {
OutgoingMessage::AppServerNotification(
ServerNotification::AccountRateLimitsUpdated(payload),
@@ -2385,7 +2278,10 @@ mod tests {
.await;
// Verify: A turn 1
let msg = recv_broadcast_message(&mut rx).await?;
let msg = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send first notification"))?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => {
assert_eq!(n.turn.id, a_turn1);
@@ -2403,7 +2299,10 @@ mod tests {
}
// Verify: B turn 1
let msg = recv_broadcast_message(&mut rx).await?;
let msg = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send second notification"))?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => {
assert_eq!(n.turn.id, b_turn1);
@@ -2421,7 +2320,10 @@ mod tests {
}
// Verify: A turn 2
let msg = recv_broadcast_message(&mut rx).await?;
let msg = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send third notification"))?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => {
assert_eq!(n.turn.id, a_turn2);
@@ -2587,7 +2489,10 @@ mod tests {
)
.await;
let msg = recv_broadcast_message(&mut rx).await?;
let msg = rx
.recv()
.await
.ok_or_else(|| anyhow!("should send one notification"))?;
match msg {
OutgoingMessage::AppServerNotification(ServerNotification::TurnDiffUpdated(
notification,

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@ use codex_app_server_protocol::ConfigValueWriteParams;
use codex_app_server_protocol::ConfigWriteErrorCode;
use codex_app_server_protocol::ConfigWriteResponse;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::NetworkRequirements;
use codex_app_server_protocol::SandboxMode;
use codex_core::config::ConfigService;
use codex_core::config::ConfigServiceError;
@@ -18,19 +17,13 @@ use codex_core::config_loader::ConfigRequirementsToml;
use codex_core::config_loader::LoaderOverrides;
use codex_core::config_loader::ResidencyRequirement as CoreResidencyRequirement;
use codex_core::config_loader::SandboxModeRequirement as CoreSandboxModeRequirement;
use codex_protocol::config_types::WebSearchMode;
use serde_json::json;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::RwLock;
use toml::Value as TomlValue;
#[derive(Clone)]
pub(crate) struct ConfigApi {
codex_home: PathBuf,
cli_overrides: Vec<(String, TomlValue)>,
loader_overrides: LoaderOverrides,
cloud_requirements: Arc<RwLock<CloudRequirementsLoader>>,
service: ConfigService,
}
impl ConfigApi {
@@ -38,42 +31,30 @@ impl ConfigApi {
codex_home: PathBuf,
cli_overrides: Vec<(String, TomlValue)>,
loader_overrides: LoaderOverrides,
cloud_requirements: Arc<RwLock<CloudRequirementsLoader>>,
cloud_requirements: CloudRequirementsLoader,
) -> Self {
Self {
codex_home,
cli_overrides,
loader_overrides,
cloud_requirements,
service: ConfigService::new(
codex_home,
cli_overrides,
loader_overrides,
cloud_requirements,
),
}
}
fn config_service(&self) -> ConfigService {
let cloud_requirements = self
.cloud_requirements
.read()
.map(|guard| guard.clone())
.unwrap_or_default();
ConfigService::new(
self.codex_home.clone(),
self.cli_overrides.clone(),
self.loader_overrides.clone(),
cloud_requirements,
)
}
pub(crate) async fn read(
&self,
params: ConfigReadParams,
) -> Result<ConfigReadResponse, JSONRPCErrorError> {
self.config_service().read(params).await.map_err(map_error)
self.service.read(params).await.map_err(map_error)
}
pub(crate) async fn config_requirements_read(
&self,
) -> Result<ConfigRequirementsReadResponse, JSONRPCErrorError> {
let requirements = self
.config_service()
.service
.read_requirements()
.await
.map_err(map_error)?
@@ -86,20 +67,14 @@ impl ConfigApi {
&self,
params: ConfigValueWriteParams,
) -> Result<ConfigWriteResponse, JSONRPCErrorError> {
self.config_service()
.write_value(params)
.await
.map_err(map_error)
self.service.write_value(params).await.map_err(map_error)
}
pub(crate) async fn batch_write(
&self,
params: ConfigBatchWriteParams,
) -> Result<ConfigWriteResponse, JSONRPCErrorError> {
self.config_service()
.batch_write(params)
.await
.map_err(map_error)
self.service.batch_write(params).await.map_err(map_error)
}
}
@@ -117,20 +92,9 @@ fn map_requirements_toml_to_api(requirements: ConfigRequirementsToml) -> ConfigR
.filter_map(map_sandbox_mode_requirement_to_api)
.collect()
}),
allowed_web_search_modes: requirements.allowed_web_search_modes.map(|modes| {
let mut normalized = modes
.into_iter()
.map(Into::into)
.collect::<Vec<WebSearchMode>>();
if !normalized.contains(&WebSearchMode::Disabled) {
normalized.push(WebSearchMode::Disabled);
}
normalized
}),
enforce_residency: requirements
.enforce_residency
.map(map_residency_requirement_to_api),
network: requirements.network.map(map_network_requirements_to_api),
}
}
@@ -151,23 +115,6 @@ fn map_residency_requirement_to_api(
}
}
fn map_network_requirements_to_api(
network: codex_core::config_loader::NetworkRequirementsToml,
) -> NetworkRequirements {
NetworkRequirements {
enabled: network.enabled,
http_port: network.http_port,
socks_port: network.socks_port,
allow_upstream_proxy: network.allow_upstream_proxy,
dangerously_allow_non_loopback_proxy: network.dangerously_allow_non_loopback_proxy,
dangerously_allow_non_loopback_admin: network.dangerously_allow_non_loopback_admin,
allowed_domains: network.allowed_domains,
denied_domains: network.denied_domains,
allow_unix_sockets: network.allow_unix_sockets,
allow_local_binding: network.allow_local_binding,
}
}
fn map_error(err: ConfigServiceError) -> JSONRPCErrorError {
if let Some(code) = err.write_error_code() {
return config_write_error(code, err.to_string());
@@ -193,7 +140,6 @@ fn config_write_error(code: ConfigWriteErrorCode, message: impl Into<String>) ->
#[cfg(test)]
mod tests {
use super::*;
use codex_core::config_loader::NetworkRequirementsToml as CoreNetworkRequirementsToml;
use codex_protocol::protocol::AskForApproval as CoreAskForApproval;
use pretty_assertions::assert_eq;
@@ -208,24 +154,9 @@ mod tests {
CoreSandboxModeRequirement::ReadOnly,
CoreSandboxModeRequirement::ExternalSandbox,
]),
allowed_web_search_modes: Some(vec![
codex_core::config_loader::WebSearchModeRequirement::Cached,
]),
mcp_servers: None,
rules: None,
enforce_residency: Some(CoreResidencyRequirement::Us),
network: Some(CoreNetworkRequirementsToml {
enabled: Some(true),
http_port: Some(8080),
socks_port: Some(1080),
allow_upstream_proxy: Some(false),
dangerously_allow_non_loopback_proxy: Some(false),
dangerously_allow_non_loopback_admin: Some(false),
allowed_domains: Some(vec!["api.openai.com".to_string()]),
denied_domains: Some(vec!["example.com".to_string()]),
allow_unix_sockets: Some(vec!["/tmp/proxy.sock".to_string()]),
allow_local_binding: Some(true),
}),
};
let mapped = map_requirements_toml_to_api(requirements);
@@ -241,48 +172,9 @@ mod tests {
mapped.allowed_sandbox_modes,
Some(vec![SandboxMode::ReadOnly]),
);
assert_eq!(
mapped.allowed_web_search_modes,
Some(vec![WebSearchMode::Cached, WebSearchMode::Disabled]),
);
assert_eq!(
mapped.enforce_residency,
Some(codex_app_server_protocol::ResidencyRequirement::Us),
);
assert_eq!(
mapped.network,
Some(NetworkRequirements {
enabled: Some(true),
http_port: Some(8080),
socks_port: Some(1080),
allow_upstream_proxy: Some(false),
dangerously_allow_non_loopback_proxy: Some(false),
dangerously_allow_non_loopback_admin: Some(false),
allowed_domains: Some(vec!["api.openai.com".to_string()]),
denied_domains: Some(vec!["example.com".to_string()]),
allow_unix_sockets: Some(vec!["/tmp/proxy.sock".to_string()]),
allow_local_binding: Some(true),
}),
);
}
#[test]
fn map_requirements_toml_to_api_normalizes_allowed_web_search_modes() {
let requirements = ConfigRequirementsToml {
allowed_approval_policies: None,
allowed_sandbox_modes: None,
allowed_web_search_modes: Some(Vec::new()),
mcp_servers: None,
rules: None,
enforce_residency: None,
network: None,
};
let mapped = map_requirements_toml_to_api(requirements);
assert_eq!(
mapped.allowed_web_search_modes,
Some(vec![WebSearchMode::Disabled])
);
}
}

View File

@@ -8,24 +8,14 @@ use codex_core::config::ConfigBuilder;
use codex_core::config_loader::CloudRequirementsLoader;
use codex_core::config_loader::ConfigLayerStackOrdering;
use codex_core::config_loader::LoaderOverrides;
use std::collections::HashMap;
use std::io::ErrorKind;
use std::io::Result as IoResult;
use std::path::PathBuf;
use std::sync::Arc;
use crate::message_processor::MessageProcessor;
use crate::message_processor::MessageProcessorArgs;
use crate::outgoing_message::ConnectionId;
use crate::outgoing_message::OutgoingEnvelope;
use crate::outgoing_message::OutgoingMessage;
use crate::outgoing_message::OutgoingMessageSender;
use crate::transport::CHANNEL_CAPACITY;
use crate::transport::ConnectionState;
use crate::transport::TransportEvent;
use crate::transport::has_initialized_connections;
use crate::transport::route_outgoing_envelope;
use crate::transport::start_stdio_connection;
use crate::transport::start_websocket_acceptor;
use codex_app_server_protocol::ConfigLayerSource;
use codex_app_server_protocol::ConfigWarningNotification;
use codex_app_server_protocol::JSONRPCMessage;
@@ -36,9 +26,13 @@ use codex_core::check_execpolicy_for_warnings;
use codex_core::config_loader::ConfigLoadError;
use codex_core::config_loader::TextRange as CoreTextRange;
use codex_feedback::CodexFeedback;
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncWriteExt;
use tokio::io::BufReader;
use tokio::io::{self};
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use toml::Value as TomlValue;
use tracing::debug;
use tracing::error;
use tracing::info;
use tracing::warn;
@@ -57,9 +51,11 @@ mod fuzzy_file_search;
mod message_processor;
mod models;
mod outgoing_message;
mod transport;
pub use crate::transport::AppServerTransport;
/// Size of the bounded channels used to communicate between tasks. The value
/// is a balance between throughput and memory usage 128 messages should be
/// plenty for an interactive CLI.
const CHANNEL_CAPACITY: usize = 128;
fn config_warning_from_error(
summary: impl Into<String>,
@@ -177,39 +173,32 @@ pub async fn run_main(
loader_overrides: LoaderOverrides,
default_analytics_enabled: bool,
) -> IoResult<()> {
run_main_with_transport(
codex_linux_sandbox_exe,
cli_config_overrides,
loader_overrides,
default_analytics_enabled,
AppServerTransport::Stdio,
)
.await
}
// Set up channels.
let (incoming_tx, mut incoming_rx) = mpsc::channel::<JSONRPCMessage>(CHANNEL_CAPACITY);
let (outgoing_tx, mut outgoing_rx) = mpsc::channel::<OutgoingMessage>(CHANNEL_CAPACITY);
pub async fn run_main_with_transport(
codex_linux_sandbox_exe: Option<PathBuf>,
cli_config_overrides: CliConfigOverrides,
loader_overrides: LoaderOverrides,
default_analytics_enabled: bool,
transport: AppServerTransport,
) -> IoResult<()> {
let (transport_event_tx, mut transport_event_rx) =
mpsc::channel::<TransportEvent>(CHANNEL_CAPACITY);
let (outgoing_tx, mut outgoing_rx) = mpsc::channel::<OutgoingEnvelope>(CHANNEL_CAPACITY);
// Task: read from stdin, push to `incoming_tx`.
let stdin_reader_handle = tokio::spawn({
async move {
let stdin = io::stdin();
let reader = BufReader::new(stdin);
let mut lines = reader.lines();
let mut stdio_handles = Vec::<JoinHandle<()>>::new();
let mut websocket_accept_handle = None;
match transport {
AppServerTransport::Stdio => {
start_stdio_connection(transport_event_tx.clone(), &mut stdio_handles).await?;
while let Some(line) = lines.next_line().await.unwrap_or_default() {
match serde_json::from_str::<JSONRPCMessage>(&line) {
Ok(msg) => {
if incoming_tx.send(msg).await.is_err() {
// Receiver gone nothing left to do.
break;
}
}
Err(e) => error!("Failed to deserialize JSONRPCMessage: {e}"),
}
}
debug!("stdin reader finished (EOF)");
}
AppServerTransport::WebSocket { bind_address } => {
websocket_accept_handle =
Some(start_websocket_acceptor(bind_address, transport_event_tx.clone()).await?);
}
}
let shutdown_when_no_connections = matches!(transport, AppServerTransport::Stdio);
});
// Parse CLI overrides once and derive the base Config eagerly so later
// components do not need to work with raw TOML values.
@@ -278,7 +267,9 @@ pub async fn run_main_with_transport(
}
};
if let Ok(Some(err)) = check_execpolicy_for_warnings(&config.config_layer_stack).await {
if let Ok(Some(err)) =
check_execpolicy_for_warnings(&config.features, &config.config_layer_stack).await
{
let (path, range) = exec_policy_warning_location(&err);
let message = ConfigWarningNotification {
summary: "Error parsing rules; custom rules not applied.".to_string(),
@@ -336,14 +327,15 @@ pub async fn run_main_with_transport(
}
}
// Task: process incoming messages.
let processor_handle = tokio::spawn({
let outgoing_message_sender = Arc::new(OutgoingMessageSender::new(outgoing_tx));
let outgoing_message_sender = OutgoingMessageSender::new(outgoing_tx);
let cli_overrides: Vec<(String, TomlValue)> = cli_kv_overrides.clone();
let loader_overrides = loader_overrides_for_config_api;
let mut processor = MessageProcessor::new(MessageProcessorArgs {
outgoing: outgoing_message_sender,
codex_linux_sandbox_exe,
config: Arc::new(config),
config: std::sync::Arc::new(config),
cli_overrides,
loader_overrides,
cloud_requirements: cloud_requirements.clone(),
@@ -351,65 +343,25 @@ pub async fn run_main_with_transport(
config_warnings,
});
let mut thread_created_rx = processor.thread_created_receiver();
let mut connections = HashMap::<ConnectionId, ConnectionState>::new();
async move {
let mut listen_for_threads = true;
loop {
tokio::select! {
event = transport_event_rx.recv() => {
let Some(event) = event else {
msg = incoming_rx.recv() => {
let Some(msg) = msg else {
break;
};
match event {
TransportEvent::ConnectionOpened { connection_id, writer } => {
connections.insert(connection_id, ConnectionState::new(writer));
}
TransportEvent::ConnectionClosed { connection_id } => {
connections.remove(&connection_id);
if shutdown_when_no_connections && connections.is_empty() {
break;
}
}
TransportEvent::IncomingMessage { connection_id, message } => {
match message {
JSONRPCMessage::Request(request) => {
let Some(connection_state) = connections.get_mut(&connection_id) else {
warn!("dropping request from unknown connection: {:?}", connection_id);
continue;
};
processor
.process_request(
connection_id,
request,
&mut connection_state.session,
)
.await;
}
JSONRPCMessage::Response(response) => {
processor.process_response(response).await;
}
JSONRPCMessage::Notification(notification) => {
processor.process_notification(notification).await;
}
JSONRPCMessage::Error(err) => {
processor.process_error(err).await;
}
}
}
match msg {
JSONRPCMessage::Request(r) => processor.process_request(r).await,
JSONRPCMessage::Response(r) => processor.process_response(r).await,
JSONRPCMessage::Notification(n) => processor.process_notification(n).await,
JSONRPCMessage::Error(e) => processor.process_error(e).await,
}
}
envelope = outgoing_rx.recv() => {
let Some(envelope) = envelope else {
break;
};
route_outgoing_envelope(&mut connections, envelope).await;
}
created = thread_created_rx.recv(), if listen_for_threads => {
match created {
Ok(thread_id) => {
if has_initialized_connections(&connections) {
processor.try_attach_thread_listener(thread_id).await;
}
processor.try_attach_thread_listener(thread_id).await;
}
Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => {
// TODO(jif) handle lag.
@@ -430,17 +382,33 @@ pub async fn run_main_with_transport(
}
});
drop(transport_event_tx);
// Task: write outgoing messages to stdout.
let stdout_writer_handle = tokio::spawn(async move {
let mut stdout = io::stdout();
while let Some(outgoing_message) = outgoing_rx.recv().await {
let Ok(value) = serde_json::to_value(outgoing_message) else {
error!("Failed to convert OutgoingMessage to JSON value");
continue;
};
match serde_json::to_string(&value) {
Ok(mut json) => {
json.push('\n');
if let Err(e) = stdout.write_all(json.as_bytes()).await {
error!("Failed to write to stdout: {e}");
break;
}
}
Err(e) => error!("Failed to serialize JSONRPCMessage: {e}"),
}
}
let _ = processor_handle.await;
info!("stdout writer exited (channel closed)");
});
if let Some(handle) = websocket_accept_handle {
handle.abort();
}
for handle in stdio_handles {
let _ = handle.await;
}
// Wait for all tasks to finish. The typical exit path is the stdin reader
// hitting EOF which, once it drops `incoming_tx`, propagates shutdown to
// the processor and then to the stdout task.
let _ = tokio::join!(stdin_reader_handle, processor_handle, stdout_writer_handle);
Ok(())
}

View File

@@ -1,6 +1,4 @@
use clap::Parser;
use codex_app_server::AppServerTransport;
use codex_app_server::run_main_with_transport;
use codex_app_server::run_main;
use codex_arg0::arg0_dispatch_or_else;
use codex_common::CliConfigOverrides;
use codex_core::config_loader::LoaderOverrides;
@@ -10,34 +8,19 @@ use std::path::PathBuf;
// managed config file without writing to /etc.
const MANAGED_CONFIG_PATH_ENV_VAR: &str = "CODEX_APP_SERVER_MANAGED_CONFIG_PATH";
#[derive(Debug, Parser)]
struct AppServerArgs {
/// Transport endpoint URL. Supported values: `stdio://` (default),
/// `ws://IP:PORT`.
#[arg(
long = "listen",
value_name = "URL",
default_value = AppServerTransport::DEFAULT_LISTEN_URL
)]
listen: AppServerTransport,
}
fn main() -> anyhow::Result<()> {
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
let args = AppServerArgs::parse();
let managed_config_path = managed_config_path_from_debug_env();
let loader_overrides = LoaderOverrides {
managed_config_path,
..Default::default()
};
let transport = args.listen;
run_main_with_transport(
run_main(
codex_linux_sandbox_exe,
CliConfigOverrides::default(),
loader_overrides,
false,
transport,
)
.await?;
Ok(())

View File

@@ -1,13 +1,12 @@
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::RwLock;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use crate::codex_message_processor::CodexMessageProcessor;
use crate::codex_message_processor::CodexMessageProcessorArgs;
use crate::config_api::ConfigApi;
use crate::error_code::INVALID_REQUEST_ERROR_CODE;
use crate::outgoing_message::ConnectionId;
use crate::outgoing_message::ConnectionRequestId;
use crate::outgoing_message::OutgoingMessageSender;
use async_trait::async_trait;
use codex_app_server_protocol::ChatgptAuthTokensRefreshParams;
@@ -26,6 +25,7 @@ use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::JSONRPCNotification;
use codex_app_server_protocol::JSONRPCRequest;
use codex_app_server_protocol::JSONRPCResponse;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::ServerNotification;
use codex_app_server_protocol::ServerRequestPayload;
use codex_app_server_protocol::experimental_required_message;
@@ -110,17 +110,13 @@ pub(crate) struct MessageProcessor {
codex_message_processor: CodexMessageProcessor,
config_api: ConfigApi,
config: Arc<Config>,
config_warnings: Arc<Vec<ConfigWarningNotification>>,
}
#[derive(Debug, Default)]
pub(crate) struct ConnectionSessionState {
pub(crate) initialized: bool,
experimental_api_enabled: bool,
initialized: bool,
experimental_api_enabled: Arc<AtomicBool>,
config_warnings: Vec<ConfigWarningNotification>,
}
pub(crate) struct MessageProcessorArgs {
pub(crate) outgoing: Arc<OutgoingMessageSender>,
pub(crate) outgoing: OutgoingMessageSender,
pub(crate) codex_linux_sandbox_exe: Option<PathBuf>,
pub(crate) config: Arc<Config>,
pub(crate) cli_overrides: Vec<(String, TomlValue)>,
@@ -144,6 +140,8 @@ impl MessageProcessor {
feedback,
config_warnings,
} = args;
let outgoing = Arc::new(outgoing);
let experimental_api_enabled = Arc::new(AtomicBool::new(false));
let auth_manager = AuthManager::shared(
config.codex_home.clone(),
false,
@@ -158,7 +156,6 @@ impl MessageProcessor {
auth_manager.clone(),
SessionSource::VSCode,
));
let cloud_requirements = Arc::new(RwLock::new(cloud_requirements));
let codex_message_processor = CodexMessageProcessor::new(CodexMessageProcessorArgs {
auth_manager,
thread_manager,
@@ -181,20 +178,14 @@ impl MessageProcessor {
codex_message_processor,
config_api,
config,
config_warnings: Arc::new(config_warnings),
initialized: false,
experimental_api_enabled,
config_warnings,
}
}
pub(crate) async fn process_request(
&mut self,
connection_id: ConnectionId,
request: JSONRPCRequest,
session: &mut ConnectionSessionState,
) {
let request_id = ConnectionRequestId {
connection_id,
request_id: request.id.clone(),
};
pub(crate) async fn process_request(&mut self, request: JSONRPCRequest) {
let request_id = request.id.clone();
let request_json = match serde_json::to_value(&request) {
Ok(request_json) => request_json,
Err(err) => {
@@ -225,11 +216,7 @@ impl MessageProcessor {
// Handle Initialize internally so CodexMessageProcessor does not have to concern
// itself with the `initialized` bool.
ClientRequest::Initialize { request_id, params } => {
let request_id = ConnectionRequestId {
connection_id,
request_id,
};
if session.initialized {
if self.initialized {
let error = JSONRPCErrorError {
code: INVALID_REQUEST_ERROR_CODE,
message: "Already initialized".to_string(),
@@ -238,16 +225,12 @@ impl MessageProcessor {
self.outgoing.send_error(request_id, error).await;
return;
} else {
// TODO(maxj): Revisit capability scoping for `experimental_api_enabled`.
// Current behavior is per-connection. Reviewer feedback notes this can
// create odd cross-client behavior (for example dynamic tool calls on a
// shared thread when another connected client did not opt into
// experimental API). Proposed direction is instance-global first-write-wins
// with initialize-time mismatch rejection.
session.experimental_api_enabled = params
let experimental_api_enabled = params
.capabilities
.as_ref()
.is_some_and(|cap| cap.experimental_api);
self.experimental_api_enabled
.store(experimental_api_enabled, Ordering::Relaxed);
let ClientInfo {
name,
title: _title,
@@ -263,7 +246,7 @@ impl MessageProcessor {
),
data: None,
};
self.outgoing.send_error(request_id.clone(), error).await;
self.outgoing.send_error(request_id, error).await;
return;
}
SetOriginatorError::AlreadyInitialized => {
@@ -284,20 +267,22 @@ impl MessageProcessor {
let response = InitializeResponse { user_agent };
self.outgoing.send_response(request_id, response).await;
session.initialized = true;
for notification in self.config_warnings.iter().cloned() {
self.outgoing
.send_server_notification(ServerNotification::ConfigWarning(
notification,
))
.await;
self.initialized = true;
if !self.config_warnings.is_empty() {
for notification in self.config_warnings.drain(..) {
self.outgoing
.send_server_notification(ServerNotification::ConfigWarning(
notification,
))
.await;
}
}
return;
}
}
_ => {
if !session.initialized {
if !self.initialized {
let error = JSONRPCErrorError {
code: INVALID_REQUEST_ERROR_CODE,
message: "Not initialized".to_string(),
@@ -310,7 +295,7 @@ impl MessageProcessor {
}
if let Some(reason) = codex_request.experimental_reason()
&& !session.experimental_api_enabled
&& !self.experimental_api_enabled.load(Ordering::Relaxed)
{
let error = JSONRPCErrorError {
code: INVALID_REQUEST_ERROR_CODE,
@@ -323,49 +308,22 @@ impl MessageProcessor {
match codex_request {
ClientRequest::ConfigRead { request_id, params } => {
self.handle_config_read(
ConnectionRequestId {
connection_id,
request_id,
},
params,
)
.await;
self.handle_config_read(request_id, params).await;
}
ClientRequest::ConfigValueWrite { request_id, params } => {
self.handle_config_value_write(
ConnectionRequestId {
connection_id,
request_id,
},
params,
)
.await;
self.handle_config_value_write(request_id, params).await;
}
ClientRequest::ConfigBatchWrite { request_id, params } => {
self.handle_config_batch_write(
ConnectionRequestId {
connection_id,
request_id,
},
params,
)
.await;
self.handle_config_batch_write(request_id, params).await;
}
ClientRequest::ConfigRequirementsRead {
request_id,
params: _,
} => {
self.handle_config_requirements_read(ConnectionRequestId {
connection_id,
request_id,
})
.await;
self.handle_config_requirements_read(request_id).await;
}
other => {
self.codex_message_processor
.process_request(connection_id, other)
.await;
self.codex_message_processor.process_request(other).await;
}
}
}
@@ -381,6 +339,9 @@ impl MessageProcessor {
}
pub(crate) async fn try_attach_thread_listener(&mut self, thread_id: ThreadId) {
if !self.initialized {
return;
}
self.codex_message_processor
.try_attach_thread_listener(thread_id)
.await;
@@ -399,7 +360,7 @@ impl MessageProcessor {
self.outgoing.notify_client_error(err.id, err.error).await;
}
async fn handle_config_read(&self, request_id: ConnectionRequestId, params: ConfigReadParams) {
async fn handle_config_read(&self, request_id: RequestId, params: ConfigReadParams) {
match self.config_api.read(params).await {
Ok(response) => self.outgoing.send_response(request_id, response).await,
Err(error) => self.outgoing.send_error(request_id, error).await,
@@ -408,7 +369,7 @@ impl MessageProcessor {
async fn handle_config_value_write(
&self,
request_id: ConnectionRequestId,
request_id: RequestId,
params: ConfigValueWriteParams,
) {
match self.config_api.write_value(params).await {
@@ -419,7 +380,7 @@ impl MessageProcessor {
async fn handle_config_batch_write(
&self,
request_id: ConnectionRequestId,
request_id: RequestId,
params: ConfigBatchWriteParams,
) {
match self.config_api.batch_write(params).await {
@@ -428,7 +389,7 @@ impl MessageProcessor {
}
}
async fn handle_config_requirements_read(&self, request_id: ConnectionRequestId) {
async fn handle_config_requirements_read(&self, request_id: RequestId) {
match self.config_api.config_requirements_read().await {
Ok(response) => self.outgoing.send_response(request_id, response).await,
Err(error) => self.outgoing.send_error(request_id, error).await,

View File

@@ -19,39 +19,17 @@ use crate::error_code::INTERNAL_ERROR_CODE;
#[cfg(test)]
use codex_protocol::account::PlanType;
/// Stable identifier for a transport connection.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub(crate) struct ConnectionId(pub(crate) u64);
/// Stable identifier for a client request scoped to a transport connection.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) struct ConnectionRequestId {
pub(crate) connection_id: ConnectionId,
pub(crate) request_id: RequestId,
}
#[derive(Debug, Clone)]
pub(crate) enum OutgoingEnvelope {
ToConnection {
connection_id: ConnectionId,
message: OutgoingMessage,
},
Broadcast {
message: OutgoingMessage,
},
}
/// Sends messages to the client and manages request callbacks.
pub(crate) struct OutgoingMessageSender {
next_server_request_id: AtomicI64,
sender: mpsc::Sender<OutgoingEnvelope>,
next_request_id: AtomicI64,
sender: mpsc::Sender<OutgoingMessage>,
request_id_to_callback: Mutex<HashMap<RequestId, oneshot::Sender<Result>>>,
}
impl OutgoingMessageSender {
pub(crate) fn new(sender: mpsc::Sender<OutgoingEnvelope>) -> Self {
pub(crate) fn new(sender: mpsc::Sender<OutgoingMessage>) -> Self {
Self {
next_server_request_id: AtomicI64::new(0),
next_request_id: AtomicI64::new(0),
sender,
request_id_to_callback: Mutex::new(HashMap::new()),
}
@@ -69,7 +47,7 @@ impl OutgoingMessageSender {
&self,
request: ServerRequestPayload,
) -> (RequestId, oneshot::Receiver<Result>) {
let id = RequestId::Integer(self.next_server_request_id.fetch_add(1, Ordering::Relaxed));
let id = RequestId::Integer(self.next_request_id.fetch_add(1, Ordering::Relaxed));
let outgoing_message_id = id.clone();
let (tx_approve, rx_approve) = oneshot::channel();
{
@@ -79,13 +57,7 @@ impl OutgoingMessageSender {
let outgoing_message =
OutgoingMessage::Request(request.request_with_id(outgoing_message_id.clone()));
if let Err(err) = self
.sender
.send(OutgoingEnvelope::Broadcast {
message: outgoing_message,
})
.await
{
if let Err(err) = self.sender.send(outgoing_message).await {
warn!("failed to send request {outgoing_message_id:?} to client: {err:?}");
let mut request_id_to_callback = self.request_id_to_callback.lock().await;
request_id_to_callback.remove(&outgoing_message_id);
@@ -135,31 +107,17 @@ impl OutgoingMessageSender {
entry.is_some()
}
pub(crate) async fn send_response<T: Serialize>(
&self,
request_id: ConnectionRequestId,
response: T,
) {
pub(crate) async fn send_response<T: Serialize>(&self, id: RequestId, response: T) {
match serde_json::to_value(response) {
Ok(result) => {
let outgoing_message = OutgoingMessage::Response(OutgoingResponse {
id: request_id.request_id,
result,
});
if let Err(err) = self
.sender
.send(OutgoingEnvelope::ToConnection {
connection_id: request_id.connection_id,
message: outgoing_message,
})
.await
{
let outgoing_message = OutgoingMessage::Response(OutgoingResponse { id, result });
if let Err(err) = self.sender.send(outgoing_message).await {
warn!("failed to send response to client: {err:?}");
}
}
Err(err) => {
self.send_error(
request_id,
id,
JSONRPCErrorError {
code: INTERNAL_ERROR_CODE,
message: format!("failed to serialize response: {err}"),
@@ -174,9 +132,7 @@ impl OutgoingMessageSender {
pub(crate) async fn send_server_notification(&self, notification: ServerNotification) {
if let Err(err) = self
.sender
.send(OutgoingEnvelope::Broadcast {
message: OutgoingMessage::AppServerNotification(notification),
})
.send(OutgoingMessage::AppServerNotification(notification))
.await
{
warn!("failed to send server notification to client: {err:?}");
@@ -187,34 +143,14 @@ impl OutgoingMessageSender {
/// [`OutgoingMessage::Notification`] should be removed.
pub(crate) async fn send_notification(&self, notification: OutgoingNotification) {
let outgoing_message = OutgoingMessage::Notification(notification);
if let Err(err) = self
.sender
.send(OutgoingEnvelope::Broadcast {
message: outgoing_message,
})
.await
{
if let Err(err) = self.sender.send(outgoing_message).await {
warn!("failed to send notification to client: {err:?}");
}
}
pub(crate) async fn send_error(
&self,
request_id: ConnectionRequestId,
error: JSONRPCErrorError,
) {
let outgoing_message = OutgoingMessage::Error(OutgoingError {
id: request_id.request_id,
error,
});
if let Err(err) = self
.sender
.send(OutgoingEnvelope::ToConnection {
connection_id: request_id.connection_id,
message: outgoing_message,
})
.await
{
pub(crate) async fn send_error(&self, id: RequestId, error: JSONRPCErrorError) {
let outgoing_message = OutgoingMessage::Error(OutgoingError { id, error });
if let Err(err) = self.sender.send(outgoing_message).await {
warn!("failed to send error to client: {err:?}");
}
}
@@ -254,8 +190,6 @@ pub(crate) struct OutgoingError {
#[cfg(test)]
mod tests {
use std::time::Duration;
use codex_app_server_protocol::AccountLoginCompletedNotification;
use codex_app_server_protocol::AccountRateLimitsUpdatedNotification;
use codex_app_server_protocol::AccountUpdatedNotification;
@@ -266,7 +200,6 @@ mod tests {
use codex_app_server_protocol::RateLimitWindow;
use pretty_assertions::assert_eq;
use serde_json::json;
use tokio::time::timeout;
use uuid::Uuid;
use super::*;
@@ -403,75 +336,4 @@ mod tests {
"ensure the notification serializes correctly"
);
}
#[tokio::test]
async fn send_response_routes_to_target_connection() {
let (tx, mut rx) = mpsc::channel::<OutgoingEnvelope>(4);
let outgoing = OutgoingMessageSender::new(tx);
let request_id = ConnectionRequestId {
connection_id: ConnectionId(42),
request_id: RequestId::Integer(7),
};
outgoing
.send_response(request_id.clone(), json!({ "ok": true }))
.await;
let envelope = timeout(Duration::from_secs(1), rx.recv())
.await
.expect("should receive envelope before timeout")
.expect("channel should contain one message");
match envelope {
OutgoingEnvelope::ToConnection {
connection_id,
message,
} => {
assert_eq!(connection_id, ConnectionId(42));
let OutgoingMessage::Response(response) = message else {
panic!("expected response message");
};
assert_eq!(response.id, request_id.request_id);
assert_eq!(response.result, json!({ "ok": true }));
}
other => panic!("expected targeted response envelope, got: {other:?}"),
}
}
#[tokio::test]
async fn send_error_routes_to_target_connection() {
let (tx, mut rx) = mpsc::channel::<OutgoingEnvelope>(4);
let outgoing = OutgoingMessageSender::new(tx);
let request_id = ConnectionRequestId {
connection_id: ConnectionId(9),
request_id: RequestId::Integer(3),
};
let error = JSONRPCErrorError {
code: INTERNAL_ERROR_CODE,
message: "boom".to_string(),
data: None,
};
outgoing.send_error(request_id.clone(), error.clone()).await;
let envelope = timeout(Duration::from_secs(1), rx.recv())
.await
.expect("should receive envelope before timeout")
.expect("channel should contain one message");
match envelope {
OutgoingEnvelope::ToConnection {
connection_id,
message,
} => {
assert_eq!(connection_id, ConnectionId(9));
let OutgoingMessage::Error(outgoing_error) = message else {
panic!("expected error message");
};
assert_eq!(outgoing_error.id, RequestId::Integer(3));
assert_eq!(outgoing_error.error, error);
}
other => panic!("expected targeted error envelope, got: {other:?}"),
}
}
}

View File

@@ -1,459 +0,0 @@
use crate::message_processor::ConnectionSessionState;
use crate::outgoing_message::ConnectionId;
use crate::outgoing_message::OutgoingEnvelope;
use crate::outgoing_message::OutgoingMessage;
use codex_app_server_protocol::JSONRPCMessage;
use futures::SinkExt;
use futures::StreamExt;
use owo_colors::OwoColorize;
use owo_colors::Stream;
use owo_colors::Style;
use std::collections::HashMap;
use std::io::ErrorKind;
use std::io::Result as IoResult;
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering;
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncWriteExt;
use tokio::io::BufReader;
use tokio::io::{self};
use tokio::net::TcpListener;
use tokio::net::TcpStream;
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use tokio_tungstenite::accept_async;
use tokio_tungstenite::tungstenite::Message as WebSocketMessage;
use tracing::debug;
use tracing::error;
use tracing::info;
use tracing::warn;
/// Size of the bounded channels used to communicate between tasks. The value
/// is a balance between throughput and memory usage - 128 messages should be
/// plenty for an interactive CLI.
pub(crate) const CHANNEL_CAPACITY: usize = 128;
fn colorize(text: &str, style: Style) -> String {
text.if_supports_color(Stream::Stderr, |value| value.style(style))
.to_string()
}
#[allow(clippy::print_stderr)]
fn print_websocket_startup_banner(addr: SocketAddr) {
let title = colorize("codex app-server (WebSockets)", Style::new().bold().cyan());
let listening_label = colorize("listening on:", Style::new().dimmed());
let listen_url = colorize(&format!("ws://{addr}"), Style::new().green());
let note_label = colorize("note:", Style::new().dimmed());
eprintln!("{title}");
eprintln!(" {listening_label} {listen_url}");
if addr.ip().is_loopback() {
eprintln!(
" {note_label} binds localhost only (use SSH port-forwarding for remote access)"
);
} else {
eprintln!(
" {note_label} this is a raw WS server; consider running behind TLS/auth for real remote use"
);
}
}
#[allow(clippy::print_stderr)]
fn print_websocket_connection(peer_addr: SocketAddr) {
let connected_label = colorize("websocket client connected from", Style::new().dimmed());
eprintln!("{connected_label} {peer_addr}");
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AppServerTransport {
Stdio,
WebSocket { bind_address: SocketAddr },
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum AppServerTransportParseError {
UnsupportedListenUrl(String),
InvalidWebSocketListenUrl(String),
}
impl std::fmt::Display for AppServerTransportParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AppServerTransportParseError::UnsupportedListenUrl(listen_url) => write!(
f,
"unsupported --listen URL `{listen_url}`; expected `stdio://` or `ws://IP:PORT`"
),
AppServerTransportParseError::InvalidWebSocketListenUrl(listen_url) => write!(
f,
"invalid websocket --listen URL `{listen_url}`; expected `ws://IP:PORT`"
),
}
}
}
impl std::error::Error for AppServerTransportParseError {}
impl AppServerTransport {
pub const DEFAULT_LISTEN_URL: &'static str = "stdio://";
pub fn from_listen_url(listen_url: &str) -> Result<Self, AppServerTransportParseError> {
if listen_url == Self::DEFAULT_LISTEN_URL {
return Ok(Self::Stdio);
}
if let Some(socket_addr) = listen_url.strip_prefix("ws://") {
let bind_address = socket_addr.parse::<SocketAddr>().map_err(|_| {
AppServerTransportParseError::InvalidWebSocketListenUrl(listen_url.to_string())
})?;
return Ok(Self::WebSocket { bind_address });
}
Err(AppServerTransportParseError::UnsupportedListenUrl(
listen_url.to_string(),
))
}
}
impl FromStr for AppServerTransport {
type Err = AppServerTransportParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_listen_url(s)
}
}
#[derive(Debug)]
pub(crate) enum TransportEvent {
ConnectionOpened {
connection_id: ConnectionId,
writer: mpsc::Sender<OutgoingMessage>,
},
ConnectionClosed {
connection_id: ConnectionId,
},
IncomingMessage {
connection_id: ConnectionId,
message: JSONRPCMessage,
},
}
pub(crate) struct ConnectionState {
pub(crate) writer: mpsc::Sender<OutgoingMessage>,
pub(crate) session: ConnectionSessionState,
}
impl ConnectionState {
pub(crate) fn new(writer: mpsc::Sender<OutgoingMessage>) -> Self {
Self {
writer,
session: ConnectionSessionState::default(),
}
}
}
pub(crate) async fn start_stdio_connection(
transport_event_tx: mpsc::Sender<TransportEvent>,
stdio_handles: &mut Vec<JoinHandle<()>>,
) -> IoResult<()> {
let connection_id = ConnectionId(0);
let (writer_tx, mut writer_rx) = mpsc::channel::<OutgoingMessage>(CHANNEL_CAPACITY);
transport_event_tx
.send(TransportEvent::ConnectionOpened {
connection_id,
writer: writer_tx,
})
.await
.map_err(|_| std::io::Error::new(ErrorKind::BrokenPipe, "processor unavailable"))?;
let transport_event_tx_for_reader = transport_event_tx.clone();
stdio_handles.push(tokio::spawn(async move {
let stdin = io::stdin();
let reader = BufReader::new(stdin);
let mut lines = reader.lines();
loop {
match lines.next_line().await {
Ok(Some(line)) => {
if !forward_incoming_message(
&transport_event_tx_for_reader,
connection_id,
&line,
)
.await
{
break;
}
}
Ok(None) => break,
Err(err) => {
error!("Failed reading stdin: {err}");
break;
}
}
}
let _ = transport_event_tx_for_reader
.send(TransportEvent::ConnectionClosed { connection_id })
.await;
debug!("stdin reader finished (EOF)");
}));
stdio_handles.push(tokio::spawn(async move {
let mut stdout = io::stdout();
while let Some(outgoing_message) = writer_rx.recv().await {
let Some(mut json) = serialize_outgoing_message(outgoing_message) else {
continue;
};
json.push('\n');
if let Err(err) = stdout.write_all(json.as_bytes()).await {
error!("Failed to write to stdout: {err}");
break;
}
}
info!("stdout writer exited (channel closed)");
}));
Ok(())
}
pub(crate) async fn start_websocket_acceptor(
bind_address: SocketAddr,
transport_event_tx: mpsc::Sender<TransportEvent>,
) -> IoResult<JoinHandle<()>> {
let listener = TcpListener::bind(bind_address).await?;
let local_addr = listener.local_addr()?;
print_websocket_startup_banner(local_addr);
info!("app-server websocket listening on ws://{local_addr}");
let connection_counter = Arc::new(AtomicU64::new(1));
Ok(tokio::spawn(async move {
loop {
match listener.accept().await {
Ok((stream, peer_addr)) => {
print_websocket_connection(peer_addr);
let connection_id =
ConnectionId(connection_counter.fetch_add(1, Ordering::Relaxed));
let transport_event_tx_for_connection = transport_event_tx.clone();
tokio::spawn(async move {
run_websocket_connection(
connection_id,
stream,
transport_event_tx_for_connection,
)
.await;
});
}
Err(err) => {
error!("failed to accept websocket connection: {err}");
}
}
}
}))
}
async fn run_websocket_connection(
connection_id: ConnectionId,
stream: TcpStream,
transport_event_tx: mpsc::Sender<TransportEvent>,
) {
let websocket_stream = match accept_async(stream).await {
Ok(stream) => stream,
Err(err) => {
warn!("failed to complete websocket handshake: {err}");
return;
}
};
let (writer_tx, mut writer_rx) = mpsc::channel::<OutgoingMessage>(CHANNEL_CAPACITY);
if transport_event_tx
.send(TransportEvent::ConnectionOpened {
connection_id,
writer: writer_tx,
})
.await
.is_err()
{
return;
}
let (mut websocket_writer, mut websocket_reader) = websocket_stream.split();
loop {
tokio::select! {
outgoing_message = writer_rx.recv() => {
let Some(outgoing_message) = outgoing_message else {
break;
};
let Some(json) = serialize_outgoing_message(outgoing_message) else {
continue;
};
if websocket_writer.send(WebSocketMessage::Text(json.into())).await.is_err() {
break;
}
}
incoming_message = websocket_reader.next() => {
match incoming_message {
Some(Ok(WebSocketMessage::Text(text))) => {
if !forward_incoming_message(&transport_event_tx, connection_id, &text).await {
break;
}
}
Some(Ok(WebSocketMessage::Ping(payload))) => {
if websocket_writer.send(WebSocketMessage::Pong(payload)).await.is_err() {
break;
}
}
Some(Ok(WebSocketMessage::Pong(_))) => {}
Some(Ok(WebSocketMessage::Close(_))) | None => break,
Some(Ok(WebSocketMessage::Binary(_))) => {
warn!("dropping unsupported binary websocket message");
}
Some(Ok(WebSocketMessage::Frame(_))) => {}
Some(Err(err)) => {
warn!("websocket receive error: {err}");
break;
}
}
}
}
}
let _ = transport_event_tx
.send(TransportEvent::ConnectionClosed { connection_id })
.await;
}
async fn forward_incoming_message(
transport_event_tx: &mpsc::Sender<TransportEvent>,
connection_id: ConnectionId,
payload: &str,
) -> bool {
match serde_json::from_str::<JSONRPCMessage>(payload) {
Ok(message) => transport_event_tx
.send(TransportEvent::IncomingMessage {
connection_id,
message,
})
.await
.is_ok(),
Err(err) => {
error!("Failed to deserialize JSONRPCMessage: {err}");
true
}
}
}
fn serialize_outgoing_message(outgoing_message: OutgoingMessage) -> Option<String> {
let value = match serde_json::to_value(outgoing_message) {
Ok(value) => value,
Err(err) => {
error!("Failed to convert OutgoingMessage to JSON value: {err}");
return None;
}
};
match serde_json::to_string(&value) {
Ok(json) => Some(json),
Err(err) => {
error!("Failed to serialize JSONRPCMessage: {err}");
None
}
}
}
pub(crate) async fn route_outgoing_envelope(
connections: &mut HashMap<ConnectionId, ConnectionState>,
envelope: OutgoingEnvelope,
) {
match envelope {
OutgoingEnvelope::ToConnection {
connection_id,
message,
} => {
let Some(connection_state) = connections.get(&connection_id) else {
warn!(
"dropping message for disconnected connection: {:?}",
connection_id
);
return;
};
if connection_state.writer.send(message).await.is_err() {
connections.remove(&connection_id);
}
}
OutgoingEnvelope::Broadcast { message } => {
let target_connections: Vec<ConnectionId> = connections
.iter()
.filter_map(|(connection_id, connection_state)| {
if connection_state.session.initialized {
Some(*connection_id)
} else {
None
}
})
.collect();
for connection_id in target_connections {
let Some(connection_state) = connections.get(&connection_id) else {
continue;
};
if connection_state.writer.send(message.clone()).await.is_err() {
connections.remove(&connection_id);
}
}
}
}
}
pub(crate) fn has_initialized_connections(
connections: &HashMap<ConnectionId, ConnectionState>,
) -> bool {
connections
.values()
.any(|connection| connection.session.initialized)
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn app_server_transport_parses_stdio_listen_url() {
let transport = AppServerTransport::from_listen_url(AppServerTransport::DEFAULT_LISTEN_URL)
.expect("stdio listen URL should parse");
assert_eq!(transport, AppServerTransport::Stdio);
}
#[test]
fn app_server_transport_parses_websocket_listen_url() {
let transport = AppServerTransport::from_listen_url("ws://127.0.0.1:1234")
.expect("websocket listen URL should parse");
assert_eq!(
transport,
AppServerTransport::WebSocket {
bind_address: "127.0.0.1:1234".parse().expect("valid socket address"),
}
);
}
#[test]
fn app_server_transport_rejects_invalid_websocket_listen_url() {
let err = AppServerTransport::from_listen_url("ws://localhost:1234")
.expect_err("hostname bind address should be rejected");
assert_eq!(
err.to_string(),
"invalid websocket --listen URL `ws://localhost:1234`; expected `ws://IP:PORT`"
);
}
#[test]
fn app_server_transport_rejects_unsupported_listen_url() {
let err = AppServerTransport::from_listen_url("http://127.0.0.1:1234")
.expect_err("unsupported scheme should fail");
assert_eq!(
err.to_string(),
"unsupported --listen URL `http://127.0.0.1:1234`; expected `stdio://` or `ws://IP:PORT`"
);
}
}

View File

@@ -22,7 +22,6 @@ use codex_app_server_protocol::CollaborationModeListParams;
use codex_app_server_protocol::ConfigBatchWriteParams;
use codex_app_server_protocol::ConfigReadParams;
use codex_app_server_protocol::ConfigValueWriteParams;
use codex_app_server_protocol::ExperimentalFeatureListParams;
use codex_app_server_protocol::FeedbackUploadParams;
use codex_app_server_protocol::ForkConversationParams;
use codex_app_server_protocol::GetAccountParams;
@@ -50,7 +49,6 @@ use codex_app_server_protocol::SendUserMessageParams;
use codex_app_server_protocol::SendUserTurnParams;
use codex_app_server_protocol::ServerRequest;
use codex_app_server_protocol::SetDefaultModelParams;
use codex_app_server_protocol::SkillsListParams;
use codex_app_server_protocol::ThreadArchiveParams;
use codex_app_server_protocol::ThreadCompactStartParams;
use codex_app_server_protocol::ThreadForkParams;
@@ -63,7 +61,6 @@ use codex_app_server_protocol::ThreadStartParams;
use codex_app_server_protocol::ThreadUnarchiveParams;
use codex_app_server_protocol::TurnInterruptParams;
use codex_app_server_protocol::TurnStartParams;
use codex_app_server_protocol::TurnSteerParams;
use codex_core::default_client::CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR;
use tokio::process::Command;
@@ -476,30 +473,12 @@ impl McpProcess {
self.send_request("model/list", params).await
}
/// Send an `experimentalFeature/list` JSON-RPC request.
pub async fn send_experimental_feature_list_request(
&mut self,
params: ExperimentalFeatureListParams,
) -> anyhow::Result<i64> {
let params = Some(serde_json::to_value(params)?);
self.send_request("experimentalFeature/list", params).await
}
/// Send an `app/list` JSON-RPC request.
pub async fn send_apps_list_request(&mut self, params: AppsListParams) -> anyhow::Result<i64> {
let params = Some(serde_json::to_value(params)?);
self.send_request("app/list", params).await
}
/// Send a `skills/list` JSON-RPC request.
pub async fn send_skills_list_request(
&mut self,
params: SkillsListParams,
) -> anyhow::Result<i64> {
let params = Some(serde_json::to_value(params)?);
self.send_request("skills/list", params).await
}
/// Send a `collaborationMode/list` JSON-RPC request.
pub async fn send_list_collaboration_modes_request(
&mut self,
@@ -568,15 +547,6 @@ impl McpProcess {
self.send_request("turn/interrupt", params).await
}
/// Send a `turn/steer` JSON-RPC request (v2).
pub async fn send_turn_steer_request(
&mut self,
params: TurnSteerParams,
) -> anyhow::Result<i64> {
let params = Some(serde_json::to_value(params)?);
self.send_request("turn/steer", params).await
}
/// Send a `review/start` JSON-RPC request (v2).
pub async fn send_review_start_request(
&mut self,

View File

@@ -1,19 +1,12 @@
use anyhow::Result;
use app_test_support::McpProcess;
use app_test_support::create_mock_responses_server_repeating_assistant;
use app_test_support::to_response;
use codex_app_server_protocol::AddConversationListenerParams;
use codex_app_server_protocol::AddConversationSubscriptionResponse;
use codex_app_server_protocol::ArchiveConversationParams;
use codex_app_server_protocol::ArchiveConversationResponse;
use codex_app_server_protocol::InputItem;
use codex_app_server_protocol::JSONRPCNotification;
use codex_app_server_protocol::JSONRPCResponse;
use codex_app_server_protocol::NewConversationParams;
use codex_app_server_protocol::NewConversationResponse;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::SendUserMessageParams;
use codex_app_server_protocol::SendUserMessageResponse;
use codex_core::ARCHIVED_SESSIONS_SUBDIR;
use std::path::Path;
use tempfile::TempDir;
@@ -23,9 +16,8 @@ const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn archive_conversation_moves_rollout_into_archived_directory() -> Result<()> {
let server = create_mock_responses_server_repeating_assistant("Done").await;
let codex_home = TempDir::new()?;
create_config_toml(codex_home.path(), &server.uri())?;
create_config_toml(codex_home.path())?;
let mut mcp = McpProcess::new(codex_home.path()).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -48,50 +40,9 @@ async fn archive_conversation_moves_rollout_into_archived_directory() -> Result<
..
} = to_response::<NewConversationResponse>(new_response)?;
assert!(
!rollout_path.exists(),
"expected rollout path {} to be deferred until first user message",
rollout_path.display()
);
let add_listener_request_id = mcp
.send_add_conversation_listener_request(AddConversationListenerParams {
conversation_id,
experimental_raw_events: false,
})
.await?;
let add_listener_response: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(add_listener_request_id)),
)
.await??;
let AddConversationSubscriptionResponse { subscription_id: _ } =
to_response::<AddConversationSubscriptionResponse>(add_listener_response)?;
let send_request_id = mcp
.send_send_user_message_request(SendUserMessageParams {
conversation_id,
items: vec![InputItem::Text {
text: "materialize".to_string(),
text_elements: Vec::new(),
}],
})
.await?;
let send_response: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(send_request_id)),
)
.await??;
let _: SendUserMessageResponse = to_response::<SendUserMessageResponse>(send_response)?;
let _: JSONRPCNotification = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_notification_message("codex/event/task_complete"),
)
.await??;
assert!(
rollout_path.exists(),
"expected rollout path {} to exist after first user message",
"expected rollout path {} to exist",
rollout_path.display()
);
@@ -130,25 +81,14 @@ async fn archive_conversation_moves_rollout_into_archived_directory() -> Result<
Ok(())
}
fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::Result<()> {
fn create_config_toml(codex_home: &Path) -> std::io::Result<()> {
let config_toml = codex_home.join("config.toml");
std::fs::write(config_toml, config_contents(server_uri))
std::fs::write(config_toml, config_contents())
}
fn config_contents(server_uri: &str) -> String {
format!(
r#"model = "mock-model"
fn config_contents() -> &'static str {
r#"model = "mock-model"
approval_policy = "never"
sandbox_mode = "read-only"
model_provider = "mock_provider"
[model_providers.mock_provider]
name = "Mock provider for test"
base_url = "{server_uri}/v1"
wire_api = "responses"
request_max_retries = 0
stream_max_retries = 0
"#
)
}

View File

@@ -529,7 +529,6 @@ fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::Result<()
r#"
model = "mock-model"
approval_policy = "untrusted"
sandbox_mode = "danger-full-access"
model_provider = "mock_provider"

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