From b200dd1b6f3492a122a7f45d4130ec80d372c745 Mon Sep 17 00:00:00 2001 From: Michael Zeng Date: Sat, 16 May 2026 12:48:28 -0700 Subject: [PATCH 1/5] exec-server: support auth-backed remote executor registration (#22769) This updates remote `exec-server` registration to use normal Codex auth instead of a registry-issued credential. The registry request is built from the existing auth-provider path, which preserves the biscuit-only registry contract introduced in [openai/openai#924101](https://github.com/openai/openai/pull/924101) while removing the old remote registry bearer env var and its direct transport assumptions. The default remote flow uses persisted ChatGPT auth from the normal Codex config/storage path. This PR also includes the containerized Agent Identity path needed by [openai/openai#924260](https://github.com/openai/openai/pull/924260): remote `exec-server` accepts `--allow-agent-identity-auth`, permits Agent Identity auth loaded from `CODEX_ACCESS_TOKEN` only when that flag is present, and reuses the existing Agent task registration plus derived `AgentAssertion` header generation. API-key auth remains unsupported, and Agent Identity stays opt-in. Validation performed beyond normal presubmit coverage: - `cargo fmt --all --check` - `cargo check -p codex-cli` - `cargo test -p codex-exec-server` - `cargo test -p codex-cli exec_server_agent_identity_auth_flag_` - `cargo test -p codex-cli remote_exec_server_auth_mode_` I also attempted `cargo test -p codex-cli`. The new CLI tests passed inside that run, but the suite ended on an unrelated local marketplace-state failure in `plugin_list_excludes_unconfigured_repo_local_marketplaces`. --- codex-rs/Cargo.lock | 2 + codex-rs/cli/src/main.rs | 93 ++++++++++++++++++++++++- codex-rs/exec-server/Cargo.toml | 2 + codex-rs/exec-server/README.md | 10 ++- codex-rs/exec-server/src/lib.rs | 1 - codex-rs/exec-server/src/remote.rs | 103 +++++++++++++--------------- codex-rs/exec-server/tests/relay.rs | 24 ++++++- 7 files changed, 173 insertions(+), 62 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index eb2439401b..83ca0d1191 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2748,6 +2748,7 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", + "codex-api", "codex-app-server-protocol", "codex-client", "codex-file-system", @@ -2759,6 +2760,7 @@ dependencies = [ "codex-utils-rustls-provider", "ctor 0.6.3", "futures", + "http 1.4.0", "pretty_assertions", "prost 0.14.3", "reqwest", diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 7ffdff0011..6c79c34c99 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -73,6 +73,8 @@ use codex_features::FEATURES; use codex_features::Stage; use codex_features::is_known_feature_key; use codex_login::AuthManager; +use codex_login::CodexAuth; +use codex_login::read_codex_access_token_from_env; use codex_memories_write::clear_memory_roots_contents; use codex_models_manager::bundled_models_response; use codex_models_manager::manager::RefreshStrategy; @@ -489,6 +491,10 @@ struct ExecServerCommand { /// Human-readable executor name. #[arg(long = "name", value_name = "NAME")] name: Option, + + /// Use Agent Identity auth from CODEX_ACCESS_TOKEN for remote registration. + #[arg(long = "use-agent-identity-auth", requires = "remote")] + use_agent_identity_auth: bool, } #[derive(Debug, clap::Subcommand)] @@ -1388,7 +1394,13 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> { root_remote_auth_token_env.as_deref(), "exec-server", )?; - run_exec_server_command(cmd, &arg0_paths).await?; + run_exec_server_command( + cmd, + &arg0_paths, + &root_config_overrides, + interactive.config_profile.clone(), + ) + .await?; } Some(Subcommand::Features(FeaturesCli { sub })) => match sub { FeaturesSubcommand::List => { @@ -1485,6 +1497,8 @@ fn profile_v2_for_subcommand<'a>( async fn run_exec_server_command( cmd: ExecServerCommand, arg0_paths: &Arg0DispatchPaths, + root_config_overrides: &CliConfigOverrides, + config_profile: Option, ) -> anyhow::Result<()> { let codex_self_exe = arg0_paths .codex_self_exe @@ -1498,8 +1512,14 @@ async fn run_exec_server_command( let executor_id = cmd .executor_id .ok_or_else(|| anyhow::anyhow!("--executor-id is required when --remote is set"))?; + let auth_provider = load_exec_server_remote_auth_provider( + root_config_overrides, + config_profile, + cmd.use_agent_identity_auth, + ) + .await?; let mut remote_config = - codex_exec_server::RemoteExecutorConfig::new(base_url, executor_id)?; + codex_exec_server::RemoteExecutorConfig::new(base_url, executor_id, auth_provider)?; if let Some(name) = cmd.name { remote_config.name = name; } @@ -1515,6 +1535,75 @@ async fn run_exec_server_command( .map_err(anyhow::Error::from_boxed) } +async fn load_exec_server_remote_auth_provider( + root_config_overrides: &CliConfigOverrides, + config_profile: Option, + use_agent_identity_auth: bool, +) -> anyhow::Result { + let config = load_exec_server_remote_config(root_config_overrides, config_profile).await?; + if use_agent_identity_auth { + let agent_identity_jwt = read_codex_access_token_from_env().ok_or_else(|| { + anyhow::anyhow!("CODEX_ACCESS_TOKEN is required when --use-agent-identity-auth is set") + })?; + let auth = + CodexAuth::from_agent_identity_jwt(&agent_identity_jwt, Some(&config.chatgpt_base_url)) + .await?; + return Ok(codex_model_provider::auth_provider_from_auth(&auth)); + } + + let auth = load_exec_server_remote_auth( + &config, + "remote exec-server registration requires ChatGPT authentication; run `codex login` first", + ) + .await?; + + if !auth.is_chatgpt_auth() { + anyhow::bail!( + "remote exec-server registration requires ChatGPT authentication; API key and Agent Identity auth are not supported" + ); + } + + Ok(codex_model_provider::auth_provider_from_auth(&auth)) +} + +async fn load_exec_server_remote_config( + root_config_overrides: &CliConfigOverrides, + config_profile: Option, +) -> anyhow::Result { + let cli_kv_overrides = root_config_overrides + .parse_overrides() + .map_err(anyhow::Error::msg)?; + Ok(ConfigBuilder::default() + .cli_overrides(cli_kv_overrides) + .harness_overrides(ConfigOverrides { + config_profile, + ..Default::default() + }) + .build() + .await?) +} + +async fn load_exec_server_remote_auth( + config: &codex_core::config::Config, + missing_auth_error: &'static str, +) -> anyhow::Result { + let auth_manager = + AuthManager::shared_from_config(config, /*enable_codex_api_key_env*/ true).await; + + let auth = match auth_manager.auth().await { + Some(auth) => auth, + None => { + auth_manager.reload().await; + auth_manager + .auth() + .await + .ok_or_else(|| anyhow::anyhow!(missing_auth_error))? + } + }; + + Ok(auth) +} + async fn enable_feature_in_config(interactive: &TuiCli, feature: &str) -> anyhow::Result<()> { FeatureToggles::validate_feature(feature)?; let codex_home = find_codex_home()?; diff --git a/codex-rs/exec-server/Cargo.toml b/codex-rs/exec-server/Cargo.toml index 09a9a71ea0..d842094a16 100644 --- a/codex-rs/exec-server/Cargo.toml +++ b/codex-rs/exec-server/Cargo.toml @@ -17,6 +17,7 @@ axum = { workspace = true, features = ["http1", "tokio", "ws"] } base64 = { workspace = true } bytes = { workspace = true } codex-app-server-protocol = { workspace = true } +codex-api = { workspace = true } codex-client = { workspace = true } codex-file-system = { workspace = true } codex-protocol = { workspace = true } @@ -51,6 +52,7 @@ uuid = { workspace = true, features = ["v4"] } anyhow = { workspace = true } codex-test-binary-support = { workspace = true } ctor = { workspace = true } +http = { workspace = true } pretty_assertions = { workspace = true } serial_test = { workspace = true } tempfile = { workspace = true } diff --git a/codex-rs/exec-server/README.md b/codex-rs/exec-server/README.md index 1eaf6e69eb..01439108f8 100644 --- a/codex-rs/exec-server/README.md +++ b/codex-rs/exec-server/README.md @@ -26,7 +26,11 @@ The CLI entrypoint supports: Remote mode registers the local exec-server with the executor registry, then reconnects to the service-provided rendezvous websocket as the executor. -It requires a bearer token in `CODEX_EXEC_SERVER_REMOTE_BEARER_TOKEN`. +It uses the standard Codex ChatGPT sign-in state; run `codex login` first when +remote registration needs authentication. Containerized callers that receive an +Agent Identity JWT in `CODEX_ACCESS_TOKEN` can opt into that auth path with +`--use-agent-identity-auth`; Codex then registers an Agent task and sends the +derived AgentAssertion headers on the registry request. Wire framing: @@ -376,7 +380,9 @@ The crate exports: Callers must pass `ExecServerRuntimePaths` to `run_main()`. The top-level `codex exec-server` command builds these paths from the `codex` arg0 dispatch -state. +state. `RemoteExecutorConfig::new(...)` also takes the auth provider that +remote registration should use; the CLI builds that provider from Codex auth +state before starting remote mode. ## Example session diff --git a/codex-rs/exec-server/src/lib.rs b/codex-rs/exec-server/src/lib.rs index 872f16ce32..bd556638ea 100644 --- a/codex-rs/exec-server/src/lib.rs +++ b/codex-rs/exec-server/src/lib.rs @@ -91,7 +91,6 @@ pub use protocol::TerminateResponse; pub use protocol::WriteParams; pub use protocol::WriteResponse; pub use protocol::WriteStatus; -pub use remote::CODEX_EXEC_SERVER_REMOTE_BEARER_TOKEN_ENV_VAR; pub use remote::RemoteExecutorConfig; pub use remote::run_remote_executor; pub use runtime_paths::ExecServerRuntimePaths; diff --git a/codex-rs/exec-server/src/remote.rs b/codex-rs/exec-server/src/remote.rs index 32c0d5bc8e..2493135c9a 100644 --- a/codex-rs/exec-server/src/remote.rs +++ b/codex-rs/exec-server/src/remote.rs @@ -1,6 +1,6 @@ -use std::env; use std::time::Duration; +use codex_api::SharedAuthProvider; use reqwest::StatusCode; use serde::Deserialize; use tokio::time::sleep; @@ -14,15 +14,12 @@ use crate::ExecServerRuntimePaths; use crate::relay::run_multiplexed_executor; use crate::server::ConnectionProcessor; -pub const CODEX_EXEC_SERVER_REMOTE_BEARER_TOKEN_ENV_VAR: &str = - "CODEX_EXEC_SERVER_REMOTE_BEARER_TOKEN"; - const ERROR_BODY_PREVIEW_BYTES: usize = 4096; #[derive(Clone)] struct ExecutorRegistryClient { base_url: String, - bearer_token: String, + auth_provider: SharedAuthProvider, http: reqwest::Client, } @@ -30,17 +27,17 @@ impl std::fmt::Debug for ExecutorRegistryClient { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ExecutorRegistryClient") .field("base_url", &self.base_url) - .field("bearer_token", &"") + .field("auth_provider", &"") .finish_non_exhaustive() } } impl ExecutorRegistryClient { - fn new(base_url: String, bearer_token: String) -> Result { + fn new(base_url: String, auth_provider: SharedAuthProvider) -> Result { let base_url = normalize_base_url(base_url)?; Ok(Self { base_url, - bearer_token, + auth_provider, http: reqwest::Client::new(), }) } @@ -55,7 +52,7 @@ impl ExecutorRegistryClient { &self.base_url, &format!("/cloud/executor/{executor_id}/register"), )) - .bearer_auth(&self.bearer_token) + .headers(self.auth_provider.to_auth_headers()) .send() .await?; self.parse_json_response(response).await @@ -89,12 +86,12 @@ struct ExecutorRegistryExecutorRegistrationResponse { } /// Configuration for registering an exec-server for remote use. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone)] pub struct RemoteExecutorConfig { pub base_url: String, pub executor_id: String, pub name: String, - bearer_token: String, + auth_provider: SharedAuthProvider, } impl std::fmt::Debug for RemoteExecutorConfig { @@ -103,28 +100,23 @@ impl std::fmt::Debug for RemoteExecutorConfig { .field("base_url", &self.base_url) .field("executor_id", &self.executor_id) .field("name", &self.name) - .field("bearer_token", &"") + .field("auth_provider", &"") .finish() } } impl RemoteExecutorConfig { - pub fn new(base_url: String, executor_id: String) -> Result { - Self::with_bearer_token(base_url, executor_id, read_remote_bearer_token_from_env()?) - } - - pub fn with_bearer_token( + pub fn new( base_url: String, executor_id: String, - bearer_token: String, + auth_provider: SharedAuthProvider, ) -> Result { let executor_id = normalize_executor_id(executor_id)?; - let bearer_token = normalize_bearer_token(bearer_token)?; Ok(Self { base_url, executor_id, name: "codex-exec-server".to_string(), - bearer_token, + auth_provider, }) } } @@ -136,7 +128,8 @@ pub async fn run_remote_executor( runtime_paths: ExecServerRuntimePaths, ) -> Result<(), ExecServerError> { ensure_rustls_crypto_provider(); - let client = ExecutorRegistryClient::new(config.base_url.clone(), config.bearer_token.clone())?; + let client = + ExecutorRegistryClient::new(config.base_url.clone(), config.auth_provider.clone())?; let processor = ConnectionProcessor::new(runtime_paths); let mut backoff = Duration::from_secs(1); @@ -162,32 +155,6 @@ pub async fn run_remote_executor( } } -fn read_remote_bearer_token_from_env() -> Result { - read_remote_bearer_token_from_env_with(|name| env::var(name)) -} - -fn read_remote_bearer_token_from_env_with(get_var: F) -> Result -where - F: FnOnce(&str) -> Result, -{ - let bearer_token = get_var(CODEX_EXEC_SERVER_REMOTE_BEARER_TOKEN_ENV_VAR).map_err(|_| { - ExecServerError::ExecutorRegistryAuth(format!( - "executor registry bearer token environment variable `{CODEX_EXEC_SERVER_REMOTE_BEARER_TOKEN_ENV_VAR}` is not set" - )) - })?; - normalize_bearer_token(bearer_token) -} - -fn normalize_bearer_token(bearer_token: String) -> Result { - let bearer_token = bearer_token.trim().to_string(); - if bearer_token.is_empty() { - return Err(ExecServerError::ExecutorRegistryAuth(format!( - "executor registry bearer token environment variable `{CODEX_EXEC_SERVER_REMOTE_BEARER_TOKEN_ENV_VAR}` is empty" - ))); - } - Ok(bearer_token) -} - fn normalize_executor_id(executor_id: String) -> Result { let executor_id = executor_id.trim().to_string(); if executor_id.is_empty() { @@ -274,6 +241,11 @@ fn preview_error_body(body: &str) -> Option { #[cfg(test)] mod tests { + use std::sync::Arc; + + use codex_api::AuthProvider; + use http::HeaderMap; + use http::HeaderValue; use pretty_assertions::assert_eq; use wiremock::Mock; use wiremock::MockServer; @@ -284,25 +256,46 @@ mod tests { use super::*; + #[derive(Debug)] + struct StaticRegistryAuthProvider; + + impl AuthProvider for StaticRegistryAuthProvider { + fn add_auth_headers(&self, headers: &mut HeaderMap) { + let _ = headers.insert( + http::header::AUTHORIZATION, + HeaderValue::from_static("Bearer registry-token"), + ); + let _ = headers.insert( + "ChatGPT-Account-ID", + HeaderValue::from_static("workspace-123"), + ); + } + } + + fn static_registry_auth_provider() -> SharedAuthProvider { + Arc::new(StaticRegistryAuthProvider) + } + #[tokio::test] - async fn register_executor_posts_with_bearer_token_header() { + async fn register_executor_posts_with_auth_provider_headers() { let server = MockServer::start().await; - let config = RemoteExecutorConfig::with_bearer_token( + let config = RemoteExecutorConfig::new( server.uri(), "exec-requested".to_string(), - "registry-token".to_string(), + static_registry_auth_provider(), ) .expect("config"); Mock::given(method("POST")) .and(path("/cloud/executor/exec-requested/register")) .and(header("authorization", "Bearer registry-token")) + .and(header("chatgpt-account-id", "workspace-123")) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "executor_id": "exec-1", "url": "wss://rendezvous.test/executor/exec-1?role=executor&sig=abc" }))) .mount(&server) .await; - let client = ExecutorRegistryClient::new(server.uri(), "registry-token".to_string()) + let client = ExecutorRegistryClient::new(server.uri(), static_registry_auth_provider()) .expect("client"); let response = client @@ -320,17 +313,17 @@ mod tests { } #[test] - fn debug_output_redacts_bearer_token() { - let config = RemoteExecutorConfig::with_bearer_token( + fn debug_output_redacts_auth_provider() { + let config = RemoteExecutorConfig::new( "https://registry.example".to_string(), "exec-1".to_string(), - "secret-token".to_string(), + static_registry_auth_provider(), ) .expect("config"); let debug = format!("{config:?}"); assert!(debug.contains("")); - assert!(!debug.contains("secret-token")); + assert!(!debug.contains("workspace-123")); } } diff --git a/codex-rs/exec-server/tests/relay.rs b/codex-rs/exec-server/tests/relay.rs index d228db9ee5..9f985e6982 100644 --- a/codex-rs/exec-server/tests/relay.rs +++ b/codex-rs/exec-server/tests/relay.rs @@ -10,6 +10,7 @@ use anyhow::Context; use anyhow::Result; use anyhow::anyhow; use anyhow::bail; +use codex_api::AuthProvider; use codex_app_server_protocol::JSONRPCError; use codex_app_server_protocol::JSONRPCMessage; use codex_app_server_protocol::JSONRPCNotification; @@ -22,12 +23,15 @@ use codex_exec_server::InitializeResponse; use codex_exec_server::RemoteExecutorConfig; use futures::SinkExt; use futures::StreamExt; +use http::HeaderMap; +use http::HeaderValue; use pretty_assertions::assert_eq; use prost::Message as ProstMessage; use relay_proto::RelayData; use relay_proto::RelayMessageFrame; use relay_proto::RelayReset; use relay_proto::relay_message_frame; +use std::sync::Arc; use tokio::net::TcpListener; use tokio::time::timeout; use tokio_tungstenite::WebSocketStream; @@ -46,6 +50,22 @@ const REGISTRY_TOKEN: &str = "registry-token"; const RELAY_MESSAGE_FRAME_VERSION: u32 = 1; const TEST_TIMEOUT: Duration = Duration::from_secs(5); +#[derive(Debug)] +struct StaticRegistryAuthProvider; + +impl AuthProvider for StaticRegistryAuthProvider { + fn add_auth_headers(&self, headers: &mut HeaderMap) { + let _ = headers.insert( + http::header::AUTHORIZATION, + HeaderValue::from_static("Bearer registry-token"), + ); + } +} + +fn static_registry_auth_provider() -> codex_api::SharedAuthProvider { + Arc::new(StaticRegistryAuthProvider) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn multiplexed_remote_executor_routes_independent_virtual_streams() -> Result<()> { let listener = TcpListener::bind("127.0.0.1:0").await?; @@ -63,10 +83,10 @@ async fn multiplexed_remote_executor_routes_independent_virtual_streams() -> Res let (codex_exe, codex_linux_sandbox_exe) = common::current_test_binary_helper_paths()?; let runtime_paths = ExecServerRuntimePaths::new(codex_exe, codex_linux_sandbox_exe)?; - let config = RemoteExecutorConfig::with_bearer_token( + let config = RemoteExecutorConfig::new( registry.uri(), EXECUTOR_ID.to_string(), - REGISTRY_TOKEN.to_string(), + static_registry_auth_provider(), )?; let remote_executor = tokio::spawn(codex_exec_server::run_remote_executor( config, From 6941f5c2c5a17a6f61bfbc9f4c96cb0c81401623 Mon Sep 17 00:00:00 2001 From: Miaolin Min Date: Sat, 16 May 2026 13:27:44 -0700 Subject: [PATCH 2/5] [codex] preserve MCP result meta in McpToolCallItemResult (#22946) ## Summary https://openai.slack.com/archives/C0ARA9UAQEA/p1778890981647319?thread_ts=1778888537.934319&cid=C0ARA9UAQEA - Add `_meta` to exec JSONL MCP tool call result events. - Copy MCP result metadata through the JSONL event conversion. - Add a focused test that verifies `_meta` is serialized as `_meta` and not `meta`. ## Verification https://www.notion.so/openai/Miaolin-0516-_meta-population-debug-3628e50b62b08074b365e0ce1ffb8f74 --- .../src/event_processor_with_jsonl_output.rs | 1 + ...event_processor_with_jsonl_output_tests.rs | 51 +++++++++++++++++++ codex-rs/exec/src/exec_events.rs | 3 ++ .../tests/event_processor_with_json_output.rs | 2 + sdk/typescript/src/items.ts | 1 + 5 files changed, 58 insertions(+) diff --git a/codex-rs/exec/src/event_processor_with_jsonl_output.rs b/codex-rs/exec/src/event_processor_with_jsonl_output.rs index 045baacc7b..89e41eacb1 100644 --- a/codex-rs/exec/src/event_processor_with_jsonl_output.rs +++ b/codex-rs/exec/src/event_processor_with_jsonl_output.rs @@ -223,6 +223,7 @@ impl EventProcessorWithJsonOutput { arguments, result: result.map(|result| McpToolCallItemResult { content: result.content, + meta: result.meta, structured_content: result.structured_content, }), error: error.map(|error| McpToolCallItemError { diff --git a/codex-rs/exec/src/event_processor_with_jsonl_output_tests.rs b/codex-rs/exec/src/event_processor_with_jsonl_output_tests.rs index 88fd042f7c..6bd358f6be 100644 --- a/codex-rs/exec/src/event_processor_with_jsonl_output_tests.rs +++ b/codex-rs/exec/src/event_processor_with_jsonl_output_tests.rs @@ -1,5 +1,6 @@ use super::*; use pretty_assertions::assert_eq; +use serde_json::json; use tempfile::tempdir; #[test] @@ -57,3 +58,53 @@ fn failed_turn_does_not_overwrite_output_last_message_file() { "keep existing contents" ); } + +#[test] +fn mcp_tool_call_result_preserves_meta_in_jsonl_event() { + let mut processor = EventProcessorWithJsonOutput::new(/*last_message_path*/ None); + + let collected = processor.collect_thread_events(ServerNotification::ItemCompleted( + codex_app_server_protocol::ItemCompletedNotification { + item: ThreadItem::McpToolCall { + id: "mcp-1".to_string(), + server: "search service".to_string(), + tool: "web_run".to_string(), + status: McpToolCallStatus::Completed, + arguments: json!({"search_query": [{"q": "OpenAI Codex CLI documentation"}]}), + mcp_app_resource_uri: None, + result: Some(Box::new(codex_app_server_protocol::McpToolCallResult { + content: vec![json!({"type": "text", "text": "search result"})], + structured_content: None, + meta: Some(json!({"raw_messages": [{"ref_id": "turn0search0"}]})), + })), + error: None, + duration_ms: Some(42), + }, + thread_id: "thread-1".to_string(), + turn_id: "turn-1".to_string(), + completed_at_ms: 0, + }, + )); + + assert_eq!(collected.status, CodexStatus::Running); + assert_eq!(collected.events.len(), 1); + + let ThreadEvent::ItemCompleted(ItemCompletedEvent { item }) = &collected.events[0] else { + panic!("expected item.completed event"); + }; + let ThreadItemDetails::McpToolCall(item) = &item.details else { + panic!("expected MCP tool call item"); + }; + let result = item.result.as_ref().expect("expected MCP tool result"); + assert_eq!( + result.meta, + Some(json!({"raw_messages": [{"ref_id": "turn0search0"}]})) + ); + + let serialized = serde_json::to_value(&collected.events[0]).expect("serialize event"); + assert_eq!( + serialized["item"]["result"]["_meta"], + json!({"raw_messages": [{"ref_id": "turn0search0"}]}) + ); + assert!(serialized["item"]["result"].get("meta").is_none()); +} diff --git a/codex-rs/exec/src/exec_events.rs b/codex-rs/exec/src/exec_events.rs index 4a84ef7494..078a876ef4 100644 --- a/codex-rs/exec/src/exec_events.rs +++ b/codex-rs/exec/src/exec_events.rs @@ -266,6 +266,9 @@ pub struct McpToolCallItemResult { // representations). Using `JsonValue` keeps the payload wire-shaped and // easy to export. pub content: Vec, + #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub meta: Option, pub structured_content: Option, } diff --git a/codex-rs/exec/tests/event_processor_with_json_output.rs b/codex-rs/exec/tests/event_processor_with_json_output.rs index efda58f412..acbac8254c 100644 --- a/codex-rs/exec/tests/event_processor_with_json_output.rs +++ b/codex-rs/exec/tests/event_processor_with_json_output.rs @@ -540,6 +540,7 @@ fn mcp_tool_call_begin_and_end_emit_item_events() { arguments: json!({ "key": "value" }), result: Some(McpToolCallItemResult { content: Vec::new(), + meta: None, structured_content: None, }), error: None, @@ -681,6 +682,7 @@ fn mcp_tool_call_defaults_arguments_and_preserves_structured_content() { "type": "text", "text": "done", })], + meta: None, structured_content: Some(json!({ "status": "ok" })), }), error: None, diff --git a/sdk/typescript/src/items.ts b/sdk/typescript/src/items.ts index 182878f55b..8fd3b2c7ff 100644 --- a/sdk/typescript/src/items.ts +++ b/sdk/typescript/src/items.ts @@ -60,6 +60,7 @@ export type McpToolCallItem = { /** Result payload returned by the MCP server for successful calls. */ result?: { content: McpContentBlock[]; + _meta?: unknown; structured_content: unknown; }; /** Error message reported for failed calls. */ From 061a614d857e0f2289457770e90caf8a45b5e430 Mon Sep 17 00:00:00 2001 From: sayan-oai Date: Sat, 16 May 2026 13:43:30 -0700 Subject: [PATCH 3/5] multiagent: trim model-visible description, cap to 5 models (#23069) ## Why The `spawn_agent` model override guidance is uncapped and bloating context. We need to trim down each entry and cap total entries. picked 5 as cap, we can change ## What changed - Cap the model override summaries shown in `spawn_agent` to the first 5 picker-visible models, preserving the existing priority ordering from the models manager. - Condense each rendered entry to the actionable pieces the model needs: - use the model slug as the label - render compact reasoning effort lists with the default marked inline - render only service tier IDs, and omit the clause when no tiers are available - Update coverage so the compact formatter shape and the top-5 cap are exercised, and keep the end-to-end request assertion aligned with real model metadata. ## Example Before: `- gpt-5.4 ('gpt-5.4\'): Strong model for everyday coding. Default reasoning effort: medium. Supported reasoning efforts: low (Fast responses with lighter reasoning), medium (Balances speed and reasoning depth for everyday tasks), high (Greater reasoning depth for complex problems), xhigh (Extra high reasoning depth for complex problems). Supported service tiers: priority (Fast: 1.5x speed, increased usage).` After: `- 'gpt-5.4': Strong model for everyday coding. Reasoning efforts: low, medium (default), high, xhigh. Service tiers: priority.` --- .../src/tools/handlers/multi_agents_spec.rs | 50 ++++++++++++------- .../tools/handlers/multi_agents_spec_tests.rs | 41 ++++++++++++--- .../tests/suite/spawn_agent_description.rs | 17 +++---- 3 files changed, 75 insertions(+), 33 deletions(-) diff --git a/codex-rs/core/src/tools/handlers/multi_agents_spec.rs b/codex-rs/core/src/tools/handlers/multi_agents_spec.rs index 111ab67798..e1295b50de 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_spec.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_spec.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; const SPAWN_AGENT_INHERITED_MODEL_GUIDANCE: &str = "Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed."; const SPAWN_AGENT_MODEL_OVERRIDE_DESCRIPTION: &str = "Optional model override for the new agent. Leave unset to inherit the same model as the parent, which is the preferred default. Only set this when the user explicitly asks for a different model or the task clearly requires one."; const SPAWN_AGENT_SERVICE_TIER_OVERRIDE_DESCRIPTION: &str = "Optional service tier override for the new agent. Leave unset unless the user explicitly asks for one."; +const MAX_MODEL_OVERRIDES_IN_SPAWN_AGENT_DESCRIPTION: usize = 5; #[derive(Debug, Clone, Default)] pub struct SpawnAgentToolOptions { @@ -711,8 +712,11 @@ The new agent's canonical task name will be provided to it along with the messag } fn spawn_agent_models_description(models: &[ModelPreset]) -> String { - let visible_models: Vec<&ModelPreset> = - models.iter().filter(|model| model.show_in_picker).collect(); + let visible_models: Vec<&ModelPreset> = models + .iter() + .filter(|model| model.show_in_picker) + .take(MAX_MODEL_OVERRIDES_IN_SPAWN_AGENT_DESCRIPTION) + .collect(); if visible_models.is_empty() { return "No picker-visible model overrides are currently loaded.".to_string(); } @@ -720,30 +724,40 @@ fn spawn_agent_models_description(models: &[ModelPreset]) -> String { let model_descriptions = visible_models .into_iter() .map(|model| { + let default_reasoning_effort = model.default_reasoning_effort; let efforts = model .supported_reasoning_efforts .iter() - .map(|preset| format!("{} ({})", preset.effort, preset.description)) + .map(|preset| { + let effort = preset.effort; + if effort == default_reasoning_effort { + format!("{effort} (default)") + } else { + effort.to_string() + } + }) .collect::>() .join(", "); - let service_tiers = if model.service_tiers.is_empty() { - "none".to_string() + let reasoning_efforts_suffix = if efforts.is_empty() { + String::new() } else { - model - .service_tiers - .iter() - .map(|tier| format!("{} ({}: {})", tier.id, tier.name, tier.description)) - .collect::>() - .join(", ") + format!(" Reasoning efforts: {efforts}.") }; + let service_tiers = model + .service_tiers + .iter() + .map(|tier| tier.id.as_str()) + .collect::>() + .join(", "); + let service_tiers_suffix = if service_tiers.is_empty() { + String::new() + } else { + format!(" Service tiers: {service_tiers}.") + }; + let model_slug = &model.model; + let description = &model.description; format!( - "- {} (`{}`): {} Default reasoning effort: {}. Supported reasoning efforts: {}. Supported service tiers: {}.", - model.display_name, - model.model, - model.description, - model.default_reasoning_effort, - efforts, - service_tiers + "- `{model_slug}`: {description}{reasoning_efforts_suffix}{service_tiers_suffix}" ) }) .collect::>() diff --git a/codex-rs/core/src/tools/handlers/multi_agents_spec_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_spec_tests.rs index aaa29d6d4c..711db2fd11 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_spec_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_spec_tests.rs @@ -74,12 +74,10 @@ fn spawn_agent_tool_v2_requires_task_name_and_lists_visible_models() { description .contains("Available model overrides (optional; inherited parent model is preferred):") ); - assert!(description.contains("visible display (`visible-model`)")); - assert!( - description - .contains("Supported service tiers: priority (Fast: 1.5x speed, increased usage).") - ); - assert!(!description.contains("hidden display (`hidden-model`)")); + assert!(description.contains( + "- `visible-model`: visible description Reasoning efforts: medium (default). Service tiers: priority." + )); + assert!(!description.contains("hidden-model")); assert!(properties.contains_key("task_name")); assert!(properties.contains_key("message")); assert!(properties.contains_key("fork_turns")); @@ -150,6 +148,37 @@ fn spawn_agent_tool_v1_keeps_legacy_fork_context_field() { ); } +#[test] +fn spawn_agent_tool_caps_visible_model_summaries() { + let tool = create_spawn_agent_tool_v2(SpawnAgentToolOptions { + available_models: vec![ + model_preset("first", /*show_in_picker*/ true), + model_preset("second", /*show_in_picker*/ true), + model_preset("third", /*show_in_picker*/ true), + model_preset("fourth", /*show_in_picker*/ true), + model_preset("fifth", /*show_in_picker*/ true), + model_preset("sixth", /*show_in_picker*/ true), + ], + agent_type_description: "role help".to_string(), + hide_agent_type_model_reasoning: false, + include_usage_hint: true, + usage_hint_text: None, + max_concurrent_threads_per_session: Some(4), + }); + + let ToolSpec::Function(ResponsesApiTool { description, .. }) = tool else { + panic!("spawn_agent should be a function tool"); + }; + + for model in ["first", "second", "third", "fourth", "fifth"] { + assert!( + description.contains(&format!("`{model}-model`")), + "expected {model} model summary in spawn_agent description: {description:?}" + ); + } + assert!(!description.contains("`sixth-model`")); +} + #[test] fn spawn_agent_tool_hides_service_tier_with_spawn_metadata() { let tool = create_spawn_agent_tool_v2(SpawnAgentToolOptions { diff --git a/codex-rs/core/tests/suite/spawn_agent_description.rs b/codex-rs/core/tests/suite/spawn_agent_description.rs index 5aab77cd7d..c70f9bce37 100644 --- a/codex-rs/core/tests/suite/spawn_agent_description.rs +++ b/codex-rs/core/tests/suite/spawn_agent_description.rs @@ -123,6 +123,10 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() -> effort: ReasoningEffort::Low, description: "Quick scan".to_string(), }, + ReasoningEffortPreset { + effort: ReasoningEffort::Medium, + description: "Balanced".to_string(), + }, ReasoningEffortPreset { effort: ReasoningEffort::High, description: "Deep dive".to_string(), @@ -175,7 +179,7 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() -> spawn_agent_description(&body).expect("spawn_agent description should be present"); assert!( - description.contains("- Visible Model (`visible-model`): Fast and capable"), + description.contains("- `visible-model`: Fast and capable"), "expected visible model summary in spawn_agent description: {description:?}" ); assert!( @@ -196,20 +200,15 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() -> "expected model override usage guidance in spawn_agent description: {description:?}" ); assert!( - description.contains("Default reasoning effort: medium."), + description.contains("Reasoning efforts: low, medium (default), high."), "expected default reasoning effort in spawn_agent description: {description:?}" ); assert!( - description.contains("low (Quick scan), high (Deep dive)."), - "expected reasoning efforts in spawn_agent description: {description:?}" - ); - assert!( - description - .contains("Supported service tiers: priority (Fast: 1.5x speed, increased usage)."), + description.contains("Service tiers: priority."), "expected service tier guidance in spawn_agent description: {description:?}" ); assert!( - !description.contains("Hidden Model"), + !description.contains("hidden-model"), "hidden picker model should be omitted from spawn_agent description: {description:?}" ); assert!( From 3b1b54ace7c49e65c159eb19dc7039c96780c183 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sat, 16 May 2026 14:16:20 -0700 Subject: [PATCH 4/5] Add turn context to UserInput --- .../src/request_processors/turn_processor.rs | 1 + codex-rs/core/src/agent/control_tests.rs | 3 + codex-rs/core/src/codex_delegate.rs | 1 + codex-rs/core/src/session/handlers.rs | 69 +++++++++++++-- codex-rs/core/src/session/mod.rs | 1 + codex-rs/core/src/session/tests.rs | 6 ++ .../src/tools/handlers/multi_agents_tests.rs | 1 + codex-rs/core/tests/suite/abort_tasks.rs | 5 ++ codex-rs/core/tests/suite/client.rs | 32 +++++++ .../core/tests/suite/client_websockets.rs | 2 + .../tests/suite/collaboration_instructions.rs | 14 +++ codex-rs/core/tests/suite/compact.rs | 31 +++++++ codex-rs/core/tests/suite/compact_remote.rs | 53 +++++++++++ .../core/tests/suite/compact_remote_parity.rs | 1 + .../core/tests/suite/compact_resume_fork.rs | 1 + codex-rs/core/tests/suite/fork_thread.rs | 2 + codex-rs/core/tests/suite/hooks.rs | 2 + codex-rs/core/tests/suite/items.rs | 9 ++ .../core/tests/suite/model_visible_layout.rs | 3 + codex-rs/core/tests/suite/otel.rs | 22 +++++ codex-rs/core/tests/suite/pending_input.rs | 3 + .../core/tests/suite/permissions_messages.rs | 15 ++++ codex-rs/core/tests/suite/plugins.rs | 3 + codex-rs/core/tests/suite/prompt_caching.rs | 10 +++ codex-rs/core/tests/suite/quota_exceeded.rs | 1 + .../core/tests/suite/realtime_conversation.rs | 3 + .../core/tests/suite/request_compression.rs | 2 + codex-rs/core/tests/suite/resume.rs | 7 ++ codex-rs/core/tests/suite/review.rs | 1 + codex-rs/core/tests/suite/search_tool.rs | 4 + .../suite/stream_error_allows_next_turn.rs | 2 + .../core/tests/suite/stream_no_completed.rs | 1 + .../core/tests/suite/user_notification.rs | 1 + codex-rs/core/tests/suite/window_headers.rs | 1 + codex-rs/mcp-server/src/codex_tool_runner.rs | 2 + codex-rs/memories/write/src/runtime.rs | 1 + codex-rs/protocol/src/protocol.rs | 88 ++++++++++++++++++- codex-rs/thread-manager-sample/src/main.rs | 1 + 38 files changed, 392 insertions(+), 13 deletions(-) diff --git a/codex-rs/app-server/src/request_processors/turn_processor.rs b/codex-rs/app-server/src/request_processors/turn_processor.rs index 54fc0383e4..9895d9aad8 100644 --- a/codex-rs/app-server/src/request_processors/turn_processor.rs +++ b/codex-rs/app-server/src/request_processors/turn_processor.rs @@ -532,6 +532,7 @@ impl TurnRequestProcessor { environments: environment_selections, final_output_json_schema: params.output_schema, responsesapi_client_metadata: params.responsesapi_client_metadata, + turn_context: Default::default(), } }; let turn_id = self diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index b95aad4489..abf9fd9b87 100644 --- a/codex-rs/core/src/agent/control_tests.rs +++ b/codex-rs/core/src/agent/control_tests.rs @@ -442,6 +442,7 @@ async fn send_input_submits_user_message() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }, ); let captured = harness @@ -589,6 +590,7 @@ async fn spawn_agent_creates_thread_and_sends_prompt() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }, ); let captured = harness @@ -737,6 +739,7 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }, ); let captured = harness diff --git a/codex-rs/core/src/codex_delegate.rs b/codex-rs/core/src/codex_delegate.rs index 493771e28c..d7c6abdd70 100644 --- a/codex-rs/core/src/codex_delegate.rs +++ b/codex-rs/core/src/codex_delegate.rs @@ -192,6 +192,7 @@ pub(crate) async fn run_codex_thread_one_shot( items: input, final_output_json_schema, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; diff --git a/codex-rs/core/src/session/handlers.rs b/codex-rs/core/src/session/handlers.rs index a26b9bb3f6..212168341b 100644 --- a/codex-rs/core/src/session/handlers.rs +++ b/codex-rs/core/src/session/handlers.rs @@ -43,6 +43,7 @@ use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::ThreadMemoryMode; use codex_protocol::protocol::ThreadRolledBackEvent; use codex_protocol::protocol::TurnAbortReason; +use codex_protocol::protocol::TurnContextOverrides; use codex_protocol::protocol::WarningEvent; use codex_protocol::request_permissions::RequestPermissionsResponse; use codex_protocol::request_user_input::RequestUserInputResponse; @@ -224,15 +225,17 @@ pub(super) async fn user_input_or_turn_inner( environments, final_output_json_schema, responsesapi_client_metadata, - } => ( - items, - SessionSettingsUpdate { - final_output_json_schema: Some(final_output_json_schema), - environments, - ..Default::default() - }, - responsesapi_client_metadata, - ), + turn_context, + } => { + let mut updates = if turn_context == TurnContextOverrides::default() { + SessionSettingsUpdate::default() + } else { + turn_context_settings_update(sess, turn_context).await + }; + updates.final_output_json_schema = Some(final_output_json_schema); + updates.environments = environments; + (items, updates, responsesapi_client_metadata) + } _ => unreachable!(), }; @@ -289,6 +292,54 @@ pub(super) async fn user_input_or_turn_inner( } } +async fn turn_context_settings_update( + sess: &Session, + turn_context: TurnContextOverrides, +) -> SessionSettingsUpdate { + let TurnContextOverrides { + cwd, + workspace_roots, + profile_workspace_roots, + approval_policy, + approvals_reviewer, + sandbox_policy, + permission_profile, + active_permission_profile, + windows_sandbox_level, + model, + effort, + summary, + service_tier, + collaboration_mode, + personality, + } = turn_context; + let collaboration_mode = if let Some(collaboration_mode) = collaboration_mode { + collaboration_mode + } else { + let state = sess.state.lock().await; + state + .session_configuration + .collaboration_mode + .with_updates(model, effort, /*developer_instructions*/ None) + }; + SessionSettingsUpdate { + cwd, + workspace_roots, + profile_workspace_roots, + approval_policy, + approvals_reviewer, + sandbox_policy, + permission_profile, + active_permission_profile, + windows_sandbox_level, + collaboration_mode: Some(collaboration_mode), + reasoning_summary: summary, + service_tier, + personality, + ..Default::default() + } +} + async fn mirror_user_text_to_realtime(sess: &Arc, items: &[UserInput]) { let text = UserMessageItem::new(items).message(); if text.is_empty() { diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index d67e79f794..9a78242222 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -1096,6 +1096,7 @@ impl Session { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }, /*mirror_user_text_to_realtime*/ None, ) diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index bb858999e4..4dbcc80b4f 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -2220,6 +2220,7 @@ async fn fork_startup_context_then_first_turn_diff_snapshot() -> anyhow::Result< }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2283,6 +2284,7 @@ async fn fork_startup_context_then_first_turn_diff_snapshot() -> anyhow::Result< }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&forked.thread, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -5228,6 +5230,7 @@ fn op_kind_distinguishes_turn_ops() { items: vec![], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), } .kind(), "user_input" @@ -8335,6 +8338,7 @@ async fn active_goal_continuation_runs_again_after_no_tool_turn() -> anyhow::Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -8439,6 +8443,7 @@ async fn pending_request_user_input_does_not_spawn_extra_goal_continuation() -> }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -8863,6 +8868,7 @@ async fn completed_goal_accounts_current_turn_tokens_before_tool_response() -> a }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; diff --git a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs index 0044bf8522..c0c0016907 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs @@ -2470,6 +2470,7 @@ async fn send_input_accepts_structured_items() { ], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }; let captured = manager .captured_ops() diff --git a/codex-rs/core/tests/suite/abort_tasks.rs b/codex-rs/core/tests/suite/abort_tasks.rs index c81a1c2f68..fd2c06aef3 100644 --- a/codex-rs/core/tests/suite/abort_tasks.rs +++ b/codex-rs/core/tests/suite/abort_tasks.rs @@ -53,6 +53,7 @@ async fn interrupt_long_running_tool_emits_turn_aborted() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -109,6 +110,7 @@ async fn interrupt_tool_records_history_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -129,6 +131,7 @@ async fn interrupt_tool_records_history_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -211,6 +214,7 @@ async fn interrupt_persists_turn_aborted_marker_in_next_request() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -231,6 +235,7 @@ async fn interrupt_persists_turn_aborted_marker_in_next_request() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index 42bacb9bdc..3092c61372 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -392,6 +392,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -757,6 +758,7 @@ async fn includes_session_id_thread_id_and_model_headers_in_request() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -967,6 +969,7 @@ async fn includes_base_instructions_override_in_request() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1023,6 +1026,7 @@ async fn chatgpt_auth_sends_correct_request() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1146,6 +1150,7 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1184,6 +1189,7 @@ async fn includes_user_instructions_message_in_request() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1271,6 +1277,7 @@ async fn includes_apps_guidance_as_developer_message_for_chatgpt_auth() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1333,6 +1340,7 @@ async fn omits_apps_guidance_for_api_key_auth_even_when_feature_enabled() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1391,6 +1399,7 @@ async fn omits_apps_guidance_when_configured_off() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1432,6 +1441,7 @@ async fn omits_environment_context_when_configured_off() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1488,6 +1498,7 @@ async fn skills_append_to_developer_message() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1570,6 +1581,7 @@ async fn skills_use_aliases_in_developer_message_under_budget_pressure() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1630,6 +1642,7 @@ async fn includes_configured_effort_in_request() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1671,6 +1684,7 @@ async fn includes_no_effort_in_request() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1713,6 +1727,7 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_info }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1826,6 +1841,7 @@ async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1945,6 +1961,7 @@ async fn reasoning_summary_is_omitted_when_disabled() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2003,6 +2020,7 @@ async fn reasoning_summary_none_overrides_model_catalog_default() -> anyhow::Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2041,6 +2059,7 @@ async fn includes_default_verbosity_in_request() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2088,6 +2107,7 @@ async fn configured_verbosity_not_sent_for_models_without_support() -> anyhow::R }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2134,6 +2154,7 @@ async fn configured_verbosity_is_sent() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2185,6 +2206,7 @@ async fn includes_developer_instructions_message_in_request() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2480,6 +2502,7 @@ async fn token_count_includes_rate_limits_snapshot() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2618,6 +2641,7 @@ async fn usage_limit_error_emits_rate_limit_event() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submission should succeed while emitting usage limit error events"); @@ -2694,6 +2718,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -2708,6 +2733,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -2792,6 +2818,7 @@ async fn incomplete_response_emits_content_filter_error_message() -> anyhow::Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -2902,6 +2929,7 @@ async fn azure_overrides_assign_properties_used_for_responses_url() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2990,6 +3018,7 @@ async fn env_var_overrides_loaded_auth() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -3046,6 +3075,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -3061,6 +3091,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -3076,6 +3107,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/client_websockets.rs b/codex-rs/core/tests/suite/client_websockets.rs index a14ee6518f..3ae5be3e90 100755 --- a/codex-rs/core/tests/suite/client_websockets.rs +++ b/codex-rs/core/tests/suite/client_websockets.rs @@ -1208,6 +1208,7 @@ async fn responses_websocket_usage_limit_error_emits_rate_limit_event() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submission should succeed while emitting usage limit error events"); @@ -1296,6 +1297,7 @@ async fn responses_websocket_invalid_request_error_with_status_is_forwarded() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submission should succeed while emitting invalid request events"); diff --git a/codex-rs/core/tests/suite/collaboration_instructions.rs b/codex-rs/core/tests/suite/collaboration_instructions.rs index 5ce115cdaf..e2890c5828 100644 --- a/codex-rs/core/tests/suite/collaboration_instructions.rs +++ b/codex-rs/core/tests/suite/collaboration_instructions.rs @@ -86,6 +86,7 @@ async fn no_collaboration_instructions_by_default() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -148,6 +149,7 @@ async fn user_input_includes_collaboration_instructions_after_override() -> Resu }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -305,6 +307,7 @@ async fn override_then_next_turn_uses_updated_collaboration_instructions() -> Re }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -434,6 +437,7 @@ async fn collaboration_mode_update_emits_new_instruction_message() -> Result<()> }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -464,6 +468,7 @@ async fn collaboration_mode_update_emits_new_instruction_message() -> Result<()> }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -523,6 +528,7 @@ async fn collaboration_mode_update_noop_does_not_append() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -553,6 +559,7 @@ async fn collaboration_mode_update_noop_does_not_append() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -614,6 +621,7 @@ async fn collaboration_mode_update_emits_new_instruction_message_when_mode_chang }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -647,6 +655,7 @@ async fn collaboration_mode_update_emits_new_instruction_message_when_mode_chang }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -709,6 +718,7 @@ async fn collaboration_mode_update_noop_does_not_append_when_mode_is_unchanged() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -742,6 +752,7 @@ async fn collaboration_mode_update_noop_does_not_append_when_mode_is_unchanged() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -808,6 +819,7 @@ async fn resume_replays_collaboration_instructions() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -823,6 +835,7 @@ async fn resume_replays_collaboration_instructions() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -882,6 +895,7 @@ async fn empty_collaboration_instructions_are_ignored() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; diff --git a/codex-rs/core/tests/suite/compact.rs b/codex-rs/core/tests/suite/compact.rs index be3423a85e..1ca9b49d48 100644 --- a/codex-rs/core/tests/suite/compact.rs +++ b/codex-rs/core/tests/suite/compact.rs @@ -386,6 +386,7 @@ async fn summarize_context_three_requests_and_instructions() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -410,6 +411,7 @@ async fn summarize_context_three_requests_and_instructions() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -586,6 +588,7 @@ async fn manual_pre_compact_block_decision_does_not_block_compaction() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit first user turn"); @@ -658,6 +661,7 @@ async fn compact_hooks_respect_matchers_and_post_runs_after_compaction() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit first user turn"); @@ -727,6 +731,7 @@ async fn manual_compact_uses_custom_prompt() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit first user turn"); @@ -873,6 +878,7 @@ async fn manual_compact_emits_context_compaction_items() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1038,6 +1044,7 @@ async fn multiple_auto_compact_per_task_runs_after_token_limit_hit() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit user input"); @@ -1509,6 +1516,7 @@ async fn auto_compact_runs_after_token_limit_hit() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1524,6 +1532,7 @@ async fn auto_compact_runs_after_token_limit_hit() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1539,6 +1548,7 @@ async fn auto_compact_runs_after_token_limit_hit() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1709,6 +1719,7 @@ async fn auto_compact_emits_context_compaction_items() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1789,6 +1800,7 @@ async fn auto_compact_starts_after_turn_started() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1803,6 +1815,7 @@ async fn auto_compact_starts_after_turn_started() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1817,6 +1830,7 @@ async fn auto_compact_starts_after_turn_started() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2250,6 +2264,7 @@ async fn auto_compact_persists_rollout_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2264,6 +2279,7 @@ async fn auto_compact_persists_rollout_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2278,6 +2294,7 @@ async fn auto_compact_persists_rollout_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2366,6 +2383,7 @@ async fn manual_compact_retries_after_context_window_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2469,6 +2487,7 @@ async fn manual_compact_non_context_failure_retries_then_emits_task_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit user input"); @@ -2563,6 +2582,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2580,6 +2600,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2597,6 +2618,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2760,6 +2782,7 @@ async fn auto_compact_allows_multiple_attempts_when_interleaved_with_other_turn_ }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -2864,6 +2887,7 @@ async fn snapshot_request_shape_mid_turn_continuation_compaction() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -3063,6 +3087,7 @@ async fn auto_compact_counts_encrypted_reasoning_before_last_user() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -3181,6 +3206,7 @@ async fn auto_compact_runs_when_reasoning_header_clears_between_turns() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -3242,6 +3268,7 @@ async fn snapshot_request_shape_pre_turn_compaction_including_incoming_user_mess }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit user input"); @@ -3281,6 +3308,7 @@ async fn snapshot_request_shape_pre_turn_compaction_including_incoming_user_mess ], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit user input"); @@ -3469,6 +3497,7 @@ async fn snapshot_request_shape_pre_turn_compaction_context_window_exceeded() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit first user"); @@ -3483,6 +3512,7 @@ async fn snapshot_request_shape_pre_turn_compaction_context_window_exceeded() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit second user"); @@ -3555,6 +3585,7 @@ async fn snapshot_request_shape_manual_compact_without_previous_user_messages() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit follow-up user input"); diff --git a/codex-rs/core/tests/suite/compact_remote.rs b/codex-rs/core/tests/suite/compact_remote.rs index 15197042fa..1f5f661f05 100644 --- a/codex-rs/core/tests/suite/compact_remote.rs +++ b/codex-rs/core/tests/suite/compact_remote.rs @@ -326,6 +326,7 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -342,6 +343,7 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -516,6 +518,7 @@ async fn assert_remote_manual_compact_request_parity( }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -535,6 +538,7 @@ async fn assert_remote_manual_compact_request_parity( ], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -548,6 +552,7 @@ async fn assert_remote_manual_compact_request_parity( }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -567,6 +572,7 @@ async fn assert_remote_manual_compact_request_parity( ], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -580,6 +586,7 @@ async fn assert_remote_manual_compact_request_parity( }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -747,6 +754,7 @@ async fn remote_compact_v2_reuses_compaction_trigger_for_followups() -> Result<( }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -763,6 +771,7 @@ async fn remote_compact_v2_reuses_compaction_trigger_for_followups() -> Result<( }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -856,6 +865,7 @@ async fn remote_compact_v2_accepts_additional_output_items_before_compaction() - }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -872,6 +882,7 @@ async fn remote_compact_v2_accepts_additional_output_items_before_compaction() - }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -962,6 +973,7 @@ async fn remote_compact_filters_deferred_dynamic_tools() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_turn_complete(&codex).await; @@ -1033,6 +1045,7 @@ async fn remote_compact_runs_automatically() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -1113,6 +1126,7 @@ async fn remote_compact_trims_function_call_history_to_fit_context_window() -> R }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -1126,6 +1140,7 @@ async fn remote_compact_trims_function_call_history_to_fit_context_window() -> R }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -1243,6 +1258,7 @@ async fn auto_remote_compact_trims_function_call_history_to_fit_context_window() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -1256,6 +1272,7 @@ async fn auto_remote_compact_trims_function_call_history_to_fit_context_window() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -1275,6 +1292,7 @@ async fn auto_remote_compact_trims_function_call_history_to_fit_context_window() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -1374,6 +1392,7 @@ async fn auto_remote_compact_failure_stops_agent_loop() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -1387,6 +1406,7 @@ async fn auto_remote_compact_failure_stops_agent_loop() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -1480,6 +1500,7 @@ async fn remote_compact_trim_estimate_uses_session_base_instructions() -> Result }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&baseline_codex, |event| { @@ -1496,6 +1517,7 @@ async fn remote_compact_trim_estimate_uses_session_base_instructions() -> Result }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&baseline_codex, |event| { @@ -1586,6 +1608,7 @@ async fn remote_compact_trim_estimate_uses_session_base_instructions() -> Result }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&override_codex, |event| { @@ -1602,6 +1625,7 @@ async fn remote_compact_trim_estimate_uses_session_base_instructions() -> Result }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&override_codex, |event| { @@ -1672,6 +1696,7 @@ async fn remote_manual_compact_emits_context_compaction_items() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -1752,6 +1777,7 @@ async fn remote_manual_compact_failure_emits_task_error_event() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -1835,6 +1861,7 @@ async fn remote_compact_persists_replacement_history_in_rollout() -> Result<()> }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -1977,6 +2004,7 @@ async fn remote_compact_and_resume_refresh_stale_developer_instructions() -> Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -1994,6 +2022,7 @@ async fn remote_compact_and_resume_refresh_stale_developer_instructions() -> Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2018,6 +2047,7 @@ async fn remote_compact_and_resume_refresh_stale_developer_instructions() -> Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2113,6 +2143,7 @@ async fn remote_compact_refreshes_stale_developer_instructions_without_resume() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2129,6 +2160,7 @@ async fn remote_compact_refreshes_stale_developer_instructions_without_resume() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2200,6 +2232,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_restates_realtime_sta }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2213,6 +2246,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_restates_realtime_sta }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2279,6 +2313,7 @@ async fn remote_request_uses_custom_experimental_realtime_start_instructions() - }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2339,6 +2374,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_restates_realtime_end }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2354,6 +2390,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_restates_realtime_end }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2428,6 +2465,7 @@ async fn snapshot_request_shape_remote_manual_compact_restates_realtime_start() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2444,6 +2482,7 @@ async fn snapshot_request_shape_remote_manual_compact_restates_realtime_start() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2526,6 +2565,7 @@ async fn snapshot_request_shape_remote_mid_turn_compaction_does_not_restate_real }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2541,6 +2581,7 @@ async fn snapshot_request_shape_remote_mid_turn_compaction_does_not_restate_real }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2631,6 +2672,7 @@ async fn snapshot_request_shape_remote_compact_resume_restates_realtime_end() -> }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2660,6 +2702,7 @@ async fn snapshot_request_shape_remote_compact_resume_restates_realtime_end() -> }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2756,6 +2799,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_including_incoming_us }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2842,6 +2886,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_strips_incoming_model }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2871,6 +2916,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_strips_incoming_model }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -2989,6 +3035,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_context_window_exceed }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -3002,6 +3049,7 @@ async fn snapshot_request_shape_remote_pre_turn_compaction_context_window_exceed }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; let error_message = wait_for_event_match(&codex, |event| match event { @@ -3086,6 +3134,7 @@ async fn snapshot_request_shape_remote_mid_turn_continuation_compaction() -> Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -3163,6 +3212,7 @@ async fn snapshot_request_shape_remote_mid_turn_compaction_summary_only_reinject }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -3248,6 +3298,7 @@ async fn snapshot_request_shape_remote_mid_turn_compaction_multi_summary_reinjec }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -3264,6 +3315,7 @@ async fn snapshot_request_shape_remote_mid_turn_compaction_multi_summary_reinjec }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -3345,6 +3397,7 @@ async fn snapshot_request_shape_remote_manual_compact_without_previous_user_mess }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; diff --git a/codex-rs/core/tests/suite/compact_remote_parity.rs b/codex-rs/core/tests/suite/compact_remote_parity.rs index 32c13b0b27..c465480558 100644 --- a/codex-rs/core/tests/suite/compact_remote_parity.rs +++ b/codex-rs/core/tests/suite/compact_remote_parity.rs @@ -606,6 +606,7 @@ async fn submit_user_input(codex: &codex_core::CodexThread, items: Vec, text: &str) { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .expect("submit user turn"); diff --git a/codex-rs/core/tests/suite/fork_thread.rs b/codex-rs/core/tests/suite/fork_thread.rs index 37456dce63..75256ae786 100644 --- a/codex-rs/core/tests/suite/fork_thread.rs +++ b/codex-rs/core/tests/suite/fork_thread.rs @@ -57,6 +57,7 @@ async fn fork_thread_twice_drops_to_first_message() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -180,6 +181,7 @@ async fn fork_thread_from_history_does_not_require_source_rollout_path() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/hooks.rs b/codex-rs/core/tests/suite/hooks.rs index 53649afff8..8eda28354e 100644 --- a/codex-rs/core/tests/suite/hooks.rs +++ b/codex-rs/core/tests/suite/hooks.rs @@ -1440,6 +1440,7 @@ async fn blocked_queued_prompt_does_not_strand_earlier_accepted_prompt() -> Resu }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -1458,6 +1459,7 @@ async fn blocked_queued_prompt_does_not_strand_earlier_accepted_prompt() -> Resu }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; } diff --git a/codex-rs/core/tests/suite/items.rs b/codex-rs/core/tests/suite/items.rs index 65087a1fe7..9f6407b353 100644 --- a/codex-rs/core/tests/suite/items.rs +++ b/codex-rs/core/tests/suite/items.rs @@ -121,6 +121,7 @@ async fn user_message_item_is_emitted() -> anyhow::Result<()> { items: vec![expected_input.clone()], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -179,6 +180,7 @@ async fn assistant_message_item_is_emitted() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -239,6 +241,7 @@ async fn reasoning_item_is_emitted() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -300,6 +303,7 @@ async fn web_search_item_is_emitted() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -379,6 +383,7 @@ async fn image_generation_call_event_is_emitted() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -465,6 +470,7 @@ async fn image_generation_call_event_is_emitted_when_image_save_fails() -> anyho }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -520,6 +526,7 @@ async fn agent_message_content_delta_has_item_metadata() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -1103,6 +1110,7 @@ async fn reasoning_content_delta_has_item_metadata() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -1157,6 +1165,7 @@ async fn reasoning_raw_content_delta_respects_flag() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; diff --git a/codex-rs/core/tests/suite/model_visible_layout.rs b/codex-rs/core/tests/suite/model_visible_layout.rs index 7ae148788e..6c0275b132 100644 --- a/codex-rs/core/tests/suite/model_visible_layout.rs +++ b/codex-rs/core/tests/suite/model_visible_layout.rs @@ -344,6 +344,7 @@ async fn snapshot_model_visible_layout_resume_with_personality_change() -> Resul }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -451,6 +452,7 @@ async fn snapshot_model_visible_layout_resume_override_matches_rollout_model() - }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -499,6 +501,7 @@ async fn snapshot_model_visible_layout_resume_override_matches_rollout_model() - }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |event| { diff --git a/codex-rs/core/tests/suite/otel.rs b/codex-rs/core/tests/suite/otel.rs index 03692ac547..31477f0d3a 100644 --- a/codex-rs/core/tests/suite/otel.rs +++ b/codex-rs/core/tests/suite/otel.rs @@ -122,6 +122,7 @@ async fn responses_api_emits_api_request_event() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -167,6 +168,7 @@ async fn process_sse_emits_tracing_for_output_item() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -212,6 +214,7 @@ async fn process_sse_emits_failed_event_on_parse_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -258,6 +261,7 @@ async fn process_sse_records_failed_event_when_stream_closes_without_completed() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -324,6 +328,7 @@ async fn process_sse_failed_event_records_response_error_message() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -388,6 +393,7 @@ async fn process_sse_failed_event_logs_parse_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -439,6 +445,7 @@ async fn process_sse_failed_event_logs_missing_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -499,6 +506,7 @@ async fn process_sse_failed_event_logs_response_completed_parse_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -553,6 +561,7 @@ async fn process_sse_emits_completed_telemetry() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -631,6 +640,7 @@ async fn turn_and_completed_response_spans_record_token_usage() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -718,6 +728,7 @@ async fn handle_responses_span_records_response_kind_and_tool_name() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -811,6 +822,7 @@ async fn record_responses_sets_span_fields_for_response_events() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -899,6 +911,7 @@ async fn handle_response_item_records_tool_result_for_custom_tool_call() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -974,6 +987,7 @@ async fn handle_response_item_records_tool_result_for_function_call() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1050,6 +1064,7 @@ async fn handle_response_item_records_tool_result_for_shell_command_call() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1158,6 +1173,7 @@ async fn handle_shell_command_autoapprove_from_config_records_tool_decision() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1212,6 +1228,7 @@ async fn handle_shell_command_user_approved_records_tool_decision() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1281,6 +1298,7 @@ async fn handle_shell_command_user_approved_for_session_records_tool_decision() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1350,6 +1368,7 @@ async fn handle_sandbox_error_user_approves_retry_records_tool_decision() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1419,6 +1438,7 @@ async fn handle_shell_command_user_denies_records_tool_decision() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1488,6 +1508,7 @@ async fn handle_sandbox_error_user_approves_for_session_records_tool_decision() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -1558,6 +1579,7 @@ async fn handle_sandbox_error_user_denies_records_tool_decision() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/pending_input.rs b/codex-rs/core/tests/suite/pending_input.rs index 62851c515d..ebc18a3fd8 100644 --- a/codex-rs/core/tests/suite/pending_input.rs +++ b/codex-rs/core/tests/suite/pending_input.rs @@ -103,6 +103,7 @@ async fn submit_user_input(codex: &CodexThread, text: &str) { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap_or_else(|err| panic!("submit user input: {err}")); @@ -285,6 +286,7 @@ async fn injected_user_input_triggers_follow_up_request_with_deltas() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -303,6 +305,7 @@ async fn injected_user_input_triggers_follow_up_request_with_deltas() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/permissions_messages.rs b/codex-rs/core/tests/suite/permissions_messages.rs index c4f1bef968..d22b37a843 100644 --- a/codex-rs/core/tests/suite/permissions_messages.rs +++ b/codex-rs/core/tests/suite/permissions_messages.rs @@ -58,6 +58,7 @@ async fn permissions_message_sent_once_on_start() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -97,6 +98,7 @@ async fn permissions_message_added_on_override_change() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -127,6 +129,7 @@ async fn permissions_message_added_on_override_change() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -172,6 +175,7 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -185,6 +189,7 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -230,6 +235,7 @@ async fn permissions_message_omitted_when_disabled() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -260,6 +266,7 @@ async fn permissions_message_omitted_when_disabled() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -318,6 +325,7 @@ async fn resume_replays_permissions_messages() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -350,6 +358,7 @@ async fn resume_replays_permissions_messages() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -365,6 +374,7 @@ async fn resume_replays_permissions_messages() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -424,6 +434,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -456,6 +467,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -477,6 +489,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -512,6 +525,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&forked.thread, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -572,6 +586,7 @@ async fn permissions_message_includes_writable_roots() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; diff --git a/codex-rs/core/tests/suite/plugins.rs b/codex-rs/core/tests/suite/plugins.rs index 7b10db0259..f2b9cfeba9 100644 --- a/codex-rs/core/tests/suite/plugins.rs +++ b/codex-rs/core/tests/suite/plugins.rs @@ -227,6 +227,7 @@ async fn capability_sections_render_in_developer_message_in_order() -> Result<() }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -305,6 +306,7 @@ async fn explicit_plugin_mentions_inject_plugin_guidance() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -386,6 +388,7 @@ async fn explicit_plugin_mentions_track_plugin_used_analytics() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index f887131eef..b72b7fc1d0 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -153,6 +153,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -166,6 +167,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -249,6 +251,7 @@ async fn gpt_5_tools_without_apply_patch_append_apply_patch_instructions() -> an }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -262,6 +265,7 @@ async fn gpt_5_tools_without_apply_patch_append_apply_patch_instructions() -> an }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -326,6 +330,7 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -339,6 +344,7 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -421,6 +427,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -462,6 +469,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -547,6 +555,7 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -700,6 +709,7 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; diff --git a/codex-rs/core/tests/suite/quota_exceeded.rs b/codex-rs/core/tests/suite/quota_exceeded.rs index 4c0677e69a..d93fc6c956 100644 --- a/codex-rs/core/tests/suite/quota_exceeded.rs +++ b/codex-rs/core/tests/suite/quota_exceeded.rs @@ -48,6 +48,7 @@ async fn quota_exceeded_emits_single_error_event() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/realtime_conversation.rs b/codex-rs/core/tests/suite/realtime_conversation.rs index 31d30d824e..39ae60f411 100644 --- a/codex-rs/core/tests/suite/realtime_conversation.rs +++ b/codex-rs/core/tests/suite/realtime_conversation.rs @@ -2168,6 +2168,7 @@ async fn conversation_user_text_turn_is_sent_to_realtime_when_active() -> Result }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -2302,6 +2303,7 @@ async fn conversation_user_text_turn_is_capped_when_mirrored_to_realtime() -> Re }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -3497,6 +3499,7 @@ async fn inbound_handoff_request_steers_active_turn() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; diff --git a/codex-rs/core/tests/suite/request_compression.rs b/codex-rs/core/tests/suite/request_compression.rs index fddb18f15b..f86b7ffafb 100644 --- a/codex-rs/core/tests/suite/request_compression.rs +++ b/codex-rs/core/tests/suite/request_compression.rs @@ -47,6 +47,7 @@ async fn request_body_is_zstd_compressed_for_codex_backend_when_enabled() -> any }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -96,6 +97,7 @@ async fn request_body_is_not_compressed_for_api_key_auth_even_when_enabled() -> }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; diff --git a/codex-rs/core/tests/suite/resume.rs b/codex-rs/core/tests/suite/resume.rs index 3211d336e5..29a8f4ff7a 100644 --- a/codex-rs/core/tests/suite/resume.rs +++ b/codex-rs/core/tests/suite/resume.rs @@ -93,6 +93,7 @@ async fn resume_includes_initial_messages_from_rollout_events() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -178,6 +179,7 @@ async fn resume_includes_initial_messages_from_reasoning_events() -> Result<()> }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -267,6 +269,7 @@ async fn resume_switches_models_preserves_base_instructions() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -309,6 +312,7 @@ async fn resume_switches_models_preserves_base_instructions() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |event| { @@ -326,6 +330,7 @@ async fn resume_switches_models_preserves_base_instructions() -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |event| { @@ -398,6 +403,7 @@ async fn resume_model_switch_is_not_duplicated_after_pre_turn_override() -> Resu }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; @@ -444,6 +450,7 @@ async fn resume_model_switch_is_not_duplicated_after_pre_turn_override() -> Resu }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&resumed.codex, |event| { diff --git a/codex-rs/core/tests/suite/review.rs b/codex-rs/core/tests/suite/review.rs index 3611df6a6a..57467a1007 100644 --- a/codex-rs/core/tests/suite/review.rs +++ b/codex-rs/core/tests/suite/review.rs @@ -684,6 +684,7 @@ async fn review_history_surfaces_in_parent_session() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/search_tool.rs b/codex-rs/core/tests/suite/search_tool.rs index 69dc74dce3..6c0d3ce17b 100644 --- a/codex-rs/core/tests/suite/search_tool.rs +++ b/codex-rs/core/tests/suite/search_tool.rs @@ -494,6 +494,7 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() - }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -804,6 +805,7 @@ async fn tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call() - }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -1111,6 +1113,7 @@ async fn tool_search_surfaced_mcp_tool_errors_are_returned_to_model() -> Result< }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; @@ -1430,6 +1433,7 @@ async fn tool_search_matches_dynamic_tools_by_name_description_namespace_and_sch }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; diff --git a/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs b/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs index af86141261..673fc63526 100644 --- a/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs +++ b/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs @@ -101,6 +101,7 @@ async fn continue_after_stream_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); @@ -122,6 +123,7 @@ async fn continue_after_stream_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/stream_no_completed.rs b/codex-rs/core/tests/suite/stream_no_completed.rs index 30574718f2..841851562c 100644 --- a/codex-rs/core/tests/suite/stream_no_completed.rs +++ b/codex-rs/core/tests/suite/stream_no_completed.rs @@ -83,6 +83,7 @@ async fn retries_on_early_close() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/user_notification.rs b/codex-rs/core/tests/suite/user_notification.rs index 5fe08789f4..7e0210fc77 100644 --- a/codex-rs/core/tests/suite/user_notification.rs +++ b/codex-rs/core/tests/suite/user_notification.rs @@ -64,6 +64,7 @@ mv "${tmp_path}" "${payload_path}""#, }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; diff --git a/codex-rs/core/tests/suite/window_headers.rs b/codex-rs/core/tests/suite/window_headers.rs index d0e207d963..f823d9db7e 100644 --- a/codex-rs/core/tests/suite/window_headers.rs +++ b/codex-rs/core/tests/suite/window_headers.rs @@ -112,6 +112,7 @@ async fn submit_user_turn(codex: &Arc, text: &str) -> Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await?; wait_for_event(codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; diff --git a/codex-rs/mcp-server/src/codex_tool_runner.rs b/codex-rs/mcp-server/src/codex_tool_runner.rs index 9e3ac1bae9..a38fbf6b55 100644 --- a/codex-rs/mcp-server/src/codex_tool_runner.rs +++ b/codex-rs/mcp-server/src/codex_tool_runner.rs @@ -116,6 +116,7 @@ pub async fn run_codex_tool_session( }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }, trace: None, }; @@ -165,6 +166,7 @@ pub async fn run_codex_tool_session_reply( }], final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await { diff --git a/codex-rs/memories/write/src/runtime.rs b/codex-rs/memories/write/src/runtime.rs index 18b1bd6db1..0bd301709b 100644 --- a/codex-rs/memories/write/src/runtime.rs +++ b/codex-rs/memories/write/src/runtime.rs @@ -261,6 +261,7 @@ impl MemoryStartupContext { environments: None, final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await { diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 94753780cb..a067ff6998 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -396,6 +396,80 @@ pub struct ConversationTextParams { pub text: String, } +/// Persistent turn-context overrides that can be applied before user input. +#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, JsonSchema)] +pub struct TurnContextOverrides { + /// Updated `cwd` for sandbox/tool calls. + #[serde(skip_serializing_if = "Option::is_none")] + pub cwd: Option, + + /// Updated runtime workspace roots used to materialize symbolic + /// `:workspace_roots` filesystem permissions. + #[serde(skip_serializing_if = "Option::is_none")] + pub workspace_roots: Option>, + + /// Updated profile-defined workspace roots for status summaries and + /// per-turn config reconstruction. + #[serde(skip_serializing_if = "Option::is_none")] + pub profile_workspace_roots: Option>, + + /// Updated command approval policy. + #[serde(skip_serializing_if = "Option::is_none")] + pub approval_policy: Option, + + /// Updated approval reviewer for future approval prompts. + #[serde(skip_serializing_if = "Option::is_none")] + pub approvals_reviewer: Option, + + /// Updated sandbox policy for tool calls. + #[serde(skip_serializing_if = "Option::is_none")] + pub sandbox_policy: Option, + + /// Updated permissions profile for tool calls. + #[serde(skip_serializing_if = "Option::is_none")] + pub permission_profile: Option, + + /// Named or built-in profile that produced `permission_profile`, if the + /// update selected a profile rather than supplying raw permissions. + #[serde(skip_serializing_if = "Option::is_none")] + pub active_permission_profile: Option, + + /// Updated Windows sandbox mode for tool execution. + #[serde(skip_serializing_if = "Option::is_none")] + pub windows_sandbox_level: Option, + + /// Updated model slug. When set, the model info is derived automatically. + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, + + /// Updated reasoning effort (honored only for reasoning-capable models). + /// + /// Use `Some(Some(_))` to set a specific effort, `Some(None)` to clear the + /// effort, or `None` to leave the existing value unchanged. + #[serde(skip_serializing_if = "Option::is_none")] + pub effort: Option>, + + /// Updated reasoning summary preference (honored only for reasoning-capable models). + #[serde(skip_serializing_if = "Option::is_none")] + pub summary: Option, + + /// Updated service tier preference for future turns. + /// + /// Use `Some(Some(_))` to set a specific tier, `Some(None)` to clear the + /// preference, or `None` to leave the existing value unchanged. + #[serde(skip_serializing_if = "Option::is_none")] + pub service_tier: Option>, + + /// EXPERIMENTAL - set a pre-set collaboration mode. + /// Takes precedence over model, effort, and developer instructions if set. + #[serde(skip_serializing_if = "Option::is_none")] + pub collaboration_mode: Option, + + /// Updated personality preference. + #[serde(skip_serializing_if = "Option::is_none")] + pub personality: Option, +} + /// Submission operation #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema)] #[serde(tag = "type", rename_all = "snake_case")] @@ -425,10 +499,7 @@ pub enum Op { /// Request the list of voices supported by realtime conversation streams. RealtimeConversationListVoices, - /// Legacy user input. - /// - /// Prefer [`Op::UserTurn`] so the caller provides full turn context - /// (cwd/approval/sandbox/model/etc.) for each turn. + /// User input, optionally with turn-context overrides applied first. UserInput { /// User input items, see `InputItem` items: Vec, @@ -441,6 +512,10 @@ pub enum Op { /// Optional turn-scoped Responses API `client_metadata`. #[serde(default, skip_serializing_if = "Option::is_none")] responsesapi_client_metadata: Option>, + + /// Persistent turn-context overrides to apply before the input. + #[serde(default, flatten)] + turn_context: TurnContextOverrides, }, /// Similar to [`Op::UserInput`], but first applies persistent turn-context @@ -790,6 +865,7 @@ impl From> for Op { items: value, final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: TurnContextOverrides::default(), } } } @@ -5032,6 +5108,7 @@ mod tests { items: Vec::new(), final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }; let json_op = serde_json::to_value(op)?; @@ -5051,6 +5128,7 @@ mod tests { items: Vec::new(), final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), } ); @@ -5072,6 +5150,7 @@ mod tests { items: Vec::new(), final_output_json_schema: Some(schema.clone()), responsesapi_client_metadata: None, + turn_context: Default::default(), }; let json_op = serde_json::to_value(op)?; @@ -5097,6 +5176,7 @@ mod tests { "fiber_run_id".to_string(), "fiber-123".to_string(), )])), + turn_context: Default::default(), }; let json_op = serde_json::to_value(&op)?; diff --git a/codex-rs/thread-manager-sample/src/main.rs b/codex-rs/thread-manager-sample/src/main.rs index 8a168bad13..14564de778 100644 --- a/codex-rs/thread-manager-sample/src/main.rs +++ b/codex-rs/thread-manager-sample/src/main.rs @@ -293,6 +293,7 @@ async fn run_turn(thread: &CodexThread, thread_id: &str, prompt: String) -> anyh environments: None, final_output_json_schema: None, responsesapi_client_metadata: None, + turn_context: Default::default(), }) .await .context("submit user input")?; From cb528adaceea074fbd6fe3373402fa0b41d0545e Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sat, 16 May 2026 14:20:00 -0700 Subject: [PATCH 5/5] Remove UserInputWithTurnContext op --- .../src/request_processors/turn_processor.rs | 26 +++--- codex-rs/core/src/session/handlers.rs | 60 +------------ codex-rs/core/src/session/tests.rs | 25 ------ codex-rs/protocol/src/protocol.rs | 90 ------------------- 4 files changed, 13 insertions(+), 188 deletions(-) diff --git a/codex-rs/app-server/src/request_processors/turn_processor.rs b/codex-rs/app-server/src/request_processors/turn_processor.rs index 9895d9aad8..85b5e4e554 100644 --- a/codex-rs/app-server/src/request_processors/turn_processor.rs +++ b/codex-rs/app-server/src/request_processors/turn_processor.rs @@ -503,13 +503,8 @@ impl TurnRequestProcessor { .map_err(|err| invalid_request(format!("invalid turn context override: {err}")))?; } - // Start the turn by submitting the user input. Return its submission id as turn_id. - let turn_op = if has_any_overrides { - Op::UserInputWithTurnContext { - items: mapped_items, - environments: environment_selections, - final_output_json_schema: params.output_schema, - responsesapi_client_metadata: params.responsesapi_client_metadata, + let turn_context = if has_any_overrides { + codex_protocol::protocol::TurnContextOverrides { cwd, workspace_roots: runtime_workspace_roots, profile_workspace_roots, @@ -527,13 +522,16 @@ impl TurnRequestProcessor { personality, } } else { - Op::UserInput { - items: mapped_items, - environments: environment_selections, - final_output_json_schema: params.output_schema, - responsesapi_client_metadata: params.responsesapi_client_metadata, - turn_context: Default::default(), - } + Default::default() + }; + + // Start the turn by submitting the user input. Return its submission id as turn_id. + let turn_op = Op::UserInput { + items: mapped_items, + environments: environment_selections, + final_output_json_schema: params.output_schema, + responsesapi_client_metadata: params.responsesapi_client_metadata, + turn_context, }; let turn_id = self .submit_core_op(&request_id, thread.as_ref(), turn_op) diff --git a/codex-rs/core/src/session/handlers.rs b/codex-rs/core/src/session/handlers.rs index 212168341b..e8171542e8 100644 --- a/codex-rs/core/src/session/handlers.rs +++ b/codex-rs/core/src/session/handlers.rs @@ -164,62 +164,6 @@ pub(super) async fn user_input_or_turn_inner( None, ) } - Op::UserInputWithTurnContext { - cwd, - workspace_roots, - profile_workspace_roots, - approval_policy, - approvals_reviewer, - sandbox_policy, - permission_profile, - active_permission_profile, - windows_sandbox_level, - model, - effort, - summary, - service_tier, - final_output_json_schema, - items, - responsesapi_client_metadata, - collaboration_mode, - personality, - environments, - } => { - let collaboration_mode = if let Some(collab_mode) = collaboration_mode { - Some(collab_mode) - } else { - let state = sess.state.lock().await; - Some( - state - .session_configuration - .collaboration_mode - .with_updates(model, effort, /*developer_instructions*/ None), - ) - }; - ( - items, - SessionSettingsUpdate { - cwd, - workspace_roots, - profile_workspace_roots, - approval_policy, - approvals_reviewer, - sandbox_policy, - permission_profile, - active_permission_profile, - windows_sandbox_level, - collaboration_mode, - reasoning_summary: summary, - service_tier, - final_output_json_schema: Some(final_output_json_schema), - environments, - personality, - app_server_client_name: None, - app_server_client_version: None, - }, - responsesapi_client_metadata, - ) - } Op::UserInput { items, environments, @@ -876,9 +820,7 @@ pub(super) async fn submission_loop( .await; false } - Op::UserInput { .. } - | Op::UserInputWithTurnContext { .. } - | Op::UserTurn { .. } => { + Op::UserInput { .. } | Op::UserTurn { .. } => { user_input_or_turn(&sess, sub.id.clone(), sub.op).await; false } diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 4dbcc80b4f..d7fcb84ab9 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -5235,31 +5235,6 @@ fn op_kind_distinguishes_turn_ops() { .kind(), "user_input" ); - assert_eq!( - Op::UserInputWithTurnContext { - environments: None, - items: vec![], - final_output_json_schema: None, - responsesapi_client_metadata: None, - cwd: None, - workspace_roots: None, - profile_workspace_roots: None, - approval_policy: None, - approvals_reviewer: None, - sandbox_policy: None, - permission_profile: None, - active_permission_profile: None, - windows_sandbox_level: None, - model: None, - effort: None, - summary: None, - service_tier: None, - collaboration_mode: None, - personality: None, - } - .kind(), - "user_input_with_turn_context" - ); } #[tokio::test] diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index a067ff6998..b9cb7dcfe8 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -518,95 +518,6 @@ pub enum Op { turn_context: TurnContextOverrides, }, - /// Similar to [`Op::UserInput`], but first applies persistent turn-context - /// overrides in the same queued operation. This preserves submission order - /// and prevents the input from starting if the overrides are rejected. - UserInputWithTurnContext { - /// User input items, see `InputItem` - items: Vec, - /// Optional turn-scoped environment selections. - #[serde(default, skip_serializing_if = "Option::is_none")] - environments: Option>, - /// Optional JSON Schema used to constrain the final assistant message for this turn. - #[serde(skip_serializing_if = "Option::is_none")] - final_output_json_schema: Option, - /// Optional turn-scoped Responses API `client_metadata`. - #[serde(default, skip_serializing_if = "Option::is_none")] - responsesapi_client_metadata: Option>, - - /// Updated `cwd` for sandbox/tool calls. - #[serde(skip_serializing_if = "Option::is_none")] - cwd: Option, - - /// Updated runtime workspace roots used to materialize symbolic - /// `:workspace_roots` filesystem permissions. - #[serde(skip_serializing_if = "Option::is_none")] - workspace_roots: Option>, - - /// Updated profile-defined workspace roots for status summaries and - /// per-turn config reconstruction. - #[serde(skip_serializing_if = "Option::is_none")] - profile_workspace_roots: Option>, - - /// Updated command approval policy. - #[serde(skip_serializing_if = "Option::is_none")] - approval_policy: Option, - - /// Updated approval reviewer for future approval prompts. - #[serde(skip_serializing_if = "Option::is_none")] - approvals_reviewer: Option, - - /// Updated sandbox policy for tool calls. - #[serde(skip_serializing_if = "Option::is_none")] - sandbox_policy: Option, - - /// Updated permissions profile for tool calls. - #[serde(skip_serializing_if = "Option::is_none")] - permission_profile: Option, - - /// Named or built-in profile that produced `permission_profile`, if - /// the update selected a profile rather than supplying raw - /// permissions. - #[serde(skip_serializing_if = "Option::is_none")] - active_permission_profile: Option, - - /// Updated Windows sandbox mode for tool execution. - #[serde(skip_serializing_if = "Option::is_none")] - windows_sandbox_level: Option, - - /// Updated model slug. When set, the model info is derived - /// automatically. - #[serde(skip_serializing_if = "Option::is_none")] - model: Option, - - /// Updated reasoning effort (honored only for reasoning-capable models). - /// - /// Use `Some(Some(_))` to set a specific effort, `Some(None)` to clear - /// the effort, or `None` to leave the existing value unchanged. - #[serde(skip_serializing_if = "Option::is_none")] - effort: Option>, - - /// Updated reasoning summary preference (honored only for reasoning-capable models). - #[serde(skip_serializing_if = "Option::is_none")] - summary: Option, - - /// Updated service tier preference for future turns. - /// - /// Use `Some(Some(_))` to set a specific tier, `Some(None)` to clear the - /// preference, or `None` to leave the existing value unchanged. - #[serde(skip_serializing_if = "Option::is_none")] - service_tier: Option>, - - /// EXPERIMENTAL - set a pre-set collaboration mode. - /// Takes precedence over model, effort, and developer instructions if set. - #[serde(skip_serializing_if = "Option::is_none")] - collaboration_mode: Option, - - /// Updated personality preference. - #[serde(skip_serializing_if = "Option::is_none")] - personality: Option, - }, - /// Similar to [`Op::UserInput`], but contains additional context required /// for a turn of a [`crate::codex_thread::CodexThread`]. UserTurn { @@ -932,7 +843,6 @@ impl Op { Self::RealtimeConversationClose => "realtime_conversation_close", Self::RealtimeConversationListVoices => "realtime_conversation_list_voices", Self::UserInput { .. } => "user_input", - Self::UserInputWithTurnContext { .. } => "user_input_with_turn_context", Self::UserTurn { .. } => "user_turn", Self::InterAgentCommunication { .. } => "inter_agent_communication", Self::OverrideTurnContext { .. } => "override_turn_context",