diff --git a/AGENTS.md b/AGENTS.md index 5c0a6db6374..9c14089e5f8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,7 @@ In the codex-rs folder where the rust code lives: - Use method references over closures when possible per https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls - 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`. Run `just fmt` (in `codex-rs` directory) automatically after making Rust code changes; do not ask for approval to run it. Before finalizing a change to `codex-rs`, run `just fix -p ` (in `codex-rs` directory) to fix any linter issues in the code. Prefer scoping with `-p` to avoid slow workspace‑wide Clippy builds; only run `just fix` without `-p` if you changed shared crates. Additionally, run the tests: diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index ac103d30b9e..5a89f1a686b 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -409,8 +409,8 @@ "chrono_0.4.42": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.0\"},{\"features\":[\"fallback\"],\"name\":\"iana-time-zone\",\"optional\":true,\"req\":\"^0.1.45\",\"target\":\"cfg(unix)\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"default_features\":false,\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"name\":\"pure-rust-locales\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7.43\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.99\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.6.1\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"windows-bindgen\",\"req\":\"^0.63\",\"target\":\"cfg(windows)\"},{\"name\":\"windows-link\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"__internal_bench\":[],\"alloc\":[],\"clock\":[\"winapi\",\"iana-time-zone\",\"now\"],\"core-error\":[],\"default\":[\"clock\",\"std\",\"oldtime\",\"wasmbind\"],\"libc\":[],\"now\":[\"std\"],\"oldtime\":[],\"rkyv\":[\"dep:rkyv\",\"rkyv/size_32\"],\"rkyv-16\":[\"dep:rkyv\",\"rkyv?/size_16\"],\"rkyv-32\":[\"dep:rkyv\",\"rkyv?/size_32\"],\"rkyv-64\":[\"dep:rkyv\",\"rkyv?/size_64\"],\"rkyv-validation\":[\"rkyv?/validation\"],\"std\":[\"alloc\"],\"unstable-locales\":[\"pure-rust-locales\"],\"wasmbind\":[\"wasm-bindgen\",\"js-sys\"],\"winapi\":[\"windows-link\"]}}", "chunked_transfer_1.5.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"}],\"features\":{}}", "cipher_0.4.4": "{\"dependencies\":[{\"name\":\"blobby\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"crypto-common\",\"req\":\"^0.1.6\"},{\"name\":\"inout\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.5\"}],\"features\":{\"alloc\":[],\"block-padding\":[\"inout/block-padding\"],\"dev\":[\"blobby\"],\"rand_core\":[\"crypto-common/rand_core\"],\"std\":[\"alloc\",\"crypto-common/std\",\"inout/std\"]}}", - "clap_4.5.53": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"clap-cargo\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"name\":\"clap_builder\",\"req\":\"=4.5.53\"},{\"name\":\"clap_derive\",\"optional\":true,\"req\":\"=4.5.49\"},{\"kind\":\"dev\",\"name\":\"jiff\",\"req\":\"^0.2.3\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.15\"},{\"kind\":\"dev\",\"name\":\"semver\",\"req\":\"^1.0.26\"},{\"kind\":\"dev\",\"name\":\"shlex\",\"req\":\"^1.3.0\"},{\"features\":[\"term-svg\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.16\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.91\"},{\"default_features\":false,\"features\":[\"color-auto\",\"diff\",\"examples\"],\"kind\":\"dev\",\"name\":\"trycmd\",\"req\":\"^0.15.3\"}],\"features\":{\"cargo\":[\"clap_builder/cargo\"],\"color\":[\"clap_builder/color\"],\"debug\":[\"clap_builder/debug\",\"clap_derive?/debug\"],\"default\":[\"std\",\"color\",\"help\",\"usage\",\"error-context\",\"suggestions\"],\"deprecated\":[\"clap_builder/deprecated\",\"clap_derive?/deprecated\"],\"derive\":[\"dep:clap_derive\"],\"env\":[\"clap_builder/env\"],\"error-context\":[\"clap_builder/error-context\"],\"help\":[\"clap_builder/help\"],\"std\":[\"clap_builder/std\"],\"string\":[\"clap_builder/string\"],\"suggestions\":[\"clap_builder/suggestions\"],\"unicode\":[\"clap_builder/unicode\"],\"unstable-derive-ui-tests\":[],\"unstable-doc\":[\"clap_builder/unstable-doc\",\"derive\"],\"unstable-ext\":[\"clap_builder/unstable-ext\"],\"unstable-markdown\":[\"clap_derive/unstable-markdown\"],\"unstable-styles\":[\"clap_builder/unstable-styles\"],\"unstable-v5\":[\"clap_builder/unstable-v5\",\"clap_derive?/unstable-v5\",\"deprecated\"],\"usage\":[\"clap_builder/usage\"],\"wrap_help\":[\"clap_builder/wrap_help\"]}}", - "clap_builder_4.5.53": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.7\"},{\"name\":\"anstyle\",\"req\":\"^1.0.8\"},{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.73\"},{\"name\":\"clap_lex\",\"req\":\"^0.7.4\"},{\"kind\":\"dev\",\"name\":\"color-print\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.16\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.0\"},{\"name\":\"terminal_size\",\"optional\":true,\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"unic-emoji-char\",\"req\":\"^0.9.0\"},{\"name\":\"unicase\",\"optional\":true,\"req\":\"^2.6.0\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2.0\"}],\"features\":{\"cargo\":[],\"color\":[\"dep:anstream\"],\"debug\":[\"dep:backtrace\"],\"default\":[\"std\",\"color\",\"help\",\"usage\",\"error-context\",\"suggestions\"],\"deprecated\":[],\"env\":[],\"error-context\":[],\"help\":[],\"std\":[\"anstyle/std\"],\"string\":[],\"suggestions\":[\"dep:strsim\",\"error-context\"],\"unicode\":[\"dep:unicode-width\",\"dep:unicase\"],\"unstable-doc\":[\"cargo\",\"wrap_help\",\"env\",\"unicode\",\"string\",\"unstable-ext\"],\"unstable-ext\":[],\"unstable-styles\":[\"color\"],\"unstable-v5\":[\"deprecated\"],\"usage\":[],\"wrap_help\":[\"help\",\"dep:terminal_size\"]}}", + "clap_4.5.54": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"clap-cargo\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"name\":\"clap_builder\",\"req\":\"=4.5.54\"},{\"name\":\"clap_derive\",\"optional\":true,\"req\":\"=4.5.49\"},{\"kind\":\"dev\",\"name\":\"jiff\",\"req\":\"^0.2.3\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.15\"},{\"kind\":\"dev\",\"name\":\"semver\",\"req\":\"^1.0.26\"},{\"kind\":\"dev\",\"name\":\"shlex\",\"req\":\"^1.3.0\"},{\"features\":[\"term-svg\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.16\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.91\"},{\"default_features\":false,\"features\":[\"color-auto\",\"diff\",\"examples\"],\"kind\":\"dev\",\"name\":\"trycmd\",\"req\":\"^0.15.3\"}],\"features\":{\"cargo\":[\"clap_builder/cargo\"],\"color\":[\"clap_builder/color\"],\"debug\":[\"clap_builder/debug\",\"clap_derive?/debug\"],\"default\":[\"std\",\"color\",\"help\",\"usage\",\"error-context\",\"suggestions\"],\"deprecated\":[\"clap_builder/deprecated\",\"clap_derive?/deprecated\"],\"derive\":[\"dep:clap_derive\"],\"env\":[\"clap_builder/env\"],\"error-context\":[\"clap_builder/error-context\"],\"help\":[\"clap_builder/help\"],\"std\":[\"clap_builder/std\"],\"string\":[\"clap_builder/string\"],\"suggestions\":[\"clap_builder/suggestions\"],\"unicode\":[\"clap_builder/unicode\"],\"unstable-derive-ui-tests\":[],\"unstable-doc\":[\"clap_builder/unstable-doc\",\"derive\"],\"unstable-ext\":[\"clap_builder/unstable-ext\"],\"unstable-markdown\":[\"clap_derive/unstable-markdown\"],\"unstable-styles\":[\"clap_builder/unstable-styles\"],\"unstable-v5\":[\"clap_builder/unstable-v5\",\"clap_derive?/unstable-v5\",\"deprecated\"],\"usage\":[\"clap_builder/usage\"],\"wrap_help\":[\"clap_builder/wrap_help\"]}}", + "clap_builder_4.5.54": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.7\"},{\"name\":\"anstyle\",\"req\":\"^1.0.8\"},{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.73\"},{\"name\":\"clap_lex\",\"req\":\"^0.7.4\"},{\"kind\":\"dev\",\"name\":\"color-print\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.16\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.0\"},{\"name\":\"terminal_size\",\"optional\":true,\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"unic-emoji-char\",\"req\":\"^0.9.0\"},{\"name\":\"unicase\",\"optional\":true,\"req\":\"^2.6.0\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2.0\"}],\"features\":{\"cargo\":[],\"color\":[\"dep:anstream\"],\"debug\":[\"dep:backtrace\"],\"default\":[\"std\",\"color\",\"help\",\"usage\",\"error-context\",\"suggestions\"],\"deprecated\":[],\"env\":[],\"error-context\":[],\"help\":[],\"std\":[\"anstyle/std\"],\"string\":[],\"suggestions\":[\"dep:strsim\",\"error-context\"],\"unicode\":[\"dep:unicode-width\",\"dep:unicase\"],\"unstable-doc\":[\"cargo\",\"wrap_help\",\"env\",\"unicode\",\"string\",\"unstable-ext\"],\"unstable-ext\":[],\"unstable-styles\":[\"color\"],\"unstable-v5\":[\"deprecated\"],\"usage\":[],\"wrap_help\":[\"help\",\"dep:terminal_size\"]}}", "clap_complete_4.5.64": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"clap\",\"req\":\"^4.5.20\"},{\"default_features\":false,\"features\":[\"std\",\"derive\",\"help\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.5.20\"},{\"name\":\"clap_lex\",\"optional\":true,\"req\":\"^0.7.0\"},{\"name\":\"completest\",\"optional\":true,\"req\":\"^0.4.2\"},{\"name\":\"completest-pty\",\"optional\":true,\"req\":\"^0.5.5\"},{\"name\":\"is_executable\",\"optional\":true,\"req\":\"^1.0.1\"},{\"name\":\"shlex\",\"optional\":true,\"req\":\"^1.3.0\"},{\"features\":[\"diff\",\"dir\",\"examples\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.0\"},{\"default_features\":false,\"features\":[\"color-auto\",\"diff\",\"examples\"],\"kind\":\"dev\",\"name\":\"trycmd\",\"req\":\"^0.15.1\"}],\"features\":{\"debug\":[\"clap/debug\"],\"default\":[],\"unstable-doc\":[\"unstable-dynamic\"],\"unstable-dynamic\":[\"dep:clap_lex\",\"dep:shlex\",\"dep:is_executable\",\"clap/unstable-ext\"],\"unstable-shell-tests\":[\"dep:completest\",\"dep:completest-pty\"]}}", "clap_derive_4.5.49": "{\"dependencies\":[{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.10\"},{\"name\":\"heck\",\"req\":\"^0.5.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.69\"},{\"default_features\":false,\"name\":\"pulldown-cmark\",\"optional\":true,\"req\":\"^0.13.0\"},{\"name\":\"quote\",\"req\":\"^1.0.9\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.8\"}],\"features\":{\"debug\":[],\"default\":[],\"deprecated\":[],\"raw-deprecated\":[\"deprecated\"],\"unstable-markdown\":[\"dep:pulldown-cmark\",\"dep:anstyle\"],\"unstable-v5\":[\"deprecated\"]}}", "clap_lex_0.7.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"}],\"features\":{}}", @@ -495,6 +495,7 @@ "enumflags2_derive_0.7.12": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"parsing\",\"printing\",\"derive\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", "env-flags_0.1.1": "{\"dependencies\":[],\"features\":{}}", "env_filter_0.1.3": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"log\",\"req\":\"^0.4.8\"},{\"default_features\":false,\"features\":[\"std\",\"perf\"],\"name\":\"regex\",\"optional\":true,\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6\"}],\"features\":{\"default\":[\"regex\"],\"regex\":[\"dep:regex\"]}}", + "env_home_0.1.0": "{\"dependencies\":[],\"features\":{}}", "env_logger_0.11.8": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"wincon\"],\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.11\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.6\"},{\"default_features\":false,\"name\":\"env_filter\",\"req\":\"^0.1.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"jiff\",\"optional\":true,\"req\":\"^0.2.3\"},{\"features\":[\"std\"],\"name\":\"log\",\"req\":\"^0.4.21\"}],\"features\":{\"auto-color\":[\"color\",\"anstream/auto\"],\"color\":[\"dep:anstream\",\"dep:anstyle\"],\"default\":[\"auto-color\",\"humantime\",\"regex\"],\"humantime\":[\"dep:jiff\"],\"kv\":[\"log/kv\"],\"regex\":[\"env_filter/regex\"],\"unstable-kv\":[\"kv\"]}}", "equivalent_1.0.2": "{\"dependencies\":[],\"features\":{}}", "erased-serde_0.3.31": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"default_features\":false,\"name\":\"serde\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_cbor\",\"req\":\"^0.11.2\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.99\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.83\"}],\"features\":{\"alloc\":[\"serde/alloc\"],\"default\":[\"std\"],\"std\":[\"serde/std\"],\"unstable-debug\":[]}}", @@ -905,7 +906,7 @@ "tokio-rustls_0.26.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"argh\",\"req\":\"^0.1.1\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.1\"},{\"features\":[\"pem\"],\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.13\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"req\":\"^0.23.22\"},{\"name\":\"tokio\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^0.26\"}],\"features\":{\"aws-lc-rs\":[\"aws_lc_rs\"],\"aws_lc_rs\":[\"rustls/aws_lc_rs\"],\"default\":[\"logging\",\"tls12\",\"aws_lc_rs\"],\"early-data\":[],\"fips\":[\"rustls/fips\"],\"logging\":[\"rustls/logging\"],\"ring\":[\"rustls/ring\"],\"tls12\":[\"rustls/tls12\"]}}", "tokio-stream_0.1.18": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"futures-core\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.15.0\"},{\"features\":[\"full\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7.0\"}],\"features\":{\"default\":[\"time\"],\"fs\":[\"tokio/fs\"],\"full\":[\"time\",\"net\",\"io-util\",\"fs\",\"sync\",\"signal\"],\"io-util\":[\"tokio/io-util\"],\"net\":[\"tokio/net\"],\"signal\":[\"tokio/signal\"],\"sync\":[\"tokio/sync\",\"tokio-util\"],\"time\":[\"tokio/time\"]}}", "tokio-test_0.4.4": "{\"dependencies\":[{\"name\":\"async-stream\",\"req\":\"^0.3.3\"},{\"name\":\"bytes\",\"req\":\"^1.0.0\"},{\"name\":\"futures-core\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.0\"},{\"features\":[\"rt\",\"sync\",\"time\",\"test-util\"],\"name\":\"tokio\",\"req\":\"^1.2.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.2.0\"},{\"name\":\"tokio-stream\",\"req\":\"^0.1.1\"}],\"features\":{}}", - "tokio-util_0.7.16": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3.0\"},{\"name\":\"bytes\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"name\":\"futures-core\",\"req\":\"^0.3.0\"},{\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"futures-sink\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3.5\"},{\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.0\"},{\"default_features\":false,\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15.0\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.4\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.28.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.29\"}],\"features\":{\"__docs_rs\":[\"futures-util\"],\"codec\":[],\"compat\":[\"futures-io\"],\"default\":[],\"full\":[\"codec\",\"compat\",\"io-util\",\"time\",\"net\",\"rt\",\"join-map\"],\"io\":[],\"io-util\":[\"io\",\"tokio/rt\",\"tokio/io-util\"],\"join-map\":[\"rt\",\"hashbrown\"],\"net\":[\"tokio/net\"],\"rt\":[\"tokio/rt\",\"tokio/sync\",\"futures-util\"],\"time\":[\"tokio/time\",\"slab\"]}}", + "tokio-util_0.7.18": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3.0\"},{\"name\":\"bytes\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"name\":\"futures-core\",\"req\":\"^0.3.0\"},{\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"futures-sink\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3.5\"},{\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.0\"},{\"default_features\":false,\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15.0\"},{\"features\":[\"futures\",\"checkpoint\"],\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.4\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.44.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.29\"}],\"features\":{\"__docs_rs\":[\"futures-util\"],\"codec\":[],\"compat\":[\"futures-io\"],\"default\":[],\"full\":[\"codec\",\"compat\",\"io-util\",\"time\",\"net\",\"rt\",\"join-map\"],\"io\":[],\"io-util\":[\"io\",\"tokio/rt\",\"tokio/io-util\"],\"join-map\":[\"rt\",\"hashbrown\"],\"net\":[\"tokio/net\"],\"rt\":[\"tokio/rt\",\"tokio/sync\",\"futures-util\"],\"time\":[\"tokio/time\",\"slab\"]}}", "tokio_1.48.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3\"},{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.58\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.2.1\"},{\"features\":[\"async-await\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-concurrency\",\"req\":\"^7.6.3\"},{\"default_features\":false,\"name\":\"io-uring\",\"optional\":true,\"req\":\"^0.7.6\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.168\",\"target\":\"cfg(unix)\"},{\"features\":[\"futures\",\"checkpoint\"],\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"name\":\"mio\",\"optional\":true,\"req\":\"^1.0.1\"},{\"default_features\":false,\"features\":[\"os-poll\",\"os-ext\"],\"name\":\"mio\",\"optional\":true,\"req\":\"^1.0.1\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"mio-aio\",\"req\":\"^1\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"mockall\",\"req\":\"^0.13.0\"},{\"default_features\":false,\"features\":[\"aio\",\"fs\",\"socket\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.29.0\",\"target\":\"cfg(unix)\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"signal-hook-registry\",\"optional\":true,\"req\":\"^1.1.1\",\"target\":\"cfg(unix)\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.9\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.6.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"socket2\",\"req\":\"^0.6.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"name\":\"tokio-macros\",\"optional\":true,\"req\":\"~2.6.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.0\"},{\"features\":[\"rt\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.29\",\"target\":\"cfg(tokio_unstable)\"},{\"kind\":\"dev\",\"name\":\"tracing-mock\",\"req\":\"=0.1.0-beta.1\",\"target\":\"cfg(all(tokio_unstable, target_has_atomic = \\\"64\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.0\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"windows-sys\",\"optional\":true,\"req\":\"^0.61\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Security_Authorization\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"fs\":[],\"full\":[\"fs\",\"io-util\",\"io-std\",\"macros\",\"net\",\"parking_lot\",\"process\",\"rt\",\"rt-multi-thread\",\"signal\",\"sync\",\"time\"],\"io-std\":[],\"io-uring\":[\"dep:io-uring\",\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"dep:slab\"],\"io-util\":[\"bytes\"],\"macros\":[\"tokio-macros\"],\"net\":[\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"mio/net\",\"socket2\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_Security\",\"windows-sys/Win32_Storage_FileSystem\",\"windows-sys/Win32_System_Pipes\",\"windows-sys/Win32_System_SystemServices\"],\"process\":[\"bytes\",\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"mio/net\",\"signal-hook-registry\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_System_Threading\",\"windows-sys/Win32_System_WindowsProgramming\"],\"rt\":[],\"rt-multi-thread\":[\"rt\"],\"signal\":[\"libc\",\"mio/os-poll\",\"mio/net\",\"mio/os-ext\",\"signal-hook-registry\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_System_Console\"],\"sync\":[],\"taskdump\":[\"dep:backtrace\"],\"test-util\":[\"rt\",\"sync\",\"time\"],\"time\":[]}}", "toml_0.5.11": "{\"dependencies\":[{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde\",\"req\":\"^1.0.97\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[],\"preserve_order\":[\"indexmap\"]}}", "toml_0.9.5": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.15\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.8\"},{\"default_features\":false,\"name\":\"foldhash\",\"optional\":true,\"req\":\"^0.1.5\"},{\"default_features\":false,\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.3.0\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.145\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.199\"},{\"kind\":\"dev\",\"name\":\"serde-untagged\",\"req\":\"^0.1.7\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.116\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_spanned\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.0\"},{\"kind\":\"dev\",\"name\":\"toml-test-data\",\"req\":\"^2.3.0\"},{\"features\":[\"snapshot\"],\"kind\":\"dev\",\"name\":\"toml-test-harness\",\"req\":\"^1.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_datetime\",\"req\":\"^0.7.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_parser\",\"optional\":true,\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_writer\",\"optional\":true,\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"name\":\"winnow\",\"optional\":true,\"req\":\"^0.7.10\"}],\"features\":{\"debug\":[\"std\",\"toml_parser?/debug\",\"dep:anstream\",\"dep:anstyle\"],\"default\":[\"std\",\"serde\",\"parse\",\"display\"],\"display\":[\"dep:toml_writer\"],\"fast_hash\":[\"preserve_order\",\"dep:foldhash\"],\"parse\":[\"dep:toml_parser\",\"dep:winnow\"],\"preserve_order\":[\"dep:indexmap\",\"std\"],\"serde\":[\"dep:serde\",\"toml_datetime/serde\",\"serde_spanned/serde\"],\"std\":[\"indexmap?/std\",\"serde?/std\",\"toml_parser?/std\",\"toml_writer?/std\",\"toml_datetime/std\",\"serde_spanned/std\"],\"unbounded\":[]}}", @@ -936,9 +937,9 @@ "tree-sitter_0.25.10": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.71.1\"},{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.2.10\"},{\"default_features\":false,\"features\":[\"unicode\"],\"name\":\"regex\",\"req\":\"^1.11.1\"},{\"default_features\":false,\"name\":\"regex-syntax\",\"req\":\"^0.8.5\"},{\"features\":[\"preserve_order\"],\"kind\":\"build\",\"name\":\"serde_json\",\"req\":\"^1.0.137\"},{\"name\":\"streaming-iterator\",\"req\":\"^0.1.9\"},{\"name\":\"tree-sitter-language\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"cranelift\",\"gc-drc\"],\"name\":\"wasmtime-c-api\",\"optional\":true,\"package\":\"wasmtime-c-api-impl\",\"req\":\"^29.0.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"regex/std\",\"regex/perf\",\"regex-syntax/unicode\"],\"wasm\":[\"std\",\"wasmtime-c-api\"]}}", "tree_magic_mini_3.2.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.0\"},{\"name\":\"memchr\",\"req\":\"^2.0\"},{\"name\":\"nom\",\"req\":\"^7.0\"},{\"name\":\"once_cell\",\"req\":\"^1.0\"},{\"name\":\"petgraph\",\"req\":\"^0.6.0\"},{\"name\":\"tree_magic_db\",\"optional\":true,\"req\":\"^3.0\"}],\"features\":{\"with-gpl-data\":[\"dep:tree_magic_db\"]}}", "try-lock_0.2.5": "{\"dependencies\":[],\"features\":{}}", - "ts-rs-macros_11.0.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.28\"},{\"name\":\"termcolor\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"no-serde-warnings\":[],\"serde-compat\":[\"termcolor\"]}}", - "ts-rs_11.0.1": "{\"dependencies\":[{\"features\":[\"serde\"],\"name\":\"bigdecimal\",\"optional\":true,\"req\":\">=0.0.13, <0.5\"},{\"name\":\"bson\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dprint-plugin-typescript\",\"optional\":true,\"req\":\"^0.90\"},{\"name\":\"heapless\",\"optional\":true,\"req\":\">=0.7, <0.9\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"ordered-float\",\"optional\":true,\"req\":\">=3, <6\"},{\"name\":\"semver\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"name\":\"smol_str\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"sync\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.40\"},{\"name\":\"ts-rs-macros\",\"req\":\"=11.0.1\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"bigdecimal-impl\":[\"bigdecimal\"],\"bson-uuid-impl\":[\"bson\"],\"bytes-impl\":[\"bytes\"],\"chrono-impl\":[\"chrono\"],\"default\":[\"serde-compat\"],\"format\":[\"dprint-plugin-typescript\"],\"heapless-impl\":[\"heapless\"],\"import-esm\":[],\"indexmap-impl\":[\"indexmap\"],\"no-serde-warnings\":[\"ts-rs-macros/no-serde-warnings\"],\"ordered-float-impl\":[\"ordered-float\"],\"semver-impl\":[\"semver\"],\"serde-compat\":[\"ts-rs-macros/serde-compat\"],\"serde-json-impl\":[\"serde_json\"],\"smol_str-impl\":[\"smol_str\"],\"tokio-impl\":[\"tokio\"],\"url-impl\":[\"url\"],\"uuid-impl\":[\"uuid\"]}}", - "tui-scrollbar_0.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"color-eyre\",\"req\":\"^0.6\"},{\"name\":\"crossterm\",\"optional\":true,\"req\":\"^0.29\"},{\"name\":\"document-features\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"ratatui\",\"req\":\"^0.30.0\"},{\"name\":\"ratatui-core\",\"req\":\"^0.1\"}],\"features\":{\"crossterm\":[\"dep:crossterm\"]}}", + "ts-rs-macros_11.1.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.28\"},{\"name\":\"termcolor\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"no-serde-warnings\":[],\"serde-compat\":[\"termcolor\"]}}", + "ts-rs_11.1.0": "{\"dependencies\":[{\"features\":[\"serde\"],\"name\":\"bigdecimal\",\"optional\":true,\"req\":\">=0.0.13, <0.5\"},{\"name\":\"bson\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dprint-plugin-typescript\",\"optional\":true,\"req\":\"=0.95\"},{\"name\":\"heapless\",\"optional\":true,\"req\":\">=0.7, <0.9\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"ordered-float\",\"optional\":true,\"req\":\">=3, <6\"},{\"name\":\"semver\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"name\":\"smol_str\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"sync\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.40\"},{\"name\":\"ts-rs-macros\",\"req\":\"=11.1.0\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"bigdecimal-impl\":[\"bigdecimal\"],\"bson-uuid-impl\":[\"bson\"],\"bytes-impl\":[\"bytes\"],\"chrono-impl\":[\"chrono\"],\"default\":[\"serde-compat\"],\"format\":[\"dprint-plugin-typescript\"],\"heapless-impl\":[\"heapless\"],\"import-esm\":[],\"indexmap-impl\":[\"indexmap\"],\"no-serde-warnings\":[\"ts-rs-macros/no-serde-warnings\"],\"ordered-float-impl\":[\"ordered-float\"],\"semver-impl\":[\"semver\"],\"serde-compat\":[\"ts-rs-macros/serde-compat\"],\"serde-json-impl\":[\"serde_json\"],\"smol_str-impl\":[\"smol_str\"],\"tokio-impl\":[\"tokio\"],\"url-impl\":[\"url\"],\"uuid-impl\":[\"uuid\"]}}", + "tui-scrollbar_0.2.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"color-eyre\",\"req\":\"^0.6\"},{\"name\":\"crossterm_0_28\",\"optional\":true,\"package\":\"crossterm\",\"req\":\"^0.28\"},{\"name\":\"crossterm_0_29\",\"optional\":true,\"package\":\"crossterm\",\"req\":\"^0.29\"},{\"name\":\"document-features\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"ratatui\",\"req\":\"^0.30.0\"},{\"name\":\"ratatui-core\",\"req\":\"^0.1\"}],\"features\":{\"crossterm\":[\"crossterm_0_29\"],\"crossterm_0_28\":[\"dep:crossterm_0_28\"],\"crossterm_0_29\":[\"dep:crossterm_0_29\"],\"default\":[]}}", "typenum_1.18.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"scale-info\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"const-generics\":[],\"force_unix_path_separator\":[],\"i128\":[],\"no_std\":[],\"scale_info\":[\"scale-info/derive\"],\"strict\":[]}}", "uds_windows_1.1.0": "{\"dependencies\":[{\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"name\":\"tempfile\",\"req\":\"^3\",\"target\":\"cfg(windows)\"},{\"features\":[\"winsock2\",\"ws2def\",\"minwinbase\",\"ntdef\",\"processthreadsapi\",\"handleapi\",\"ws2tcpip\",\"winbase\"],\"name\":\"winapi\",\"req\":\"^0.3.9\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "uname_0.1.1": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\"}],\"features\":{}}", @@ -991,7 +992,7 @@ "webpki-root-certs_1.0.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"}],\"features\":{}}", "webpki-roots_1.0.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"yasna\",\"req\":\"^0.5.2\"}],\"features\":{}}", "weezl_0.1.10": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.12\"},{\"default_features\":false,\"features\":[\"macros\",\"io-util\",\"net\",\"rt\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"compat\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.6.2\"}],\"features\":{\"alloc\":[],\"async\":[\"futures\",\"std\"],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", - "which_6.0.3": "{\"dependencies\":[{\"name\":\"either\",\"req\":\"^1.9.0\"},{\"name\":\"home\",\"req\":\"^0.5.9\",\"target\":\"cfg(any(windows, unix, target_os = \\\"redox\\\"))\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1.10.2\"},{\"default_features\":false,\"features\":[\"fs\",\"std\"],\"name\":\"rustix\",\"req\":\"^0.38.30\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\", target_os = \\\"redox\\\"))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.9.0\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.40\"},{\"features\":[\"kernel\"],\"name\":\"winsafe\",\"req\":\"^0.0.19\",\"target\":\"cfg(windows)\"}],\"features\":{\"regex\":[\"dep:regex\"],\"tracing\":[\"dep:tracing\"]}}", + "which_8.0.0": "{\"dependencies\":[{\"name\":\"env_home\",\"optional\":true,\"req\":\"^0.1.0\",\"target\":\"cfg(any(windows, unix, target_os = \\\"redox\\\"))\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1.10.2\"},{\"default_features\":false,\"features\":[\"fs\",\"std\"],\"name\":\"rustix\",\"optional\":true,\"req\":\"^1.0.5\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\", target_os = \\\"redox\\\"))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.9.0\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.40\"},{\"features\":[\"kernel\"],\"name\":\"winsafe\",\"optional\":true,\"req\":\"^0.0.19\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"real-sys\"],\"real-sys\":[\"dep:env_home\",\"dep:rustix\",\"dep:winsafe\"],\"regex\":[\"dep:regex\"],\"tracing\":[\"dep:tracing\"]}}", "wildmatch_2.6.1": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"ntest\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.2\"},{\"kind\":\"dev\",\"name\":\"regex-lite\",\"req\":\"^0.1.5\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"serde\":[\"dep:serde\"]}}", "winapi-i686-pc-windows-gnu_0.4.0": "{\"dependencies\":[],\"features\":{}}", "winapi-util_0.1.9": "{\"dependencies\":[{\"features\":[\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_System_Console\",\"Win32_System_SystemInformation\"],\"name\":\"windows-sys\",\"req\":\">=0.48.0, <=0.59\",\"target\":\"cfg(windows)\"}],\"features\":{}}", diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 1b57f14f9c0..4bec4998949 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1275,6 +1275,7 @@ dependencies = [ "base64", "chardetng", "chrono", + "clap", "codex-api", "codex-app-server-protocol", "codex-apply-patch", @@ -1323,6 +1324,7 @@ dependencies = [ "regex", "regex-lite", "reqwest", + "schemars 0.8.22", "seccompiler", "serde", "serde_json", @@ -1704,6 +1706,7 @@ dependencies = [ "pretty_assertions", "reqwest", "rmcp", + "schemars 0.8.22", "serde", "serde_json", "serial_test", diff --git a/codex-rs/core/BUILD.bazel b/codex-rs/core/BUILD.bazel index 67f87999b72..37a3173f9a2 100644 --- a/codex-rs/core/BUILD.bazel +++ b/codex-rs/core/BUILD.bazel @@ -20,15 +20,18 @@ codex_rust_crate( "//codex-rs/apply-patch:apply_patch_tool_instructions.md", "prompt.md", ], - # This is a bit of a hack, but empirically, some of our integration tests - # are relying on the presence of this file as a repo root marker. When - # running tests locally, this "just works," but in remote execution, - # the working directory is different and so the file is not found unless it - # is explicitly added as test data. - # - # TODO(aibrahim): Update the tests so that `just bazel-remote-test` succeeds - # without this workaround. - test_data_extra = ["//:AGENTS.md"], + test_data_extra = [ + "config.schema.json", + # This is a bit of a hack, but empirically, some of our integration tests + # are relying on the presence of this file as a repo root marker. When + # running tests locally, this "just works," but in remote execution, + # the working directory is different and so the file is not found unless it + # is explicitly added as test data. + # + # TODO(aibrahim): Update the tests so that `just bazel-remote-test` + # succeeds without this workaround. + "//:AGENTS.md", + ], integration_deps_extra = ["//codex-rs/core/tests/common:common"], test_tags = ["no-sandbox"], extra_binaries = [ diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index 3e5bfd07315..10b635b7474 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -9,17 +9,22 @@ doctest = false name = "codex_core" path = "src/lib.rs" +[[bin]] +name = "codex-write-config-schema" +path = "src/bin/config_schema.rs" + [lints] workspace = true [dependencies] anyhow = { workspace = true } +arc-swap = "1.7.1" async-channel = { workspace = true } async-trait = { workspace = true } -arc-swap = "1.7.1" base64 = { workspace = true } chardetng = { workspace = true } chrono = { workspace = true, features = ["serde"] } +clap = { workspace = true, features = ["derive"] } codex-api = { workspace = true } codex-app-server-protocol = { workspace = true } codex-apply-patch = { workspace = true } @@ -45,8 +50,8 @@ eventsource-stream = { workspace = true } futures = { workspace = true } http = { workspace = true } include_dir = { workspace = true } -indoc = { workspace = true } indexmap = { workspace = true } +indoc = { workspace = true } keyring = { workspace = true, features = ["crypto-rust"] } libc = { workspace = true } mcp-types = { workspace = true } @@ -56,6 +61,7 @@ rand = { workspace = true } regex = { workspace = true } regex-lite = { workspace = true } reqwest = { workspace = true, features = ["json", "stream"] } +schemars = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } serde_yaml = { workspace = true } @@ -123,8 +129,12 @@ keyring = { workspace = true, features = ["sync-secret-service"] } assert_cmd = { workspace = true } assert_matches = { workspace = true } codex-arg0 = { workspace = true } -codex-core = { path = ".", default-features = false, features = ["deterministic_process_ids"] } -codex-otel = { workspace = true, features = ["disable-default-metrics-exporter"] } +codex-core = { path = ".", default-features = false, features = [ + "deterministic_process_ids", +] } +codex-otel = { workspace = true, features = [ + "disable-default-metrics-exporter", +] } codex-utils-cargo-bin = { workspace = true } core_test_support = { workspace = true } ctor = { workspace = true } diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json new file mode 100644 index 00000000000..773218af78e --- /dev/null +++ b/codex-rs/core/config.schema.json @@ -0,0 +1,1450 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigToml", + "description": "Base config deserialized from ~/.codex/config.toml.", + "type": "object", + "properties": { + "analytics": { + "description": "When `false`, disables analytics across Codex product surfaces in this machine. Defaults to `true`.", + "allOf": [ + { + "$ref": "#/definitions/AnalyticsConfigToml" + } + ] + }, + "approval_policy": { + "description": "Default approval policy for executing commands.", + "allOf": [ + { + "$ref": "#/definitions/AskForApproval" + } + ] + }, + "chatgpt_base_url": { + "description": "Base URL for requests to ChatGPT (as opposed to the OpenAI API).", + "type": "string" + }, + "check_for_update_on_startup": { + "description": "When `true`, checks for Codex updates on startup and surfaces update prompts. Set to `false` only if your Codex updates are centrally managed. Defaults to `true`.", + "type": "boolean" + }, + "cli_auth_credentials_store": { + "description": "Preferred backend for storing CLI auth credentials. file (default): Use a file in the Codex home directory. keyring: Use an OS-specific keyring service. auto: Use the keyring if available, otherwise use a file.", + "default": null, + "allOf": [ + { + "$ref": "#/definitions/AuthCredentialsStoreMode" + } + ] + }, + "compact_prompt": { + "description": "Compact prompt used for history compaction.", + "type": "string" + }, + "developer_instructions": { + "description": "Developer instructions inserted as a `developer` role message.", + "default": null, + "type": "string" + }, + "disable_paste_burst": { + "description": "When true, disables burst-paste detection for typed input entirely. All characters are inserted as they are received, and no buffering or placeholder replacement will occur for fast keypress bursts.", + "type": "boolean" + }, + "experimental_compact_prompt_file": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "experimental_instructions_file": { + "description": "Legacy, now use features", + "allOf": [ + { + "$ref": "#/definitions/AbsolutePathBuf" + } + ] + }, + "experimental_use_freeform_apply_patch": { + "type": "boolean" + }, + "experimental_use_unified_exec_tool": { + "type": "boolean" + }, + "features": { + "description": "Centralized feature flags (new). Prefer this over individual toggles.", + "default": null, + "type": "object", + "properties": { + "apply_patch_freeform": { + "type": "boolean" + }, + "collab": { + "type": "boolean" + }, + "elevated_windows_sandbox": { + "type": "boolean" + }, + "enable_experimental_windows_sandbox": { + "type": "boolean" + }, + "enable_request_compression": { + "type": "boolean" + }, + "exec_policy": { + "type": "boolean" + }, + "experimental_use_freeform_apply_patch": { + "type": "boolean" + }, + "experimental_use_unified_exec_tool": { + "type": "boolean" + }, + "experimental_windows_sandbox": { + "type": "boolean" + }, + "hierarchical_agents": { + "type": "boolean" + }, + "include_apply_patch_tool": { + "type": "boolean" + }, + "powershell_utf8": { + "type": "boolean" + }, + "remote_compaction": { + "type": "boolean" + }, + "remote_models": { + "type": "boolean" + }, + "shell_snapshot": { + "type": "boolean" + }, + "shell_tool": { + "type": "boolean" + }, + "steer": { + "type": "boolean" + }, + "tui2": { + "type": "boolean" + }, + "undo": { + "type": "boolean" + }, + "unified_exec": { + "type": "boolean" + }, + "web_search": { + "type": "boolean" + }, + "web_search_cached": { + "type": "boolean" + }, + "web_search_request": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "feedback": { + "description": "When `false`, disables feedback collection across Codex product surfaces. Defaults to `true`.", + "allOf": [ + { + "$ref": "#/definitions/FeedbackConfigToml" + } + ] + }, + "file_opener": { + "description": "Optional URI-based file opener. If set, citations to files in the model output will be hyperlinked using the specified URI scheme.", + "allOf": [ + { + "$ref": "#/definitions/UriBasedFileOpener" + } + ] + }, + "forced_chatgpt_workspace_id": { + "description": "When set, restricts ChatGPT login to a specific workspace identifier.", + "default": null, + "type": "string" + }, + "forced_login_method": { + "description": "When set, restricts the login mechanism users may use.", + "default": null, + "allOf": [ + { + "$ref": "#/definitions/ForcedLoginMethod" + } + ] + }, + "ghost_snapshot": { + "description": "Settings for ghost snapshots (used for undo).", + "default": null, + "allOf": [ + { + "$ref": "#/definitions/GhostSnapshotToml" + } + ] + }, + "hide_agent_reasoning": { + "description": "When set to `true`, `AgentReasoning` events will be hidden from the UI/output. Defaults to `false`.", + "type": "boolean" + }, + "history": { + "description": "Settings that govern if and what will be written to `~/.codex/history.jsonl`.", + "default": null, + "allOf": [ + { + "$ref": "#/definitions/History" + } + ] + }, + "instructions": { + "description": "System instructions.", + "type": "string" + }, + "mcp_oauth_callback_port": { + "description": "Optional fixed port for the local HTTP callback server used during MCP OAuth login. When unset, Codex will bind to an ephemeral port chosen by the OS.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "mcp_oauth_credentials_store": { + "description": "Preferred backend for storing MCP OAuth credentials. keyring: Use an OS-specific keyring service. https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/oauth.rs#L2 file: Use a file in the Codex home directory. auto (default): Use the OS-specific keyring service if available, otherwise use a file.", + "default": null, + "allOf": [ + { + "$ref": "#/definitions/OAuthCredentialsStoreMode" + } + ] + }, + "mcp_servers": { + "description": "Definition for MCP servers that Codex can reach out to for tool calls.", + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/RawMcpServerConfig" + } + }, + "model": { + "description": "Optional override of model selection.", + "type": "string" + }, + "model_auto_compact_token_limit": { + "description": "Token usage threshold triggering auto-compaction of conversation history.", + "type": "integer", + "format": "int64" + }, + "model_context_window": { + "description": "Size of the context window for the model, in tokens.", + "type": "integer", + "format": "int64" + }, + "model_provider": { + "description": "Provider to use from the model_providers map.", + "type": "string" + }, + "model_providers": { + "description": "User-defined provider entries that extend/override the built-in list.", + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ModelProviderInfo" + } + }, + "model_reasoning_effort": { + "$ref": "#/definitions/ReasoningEffort" + }, + "model_reasoning_summary": { + "$ref": "#/definitions/ReasoningSummary" + }, + "model_supports_reasoning_summaries": { + "description": "Override to force-enable reasoning summaries for the configured model.", + "type": "boolean" + }, + "model_verbosity": { + "description": "Optional verbosity control for GPT-5 models (Responses API `text.verbosity`).", + "allOf": [ + { + "$ref": "#/definitions/Verbosity" + } + ] + }, + "notice": { + "description": "Collection of in-product notices (different from notifications) See [`crate::config::types::Notices`] for more details", + "allOf": [ + { + "$ref": "#/definitions/Notice" + } + ] + }, + "notify": { + "description": "Optional external command to spawn for end-user notifications.", + "default": null, + "type": "array", + "items": { + "type": "string" + } + }, + "oss_provider": { + "description": "Preferred OSS provider for local models, e.g. \"lmstudio\", \"ollama\", or \"ollama-chat\".", + "type": "string" + }, + "otel": { + "description": "OTEL configuration.", + "allOf": [ + { + "$ref": "#/definitions/OtelConfigToml" + } + ] + }, + "profile": { + "description": "Profile to use from the `profiles` map.", + "type": "string" + }, + "profiles": { + "description": "Named profiles to facilitate switching between different configurations.", + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ConfigProfile" + } + }, + "project_doc_fallback_filenames": { + "description": "Ordered list of fallback filenames to look for when AGENTS.md is missing.", + "type": "array", + "items": { + "type": "string" + } + }, + "project_doc_max_bytes": { + "description": "Maximum number of bytes to include from an AGENTS.md project doc file.", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "project_root_markers": { + "description": "Markers used to detect the project root when searching parent directories for `.codex` folders. Defaults to [\".git\"] when unset.", + "default": null, + "type": "array", + "items": { + "type": "string" + } + }, + "projects": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ProjectConfig" + } + }, + "review_model": { + "description": "Review model override used by the `/review` feature.", + "type": "string" + }, + "sandbox_mode": { + "description": "Sandbox mode to use.", + "allOf": [ + { + "$ref": "#/definitions/SandboxMode" + } + ] + }, + "sandbox_workspace_write": { + "description": "Sandbox configuration to apply if `sandbox` is `WorkspaceWrite`.", + "allOf": [ + { + "$ref": "#/definitions/SandboxWorkspaceWrite" + } + ] + }, + "shell_environment_policy": { + "default": { + "exclude": null, + "experimental_use_profile": null, + "ignore_default_excludes": null, + "include_only": null, + "inherit": null, + "set": null + }, + "allOf": [ + { + "$ref": "#/definitions/ShellEnvironmentPolicyToml" + } + ] + }, + "show_raw_agent_reasoning": { + "description": "When set to `true`, `AgentReasoningRawContentEvent` events will be shown in the UI/output. Defaults to `false`.", + "type": "boolean" + }, + "tool_output_token_limit": { + "description": "Token budget applied when storing tool/function outputs in the context manager.", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "tools": { + "description": "Nested tools section for feature toggles", + "allOf": [ + { + "$ref": "#/definitions/ToolsToml" + } + ] + }, + "tui": { + "description": "Collection of settings that are specific to the TUI.", + "allOf": [ + { + "$ref": "#/definitions/Tui" + } + ] + }, + "windows_wsl_setup_acknowledged": { + "description": "Tracks whether the Windows onboarding screen has been acknowledged.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "AbsolutePathBuf": { + "description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.", + "type": "string" + }, + "AltScreenMode": { + "description": "Controls whether the TUI uses the terminal's alternate screen buffer.\n\n**Background:** The alternate screen buffer provides a cleaner fullscreen experience without polluting the terminal's scrollback history. However, it conflicts with terminal multiplexers like Zellij that strictly follow the xterm specification, which defines that alternate screen buffers should not have scrollback.\n\n**Zellij's behavior:** Zellij intentionally disables scrollback in alternate screen mode (see https://github.com/zellij-org/zellij/pull/1032) to comply with the xterm spec. This is by design and not configurable in Zellij—there is no option to enable scrollback in alternate screen mode.\n\n**Solution:** This setting provides a pragmatic workaround: - `auto` (default): Automatically detect the terminal multiplexer. If running in Zellij, disable alternate screen to preserve scrollback. Enable it everywhere else. - `always`: Always use alternate screen mode (original behavior before this fix). - `never`: Never use alternate screen mode. Runs in inline mode, preserving scrollback in all multiplexers.\n\nThe CLI flag `--no-alt-screen` can override this setting at runtime.", + "oneOf": [ + { + "description": "Auto-detect: disable alternate screen in Zellij, enable elsewhere.", + "type": "string", + "enum": [ + "auto" + ] + }, + { + "description": "Always use alternate screen (original behavior).", + "type": "string", + "enum": [ + "always" + ] + }, + { + "description": "Never use alternate screen (inline mode only).", + "type": "string", + "enum": [ + "never" + ] + } + ] + }, + "AnalyticsConfigToml": { + "description": "Analytics settings loaded from config.toml. Fields are optional so we can apply defaults.", + "type": "object", + "properties": { + "enabled": { + "description": "When `false`, disables analytics across Codex product surfaces in this profile.", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "AskForApproval": { + "description": "Determines the conditions under which the user is consulted to approve running the command proposed by Codex.", + "oneOf": [ + { + "description": "Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.", + "type": "string", + "enum": [ + "untrusted" + ] + }, + { + "description": "*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.", + "type": "string", + "enum": [ + "on-failure" + ] + }, + { + "description": "The model decides when to ask the user for approval.", + "type": "string", + "enum": [ + "on-request" + ] + }, + { + "description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.", + "type": "string", + "enum": [ + "never" + ] + } + ] + }, + "AuthCredentialsStoreMode": { + "description": "Determine where Codex should store CLI auth credentials.", + "oneOf": [ + { + "description": "Persist credentials in CODEX_HOME/auth.json.", + "type": "string", + "enum": [ + "file" + ] + }, + { + "description": "Persist credentials in the keyring. Fail if unavailable.", + "type": "string", + "enum": [ + "keyring" + ] + }, + { + "description": "Use keyring when available; otherwise, fall back to a file in CODEX_HOME.", + "type": "string", + "enum": [ + "auto" + ] + } + ] + }, + "ConfigProfile": { + "description": "Collection of common configuration options that a user can define as a unit in `config.toml`.", + "type": "object", + "properties": { + "analytics": { + "$ref": "#/definitions/AnalyticsConfigToml" + }, + "approval_policy": { + "$ref": "#/definitions/AskForApproval" + }, + "chatgpt_base_url": { + "type": "string" + }, + "experimental_compact_prompt_file": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "experimental_instructions_file": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "experimental_use_freeform_apply_patch": { + "type": "boolean" + }, + "experimental_use_unified_exec_tool": { + "type": "boolean" + }, + "features": { + "description": "Optional feature toggles scoped to this profile.", + "default": null, + "type": "object", + "properties": { + "apply_patch_freeform": { + "type": "boolean" + }, + "collab": { + "type": "boolean" + }, + "elevated_windows_sandbox": { + "type": "boolean" + }, + "enable_experimental_windows_sandbox": { + "type": "boolean" + }, + "enable_request_compression": { + "type": "boolean" + }, + "exec_policy": { + "type": "boolean" + }, + "experimental_use_freeform_apply_patch": { + "type": "boolean" + }, + "experimental_use_unified_exec_tool": { + "type": "boolean" + }, + "experimental_windows_sandbox": { + "type": "boolean" + }, + "hierarchical_agents": { + "type": "boolean" + }, + "include_apply_patch_tool": { + "type": "boolean" + }, + "powershell_utf8": { + "type": "boolean" + }, + "remote_compaction": { + "type": "boolean" + }, + "remote_models": { + "type": "boolean" + }, + "shell_snapshot": { + "type": "boolean" + }, + "shell_tool": { + "type": "boolean" + }, + "steer": { + "type": "boolean" + }, + "tui2": { + "type": "boolean" + }, + "undo": { + "type": "boolean" + }, + "unified_exec": { + "type": "boolean" + }, + "web_search": { + "type": "boolean" + }, + "web_search_cached": { + "type": "boolean" + }, + "web_search_request": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "include_apply_patch_tool": { + "type": "boolean" + }, + "model": { + "type": "string" + }, + "model_provider": { + "description": "The key in the `model_providers` map identifying the [`ModelProviderInfo`] to use.", + "type": "string" + }, + "model_reasoning_effort": { + "$ref": "#/definitions/ReasoningEffort" + }, + "model_reasoning_summary": { + "$ref": "#/definitions/ReasoningSummary" + }, + "model_verbosity": { + "$ref": "#/definitions/Verbosity" + }, + "oss_provider": { + "type": "string" + }, + "sandbox_mode": { + "$ref": "#/definitions/SandboxMode" + }, + "tools_view_image": { + "type": "boolean" + }, + "tools_web_search": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "FeedbackConfigToml": { + "type": "object", + "properties": { + "enabled": { + "description": "When `false`, disables the feedback flow across Codex product surfaces.", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "ForcedLoginMethod": { + "type": "string", + "enum": [ + "chatgpt", + "api" + ] + }, + "GhostSnapshotToml": { + "type": "object", + "properties": { + "disable_warnings": { + "description": "Disable all ghost snapshot warning events.", + "type": "boolean" + }, + "ignore_large_untracked_dirs": { + "description": "Ignore untracked directories that contain this many files or more. (Still emits a warning unless warnings are disabled.)", + "type": "integer", + "format": "int64" + }, + "ignore_large_untracked_files": { + "description": "Exclude untracked files larger than this many bytes from ghost snapshots.", + "type": "integer", + "format": "int64" + } + }, + "additionalProperties": false + }, + "History": { + "description": "Settings that govern if and what will be written to `~/.codex/history.jsonl`.", + "type": "object", + "required": [ + "persistence" + ], + "properties": { + "max_bytes": { + "description": "If set, the maximum size of the history file in bytes. The oldest entries are dropped once the file exceeds this limit.", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "persistence": { + "description": "If true, history entries will not be written to disk.", + "allOf": [ + { + "$ref": "#/definitions/HistoryPersistence" + } + ] + } + }, + "additionalProperties": false + }, + "HistoryPersistence": { + "oneOf": [ + { + "description": "Save all history entries to disk.", + "type": "string", + "enum": [ + "save-all" + ] + }, + { + "description": "Do not write history to disk.", + "type": "string", + "enum": [ + "none" + ] + } + ] + }, + "ModelProviderInfo": { + "description": "Serializable representation of a provider definition.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "base_url": { + "description": "Base URL for the provider's OpenAI-compatible API.", + "type": "string" + }, + "env_http_headers": { + "description": "Optional HTTP headers to include in requests to this provider where the (key, value) pairs are the header name and _environment variable_ whose value should be used. If the environment variable is not set, or the value is empty, the header will not be included in the request.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "env_key": { + "description": "Environment variable that stores the user's API key for this provider.", + "type": "string" + }, + "env_key_instructions": { + "description": "Optional instructions to help the user get a valid value for the variable and set it.", + "type": "string" + }, + "experimental_bearer_token": { + "description": "Value to use with `Authorization: Bearer ` header. Use of this config is discouraged in favor of `env_key` for security reasons, but this may be necessary when using this programmatically.", + "type": "string" + }, + "http_headers": { + "description": "Additional HTTP headers to include in requests to this provider where the (key, value) pairs are the header name and value.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "name": { + "description": "Friendly display name.", + "type": "string" + }, + "query_params": { + "description": "Optional query parameters to append to the base URL.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "request_max_retries": { + "description": "Maximum number of times to retry a failed HTTP request to this provider.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "requires_openai_auth": { + "description": "Does this provider require an OpenAI API Key or ChatGPT login token? If true, user is presented with login screen on first run, and login preference and token/key are stored in auth.json. If false (which is the default), login screen is skipped, and API key (if needed) comes from the \"env_key\" environment variable.", + "default": false, + "type": "boolean" + }, + "stream_idle_timeout_ms": { + "description": "Idle timeout (in milliseconds) to wait for activity on a streaming response before treating the connection as lost.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "stream_max_retries": { + "description": "Number of times to retry reconnecting a dropped streaming response before failing.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "wire_api": { + "description": "Which wire protocol this provider expects.", + "default": "chat", + "allOf": [ + { + "$ref": "#/definitions/WireApi" + } + ] + } + }, + "additionalProperties": false + }, + "Notice": { + "description": "Settings for notices we display to users via the tui and app-server clients (primarily the Codex IDE extension). NOTE: these are different from notifications - notices are warnings, NUX screens, acknowledgements, etc.", + "type": "object", + "properties": { + "hide_full_access_warning": { + "description": "Tracks whether the user has acknowledged the full access warning prompt.", + "type": "boolean" + }, + "hide_gpt-5.1-codex-max_migration_prompt": { + "description": "Tracks whether the user has seen the gpt-5.1-codex-max migration prompt", + "type": "boolean" + }, + "hide_gpt5_1_migration_prompt": { + "description": "Tracks whether the user has seen the model migration prompt", + "type": "boolean" + }, + "hide_rate_limit_model_nudge": { + "description": "Tracks whether the user opted out of the rate limit model switch reminder.", + "type": "boolean" + }, + "hide_world_writable_warning": { + "description": "Tracks whether the user has acknowledged the Windows world-writable directories warning.", + "type": "boolean" + }, + "model_migrations": { + "description": "Tracks acknowledged model migrations as old->new model slug mappings.", + "default": {}, + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "Notifications": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "OAuthCredentialsStoreMode": { + "description": "Determine where Codex should store and read MCP credentials.", + "oneOf": [ + { + "description": "`Keyring` when available; otherwise, `File`. Credentials stored in the keyring will only be readable by Codex unless the user explicitly grants access via OS-level keyring access.", + "type": "string", + "enum": [ + "auto" + ] + }, + { + "description": "CODEX_HOME/.credentials.json This file will be readable to Codex and other applications running as the same user.", + "type": "string", + "enum": [ + "file" + ] + }, + { + "description": "Keyring when available, otherwise fail.", + "type": "string", + "enum": [ + "keyring" + ] + } + ] + }, + "OtelConfigToml": { + "description": "OTEL settings loaded from config.toml. Fields are optional so we can apply defaults.", + "type": "object", + "properties": { + "environment": { + "description": "Mark traces with environment (dev, staging, prod, test). Defaults to dev.", + "type": "string" + }, + "exporter": { + "description": "Optional log exporter", + "allOf": [ + { + "$ref": "#/definitions/OtelExporterKind" + } + ] + }, + "log_user_prompt": { + "description": "Log user prompt in traces", + "type": "boolean" + }, + "trace_exporter": { + "description": "Optional trace exporter", + "allOf": [ + { + "$ref": "#/definitions/OtelExporterKind" + } + ] + } + }, + "additionalProperties": false + }, + "OtelExporterKind": { + "description": "Which OTEL exporter to use.", + "oneOf": [ + { + "type": "string", + "enum": [ + "none", + "statsig" + ] + }, + { + "type": "object", + "required": [ + "otlp-http" + ], + "properties": { + "otlp-http": { + "type": "object", + "required": [ + "endpoint", + "protocol" + ], + "properties": { + "endpoint": { + "type": "string" + }, + "headers": { + "default": {}, + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "protocol": { + "$ref": "#/definitions/OtelHttpProtocol" + }, + "tls": { + "default": null, + "allOf": [ + { + "$ref": "#/definitions/OtelTlsConfig" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "otlp-grpc" + ], + "properties": { + "otlp-grpc": { + "type": "object", + "required": [ + "endpoint" + ], + "properties": { + "endpoint": { + "type": "string" + }, + "headers": { + "default": {}, + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "tls": { + "default": null, + "allOf": [ + { + "$ref": "#/definitions/OtelTlsConfig" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "OtelHttpProtocol": { + "oneOf": [ + { + "description": "Binary payload", + "type": "string", + "enum": [ + "binary" + ] + }, + { + "description": "JSON payload", + "type": "string", + "enum": [ + "json" + ] + } + ] + }, + "OtelTlsConfig": { + "type": "object", + "properties": { + "ca-certificate": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "client-certificate": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "client-private-key": { + "$ref": "#/definitions/AbsolutePathBuf" + } + }, + "additionalProperties": false + }, + "ProjectConfig": { + "type": "object", + "properties": { + "trust_level": { + "$ref": "#/definitions/TrustLevel" + } + }, + "additionalProperties": false + }, + "RawMcpServerConfig": { + "type": "object", + "properties": { + "args": { + "default": null, + "type": "array", + "items": { + "type": "string" + } + }, + "bearer_token": { + "type": "string" + }, + "bearer_token_env_var": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "default": null, + "type": "string" + }, + "disabled_tools": { + "default": null, + "type": "array", + "items": { + "type": "string" + } + }, + "enabled": { + "default": null, + "type": "boolean" + }, + "enabled_tools": { + "default": null, + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "default": null, + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "env_http_headers": { + "default": null, + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "env_vars": { + "default": null, + "type": "array", + "items": { + "type": "string" + } + }, + "http_headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "startup_timeout_ms": { + "default": null, + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "startup_timeout_sec": { + "default": null, + "type": "number", + "format": "double" + }, + "tool_timeout_sec": { + "default": null, + "type": "number", + "format": "double" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "ReasoningEffort": { + "description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning", + "type": "string", + "enum": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + }, + "ReasoningSummary": { + "description": "A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries", + "oneOf": [ + { + "type": "string", + "enum": [ + "auto", + "concise", + "detailed" + ] + }, + { + "description": "Option to disable reasoning summaries.", + "type": "string", + "enum": [ + "none" + ] + } + ] + }, + "SandboxMode": { + "type": "string", + "enum": [ + "read-only", + "workspace-write", + "danger-full-access" + ] + }, + "SandboxWorkspaceWrite": { + "type": "object", + "properties": { + "exclude_slash_tmp": { + "default": false, + "type": "boolean" + }, + "exclude_tmpdir_env_var": { + "default": false, + "type": "boolean" + }, + "network_access": { + "default": false, + "type": "boolean" + }, + "writable_roots": { + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/AbsolutePathBuf" + } + } + }, + "additionalProperties": false + }, + "ScrollInputMode": { + "description": "How TUI2 should interpret mouse scroll events.\n\nTerminals generally encode both mouse wheels and trackpads as the same \"scroll up/down\" mouse button events, without a magnitude. This setting controls whether Codex uses a heuristic to infer wheel vs trackpad per stream, or forces a specific behavior.", + "oneOf": [ + { + "description": "Infer wheel vs trackpad behavior per scroll stream.", + "type": "string", + "enum": [ + "auto" + ] + }, + { + "description": "Always treat scroll events as mouse-wheel input (fixed lines per tick).", + "type": "string", + "enum": [ + "wheel" + ] + }, + { + "description": "Always treat scroll events as trackpad input (fractional accumulation).", + "type": "string", + "enum": [ + "trackpad" + ] + } + ] + }, + "ShellEnvironmentPolicyInherit": { + "oneOf": [ + { + "description": "\"Core\" environment variables for the platform. On UNIX, this would include HOME, LOGNAME, PATH, SHELL, and USER, among others.", + "type": "string", + "enum": [ + "core" + ] + }, + { + "description": "Inherits the full environment from the parent process.", + "type": "string", + "enum": [ + "all" + ] + }, + { + "description": "Do not inherit any environment variables from the parent process.", + "type": "string", + "enum": [ + "none" + ] + } + ] + }, + "ShellEnvironmentPolicyToml": { + "description": "Policy for building the `env` when spawning a process via either the `shell` or `local_shell` tool.", + "type": "object", + "properties": { + "exclude": { + "description": "List of regular expressions.", + "type": "array", + "items": { + "type": "string" + } + }, + "experimental_use_profile": { + "type": "boolean" + }, + "ignore_default_excludes": { + "type": "boolean" + }, + "include_only": { + "description": "List of regular expressions.", + "type": "array", + "items": { + "type": "string" + } + }, + "inherit": { + "$ref": "#/definitions/ShellEnvironmentPolicyInherit" + }, + "set": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "ToolsToml": { + "type": "object", + "properties": { + "view_image": { + "description": "Enable the `view_image` tool that lets the agent attach local images.", + "default": null, + "type": "boolean" + }, + "web_search": { + "default": null, + "type": "boolean" + } + }, + "additionalProperties": false + }, + "TrustLevel": { + "description": "Represents the trust level for a project directory. This determines the approval policy and sandbox mode applied.", + "type": "string", + "enum": [ + "trusted", + "untrusted" + ] + }, + "Tui": { + "description": "Collection of settings that are specific to the TUI.", + "type": "object", + "properties": { + "alternate_screen": { + "description": "Controls whether the TUI uses the terminal's alternate screen buffer.\n\n- `auto` (default): Disable alternate screen in Zellij, enable elsewhere. - `always`: Always use alternate screen (original behavior). - `never`: Never use alternate screen (inline mode only, preserves scrollback).\n\nUsing alternate screen provides a cleaner fullscreen experience but prevents scrollback in terminal multiplexers like Zellij that follow the xterm spec.", + "default": "auto", + "allOf": [ + { + "$ref": "#/definitions/AltScreenMode" + } + ] + }, + "animations": { + "description": "Enable animations (welcome screen, shimmer effects, spinners). Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "notifications": { + "description": "Enable desktop notifications from the TUI when the terminal is unfocused. Defaults to `true`.", + "default": true, + "allOf": [ + { + "$ref": "#/definitions/Notifications" + } + ] + }, + "scroll_events_per_tick": { + "description": "Override the *wheel* event density used to normalize TUI2 scrolling.\n\nTerminals generally deliver both mouse wheels and trackpads as discrete `scroll up/down` mouse events with direction but no magnitude. Unfortunately, the *number* of raw events per physical wheel notch varies by terminal (commonly 1, 3, or 9+). TUI2 uses this value to normalize that raw event density into consistent \"wheel tick\" behavior.\n\nWheel math (conceptually):\n\n- A single event contributes `1 / scroll_events_per_tick` tick-equivalents. - Wheel-like streams then scale that by `scroll_wheel_lines` so one physical notch scrolls a fixed number of lines.\n\nTrackpad math is intentionally *not* fully tied to this value: in trackpad-like mode, TUI2 uses `min(scroll_events_per_tick, 3)` as the divisor so terminals with dense wheel ticks (e.g. 9 events per notch) do not make trackpads feel artificially slow.\n\nDefaults are derived per terminal from [`crate::terminal::TerminalInfo`] when TUI2 starts. See `codex-rs/tui2/docs/scroll_input_model.md` for the probe data and rationale.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "scroll_invert": { + "description": "Invert mouse scroll direction in TUI2.\n\nThis flips the scroll sign after terminal detection. It is applied consistently to both wheel and trackpad input.", + "default": false, + "type": "boolean" + }, + "scroll_mode": { + "description": "Select how TUI2 interprets mouse scroll input.\n\n- `auto` (default): infer wheel vs trackpad per scroll stream. - `wheel`: always use wheel behavior (fixed lines per wheel notch). - `trackpad`: always use trackpad behavior (fractional accumulation; wheel may feel slow).", + "default": "auto", + "allOf": [ + { + "$ref": "#/definitions/ScrollInputMode" + } + ] + }, + "scroll_trackpad_accel_events": { + "description": "Trackpad acceleration: approximate number of events required to gain +1x speed in TUI2.\n\nThis keeps small swipes precise while allowing large/faster swipes to cover more content. Defaults are chosen to address terminals where trackpad event density is comparatively low.\n\nConcretely, TUI2 computes an acceleration multiplier for trackpad-like streams:\n\n- `multiplier = clamp(1 + abs(events) / scroll_trackpad_accel_events, 1..scroll_trackpad_accel_max)`\n\nThe multiplier is applied to the stream’s computed line delta (including any carried fractional remainder).", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "scroll_trackpad_accel_max": { + "description": "Trackpad acceleration: maximum multiplier applied to trackpad-like streams.\n\nSet to 1 to effectively disable trackpad acceleration.\n\nSee [`Tui::scroll_trackpad_accel_events`] for the exact multiplier formula.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "scroll_trackpad_lines": { + "description": "Override baseline trackpad scroll sensitivity in TUI2.\n\nTrackpads do not have discrete notches, but terminals still emit discrete `scroll up/down` events. In trackpad-like mode, TUI2 accumulates fractional scroll and only applies whole lines to the viewport.\n\nTrackpad per-event contribution is:\n\n- `scroll_trackpad_lines / min(scroll_events_per_tick, 3)`\n\n(plus optional bounded acceleration; see `scroll_trackpad_accel_*`). The `min(..., 3)` divisor is deliberate: `scroll_events_per_tick` is calibrated from *wheel* behavior and can be much larger than trackpad event density, which would otherwise make trackpads feel too slow in dense-wheel terminals.\n\nDefaults to 1, meaning one tick-equivalent maps to one transcript line.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "scroll_wheel_like_max_duration_ms": { + "description": "Auto-mode fallback: maximum duration (ms) that a very small stream is still treated as wheel-like.\n\nThis is only used when `scroll_events_per_tick` is effectively 1 (one event per wheel notch). In that case, we cannot observe a \"tick completion time\", so TUI2 treats a short-lived, small stream (<= 2 events) as wheel-like to preserve classic wheel behavior.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "scroll_wheel_lines": { + "description": "Override how many transcript lines one physical *wheel notch* should scroll in TUI2.\n\nThis is the \"classic feel\" knob. Defaults to 3.\n\nWheel-like per-event contribution is `scroll_wheel_lines / scroll_events_per_tick`. For example, in a terminal that emits 9 events per notch, the default `3 / 9` yields 1/3 of a line per event and totals 3 lines once the full notch burst arrives.\n\nSee `codex-rs/tui2/docs/scroll_input_model.md` for details on the stream model and the wheel/trackpad heuristic.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "scroll_wheel_tick_detect_max_ms": { + "description": "Auto-mode threshold: maximum time (ms) for the first tick-worth of events to arrive.\n\nIn `scroll_mode = \"auto\"`, TUI2 starts a stream as trackpad-like (to avoid overshoot) and promotes it to wheel-like if `scroll_events_per_tick` events arrive \"quickly enough\". This threshold controls what \"quickly enough\" means.\n\nMost users should leave this unset; it is primarily for terminals that emit wheel ticks batched over longer time spans.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "show_tooltips": { + "description": "Show startup tooltips in the TUI welcome screen. Defaults to `true`.", + "default": true, + "type": "boolean" + } + }, + "additionalProperties": false + }, + "UriBasedFileOpener": { + "oneOf": [ + { + "type": "string", + "enum": [ + "vscode", + "vscode-insiders", + "windsurf", + "cursor" + ] + }, + { + "description": "Option to disable the URI-based file opener.", + "type": "string", + "enum": [ + "none" + ] + } + ] + }, + "Verbosity": { + "description": "Controls output length/detail on GPT-5 models via the Responses API. Serialized with lowercase values to match the OpenAI API.", + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "WireApi": { + "description": "Wire protocol that the provider speaks. Most third-party services only implement the classic OpenAI Chat Completions JSON schema, whereas OpenAI itself (and a handful of others) additionally expose the more modern *Responses* API. The two protocols use different request/response shapes and *cannot* be auto-detected at runtime, therefore each provider entry must declare which one it expects.", + "oneOf": [ + { + "description": "The Responses API exposed by OpenAI at `/v1/responses`.", + "type": "string", + "enum": [ + "responses" + ] + }, + { + "description": "Experimental: Responses API over WebSocket transport.", + "type": "string", + "enum": [ + "responses_websocket" + ] + }, + { + "description": "Regular Chat Completions compatible with `/v1/chat/completions`.", + "type": "string", + "enum": [ + "chat" + ] + } + ] + } + } +} \ No newline at end of file diff --git a/codex-rs/core/src/auth/storage.rs b/codex-rs/core/src/auth/storage.rs index a238eb9c38e..48b67aca057 100644 --- a/codex-rs/core/src/auth/storage.rs +++ b/codex-rs/core/src/auth/storage.rs @@ -1,5 +1,6 @@ use chrono::DateTime; use chrono::Utc; +use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use sha2::Digest; @@ -21,7 +22,7 @@ use codex_keyring_store::DefaultKeyringStore; use codex_keyring_store::KeyringStore; /// Determine where Codex should store CLI auth credentials. -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum AuthCredentialsStoreMode { #[default] diff --git a/codex-rs/core/src/bin/config_schema.rs b/codex-rs/core/src/bin/config_schema.rs new file mode 100644 index 00000000000..8d33df42e1f --- /dev/null +++ b/codex-rs/core/src/bin/config_schema.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use clap::Parser; +use std::path::PathBuf; + +/// Generate the JSON Schema for `config.toml` and write it to `config.schema.json`. +#[derive(Parser)] +#[command(name = "codex-write-config-schema")] +struct Args { + #[arg(short, long, value_name = "PATH")] + out: Option, +} + +fn main() -> Result<()> { + let args = Args::parse(); + let out_path = args + .out + .unwrap_or_else(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("config.schema.json")); + codex_core::config::schema::write_config_schema(&out_path)?; + Ok(()) +} diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index f01db91fac5..14342682538 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -44,6 +44,7 @@ use codex_rmcp_client::OAuthCredentialsStoreMode; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBufGuard; use dirs::home_dir; +use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use similar::DiffableStr; @@ -62,6 +63,7 @@ use toml_edit::DocumentMut; mod constraint; pub mod edit; pub mod profile; +pub mod schema; pub mod service; pub mod types; pub use constraint::Constrained; @@ -688,7 +690,8 @@ pub fn set_default_oss_provider(codex_home: &Path, provider: &str) -> std::io::R } /// Base config deserialized from ~/.codex/config.toml. -#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct ConfigToml { /// Optional override of model selection. pub model: Option, @@ -747,6 +750,8 @@ pub struct ConfigToml { /// Definition for MCP servers that Codex can reach out to for tool calls. #[serde(default)] + // Uses the raw MCP input shape (custom deserialization) rather than `McpServerConfig`. + #[schemars(schema_with = "crate::config::schema::mcp_servers_schema")] pub mcp_servers: HashMap, /// Preferred backend for storing MCP OAuth credentials. @@ -818,6 +823,8 @@ pub struct ConfigToml { /// Centralized feature flags (new). Prefer this over individual toggles. #[serde(default)] + // Injects known feature keys into the schema and forbids unknown keys. + #[schemars(schema_with = "crate::config::schema::features_schema")] pub features: Option, /// Settings for ghost snapshots (used for undo). @@ -891,7 +898,8 @@ impl From for UserSavedConfig { } } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct ProjectConfig { pub trust_level: Option, } @@ -906,7 +914,8 @@ impl ProjectConfig { } } -#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct ToolsToml { #[serde(default, alias = "web_search_request")] pub web_search: Option, @@ -925,7 +934,8 @@ impl From for Tools { } } -#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct GhostSnapshotToml { /// Exclude untracked files larger than this many bytes from ghost snapshots. #[serde(alias = "ignore_untracked_files_over_bytes")] diff --git a/codex-rs/core/src/config/profile.rs b/codex-rs/core/src/config/profile.rs index e1c45c1f169..54f9a08ad0f 100644 --- a/codex-rs/core/src/config/profile.rs +++ b/codex-rs/core/src/config/profile.rs @@ -1,4 +1,5 @@ use codex_utils_absolute_path::AbsolutePathBuf; +use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; @@ -10,7 +11,8 @@ use codex_protocol::openai_models::ReasoningEffort; /// Collection of common configuration options that a user can define as a unit /// in `config.toml`. -#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct ConfigProfile { pub model: Option, /// The key in the `model_providers` map identifying the @@ -32,6 +34,8 @@ pub struct ConfigProfile { pub analytics: Option, /// Optional feature toggles scoped to this profile. #[serde(default)] + // Injects known feature keys into the schema and forbids unknown keys. + #[schemars(schema_with = "crate::config::schema::features_schema")] pub features: Option, pub oss_provider: Option, } diff --git a/codex-rs/core/src/config/schema.md b/codex-rs/core/src/config/schema.md new file mode 100644 index 00000000000..101c57b3630 --- /dev/null +++ b/codex-rs/core/src/config/schema.md @@ -0,0 +1,11 @@ +# Config JSON Schema + +We generate a JSON Schema for `~/.codex/config.toml` from the `ConfigToml` type +and commit it at `codex-rs/core/config.schema.json` for editor integration. + +When you change any fields included in `ConfigToml` (or nested config types), +regenerate the schema: + +``` +just write-config-schema +``` diff --git a/codex-rs/core/src/config/schema.rs b/codex-rs/core/src/config/schema.rs new file mode 100644 index 00000000000..674dca3aafe --- /dev/null +++ b/codex-rs/core/src/config/schema.rs @@ -0,0 +1,127 @@ +use crate::config::ConfigToml; +use crate::config::types::RawMcpServerConfig; +use crate::features::FEATURES; +use schemars::r#gen::SchemaGenerator; +use schemars::r#gen::SchemaSettings; +use schemars::schema::InstanceType; +use schemars::schema::ObjectValidation; +use schemars::schema::RootSchema; +use schemars::schema::Schema; +use schemars::schema::SchemaObject; +use std::path::Path; + +/// Schema for the `[features]` map with known + legacy keys only. +pub(crate) fn features_schema(schema_gen: &mut SchemaGenerator) -> Schema { + let mut object = SchemaObject { + instance_type: Some(InstanceType::Object.into()), + ..Default::default() + }; + + let mut validation = ObjectValidation::default(); + for feature in FEATURES { + validation + .properties + .insert(feature.key.to_string(), schema_gen.subschema_for::()); + } + for legacy_key in crate::features::legacy_feature_keys() { + validation + .properties + .insert(legacy_key.to_string(), schema_gen.subschema_for::()); + } + validation.additional_properties = Some(Box::new(Schema::Bool(false))); + object.object = Some(Box::new(validation)); + + Schema::Object(object) +} + +/// Schema for the `[mcp_servers]` map using the raw input shape. +pub(crate) fn mcp_servers_schema(schema_gen: &mut SchemaGenerator) -> Schema { + let mut object = SchemaObject { + instance_type: Some(InstanceType::Object.into()), + ..Default::default() + }; + + let validation = ObjectValidation { + additional_properties: Some(Box::new(schema_gen.subschema_for::())), + ..Default::default() + }; + object.object = Some(Box::new(validation)); + + Schema::Object(object) +} + +/// Build the config schema for `config.toml`. +pub fn config_schema() -> RootSchema { + SchemaSettings::draft07() + .with(|settings| { + settings.option_add_null_type = false; + }) + .into_generator() + .into_root_schema_for::() +} + +/// Render the config schema as pretty-printed JSON. +pub fn config_schema_json() -> anyhow::Result> { + let schema = config_schema(); + let json = serde_json::to_vec_pretty(&schema)?; + Ok(json) +} + +/// Write the config schema fixture to disk. +pub fn write_config_schema(out_path: &Path) -> anyhow::Result<()> { + let json = config_schema_json()?; + std::fs::write(out_path, json)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::config_schema_json; + use serde_json::Map; + use serde_json::Value; + use similar::TextDiff; + + fn canonicalize(value: &Value) -> Value { + match value { + Value::Array(items) => Value::Array(items.iter().map(canonicalize).collect()), + Value::Object(map) => { + let mut entries: Vec<_> = map.iter().collect(); + entries.sort_by(|(left, _), (right, _)| left.cmp(right)); + let mut sorted = Map::with_capacity(map.len()); + for (key, child) in entries { + sorted.insert(key.clone(), canonicalize(child)); + } + Value::Object(sorted) + } + _ => value.clone(), + } + } + + #[test] + fn config_schema_matches_fixture() { + let fixture_path = codex_utils_cargo_bin::find_resource!("config.schema.json") + .expect("resolve config schema fixture path"); + let fixture = std::fs::read_to_string(fixture_path).expect("read config schema fixture"); + let fixture_value: serde_json::Value = + serde_json::from_str(&fixture).expect("parse config schema fixture"); + let schema_json = config_schema_json().expect("serialize config schema"); + let schema_value: serde_json::Value = + serde_json::from_slice(&schema_json).expect("decode schema json"); + let fixture_value = canonicalize(&fixture_value); + let schema_value = canonicalize(&schema_value); + if fixture_value != schema_value { + let expected = + serde_json::to_string_pretty(&fixture_value).expect("serialize fixture json"); + let actual = + serde_json::to_string_pretty(&schema_value).expect("serialize schema json"); + let diff = TextDiff::from_lines(&expected, &actual) + .unified_diff() + .header("fixture", "generated") + .to_string(); + panic!( + "Current schema for `config.toml` doesn't match the fixture. \ +Run `just write-config-schema` to overwrite with your changes.\n\n{diff}" + ); + } + } +} diff --git a/codex-rs/core/src/config/types.rs b/codex-rs/core/src/config/types.rs index 2b41c3c52fb..c57e550a7a8 100644 --- a/codex-rs/core/src/config/types.rs +++ b/codex-rs/core/src/config/types.rs @@ -11,6 +11,7 @@ use std::path::PathBuf; use std::time::Duration; use wildmatch::WildMatchPattern; +use schemars::JsonSchema; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; @@ -48,47 +49,51 @@ pub struct McpServerConfig { pub disabled_tools: Option>, } +// Raw MCP config shape used for deserialization and JSON Schema generation. +// Keep this in sync with the validation logic in `McpServerConfig`. +#[derive(Deserialize, Clone, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub(crate) struct RawMcpServerConfig { + // stdio + pub command: Option, + #[serde(default)] + pub args: Option>, + #[serde(default)] + pub env: Option>, + #[serde(default)] + pub env_vars: Option>, + #[serde(default)] + pub cwd: Option, + pub http_headers: Option>, + #[serde(default)] + pub env_http_headers: Option>, + + // streamable_http + pub url: Option, + pub bearer_token: Option, + pub bearer_token_env_var: Option, + + // shared + #[serde(default)] + pub startup_timeout_sec: Option, + #[serde(default)] + pub startup_timeout_ms: Option, + #[serde(default, with = "option_duration_secs")] + #[schemars(with = "Option")] + pub tool_timeout_sec: Option, + #[serde(default)] + pub enabled: Option, + #[serde(default)] + pub enabled_tools: Option>, + #[serde(default)] + pub disabled_tools: Option>, +} + impl<'de> Deserialize<'de> for McpServerConfig { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - #[derive(Deserialize, Clone)] - struct RawMcpServerConfig { - // stdio - command: Option, - #[serde(default)] - args: Option>, - #[serde(default)] - env: Option>, - #[serde(default)] - env_vars: Option>, - #[serde(default)] - cwd: Option, - http_headers: Option>, - #[serde(default)] - env_http_headers: Option>, - - // streamable_http - url: Option, - bearer_token: Option, - bearer_token_env_var: Option, - - // shared - #[serde(default)] - startup_timeout_sec: Option, - #[serde(default)] - startup_timeout_ms: Option, - #[serde(default, with = "option_duration_secs")] - tool_timeout_sec: Option, - #[serde(default)] - enabled: Option, - #[serde(default)] - enabled_tools: Option>, - #[serde(default)] - disabled_tools: Option>, - } - let mut raw = RawMcpServerConfig::deserialize(deserializer)?; let startup_timeout_sec = match (raw.startup_timeout_sec, raw.startup_timeout_ms) { @@ -164,7 +169,7 @@ const fn default_enabled() -> bool { true } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)] #[serde(untagged, deny_unknown_fields, rename_all = "snake_case")] pub enum McpServerTransportConfig { /// https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio @@ -222,7 +227,7 @@ mod option_duration_secs { } } -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, JsonSchema)] pub enum UriBasedFileOpener { #[serde(rename = "vscode")] VsCode, @@ -254,7 +259,8 @@ impl UriBasedFileOpener { } /// Settings that govern if and what will be written to `~/.codex/history.jsonl`. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct History { /// If true, history entries will not be written to disk. pub persistence: HistoryPersistence, @@ -264,7 +270,7 @@ pub struct History { pub max_bytes: Option, } -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default, JsonSchema)] #[serde(rename_all = "kebab-case")] pub enum HistoryPersistence { /// Save all history entries to disk. @@ -277,13 +283,15 @@ pub enum HistoryPersistence { // ===== Analytics configuration ===== /// Analytics settings loaded from config.toml. Fields are optional so we can apply defaults. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct AnalyticsConfigToml { /// When `false`, disables analytics across Codex product surfaces in this profile. pub enabled: Option, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct FeedbackConfigToml { /// When `false`, disables the feedback flow across Codex product surfaces. pub enabled: Option, @@ -291,7 +299,7 @@ pub struct FeedbackConfigToml { // ===== OTEL configuration ===== -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)] #[serde(rename_all = "kebab-case")] pub enum OtelHttpProtocol { /// Binary payload @@ -300,7 +308,8 @@ pub enum OtelHttpProtocol { Json, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] #[serde(rename_all = "kebab-case")] pub struct OtelTlsConfig { pub ca_certificate: Option, @@ -309,7 +318,8 @@ pub struct OtelTlsConfig { } /// Which OTEL exporter to use. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)] +#[schemars(deny_unknown_fields)] #[serde(rename_all = "kebab-case")] pub enum OtelExporterKind { None, @@ -332,7 +342,8 @@ pub enum OtelExporterKind { } /// OTEL settings loaded from config.toml. Fields are optional so we can apply defaults. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct OtelConfigToml { /// Log user prompt in traces pub log_user_prompt: Option, @@ -369,7 +380,7 @@ impl Default for OtelConfig { } } -#[derive(Serialize, Debug, Clone, PartialEq, Eq, Deserialize)] +#[derive(Serialize, Debug, Clone, PartialEq, Eq, Deserialize, JsonSchema)] #[serde(untagged)] pub enum Notifications { Enabled(bool), @@ -387,7 +398,7 @@ impl Default for Notifications { /// Terminals generally encode both mouse wheels and trackpads as the same "scroll up/down" mouse /// button events, without a magnitude. This setting controls whether Codex uses a heuristic to /// infer wheel vs trackpad per stream, or forces a specific behavior. -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ScrollInputMode { /// Infer wheel vs trackpad behavior per scroll stream. @@ -405,7 +416,8 @@ impl Default for ScrollInputMode { } /// Collection of settings that are specific to the TUI. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct Tui { /// Enable desktop notifications from the TUI when the terminal is unfocused. /// Defaults to `true`. @@ -544,7 +556,8 @@ const fn default_true() -> bool { /// Settings for notices we display to users via the tui and app-server clients /// (primarily the Codex IDE extension). NOTE: these are different from /// notifications - notices are warnings, NUX screens, acknowledgements, etc. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct Notice { /// Tracks whether the user has acknowledged the full access warning prompt. pub hide_full_access_warning: Option, @@ -567,7 +580,8 @@ impl Notice { pub(crate) const TABLE_KEY: &'static str = "notice"; } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct SandboxWorkspaceWrite { #[serde(default)] pub writable_roots: Vec, @@ -590,7 +604,7 @@ impl From for codex_app_server_protocol::SandboxSettings } } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] #[serde(rename_all = "kebab-case")] pub enum ShellEnvironmentPolicyInherit { /// "Core" environment variables for the platform. On UNIX, this would @@ -607,7 +621,8 @@ pub enum ShellEnvironmentPolicyInherit { /// Policy for building the `env` when spawning a process via either the /// `shell` or `local_shell` tool. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct ShellEnvironmentPolicyToml { pub inherit: Option, diff --git a/codex-rs/core/src/features.rs b/codex-rs/core/src/features.rs index baed1f6deff..5a9054eb983 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/core/src/features.rs @@ -8,6 +8,7 @@ use crate::config::ConfigToml; use crate::config::profile::ConfigProfile; use codex_otel::OtelManager; +use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use std::collections::BTreeMap; @@ -15,6 +16,7 @@ use std::collections::BTreeSet; mod legacy; pub(crate) use legacy::LegacyFeatureToggles; +pub(crate) use legacy::legacy_feature_keys; /// High-level lifecycle stage for a feature. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -294,7 +296,7 @@ pub fn is_known_feature_key(key: &str) -> bool { } /// Deserializable features table for TOML. -#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema)] pub struct FeaturesToml { #[serde(flatten)] pub entries: BTreeMap, diff --git a/codex-rs/core/src/features/legacy.rs b/codex-rs/core/src/features/legacy.rs index ed508ffb5a7..2b5a9e7fe00 100644 --- a/codex-rs/core/src/features/legacy.rs +++ b/codex-rs/core/src/features/legacy.rs @@ -31,6 +31,10 @@ const ALIASES: &[Alias] = &[ }, ]; +pub(crate) fn legacy_feature_keys() -> impl Iterator { + ALIASES.iter().map(|alias| alias.legacy_key) +} + pub(crate) fn feature_for_key(key: &str) -> Option { ALIASES .iter() diff --git a/codex-rs/core/src/model_provider_info.rs b/codex-rs/core/src/model_provider_info.rs index 1acb25ef81e..b1422458cad 100644 --- a/codex-rs/core/src/model_provider_info.rs +++ b/codex-rs/core/src/model_provider_info.rs @@ -12,6 +12,7 @@ use codex_app_server_protocol::AuthMode; use http::HeaderMap; use http::header::HeaderName; use http::header::HeaderValue; +use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use std::collections::HashMap; @@ -36,7 +37,7 @@ const OPENAI_PROVIDER_NAME: &str = "OpenAI"; /// *Responses* API. The two protocols use different request/response shapes /// and *cannot* be auto-detected at runtime, therefore each provider entry /// must declare which one it expects. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum WireApi { /// The Responses API exposed by OpenAI at `/v1/responses`. @@ -52,7 +53,8 @@ pub enum WireApi { } /// Serializable representation of a provider definition. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema)] +#[schemars(deny_unknown_fields)] pub struct ModelProviderInfo { /// Friendly display name. pub name: String, diff --git a/codex-rs/rmcp-client/Cargo.toml b/codex-rs/rmcp-client/Cargo.toml index efcea2d805b..8aa7512faf1 100644 --- a/codex-rs/rmcp-client/Cargo.toml +++ b/codex-rs/rmcp-client/Cargo.toml @@ -36,6 +36,7 @@ rmcp = { workspace = true, default-features = false, features = [ "transport-streamable-http-client-reqwest", "transport-streamable-http-server", ] } +schemars = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } sha2 = { workspace = true } diff --git a/codex-rs/rmcp-client/src/oauth.rs b/codex-rs/rmcp-client/src/oauth.rs index f8eafaf23e1..a3a256374bb 100644 --- a/codex-rs/rmcp-client/src/oauth.rs +++ b/codex-rs/rmcp-client/src/oauth.rs @@ -26,6 +26,7 @@ use oauth2::Scope; use oauth2::TokenResponse; use oauth2::basic::BasicTokenType; use rmcp::transport::auth::OAuthTokenResponse; +use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use serde_json::Value; @@ -63,7 +64,7 @@ pub struct StoredOAuthTokens { } /// Determine where Codex should store and read MCP credentials. -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum OAuthCredentialsStoreMode { /// `Keyring` when available; otherwise, `File`. diff --git a/docs/config.md b/docs/config.md index 2b64253d309..0b5fecf6e7a 100644 --- a/docs/config.md +++ b/docs/config.md @@ -17,3 +17,7 @@ Codex can connect to MCP servers configured in `~/.codex/config.toml`. See the c Codex can run a notification hook when the agent finishes a turn. See the configuration reference for the latest notification settings: - https://developers.openai.com/codex/config-reference + +## JSON Schema + +The generated JSON Schema for `config.toml` lives at `codex-rs/core/config.schema.json`. diff --git a/justfile b/justfile index bbe18987a87..64f470ca7a8 100644 --- a/justfile +++ b/justfile @@ -56,3 +56,7 @@ build-for-release: # Run the MCP server mcp-server-run *args: cargo run -p codex-mcp-server -- "$@" + +# Regenerate the json schema for config.toml from the current config types. +write-config-schema: + cargo run -p codex-core --bin codex-write-config-schema