diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 9d0a509c4c0b..89a4a3560ee5 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -831,7 +831,7 @@ Use `thread/backgroundTerminals/clean` to terminate all running background termi ### Example: Steer an active turn Use `turn/steer` to append additional user input to the currently active regular turn. This does -not emit `turn/started` and does not accept turn context overrides. +not emit `turn/started` and does not accept thread settings overrides. ```json { "method": "turn/steer", "id": 32, "params": { diff --git a/codex-rs/app-server/src/request_processors.rs b/codex-rs/app-server/src/request_processors.rs index d5853b56c557..4e77831e5ba8 100644 --- a/codex-rs/app-server/src/request_processors.rs +++ b/codex-rs/app-server/src/request_processors.rs @@ -258,7 +258,7 @@ use codex_config::ConfigLayerStack; use codex_config::loader::project_trust_key; use codex_config::types::McpServerTransportConfig; use codex_core::CodexThread; -use codex_core::CodexThreadTurnContextOverrides; +use codex_core::CodexThreadSettingsOverrides; use codex_core::ExternalGoalPreviousStatus; use codex_core::ExternalGoalSet; use codex_core::ForkSnapshot; 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 54fc0383e44b..5ac55372106a 100644 --- a/codex-rs/app-server/src/request_processors/turn_processor.rs +++ b/codex-rs/app-server/src/request_processors/turn_processor.rs @@ -460,7 +460,7 @@ impl TurnRequestProcessor { warning.contains("Configured value for `permission_profile` is disallowed") }) { return Err(invalid_request(format!( - "invalid turn context override: {warning}" + "invalid thread settings override: {warning}" ))); } ( @@ -482,7 +482,7 @@ impl TurnRequestProcessor { // still queued together with the input below to preserve submission order. if has_any_overrides { thread - .validate_turn_context_overrides(CodexThreadTurnContextOverrides { + .validate_thread_settings_overrides(CodexThreadSettingsOverrides { cwd: cwd.clone(), workspace_roots: runtime_workspace_roots.clone(), approval_policy, @@ -500,7 +500,9 @@ impl TurnRequestProcessor { personality, }) .await - .map_err(|err| invalid_request(format!("invalid turn context override: {err}")))?; + .map_err(|err| { + invalid_request(format!("invalid thread settings override: {err}")) + })?; } // Start the turn by submitting the user input. Return its submission id as turn_id. @@ -532,6 +534,7 @@ impl TurnRequestProcessor { environments: environment_selections, final_output_json_schema: params.output_schema, responsesapi_client_metadata: params.responsesapi_client_metadata, + thread_settings: Default::default(), } }; let turn_id = self diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 844d9ab1bbe1..01e535a5a402 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -904,7 +904,11 @@ async fn turn_start_rejects_invalid_permission_selection_before_starting_turn() .await??; assert_eq!(err.error.code, INVALID_REQUEST_ERROR_CODE); - assert!(err.error.message.contains("invalid turn context override")); + assert!( + err.error + .message + .contains("invalid thread settings override") + ); assert!( err.error.message.contains("allowed set [ReadOnly]"), "unexpected error message: {}", diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index b95aad4489fd..3ced8630ea60 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }, ); let captured = harness diff --git a/codex-rs/core/src/codex_delegate.rs b/codex-rs/core/src/codex_delegate.rs index 493771e28c80..4ed6b2368cbf 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, + thread_settings: Default::default(), }) .await?; diff --git a/codex-rs/core/src/codex_thread.rs b/codex-rs/core/src/codex_thread.rs index 1a2d5ed710ab..e86f34742d35 100644 --- a/codex-rs/core/src/codex_thread.rs +++ b/codex-rs/core/src/codex_thread.rs @@ -80,9 +80,9 @@ impl ThreadConfigSnapshot { } } -/// Turn context overrides that app-server validates before starting a turn. +/// Thread settings overrides that app-server validates before starting a turn. #[derive(Clone, Default)] -pub struct CodexThreadTurnContextOverrides { +pub struct CodexThreadSettingsOverrides { pub cwd: Option, pub workspace_roots: Option>, pub profile_workspace_roots: Option>, @@ -255,12 +255,12 @@ impl CodexThread { .await } - /// Validate persistent turn context overrides without committing them. - pub async fn validate_turn_context_overrides( + /// Validate persistent thread settings overrides without committing them. + pub async fn validate_thread_settings_overrides( &self, - overrides: CodexThreadTurnContextOverrides, + overrides: CodexThreadSettingsOverrides, ) -> ConstraintResult<()> { - let CodexThreadTurnContextOverrides { + let CodexThreadSettingsOverrides { cwd, workspace_roots, profile_workspace_roots, diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index 325d65745f36..b18932e65f75 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -20,7 +20,7 @@ mod compact_remote; mod compact_remote_v2; mod config_lock; pub use codex_thread::CodexThread; -pub use codex_thread::CodexThreadTurnContextOverrides; +pub use codex_thread::CodexThreadSettingsOverrides; pub use codex_thread::ThreadConfigSnapshot; pub use session::turn_context::TurnContext; mod agent; diff --git a/codex-rs/core/src/session/handlers.rs b/codex-rs/core/src/session/handlers.rs index a26b9bb3f6fe..3cacd168a7c9 100644 --- a/codex-rs/core/src/session/handlers.rs +++ b/codex-rs/core/src/session/handlers.rs @@ -42,6 +42,7 @@ use codex_protocol::protocol::ReviewRequest; use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::ThreadMemoryMode; use codex_protocol::protocol::ThreadRolledBackEvent; +use codex_protocol::protocol::ThreadSettingsOverrides; use codex_protocol::protocol::TurnAbortReason; use codex_protocol::protocol::WarningEvent; use codex_protocol::request_permissions::RequestPermissionsResponse; @@ -184,20 +185,9 @@ pub(super) async fn user_input_or_turn_inner( 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 { + let mut updates = thread_settings_update( + sess, + ThreadSettingsOverrides { cwd, workspace_roots, profile_workspace_roots, @@ -207,32 +197,35 @@ pub(super) async fn user_input_or_turn_inner( permission_profile, active_permission_profile, windows_sandbox_level, - collaboration_mode, - reasoning_summary: summary, + model, + effort, + summary, service_tier, - final_output_json_schema: Some(final_output_json_schema), - environments, + collaboration_mode, personality, - app_server_client_name: None, - app_server_client_version: None, }, - responsesapi_client_metadata, ) + .await; + updates.final_output_json_schema = Some(final_output_json_schema); + updates.environments = environments; + (items, updates, responsesapi_client_metadata) } Op::UserInput { items, 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, - ), + thread_settings, + } => { + let mut updates = if thread_settings == ThreadSettingsOverrides::default() { + SessionSettingsUpdate::default() + } else { + thread_settings_update(sess, thread_settings).await + }; + updates.final_output_json_schema = Some(final_output_json_schema); + updates.environments = environments; + (items, updates, responsesapi_client_metadata) + } _ => unreachable!(), }; @@ -289,6 +282,56 @@ pub(super) async fn user_input_or_turn_inner( } } +async fn thread_settings_update( + sess: &Session, + thread_settings: ThreadSettingsOverrides, +) -> SessionSettingsUpdate { + let ThreadSettingsOverrides { + 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, + } = thread_settings; + let collaboration_mode = if let Some(collaboration_mode) = collaboration_mode { + collaboration_mode + } else { + let state = sess.state.lock().await; + // Model and reasoning effort live in CollaborationMode settings today, so + // partial thread-settings updates refresh those fields on the active mode. + 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 d67e79f79417..5af22abb71ea 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, + thread_settings: 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 bb858999e48c..7b6feea29f1b 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 0044bf852247..27b12dbe4e68 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, + thread_settings: 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 c81a1c2f68ac..c41502a9dc9a 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, + thread_settings: Default::default(), }) .await .unwrap(); @@ -109,6 +110,7 @@ async fn interrupt_tool_records_history_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: Default::default(), }) .await .unwrap(); @@ -129,6 +131,7 @@ async fn interrupt_tool_records_history_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index 42bacb9bdc5f..818e0eba7bdb 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await .unwrap(); @@ -1023,6 +1026,7 @@ async fn chatgpt_auth_sends_correct_request() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await .unwrap(); @@ -1488,6 +1498,7 @@ async fn skills_append_to_developer_message() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await .unwrap(); @@ -2990,6 +3018,7 @@ async fn env_var_overrides_loaded_auth() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 a14ee6518fa6..feee9d42272f 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, + thread_settings: 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, + thread_settings: 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 5ce115cdaff8..2b0c20ab26ea 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 be3423a85ead..d1384ff800fc 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await .unwrap(); @@ -2250,6 +2264,7 @@ async fn auto_compact_persists_rollout_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: Default::default(), }) .await .unwrap(); @@ -2264,6 +2279,7 @@ async fn auto_compact_persists_rollout_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: Default::default(), }) .await .unwrap(); @@ -2278,6 +2294,7 @@ async fn auto_compact_persists_rollout_entries() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 15197042fae8..9db94016e6af 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 32c13b0b27c4..ccbd427b1f5c 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 Resu } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -/// Scenario: rolling back a turn that introduced persistent pre-turn context +/// Scenario: rolling back a turn that introduced persistent thread settings /// diffs should trim those context updates so the next request includes them /// only once. async fn snapshot_rollback_followup_turn_trims_context_updates() -> Result<()> { @@ -790,6 +790,7 @@ async fn user_turn(conversation: &Arc, text: &str) { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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 37456dce631e..68a167f79022 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await .unwrap(); diff --git a/codex-rs/core/tests/suite/hooks.rs b/codex-rs/core/tests/suite/hooks.rs index 53649afff849..d7549e0e0edd 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await?; } diff --git a/codex-rs/core/tests/suite/items.rs b/codex-rs/core/tests/suite/items.rs index 65087a1fe79a..0015a418ca62 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await?; @@ -239,6 +241,7 @@ async fn reasoning_item_is_emitted() -> anyhow::Result<()> { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 7ae148788ef8..d9c636b35f3d 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 03692ac547ac..9d2df4e60e79 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await .unwrap(); @@ -553,6 +561,7 @@ async fn process_sse_emits_completed_telemetry() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 62851c515d59..d511c8f0b657 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 c4f1bef968a6..33d494ae450a 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 7b10db025915..45fdf3df0764 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 f887131eef95..2023778a1127 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; @@ -485,7 +493,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an }); let expected_permissions_msg = body1["input"][0].clone(); let body1_input = body1["input"].as_array().expect("input array"); - // After overriding the turn context, emit one updated permissions message. + // After overriding thread settings, emit one updated permissions message. let expected_permissions_msg_2 = body2["input"][body1_input.len()].clone(); assert_ne!( expected_permissions_msg_2, expected_permissions_msg, @@ -547,6 +555,7 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: 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 4c0677e69a58..413855f64132 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, + thread_settings: 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 31d30d824ee3..232d2380b0a1 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await?; diff --git a/codex-rs/core/tests/suite/request_compression.rs b/codex-rs/core/tests/suite/request_compression.rs index fddb18f15bfb..2d66389d5347 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await?; diff --git a/codex-rs/core/tests/suite/resume.rs b/codex-rs/core/tests/suite/resume.rs index 3211d336e55b..db6428935477 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 3611df6a6aaf..e128a189b5d0 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, + thread_settings: 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 69dc74dce347..5b86c6ee7d98 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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, + thread_settings: 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 af861412616a..5709cdd12b89 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, + thread_settings: Default::default(), }) .await .unwrap(); @@ -122,6 +123,7 @@ async fn continue_after_stream_error() { }], final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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 30574718f20e..40763960f40c 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, + thread_settings: 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 5fe08789f438..343afd8d533f 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, + thread_settings: 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 d0e207d9639a..76fcacf7a99e 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, + thread_settings: 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 9e3ac1bae961..1898bebeeb54 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, + thread_settings: 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, + thread_settings: Default::default(), }) .await { diff --git a/codex-rs/memories/write/src/runtime.rs b/codex-rs/memories/write/src/runtime.rs index 18b1bd6db1ac..b1ffb2d215f7 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, + thread_settings: Default::default(), }) .await { diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 94753780cb17..42e327093495 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 thread-settings overrides that can be applied before user input. +#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, JsonSchema)] +pub struct ThreadSettingsOverrides { + /// 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 thread-settings overrides applied first. UserInput { /// User input items, see `InputItem` items: Vec, @@ -441,9 +512,13 @@ pub enum Op { /// Optional turn-scoped Responses API `client_metadata`. #[serde(default, skip_serializing_if = "Option::is_none")] responsesapi_client_metadata: Option>, + + /// Persistent thread-settings overrides to apply before the input. + #[serde(default, flatten)] + thread_settings: ThreadSettingsOverrides, }, - /// Similar to [`Op::UserInput`], but first applies persistent turn-context + /// Similar to [`Op::UserInput`], but first applies persistent thread-settings /// overrides in the same queued operation. This preserves submission order /// and prevents the input from starting if the overrides are rejected. UserInputWithTurnContext { @@ -606,11 +681,11 @@ pub enum Op { communication: InterAgentCommunication, }, - /// Override parts of the persistent turn context for subsequent turns. + /// Override parts of the persistent thread settings for subsequent turns. /// /// All fields are optional; when omitted, the existing value is preserved. /// This does not enqueue any input – it only updates defaults used for - /// turns that rely on persistent session-level context (for example, + /// turns that rely on persistent session-level settings (for example, /// [`Op::UserInput`]). OverrideTurnContext { /// Updated `cwd` for sandbox/tool calls. @@ -790,6 +865,7 @@ impl From> for Op { items: value, final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: ThreadSettingsOverrides::default(), } } } @@ -5032,6 +5108,7 @@ mod tests { items: Vec::new(), final_output_json_schema: None, responsesapi_client_metadata: None, + thread_settings: 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, + thread_settings: Default::default(), } ); @@ -5072,6 +5150,7 @@ mod tests { items: Vec::new(), final_output_json_schema: Some(schema.clone()), responsesapi_client_metadata: None, + thread_settings: 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(), )])), + thread_settings: 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 8a168bad138c..8593b8a477cb 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, + thread_settings: Default::default(), }) .await .context("submit user input")?;