merge main into resume fork optimization

This commit is contained in:
anais
2026-06-23 08:53:41 -07:00
554 changed files with 19603 additions and 5441 deletions
+4 -1
View File
@@ -173,8 +173,11 @@ jobs:
runner: macos-15-xlarge
timeout_minutes: 30
- name: Windows
runner: windows-2022
runner: windows-x64
timeout_minutes: 30
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
steps:
- name: Check whether argument comment lint should run
id: argument_comment_lint_gate
+6 -7
View File
@@ -2,18 +2,17 @@ module(name = "codex")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "llvm", version = "0.7.1")
bazel_dep(name = "llvm", version = "0.7.9")
# The upstream LLVM archive contains a few unix-only symlink entries and is
# missing a couple of MinGW compatibility archives that windows-gnullvm needs
# during extraction and linking, so patch it until upstream grows native support.
# Patch hermetic LLVM for Codex's custom libc++ and Windows gnullvm runtime
# needs that have not landed upstream.
single_version_override(
module_name = "llvm",
patch_strip = 1,
patches = [
"//patches:llvm_rusty_v8_custom_libcxx.patch",
"//patches:llvm_windows_arm64_powl.patch",
"//patches:llvm_windows_symlink_extract.patch",
"//patches:llvm_windows_mingw_compat.patch",
],
)
@@ -80,7 +79,7 @@ use_repo(osx, "macos_sdk")
# Needed to disable xcode...
bazel_dep(name = "apple_support", version = "2.1.0")
bazel_dep(name = "rules_cc", version = "0.2.16")
bazel_dep(name = "rules_cc", version = "0.2.18")
single_version_override(
module_name = "rules_cc",
patch_strip = 1,
@@ -310,7 +309,7 @@ crate.annotation(
# Build scripts compile in Bazel's exec configuration, so target-specific
# optional build deps are otherwise dropped for the musl release platforms.
build_script_deps = [
"@crates//:openssl-src-300.5.5+3.5.5",
"@crates//:openssl-src-300.6.1+3.6.3",
],
build_script_env = {
"OPENSSL_DIR": "$(execpath @openssl//:gen_dir)",
+14 -10
View File
@@ -45,6 +45,7 @@
"https://bcr.bazel.build/modules/bazel_features/1.34.0/MODULE.bazel": "e8475ad7c8965542e0c7aac8af68eb48c4af904be3d614b6aa6274c092c2ea1e",
"https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
"https://bcr.bazel.build/modules/bazel_features/1.42.0/MODULE.bazel": "e8ca15cb2639c5f12183db6dcb678735555d0cdd739b32a0418b6532b5e565f8",
"https://bcr.bazel.build/modules/bazel_features/1.43.0/MODULE.bazel": "defa2226f06ba20550d6548c3a2ea2a7929634437a52973869c20c225450eb91",
"https://bcr.bazel.build/modules/bazel_features/1.45.0/MODULE.bazel": "7daec6d87ab0703417486d4cb948af0b06f55d4d7c08cbb5978c80e79b538edf",
"https://bcr.bazel.build/modules/bazel_features/1.45.0/source.json": "635e4536e09ff125b8972e0fa239c135fde5f18701f7d5115680560651dfb41d",
"https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b",
@@ -63,6 +64,7 @@
"https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
"https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d",
"https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.0/MODULE.bazel": "2fb3fb53675f6adfc1ca5bfbd5cfb655ae350fba4706d924a8ec7e3ba945671c",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67",
"https://bcr.bazel.build/modules/bazel_skylib/1.9.0/MODULE.bazel": "72997b29dfd95c3fa0d0c48322d05590418edef451f8db8db5509c57875fb4b7",
@@ -87,8 +89,8 @@
"https://bcr.bazel.build/modules/libcap/2.27.bcr.1/source.json": "3b116cbdbd25a68ffb587b672205f6d353a4c19a35452e480d58fc89531e0a10",
"https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
"https://bcr.bazel.build/modules/llvm/0.7.0/MODULE.bazel": "3c07a4e5734b0ad41fe24dedaacbf3a35ce4377b7e1a21f24488a0c9ac4f1e6b",
"https://bcr.bazel.build/modules/llvm/0.7.1/MODULE.bazel": "74ac75efc6385b8a95d83bfa36ad399500f747c3d0f50287f9b6f9e854ec4814",
"https://bcr.bazel.build/modules/llvm/0.7.1/source.json": "0cac59d04dafa0ca1f70ea21cf1569e034ef8736c2c7510e834a9e8141d7e631",
"https://bcr.bazel.build/modules/llvm/0.7.9/MODULE.bazel": "452b272a6b5ecc8747db8e5b7fd9d6799ef0db17b8d437aa8d4dda4f56a3e9ea",
"https://bcr.bazel.build/modules/llvm/0.7.9/source.json": "0c098ab546a8c4b0a720dcb010714e13f9ba6ceab0d23a87b7f1083788928c31",
"https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74",
"https://bcr.bazel.build/modules/openssl/3.5.4.bcr.0/MODULE.bazel": "0f6b8f20b192b9ff0781406256150bcd46f19e66d807dcb0c540548439d6fc35",
"https://bcr.bazel.build/modules/openssl/3.5.4.bcr.0/source.json": "543ed7627cc18e6460b9c1ae4a1b6b1debc5a5e0aca878b00f7531c7186b73da",
@@ -143,7 +145,8 @@
"https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37",
"https://bcr.bazel.build/modules/rules_cc/0.2.14/MODULE.bazel": "353c99ed148887ee89c54a17d4100ae7e7e436593d104b668476019023b58df8",
"https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4",
"https://bcr.bazel.build/modules/rules_cc/0.2.16/source.json": "d03d5cde49376d87e14ec14b666c56075e5e3926930327fd5d0484a1ff2ac1cc",
"https://bcr.bazel.build/modules/rules_cc/0.2.18/MODULE.bazel": "4460ec36adc8f722a6a2a4ac9374cb91f2acebadaa93fc37966129afb3dece87",
"https://bcr.bazel.build/modules/rules_cc/0.2.18/source.json": "abad668ff2fd63ada1ac49bf386d37e27048b89a3465a6fd968bb832b00a09d3",
"https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc",
"https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
@@ -221,8 +224,9 @@
"https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91",
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f",
"https://bcr.bazel.build/modules/tar.bzl/0.10.4/MODULE.bazel": "e8f9ff79199e8d9eaad7f1b0a77ad74b30bb82d794b87d8ca942bead5de83ae9",
"https://bcr.bazel.build/modules/tar.bzl/0.10.4/source.json": "20143442376c03426f6135292ba02d825cb75308aa47e6bf42dd4cc5a435c2ff",
"https://bcr.bazel.build/modules/tar.bzl/0.9.0/MODULE.bazel": "452a22d7f02b1c9d7a22ab25edf20f46f3e1101f0f67dc4bfbf9a474ddf02445",
"https://bcr.bazel.build/modules/tar.bzl/0.9.0/source.json": "c732760a374831a2cf5b08839e4be75017196b4d796a5aa55235272ee17cd839",
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
"https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/MODULE.bazel": "b573395fe63aef4299ba095173e2f62ccfee5ad9bbf7acaa95dba73af9fc2b38",
"https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/source.json": "3f3fbaeafecaf629877ad152a2c9def21f8d330d91aa94c5dc75bbb98c10b8b8",
@@ -982,8 +986,8 @@
"git+https://github.com/juberti-oai/rust-sdks.git?rev=e2d1d1d230c6fc9df171ccb181423f957bb3c1f0#e2d1d1d230c6fc9df171ccb181423f957bb3c1f0_webrtc-sys-build": "{\"dependencies\":[{\"name\":\"anyhow\"},{\"name\":\"fs2\"},{\"name\":\"regex\"},{\"default_features\":false,\"features\":[\"rustls-tls-native-roots\",\"blocking\"],\"name\":\"reqwest\",\"optional\":false},{\"name\":\"scratch\"},{\"name\":\"semver\"},{\"name\":\"zip\"}],\"features\":{},\"strip_prefix\":\"webrtc-sys/build\"}",
"git+https://github.com/nornagon/crossterm?rev=87db8bfa6dc99427fd3b071681b07fc31c6ce995#87db8bfa6dc99427fd3b071681b07fc31c6ce995_crossterm": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"bitflags\",\"optional\":false},{\"default_features\":false,\"features\":[],\"name\":\"futures-core\",\"optional\":true},{\"name\":\"parking_lot\"},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"filedescriptor\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":false,\"features\":[],\"name\":\"libc\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[\"os-poll\"],\"name\":\"mio\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":false,\"features\":[\"std\",\"stdio\",\"termios\"],\"name\":\"rustix\",\"optional\":false,\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[],\"name\":\"signal-hook\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[\"support-v1_0\"],\"name\":\"signal-hook-mio\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[],\"name\":\"crossterm_winapi\",\"optional\":true,\"target\":\"cfg(windows)\"},{\"default_features\":true,\"features\":[\"winuser\",\"winerror\"],\"name\":\"winapi\",\"optional\":true,\"target\":\"cfg(windows)\"}],\"features\":{\"bracketed-paste\":[],\"default\":[\"bracketed-paste\",\"windows\",\"events\"],\"event-stream\":[\"dep:futures-core\",\"events\"],\"events\":[\"dep:mio\",\"dep:signal-hook\",\"dep:signal-hook-mio\"],\"serde\":[\"dep:serde\",\"bitflags/serde\"],\"use-dev-tty\":[\"filedescriptor\",\"rustix/process\"],\"windows\":[\"dep:winapi\",\"dep:crossterm_winapi\"]},\"strip_prefix\":\"\"}",
"git+https://github.com/nornagon/ratatui?rev=9b2ad1298408c45918ee9f8241a6f95498cdbed2#9b2ad1298408c45918ee9f8241a6f95498cdbed2_ratatui": "{\"dependencies\":[{\"name\":\"bitflags\"},{\"name\":\"cassowary\"},{\"name\":\"compact_str\"},{\"default_features\":true,\"features\":[],\"name\":\"crossterm\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"document-features\",\"optional\":true},{\"name\":\"indoc\"},{\"name\":\"instability\"},{\"name\":\"itertools\"},{\"name\":\"lru\"},{\"default_features\":true,\"features\":[],\"name\":\"palette\",\"optional\":true},{\"name\":\"paste\"},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"strum\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"termwiz\",\"optional\":true},{\"default_features\":true,\"features\":[\"local-offset\"],\"name\":\"time\",\"optional\":true},{\"name\":\"unicode-segmentation\"},{\"name\":\"unicode-truncate\"},{\"name\":\"unicode-width\"},{\"default_features\":true,\"features\":[],\"name\":\"termion\",\"optional\":true,\"target\":\"cfg(not(windows))\"}],\"features\":{\"all-widgets\":[\"widget-calendar\"],\"crossterm\":[\"dep:crossterm\"],\"default\":[\"crossterm\",\"underline-color\"],\"macros\":[],\"palette\":[\"dep:palette\"],\"scrolling-regions\":[],\"serde\":[\"dep:serde\",\"bitflags/serde\",\"compact_str/serde\"],\"termion\":[\"dep:termion\"],\"termwiz\":[\"dep:termwiz\"],\"underline-color\":[\"dep:crossterm\"],\"unstable\":[\"unstable-rendered-line-info\",\"unstable-widget-ref\",\"unstable-backend-writer\"],\"unstable-backend-writer\":[],\"unstable-rendered-line-info\":[],\"unstable-widget-ref\":[],\"widget-calendar\":[\"dep:time\"]},\"strip_prefix\":\"\"}",
"git+https://github.com/openai-oss-forks/tokio-tungstenite?rev=132f5b39c862e3a970f731d709608b3e6276d5f6#132f5b39c862e3a970f731d709608b3e6276d5f6_tokio-tungstenite": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"sink\",\"std\"],\"name\":\"futures-util\",\"optional\":false},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"default_features\":false,\"features\":[],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"tokio-native-tls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tokio-rustls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tungstenite\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\",\"tokio-rustls\",\"stream\",\"tungstenite/__rustls-tls\",\"handshake\"],\"connect\":[\"stream\",\"tokio/net\",\"handshake\"],\"default\":[\"connect\",\"handshake\"],\"handshake\":[\"tungstenite/handshake\"],\"native-tls\":[\"native-tls-crate\",\"tokio-native-tls\",\"stream\",\"tungstenite/native-tls\",\"handshake\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\",\"tungstenite/native-tls-vendored\"],\"proxy\":[\"tungstenite/proxy\",\"tokio/net\",\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"stream\":[],\"url\":[\"tungstenite/url\"]},\"strip_prefix\":\"\"}",
"git+https://github.com/openai-oss-forks/tungstenite-rs?rev=9200079d3b54a1ff51072e24d81fd354f085156f#9200079d3b54a1ff51072e24d81fd354f085156f_tungstenite": "{\"dependencies\":[{\"name\":\"bytes\"},{\"default_features\":true,\"features\":[],\"name\":\"data-encoding\",\"optional\":true},{\"default_features\":false,\"features\":[\"zlib\"],\"name\":\"flate2\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"headers\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"http\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"httparse\",\"optional\":true},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"name\":\"rand\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"sha1\",\"optional\":true},{\"name\":\"thiserror\"},{\"default_features\":true,\"features\":[],\"name\":\"url\",\"optional\":true},{\"name\":\"utf-8\"},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\"],\"default\":[\"handshake\"],\"deflate\":[\"headers\",\"flate2\"],\"handshake\":[\"data-encoding\",\"headers\",\"httparse\",\"sha1\"],\"headers\":[\"http\",\"dep:headers\"],\"native-tls\":[\"native-tls-crate\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\"],\"proxy\":[\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"url\":[\"dep:url\"]},\"strip_prefix\":\"\"}",
"git+https://github.com/openai-oss-forks/tokio-tungstenite?rev=0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186#0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186_tokio-tungstenite": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"sink\",\"std\"],\"name\":\"futures-util\",\"optional\":false},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"default_features\":false,\"features\":[],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"tokio-native-tls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tokio-rustls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tungstenite\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\",\"tokio-rustls\",\"stream\",\"tungstenite/__rustls-tls\",\"handshake\"],\"connect\":[\"stream\",\"tokio/net\",\"tokio/time\",\"handshake\"],\"default\":[\"connect\",\"handshake\"],\"handshake\":[\"tungstenite/handshake\"],\"native-tls\":[\"native-tls-crate\",\"tokio-native-tls\",\"stream\",\"tungstenite/native-tls\",\"handshake\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\",\"tungstenite/native-tls-vendored\"],\"proxy\":[\"tungstenite/proxy\",\"tokio/net\",\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"stream\":[],\"url\":[\"tungstenite/url\"]},\"strip_prefix\":\"\"}",
"git+https://github.com/openai-oss-forks/tungstenite-rs?rev=4fffad30fe373adbdcffab9545e9e9bf4f2fc19f#4fffad30fe373adbdcffab9545e9e9bf4f2fc19f_tungstenite": "{\"dependencies\":[{\"name\":\"bytes\"},{\"default_features\":true,\"features\":[],\"name\":\"data-encoding\",\"optional\":true},{\"default_features\":false,\"features\":[\"zlib-rs\"],\"name\":\"flate2\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"headers\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"http\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"httparse\",\"optional\":true},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"name\":\"rand\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"sha1\",\"optional\":true},{\"name\":\"thiserror\"},{\"default_features\":true,\"features\":[],\"name\":\"url\",\"optional\":true},{\"name\":\"utf-8\"},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\"],\"default\":[\"handshake\"],\"deflate\":[\"headers\",\"flate2\"],\"handshake\":[\"data-encoding\",\"headers\",\"httparse\",\"sha1\"],\"headers\":[\"http\",\"dep:headers\"],\"native-tls\":[\"native-tls-crate\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\"],\"proxy\":[\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"url\":[\"dep:url\"]},\"strip_prefix\":\"\"}",
"git+https://github.com/rust-lang/rust-clippy?rev=20ce69b9a63bcd2756cd906fe0964d1e901e042a#20ce69b9a63bcd2756cd906fe0964d1e901e042a_clippy_utils": "{\"dependencies\":[{\"default_features\":false,\"features\":[],\"name\":\"arrayvec\",\"optional\":false},{\"name\":\"itertools\"},{\"name\":\"rustc_apfloat\"},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":false}],\"features\":{},\"strip_prefix\":\"clippy_utils\"}",
"git2_0.20.4": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.4.13\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"libgit2-sys\",\"req\":\"^0.18.3\"},{\"name\":\"log\",\"req\":\"^0.4.8\"},{\"name\":\"openssl-probe\",\"optional\":true,\"req\":\"^0.1\",\"target\":\"cfg(all(unix, not(target_os = \\\"macos\\\")))\"},{\"name\":\"openssl-sys\",\"optional\":true,\"req\":\"^0.9.45\",\"target\":\"cfg(all(unix, not(target_os = \\\"macos\\\")))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"},{\"features\":[\"formatting\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.37\"},{\"name\":\"url\",\"req\":\"^2.5.4\"}],\"features\":{\"default\":[\"ssh\",\"https\"],\"https\":[\"libgit2-sys/https\",\"openssl-sys\",\"openssl-probe\"],\"ssh\":[\"libgit2-sys/ssh\"],\"unstable\":[],\"vendored-libgit2\":[\"libgit2-sys/vendored\"],\"vendored-openssl\":[\"openssl-sys/vendored\",\"libgit2-sys/vendored-openssl\"],\"zlib-ng-compat\":[\"libgit2-sys/zlib-ng-compat\"]}}",
"gix-actor_0.40.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\",\"unicode\"],\"name\":\"bstr\",\"req\":\"^1.12.0\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"gix-date\",\"req\":\"^0.15.0\"},{\"name\":\"gix-error\",\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.114\"},{\"features\":[\"simd\"],\"name\":\"winnow\",\"req\":\"^0.7.14\"}],\"features\":{\"serde\":[\"dep:serde\",\"bstr/serde\",\"gix-date/serde\"]}}",
@@ -1193,7 +1197,6 @@
"libredox_0.1.14": "{\"dependencies\":[{\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"ioslice\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"plain\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"redox_syscall\",\"optional\":true,\"req\":\"^0.7\"}],\"features\":{\"base\":[\"libc\"],\"call\":[\"base\"],\"default\":[\"base\",\"call\",\"std\",\"redox_syscall\",\"protocol\"],\"mkns\":[\"ioslice\"],\"protocol\":[\"plain\",\"bitflags\",\"redox_syscall\"],\"std\":[\"base\"]}}",
"libsqlite3-sys_0.37.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"runtime\"],\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.72\"},{\"kind\":\"build\",\"name\":\"cc\",\"optional\":true,\"req\":\"^1.2.27\"},{\"name\":\"openssl-sys\",\"optional\":true,\"req\":\"^0.9.103\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"optional\":true,\"req\":\"^0.3.19\"},{\"kind\":\"build\",\"name\":\"prettyplease\",\"optional\":true,\"req\":\"^0.2.20\"},{\"default_features\":false,\"kind\":\"build\",\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0.36\"},{\"features\":[\"full\",\"extra-traits\",\"visit-mut\"],\"kind\":\"build\",\"name\":\"syn\",\"optional\":true,\"req\":\"^2.0.89\"},{\"kind\":\"build\",\"name\":\"vcpkg\",\"optional\":true,\"req\":\"^0.2.15\"}],\"features\":{\"buildtime_bindgen\":[\"bindgen\",\"pkg-config\",\"vcpkg\"],\"bundled\":[\"cc\",\"bundled_bindings\"],\"bundled-sqlcipher\":[\"bundled\"],\"bundled-sqlcipher-vendored-openssl\":[\"bundled-sqlcipher\",\"openssl-sys/vendored\"],\"bundled-windows\":[\"cc\",\"bundled_bindings\"],\"bundled_bindings\":[],\"column_metadata\":[],\"default\":[\"min_sqlite_version_3_34_1\"],\"in_gecko\":[],\"loadable_extension\":[\"prettyplease\",\"quote\",\"syn\"],\"min_sqlite_version_3_34_1\":[\"pkg-config\",\"vcpkg\"],\"preupdate_hook\":[\"buildtime_bindgen\"],\"session\":[\"preupdate_hook\",\"buildtime_bindgen\"],\"sqlcipher\":[],\"unlock_notify\":[],\"wasm32-wasi-vfs\":[],\"with-asan\":[]}}",
"libssh2-sys_0.3.1": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.25\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"libc\"],\"name\":\"libz-sys\",\"req\":\"^1.1.0\"},{\"name\":\"openssl-sys\",\"req\":\"^0.9.35\",\"target\":\"cfg(unix)\"},{\"name\":\"openssl-sys\",\"optional\":true,\"req\":\"^0.9.35\",\"target\":\"cfg(windows)\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.11\"},{\"kind\":\"build\",\"name\":\"vcpkg\",\"req\":\"^0.2\",\"target\":\"cfg(target_env = \\\"msvc\\\")\"}],\"features\":{\"openssl-on-win32\":[\"openssl-sys\"],\"vendored-openssl\":[\"openssl-sys/vendored\"],\"zlib-ng-compat\":[\"libz-sys/zlib-ng\"]}}",
"libz-sys_1.1.23": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.98\"},{\"kind\":\"build\",\"name\":\"cmake\",\"optional\":true,\"req\":\"^0.1.50\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.43\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.9\"},{\"kind\":\"build\",\"name\":\"vcpkg\",\"req\":\"^0.2.11\"}],\"features\":{\"asm\":[],\"default\":[\"libc\",\"stock-zlib\"],\"static\":[],\"stock-zlib\":[],\"zlib-ng\":[\"libc\",\"cmake\"],\"zlib-ng-no-cmake-experimental-community-maintained\":[\"libc\"]}}",
"libz-sys_1.1.25": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.98\"},{\"kind\":\"build\",\"name\":\"cmake\",\"optional\":true,\"req\":\"^0.1.50\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.43\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.9\"},{\"kind\":\"build\",\"name\":\"vcpkg\",\"req\":\"^0.2.11\"}],\"features\":{\"asm\":[],\"default\":[\"libc\",\"stock-zlib\"],\"static\":[],\"stock-zlib\":[],\"zlib-ng\":[\"libc\",\"cmake\"],\"zlib-ng-no-cmake-experimental-community-maintained\":[\"libc\"]}}",
"link-cplusplus_1.0.12": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1\"}],\"features\":{\"default\":[],\"libc++\":[],\"libcxx\":[\"libc++\"],\"libstdc++\":[],\"libstdcxx\":[\"libstdc++\"],\"nothing\":[]}}",
"link-section_0.17.2": "{\"dependencies\":[{\"features\":[\"link_section\"],\"name\":\"linktime-proc-macro\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"macrotest\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2\"}],\"features\":{\"default\":[\"proc_macro\",\"std\"],\"proc_macro\":[\"dep:linktime-proc-macro\"],\"std\":[]}}",
@@ -1290,7 +1293,7 @@
"openssl-macros_0.1.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}",
"openssl-probe_0.1.6": "{\"dependencies\":[],\"features\":{}}",
"openssl-probe_0.2.1": "{\"dependencies\":[],\"features\":{}}",
"openssl-src_300.5.5+3.5.5": "{\"dependencies\":[{\"name\":\"cc\",\"req\":\"^1.0.79\"}],\"features\":{\"camellia\":[],\"default\":[],\"force-engine\":[],\"idea\":[],\"ktls\":[],\"legacy\":[],\"no-dso\":[],\"seed\":[],\"ssl3\":[],\"weak-crypto\":[]}}",
"openssl-src_300.6.1+3.6.3": "{\"dependencies\":[{\"name\":\"cc\",\"req\":\"^1.0.79\"}],\"features\":{\"camellia\":[],\"default\":[],\"force-engine\":[],\"idea\":[],\"ktls\":[],\"legacy\":[],\"no-dso\":[],\"seed\":[],\"ssl3\":[],\"weak-crypto\":[]}}",
"openssl-sys_0.9.111": "{\"dependencies\":[{\"features\":[\"ssl\",\"bindgen\"],\"name\":\"aws-lc-fips-sys\",\"optional\":true,\"req\":\"^0.13\"},{\"features\":[\"ssl\"],\"name\":\"aws-lc-sys\",\"optional\":true,\"req\":\"^0.27\"},{\"features\":[\"experimental\"],\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.72.0\"},{\"name\":\"bssl-sys\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.61\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"features\":[\"legacy\"],\"kind\":\"build\",\"name\":\"openssl-src\",\"optional\":true,\"req\":\"^300.2.0\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.9\"},{\"kind\":\"build\",\"name\":\"vcpkg\",\"req\":\"^0.2.8\"}],\"features\":{\"aws-lc\":[\"dep:aws-lc-sys\"],\"aws-lc-fips\":[\"dep:aws-lc-fips-sys\"],\"unstable_boringssl\":[\"bssl-sys\"],\"vendored\":[\"openssl-src\"]}}",
"openssl-sys_0.9.112": "{\"dependencies\":[{\"features\":[\"ssl\",\"bindgen\"],\"name\":\"aws-lc-fips-sys\",\"optional\":true,\"req\":\"^0.13\"},{\"features\":[\"ssl\"],\"name\":\"aws-lc-sys\",\"optional\":true,\"req\":\"^0.38\"},{\"features\":[\"experimental\"],\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.72.0\"},{\"name\":\"bssl-sys\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.61\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"features\":[\"legacy\"],\"kind\":\"build\",\"name\":\"openssl-src\",\"optional\":true,\"req\":\"^300.2.0\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.9\"},{\"kind\":\"build\",\"name\":\"vcpkg\",\"req\":\"^0.2.8\"}],\"features\":{\"aws-lc\":[\"dep:aws-lc-sys\"],\"aws-lc-fips\":[\"dep:aws-lc-fips-sys\"],\"unstable_boringssl\":[\"bssl-sys\"],\"vendored\":[\"openssl-src\"]}}",
"openssl_0.10.75": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.2.1\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"ffi\",\"package\":\"openssl-sys\",\"req\":\"^0.9.111\"},{\"name\":\"foreign-types\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"once_cell\",\"req\":\"^1.5.2\"},{\"name\":\"openssl-macros\",\"req\":\"^0.1.1\"}],\"features\":{\"aws-lc\":[\"ffi/aws-lc\"],\"aws-lc-fips\":[\"ffi/aws-lc-fips\"],\"bindgen\":[\"ffi/bindgen\"],\"default\":[],\"unstable_boringssl\":[\"ffi/unstable_boringssl\"],\"v101\":[],\"v102\":[],\"v110\":[],\"v111\":[],\"vendored\":[\"ffi/vendored\"]}}",
@@ -1446,8 +1449,8 @@
"resolv-conf_0.7.6": "{\"dependencies\":[],\"features\":{\"system\":[]}}",
"rfc6979_0.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"reset\"],\"name\":\"hmac\",\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2\"}],\"features\":{}}",
"ring_0.17.14": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.2.8\"},{\"default_features\":false,\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"getrandom\",\"req\":\"^0.2.10\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.148\",\"target\":\"cfg(all(any(all(target_arch = \\\"aarch64\\\", target_endian = \\\"little\\\"), all(target_arch = \\\"arm\\\", target_endian = \\\"little\\\")), any(target_os = \\\"android\\\", target_os = \\\"linux\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(all(target_arch = \\\"aarch64\\\", target_endian = \\\"little\\\"), target_vendor = \\\"apple\\\", any(target_os = \\\"ios\\\", target_os = \\\"macos\\\", target_os = \\\"tvos\\\", target_os = \\\"visionos\\\", target_os = \\\"watchos\\\")))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.148\",\"target\":\"cfg(any(unix, windows, target_os = \\\"wasi\\\"))\"},{\"name\":\"untrusted\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.37\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Threading\"],\"name\":\"windows-sys\",\"req\":\"^0.52\",\"target\":\"cfg(all(all(target_arch = \\\"aarch64\\\", target_endian = \\\"little\\\"), target_os = \\\"windows\\\"))\"}],\"features\":{\"alloc\":[],\"default\":[\"alloc\",\"dev_urandom_fallback\"],\"dev_urandom_fallback\":[],\"less-safe-getrandom-custom-or-rdrand\":[],\"less-safe-getrandom-espidf\":[],\"slow_tests\":[],\"std\":[\"alloc\"],\"test_logging\":[],\"unstable-testing-arm-no-hw\":[],\"unstable-testing-arm-no-neon\":[],\"wasm32_unknown_unknown_js\":[\"getrandom/js\"]}}",
"rmcp-macros_1.7.0": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.23\"},{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"local\":[]}}",
"rmcp_1.7.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"async-trait\",\"req\":\"^0.1.89\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"http1\",\"tokio\"],\"kind\":\"dev\",\"name\":\"axum\",\"req\":\"^0.8\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"serde\",\"clock\",\"std\",\"oldtime\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"serde\",\"now\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"http\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"client\",\"http1\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"server\",\"http1\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"name\":\"jsonwebtoken\",\"optional\":true,\"req\":\"^10\"},{\"default_features\":false,\"name\":\"oauth2\",\"optional\":true,\"req\":\"^5.0\"},{\"name\":\"pastey\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"tokio1\"],\"name\":\"process-wrap\",\"optional\":true,\"req\":\"^9.0\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.10\"},{\"default_features\":false,\"features\":[\"json\",\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.13.2\"},{\"name\":\"rmcp-macros\",\"optional\":true,\"req\":\"^1.7.0\"},{\"features\":[\"chrono04\"],\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"chrono04\"],\"kind\":\"dev\",\"name\":\"schemars\",\"req\":\"^1.1.0\"},{\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sse-stream\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\",\"macros\",\"rt\",\"time\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tower-service\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.4\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2.4\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"which\",\"optional\":true,\"req\":\"^8\"}],\"features\":{\"__reqwest\":[\"dep:reqwest\"],\"auth\":[\"dep:oauth2\",\"__reqwest\",\"dep:url\"],\"auth-client-credentials-jwt\":[\"auth\",\"dep:jsonwebtoken\",\"uuid\"],\"client\":[\"dep:tokio-stream\"],\"client-side-sse\":[\"dep:sse-stream\",\"dep:http\"],\"default\":[\"base64\",\"macros\",\"server\"],\"elicitation\":[\"dep:url\"],\"local\":[\"rmcp-macros?/local\"],\"macros\":[\"dep:rmcp-macros\",\"dep:pastey\"],\"reqwest\":[\"__reqwest\",\"reqwest?/rustls\"],\"reqwest-native-tls\":[\"__reqwest\",\"reqwest?/native-tls\"],\"reqwest-tls-no-provider\":[\"__reqwest\",\"reqwest?/rustls-no-provider\"],\"schemars\":[\"dep:schemars\"],\"server\":[\"transport-async-rw\",\"dep:schemars\",\"dep:pastey\"],\"server-side-http\":[\"uuid\",\"dep:rand\",\"dep:tokio-stream\",\"dep:http\",\"dep:http-body\",\"dep:http-body-util\",\"dep:bytes\",\"dep:sse-stream\",\"tower\"],\"tower\":[\"dep:tower-service\"],\"transport-async-rw\":[\"tokio/io-util\",\"tokio-util/codec\"],\"transport-child-process\":[\"transport-async-rw\",\"tokio/process\",\"dep:process-wrap\"],\"transport-io\":[\"transport-async-rw\",\"tokio/io-std\"],\"transport-streamable-http-client\":[\"client-side-sse\",\"transport-worker\"],\"transport-streamable-http-client-reqwest\":[\"transport-streamable-http-client\",\"__reqwest\"],\"transport-streamable-http-client-unix-socket\":[\"transport-streamable-http-client\",\"dep:hyper\",\"dep:hyper-util\",\"dep:http-body-util\",\"dep:http\",\"dep:bytes\",\"tokio/net\"],\"transport-streamable-http-server\":[\"transport-streamable-http-server-session\",\"server-side-http\",\"transport-worker\"],\"transport-streamable-http-server-session\":[\"transport-async-rw\",\"dep:tokio-stream\"],\"transport-worker\":[\"dep:tokio-stream\"],\"which-command\":[\"transport-child-process\",\"dep:which\"]}}",
"rmcp-macros_1.8.0": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.23\"},{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"local\":[]}}",
"rmcp_1.8.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"async-trait\",\"req\":\"^0.1.89\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"http1\",\"tokio\"],\"kind\":\"dev\",\"name\":\"axum\",\"req\":\"^0.8\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"serde\",\"clock\",\"std\",\"oldtime\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"serde\",\"now\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"http\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"client\",\"http1\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"server\",\"http1\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"name\":\"jsonwebtoken\",\"optional\":true,\"req\":\"^10\"},{\"default_features\":false,\"name\":\"oauth2\",\"optional\":true,\"req\":\"^5.0\"},{\"name\":\"pastey\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"tokio1\"],\"name\":\"process-wrap\",\"optional\":true,\"req\":\"^9.0\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.10\"},{\"default_features\":false,\"features\":[\"json\",\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.13.2\"},{\"name\":\"rmcp-macros\",\"optional\":true,\"req\":\"^1.8.0\"},{\"features\":[\"chrono04\"],\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"chrono04\"],\"kind\":\"dev\",\"name\":\"schemars\",\"req\":\"^1.1.0\"},{\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sse-stream\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\",\"macros\",\"rt\",\"time\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tower-service\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.4\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2.4\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"which\",\"optional\":true,\"req\":\"^8\"}],\"features\":{\"__reqwest\":[\"dep:reqwest\"],\"auth\":[\"dep:oauth2\",\"__reqwest\",\"dep:url\"],\"auth-client-credentials-jwt\":[\"auth\",\"dep:jsonwebtoken\",\"uuid\"],\"client\":[\"dep:tokio-stream\"],\"client-side-sse\":[\"dep:sse-stream\",\"dep:http\"],\"default\":[\"base64\",\"macros\",\"server\"],\"elicitation\":[\"dep:url\"],\"local\":[\"rmcp-macros?/local\"],\"macros\":[\"dep:rmcp-macros\",\"dep:pastey\"],\"reqwest\":[\"__reqwest\",\"reqwest?/rustls\"],\"reqwest-native-tls\":[\"__reqwest\",\"reqwest?/native-tls\"],\"reqwest-tls-no-provider\":[\"__reqwest\",\"reqwest?/rustls-no-provider\"],\"schemars\":[\"dep:schemars\"],\"server\":[\"transport-async-rw\",\"dep:schemars\",\"dep:pastey\"],\"server-side-http\":[\"uuid\",\"dep:rand\",\"dep:tokio-stream\",\"dep:http\",\"dep:http-body\",\"dep:http-body-util\",\"dep:bytes\",\"dep:sse-stream\",\"tower\"],\"tower\":[\"dep:tower-service\"],\"transport-async-rw\":[\"tokio/io-util\",\"tokio-util/codec\"],\"transport-child-process\":[\"transport-async-rw\",\"tokio/process\",\"dep:process-wrap\"],\"transport-io\":[\"transport-async-rw\",\"tokio/io-std\"],\"transport-streamable-http-client\":[\"client-side-sse\",\"transport-worker\"],\"transport-streamable-http-client-reqwest\":[\"transport-streamable-http-client\",\"__reqwest\"],\"transport-streamable-http-client-unix-socket\":[\"transport-streamable-http-client\",\"dep:hyper\",\"dep:hyper-util\",\"dep:http-body-util\",\"dep:http\",\"dep:bytes\",\"tokio/net\"],\"transport-streamable-http-server\":[\"transport-streamable-http-server-session\",\"server-side-http\",\"transport-worker\"],\"transport-streamable-http-server-session\":[\"transport-async-rw\",\"dep:tokio-stream\"],\"transport-worker\":[\"dep:tokio-stream\"],\"which-command\":[\"transport-child-process\",\"dep:which\"]}}",
"rtrb_0.3.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"crossbeam-utils\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.10\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"rust-embed-impl_8.11.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"rust-embed-utils\",\"req\":\"^8.11.0\"},{\"name\":\"shellexpand\",\"optional\":true,\"req\":\"^3\"},{\"default_features\":false,\"features\":[\"derive\",\"parsing\",\"proc-macro\",\"printing\"],\"name\":\"syn\",\"req\":\"^2\"},{\"name\":\"walkdir\",\"req\":\"^2.3.1\"}],\"features\":{\"compression\":[],\"debug-embed\":[],\"deterministic-timestamps\":[],\"include-exclude\":[\"rust-embed-utils/include-exclude\"],\"interpolate-folder-path\":[\"shellexpand\"],\"mime-guess\":[\"rust-embed-utils/mime-guess\"]}}",
"rust-embed-utils_8.11.0": "{\"dependencies\":[{\"name\":\"globset\",\"optional\":true,\"req\":\"^0.4.8\"},{\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0.4\"},{\"name\":\"sha2\",\"req\":\"^0.10.5\"},{\"name\":\"walkdir\",\"req\":\"^2.3.1\"}],\"features\":{\"debug-embed\":[],\"include-exclude\":[\"globset\"],\"mime-guess\":[\"mime_guess\"]}}",
@@ -1872,6 +1875,7 @@
"zerovec_0.11.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"features\":[\"wasm_js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"iai\",\"req\":\"^0.1.1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.43.2\"},{\"default_features\":false,\"features\":[\"use-std\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_distr\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rmp-serde\",\"req\":\"^1.2.0\"},{\"default_features\":false,\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0.4\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"features\":[\"xxhash64\"],\"name\":\"twox-hash\",\"optional\":true,\"req\":\"^2.0.0\"},{\"default_features\":false,\"name\":\"yoke\",\"optional\":true,\"req\":\"^0.8.2\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"yoke\",\"req\":\"^0.8.2\"},{\"default_features\":false,\"name\":\"zerofrom\",\"req\":\"^0.1.6\"},{\"default_features\":false,\"name\":\"zerovec-derive\",\"optional\":true,\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"serde?/alloc\"],\"databake\":[\"dep:databake\"],\"derive\":[\"dep:zerovec-derive\"],\"hashmap\":[\"dep:twox-hash\",\"alloc\"],\"schemars\":[\"dep:schemars\",\"alloc\"],\"serde\":[\"dep:serde\"],\"std\":[],\"yoke\":[\"dep:yoke\"]}}",
"zip_0.6.6": "{\"dependencies\":[{\"name\":\"aes\",\"optional\":true,\"req\":\"^0.8.2\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"byteorder\",\"req\":\"^1.4.3\"},{\"name\":\"bzip2\",\"optional\":true,\"req\":\"^0.4.3\"},{\"name\":\"constant_time_eq\",\"optional\":true,\"req\":\"^0.1.5\"},{\"name\":\"crc32fast\",\"req\":\"^1.3.2\"},{\"name\":\"crossbeam-utils\",\"req\":\"^0.8.8\",\"target\":\"cfg(any(all(target_arch = \\\"arm\\\", target_pointer_width = \\\"32\\\"), target_arch = \\\"mips\\\", target_arch = \\\"powerpc\\\"))\"},{\"default_features\":false,\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0.23\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.5\"},{\"features\":[\"reset\"],\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12.1\"},{\"name\":\"pbkdf2\",\"optional\":true,\"req\":\"^0.11.0\"},{\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.7\"},{\"features\":[\"formatting\",\"macros\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.7\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3.2\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.11.2\"}],\"features\":{\"aes-crypto\":[\"aes\",\"constant_time_eq\",\"hmac\",\"pbkdf2\",\"sha1\"],\"default\":[\"aes-crypto\",\"bzip2\",\"deflate\",\"time\",\"zstd\"],\"deflate\":[\"flate2/rust_backend\"],\"deflate-miniz\":[\"flate2/default\"],\"deflate-zlib\":[\"flate2/zlib\"],\"unreserved\":[]}}",
"zip_2.4.2": "{\"dependencies\":[{\"name\":\"aes\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.95\"},{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"req\":\"^1.4.1\",\"target\":\"cfg(fuzzing)\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bzip2\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"=4.4.18\"},{\"name\":\"constant_time_eq\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"crc32fast\",\"req\":\"^1.4\"},{\"name\":\"crossbeam-utils\",\"req\":\"^0.8.21\",\"target\":\"cfg(any(all(target_arch = \\\"arm\\\", target_pointer_width = \\\"32\\\"), target_arch = \\\"mips\\\", target_arch = \\\"powerpc\\\"))\"},{\"name\":\"deflate64\",\"optional\":true,\"req\":\"^0.1.9\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"wasm_js\",\"std\"],\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.1\"},{\"features\":[\"wasm_js\",\"std\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3.1\"},{\"features\":[\"reset\"],\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"indexmap\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"lzma-rs\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"memchr\",\"req\":\"^2.7\"},{\"default_features\":false,\"name\":\"nt-time\",\"optional\":true,\"req\":\"^0.10.6\"},{\"name\":\"pbkdf2\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.15\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.37\"},{\"default_features\":false,\"features\":[\"formatting\",\"macros\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.37\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5\"},{\"name\":\"xz2\",\"optional\":true,\"req\":\"^0.1.7\"},{\"features\":[\"zeroize_derive\"],\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.8\"},{\"name\":\"zopfli\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13\"}],\"features\":{\"_all-features\":[],\"_deflate-any\":[],\"aes-crypto\":[\"aes\",\"constant_time_eq\",\"hmac\",\"pbkdf2\",\"sha1\",\"getrandom\",\"zeroize\"],\"chrono\":[\"chrono/default\"],\"default\":[\"aes-crypto\",\"bzip2\",\"deflate64\",\"deflate\",\"lzma\",\"time\",\"zstd\",\"xz\"],\"deflate\":[\"flate2/rust_backend\",\"deflate-zopfli\",\"deflate-flate2\"],\"deflate-flate2\":[\"_deflate-any\"],\"deflate-miniz\":[\"deflate\",\"deflate-flate2\"],\"deflate-zlib\":[\"flate2/zlib\",\"deflate-flate2\"],\"deflate-zlib-ng\":[\"flate2/zlib-ng\",\"deflate-flate2\"],\"deflate-zopfli\":[\"zopfli\",\"_deflate-any\"],\"lzma\":[\"lzma-rs/stream\"],\"nt-time\":[\"dep:nt-time\"],\"unreserved\":[],\"xz\":[\"dep:xz2\"]}}",
"zlib-rs_0.5.5": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"crc32fast\",\"req\":\"^1.3.2\"},{\"kind\":\"dev\",\"name\":\"memoffset\",\"req\":\"^0.9.1\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"}],\"features\":{\"ZLIB_DEBUG\":[],\"__internal-fuzz\":[\"arbitrary\"],\"__internal-fuzz-disable-checksum\":[],\"__internal-test\":[\"quickcheck\"],\"avx512\":[\"vpclmulqdq\"],\"c-allocator\":[],\"default\":[\"std\",\"c-allocator\"],\"rust-allocator\":[],\"std\":[\"rust-allocator\"],\"vpclmulqdq\":[]}}",
"zlib-rs_0.6.3": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"crc32fast\",\"req\":\"^1.3.2\"},{\"kind\":\"dev\",\"name\":\"memoffset\",\"req\":\"^0.9.1\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"}],\"features\":{\"ZLIB_DEBUG\":[],\"__internal-api\":[],\"__internal-fuzz\":[\"arbitrary\"],\"__internal-fuzz-disable-checksum\":[],\"__internal-test\":[\"quickcheck\"],\"avx512\":[\"vpclmulqdq\"],\"c-allocator\":[],\"default\":[\"std\",\"c-allocator\"],\"rust-allocator\":[],\"std\":[\"rust-allocator\"],\"vpclmulqdq\":[]}}",
"zmij_1.0.19": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\",\"target\":\"cfg(not(miri))\"},{\"name\":\"no-panic\",\"optional\":true,\"req\":\"^0.1.36\"},{\"kind\":\"dev\",\"name\":\"num-bigint\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"num-integer\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"opt-level\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"ryu\",\"req\":\"^1\"}],\"features\":{}}",
"zmij_1.0.21": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\",\"target\":\"cfg(not(miri))\"},{\"name\":\"no-panic\",\"optional\":true,\"req\":\"^0.1.36\"},{\"kind\":\"dev\",\"name\":\"num-bigint\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"num-integer\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"opt-level\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"ryu\",\"req\":\"^1\"}],\"features\":{}}",
+24 -21
View File
@@ -2136,6 +2136,7 @@ dependencies = [
"codex-otel",
"codex-protocol",
"codex-utils-cli",
"pretty_assertions",
"serde",
"serde_json",
"tokio",
@@ -2403,12 +2404,15 @@ dependencies = [
"rustls-pki-types",
"serde",
"serde_json",
"sha2 0.10.9",
"system-configuration",
"tempfile",
"thiserror 2.0.18",
"tokio",
"tracing",
"tracing-opentelemetry",
"tracing-subscriber",
"windows-sys 0.52.0",
"zstd 0.13.3",
]
@@ -2809,6 +2813,7 @@ dependencies = [
"codex-utils-plugins",
"dirs",
"dunce",
"futures",
"pretty_assertions",
"serde",
"serde_json",
@@ -3528,6 +3533,8 @@ dependencies = [
"rama-tls-rustls",
"rama-unix",
"rustls-native-certs",
"schannel",
"security-framework 3.5.1",
"serde",
"serde_json",
"sha2 0.10.9",
@@ -4015,6 +4022,7 @@ name = "codex-tui"
version = "0.0.0"
dependencies = [
"anyhow",
"app_test_support",
"arboard",
"assert_matches",
"base64 0.22.1",
@@ -6023,8 +6031,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
dependencies = [
"crc32fast",
"libz-sys",
"miniz_oxide",
"zlib-rs 0.5.5",
]
[[package]]
@@ -6712,7 +6720,7 @@ dependencies = [
"prodash",
"thiserror 2.0.18",
"walkdir",
"zlib-rs",
"zlib-rs 0.6.3",
]
[[package]]
@@ -8765,17 +8773,6 @@ dependencies = [
"webrtc-sys",
]
[[package]]
name = "libz-sys"
version = "1.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "link-cplusplus"
version = "1.0.12"
@@ -9739,9 +9736,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "openssl-src"
version = "300.5.5+3.5.5"
version = "300.6.1+3.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709"
checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846"
dependencies = [
"cc",
]
@@ -11458,9 +11455,9 @@ dependencies = [
[[package]]
name = "rmcp"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e"
checksum = "1d1f571c72940a19d9532fe52dbea8bc9912bf1d766c2970bb824056b86f3f59"
dependencies = [
"async-trait",
"base64 0.22.1",
@@ -11493,9 +11490,9 @@ dependencies = [
[[package]]
name = "rmcp-macros"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aefac48c364756e97f04c0401ba3231e8607882c7c1d92da0437dc16307904d"
checksum = "1aad0035b69380782d78ea95b508327e6deaa2235909053e596eea8f27b5e1d5"
dependencies = [
"darling 0.23.0",
"proc-macro2",
@@ -13603,7 +13600,7 @@ dependencies = [
[[package]]
name = "tokio-tungstenite"
version = "0.28.0"
source = "git+https://github.com/openai-oss-forks/tokio-tungstenite?rev=132f5b39c862e3a970f731d709608b3e6276d5f6#132f5b39c862e3a970f731d709608b3e6276d5f6"
source = "git+https://github.com/openai-oss-forks/tokio-tungstenite?rev=0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186#0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186"
dependencies = [
"futures-util",
"log",
@@ -14041,7 +14038,7 @@ dependencies = [
[[package]]
name = "tungstenite"
version = "0.27.0"
source = "git+https://github.com/openai-oss-forks/tungstenite-rs?rev=9200079d3b54a1ff51072e24d81fd354f085156f#9200079d3b54a1ff51072e24d81fd354f085156f"
source = "git+https://github.com/openai-oss-forks/tungstenite-rs?rev=4fffad30fe373adbdcffab9545e9e9bf4f2fc19f#4fffad30fe373adbdcffab9545e9e9bf4f2fc19f"
dependencies = [
"bytes",
"data-encoding",
@@ -15802,6 +15799,12 @@ dependencies = [
"zstd 0.13.3",
]
[[package]]
name = "zlib-rs"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3"
[[package]]
name = "zlib-rs"
version = "0.6.3"
+6 -5
View File
@@ -368,7 +368,7 @@ rcgen = { version = "0.14.7", default-features = false, features = [
regex = "1.12.3"
regex-lite = "0.1.8"
reqwest = { version = "0.12", features = ["cookies"] }
rmcp = { version = "1.7.0", default-features = false }
rmcp = { version = "1.8.0", default-features = false }
runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" }
rustls = { version = "0.23", default-features = false, features = [
"aws_lc_rs",
@@ -380,7 +380,7 @@ schemars = "0.8.22"
seccompiler = "0.5.0"
semver = "1.0"
sentry = "0.46.0"
serde = "1"
serde = { version = "1", features = ["rc"] }
serde_ignored = "0.1.14"
serde_json = "1"
serde_path_to_error = "0.1.20"
@@ -409,6 +409,7 @@ strum_macros = "0.28.0"
supports-color = "3.0.2"
syntect = "5"
sys-locale = "0.3.2"
system-configuration = "0.7"
tar = { version = "=0.4.45", default-features = false }
tempfile = "3.23.0"
test-log = "0.2.19"
@@ -546,11 +547,11 @@ opt-level = 0
# ratatui = { path = "../../ratatui" }
crossterm = { git = "https://github.com/nornagon/crossterm", rev = "87db8bfa6dc99427fd3b071681b07fc31c6ce995" }
ratatui = { git = "https://github.com/nornagon/ratatui", rev = "9b2ad1298408c45918ee9f8241a6f95498cdbed2" }
tokio-tungstenite = { git = "https://github.com/openai-oss-forks/tokio-tungstenite", rev = "132f5b39c862e3a970f731d709608b3e6276d5f6" }
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "9200079d3b54a1ff51072e24d81fd354f085156f" }
tokio-tungstenite = { git = "https://github.com/openai-oss-forks/tokio-tungstenite", rev = "0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186" }
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "4fffad30fe373adbdcffab9545e9e9bf4f2fc19f" }
# Uncomment to debug local changes.
# rmcp = { path = "../../rust-sdk/crates/rmcp" }
[patch."ssh://git@github.com/openai-oss-forks/tungstenite-rs.git"]
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "9200079d3b54a1ff51072e24d81fd354f085156f" }
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "4fffad30fe373adbdcffab9545e9e9bf4f2fc19f" }
+24 -4
View File
@@ -119,7 +119,7 @@ pub struct AgentIdentityJwtClaims {
pub agent_private_key: String,
pub account_id: String,
pub chatgpt_user_id: String,
pub email: String,
pub email: Option<String>,
pub plan_type: AuthPlanType,
pub chatgpt_account_is_fedramp: bool,
}
@@ -667,13 +667,33 @@ mod tests {
agent_private_key: "private-key".to_string(),
account_id: "account-id".to_string(),
chatgpt_user_id: "user-id".to_string(),
email: "user@example.com".to_string(),
email: Some("user@example.com".to_string()),
plan_type: AuthPlanType::Known(KnownPlan::Pro),
chatgpt_account_is_fedramp: false,
}
);
}
#[test]
fn decode_agent_identity_jwt_accepts_missing_email() {
let jwt = jwt_with_payload(serde_json::json!({
"iss": AGENT_IDENTITY_JWT_ISSUER,
"aud": AGENT_IDENTITY_JWT_AUDIENCE,
"iat": 1_700_000_000usize,
"exp": 4_000_000_000usize,
"agent_runtime_id": "agent-runtime-id",
"agent_private_key": "private-key",
"account_id": "account-id",
"chatgpt_user_id": "user-id",
"plan_type": "pro",
"chatgpt_account_is_fedramp": false,
}));
let claims = decode_agent_identity_jwt(&jwt, /*jwks*/ None).expect("JWT should decode");
assert_eq!(claims.email, None);
}
#[test]
fn decode_agent_identity_jwt_maps_raw_plan_aliases() {
let jwt = jwt_with_payload(serde_json::json!({
@@ -707,7 +727,7 @@ mod tests {
agent_private_key: "private-key".to_string(),
account_id: "account-id".to_string(),
chatgpt_user_id: "user-id".to_string(),
email: "user@example.com".to_string(),
email: Some("user@example.com".to_string()),
plan_type: AuthPlanType::Known(KnownPlan::Pro),
chatgpt_account_is_fedramp: false,
};
@@ -739,7 +759,7 @@ mod tests {
agent_private_key: "private-key".to_string(),
account_id: "account-id".to_string(),
chatgpt_user_id: "user-id".to_string(),
email: "user@example.com".to_string(),
email: Some("user@example.com".to_string()),
plan_type: AuthPlanType::Known(KnownPlan::Pro),
chatgpt_account_is_fedramp: false,
};
@@ -3504,10 +3504,11 @@ async fn reducer_ingests_plugin_install_failed_fact() {
async fn reducer_ingests_plugin_install_failed_fact_without_detail() {
let mut reducer = AnalyticsReducer::default();
let mut events = Vec::new();
let mut plugin = PluginTelemetryMetadata::from_plugin_id(
&PluginId::parse("unknown@openai-curated-remote").expect("valid plugin id"),
);
plugin.remote_plugin_id = Some("plugins~Plugin_00000000000000000000000000000000".to_string());
let plugin = PluginTelemetryMetadata {
plugin_id: PluginId::parse("unknown@openai-curated-remote").expect("valid plugin id"),
remote_plugin_id: Some("plugins~Plugin_00000000000000000000000000000000".to_string()),
capability_summary: None,
};
reducer
.ingest(
+65 -56
View File
@@ -1315,6 +1315,18 @@
],
"type": "object"
},
"InternalChatMessageMetadataPassthrough": {
"description": "Internal Responses API passthrough metadata copied into underlying chat messages.\n\nResponses API strongly types this payload. Do not modify it without first getting API approval and making the corresponding Responses API change.",
"properties": {
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"LegacyAppPathString": {
"type": "string"
},
@@ -1761,8 +1773,9 @@
"type": "object"
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -2301,10 +2314,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2357,10 +2370,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2411,10 +2424,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2461,10 +2474,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2504,10 +2517,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2558,10 +2571,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2601,10 +2614,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2644,10 +2657,10 @@
"input": {
"type": "string"
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2691,10 +2704,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2743,10 +2756,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2795,10 +2808,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2833,10 +2846,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2882,10 +2895,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2909,16 +2922,6 @@
},
{
"properties": {
"metadata": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
},
{
"type": "null"
}
]
},
"type": {
"enum": [
"compaction_trigger"
@@ -2947,10 +2950,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -2989,23 +2992,6 @@
}
]
},
"ResponseItemMetadata": {
"properties": {
"source_call_id": {
"type": [
"string",
"null"
]
},
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"ResponsesApiWebSearchAction": {
"oneOf": [
{
@@ -6461,6 +6447,29 @@
"title": "Account/usage/readRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"account/workspaceMessages/read"
],
"title": "Account/workspaceMessages/readRequestMethod",
"type": "string"
},
"params": {
"type": "null"
}
},
"required": [
"id",
"method"
],
"title": "Account/workspaceMessages/readRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -2583,6 +2583,49 @@
],
"type": "object"
},
"ModelSafetyBufferingUpdatedNotification": {
"properties": {
"fasterModel": {
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"reasons": {
"items": {
"type": "string"
},
"type": "array"
},
"showBufferingUi": {
"type": "boolean"
},
"threadId": {
"type": "string"
},
"turnId": {
"type": "string"
},
"useCases": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"model",
"reasons",
"showBufferingUi",
"threadId",
"turnId",
"useCases"
],
"type": "object"
},
"ModelVerification": {
"enum": [
"trustedAccessForCyber"
@@ -2612,8 +2655,9 @@
"type": "object"
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -6608,6 +6652,26 @@
"title": "Turn/moderationMetadataNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"model/safetyBuffering/updated"
],
"title": "Model/safetyBuffering/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ModelSafetyBufferingUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Model/safetyBuffering/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -1904,6 +1904,29 @@
"title": "Account/usage/readRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"account/workspaceMessages/read"
],
"title": "Account/workspaceMessages/readRequestMethod",
"type": "string"
},
"params": {
"type": "null"
}
},
"required": [
"id",
"method"
],
"title": "Account/workspaceMessages/readRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -5091,6 +5114,26 @@
"title": "Turn/moderationMetadataNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"model/safetyBuffering/updated"
],
"title": "Model/safetyBuffering/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ModelSafetyBufferingUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Model/safetyBuffering/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -5857,7 +5900,10 @@
{
"properties": {
"email": {
"type": "string"
"type": [
"string",
"null"
]
},
"planType": {
"$ref": "#/definitions/v2/PlanType"
@@ -10334,6 +10380,28 @@
"title": "GetAccountTokenUsageResponse",
"type": "object"
},
"GetWorkspaceMessagesResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"featureEnabled": {
"description": "Whether the workspace-message backend route is available for this client.",
"type": "boolean"
},
"messages": {
"description": "Active workspace messages returned by the backend.",
"items": {
"$ref": "#/definitions/v2/WorkspaceMessage"
},
"type": "array"
}
},
"required": [
"featureEnabled",
"messages"
],
"title": "GetWorkspaceMessagesResponse",
"type": "object"
},
"GitInfo": {
"properties": {
"branch": {
@@ -11080,6 +11148,18 @@
}
]
},
"InternalChatMessageMetadataPassthrough": {
"description": "Internal Responses API passthrough metadata copied into underlying chat messages.\n\nResponses API strongly types this payload. Do not modify it without first getting API approval and making the corresponding Responses API change.",
"properties": {
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"ItemCompletedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -12559,6 +12639,51 @@
"title": "ModelReroutedNotification",
"type": "object"
},
"ModelSafetyBufferingUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"fasterModel": {
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"reasons": {
"items": {
"type": "string"
},
"type": "array"
},
"showBufferingUi": {
"type": "boolean"
},
"threadId": {
"type": "string"
},
"turnId": {
"type": "string"
},
"useCases": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"model",
"reasons",
"showBufferingUi",
"threadId",
"turnId",
"useCases"
],
"title": "ModelSafetyBufferingUpdatedNotification",
"type": "object"
},
"ModelServiceTier": {
"properties": {
"description": {
@@ -12638,8 +12763,9 @@
"type": "object"
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -12929,6 +13055,10 @@
},
"PermissionProfileSummary": {
"properties": {
"allowed": {
"description": "Whether the effective requirements allow selecting this profile.",
"type": "boolean"
},
"description": {
"description": "Optional user-facing description for display in clients.",
"type": [
@@ -12942,6 +13072,7 @@
}
},
"required": [
"allowed",
"id"
],
"type": "object"
@@ -13286,6 +13417,17 @@
],
"description": "Local logo path, resolved from the installed plugin package."
},
"logoDark": {
"anyOf": [
{
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Local dark-mode logo path, resolved from the installed plugin package."
},
"logoUrl": {
"description": "Remote logo URL from the plugin catalog.",
"type": [
@@ -13293,6 +13435,13 @@
"null"
]
},
"logoUrlDark": {
"description": "Remote dark-mode logo URL from the plugin catalog.",
"type": [
"string",
"null"
]
},
"longDescription": {
"type": [
"string",
@@ -14847,10 +14996,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -14903,10 +15052,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -14957,10 +15106,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15007,10 +15156,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15050,10 +15199,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15104,10 +15253,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15147,10 +15296,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15190,10 +15339,10 @@
"input": {
"type": "string"
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15237,10 +15386,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15289,10 +15438,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15341,10 +15490,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15379,10 +15528,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15428,10 +15577,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15455,16 +15604,6 @@
},
{
"properties": {
"metadata": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
},
{
"type": "null"
}
]
},
"type": {
"enum": [
"compaction_trigger"
@@ -15493,10 +15632,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/v2/ResponseItemMetadata"
"$ref": "#/definitions/v2/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -15535,23 +15674,6 @@
}
]
},
"ResponseItemMetadata": {
"properties": {
"source_call_id": {
"type": [
"string",
"null"
]
},
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"ResponsesApiWebSearchAction": {
"oneOf": [
{
@@ -20508,6 +20630,49 @@
"title": "WindowsWorldWritableWarningNotification",
"type": "object"
},
"WorkspaceMessage": {
"properties": {
"archivedAt": {
"description": "Unix timestamp (in seconds) when the message was archived.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"createdAt": {
"description": "Unix timestamp (in seconds) when the message was created.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"messageBody": {
"type": "string"
},
"messageId": {
"type": "string"
},
"messageType": {
"$ref": "#/definitions/v2/WorkspaceMessageType"
}
},
"required": [
"messageBody",
"messageId",
"messageType"
],
"type": "object"
},
"WorkspaceMessageType": {
"enum": [
"headline",
"announcement",
"unknown"
],
"type": "string"
},
"WriteStatus": {
"enum": [
"ok",
@@ -26,7 +26,10 @@
{
"properties": {
"email": {
"type": "string"
"type": [
"string",
"null"
]
},
"planType": {
"$ref": "#/definitions/PlanType"
@@ -2913,6 +2916,29 @@
"title": "Account/usage/readRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"account/workspaceMessages/read"
],
"title": "Account/workspaceMessages/readRequestMethod",
"type": "string"
},
"params": {
"type": "null"
}
},
"required": [
"id",
"method"
],
"title": "Account/workspaceMessages/readRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -6705,6 +6731,28 @@
"title": "GetAccountTokenUsageResponse",
"type": "object"
},
"GetWorkspaceMessagesResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"featureEnabled": {
"description": "Whether the workspace-message backend route is available for this client.",
"type": "boolean"
},
"messages": {
"description": "Active workspace messages returned by the backend.",
"items": {
"$ref": "#/definitions/WorkspaceMessage"
},
"type": "array"
}
},
"required": [
"featureEnabled",
"messages"
],
"title": "GetWorkspaceMessagesResponse",
"type": "object"
},
"GitInfo": {
"properties": {
"branch": {
@@ -7504,6 +7552,18 @@
}
]
},
"InternalChatMessageMetadataPassthrough": {
"description": "Internal Responses API passthrough metadata copied into underlying chat messages.\n\nResponses API strongly types this payload. Do not modify it without first getting API approval and making the corresponding Responses API change.",
"properties": {
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"ItemCompletedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -8983,6 +9043,51 @@
"title": "ModelReroutedNotification",
"type": "object"
},
"ModelSafetyBufferingUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"fasterModel": {
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"reasons": {
"items": {
"type": "string"
},
"type": "array"
},
"showBufferingUi": {
"type": "boolean"
},
"threadId": {
"type": "string"
},
"turnId": {
"type": "string"
},
"useCases": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"model",
"reasons",
"showBufferingUi",
"threadId",
"turnId",
"useCases"
],
"title": "ModelSafetyBufferingUpdatedNotification",
"type": "object"
},
"ModelServiceTier": {
"properties": {
"description": {
@@ -9062,8 +9167,9 @@
"type": "object"
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -9353,6 +9459,10 @@
},
"PermissionProfileSummary": {
"properties": {
"allowed": {
"description": "Whether the effective requirements allow selecting this profile.",
"type": "boolean"
},
"description": {
"description": "Optional user-facing description for display in clients.",
"type": [
@@ -9366,6 +9476,7 @@
}
},
"required": [
"allowed",
"id"
],
"type": "object"
@@ -9710,6 +9821,17 @@
],
"description": "Local logo path, resolved from the installed plugin package."
},
"logoDark": {
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Local dark-mode logo path, resolved from the installed plugin package."
},
"logoUrl": {
"description": "Remote logo URL from the plugin catalog.",
"type": [
@@ -9717,6 +9839,13 @@
"null"
]
},
"logoUrlDark": {
"description": "Remote dark-mode logo URL from the plugin catalog.",
"type": [
"string",
"null"
]
},
"longDescription": {
"type": [
"string",
@@ -11271,10 +11400,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11327,10 +11456,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11381,10 +11510,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11431,10 +11560,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11474,10 +11603,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11528,10 +11657,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11571,10 +11700,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11614,10 +11743,10 @@
"input": {
"type": "string"
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11661,10 +11790,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11713,10 +11842,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11765,10 +11894,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11803,10 +11932,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11852,10 +11981,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11879,16 +12008,6 @@
},
{
"properties": {
"metadata": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
},
{
"type": "null"
}
]
},
"type": {
"enum": [
"compaction_trigger"
@@ -11917,10 +12036,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -11959,23 +12078,6 @@
}
]
},
"ResponseItemMetadata": {
"properties": {
"source_call_id": {
"type": [
"string",
"null"
]
},
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"ResponsesApiWebSearchAction": {
"oneOf": [
{
@@ -13412,6 +13514,26 @@
"title": "Turn/moderationMetadataNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"model/safetyBuffering/updated"
],
"title": "Model/safetyBuffering/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ModelSafetyBufferingUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Model/safetyBuffering/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -18287,6 +18409,49 @@
"title": "WindowsWorldWritableWarningNotification",
"type": "object"
},
"WorkspaceMessage": {
"properties": {
"archivedAt": {
"description": "Unix timestamp (in seconds) when the message was archived.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"createdAt": {
"description": "Unix timestamp (in seconds) when the message was created.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"messageBody": {
"type": "string"
},
"messageId": {
"type": "string"
},
"messageType": {
"$ref": "#/definitions/WorkspaceMessageType"
}
},
"required": [
"messageBody",
"messageId",
"messageType"
],
"type": "object"
},
"WorkspaceMessageType": {
"enum": [
"headline",
"announcement",
"unknown"
],
"type": "string"
},
"WriteStatus": {
"enum": [
"ok",
@@ -22,7 +22,10 @@
{
"properties": {
"email": {
"type": "string"
"type": [
"string",
"null"
]
},
"planType": {
"$ref": "#/definitions/PlanType"
@@ -0,0 +1,67 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"WorkspaceMessage": {
"properties": {
"archivedAt": {
"description": "Unix timestamp (in seconds) when the message was archived.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"createdAt": {
"description": "Unix timestamp (in seconds) when the message was created.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"messageBody": {
"type": "string"
},
"messageId": {
"type": "string"
},
"messageType": {
"$ref": "#/definitions/WorkspaceMessageType"
}
},
"required": [
"messageBody",
"messageId",
"messageType"
],
"type": "object"
},
"WorkspaceMessageType": {
"enum": [
"headline",
"announcement",
"unknown"
],
"type": "string"
}
},
"properties": {
"featureEnabled": {
"description": "Whether the workspace-message backend route is available for this client.",
"type": "boolean"
},
"messages": {
"description": "Active workspace messages returned by the backend.",
"items": {
"$ref": "#/definitions/WorkspaceMessage"
},
"type": "array"
}
},
"required": [
"featureEnabled",
"messages"
],
"title": "GetWorkspaceMessagesResponse",
"type": "object"
}
@@ -0,0 +1,45 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"fasterModel": {
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"reasons": {
"items": {
"type": "string"
},
"type": "array"
},
"showBufferingUi": {
"type": "boolean"
},
"threadId": {
"type": "string"
},
"turnId": {
"type": "string"
},
"useCases": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"model",
"reasons",
"showBufferingUi",
"threadId",
"turnId",
"useCases"
],
"title": "ModelSafetyBufferingUpdatedNotification",
"type": "object"
}
@@ -3,6 +3,10 @@
"definitions": {
"PermissionProfileSummary": {
"properties": {
"allowed": {
"description": "Whether the effective requirements allow selecting this profile.",
"type": "boolean"
},
"description": {
"description": "Optional user-facing description for display in clients.",
"type": [
@@ -16,6 +20,7 @@
}
},
"required": [
"allowed",
"id"
],
"type": "object"
@@ -134,6 +134,17 @@
],
"description": "Local logo path, resolved from the installed plugin package."
},
"logoDark": {
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Local dark-mode logo path, resolved from the installed plugin package."
},
"logoUrl": {
"description": "Remote logo URL from the plugin catalog.",
"type": [
@@ -141,6 +152,13 @@
"null"
]
},
"logoUrlDark": {
"description": "Remote dark-mode logo URL from the plugin catalog.",
"type": [
"string",
"null"
]
},
"longDescription": {
"type": [
"string",
@@ -134,6 +134,17 @@
],
"description": "Local logo path, resolved from the installed plugin package."
},
"logoDark": {
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Local dark-mode logo path, resolved from the installed plugin package."
},
"logoUrl": {
"description": "Remote logo URL from the plugin catalog.",
"type": [
@@ -141,6 +152,13 @@
"null"
]
},
"logoUrlDark": {
"description": "Remote dark-mode logo URL from the plugin catalog.",
"type": [
"string",
"null"
]
},
"longDescription": {
"type": [
"string",
@@ -313,6 +313,17 @@
],
"description": "Local logo path, resolved from the installed plugin package."
},
"logoDark": {
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Local dark-mode logo path, resolved from the installed plugin package."
},
"logoUrl": {
"description": "Remote logo URL from the plugin catalog.",
"type": [
@@ -320,6 +331,13 @@
"null"
]
},
"logoUrlDark": {
"description": "Remote dark-mode logo URL from the plugin catalog.",
"type": [
"string",
"null"
]
},
"longDescription": {
"type": [
"string",
@@ -108,6 +108,17 @@
],
"description": "Local logo path, resolved from the installed plugin package."
},
"logoDark": {
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "Local dark-mode logo path, resolved from the installed plugin package."
},
"logoUrl": {
"description": "Remote logo URL from the plugin catalog.",
"type": [
@@ -115,6 +126,13 @@
"null"
]
},
"logoUrlDark": {
"description": "Remote dark-mode logo URL from the plugin catalog.",
"type": [
"string",
"null"
]
},
"longDescription": {
"type": [
"string",
@@ -216,6 +216,18 @@
],
"type": "string"
},
"InternalChatMessageMetadataPassthrough": {
"description": "Internal Responses API passthrough metadata copied into underlying chat messages.\n\nResponses API strongly types this payload. Do not modify it without first getting API approval and making the corresponding Responses API change.",
"properties": {
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"LocalShellAction": {
"oneOf": [
{
@@ -383,10 +395,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -439,10 +451,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -493,10 +505,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -543,10 +555,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -586,10 +598,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -640,10 +652,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -683,10 +695,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -726,10 +738,10 @@
"input": {
"type": "string"
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -773,10 +785,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -825,10 +837,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -877,10 +889,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -915,10 +927,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -964,10 +976,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -991,16 +1003,6 @@
},
{
"properties": {
"metadata": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
},
{
"type": "null"
}
]
},
"type": {
"enum": [
"compaction_trigger"
@@ -1029,10 +1031,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -1071,23 +1073,6 @@
}
]
},
"ResponseItemMetadata": {
"properties": {
"source_call_id": {
"type": [
"string",
"null"
]
},
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"ResponsesApiWebSearchAction": {
"oneOf": [
{
@@ -663,8 +663,9 @@
]
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -279,6 +279,18 @@
],
"type": "string"
},
"InternalChatMessageMetadataPassthrough": {
"description": "Internal Responses API passthrough metadata copied into underlying chat messages.\n\nResponses API strongly types this payload. Do not modify it without first getting API approval and making the corresponding Responses API change.",
"properties": {
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"LocalShellAction": {
"oneOf": [
{
@@ -454,10 +466,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -510,10 +522,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -564,10 +576,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -614,10 +626,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -657,10 +669,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -711,10 +723,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -754,10 +766,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -797,10 +809,10 @@
"input": {
"type": "string"
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -844,10 +856,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -896,10 +908,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -948,10 +960,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -986,10 +998,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -1035,10 +1047,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -1062,16 +1074,6 @@
},
{
"properties": {
"metadata": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
},
{
"type": "null"
}
]
},
"type": {
"enum": [
"compaction_trigger"
@@ -1100,10 +1102,10 @@
"null"
]
},
"metadata": {
"internal_chat_message_metadata_passthrough": {
"anyOf": [
{
"$ref": "#/definitions/ResponseItemMetadata"
"$ref": "#/definitions/InternalChatMessageMetadataPassthrough"
},
{
"type": "null"
@@ -1142,23 +1144,6 @@
}
]
},
"ResponseItemMetadata": {
"properties": {
"source_call_id": {
"type": [
"string",
"null"
]
},
"turn_id": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"ResponsesApiWebSearchAction": {
"oneOf": [
{
@@ -663,8 +663,9 @@
]
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -109,8 +109,9 @@
"type": "string"
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -195,8 +195,9 @@
"type": "string"
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -663,8 +663,9 @@
]
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
@@ -142,8 +142,9 @@
"type": "string"
},
"MultiAgentMode": {
"description": "Controls whether the model should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help.",
"description": "Controls whether the model receives multi-agent delegation instructions and, when it does, whether it should only spawn sub-agents after an explicit user request or may delegate proactively when doing so would help. `none` leaves the multi-agent tools available without injecting delegation instructions.",
"enum": [
"none",
"explicitRequestOnly",
"proactive"
],
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Internal Responses API passthrough metadata copied into underlying chat messages.
*
* Responses API strongly types this payload. Do not modify it without first getting API
* approval and making the corresponding Responses API change.
*/
export type InternalChatMessageMetadataPassthrough = { turn_id?: string, };
@@ -11,10 +11,10 @@
*
* When converting from [`PathUri`], "native" refers to the supplied
* [`PathConvention`], which may be foreign to the operating system running
* this process. The inner string is private so path-producing code must convert
* from [`AbsolutePathBuf`] or use [`Self::from_path_uri`] instead of bypassing
* the intended conversion boundary. Non-UTF-8 paths are converted to UTF-8
* lossily because this API value is serialized as a JSON string.
* this process. The inner string is private so path-producing code must use a
* path conversion method instead of bypassing the intended conversion
* boundary. Non-UTF-8 paths are converted to UTF-8 lossily because this API
* value is serialized as a JSON string.
*
* Deserialization accepts any UTF-8 string without interpreting or validating
* it. That unrestricted construction path is intentionally available only to
@@ -3,7 +3,9 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Controls whether the model should only spawn sub-agents after an explicit
* user request or may delegate proactively when doing so would help.
* Controls whether the model receives multi-agent delegation instructions and,
* when it does, whether it should only spawn sub-agents after an explicit user
* request or may delegate proactively when doing so would help. `none` leaves
* the multi-agent tools available without injecting delegation instructions.
*/
export type MultiAgentMode = "explicitRequestOnly" | "proactive";
export type MultiAgentMode = "none" | "explicitRequestOnly" | "proactive";
@@ -4,15 +4,15 @@
import type { AgentMessageInputContent } from "./AgentMessageInputContent";
import type { ContentItem } from "./ContentItem";
import type { FunctionCallOutputBody } from "./FunctionCallOutputBody";
import type { InternalChatMessageMetadataPassthrough } from "./InternalChatMessageMetadataPassthrough";
import type { LocalShellAction } from "./LocalShellAction";
import type { LocalShellStatus } from "./LocalShellStatus";
import type { MessagePhase } from "./MessagePhase";
import type { ReasoningItemContent } from "./ReasoningItemContent";
import type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSummary";
import type { ResponseItemMetadata } from "./ResponseItemMetadata";
import type { WebSearchAction } from "./WebSearchAction";
export type ResponseItem = { "type": "message", id?: string, role: string, content: Array<ContentItem>, phase?: MessagePhase, metadata?: ResponseItemMetadata, } | { "type": "agent_message", id?: string, author: string, recipient: string, content: Array<AgentMessageInputContent>, metadata?: ResponseItemMetadata, } | { "type": "reasoning", id?: string, summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, metadata?: ResponseItemMetadata, } | { "type": "local_shell_call",
export type ResponseItem = { "type": "message", id?: string, role: string, content: Array<ContentItem>, phase?: MessagePhase, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "agent_message", id?: string, author: string, recipient: string, content: Array<AgentMessageInputContent>, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "reasoning", id?: string, summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "local_shell_call",
/**
* Legacy id field retained for compatibility with older payloads.
*/
@@ -20,4 +20,4 @@ id?: string,
/**
* Set when using the Responses API.
*/
call_id: string | null, status: LocalShellStatus, action: LocalShellAction, metadata?: ResponseItemMetadata, } | { "type": "function_call", id?: string, name: string, namespace?: string, arguments: string, call_id: string, metadata?: ResponseItemMetadata, } | { "type": "tool_search_call", id?: string, call_id: string | null, status?: string, execution: string, arguments: unknown, metadata?: ResponseItemMetadata, } | { "type": "function_call_output", id?: string, call_id: string, output: FunctionCallOutputBody, metadata?: ResponseItemMetadata, } | { "type": "custom_tool_call", id?: string, status?: string, call_id: string, name: string, input: string, metadata?: ResponseItemMetadata, } | { "type": "custom_tool_call_output", id?: string, call_id: string, name?: string, output: FunctionCallOutputBody, metadata?: ResponseItemMetadata, } | { "type": "tool_search_output", id?: string, call_id: string | null, status: string, execution: string, tools: unknown[], metadata?: ResponseItemMetadata, } | { "type": "web_search_call", id?: string, status?: string, action?: WebSearchAction, metadata?: ResponseItemMetadata, } | { "type": "image_generation_call", id?: string, status: string, revised_prompt?: string, result: string, metadata?: ResponseItemMetadata, } | { "type": "compaction", id?: string, encrypted_content: string, metadata?: ResponseItemMetadata, } | { "type": "compaction_trigger", metadata?: ResponseItemMetadata, } | { "type": "context_compaction", id?: string, encrypted_content?: string, metadata?: ResponseItemMetadata, } | { "type": "other" };
call_id: string | null, status: LocalShellStatus, action: LocalShellAction, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "function_call", id?: string, name: string, namespace?: string, arguments: string, call_id: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "tool_search_call", id?: string, call_id: string | null, status?: string, execution: string, arguments: unknown, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "function_call_output", id?: string, call_id: string, output: FunctionCallOutputBody, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "custom_tool_call", id?: string, status?: string, call_id: string, name: string, input: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "custom_tool_call_output", id?: string, call_id: string, name?: string, output: FunctionCallOutputBody, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "tool_search_output", id?: string, call_id: string | null, status: string, execution: string, tools: unknown[], internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "web_search_call", id?: string, status?: string, action?: WebSearchAction, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "image_generation_call", id?: string, status: string, revised_prompt?: string, result: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "compaction", id?: string, encrypted_content: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "compaction_trigger", } | { "type": "context_compaction", id?: string, encrypted_content?: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "other" };
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -41,6 +41,7 @@ export type { InitializeCapabilities } from "./InitializeCapabilities";
export type { InitializeParams } from "./InitializeParams";
export type { InitializeResponse } from "./InitializeResponse";
export type { InputModality } from "./InputModality";
export type { InternalChatMessageMetadataPassthrough } from "./InternalChatMessageMetadataPassthrough";
export type { InternalSessionSource } from "./InternalSessionSource";
export type { LegacyAppPathString } from "./LegacyAppPathString";
export type { LocalShellAction } from "./LocalShellAction";
@@ -68,7 +69,6 @@ export type { Resource } from "./Resource";
export type { ResourceContent } from "./ResourceContent";
export type { ResourceTemplate } from "./ResourceTemplate";
export type { ResponseItem } from "./ResponseItem";
export type { ResponseItemMetadata } from "./ResponseItemMetadata";
export type { ReviewDecision } from "./ReviewDecision";
export type { ServerNotification } from "./ServerNotification";
export type { ServerRequest } from "./ServerRequest";
@@ -4,4 +4,4 @@
import type { AmazonBedrockCredentialSource } from "../AmazonBedrockCredentialSource";
import type { PlanType } from "../PlanType";
export type Account = { "type": "apiKey", } | { "type": "chatgpt", email: string, planType: PlanType, } | { "type": "amazonBedrock", credentialSource: AmazonBedrockCredentialSource, };
export type Account = { "type": "apiKey", } | { "type": "chatgpt", email: string | null, planType: PlanType, } | { "type": "amazonBedrock", credentialSource: AmazonBedrockCredentialSource, };
@@ -0,0 +1,14 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { WorkspaceMessage } from "./WorkspaceMessage";
export type GetWorkspaceMessagesResponse = {
/**
* Whether the workspace-message backend route is available for this client.
*/
featureEnabled: boolean,
/**
* Active workspace messages returned by the backend.
*/
messages: Array<WorkspaceMessage>, };
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ModelSafetyBufferingUpdatedNotification = { threadId: string, turnId: string, model: string, useCases: Array<string>, reasons: Array<string>, showBufferingUi: boolean, fasterModel: string | null, };
@@ -10,4 +10,8 @@ id: string,
/**
* Optional user-facing description for display in clients.
*/
description: string | null, };
description: string | null,
/**
* Whether the effective requirements allow selecting this profile.
*/
allowed: boolean, };
@@ -21,10 +21,18 @@ composerIconUrl: string | null,
* Local logo path, resolved from the installed plugin package.
*/
logo: AbsolutePathBuf | null,
/**
* Local dark-mode logo path, resolved from the installed plugin package.
*/
logoDark: AbsolutePathBuf | null,
/**
* Remote logo URL from the plugin catalog.
*/
logoUrl: string | null,
/**
* Remote dark-mode logo URL from the plugin catalog.
*/
logoUrlDark: string | null,
/**
* Local screenshot paths, resolved from the installed plugin package.
*/
@@ -0,0 +1,14 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { WorkspaceMessageType } from "./WorkspaceMessageType";
export type WorkspaceMessage = { messageId: string, messageType: WorkspaceMessageType, messageBody: string,
/**
* Unix timestamp (in seconds) when the message was created.
*/
createdAt: number | null,
/**
* Unix timestamp (in seconds) when the message was archived.
*/
archivedAt: number | null, };
@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ResponseItemMetadata = { turn_id?: string, source_call_id?: string, };
export type WorkspaceMessageType = "headline" | "announcement" | "unknown";
@@ -157,6 +157,7 @@ export type { GetAccountParams } from "./GetAccountParams";
export type { GetAccountRateLimitsResponse } from "./GetAccountRateLimitsResponse";
export type { GetAccountResponse } from "./GetAccountResponse";
export type { GetAccountTokenUsageResponse } from "./GetAccountTokenUsageResponse";
export type { GetWorkspaceMessagesResponse } from "./GetWorkspaceMessagesResponse";
export type { GitInfo } from "./GitInfo";
export type { GrantedPermissionProfile } from "./GrantedPermissionProfile";
export type { GuardianApprovalReview } from "./GuardianApprovalReview";
@@ -260,6 +261,7 @@ export type { ModelProviderCapabilitiesReadParams } from "./ModelProviderCapabil
export type { ModelProviderCapabilitiesReadResponse } from "./ModelProviderCapabilitiesReadResponse";
export type { ModelRerouteReason } from "./ModelRerouteReason";
export type { ModelReroutedNotification } from "./ModelReroutedNotification";
export type { ModelSafetyBufferingUpdatedNotification } from "./ModelSafetyBufferingUpdatedNotification";
export type { ModelServiceTier } from "./ModelServiceTier";
export type { ModelUpgradeInfo } from "./ModelUpgradeInfo";
export type { ModelVerification } from "./ModelVerification";
@@ -493,4 +495,6 @@ export type { WindowsSandboxSetupMode } from "./WindowsSandboxSetupMode";
export type { WindowsSandboxSetupStartParams } from "./WindowsSandboxSetupStartParams";
export type { WindowsSandboxSetupStartResponse } from "./WindowsSandboxSetupStartResponse";
export type { WindowsWorldWritableWarningNotification } from "./WindowsWorldWritableWarningNotification";
export type { WorkspaceMessage } from "./WorkspaceMessage";
export type { WorkspaceMessageType } from "./WorkspaceMessageType";
export type { WriteStatus } from "./WriteStatus";
@@ -1022,6 +1022,12 @@ client_request_definitions! {
response: v2::GetAccountTokenUsageResponse,
},
GetWorkspaceMessages => "account/workspaceMessages/read" {
params: #[ts(type = "undefined")] #[serde(skip_serializing_if = "Option::is_none")] Option<()>,
serialization: None,
response: v2::GetWorkspaceMessagesResponse,
},
SendAddCreditsNudgeEmail => "account/sendAddCreditsNudgeEmail" {
params: v2::SendAddCreditsNudgeEmailParams,
serialization: global("account-auth"),
@@ -1655,6 +1661,7 @@ server_notification_definitions! {
ModelVerification => "model/verification" (v2::ModelVerificationNotification),
#[experimental("turn/moderationMetadata")]
TurnModerationMetadata => "turn/moderationMetadata" (v2::TurnModerationMetadataNotification),
ModelSafetyBufferingUpdated => "model/safetyBuffering/updated" (v2::ModelSafetyBufferingUpdatedNotification),
Warning => "warning" (v2::WarningNotification),
GuardianWarning => "guardianWarning" (v2::GuardianWarningNotification),
DeprecationNotice => "deprecationNotice" (v2::DeprecationNoticeNotification),
@@ -1700,6 +1707,7 @@ mod tests {
use codex_protocol::ThreadId;
use codex_protocol::account::AmazonBedrockCredentialSource;
use codex_protocol::account::PlanType;
use codex_protocol::config_types::MultiAgentMode;
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_READ_ONLY;
use codex_protocol::parse_command::ParsedCommand;
use codex_protocol::protocol::RealtimeConversationVersion;
@@ -2539,6 +2547,24 @@ mod tests {
Ok(())
}
#[test]
fn serialize_get_workspace_messages() -> Result<()> {
let request = ClientRequest::GetWorkspaceMessages {
request_id: RequestId::Integer(1),
params: None,
};
assert_eq!(request.id(), &RequestId::Integer(1));
assert_eq!(request.method(), "account/workspaceMessages/read");
assert_eq!(
json!({
"method": "account/workspaceMessages/read",
"id": 1,
}),
serde_json::to_value(&request)?,
);
Ok(())
}
#[test]
fn serialize_client_response() -> Result<()> {
let cwd = absolute_path("/tmp");
@@ -2583,7 +2609,7 @@ mod tests {
sandbox: v2::SandboxPolicy::DangerFullAccess,
active_permission_profile: None,
reasoning_effort: None,
multi_agent_mode: None,
multi_agent_mode: MultiAgentMode::ExplicitRequestOnly,
},
};
@@ -2632,7 +2658,7 @@ mod tests {
},
"activePermissionProfile": null,
"reasoningEffort": null,
"multiAgentMode": null
"multiAgentMode": "explicitRequestOnly"
}
}),
serde_json::to_value(&response)?,
@@ -2828,7 +2854,7 @@ mod tests {
);
let chatgpt = v2::Account::Chatgpt {
email: "user@example.com".to_string(),
email: Some("user@example.com".to_string()),
plan_type: PlanType::Plus,
};
assert_eq!(
@@ -2840,6 +2866,19 @@ mod tests {
serde_json::to_value(&chatgpt)?,
);
let chatgpt_without_email = v2::Account::Chatgpt {
email: None,
plan_type: PlanType::Pro,
};
assert_eq!(
json!({
"type": "chatgpt",
"email": null,
"planType": "pro",
}),
serde_json::to_value(&chatgpt_without_email)?,
);
let codex_managed_bedrock = v2::Account::AmazonBedrock {
credential_source: AmazonBedrockCredentialSource::CodexManaged,
};
@@ -3347,6 +3386,37 @@ mod tests {
Ok(())
}
#[test]
fn serialize_model_safety_buffering_updated_notification() -> Result<()> {
let notification = ServerNotification::ModelSafetyBufferingUpdated(
v2::ModelSafetyBufferingUpdatedNotification {
thread_id: "thr_123".to_string(),
turn_id: "turn_123".to_string(),
model: "current-model".to_string(),
use_cases: vec!["cyber".to_string()],
reasons: vec!["user_risk".to_string()],
show_buffering_ui: true,
faster_model: Some("faster-model".to_string()),
},
);
assert_eq!(
json!({
"method": "model/safetyBuffering/updated",
"params": {
"threadId": "thr_123",
"turnId": "turn_123",
"model": "current-model",
"useCases": ["cyber"],
"reasons": ["user_risk"],
"showBufferingUi": true,
"fasterModel": "faster-model"
}
}),
serde_json::to_value(&notification)?,
);
Ok(())
}
#[test]
fn serialize_thread_realtime_output_audio_delta_notification() -> Result<()> {
let notification = ServerNotification::ThreadRealtimeOutputAudioDelta(
@@ -3349,6 +3349,8 @@ mod tests {
message: String::new(),
replacement_history: None,
window_number: None,
first_window_id: None,
previous_window_id: None,
window_id: None,
}),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
@@ -3781,7 +3783,7 @@ mod tests {
text: "plain text".into(),
}],
phase: None,
metadata: None,
internal_chat_message_metadata_passthrough: None,
}),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: "turn-a".into(),
@@ -25,7 +25,11 @@ pub enum Account {
#[serde(rename = "chatgpt", rename_all = "camelCase")]
#[ts(rename = "chatgpt", rename_all = "camelCase")]
Chatgpt { email: String, plan_type: PlanType },
Chatgpt {
#[schemars(required, schema_with = "nullable_string_schema")]
email: Option<String>,
plan_type: PlanType,
},
#[serde(rename = "amazonBedrock", rename_all = "camelCase")]
#[ts(rename = "amazonBedrock", rename_all = "camelCase")]
@@ -35,6 +39,12 @@ pub enum Account {
},
}
fn nullable_string_schema(
generator: &mut schemars::r#gen::SchemaGenerator,
) -> schemars::schema::Schema {
generator.subschema_for::<Option<String>>()
}
fn default_bedrock_credential_source() -> AmazonBedrockCredentialSource {
AmazonBedrockCredentialSource::AwsManaged
}
@@ -314,6 +324,41 @@ pub struct GetAccountTokenUsageResponse {
pub daily_usage_buckets: Option<Vec<AccountTokenUsageDailyBucket>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct GetWorkspaceMessagesResponse {
/// Whether the workspace-message backend route is available for this client.
pub feature_enabled: bool,
/// Active workspace messages returned by the backend.
pub messages: Vec<WorkspaceMessage>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct WorkspaceMessage {
pub message_id: String,
pub message_type: WorkspaceMessageType,
pub message_body: String,
/// Unix timestamp (in seconds) when the message was created.
#[ts(type = "number | null")]
pub created_at: Option<i64>,
/// Unix timestamp (in seconds) when the message was archived.
#[ts(type = "number | null")]
pub archived_at: Option<i64>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/", rename_all = "snake_case")]
pub enum WorkspaceMessageType {
Headline,
Announcement,
#[serde(other)]
Unknown,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
@@ -162,3 +162,16 @@ pub struct TurnModerationMetadataNotification {
pub turn_id: String,
pub metadata: JsonValue,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ModelSafetyBufferingUpdatedNotification {
pub thread_id: String,
pub turn_id: String,
pub model: String,
pub use_cases: Vec<String>,
pub reasons: Vec<String>,
pub show_buffering_ui: bool,
pub faster_model: Option<String>,
}
@@ -378,6 +378,8 @@ pub struct PermissionProfileSummary {
pub id: String,
/// Optional user-facing description for display in clients.
pub description: Option<String>,
/// Whether the effective requirements allow selecting this profile.
pub allowed: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
@@ -716,8 +716,12 @@ pub struct PluginInterface {
pub composer_icon_url: Option<String>,
/// Local logo path, resolved from the installed plugin package.
pub logo: Option<AbsolutePathBuf>,
/// Local dark-mode logo path, resolved from the installed plugin package.
pub logo_dark: Option<AbsolutePathBuf>,
/// Remote logo URL from the plugin catalog.
pub logo_url: Option<String>,
/// Remote dark-mode logo URL from the plugin catalog.
pub logo_url_dark: Option<String>,
/// Local screenshot paths, resolved from the installed plugin package.
pub screenshots: Vec<AbsolutePathBuf>,
/// Remote screenshot URLs from the plugin catalog.
@@ -1,5 +1,6 @@
use super::*;
use codex_protocol::approvals::ElicitationRequest as CoreElicitationRequest;
use codex_protocol::config_types::MultiAgentMode;
use codex_protocol::items::AgentMessageContent;
use codex_protocol::items::AgentMessageItem;
use codex_protocol::items::FileChangeItem;
@@ -2937,6 +2938,13 @@ fn plugin_interface_serializes_local_paths_and_remote_urls_separately() {
};
let composer_icon = AbsolutePathBuf::try_from(PathBuf::from(composer_icon)).unwrap();
let composer_icon_json = composer_icon.as_path().display().to_string();
let logo_dark = if cfg!(windows) {
r"C:\plugins\linear\logo-dark.png"
} else {
"/plugins/linear/logo-dark.png"
};
let logo_dark = AbsolutePathBuf::try_from(PathBuf::from(logo_dark)).unwrap();
let logo_dark_json = logo_dark.as_path().display().to_string();
let interface = PluginInterface {
display_name: Some("Linear".to_string()),
@@ -2953,7 +2961,9 @@ fn plugin_interface_serializes_local_paths_and_remote_urls_separately() {
composer_icon: Some(composer_icon),
composer_icon_url: Some("https://example.com/linear/icon.png".to_string()),
logo: None,
logo_dark: Some(logo_dark),
logo_url: Some("https://example.com/linear/logo.png".to_string()),
logo_url_dark: Some("https://example.com/linear/logo-dark.png".to_string()),
screenshots: Vec::new(),
screenshot_urls: vec!["https://example.com/linear/screenshot.png".to_string()],
};
@@ -2975,7 +2985,9 @@ fn plugin_interface_serializes_local_paths_and_remote_urls_separately() {
"composerIcon": composer_icon_json,
"composerIconUrl": "https://example.com/linear/icon.png",
"logo": null,
"logoDark": logo_dark_json,
"logoUrl": "https://example.com/linear/logo.png",
"logoUrlDark": "https://example.com/linear/logo-dark.png",
"screenshots": [],
"screenshotUrls": ["https://example.com/linear/screenshot.png"],
}),
@@ -3708,7 +3720,11 @@ fn thread_lifecycle_responses_default_missing_optional_fields() {
resume.multi_agent_mode,
fork.multi_agent_mode,
),
(None, None, None)
(
MultiAgentMode::ExplicitRequestOnly,
MultiAgentMode::ExplicitRequestOnly,
MultiAgentMode::ExplicitRequestOnly,
)
);
let foreign_source: LegacyAppPathString =
@@ -94,9 +94,9 @@ pub struct ThreadStartParams {
pub developer_instructions: Option<String>,
#[ts(optional = nullable)]
pub personality: Option<Personality>,
/// Set the initial multi-agent mode for this thread.
/// Omitted leaves the thread without a selected mode. Eligible multi-agent
/// v2 turns still default to `explicitRequestOnly`.
/// Set the initial multi-agent mode for this thread. `none` leaves the
/// multi-agent tools available without injecting mode instructions.
/// Omitted defaults to `explicitRequestOnly`.
#[experimental("thread/start.multiAgentMode")]
#[ts(optional = nullable)]
pub multi_agent_mode: Option<MultiAgentMode>,
@@ -186,10 +186,10 @@ pub struct ThreadStartResponse {
#[serde(default)]
pub active_permission_profile: Option<ActivePermissionProfile>,
pub reasoning_effort: Option<ReasoningEffort>,
/// Current selected multi-agent mode for this thread, if one was selected.
/// Current multi-agent mode for this thread.
#[experimental("thread/start.multiAgentMode")]
#[serde(default)]
pub multi_agent_mode: Option<MultiAgentMode>,
pub multi_agent_mode: MultiAgentMode,
}
impl ThreadStartResponse {
@@ -279,10 +279,10 @@ pub struct ThreadSettings {
pub effort: Option<ReasoningEffort>,
pub summary: Option<ReasoningSummary>,
pub collaboration_mode: CollaborationMode,
/// Current selected multi-agent mode for this thread, if one was selected.
/// Current multi-agent mode for this thread.
#[experimental("thread/settings.multiAgentMode")]
#[serde(default)]
pub multi_agent_mode: Option<MultiAgentMode>,
pub multi_agent_mode: MultiAgentMode,
pub personality: Option<Personality>,
}
@@ -419,10 +419,10 @@ pub struct ThreadResumeResponse {
#[serde(default)]
pub active_permission_profile: Option<ActivePermissionProfile>,
pub reasoning_effort: Option<ReasoningEffort>,
/// Current selected multi-agent mode for this thread, if one was selected.
/// Current multi-agent mode for this thread.
#[experimental("thread/resume.multiAgentMode")]
#[serde(default)]
pub multi_agent_mode: Option<MultiAgentMode>,
pub multi_agent_mode: MultiAgentMode,
/// `thread/turns/list` page returned when requested by `initialTurnsPage`.
#[experimental("thread/resume.initialTurnsPage")]
#[serde(default)]
@@ -578,10 +578,10 @@ pub struct ThreadForkResponse {
#[serde(default)]
pub active_permission_profile: Option<ActivePermissionProfile>,
pub reasoning_effort: Option<ReasoningEffort>,
/// Current selected multi-agent mode for this thread, if one was selected.
/// Current multi-agent mode for this thread.
#[experimental("thread/fork.multiAgentMode")]
#[serde(default)]
pub multi_agent_mode: Option<MultiAgentMode>,
pub multi_agent_mode: MultiAgentMode,
}
impl ThreadForkResponse {
@@ -151,8 +151,9 @@ pub struct TurnStartParams {
#[ts(optional = nullable)]
pub collaboration_mode: Option<CollaborationMode>,
/// Controls whether multi-agent v2 delegation requires an explicit user request.
/// Omitted keeps the loaded session's current mode.
/// Controls multi-agent v2 delegation instructions. `none` leaves the
/// multi-agent tools available without injecting mode instructions. Omitted
/// keeps the loaded session's current mode.
#[experimental("turn/start.multiAgentMode")]
#[ts(optional = nullable)]
pub multi_agent_mode: Option<MultiAgentMode>,
@@ -26,3 +26,6 @@ uuid = { workspace = true, features = ["v4"] }
[lib]
doctest = false
[dev-dependencies]
pretty_assertions = { workspace = true }
@@ -18,6 +18,10 @@ cargo run -p codex-app-server-test-client -- \
cargo run -p codex-app-server-test-client -- model-list
```
`send-message` and `send-message-v2` handle `request_user_input` server requests interactively.
When Codex asks a question, choose a numbered option (or `o` for a free-form answer when offered)
and the client will send the response and continue streaming the same turn.
## Testing Plugin Analytics
The `plugin-analytics-smoke` command exercises `plugin/installed`, plugin
@@ -91,6 +91,7 @@ mod loopback_responses_server;
mod plugin_analytics_capture;
mod plugin_analytics_mutation_smoke;
mod plugin_analytics_smoke;
mod request_user_input;
const NOTIFICATIONS_TO_OPT_OUT: &[&str] = &[
// v2 item deltas.
@@ -2040,6 +2041,10 @@ impl CodexClient {
ServerRequest::FileChangeRequestApproval { request_id, params } => {
self.approve_file_change_request(request_id, params)?;
}
ServerRequest::ToolRequestUserInput { request_id, params } => {
let response = request_user_input::prompt_for_answers(&params)?;
self.send_server_request_response(request_id, &response)?;
}
other => {
bail!("received unsupported server request: {other:?}");
}
@@ -0,0 +1,148 @@
use std::collections::HashMap;
use std::io;
use std::io::BufRead;
use std::io::IsTerminal;
use std::io::Write;
use anyhow::Context;
use anyhow::Result;
use anyhow::bail;
use codex_app_server_protocol::ToolRequestUserInputAnswer;
use codex_app_server_protocol::ToolRequestUserInputParams;
use codex_app_server_protocol::ToolRequestUserInputResponse;
pub(super) fn prompt_for_answers(
params: &ToolRequestUserInputParams,
) -> Result<ToolRequestUserInputResponse> {
let stdin = io::stdin();
if !stdin.is_terminal() {
bail!("request_user_input requires an interactive stdin terminal");
}
let stdout = io::stdout();
prompt_for_answers_with(&mut stdin.lock(), &mut stdout.lock(), params)
}
fn prompt_for_answers_with(
input: &mut impl BufRead,
output: &mut impl Write,
params: &ToolRequestUserInputParams,
) -> Result<ToolRequestUserInputResponse> {
writeln!(
output,
"\n[request_user_input for thread {}, turn {}]",
params.thread_id, params.turn_id
)?;
if let Some(auto_resolution_ms) = params.auto_resolution_ms {
writeln!(
output,
"The app-server may auto-resolve this request after {auto_resolution_ms} ms."
)?;
}
let mut answers = HashMap::new();
for question in &params.questions {
writeln!(output, "\n{}: {}", question.header, question.question)?;
let options = question
.options
.as_deref()
.filter(|options| !options.is_empty());
let answer_values = if let Some(options) = options {
for (index, option) in options.iter().enumerate() {
writeln!(
output,
" {}. {} - {}",
index + 1,
option.label,
option.description
)?;
}
if question.is_other {
writeln!(output, " o. Other (free-form)")?;
}
loop {
if question.is_other {
write!(output, "Choose 1-{} or o: ", options.len())?;
} else {
write!(output, "Choose 1-{}: ", options.len())?;
}
output.flush()?;
let mut line = String::new();
if input
.read_line(&mut line)
.context("failed to read request_user_input selection")?
== 0
{
bail!("stdin closed while waiting for request_user_input selection");
}
let selection = line.trim();
if let Ok(index) = selection.parse::<usize>()
&& let Some(option) = index.checked_sub(1).and_then(|index| options.get(index))
{
break vec![option.label.clone()];
}
if let Some(option) = options
.iter()
.find(|option| option.label.eq_ignore_ascii_case(selection))
{
break vec![option.label.clone()];
}
if question.is_other && selection.eq_ignore_ascii_case("o") {
write!(output, "Other: ")?;
output.flush()?;
line.clear();
if input
.read_line(&mut line)
.context("failed to read request_user_input free-form answer")?
== 0
{
bail!("stdin closed while waiting for request_user_input free-form answer");
}
let answer = line.trim();
if !answer.is_empty() {
break vec![format!("user_note: {answer}")];
}
}
writeln!(output, "Invalid selection; try again.")?;
}
} else {
loop {
write!(output, "Answer: ")?;
output.flush()?;
let mut line = String::new();
if input
.read_line(&mut line)
.context("failed to read request_user_input answer")?
== 0
{
bail!("stdin closed while waiting for request_user_input answer");
}
let answer = line.trim();
if !answer.is_empty() {
break vec![format!("user_note: {answer}")];
}
writeln!(output, "Answer cannot be empty; try again.")?;
}
};
answers.insert(
question.id.clone(),
ToolRequestUserInputAnswer {
answers: answer_values,
},
);
}
Ok(ToolRequestUserInputResponse { answers })
}
#[cfg(test)]
#[path = "request_user_input_tests.rs"]
mod tests;
@@ -0,0 +1,125 @@
use std::collections::HashMap;
use std::io::Cursor;
use codex_app_server_protocol::ToolRequestUserInputAnswer;
use codex_app_server_protocol::ToolRequestUserInputOption;
use codex_app_server_protocol::ToolRequestUserInputParams;
use codex_app_server_protocol::ToolRequestUserInputQuestion;
use codex_app_server_protocol::ToolRequestUserInputResponse;
use pretty_assertions::assert_eq;
use super::prompt_for_answers_with;
#[test]
fn collects_option_and_free_form_answers() {
let params = ToolRequestUserInputParams {
thread_id: "thread-1".to_string(),
turn_id: "turn-1".to_string(),
item_id: "item-1".to_string(),
questions: vec![
ToolRequestUserInputQuestion {
id: "target".to_string(),
header: "Target".to_string(),
question: "Which target?".to_string(),
is_other: true,
is_secret: false,
options: Some(vec![
ToolRequestUserInputOption {
label: "Core".to_string(),
description: "Inspect core".to_string(),
},
ToolRequestUserInputOption {
label: "TUI".to_string(),
description: "Inspect TUI".to_string(),
},
]),
},
ToolRequestUserInputQuestion {
id: "details".to_string(),
header: "Details".to_string(),
question: "Anything else?".to_string(),
is_other: true,
is_secret: false,
options: None,
},
],
auto_resolution_ms: Some(60_000),
};
let mut input = Cursor::new(b"2\ninclude snapshots\n");
let mut output = Vec::new();
let response = prompt_for_answers_with(&mut input, &mut output, &params).unwrap();
assert_eq!(
response,
ToolRequestUserInputResponse {
answers: HashMap::from([
(
"target".to_string(),
ToolRequestUserInputAnswer {
answers: vec!["TUI".to_string()],
},
),
(
"details".to_string(),
ToolRequestUserInputAnswer {
answers: vec!["user_note: include snapshots".to_string()],
},
),
]),
}
);
assert_eq!(
String::from_utf8(output).unwrap(),
concat!(
"\n[request_user_input for thread thread-1, turn turn-1]\n",
"The app-server may auto-resolve this request after 60000 ms.\n",
"\nTarget: Which target?\n",
" 1. Core - Inspect core\n",
" 2. TUI - Inspect TUI\n",
" o. Other (free-form)\n",
"Choose 1-2 or o: ",
"\nDetails: Anything else?\n",
"Answer: ",
)
);
}
#[test]
fn retries_invalid_selection_and_collects_other_answer() {
let params = ToolRequestUserInputParams {
thread_id: "thread-1".to_string(),
turn_id: "turn-1".to_string(),
item_id: "item-1".to_string(),
questions: vec![ToolRequestUserInputQuestion {
id: "target".to_string(),
header: "Target".to_string(),
question: "Which target?".to_string(),
is_other: true,
is_secret: false,
options: Some(vec![ToolRequestUserInputOption {
label: "Core".to_string(),
description: "Inspect core".to_string(),
}]),
}],
auto_resolution_ms: None,
};
let mut input = Cursor::new(b"9\no\nSDK wrapper\n");
let mut output = Vec::new();
let response = prompt_for_answers_with(&mut input, &mut output, &params).unwrap();
assert_eq!(
response,
ToolRequestUserInputResponse {
answers: HashMap::from([(
"target".to_string(),
ToolRequestUserInputAnswer {
answers: vec!["user_note: SDK wrapper".to_string()],
},
)]),
}
);
let output = String::from_utf8(output).unwrap();
assert!(output.contains("Invalid selection; try again."));
}
+1
View File
@@ -57,3 +57,4 @@ chrono = { workspace = true }
codex-config = { workspace = true }
pretty_assertions = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true, features = ["test-util"] }
@@ -692,14 +692,14 @@ mod tests {
}
}
#[tokio::test]
#[tokio::test(start_paused = true)]
async fn initialize_timeout_closes_open_connection() {
let (server_event_tx, _server_event_rx) = mpsc::channel(CHANNEL_CAPACITY);
let (transport_event_tx, mut transport_event_rx) = mpsc::channel(1);
let shutdown_token = CancellationToken::new();
let client_tracker =
ClientTracker::new(server_event_tx, transport_event_tx, &shutdown_token);
let mut handle_message = tokio::spawn(async move {
let handle_message = tokio::spawn(async move {
let mut client_tracker = client_tracker;
client_tracker
.handle_message(initialize_envelope_with_stream_id(
@@ -709,13 +709,13 @@ mod tests {
.await
});
assert!(
timeout(Duration::from_millis(50), &mut handle_message)
.await
.expect("initialize timeout rollback should not wait for close delivery")
.expect("handle message task should not panic")
.is_err()
);
tokio::task::yield_now().await;
tokio::time::advance(
REMOTE_CONTROL_TRANSPORT_EVENT_SEND_TIMEOUT + Duration::from_millis(1),
)
.await;
assert!(handle_message.await.expect("handle message task").is_err());
let connection_id = match transport_event_rx.recv().await.expect("open event") {
TransportEvent::ConnectionOpened { connection_id, .. } => connection_id,
other => panic!("expected connection opened, got {other:?}"),
@@ -1041,6 +1041,7 @@ async fn remote_control_start_allows_missing_auth_when_enabled() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let (transport_event_tx, _transport_event_rx) =
@@ -1870,6 +1871,7 @@ async fn remote_control_waits_for_account_id_before_enrolling() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let expected_server_name = gethostname().to_string_lossy().trim().to_string();
@@ -1966,6 +1968,7 @@ async fn persisted_enable_does_not_follow_auth_to_an_account_without_a_preferenc
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let remote_control_target =
@@ -185,6 +185,7 @@ async fn list_remote_control_clients_recovers_auth_after_unauthorized() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
@@ -270,6 +271,7 @@ async fn list_remote_control_clients_retries_unauthorized_only_once() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
@@ -539,6 +539,7 @@ async fn remote_control_handle_recovers_auth_before_refreshing_pairing() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
@@ -808,6 +809,7 @@ async fn remote_control_handle_discards_pairing_response_after_auth_change() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let remote_handle =
@@ -2214,6 +2214,7 @@ mod tests {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
@@ -2312,6 +2313,7 @@ mod tests {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
@@ -2435,6 +2437,7 @@ mod tests {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
+26 -9
View File
@@ -137,17 +137,17 @@ Example with notification opt-out:
## API Overview
- `thread/start` — create a new thread; emits `thread/started` (including the current `thread.status`) and auto-subscribes you to turn/item events for that thread. When the request includes a `cwd` and the resolved sandbox is `workspace-write` or full access, app-server also marks that project as trusted in the user `config.toml`. Pass `sessionStartSource: "clear"` when starting a replacement thread after clearing the current session so `SessionStart` hooks receive `source: "clear"` instead of the default `"startup"`. Experimental `runtimeWorkspaceRoots` replaces the thread-scoped runtime workspace roots used to materialize `:workspace_roots`; paths must be absolute. For permissions, prefer experimental `permissions` profile selection by id; the legacy `sandbox` shorthand is still accepted but cannot be combined with `permissions`. Experimental `multiAgentMode` selects the initial thread mode; omission leaves the selected mode unset, while eligible multi-agent v2 turns still default to `explicitRequestOnly`. Experimental `environments` selects the sticky execution environments for turns on the thread; omit it to use the server default, pass `[]` to disable environments, or pass explicit environment ids with per-environment `cwd`. Experimental `selectedCapabilityRoots` selects environment-owned plugin or standalone-skill roots. Skills found below those roots are listed and read through the owning environment. Stdio MCP servers declared by selected plugins are also started in that environment; HTTP MCP declarations remain inactive.
- `thread/start` — create a new thread; emits `thread/started` (including the current `thread.status`) and auto-subscribes you to turn/item events for that thread. When the request includes a `cwd` and the resolved sandbox is `workspace-write` or full access, app-server also marks that project as trusted in the user `config.toml`. Pass `sessionStartSource: "clear"` when starting a replacement thread after clearing the current session so `SessionStart` hooks receive `source: "clear"` instead of the default `"startup"`. Experimental `runtimeWorkspaceRoots` replaces the thread-scoped runtime workspace roots used to materialize `:workspace_roots`; paths must be absolute. For permissions, prefer experimental `permissions` profile selection by id; the legacy `sandbox` shorthand is still accepted but cannot be combined with `permissions`. Experimental `multiAgentMode` selects the initial thread mode and defaults to `explicitRequestOnly` when omitted; use `none` to keep multi-agent tools available without injecting mode instructions. Experimental `environments` selects the sticky execution environments for turns on the thread; omit it to use the server default, pass `[]` to disable environments, or pass explicit environment ids with per-environment `cwd`. Experimental `selectedCapabilityRoots` selects environment-owned plugin or standalone-skill roots. Skills found below those roots are listed and read through the owning environment. Stdio MCP servers declared by selected plugins are also started in that environment; HTTP MCP declarations remain inactive.
- `thread/resume` — reopen an existing thread by id so subsequent `turn/start` calls append to it. Accepts the same permission override rules as `thread/start`. Multi-agent mode restores the last effective mode from rollout history when available; clients can select another mode on the first `turn/start`.
- `thread/fork` — fork an existing thread into a new thread id by copying the stored history; if the source thread is currently mid-turn, the fork records the same interruption marker as `turn/interrupt` instead of inheriting an unmarked partial turn suffix. The returned `thread.forkedFromId` points at the source thread when known. Accepts `ephemeral: true` for an in-memory temporary fork, emits `thread/started` (including the current `thread.status`), and auto-subscribes you to turn/item events for the new thread. Experimental clients can pass `excludeTurns: true` when they plan to page fork history via `thread/turns/list` instead of receiving the full turn array immediately. Accepts the same permission override rules as `thread/start`.
- `thread/start`, `thread/resume`, and `thread/fork` responses include the legacy `sandbox` compatibility projection. `instructionSources` lists loaded instruction files using each source environment's native absolute path syntax, including files loaded from remote environments. Experimental clients can read `runtimeWorkspaceRoots` for the thread-scoped runtime roots and `activePermissionProfile` for the named or implicit built-in profile identity/provenance when known. Their experimental `multiAgentMode` field, and the corresponding thread setting, report the thread's current selected mode or `null` when no mode was selected. Turn construction separately determines whether that mode is applicable to the selected model and runtime configuration.
- `thread/start`, `thread/resume`, and `thread/fork` responses include the legacy `sandbox` compatibility projection. `instructionSources` lists loaded instruction files using each source environment's native absolute path syntax, including files loaded from remote environments. Experimental clients can read `runtimeWorkspaceRoots` for the thread-scoped runtime roots and `activePermissionProfile` for the named or implicit built-in profile identity/provenance when known. Their experimental `multiAgentMode` field, and the corresponding thread setting, report the thread's current mode. Turn construction separately determines whether that mode is applicable to the selected model and runtime configuration.
- `thread/list` — page through stored threads; supports cursor-based pagination and optional `modelProviders`, `sourceKinds`, `archived`, `cwd`, and `searchTerm` filters. Experimental clients can use `parentThreadId` to filter direct spawned children represented by persisted spawn-edge state. Review and Guardian threads are not included because they do not participate in that spawn-edge lifecycle. Each returned `thread` includes `status` (`ThreadStatus`), defaulting to `notLoaded` when the thread is not currently loaded. Subagent threads also include `parentThreadId` when the immediate parent is known.
- `thread/loaded/list` — list the thread ids currently loaded in memory.
- `thread/read` — read a stored thread by id without resuming it; optionally include turns via `includeTurns`. The returned `thread` includes `status` (`ThreadStatus`), defaulting to `notLoaded` when the thread is not currently loaded.
- `thread/turns/list` — experimental; page through a stored thread’s turn history without resuming it; supports cursor-based pagination with `sortDirection`, `itemsView`, `nextCursor`, and `backwardsCursor`.
- `thread/turns/items/list` — experimental; reserved for paging full items for one turn. The API shape is present, but app-server currently returns an unsupported-method JSON-RPC error.
- `thread/metadata/update` — patch stored thread metadata in sqlite; currently supports updating persisted `gitInfo` fields and returns the refreshed `thread`.
- `thread/settings/update` — experimental; queue a partial update to a loaded thread’s next-turn settings without starting a turn or adding transcript items. Omitted fields leave settings unchanged; `serviceTier: null` clears the tier; `multiAgentMode` selects `explicitRequestOnly` or `proactive` for subsequent turns; `sandboxPolicy` and `permissions` cannot be combined. Returns `{}` when the update is accepted and emits `thread/settings/updated` with the full effective settings only if they actually change. `turn/start` settings overrides emit the same notification when they change the stored settings.
- `thread/settings/update` — experimental; queue a partial update to a loaded thread’s next-turn settings without starting a turn or adding transcript items. Omitted fields leave settings unchanged; `serviceTier: null` clears the tier; `multiAgentMode` selects `none`, `explicitRequestOnly`, or `proactive` for subsequent turns; `sandboxPolicy` and `permissions` cannot be combined. Returns `{}` when the update is accepted and emits `thread/settings/updated` with the full effective settings only if they actually change. `turn/start` settings overrides emit the same notification when they change the stored settings.
- `thread/memoryMode/set` — experimental; set a thread’s persisted memory eligibility to `"enabled"` or `"disabled"` for either a loaded thread or a stored rollout; returns `{}` on success.
- `memory/reset` — experimental; clear the current `CODEX_HOME/memories` directory and reset persisted memory stage data in sqlite while preserving existing thread memory modes; returns `{}` on success.
- `thread/goal/set` — create or update the single persisted goal for a materialized thread; returns the current goal and emits `thread/goal/updated`.
@@ -168,7 +168,7 @@ Example with notification opt-out:
- `thread/backgroundTerminals/list` — list running background terminals for a loaded thread (experimental; requires `capabilities.experimentalApi`); returns `data` with the running terminal ids.
- `thread/backgroundTerminals/terminate` — terminate one running background terminal by app-server `processId` (experimental; requires `capabilities.experimentalApi`); returns whether a process was terminated.
- `thread/rollback` — drop the last N turns from the agent’s in-memory context and persist a rollback marker in the rollout so future resumes see the pruned history; returns the updated `thread` (with `turns` populated) on success.
- `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Experimental `runtimeWorkspaceRoots` replaces the thread-scoped runtime workspace roots used to materialize `:workspace_roots`; paths must be absolute. Prefer experimental `permissions` profile selection by id for permission overrides; the legacy `sandboxPolicy` field is still accepted but cannot be combined with `permissions`. For `collaborationMode`, `settings.developer_instructions: null` means "use built-in instructions for the selected mode". Experimental `multiAgentMode` accepts `explicitRequestOnly` or `proactive`; omission keeps the loaded session's current selected mode, including an unset mode. The requested mode is retained for the loaded session without rejecting unsupported configurations. Eligible multi-agent v2 turns default an unset mode to explicit-request-only, use the selected mode when `features.multi_agent_mode` is enabled, and otherwise use explicit-request-only developer instructions.
- `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Experimental `runtimeWorkspaceRoots` replaces the thread-scoped runtime workspace roots used to materialize `:workspace_roots`; paths must be absolute. Prefer experimental `permissions` profile selection by id for permission overrides; the legacy `sandboxPolicy` field is still accepted but cannot be combined with `permissions`. For `collaborationMode`, `settings.developer_instructions: null` means "use built-in instructions for the selected mode". Experimental `multiAgentMode` accepts `none`, `explicitRequestOnly`, or `proactive`; `none` keeps the tools available without injecting mode instructions, and omission keeps the loaded session's current mode. The requested mode is retained for the loaded session without rejecting unsupported configurations, and eligible multi-agent v2 turns use it directly.
- `thread/inject_items` — append raw Responses API items to a loaded thread’s model-visible history without starting a user turn; returns `{}` on success.
- `turn/steer` — add user input to an already in-flight regular turn without starting a new turn; returns the active `turnId` that accepted the input. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Review and manual compaction turns reject `turn/steer`.
- `turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`.
@@ -202,7 +202,7 @@ Example with notification opt-out:
- `model/list` — list available models (set `includeHidden: true` to include entries with `hidden: true`), with model-advertised string reasoning effort options in the catalog's intended progression order, `additionalSpeedTiers`, `serviceTiers`, optional `defaultServiceTier`, optional legacy `upgrade` model ids, optional `upgradeInfo` metadata (`model`, `upgradeCopy`, `modelLink`, `migrationMarkdown`), and optional `availabilityNux` metadata. Clients should preserve the `supportedReasoningEfforts` array order rather than deriving order from the effort names.
- `modelProvider/capabilities/read` — read provider-level capabilities for the currently configured model provider.
- `experimentalFeature/list` — list feature flags with stage metadata (`beta`, `underDevelopment`, `stable`, etc.), enabled/default-enabled state, and cursor pagination. Pass `threadId` when showing feature state for an existing loaded thread so `enabled` is computed from that thread's refreshed config, including project-local config for the thread's cwd; if omitted, the server uses its default config resolution context. For non-beta flags, `displayName`/`description`/`announcement` are `null`.
- `permissionProfile/list` — beta; list available permission profile ids with optional display `description` text, using cursor pagination. Pass `cwd` when the caller needs project-local `[permissions.<id>]` entries to be included in the current catalog view.
- `permissionProfile/list` — beta; list available permission profile ids with optional display `description` text and an `allowed` flag reflecting effective requirements, using cursor pagination. Pass `cwd` when the caller needs project-local `[permissions.<id>]` entries to be included in the current catalog view.
- `experimentalFeature/enablement/set` — patch the in-memory process-wide runtime feature enablement for currently supported feature keys. For each feature, precedence is: cloud requirements > --enable <feature_name> > config.toml > experimentalFeature/enablement/set (new) > code default. Invalid keys will be ignored.
- `environment/add` — experimental; add or replace a named remote environment by `environmentId` and `execServerUrl` for later selection by `thread/start` or `turn/start`; optional `connectTimeoutMs` overrides the WebSocket connection timeout; returns `{}` and does not change the default environment.
- `collaborationMode/list` — list available collaboration mode presets (experimental, no pagination). Built-in presets do not select a model; the Plan preset selects medium reasoning effort. This response omits built-in developer instructions; clients should either pass `settings.developer_instructions: null` when setting a mode to use Codex's built-in instructions, or provide their own instructions explicitly.
@@ -714,9 +714,11 @@ If the thread does not already have an active turn, the server starts a standalo
Turns attach user input (text or images) to a thread and trigger Codex generation. The `input` field is a list of discriminated unions:
- `{"type":"text","text":"Explain this diff"}`
- `{"type":"image","url":"https://…png"}`
- `{"type":"image","url":"data:image/png;base64,…"}`
- `{"type":"localImage","path":"/tmp/screenshot.png"}`
The `image` variant accepts inline data URLs. Remote HTTP(S) image URLs are rejected; use a data URL or `localImage` instead.
You can optionally specify config overrides on the new turn. If specified, these settings become the default for subsequent turns on the same thread. `outputSchema` applies only to the current turn. Experimental `environments` is turn-scoped: omit it to inherit the thread's sticky environments, pass `[]` to run the turn with no environments, or pass explicit environment ids to override the sticky selection for this turn only.
`approvalsReviewer` accepts:
@@ -828,7 +830,7 @@ Invoke a plugin by including a UI mention token such as `@sample` in the text in
### Example: Inject raw history items
Use `thread/inject_items` to append prebuilt Responses API items to a loaded thread’s prompt history without starting a user turn. These items are persisted to the rollout and included in subsequent model requests.
Use `thread/inject_items` to append prebuilt Responses API items to a loaded thread’s prompt history without starting a user turn. These items are persisted to the rollout and included in subsequent model requests. Any `input_image` items must use inline data URLs; remote HTTP(S) image URLs are rejected.
```json
{ "method": "thread/inject_items", "id": 36, "params": {
@@ -1353,6 +1355,7 @@ The app-server streams JSON-RPC notifications while a turn is running. Each turn
- `turn/completed` — `{ turn }` where `turn.status` is `completed`, `interrupted`, or `failed`; failures carry `{ error: { message, codexErrorInfo?, additionalDetails? } }`.
- `turn/diff/updated` — `{ threadId, turnId, diff }` represents the up-to-date snapshot of the turn-level unified diff, emitted after every FileChange item. `diff` is the latest aggregated unified diff across every file change in the turn. UIs can render this to show the full "what changed" view without stitching individual `fileChange` items.
- `turn/plan/updated` — `{ turnId, explanation?, plan }` whenever the agent shares or changes its plan; each `plan` entry is `{ step, status }` with `status` in `pending`, `inProgress`, or `completed`.
- `model/safetyBuffering/updated` — `{ threadId, turnId, model, useCases, reasons, showBufferingUi, fasterModel }` when a response enters safety buffering. `fasterModel` is nullable. This notification is transient and is not persisted in rollout history.
- `model/rerouted` — `{ threadId, turnId, fromModel, toModel, reason }` when the backend reroutes a request to a different model (for example, due to high-risk cyber safety checks).
- `model/verification` — `{ threadId, turnId, verifications }` when the backend flags additional account verification, such as `trustedAccessForCyber`.
- `turn/moderationMetadata` — experimental; `{ threadId, turnId, metadata }` when a first-party backend supplies turn-scoped moderation metadata for client-side presentation.
@@ -1584,7 +1587,7 @@ The server also emits item lifecycle notifications around the request:
3. Client response.
4. `item/completed` with `item.type = "dynamicToolCall"`, final `status`, and the returned `contentItems`/`success`.
The client must respond with content items. Use `inputText` for text and `inputImage` for image URLs/data URLs:
The client must respond with content items. Use `inputText` for text and `inputImage` for inline data URLs. Remote HTTP(S) image URLs make the dynamic tool response invalid.
```json
{
@@ -1919,6 +1922,7 @@ Codex supports these authentication modes. The current mode is surfaced in `acco
- `account/rateLimits/read` — fetch ChatGPT rate limits, an optional effective monthly credit limit, and the number of earned rate-limit resets currently available. Rate-limit updates arrive via `account/rateLimits/updated` (notify); the reset count is snapshot-only.
- `account/rateLimitResetCredit/consume` — consume one earned reset using a caller-provided idempotency key.
- `account/usage/read` — fetch ChatGPT account token-activity summary and daily buckets.
- `account/workspaceMessages/read` — fetch active workspace messages, including workspace notification headlines when available.
- `account/rateLimits/updated` (notify) — emitted whenever a user's ChatGPT rate limits change. This is a sparse rolling update; merge available values into the most recent `account/rateLimits/read` response or refetch that snapshot.
- `account/sendAddCreditsNudgeEmail` — ask ChatGPT to email the workspace owner about depleted credits or a reached usage limit.
- `mcpServer/oauthLogin/completed` (notify) — emitted after a `mcpServer/oauth/login` flow finishes for a server; payload includes `{ name, success, error? }`.
@@ -1939,6 +1943,7 @@ Response examples:
{ "id": 1, "result": { "account": null, "requiresOpenaiAuth": true } } // OpenAI auth required (typical for OpenAI-hosted models)
{ "id": 1, "result": { "account": { "type": "apiKey" }, "requiresOpenaiAuth": true } }
{ "id": 1, "result": { "account": { "type": "chatgpt", "email": "user@example.com", "planType": "pro" }, "requiresOpenaiAuth": true } }
{ "id": 1, "result": { "account": { "type": "chatgpt", "email": null, "planType": "enterprise" }, "requiresOpenaiAuth": true } }
{ "id": 1, "result": { "account": { "type": "amazonBedrock", "credentialSource": "codexManaged" }, "requiresOpenaiAuth": false } }
{ "id": 1, "result": { "account": { "type": "amazonBedrock", "credentialSource": "awsManaged" }, "requiresOpenaiAuth": false } }
```
@@ -1946,6 +1951,7 @@ Response examples:
Field notes:
- `refreshToken` (bool): set `true` to force a token refresh.
- `email` is `null` when the ChatGPT account does not have an email address.
- `requiresOpenaiAuth` reflects the active provider; when `false`, Codex can run without OpenAI credentials.
- Amazon Bedrock reports `credentialSource: "codexManaged"` when it uses a Bedrock API key managed by Codex. Otherwise it reports `credentialSource: "awsManaged"` for the external AWS credential path. This identifies the selected credential source; it does not validate that the AWS credential chain can resolve credentials.
@@ -2045,7 +2051,18 @@ Field notes:
- `noCredit` means the account has no earned reset credits available.
- Refetch `account/rateLimits/read` after consuming a reset instead of inferring updated windows from this response.
### 9) Notify a workspace owner about a limit
### 9) Workspace messages (ChatGPT)
```json
{ "method": "account/workspaceMessages/read", "id": 9 }
{ "id": 9, "result": { "featureEnabled": true, "messages": [
{ "messageId": "msg_123", "messageType": "headline", "messageBody": "Workspace maintenance starts at 5pm.", "createdAt": 1781395200, "archivedAt": null }
] } }
```
When the upstream workspace-message feature is disabled, `featureEnabled` is `false` and `messages` is empty.
### 10) Notify a workspace owner about a limit
```json
{ "method": "account/sendAddCreditsNudgeEmail", "id": 9, "params": { "creditType": "credits" } }
@@ -40,6 +40,7 @@ use codex_app_server_protocol::McpServerElicitationRequestResponse;
use codex_app_server_protocol::McpServerStartupState;
use codex_app_server_protocol::McpServerStatusUpdatedNotification;
use codex_app_server_protocol::ModelReroutedNotification;
use codex_app_server_protocol::ModelSafetyBufferingUpdatedNotification;
use codex_app_server_protocol::ModelVerificationNotification;
use codex_app_server_protocol::NetworkApprovalContext as V2NetworkApprovalContext;
use codex_app_server_protocol::NetworkPolicyAmendment as V2NetworkPolicyAmendment;
@@ -351,6 +352,22 @@ pub(crate) async fn apply_bespoke_event_handling(
.send_server_notification(ServerNotification::TurnModerationMetadata(notification))
.await;
}
EventMsg::SafetyBuffering(event) => {
let notification = ModelSafetyBufferingUpdatedNotification {
thread_id: conversation_id.to_string(),
turn_id: event_turn_id.clone(),
model: event.model,
use_cases: event.use_cases,
reasons: event.reasons,
show_buffering_ui: event.show_buffering_ui,
faster_model: event.faster_model,
};
outgoing
.send_server_notification(ServerNotification::ModelSafetyBufferingUpdated(
notification,
))
.await;
}
EventMsg::RealtimeConversationStarted(event) => {
let notification = ThreadRealtimeStartedNotification {
thread_id: conversation_id.to_string(),
+17
View File
@@ -8,6 +8,8 @@ use std::sync::Arc;
use tokio::sync::oneshot;
use tracing::error;
use crate::image_url::REMOTE_IMAGE_URL_ERROR;
use crate::image_url::is_remote_image_url;
use crate::outgoing_message::ClientRequestResult;
use crate::server_request_error::is_turn_transition_server_request_error;
@@ -54,6 +56,21 @@ pub(crate) async fn on_call_response(
fn decode_response(value: serde_json::Value) -> (DynamicToolCallResponse, Option<String>) {
match serde_json::from_value::<DynamicToolCallResponse>(value) {
Ok(response)
if response.content_items.iter().any(|item| {
matches!(
item,
DynamicToolCallOutputContentItem::InputImage { image_url }
if is_remote_image_url(image_url)
)
}) =>
{
error!(
message = REMOTE_IMAGE_URL_ERROR,
"dynamic tool response was invalid"
);
fallback_response(REMOTE_IMAGE_URL_ERROR)
}
Ok(response) => (response, None),
Err(err) => {
error!("failed to deserialize DynamicToolCallResponse: {err}");
+8
View File
@@ -0,0 +1,8 @@
pub(crate) const REMOTE_IMAGE_URL_ERROR: &str =
"remote image URLs are not supported; use an inline data URL instead";
pub(crate) fn is_remote_image_url(image_url: &str) -> bool {
image_url.split_once(':').is_some_and(|(scheme, _)| {
scheme.eq_ignore_ascii_case("http") || scheme.eq_ignore_ascii_case("https")
})
}
+2 -3
View File
@@ -68,13 +68,11 @@ use tokio::sync::mpsc;
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use tracing::Level;
use tracing::error;
use tracing::info;
use tracing::warn;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::Layer;
use tracing_subscriber::filter::Targets;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::registry::Registry;
use tracing_subscriber::util::SubscriberInitExt;
@@ -98,6 +96,7 @@ mod extensions;
mod filters;
mod fs_watch;
mod fuzzy_file_search;
mod image_url;
pub mod in_process;
mod mcp_refresh;
mod message_processor;
@@ -666,7 +665,7 @@ pub async fn run_main_with_transport_options(
let log_db = state_db.clone().map(log_db::start);
let log_db_layer = log_db
.clone()
.map(|layer| layer.with_filter(Targets::new().with_default(Level::TRACE)));
.map(|layer| layer.with_filter(log_db::default_filter()));
let otel_logger_layer = otel.as_ref().and_then(|o| o.logger_layer());
let otel_tracing_layer = otel.as_ref().and_then(|o| o.tracing_layer());
let _ = tracing_subscriber::registry()
@@ -1427,6 +1427,9 @@ impl MessageProcessor {
ClientRequest::GetAccountTokenUsage { .. } => {
self.account_processor.get_account_token_usage().await
}
ClientRequest::GetWorkspaceMessages { .. } => {
self.account_processor.get_workspace_messages().await
}
ClientRequest::SendAddCreditsNudgeEmail { params, .. } => {
self.account_processor
.send_add_credits_nudge_email(params)
@@ -76,6 +76,7 @@ use codex_app_server_protocol::GetAuthStatusParams;
use codex_app_server_protocol::GetAuthStatusResponse;
use codex_app_server_protocol::GetConversationSummaryParams;
use codex_app_server_protocol::GetConversationSummaryResponse;
use codex_app_server_protocol::GetWorkspaceMessagesResponse;
use codex_app_server_protocol::GitDiffToRemoteParams;
use codex_app_server_protocol::GitDiffToRemoteResponse;
use codex_app_server_protocol::GitInfo as ApiGitInfo;
@@ -282,10 +283,16 @@ use codex_app_server_protocol::WindowsSandboxSetupCompletedNotification;
use codex_app_server_protocol::WindowsSandboxSetupMode;
use codex_app_server_protocol::WindowsSandboxSetupStartParams;
use codex_app_server_protocol::WindowsSandboxSetupStartResponse;
use codex_app_server_protocol::WorkspaceMessage;
use codex_app_server_protocol::WorkspaceMessageType;
use codex_arg0::Arg0DispatchPaths;
use codex_backend_client::AddCreditsNudgeCreditType as BackendAddCreditsNudgeCreditType;
use codex_backend_client::Client as BackendClient;
use codex_backend_client::CodexWorkspaceMessage as BackendWorkspaceMessage;
use codex_backend_client::CodexWorkspaceMessageType as BackendWorkspaceMessageType;
use codex_backend_client::CodexWorkspaceMessagesResponse as BackendWorkspaceMessagesResponse;
use codex_backend_client::ConsumeRateLimitResetCreditCode as BackendConsumeRateLimitResetCreditCode;
use codex_backend_client::RequestError as BackendRequestError;
use codex_backend_client::TokenUsageProfile;
use codex_chatgpt::connectors;
use codex_chatgpt::workspace_settings;
@@ -330,7 +337,6 @@ use codex_core_plugins::PluginUninstallError as CorePluginUninstallError;
use codex_core_plugins::PluginsManager;
use codex_core_plugins::loader::load_plugin_apps;
use codex_core_plugins::loader::load_plugin_mcp_servers;
use codex_core_plugins::loader::plugin_telemetry_metadata_from_root;
use codex_core_plugins::manifest::PluginManifestInterface;
use codex_core_plugins::marketplace::MarketplaceError;
use codex_core_plugins::marketplace::MarketplacePluginSource;
@@ -390,9 +396,6 @@ use codex_protocol::error::CodexErr;
use codex_protocol::error::Result as CodexResult;
#[cfg(test)]
use codex_protocol::items::TurnItem;
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS;
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_READ_ONLY;
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ReasoningEffort;
#[cfg(test)]
@@ -1,10 +1,13 @@
use super::*;
use chrono::DateTime;
mod rate_limit_resets;
// Duration before a browser ChatGPT login attempt is abandoned.
const LOGIN_CHATGPT_TIMEOUT: Duration = Duration::from_secs(10 * 60);
const ACCOUNT_TOKEN_USAGE_FETCH_TIMEOUT: Duration = Duration::from_secs(/*secs*/ 10);
const ACCOUNT_WORKSPACE_MESSAGES_FETCH_TIMEOUT: Duration =
Duration::from_millis(/*millis*/ 1000);
// The override is intentionally available only in debug builds, matching the login path below.
#[cfg(debug_assertions)]
const LOGIN_ISSUER_OVERRIDE_ENV_VAR: &str = "CODEX_APP_SERVER_LOGIN_ISSUER";
@@ -142,6 +145,14 @@ impl AccountRequestProcessor {
.map(|response| Some(response.into()))
}
pub(crate) async fn get_workspace_messages(
&self,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.get_workspace_messages_response()
.await
.map(|response| Some(response.into()))
}
pub(crate) async fn send_add_credits_nudge_email(
&self,
params: SendAddCreditsNudgeEmailParams,
@@ -348,6 +359,7 @@ impl AccountRequestProcessor {
config.forced_chatgpt_workspace_id.clone(),
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
config.auth_route_config(),
)
};
#[cfg(debug_assertions)]
@@ -943,6 +955,48 @@ impl AccountRequestProcessor {
Ok(Self::account_token_usage_response(profile))
}
async fn get_workspace_messages_response(
&self,
) -> Result<GetWorkspaceMessagesResponse, JSONRPCErrorError> {
let Some(auth) = self.auth_manager.auth().await else {
return Err(invalid_request(
"codex account authentication required to read workspace messages",
));
};
if !auth.uses_codex_backend() {
return Err(invalid_request(
"chatgpt authentication required to read workspace messages",
));
}
let client = BackendClient::from_auth(self.config.chatgpt_base_url.clone(), &auth)
.map_err(|err| internal_error(format!("failed to construct backend client: {err}")))?;
let messages = tokio::time::timeout(
ACCOUNT_WORKSPACE_MESSAGES_FETCH_TIMEOUT,
client.list_workspace_messages(),
)
.await
.map_err(|_| internal_error("workspace messages fetch timed out"))?;
match messages {
Ok(messages) => {
Self::workspace_messages_response(messages, /*feature_enabled*/ true)
}
Err(err) if workspace_messages_feature_disabled(&err) => {
Self::workspace_messages_response(
BackendWorkspaceMessagesResponse {
messages: Vec::new(),
},
/*feature_enabled*/ false,
)
}
Err(err) => Err(internal_error(format!(
"failed to fetch workspace messages: {err}"
))),
}
}
fn account_token_usage_response(profile: TokenUsageProfile) -> GetAccountTokenUsageResponse {
let stats = profile.stats;
GetAccountTokenUsageResponse {
@@ -965,6 +1019,20 @@ impl AccountRequestProcessor {
}
}
fn workspace_messages_response(
messages: BackendWorkspaceMessagesResponse,
feature_enabled: bool,
) -> Result<GetWorkspaceMessagesResponse, JSONRPCErrorError> {
Ok(GetWorkspaceMessagesResponse {
feature_enabled,
messages: messages
.messages
.into_iter()
.map(workspace_message_from_backend)
.collect::<Result<Vec<_>, _>>()?,
})
}
async fn send_add_credits_nudge_email_response(
&self,
params: SendAddCreditsNudgeEmailParams,
@@ -1015,6 +1083,48 @@ impl AccountRequestProcessor {
}
}
fn workspace_message_from_backend(
message: BackendWorkspaceMessage,
) -> Result<WorkspaceMessage, JSONRPCErrorError> {
Ok(WorkspaceMessage {
message_id: message.message_id,
message_type: workspace_message_type_from_backend(message.message_type),
message_body: message.message_body,
created_at: workspace_message_timestamp_from_backend(message.created_at)?,
archived_at: workspace_message_timestamp_from_backend(message.archived_at)?,
})
}
fn workspace_message_timestamp_from_backend(
timestamp: Option<String>,
) -> Result<Option<i64>, JSONRPCErrorError> {
timestamp
.map(|timestamp| {
DateTime::parse_from_rfc3339(&timestamp)
.map(|timestamp| timestamp.timestamp())
.map_err(|err| {
internal_error(format!(
"failed to parse workspace message timestamp `{timestamp}`: {err}"
))
})
})
.transpose()
}
fn workspace_message_type_from_backend(
message_type: BackendWorkspaceMessageType,
) -> WorkspaceMessageType {
match message_type {
BackendWorkspaceMessageType::Headline => WorkspaceMessageType::Headline,
BackendWorkspaceMessageType::Announcement => WorkspaceMessageType::Announcement,
BackendWorkspaceMessageType::Unknown => WorkspaceMessageType::Unknown,
}
}
fn workspace_messages_feature_disabled(err: &BackendRequestError) -> bool {
err.status().is_some_and(|status| status.as_u16() == 404)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -1055,4 +1165,55 @@ mod tests {
}
);
}
#[test]
fn workspace_messages_response_maps_backend_messages() {
let response = AccountRequestProcessor::workspace_messages_response(
BackendWorkspaceMessagesResponse {
messages: vec![BackendWorkspaceMessage {
message_id: "headline-id".to_string(),
message_type: BackendWorkspaceMessageType::Headline,
message_body: "Headline body".to_string(),
created_at: Some("2026-06-14T00:00:00Z".to_string()),
archived_at: Some("2026-06-15T00:00:00Z".to_string()),
}],
},
/*feature_enabled*/ true,
)
.expect("workspace message timestamps should parse");
assert_eq!(
response,
GetWorkspaceMessagesResponse {
feature_enabled: true,
messages: vec![WorkspaceMessage {
message_id: "headline-id".to_string(),
message_type: WorkspaceMessageType::Headline,
message_body: "Headline body".to_string(),
created_at: Some(1_781_395_200),
archived_at: Some(1_781_481_600),
}],
}
);
}
#[test]
fn workspace_messages_feature_disabled_only_for_not_found() {
let cases = [
(reqwest::StatusCode::NOT_FOUND, true),
(reqwest::StatusCode::UNAUTHORIZED, false),
(reqwest::StatusCode::FORBIDDEN, false),
];
for (status, expected) in cases {
let err = BackendRequestError::UnexpectedStatus {
method: "GET".to_string(),
url: "https://example.test/api/codex/workspace-messages".to_string(),
status,
content_type: "application/json".to_string(),
body: "{}".to_string(),
};
assert_eq!(workspace_messages_feature_disabled(&err), expected);
}
}
}
@@ -1,5 +1,5 @@
use super::*;
use codex_config::config_toml::ConfigToml;
use codex_core::config::permission_profile_catalog;
use futures::StreamExt;
#[derive(Clone)]
@@ -434,35 +434,15 @@ impl CatalogRequestProcessor {
.await
.map_err(|err| internal_error(format!("failed to reload config: {err}")))?,
};
let effective_config: ConfigToml = config_layer_stack
.effective_config()
.try_into()
.map_err(|err| internal_error(format!("failed to read effective config: {err}")))?;
let mut profiles = vec![
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_READ_ONLY.to_string(),
description: None,
},
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_WORKSPACE.to_string(),
description: None,
},
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS.to_string(),
description: None,
},
];
let mut configured_profiles = effective_config
.permissions
let profiles = permission_profile_catalog(&config_layer_stack)
.map_err(|err| internal_error(format!("failed to resolve permission profiles: {err}")))?
.into_iter()
.flat_map(|permissions| permissions.entries)
.map(|(id, profile)| PermissionProfileSummary {
id,
.map(|profile| PermissionProfileSummary {
id: profile.id,
description: profile.description,
allowed: profile.allowed,
})
.collect::<Vec<_>>();
configured_profiles.sort_by(|left, right| left.id.cmp(&right.id));
profiles.extend(configured_profiles);
let total = profiles.len();
let effective_limit = limit.unwrap_or(total as u32).max(1) as usize;
let effective_limit = effective_limit.min(total);
@@ -295,15 +295,14 @@ impl ConfigRequestProcessor {
&self,
pending_changes: std::collections::BTreeMap<String, bool>,
) {
let plugins_manager = self.thread_manager.plugins_manager();
for (plugin_id, enabled) in pending_changes {
let Ok(plugin_id) = PluginId::parse(&plugin_id) else {
continue;
};
let metadata = codex_core_plugins::loader::installed_plugin_telemetry_metadata(
self.config_manager.codex_home(),
&plugin_id,
)
.await;
let metadata = plugins_manager
.telemetry_metadata_for_installed_plugin(&plugin_id)
.await;
if enabled {
self.analytics_events_client.track_plugin_enabled(metadata);
} else {
@@ -200,6 +200,7 @@ impl ExternalAgentSessionImporter {
};
let now = Utc::now();
let create_params = CreateThreadParams {
session_id: thread_id.into(),
thread_id,
extra_config: None,
forked_from_id: None,
@@ -23,7 +23,6 @@ use codex_mcp::McpOAuthLoginSupport;
use codex_mcp::oauth_login_support;
use codex_mcp::should_retry_without_scopes;
use codex_plugin::PluginId;
use codex_plugin::PluginTelemetryMetadata;
use codex_rmcp_client::perform_oauth_login_silent;
#[derive(Clone)]
@@ -78,7 +77,9 @@ fn local_plugin_interface_to_info(interface: PluginManifestInterface) -> PluginI
composer_icon: interface.composer_icon,
composer_icon_url: None,
logo: interface.logo,
logo_dark: interface.logo_dark,
logo_url: None,
logo_url_dark: None,
screenshots: interface.screenshots,
screenshot_urls: Vec::new(),
}
@@ -746,11 +747,10 @@ impl PluginRequestProcessor {
);
}
let featured_plugin_ids = if !plugins_input.remote_plugin_enabled
&& data
.iter()
.any(|marketplace| marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME)
{
let featured_plugin_ids = if data.iter().any(|marketplace| {
marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME
|| marketplace.name == REMOTE_GLOBAL_MARKETPLACE_NAME
}) {
match plugins_manager
.featured_plugin_ids_for_config(&plugins_input, auth.as_ref())
.await
@@ -1620,9 +1620,14 @@ impl PluginRequestProcessor {
Some(self.effective_plugins_changed_callback()),
);
let mut plugin_metadata =
plugin_telemetry_metadata_from_root(&result.plugin_id, &result.installed_path).await;
plugin_metadata.remote_plugin_id = Some(remote_plugin_id.clone());
let plugin_metadata = self
.thread_manager
.plugins_manager()
.telemetry_metadata_for_installed_plugin_with_remote_id(
&result.plugin_id,
&remote_plugin_id,
)
.await;
self.analytics_events_client
.track_plugin_installed(plugin_metadata);
@@ -1713,8 +1718,10 @@ impl PluginRequestProcessor {
else {
return;
};
let mut plugin = PluginTelemetryMetadata::from_plugin_id(&plugin_id);
plugin.remote_plugin_id = Some(remote_plugin_id.to_string());
let plugin = self
.thread_manager
.plugins_manager()
.telemetry_metadata_for_plugin_id_with_remote_id(&plugin_id, remote_plugin_id);
self.analytics_events_client
.track_plugin_install_failed(plugin, error_type.to_string());
}
@@ -2782,17 +2782,18 @@ impl ThreadRequestProcessor {
}
}
}
let response_history_items = (include_turns
|| (initial_turns_page.is_some() && initial_turns_recent_items.is_none()))
.then(|| {
let mut response_history_items_storage = if include_turns
|| (initial_turns_page.is_some() && initial_turns_recent_items.is_none())
{
if matches!(history_extent, ResumeHistoryExtent::CompactedCheckpoint) {
None
} else {
Some(thread_history.get_rollout_items())
Some(thread_history.get_rollout_items().to_vec())
}
})
.flatten();
let response_history_items = if response_history_items.is_none()
} else {
None
};
if response_history_items_storage.is_none()
&& initial_turns_page.is_some()
&& initial_turns_recent_items.is_none()
{
@@ -2800,15 +2801,14 @@ impl ThreadRequestProcessor {
.load_full_resume_history_items(&thread_id, path.as_ref())
.await
{
Ok(items) => Some(items),
Ok(items) => response_history_items_storage = Some(items),
Err(error) => {
self.outgoing.send_error(request_id, error).await;
return Ok(());
}
}
} else {
response_history_items
};
}
let response_history_items = response_history_items_storage.as_deref();
match self
.thread_manager
@@ -2852,7 +2852,7 @@ impl ThreadRequestProcessor {
codex_thread.as_ref(),
ResumeResponseSource {
history: &response_history,
items: response_history_items.as_deref(),
items: response_history_items,
},
rollout_path.as_path(),
resume_source_thread,
@@ -2897,7 +2897,7 @@ impl ThreadRequestProcessor {
config_snapshot.active_permission_profile,
);
let inline_history_items = if include_turns {
let Some(history_items) = response_history_items.as_deref() else {
let Some(history_items) = response_history_items else {
self.outgoing
.send_error(
request_id,
@@ -2927,7 +2927,7 @@ impl ThreadRequestProcessor {
}
}
} else {
let Some(history_items) = response_history_items.as_deref() else {
let Some(history_items) = response_history_items else {
self.outgoing
.send_error(
request_id,
@@ -3113,7 +3113,7 @@ impl ThreadRequestProcessor {
}
};
if let Some((existing_thread_id, existing_thread, source_thread)) = running_thread {
if let Some((existing_thread_id, existing_thread, mut source_thread)) = running_thread {
let existing_thread_rollout_path = existing_thread.rollout_path();
let active_path = existing_thread_rollout_path
.as_ref()
@@ -3175,8 +3175,8 @@ impl ThreadRequestProcessor {
should_redact_thread_resume_payloads(app_server_client_name.as_deref());
let history_items = source_thread
.history
.as_ref()
.map(|history| history.items.clone())
.take()
.map(|history| history.items)
.ok_or_else(|| {
internal_error(format!(
"thread {existing_thread_id} did not include persisted history"
@@ -3200,10 +3200,8 @@ impl ThreadRequestProcessor {
)
.await?;
let mut summary_source_thread = source_thread;
summary_source_thread.history = None;
let mut thread_summary = self.stored_thread_to_api_thread(
summary_source_thread,
source_thread,
config_snapshot.model_provider_id.as_str(),
/*include_turns*/ false,
);
@@ -3451,7 +3449,7 @@ impl ThreadRequestProcessor {
})?;
Ok(InitialHistory::Resumed(ResumedHistory {
conversation_id: thread_id,
history,
history: Arc::new(history),
rollout_path: stored_thread.rollout_path.clone(),
}))
}
@@ -3668,7 +3666,7 @@ impl ThreadRequestProcessor {
let history_items = source_thread
.history
.take()
.map(|history| history.items)
.map(|history| Arc::new(history.items))
.ok_or_else(|| {
internal_error(format!(
"thread {source_thread_id} did not include persisted history"
@@ -403,7 +403,7 @@ mod thread_processor_behavior_tests {
role: role.to_string(),
content,
phase: None,
metadata: None,
internal_chat_message_metadata_passthrough: None,
}
}
@@ -412,6 +412,8 @@ mod thread_processor_behavior_tests {
message: message.to_string(),
replacement_history: Some(vec![compacted_resume_test_message("assistant", message)]),
window_number: Some(window_id),
first_window_id: None,
previous_window_id: None,
window_id: None,
})
}
@@ -720,9 +722,7 @@ mod thread_processor_behavior_tests {
},
)),
compacted_resume_test_user_event("first user event"),
RolloutItem::ResponseItem(codex_protocol::models::ResponseItem::CompactionTrigger {
metadata: None,
}),
RolloutItem::ResponseItem(codex_protocol::models::ResponseItem::CompactionTrigger {}),
compacted_resume_test_agent_event("first agent event"),
])?;
std::fs::write(&path, contents)?;
@@ -1411,6 +1411,7 @@ mod thread_processor_behavior_tests {
let timestamp = "2025-09-05T16:53:11.850Z".to_string();
let session_meta = SessionMeta {
session_id: conversation_id.into(),
id: conversation_id,
timestamp: timestamp.clone(),
model_provider: None,
@@ -1467,6 +1468,7 @@ mod thread_processor_behavior_tests {
let timestamp = "2025-09-05T16:53:11.850Z".to_string();
let session_meta = SessionMeta {
session_id: parent_thread_id.into(),
id: conversation_id,
timestamp: timestamp.clone(),
source: SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
@@ -1517,6 +1519,7 @@ mod thread_processor_behavior_tests {
let timestamp = "2025-09-05T16:53:11.850Z".to_string();
let session_meta = SessionMeta {
session_id: conversation_id.into(),
id: conversation_id,
forked_from_id: Some(forked_from_id),
timestamp: timestamp.clone(),
@@ -13,6 +13,7 @@ fn extract_conversation_summary_prefers_plain_user_messages() -> Result<()> {
let head = vec![
json!({
"session_id": conversation_id.to_string(),
"id": conversation_id.to_string(),
"timestamp": timestamp,
"cwd": "/",
@@ -1,14 +1,71 @@
use super::*;
use codex_protocol::config_types::MultiAgentMode;
use codex_protocol::models::ContentItem;
use codex_protocol::models::FunctionCallOutputContentItem;
use codex_protocol::protocol::AdditionalContextEntry as CoreAdditionalContextEntry;
use codex_protocol::protocol::AdditionalContextKind as CoreAdditionalContextKind;
use codex_protocol::protocol::MultiAgentVersion;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
use crate::image_url::REMOTE_IMAGE_URL_ERROR;
use crate::image_url::is_remote_image_url;
const DIRECT_INPUT_TO_MULTI_AGENT_V2_SUBAGENT_ERROR: &str =
"direct app-server input is not allowed for multi-agent v2 sub-agents";
fn validate_user_input_image_urls(input: &[V2UserInput]) -> Result<(), JSONRPCErrorError> {
if input.iter().any(|item| {
matches!(
item,
V2UserInput::Image { url, .. } if is_remote_image_url(url)
)
}) {
return Err(invalid_request(REMOTE_IMAGE_URL_ERROR));
}
Ok(())
}
fn validate_response_item_image_urls(items: &[ResponseItem]) -> Result<(), JSONRPCErrorError> {
if items.iter().any(|item| match item {
ResponseItem::Message { content, .. } => content.iter().any(|item| {
matches!(
item,
ContentItem::InputImage { image_url, .. } if is_remote_image_url(image_url)
)
}),
ResponseItem::FunctionCallOutput { output, .. }
| ResponseItem::CustomToolCallOutput { output, .. } => {
output.content_items().is_some_and(|content| {
content.iter().any(|item| {
matches!(
item,
FunctionCallOutputContentItem::InputImage { image_url, .. }
if is_remote_image_url(image_url)
)
})
})
}
ResponseItem::Reasoning { .. }
| ResponseItem::AgentMessage { .. }
| ResponseItem::LocalShellCall { .. }
| ResponseItem::FunctionCall { .. }
| ResponseItem::ToolSearchCall { .. }
| ResponseItem::CustomToolCall { .. }
| ResponseItem::ToolSearchOutput { .. }
| ResponseItem::WebSearchCall { .. }
| ResponseItem::ImageGenerationCall { .. }
| ResponseItem::Compaction { .. }
| ResponseItem::CompactionTrigger { .. }
| ResponseItem::ContextCompaction { .. }
| ResponseItem::AdditionalTools { .. }
| ResponseItem::Other => false,
}) {
return Err(invalid_request(REMOTE_IMAGE_URL_ERROR));
}
Ok(())
}
#[derive(Clone)]
pub(crate) struct TurnRequestProcessor {
auth_manager: Arc<AuthManager>,
@@ -105,6 +162,7 @@ impl TurnRequestProcessor {
app_server_client_version: Option<String>,
supports_openai_form_elicitation: bool,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
validate_user_input_image_urls(&params.input)?;
self.turn_start_inner(
request_id,
params,
@@ -140,6 +198,7 @@ impl TurnRequestProcessor {
request_id: &ConnectionRequestId,
params: TurnSteerParams,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
validate_user_input_image_urls(&params.input)?;
self.turn_steer_inner(request_id, params)
.await
.map(|response| Some(response.into()))
@@ -764,6 +823,7 @@ impl TurnRequestProcessor {
})
.collect::<std::result::Result<Vec<_>, _>>()
.map_err(invalid_request)?;
validate_response_item_image_urls(&items)?;
thread
.inject_response_items(items)
@@ -1177,7 +1237,7 @@ impl TurnRequestProcessor {
config.clone(),
InitialHistory::Resumed(ResumedHistory {
conversation_id: parent_thread_id,
history: parent_history.items,
history: Arc::new(parent_history.items),
rollout_path: parent_thread.rollout_path(),
}),
/*thread_source*/ None,
+9 -1
View File
@@ -1,4 +1,5 @@
use anyhow::Result;
use codex_protocol::SessionId;
use codex_protocol::ThreadId;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::GitInfo;
@@ -127,11 +128,12 @@ pub fn create_fake_rollout_with_source(
model_provider,
git_info,
source,
/*session_id*/ None,
/*parent_thread_id*/ None,
)
}
/// Create a minimal rollout file with an explicit session source and control parent.
/// Create a minimal rollout file with an explicit root session and control parent.
#[allow(clippy::too_many_arguments)]
pub fn create_fake_parented_rollout_with_source(
codex_home: &Path,
@@ -141,6 +143,7 @@ pub fn create_fake_parented_rollout_with_source(
model_provider: Option<&str>,
git_info: Option<GitInfo>,
source: SessionSource,
session_id: SessionId,
parent_thread_id: ThreadId,
) -> Result<String> {
create_fake_rollout_with_source_and_parent_thread_id(
@@ -151,6 +154,7 @@ pub fn create_fake_parented_rollout_with_source(
model_provider,
git_info,
source,
Some(session_id),
Some(parent_thread_id),
)
}
@@ -164,11 +168,13 @@ fn create_fake_rollout_with_source_and_parent_thread_id(
model_provider: Option<&str>,
git_info: Option<GitInfo>,
source: SessionSource,
session_id: Option<SessionId>,
parent_thread_id: Option<ThreadId>,
) -> Result<String> {
let uuid = Uuid::new_v4();
let uuid_str = uuid.to_string();
let conversation_id = ThreadId::from_string(&uuid_str)?;
let session_id = session_id.unwrap_or_else(|| conversation_id.into());
let file_path = rollout_path(codex_home, filename_ts, &uuid_str);
let dir = file_path
@@ -178,6 +184,7 @@ fn create_fake_rollout_with_source_and_parent_thread_id(
// Build JSONL lines
let meta = SessionMeta {
session_id,
id: conversation_id,
forked_from_id: None,
parent_thread_id,
@@ -264,6 +271,7 @@ pub fn create_fake_rollout_with_text_elements(
// Build JSONL lines
let meta = SessionMeta {
session_id: conversation_id.into(),
id: conversation_id,
forked_from_id: None,
parent_thread_id: None,
+34 -2
View File
@@ -5,7 +5,10 @@ use app_test_support::to_response;
use app_test_support::write_chatgpt_auth;
use chrono::Duration;
use chrono::Utc;
use codex_app_server_protocol::Account;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::GetAccountParams;
use codex_app_server_protocol::GetAccountResponse;
use codex_app_server_protocol::GetAuthStatusParams;
use codex_app_server_protocol::GetAuthStatusResponse;
use codex_app_server_protocol::JSONRPCError;
@@ -14,6 +17,7 @@ use codex_app_server_protocol::LoginAccountResponse;
use codex_app_server_protocol::RequestId;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
use codex_protocol::account::PlanType as AccountPlanType;
use pretty_assertions::assert_eq;
use std::path::Path;
use tempfile::TempDir;
@@ -162,7 +166,7 @@ async fn get_auth_status_with_api_key() -> Result<()> {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_auth_status_with_personal_access_token_omits_token() -> Result<()> {
async fn personal_access_token_without_email_supports_auth_status_and_account_read() -> Result<()> {
let codex_home = TempDir::new()?;
create_config_toml(codex_home.path())?;
@@ -171,7 +175,7 @@ async fn get_auth_status_with_personal_access_token_omits_token() -> Result<()>
.and(path("/v1/user-auth-credential/whoami"))
.and(header("Authorization", "Bearer at-test-token"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"email": "user@example.com",
"email": null,
"chatgpt_user_id": "user-123",
"chatgpt_account_id": "account-123",
"chatgpt_plan_type": "pro",
@@ -215,6 +219,34 @@ async fn get_auth_status_with_personal_access_token_omits_token() -> Result<()>
}
);
let request_id = mcp
.send_get_account_request(GetAccountParams {
refresh_token: false,
})
.await?;
let response: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
)
.await??;
assert_eq!(
response
.result
.get("account")
.and_then(|account| account.get("email")),
Some(&serde_json::Value::Null),
);
assert_eq!(
to_response::<GetAccountResponse>(response)?,
GetAccountResponse {
account: Some(Account::Chatgpt {
email: None,
plan_type: AccountPlanType::Pro,
}),
requires_openai_auth: true,
}
);
server.verify().await;
Ok(())
}
@@ -121,6 +121,7 @@ async fn get_conversation_summary_by_thread_id_reads_pathless_store_thread() ->
let thread_id = ThreadId::from_string("00000000-0000-4000-8000-000000000125")?;
store
.create_thread(CreateThreadParams {
session_id: thread_id.into(),
thread_id,
extra_config: None,
forked_from_id: None,
+49 -4
View File
@@ -320,7 +320,7 @@ async fn set_auth_token_updates_account_and_notifies() -> Result<()> {
account,
GetAccountResponse {
account: Some(Account::Chatgpt {
email: "embedded@example.com".to_string(),
email: Some("embedded@example.com".to_string()),
plan_type: AccountPlanType::Pro,
}),
requires_openai_auth: true,
@@ -388,7 +388,7 @@ async fn account_read_refresh_token_is_noop_in_external_mode() -> Result<()> {
account,
GetAccountResponse {
account: Some(Account::Chatgpt {
email: "embedded@example.com".to_string(),
email: Some("embedded@example.com".to_string()),
plan_type: AccountPlanType::Pro,
}),
requires_openai_auth: true,
@@ -1817,7 +1817,7 @@ async fn get_account_with_chatgpt() -> Result<()> {
let expected = GetAccountResponse {
account: Some(Account::Chatgpt {
email: "user@example.com".to_string(),
email: Some("user@example.com".to_string()),
plan_type: AccountPlanType::Pro,
}),
requires_openai_auth: true,
@@ -1826,6 +1826,51 @@ async fn get_account_with_chatgpt() -> Result<()> {
Ok(())
}
#[tokio::test]
async fn get_account_with_chatgpt_without_email() -> Result<()> {
let codex_home = TempDir::new()?;
create_config_toml(
codex_home.path(),
CreateConfigTomlParams {
requires_openai_auth: Some(true),
..Default::default()
},
)?;
write_chatgpt_auth(
codex_home.path(),
ChatGptAuthFixture::new("access-chatgpt").plan_type("pro"),
AuthCredentialsStoreMode::File,
)?;
let mut mcp =
TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let request_id = mcp
.send_get_account_request(GetAccountParams {
refresh_token: false,
})
.await?;
let response: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
)
.await??;
let received: GetAccountResponse = to_response(response)?;
assert_eq!(
received,
GetAccountResponse {
account: Some(Account::Chatgpt {
email: None,
plan_type: AccountPlanType::Pro,
}),
requires_openai_auth: true,
}
);
Ok(())
}
#[tokio::test]
async fn get_account_omits_chatgpt_after_permanent_refresh_failure() -> Result<()> {
let codex_home = TempDir::new()?;
@@ -1944,7 +1989,7 @@ async fn get_account_with_chatgpt_missing_plan_claim_returns_unknown() -> Result
let expected = GetAccountResponse {
account: Some(Account::Chatgpt {
email: "user@example.com".to_string(),
email: Some("user@example.com".to_string()),
plan_type: AccountPlanType::Unknown,
}),
requires_openai_auth: true,
@@ -63,7 +63,10 @@ async fn turn_start_forwards_client_metadata_to_responses_request_v2() -> Result
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request(ThreadStartParams {
thread_source: Some(ThreadSource::Feature("automation".to_string())),
..Default::default()
})
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
@@ -75,7 +78,6 @@ async fn turn_start_forwards_client_metadata_to_responses_request_v2() -> Result
let client_metadata = HashMap::from([
("fiber_run_id".to_string(), "fiber-start-123".to_string()),
("origin".to_string(), "gaas".to_string()),
("thread_source".to_string(), "client-supplied".to_string()),
]);
let turn_req = mcp
.send_turn_start_request(TurnStartParams {
@@ -110,7 +112,7 @@ async fn turn_start_forwards_client_metadata_to_responses_request_v2() -> Result
.expect("x-codex-turn-metadata header should be present");
assert_eq!(metadata["fiber_run_id"].as_str(), Some("fiber-start-123"));
assert_eq!(metadata["origin"].as_str(), Some("gaas"));
assert_eq!(metadata["thread_source"].as_str(), Some("client-supplied"));
assert_eq!(metadata["thread_source"].as_str(), Some("automation"));
assert_eq!(metadata["turn_id"].as_str(), Some(turn.id.as_str()));
assert!(metadata.get("installation_id").is_some());
assert!(metadata.get("session_id").is_some());
@@ -300,7 +302,7 @@ async fn review_start_sends_parent_lineage_in_turn_metadata_for_thread_fork_v2()
}
#[tokio::test]
async fn turn_start_sends_other_subagent_lineage_after_cold_thread_resume_v2() -> Result<()> {
async fn turn_start_sends_nested_subagent_lineage_after_cold_thread_resume_v2() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = responses::start_mock_server().await;
@@ -321,6 +323,8 @@ async fn turn_start_sends_other_subagent_lineage_after_cold_thread_resume_v2() -
/*supports_websockets*/ false,
)?;
let root_thread_id = CoreThreadId::new();
let root_thread_id_str = root_thread_id.to_string();
let parent_thread_id = CoreThreadId::new();
let parent_thread_id_str = parent_thread_id.to_string();
let subagent_thread_id = create_fake_parented_rollout_with_source(
@@ -331,6 +335,7 @@ async fn turn_start_sends_other_subagent_lineage_after_cold_thread_resume_v2() -
Some("mock_provider"),
/*git_info*/ None,
SessionSource::SubAgent(SubAgentSource::Other("guardian".to_string())),
root_thread_id.into(),
parent_thread_id,
)?;
@@ -350,6 +355,7 @@ async fn turn_start_sends_other_subagent_lineage_after_cold_thread_resume_v2() -
.await??;
let ThreadResumeResponse { thread, .. } = to_response::<ThreadResumeResponse>(resume_resp)?;
assert_eq!(thread.id, subagent_thread_id);
assert_eq!(thread.session_id, root_thread_id_str);
assert_eq!(thread.parent_thread_id, Some(parent_thread_id_str.clone()));
assert_eq!(
thread.source,
@@ -390,6 +396,10 @@ async fn turn_start_sends_other_subagent_lineage_after_cold_thread_resume_v2() -
Some(parent_thread_id_str.as_str())
);
assert_eq!(metadata["subagent_kind"].as_str(), Some("guardian"));
assert_eq!(
metadata["session_id"].as_str(),
Some(thread.session_id.as_str())
);
assert_eq!(metadata["thread_id"].as_str(), Some(thread.id.as_str()));
assert_eq!(metadata["turn_id"].as_str(), Some(turn.id.as_str()));
assert!(metadata.get("forked_from_thread_id").is_none());
@@ -553,7 +563,10 @@ async fn turn_start_forwards_client_metadata_to_responses_websocket_request_body
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request(ThreadStartParams {
thread_source: Some(ThreadSource::Feature("automation".to_string())),
..Default::default()
})
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
@@ -611,6 +624,7 @@ async fn turn_start_forwards_client_metadata_to_responses_websocket_request_body
.expect("websocket x-codex-turn-metadata client metadata should be present");
assert_eq!(metadata["fiber_run_id"].as_str(), Some("fiber-start-123"));
assert_eq!(metadata["origin"].as_str(), Some("gaas"));
assert_eq!(metadata["thread_source"].as_str(), Some("automation"));
assert_eq!(metadata["turn_id"].as_str(), Some(turn.id.as_str()));
assert!(metadata.get("session_id").is_some());
assert_eq!(
@@ -134,12 +134,12 @@ async fn auto_compaction_remote_emits_started_and_completed_items() -> Result<()
text: "REMOTE_COMPACT_SUMMARY".to_string(),
}],
phase: None,
metadata: None,
internal_chat_message_metadata_passthrough: None,
},
ResponseItem::Compaction {
id: None,
encrypted_content: "ENCRYPTED_COMPACTION_SUMMARY".to_string(),
metadata: None,
internal_chat_message_metadata_passthrough: None,
},
];
let compact_mock = responses::mount_compact_json_once(
@@ -38,6 +38,10 @@ use tempfile::TempDir;
use tokio::time::timeout;
use wiremock::MockServer;
const TINY_PNG_DATA_URL: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==";
const REMOTE_IMAGE_URL_ERROR: &str =
"remote image URLs are not supported; use an inline data URL instead";
// macOS and Windows Bazel CI can spend tens of seconds starting app-server
// subprocesses or processing test RPCs under load.
#[cfg(any(target_os = "macos", windows))]
@@ -550,15 +554,19 @@ async fn dynamic_tool_call_round_trip_sends_text_content_items_to_model() -> Res
Ok(())
}
/// Ensures dynamic tool call responses can include structured content items.
#[tokio::test]
async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<()> {
let call_id = "dyn-call-items-1";
struct PendingDynamicToolCall {
mcp: TestAppServer,
server: MockServer,
request_id: RequestId,
params: DynamicToolCallParams,
}
async fn start_function_dynamic_tool_call(call_id: &str) -> Result<PendingDynamicToolCall> {
let tool_name = "demo_tool";
let tool_args = json!({ "city": "Paris" });
let tool_call_arguments = serde_json::to_string(&tool_args)?;
let responses = vec![
let response_sequence = vec![
responses::sse(vec![
responses::ev_response_created("resp-1"),
responses::ev_function_call(call_id, tool_name, &tool_call_arguments),
@@ -566,7 +574,7 @@ async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<(
]),
create_final_assistant_message_sse_response("Done")?,
];
let server = create_mock_responses_server_sequence_unchecked(responses).await;
let server = create_mock_responses_server_sequence_unchecked(response_sequence).await;
let codex_home = TempDir::new()?;
create_config_toml(codex_home.path(), &server.uri())?;
@@ -630,27 +638,46 @@ async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<(
mcp.read_stream_until_request_message(),
)
.await??;
let (request_id, params) = match request {
let (request_id, actual_params) = match request {
ServerRequest::DynamicToolCall { request_id, params } => (request_id, params),
other => panic!("expected DynamicToolCall request, got {other:?}"),
};
let expected = DynamicToolCallParams {
let params = DynamicToolCallParams {
thread_id,
turn_id: turn_id.clone(),
turn_id,
call_id: call_id.to_string(),
namespace: None,
tool: tool_name.to_string(),
arguments: tool_args,
};
assert_eq!(params, expected);
assert_eq!(actual_params, params);
Ok(PendingDynamicToolCall {
mcp,
server,
request_id,
params,
})
}
/// Ensures dynamic tool call responses can include structured content items.
#[tokio::test]
async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<()> {
let call_id = "dyn-call-items-1";
let PendingDynamicToolCall {
mut mcp,
server,
request_id,
params,
} = start_function_dynamic_tool_call(call_id).await?;
let response_content_items = vec![
DynamicToolCallOutputContentItem::InputText {
text: "dynamic-ok".to_string(),
},
DynamicToolCallOutputContentItem::InputImage {
image_url: "data:image/png;base64,AAA".to_string(),
image_url: TINY_PNG_DATA_URL.to_string(),
},
];
let content_items = response_content_items
@@ -676,8 +703,8 @@ async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<(
.await?;
let completed = wait_for_dynamic_tool_completed(&mut mcp, call_id).await?;
assert_eq!(completed.thread_id, expected.thread_id.clone());
assert_eq!(completed.turn_id, turn_id);
assert_eq!(completed.thread_id, params.thread_id);
assert_eq!(completed.turn_id, params.turn_id);
let ThreadItem::DynamicToolCall {
status,
content_items: completed_content_items,
@@ -695,7 +722,7 @@ async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<(
text: "dynamic-ok".to_string(),
},
DynamicToolCallOutputContentItem::InputImage {
image_url: "data:image/png;base64,AAA".to_string(),
image_url: TINY_PNG_DATA_URL.to_string(),
},
])
);
@@ -721,7 +748,7 @@ async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<(
},
{
"type": "input_image",
"image_url": "data:image/png;base64,AAA",
"image_url": TINY_PNG_DATA_URL,
"detail": "high"
}
])
@@ -744,6 +771,62 @@ async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<(
Ok(())
}
#[tokio::test]
async fn dynamic_tool_remote_image_response_becomes_model_visible_error() -> Result<()> {
let call_id = "dyn-call-remote-image";
let PendingDynamicToolCall {
mut mcp,
server,
request_id,
params,
} = start_function_dynamic_tool_call(call_id).await?;
let response = DynamicToolCallResponse {
content_items: vec![DynamicToolCallOutputContentItem::InputImage {
image_url: "https://example.com/tool.png".to_string(),
}],
success: true,
};
mcp.send_response(request_id, serde_json::to_value(response)?)
.await?;
let completed = wait_for_dynamic_tool_completed(&mut mcp, call_id).await?;
assert_eq!(completed.thread_id, params.thread_id);
assert_eq!(completed.turn_id, params.turn_id);
let ThreadItem::DynamicToolCall {
status,
content_items,
success,
..
} = completed.item
else {
panic!("expected dynamic tool call item");
};
assert_eq!(status, DynamicToolCallStatus::Failed);
assert_eq!(
content_items,
Some(vec![DynamicToolCallOutputContentItem::InputText {
text: REMOTE_IMAGE_URL_ERROR.to_string(),
}])
);
assert_eq!(success, Some(false));
timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_notification_message("turn/completed"),
)
.await??;
let output = responses_bodies(&server)
.await?
.iter()
.find_map(|body| function_call_output_raw_output(body, call_id))
.context("expected function_call_output output in follow-up request")?;
assert_eq!(output, json!(REMOTE_IMAGE_URL_ERROR));
Ok(())
}
async fn responses_bodies(server: &MockServer) -> Result<Vec<Value>> {
let requests = server
.received_requests()
@@ -28,13 +28,13 @@ use wiremock::ResponseTemplate;
use wiremock::matchers::method;
use wiremock::matchers::path;
const RESULT: &str = "cG5n";
const RESULT: &str = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==";
const TINY_PNG_BYTES: &[u8] = &[
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0,
0, 0, 31, 21, 196, 137, 0, 0, 0, 11, 73, 68, 65, 84, 120, 156, 99, 96, 0, 2, 0, 0, 5, 0, 1,
122, 94, 171, 63, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,
0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65, 84, 120, 156, 99, 248, 207, 192, 240, 31, 0,
5, 0, 1, 255, 137, 153, 61, 29, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,
];
const TINY_PNG_DATA_URL: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR4nGNgAAIAAAUAAXpeqz8AAAAASUVORK5CYII=";
const TINY_PNG_DATA_URL: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==";
#[derive(Clone, Copy)]
enum ImagegenTestMode {
@@ -116,7 +116,7 @@ async fn standalone_image_generation_returns_saved_path_hint_to_model() -> Resul
assert_eq!(status, "completed");
assert_eq!(revised_prompt.as_deref(), Some("paint a blue whale"));
assert_eq!(result, RESULT);
assert_eq!(std::fs::read(&saved_path)?, b"png");
assert_eq!(std::fs::read(&saved_path)?, TINY_PNG_BYTES);
let requests = response_mock.requests();
assert_eq!(requests.len(), 2);
@@ -256,7 +256,7 @@ async fn standalone_image_edit_uses_attached_model_visible_image() -> Result<()>
#[tokio::test]
async fn standalone_image_edit_uses_recent_pathless_image() -> Result<()> {
let image_url = "https://example.com/reference.png";
let image_url = TINY_PNG_DATA_URL;
let edit_request = run_image_edit_test(|_| {
Ok((
json!({
@@ -52,6 +52,7 @@ mod remote_control;
mod remote_thread_store;
mod request_permissions;
mod request_user_input;
mod request_validation;
mod review;
mod safety_check_downgrade;
mod skills_list;
@@ -60,22 +60,27 @@ description = "Inspect without writes."
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_READ_ONLY.to_string(),
description: None,
allowed: true,
},
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_WORKSPACE.to_string(),
description: None,
allowed: true,
},
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS.to_string(),
description: None,
allowed: true,
},
PermissionProfileSummary {
id: "audit".to_string(),
description: Some("Inspect without writes.".to_string()),
allowed: true,
},
PermissionProfileSummary {
id: "dev".to_string(),
description: Some("Day-to-day coding work.".to_string()),
allowed: true,
},
],
next_cursor: None,
@@ -126,14 +131,17 @@ description = "Project-scoped profile."
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_READ_ONLY.to_string(),
description: None,
allowed: true,
},
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_WORKSPACE.to_string(),
description: None,
allowed: true,
},
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS.to_string(),
description: None,
allowed: true,
},
],
next_cursor: Some("3".to_string()),
@@ -155,6 +163,7 @@ description = "Project-scoped profile."
data: vec![PermissionProfileSummary {
id: "project".to_string(),
description: Some("Project-scoped profile.".to_string()),
allowed: true,
}],
next_cursor: None,
}
@@ -200,18 +209,22 @@ description = "Project-scoped profile."
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_READ_ONLY.to_string(),
description: None,
allowed: true,
},
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_WORKSPACE.to_string(),
description: None,
allowed: true,
},
PermissionProfileSummary {
id: BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS.to_string(),
description: None,
allowed: true,
},
PermissionProfileSummary {
id: "project".to_string(),
description: Some("Project-scoped profile.".to_string()),
allowed: true,
},
],
next_cursor: None,
@@ -753,7 +753,9 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab
composer_icon: None,
composer_icon_url: None,
logo: None,
logo_dark: None,
logo_url: None,
logo_url_dark: None,
screenshots: Vec::new(),
screenshot_urls: Vec::new(),
}),
@@ -1761,6 +1763,14 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
.respond_with(ResponseTemplate::new(200).set_body_string(empty_page_body))
.mount(&server)
.await;
Mock::given(method("GET"))
.and(path("/backend-api/plugins/featured"))
.and(query_param("platform", "codex"))
.respond_with(
ResponseTemplate::new(200).set_body_string(r#"["linear@openai-curated-remote"]"#),
)
.mount(&server)
.await;
let mut mcp = TestAppServer::new(codex_home.path()).await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -1854,7 +1864,10 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
cached_plugin_ids,
vec!["plugins~Plugin_00000000000000000000000000000000".to_string()]
);
assert_eq!(response.featured_plugin_ids, Vec::<String>::new());
assert_eq!(
response.featured_plugin_ids,
vec!["linear@openai-curated-remote".to_string()]
);
assert!(
!server
.received_requests()
@@ -162,7 +162,8 @@ apps = true
"short_description": "Example plugin",
"capabilities": [],
"default_prompt": "Use the legacy example prompt",
"default_prompts": []
"default_prompts": [],
"logo_url_dark": "https://example.com/example-plugin-dark.png"
},
"skills": [],
"mcp_servers": [
@@ -273,6 +274,15 @@ apps = true
.and_then(|interface| interface.default_prompt.clone()),
Some(vec!["Use the legacy example prompt".to_string()])
);
assert_eq!(
response
.plugin
.summary
.interface
.as_ref()
.and_then(|interface| interface.logo_url_dark.as_deref()),
Some("https://example.com/example-plugin-dark.png")
);
assert_eq!(
response.plugin.mcp_servers,
vec!["other-server".to_string()]
@@ -1330,6 +1340,7 @@ async fn plugin_read_returns_plugin_details_with_bundle_contents() -> Result<()>
"brandColor": "#3B82F6",
"composerIcon": "./assets/icon.png",
"logo": "./assets/logo.png",
"logoDark": "./assets/logo-dark.png",
"screenshots": ["./assets/screenshot1.png"]
}
}"##,
@@ -1508,6 +1519,18 @@ enabled = false
"Find my next action".to_string()
])
);
assert_eq!(
response
.plugin
.summary
.interface
.as_ref()
.and_then(|interface| interface.logo_dark.as_ref()),
Some(
&AbsolutePathBuf::try_from(plugin_root.join("assets/logo-dark.png"))
.expect("absolute dark logo path")
)
);
assert_eq!(
response.plugin.summary.keywords,
vec!["api-key".to_string(), "developer tools".to_string()]
@@ -1357,7 +1357,9 @@ fn expected_plugin_interface() -> PluginInterface {
composer_icon: None,
composer_icon_url: None,
logo: None,
logo_dark: None,
logo_url: None,
logo_url_dark: None,
screenshots: Vec::new(),
screenshot_urls: Vec::new(),
}
@@ -204,12 +204,11 @@ async fn consume_timeout_releases_account_auth_queue() -> Result<()> {
"rate limit reset consume timed out"
);
let account_error = read_error_response(&mut mcp, account_id).await?;
assert_eq!(account_error.error.code, INVALID_REQUEST_ERROR_CODE);
assert_eq!(
account_error.error.message,
"email and plan type are required for chatgpt authentication"
);
timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(account_id)),
)
.await??;
Ok(())
}
@@ -147,6 +147,7 @@ async fn thread_delete_with_non_local_thread_store_does_not_create_local_persist
let unloaded_thread_id = ThreadId::from_string(&Uuid::new_v4().to_string())?;
thread_store
.create_thread(StoreCreateThreadParams {
session_id: unloaded_thread_id.into(),
thread_id: unloaded_thread_id,
extra_config: None,
forked_from_id: None,
@@ -0,0 +1,110 @@
use anyhow::Result;
use app_test_support::TestAppServer;
use app_test_support::to_response;
use app_test_support::write_mock_responses_config_toml_with_chatgpt_base_url;
use codex_app_server_protocol::JSONRPCError;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::JSONRPCResponse;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::ThreadStartParams;
use codex_app_server_protocol::ThreadStartResponse;
use codex_protocol::models::FunctionCallOutputContentItem;
use codex_protocol::models::FunctionCallOutputPayload;
use codex_protocol::models::ImageDetail;
use codex_protocol::models::ResponseItem;
use pretty_assertions::assert_eq;
use serde_json::json;
use std::time::Duration;
use tempfile::TempDir;
use tokio::time::timeout;
const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(10);
const REMOTE_IMAGE_URL_ERROR: &str =
"remote image URLs are not supported; use an inline data URL instead";
#[tokio::test]
async fn request_handlers_reject_remote_image_urls() -> Result<()> {
let codex_home = TempDir::new()?;
write_mock_responses_config_toml_with_chatgpt_base_url(
codex_home.path(),
"http://localhost/unused",
"http://localhost/unused",
)?;
let mut mcp = TestAppServer::new(codex_home.path()).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_request_id = mcp
.send_thread_start_request(ThreadStartParams::default())
.await?;
let thread_response: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(thread_request_id)),
)
.await??;
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(thread_response)?;
let thread_id = thread.id;
let remote_tool_output = serde_json::to_value(ResponseItem::FunctionCallOutput {
id: None,
call_id: "call-1".to_string(),
output: FunctionCallOutputPayload::from_content_items(vec![
FunctionCallOutputContentItem::InputImage {
image_url: "https://example.com/tool.png".to_string(),
detail: Some(ImageDetail::High),
},
]),
internal_chat_message_metadata_passthrough: None,
})?;
let requests = [
(
"turn/start",
json!({
"threadId": thread_id,
"input": [{
"type": "image",
"url": "HTTP://example.com/start.png",
"detail": "high"
}]
}),
),
(
"turn/steer",
json!({
"threadId": thread_id,
"expectedTurnId": "turn-id",
"input": [{
"type": "image",
"url": "https://example.com/steer.png",
"detail": "high"
}]
}),
),
(
"thread/inject_items",
json!({
"threadId": thread_id,
"items": [remote_tool_output]
}),
),
];
for (method, params) in requests {
let request_id = mcp.send_raw_request(method, Some(params)).await?;
let actual: JSONRPCError = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_error_message(RequestId::Integer(request_id)),
)
.await??;
let expected = JSONRPCError {
id: RequestId::Integer(request_id),
error: JSONRPCErrorError {
code: -32600,
data: None,
message: REMOTE_IMAGE_URL_ERROR.to_string(),
},
};
assert_eq!(actual, expected, "unexpected response for {method}");
}
Ok(())
}
@@ -60,7 +60,7 @@ async fn thread_inject_items_adds_raw_response_items_to_thread_history() -> Resu
text: injected_text.to_string(),
}],
phase: None,
metadata: None,
internal_chat_message_metadata_passthrough: None,
};
let inject_req = mcp
@@ -86,7 +86,7 @@ async fn thread_inject_items_adds_raw_response_items_to_thread_history() -> Resu
resumed_history
.history
.iter()
.any(|item| matches!(item, RolloutItem::ResponseItem(response_item) if response_item == &injected_item)),
.any(|item| matches!(item, RolloutItem::ResponseItem(response_item) if responses::strip_metadata(response_item.clone()) == injected_item)),
"injected item should be persisted in rollout history"
);
@@ -198,7 +198,7 @@ async fn thread_inject_items_adds_raw_response_items_after_a_turn() -> Result<()
text: "Injected after first turn".to_string(),
}],
phase: None,
metadata: None,
internal_chat_message_metadata_passthrough: None,
};
let injected_value = serde_json::to_value(&injected_item)?;
@@ -1252,6 +1252,7 @@ async fn thread_list_filters_by_subagent_variant() -> Result<()> {
Some("mock_provider"),
/*git_info*/ None,
CoreSessionSource::SubAgent(SubAgentSource::Review),
parent_thread_id.into(),
parent_thread_id,
)?;
let compact_id = create_fake_rollout_with_source(
@@ -1357,6 +1357,7 @@ async fn seed_pathless_store_thread(
) -> Result<()> {
store
.create_thread(CreateThreadParams {
session_id: thread_id.into(),
thread_id,
extra_config: None,
forked_from_id: None,
@@ -2054,6 +2054,7 @@ stream_max_retries = 0
let rollout_dir = rollout_path.parent().expect("rollout parent directory");
std::fs::create_dir_all(rollout_dir)?;
let session_meta = SessionMeta {
session_id: conversation_id.into(),
id: conversation_id,
forked_from_id: None,
parent_thread_id: None,
@@ -2588,7 +2589,7 @@ async fn thread_resume_rejects_history_when_thread_is_running() -> Result<()> {
text: "history override".to_string(),
}],
phase: None,
metadata: None,
internal_chat_message_metadata_passthrough: None,
}]),
..Default::default()
})
@@ -2735,6 +2736,7 @@ async fn thread_resume_rejects_mismatched_path_for_running_thread_id() -> Result
"timestamp": "2025-01-01T00:00:00Z",
"type": "session_meta",
"payload": {
"session_id": thread_uuid,
"id": thread_uuid,
"timestamp": "2025-01-01T00:00:00Z",
"cwd": codex_home.path(),
@@ -3596,7 +3598,7 @@ async fn thread_resume_supports_history_and_overrides() -> Result<()> {
text: history_text.to_string(),
}],
phase: None,
metadata: None,
internal_chat_message_metadata_passthrough: None,
}];
// Resume with explicit history and override the model.
@@ -117,10 +117,7 @@ async fn thread_settings_update_multi_agent_mode_applies_to_future_turns() -> Re
write_mock_responses_config_toml(
codex_home.path(),
&server.uri(),
&BTreeMap::from([
(Feature::MultiAgentV2, true),
(Feature::MultiAgentMode, true),
]),
&BTreeMap::from([(Feature::MultiAgentV2, true)]),
/*auto_compact_limit*/ 200_000,
/*requires_openai_auth*/ None,
"mock_provider",
@@ -158,7 +155,7 @@ async fn thread_settings_update_multi_agent_mode_applies_to_future_turns() -> Re
assert_eq!(updated.thread_id, thread.id);
assert_eq!(
updated.thread_settings.multi_agent_mode,
Some(MultiAgentMode::Proactive)
MultiAgentMode::Proactive
);
start_text_turn(&mut mcp, thread.id).await?;
@@ -208,6 +208,7 @@ async fn thread_unarchive_preserves_pathless_store_metadata() -> Result<()> {
let parent_thread_id = ThreadId::from_string("00000000-0000-4000-8000-000000000127")?;
store
.create_thread(CreateThreadParams {
session_id: thread_id.into(),
thread_id,
extra_config: None,
forked_from_id: Some(parent_thread_id),

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