diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs index 624501db4..e0c2ff03c 100644 --- a/codex-rs/core/src/client_common.rs +++ b/codex-rs/core/src/client_common.rs @@ -1,5 +1,4 @@ pub use codex_api::ResponseEvent; -use codex_config::types::Personality; use codex_protocol::error::Result; use codex_protocol::models::BaseInstructions; use codex_protocol::models::ContentItem; @@ -29,9 +28,6 @@ pub struct Prompt { pub base_instructions: BaseInstructions, - /// Optionally specify the personality of the model. - pub personality: Option, - /// Optional the output schema for the model's response. pub output_schema: Option, @@ -46,7 +42,6 @@ impl Default for Prompt { tools: Vec::new(), parallel_tool_calls: false, base_instructions: BaseInstructions::default(), - personality: None, output_schema: None, output_schema_strict: true, } diff --git a/codex-rs/core/src/compact.rs b/codex-rs/core/src/compact.rs index 52b2ef4ef..9d79138e8 100644 --- a/codex-rs/core/src/compact.rs +++ b/codex-rs/core/src/compact.rs @@ -77,7 +77,12 @@ pub(crate) async fn run_inline_auto_compact_task( reason: CompactionReason, phase: CompactionPhase, ) -> CodexResult<()> { - let prompt = turn_context.compact_prompt().to_string(); + let prompt = turn_context + .config + .compact_prompt + .as_deref() + .unwrap_or(SUMMARIZATION_PROMPT) + .to_string(); let input = vec![UserInput::Text { text: prompt, // Compaction prompt is synthesized; no UI element ranges to preserve. @@ -209,7 +214,7 @@ async fn run_compact_task_inner_impl( let mut history = sess.clone_history().await; history.record_items( &[initial_input_for_turn.into()], - turn_context.truncation_policy, + turn_context.model_info.truncation_policy.into(), ); let max_retries = turn_context.provider.info().stream_max_retries(); @@ -234,7 +239,6 @@ async fn run_compact_task_inner_impl( let prompt = Prompt { input: turn_input, base_instructions: sess.get_base_instructions().await, - personality: turn_context.personality, ..Default::default() }; let attempt_result = drain_to_completed( diff --git a/codex-rs/core/src/compact_remote.rs b/codex-rs/core/src/compact_remote.rs index eca14af80..08ca7efcd 100644 --- a/codex-rs/core/src/compact_remote.rs +++ b/codex-rs/core/src/compact_remote.rs @@ -228,7 +228,6 @@ async fn run_remote_compact_task_inner_impl( tools: tool_router.model_visible_specs(), parallel_tool_calls: turn_context.model_info.supports_parallel_tool_calls, base_instructions, - personality: turn_context.personality, output_schema: None, output_schema_strict: true, }; diff --git a/codex-rs/core/src/compact_remote_v2.rs b/codex-rs/core/src/compact_remote_v2.rs index d13aa9406..4b4071a0d 100644 --- a/codex-rs/core/src/compact_remote_v2.rs +++ b/codex-rs/core/src/compact_remote_v2.rs @@ -237,7 +237,6 @@ async fn run_remote_compact_task_inner_impl( tools: tool_router.model_visible_specs(), parallel_tool_calls: turn_context.model_info.supports_parallel_tool_calls, base_instructions, - personality: turn_context.personality, output_schema: None, output_schema_strict: true, }; diff --git a/codex-rs/core/src/context_manager/updates.rs b/codex-rs/core/src/context_manager/updates.rs index 0ce02240b..4c69aae95 100644 --- a/codex-rs/core/src/context_manager/updates.rs +++ b/codex-rs/core/src/context_manager/updates.rs @@ -63,8 +63,12 @@ fn build_permissions_update_item( exec_policy, #[allow(deprecated)] &next.cwd, - next.features.enabled(Feature::ExecPermissionApprovals), - next.features.enabled(Feature::RequestPermissionsTool), + next.config + .features + .enabled(Feature::ExecPermissionApprovals), + next.config + .features + .enabled(Feature::RequestPermissionsTool), ) .render(), ) diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index 708d26654..e8cbb56c5 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -626,7 +626,11 @@ async fn maybe_request_codex_apps_auth_elicitation( return result; } - if !turn_context.features.enabled(Feature::AuthElicitation) { + if !turn_context + .config + .features + .enabled(Feature::AuthElicitation) + { return result; } @@ -723,10 +727,10 @@ async fn augment_mcp_tool_request_meta_with_sandbox_state( let sandbox_state = serde_json::to_value(SandboxState { permission_profile: Some(turn_context.permission_profile()), sandbox_policy: turn_context.sandbox_policy(), - codex_linux_sandbox_exe: turn_context.codex_linux_sandbox_exe.clone(), + codex_linux_sandbox_exe: turn_context.config.codex_linux_sandbox_exe.clone(), #[allow(deprecated)] sandbox_cwd: turn_context.cwd.to_path_buf(), - use_legacy_landlock: turn_context.features.use_legacy_landlock(), + use_legacy_landlock: turn_context.config.features.use_legacy_landlock(), })?; match meta.as_mut() { diff --git a/codex-rs/core/src/mcp_tool_call_tests.rs b/codex-rs/core/src/mcp_tool_call_tests.rs index 9a8a9174a..56d1ff9b6 100644 --- a/codex-rs/core/src/mcp_tool_call_tests.rs +++ b/codex-rs/core/src/mcp_tool_call_tests.rs @@ -1316,15 +1316,14 @@ async fn codex_apps_auth_elicitation_non_host_owned_server_returns_original_resu let (session, mut turn_context, rx_event) = make_session_and_context_with_rx().await; let mut features = Features::with_defaults(); features.enable(Feature::AuthElicitation); - Arc::get_mut(&mut turn_context) - .expect("single turn context ref") - .features = ManagedFeatures::from(features); + let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref"); + Arc::make_mut(&mut turn_context.config).features = ManagedFeatures::from(features); let result = codex_apps_auth_failure_result(); let metadata = codex_apps_auth_failure_metadata(); let returned = maybe_request_codex_apps_auth_elicitation( &session, - &turn_context, + turn_context, "call_123", CODEX_APPS_MCP_SERVER_NAME, Some(&metadata), @@ -1343,7 +1342,7 @@ async fn codex_apps_auth_elicitation_disallowed_by_policy_returns_original_resul let mut features = Features::with_defaults(); features.enable(Feature::AuthElicitation); let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref"); - turn_context.features = ManagedFeatures::from(features); + Arc::make_mut(&mut turn_context.config).features = ManagedFeatures::from(features); turn_context .approval_policy .set(AskForApproval::Never) @@ -1372,7 +1371,7 @@ async fn codex_apps_auth_elicitation_granular_mcp_disabled_returns_original_resu let mut features = Features::with_defaults(); features.enable(Feature::AuthElicitation); let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref"); - turn_context.features = ManagedFeatures::from(features); + Arc::make_mut(&mut turn_context.config).features = ManagedFeatures::from(features); turn_context .approval_policy .set(AskForApproval::Granular(GranularApprovalConfig { @@ -1407,9 +1406,10 @@ async fn codex_apps_auth_elicitation_feature_enabled_requests_elicitation() { *session.active_turn.lock().await = Some(ActiveTurn::default()); let mut features = Features::with_defaults(); features.enable(Feature::AuthElicitation); - Arc::get_mut(&mut turn_context) - .expect("single turn context ref") - .features = ManagedFeatures::from(features); + { + let turn_context = Arc::get_mut(&mut turn_context).expect("single turn context ref"); + Arc::make_mut(&mut turn_context.config).features = ManagedFeatures::from(features); + } let result = codex_apps_auth_failure_result(); let metadata = codex_apps_auth_failure_metadata(); diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index d632609c0..d61de71bc 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -149,7 +149,6 @@ use codex_thread_store::ReadThreadParams; use codex_thread_store::ResumeThreadParams; use codex_thread_store::ThreadPersistenceMetadata; use codex_thread_store::ThreadStore; -use codex_utils_output_truncation::TruncationPolicy; use codex_utils_path_uri::PathUri; use futures::future::BoxFuture; use futures::future::Shared; @@ -198,7 +197,6 @@ use codex_config::ConfigLayerSource; use codex_config::ConfigLayerStackOrdering; use codex_config::types::McpServerConfig; use codex_model_provider_info::ModelProviderInfo; -use codex_protocol::config_types::ShellEnvironmentPolicy; use codex_protocol::error::CodexErr; use codex_protocol::error::Result as CodexResult; #[cfg(test)] @@ -1330,7 +1328,11 @@ impl Session { } = self .reconstruct_history_from_rollout(turn_context, rollout_items) .await; - if turn_context.features.enabled(Feature::ResizeAllImages) { + if turn_context + .config + .features + .enabled(Feature::ResizeAllImages) + { // Keep the recorded rollout unchanged. Prepare its reconstructed history before // installing it, so legacy images are processed once for this resume or fork and // will be processed again if the rollout is reconstructed in a future session. @@ -2630,7 +2632,11 @@ impl Session { turn_context: &TurnContext, items: &'a [ResponseItem], ) -> Cow<'a, [ResponseItem]> { - if !turn_context.features.enabled(Feature::ResizeAllImages) { + if !turn_context + .config + .features + .enabled(Feature::ResizeAllImages) + { return Cow::Borrowed(items); } @@ -2644,7 +2650,11 @@ impl Session { turn_context: &TurnContext, input: Vec, ) -> ResponseItem { - let local_image_preparation = if turn_context.features.enabled(Feature::ResizeAllImages) { + let local_image_preparation = if turn_context + .config + .features + .enabled(Feature::ResizeAllImages) + { LocalImagePreparation::Defer } else { LocalImagePreparation::Process @@ -2664,7 +2674,10 @@ impl Session { let items = items.as_ref(); { let mut state = self.state.lock().await; - state.record_items(items.iter(), turn_context.truncation_policy); + state.record_items( + items.iter(), + turn_context.model_info.truncation_policy.into(), + ); } self.persist_rollout_response_items(items).await; self.send_raw_response_items(turn_context, items).await; @@ -2683,7 +2696,10 @@ impl Session { let items = items.as_ref(); { let mut state = self.state.lock().await; - state.record_items(items.iter(), turn_context.truncation_policy); + state.record_items( + items.iter(), + turn_context.model_info.truncation_policy.into(), + ); } self.persist_rollout_items(&[RolloutItem::InterAgentCommunication(communication)]) .await; @@ -2885,9 +2901,11 @@ impl Session { #[allow(deprecated)] &turn_context.cwd, turn_context + .config .features .enabled(Feature::ExecPermissionApprovals), turn_context + .config .features .enabled(Feature::RequestPermissionsTool), ) @@ -3031,7 +3049,7 @@ impl Session { contextual_user_sections.push(user_instructions.to_string()); } // This is full-context metadata. Steady-state context diffs should not re-emit it. - if turn_context.features.enabled(Feature::TokenBudget) + if turn_context.config.features.enabled(Feature::TokenBudget) && let Some(model_context_window) = turn_context.model_context_window() { developer_sections.push( diff --git a/codex-rs/core/src/session/review.rs b/codex-rs/core/src/session/review.rs index 746265971..0296d9e44 100644 --- a/codex-rs/core/src/session/review.rs +++ b/codex-rs/core/src/session/review.rs @@ -1,6 +1,5 @@ use super::*; use codex_core_skills::HostLoadedSkills; -use codex_protocol::openai_models::ToolMode; use std::sync::atomic::AtomicBool; /// Spawn a review thread using the given prompt. @@ -47,15 +46,14 @@ pub(super) async fn spawn_review_thread( let mut per_turn_config = (*config).clone(); per_turn_config.model = Some(model.clone()); per_turn_config.features = review_features.clone(); - let tool_mode = model_info.tool_mode.unwrap_or_else(|| { - if per_turn_config.features.enabled(Feature::CodeModeOnly) { - ToolMode::CodeModeOnly - } else if per_turn_config.features.enabled(Feature::CodeMode) { - ToolMode::CodeMode - } else { - ToolMode::Direct - } - }); + per_turn_config.permissions.shell_environment_policy = parent_turn_context + .config + .permissions + .shell_environment_policy + .clone(); + per_turn_config.codex_linux_sandbox_exe = + parent_turn_context.config.codex_linux_sandbox_exe.clone(); + per_turn_config.compact_prompt = parent_turn_context.config.compact_prompt.clone(); if let Err(err) = per_turn_config.web_search_mode.set(review_web_search_mode) { let fallback_value = per_turn_config.web_search_mode.value(); tracing::warn!( @@ -113,26 +111,20 @@ pub(super) async fn spawn_review_thread( config: per_turn_config, auth_manager: auth_manager_for_context, model_info: model_info.clone(), - comp_hash: model_info.comp_hash.clone(), - tool_mode, session_telemetry: session_telemetry_for_context, provider: provider_for_context, reasoning_effort, reasoning_summary, session_source, parent_thread_id: parent_turn_context.parent_thread_id, - thread_source: parent_turn_context.thread_source.clone(), environments: parent_turn_context.environments.clone(), available_models, unified_exec_shell_mode, - features: review_features, - ghost_snapshot: parent_turn_context.ghost_snapshot.clone(), current_date: parent_turn_context.current_date.clone(), timezone: parent_turn_context.timezone.clone(), app_server_client_name: parent_turn_context.app_server_client_name.clone(), developer_instructions: None, user_instructions: None, - compact_prompt: parent_turn_context.compact_prompt.clone(), collaboration_mode: parent_turn_context.collaboration_mode.clone(), multi_agent_version: MultiAgentVersion::Disabled, personality: parent_turn_context.personality, @@ -140,14 +132,10 @@ pub(super) async fn spawn_review_thread( permission_profile: parent_turn_context.permission_profile(), network: parent_turn_context.network.clone(), windows_sandbox_level: parent_turn_context.windows_sandbox_level, - shell_environment_policy: parent_turn_context.shell_environment_policy.clone(), #[allow(deprecated)] cwd: parent_turn_context.cwd.clone(), final_output_json_schema: None, - codex_self_exe: parent_turn_context.codex_self_exe.clone(), - codex_linux_sandbox_exe: parent_turn_context.codex_linux_sandbox_exe.clone(), dynamic_tools: parent_turn_context.dynamic_tools.clone(), - truncation_policy: model_info.truncation_policy.into(), turn_metadata_state, extension_data, turn_skills: TurnSkillsContext::new(parent_turn_context.turn_skills.outcome.clone()), diff --git a/codex-rs/core/src/session/rollout_reconstruction.rs b/codex-rs/core/src/session/rollout_reconstruction.rs index 34e7807a9..ab4838b2f 100644 --- a/codex-rs/core/src/session/rollout_reconstruction.rs +++ b/codex-rs/core/src/session/rollout_reconstruction.rs @@ -271,14 +271,14 @@ impl Session { RolloutItem::ResponseItem(response_item) => { history.record_items( std::iter::once(response_item), - turn_context.truncation_policy, + turn_context.model_info.truncation_policy.into(), ); } RolloutItem::InterAgentCommunication(communication) => { let response_item = communication.to_model_input_item(); history.record_items( std::iter::once(&response_item), - turn_context.truncation_policy, + turn_context.model_info.truncation_policy.into(), ); } RolloutItem::Compacted(compacted) => { diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index b7ddb90b1..473adaa83 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -3530,7 +3530,7 @@ async fn includes_timed_out_message() { }; let (_, turn_context) = make_session_and_context().await; - let out = format_exec_output_str(&exec, turn_context.truncation_policy); + let out = format_exec_output_str(&exec, turn_context.model_info.truncation_policy.into()); assert_eq!( out, @@ -3569,10 +3569,6 @@ async fn turn_context_with_model_updates_model_fields() { updated.config.model_reasoning_effort, Some(ReasoningEffortConfig::Medium) ); - assert_eq!( - updated.truncation_policy, - expected_model_info.truncation_policy.into() - ); } #[test] @@ -8118,18 +8114,6 @@ fn file_system_policy_with_unreadable_glob(turn_context: &TurnContext) -> FileSy policy } -#[tokio::test] -async fn turn_context_item_uses_turn_context_comp_hash_snapshot() { - let (_session, mut turn_context) = make_session_and_context().await; - turn_context.comp_hash = Some("turn-context-hash".to_string()); - turn_context.model_info.comp_hash = Some("model-info-hash".to_string()); - - assert_eq!( - turn_context.to_turn_context_item().comp_hash.as_deref(), - Some("turn-context-hash") - ); -} - #[tokio::test] async fn turn_context_item_stores_local_cwd() { let (_session, mut turn_context) = make_session_and_context().await; @@ -9706,7 +9690,7 @@ async fn sample_rollout( } live_history.record_items( initial_context.iter(), - reconstruction_turn.truncation_policy, + reconstruction_turn.model_info.truncation_policy.into(), ); let user1 = ResponseItem::Message { @@ -9720,7 +9704,7 @@ async fn sample_rollout( }; live_history.record_items( std::iter::once(&user1), - reconstruction_turn.truncation_policy, + reconstruction_turn.model_info.truncation_policy.into(), ); rollout_items.push(RolloutItem::ResponseItem(user1.clone())); @@ -9735,7 +9719,7 @@ async fn sample_rollout( }; live_history.record_items( std::iter::once(&assistant1), - reconstruction_turn.truncation_policy, + reconstruction_turn.model_info.truncation_policy.into(), ); rollout_items.push(RolloutItem::ResponseItem(assistant1.clone())); @@ -9763,7 +9747,7 @@ async fn sample_rollout( }; live_history.record_items( std::iter::once(&user2), - reconstruction_turn.truncation_policy, + reconstruction_turn.model_info.truncation_policy.into(), ); rollout_items.push(RolloutItem::ResponseItem(user2.clone())); @@ -9778,7 +9762,7 @@ async fn sample_rollout( }; live_history.record_items( std::iter::once(&assistant2), - reconstruction_turn.truncation_policy, + reconstruction_turn.model_info.truncation_policy.into(), ); rollout_items.push(RolloutItem::ResponseItem(assistant2.clone())); @@ -9806,7 +9790,7 @@ async fn sample_rollout( }; live_history.record_items( std::iter::once(&user3), - reconstruction_turn.truncation_policy, + reconstruction_turn.model_info.truncation_policy.into(), ); rollout_items.push(RolloutItem::ResponseItem(user3)); @@ -9821,7 +9805,7 @@ async fn sample_rollout( }; live_history.record_items( std::iter::once(&assistant3), - reconstruction_turn.truncation_policy, + reconstruction_turn.model_info.truncation_policy.into(), ); rollout_items.push(RolloutItem::ResponseItem(assistant3)); diff --git a/codex-rs/core/src/session/tests/guardian_tests.rs b/codex-rs/core/src/session/tests/guardian_tests.rs index e958b39e1..43cd91a99 100644 --- a/codex-rs/core/src/session/tests/guardian_tests.rs +++ b/codex-rs/core/src/session/tests/guardian_tests.rs @@ -95,11 +95,11 @@ async fn request_permissions_routes_to_guardian_when_reviewer_is_enabled() { .approval_policy .set(AskForApproval::OnRequest) .expect("test setup should allow updating approval policy"); - turn_context_raw + let mut config = (*turn_context_raw.config).clone(); + config .features .enable(Feature::GuardianApproval) .expect("test setup should allow enabling guardian approvals"); - let mut config = (*turn_context_raw.config).clone(); config.approvals_reviewer = ApprovalsReviewer::AutoReview; config.model_provider.base_url = Some(format!("{}/v1", server.uri())); let config = Arc::new(config); @@ -183,11 +183,11 @@ async fn request_permissions_guardian_review_stops_when_cancelled() { .approval_policy .set(AskForApproval::OnRequest) .expect("test setup should allow updating approval policy"); - turn_context_raw + let mut config = (*turn_context_raw.config).clone(); + config .features .enable(Feature::GuardianApproval) .expect("test setup should allow enabling guardian approvals"); - let mut config = (*turn_context_raw.config).clone(); config.approvals_reviewer = ApprovalsReviewer::AutoReview; config.model_provider.base_url = Some(format!("{}/v1", server.uri())); let config = Arc::new(config); @@ -292,21 +292,21 @@ async fn guardian_allows_shell_command_additional_permissions_requests_past_poli .await; let (mut session, mut turn_context_raw) = make_session_and_context().await; - turn_context_raw.codex_linux_sandbox_exe = codex_linux_sandbox_exe_or_skip!(); turn_context_raw .approval_policy .set(AskForApproval::OnRequest) .expect("test setup should allow updating approval policy"); - turn_context_raw - .features - .enable(Feature::GuardianApproval) - .expect("test setup should allow enabling guardian approvals"); session .features .enable(Feature::ExecPermissionApprovals) .expect("test setup should allow enabling request permissions"); turn_context_raw.permission_profile = codex_protocol::models::PermissionProfile::Disabled; let mut config = (*turn_context_raw.config).clone(); + config.codex_linux_sandbox_exe = codex_linux_sandbox_exe_or_skip!(); + config + .features + .enable(Feature::GuardianApproval) + .expect("test setup should allow enabling guardian approvals"); config.model_provider.base_url = Some(format!("{}/v1", server.uri())); let config = Arc::new(config); let models_manager = models_manager_with_provider( @@ -467,7 +467,7 @@ async fn guardian_allows_unified_exec_additional_permissions_requests_past_polic .approval_policy .set(AskForApproval::OnRequest) .expect("test setup should allow updating approval policy"); - turn_context_raw + Arc::make_mut(&mut turn_context_raw.config) .features .enable(Feature::GuardianApproval) .expect("test setup should allow enabling guardian approvals"); diff --git a/codex-rs/core/src/session/token_budget.rs b/codex-rs/core/src/session/token_budget.rs index b8f9f82fb..4548ed319 100644 --- a/codex-rs/core/src/session/token_budget.rs +++ b/codex-rs/core/src/session/token_budget.rs @@ -11,7 +11,7 @@ pub(super) async fn maybe_record_token_budget_remaining_context( tokens_before_sampling: i64, tokens_after_sampling: i64, ) { - if !turn_context.features.enabled(Feature::TokenBudget) { + if !turn_context.config.features.enabled(Feature::TokenBudget) { return; } let Some(model_context_window) = turn_context.model_context_window() else { diff --git a/codex-rs/core/src/session/turn.rs b/codex-rs/core/src/session/turn.rs index 4b052cb65..7da157cf7 100644 --- a/codex-rs/core/src/session/turn.rs +++ b/codex-rs/core/src/session/turn.rs @@ -177,7 +177,7 @@ pub(crate) async fn run_turn( .await; sess.set_previous_turn_settings(Some(PreviousTurnSettings { model: turn_context.model_info.slug.clone(), - comp_hash: turn_context.comp_hash.clone(), + comp_hash: turn_context.model_info.comp_hash.clone(), realtime_active: Some(turn_context.realtime_active), })) .await; @@ -840,7 +840,7 @@ async fn maybe_run_previous_model_inline_compact( }; let should_compact_for_comp_hash_change = comp_hash_changed( previous_turn_settings.comp_hash.as_deref(), - turn_context.comp_hash.as_deref(), + turn_context.model_info.comp_hash.as_deref(), ); let previous_model_turn_context = Arc::new( turn_context @@ -913,7 +913,11 @@ async fn run_auto_compact( phase: CompactionPhase, ) -> CodexResult<()> { if should_use_remote_compact_task(turn_context.provider.info()) { - if turn_context.features.enabled(Feature::RemoteCompactionV2) { + if turn_context + .config + .features + .enabled(Feature::RemoteCompactionV2) + { emit_compact_metric( &sess.services.session_telemetry, "remote_v2", @@ -1025,7 +1029,6 @@ pub(crate) fn build_prompt( tools: router.model_visible_specs(), parallel_tool_calls: turn_context.model_info.supports_parallel_tool_calls, base_instructions, - personality: turn_context.personality, output_schema: turn_context.final_output_json_schema.clone(), output_schema_strict: !crate::guardian::is_guardian_reviewer_source( &turn_context.session_source, diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index 526a16bac..b76c9384f 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -1,7 +1,6 @@ use super::*; use crate::SkillLoadOutcome; use crate::agents_md::LoadedAgentsMd; -use crate::config::GhostSnapshotConfig; use crate::environment_selection::TurnEnvironmentSnapshot; use crate::shell_snapshot::ShellSnapshotFile; use codex_core_skills::HostLoadedSkills; @@ -12,9 +11,7 @@ use codex_protocol::SessionId; use codex_protocol::ThreadId; use codex_protocol::models::AdditionalPermissionProfile; use codex_protocol::openai_models::ModelInfo; -use codex_protocol::openai_models::ToolMode; use codex_protocol::protocol::MultiAgentVersion; -use codex_protocol::protocol::ThreadSource; use codex_protocol::protocol::TurnEnvironmentSelection; use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile; use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy; @@ -111,15 +108,12 @@ pub struct TurnContext { pub config: Arc, pub(crate) auth_manager: Option>, pub(crate) model_info: ModelInfo, - pub(crate) comp_hash: Option, - pub(crate) tool_mode: ToolMode, pub(crate) session_telemetry: SessionTelemetry, pub(crate) provider: SharedModelProvider, pub(crate) reasoning_effort: Option, pub(crate) reasoning_summary: ReasoningSummaryConfig, pub(crate) session_source: SessionSource, pub(crate) parent_thread_id: Option, - pub(crate) thread_source: Option, pub(crate) environments: TurnEnvironmentSnapshot, /// The session's absolute working directory. All relative paths provided /// by the model as well as sandbox policies are resolved against this path @@ -130,7 +124,6 @@ pub struct TurnContext { pub(crate) timezone: Option, pub(crate) app_server_client_name: Option, pub(crate) developer_instructions: Option, - pub(crate) compact_prompt: Option, pub(crate) user_instructions: Option, pub(crate) collaboration_mode: CollaborationMode, pub(crate) multi_agent_version: MultiAgentVersion, @@ -139,15 +132,9 @@ pub struct TurnContext { pub(crate) permission_profile: PermissionProfile, pub(crate) network: Option, pub(crate) windows_sandbox_level: WindowsSandboxLevel, - pub(crate) shell_environment_policy: ShellEnvironmentPolicy, pub(crate) available_models: Vec, pub(crate) unified_exec_shell_mode: UnifiedExecShellMode, - pub features: ManagedFeatures, - pub(crate) ghost_snapshot: GhostSnapshotConfig, pub(crate) final_output_json_schema: Option, - pub(crate) codex_self_exe: Option, - pub(crate) codex_linux_sandbox_exe: Option, - pub(crate) truncation_policy: TruncationPolicy, pub(crate) dynamic_tools: Vec, pub(crate) turn_metadata_state: Arc, pub(crate) extension_data: Arc, @@ -214,7 +201,9 @@ impl TurnContext { .auth_manager .as_deref() .is_some_and(AuthManager::current_auth_uses_codex_backend); - self.features.apps_enabled_for_auth(uses_codex_backend) + self.config + .features + .apps_enabled_for_auth(uses_codex_backend) } pub(crate) fn tool_environment_mode(&self) -> ToolEnvironmentMode { @@ -231,16 +220,6 @@ impl TurnContext { let model_info = models_manager .get_model_info(model.as_str(), &config.to_models_manager_config()) .await; - let tool_mode = model_info.tool_mode.unwrap_or_else(|| { - if config.features.enabled(Feature::CodeModeOnly) { - ToolMode::CodeModeOnly - } else if config.features.enabled(Feature::CodeMode) { - ToolMode::CodeMode - } else { - ToolMode::Direct - } - }); - let truncation_policy = model_info.truncation_policy.into(); let supported_reasoning_levels = model_info .supported_reasoning_levels .iter() @@ -269,7 +248,6 @@ impl TurnContext { Some(reasoning_effort.clone()), /*developer_instructions*/ None, ); - let features = self.features.clone(); let available_models = models_manager .list_models(RefreshStrategy::OnlineIfUncached) .await; @@ -281,8 +259,6 @@ impl TurnContext { config: Arc::new(config), auth_manager: self.auth_manager.clone(), model_info: model_info.clone(), - comp_hash: model_info.comp_hash.clone(), - tool_mode, session_telemetry: self .session_telemetry .clone() @@ -292,7 +268,6 @@ impl TurnContext { reasoning_summary: self.reasoning_summary, session_source: self.session_source.clone(), parent_thread_id: self.parent_thread_id, - thread_source: self.thread_source.clone(), environments: self.environments.clone(), #[allow(deprecated)] cwd: self.cwd.clone(), @@ -300,7 +275,6 @@ impl TurnContext { timezone: self.timezone.clone(), app_server_client_name: self.app_server_client_name.clone(), developer_instructions: self.developer_instructions.clone(), - compact_prompt: self.compact_prompt.clone(), user_instructions: self.user_instructions.clone(), collaboration_mode, multi_agent_version: self.multi_agent_version, @@ -309,15 +283,9 @@ impl TurnContext { permission_profile: self.permission_profile.clone(), network: self.network.clone(), windows_sandbox_level: self.windows_sandbox_level, - shell_environment_policy: self.shell_environment_policy.clone(), available_models, unified_exec_shell_mode: self.unified_exec_shell_mode.clone(), - features, - ghost_snapshot: self.ghost_snapshot.clone(), final_output_json_schema: self.final_output_json_schema.clone(), - codex_self_exe: self.codex_self_exe.clone(), - codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.clone(), - truncation_policy, dynamic_tools: self.dynamic_tools.clone(), turn_metadata_state: self.turn_metadata_state.clone(), extension_data: Arc::clone(&self.extension_data), @@ -368,7 +336,7 @@ impl TurnContext { .config .permissions .windows_sandbox_private_desktop, - use_legacy_landlock: self.features.use_legacy_landlock(), + use_legacy_landlock: self.config.features.use_legacy_landlock(), } } @@ -388,12 +356,6 @@ impl TurnContext { .then_some(file_system_sandbox_policy) } - pub(crate) fn compact_prompt(&self) -> &str { - self.compact_prompt - .as_deref() - .unwrap_or(compact::SUMMARIZATION_PROMPT) - } - pub(crate) fn to_turn_context_item(&self) -> TurnContextItem { let workspace_roots = self.config.effective_workspace_roots(); #[allow(deprecated)] @@ -410,7 +372,7 @@ impl TurnContext { network: self.turn_context_network_item(), file_system_sandbox_policy: self.non_legacy_file_system_sandbox_policy(), model: self.model_info.slug.clone(), - comp_hash: self.comp_hash.clone(), + comp_hash: self.model_info.comp_hash.clone(), personality: self.personality, collaboration_mode: Some(self.collaboration_mode.clone()), multi_agent_version: Some(self.multi_agent_version), @@ -549,15 +511,6 @@ impl Session { ); let mut per_turn_config = per_turn_config; - let tool_mode = model_info.tool_mode.unwrap_or_else(|| { - if per_turn_config.features.enabled(Feature::CodeModeOnly) { - ToolMode::CodeModeOnly - } else if per_turn_config.features.enabled(Feature::CodeMode) { - ToolMode::CodeMode - } else { - ToolMode::Direct - } - }); per_turn_config.service_tier = get_service_tier( per_turn_config.service_tier, per_turn_config.features.enabled(Feature::FastMode), @@ -583,18 +536,15 @@ impl Session { sub_id, trace_id: current_span_trace_id(), realtime_active: false, - config: per_turn_config.clone(), + config: per_turn_config, auth_manager: auth_manager_for_context, - model_info: model_info.clone(), - comp_hash: model_info.comp_hash.clone(), - tool_mode, + model_info, session_telemetry: session_telemetry_for_context, provider: provider_for_context, reasoning_effort, reasoning_summary, session_source, parent_thread_id: session_configuration.parent_thread_id, - thread_source: session_configuration.thread_source.clone(), environments, #[allow(deprecated)] cwd, @@ -602,7 +552,6 @@ impl Session { timezone: Some(timezone), app_server_client_name: session_configuration.app_server_client_name.clone(), developer_instructions: session_configuration.developer_instructions.clone(), - compact_prompt: session_configuration.compact_prompt.clone(), user_instructions: session_configuration .loaded_agents_md .as_ref() @@ -614,15 +563,9 @@ impl Session { permission_profile: session_configuration.permission_profile(), network, windows_sandbox_level: session_configuration.windows_sandbox_level, - shell_environment_policy: per_turn_config.permissions.shell_environment_policy.clone(), available_models, unified_exec_shell_mode, - features: per_turn_config.features.clone(), - ghost_snapshot: per_turn_config.ghost_snapshot.clone(), final_output_json_schema: None, - codex_self_exe: per_turn_config.codex_self_exe.clone(), - codex_linux_sandbox_exe: per_turn_config.codex_linux_sandbox_exe.clone(), - truncation_policy: model_info.truncation_policy.into(), dynamic_tools: session_configuration.dynamic_tools.clone(), turn_metadata_state, extension_data, diff --git a/codex-rs/core/src/tasks/compact.rs b/codex-rs/core/src/tasks/compact.rs index adc98e413..dd9d6cb07 100644 --- a/codex-rs/core/src/tasks/compact.rs +++ b/codex-rs/core/src/tasks/compact.rs @@ -31,6 +31,7 @@ impl SessionTask for CompactTask { let session = session.clone_session(); let _ = if crate::compact::should_use_remote_compact_task(ctx.provider.info()) { if ctx + .config .features .enabled(codex_features::Feature::RemoteCompactionV2) { @@ -55,7 +56,12 @@ impl SessionTask for CompactTask { /*manual*/ true, ); let input = vec![UserInput::Text { - text: ctx.compact_prompt().to_string(), + text: ctx + .config + .compact_prompt + .as_deref() + .unwrap_or(crate::compact::SUMMARIZATION_PROMPT) + .to_string(), // Compaction prompt is synthesized; no UI element ranges to preserve. text_elements: Vec::new(), }]; diff --git a/codex-rs/core/src/tasks/mod.rs b/codex-rs/core/src/tasks/mod.rs index b2f337ec4..3ff0e2d5f 100644 --- a/codex-rs/core/src/tasks/mod.rs +++ b/codex-rs/core/src/tasks/mod.rs @@ -722,7 +722,7 @@ impl Session { } emit_turn_memory_metric( &self.services.session_telemetry, - turn_context.features.enabled(Feature::MemoryTool), + turn_context.config.features.enabled(Feature::MemoryTool), turn_context.config.memories.use_memories, turn_had_memory_citation, ); diff --git a/codex-rs/core/src/tasks/user_shell.rs b/codex-rs/core/src/tasks/user_shell.rs index 4c3049351..37835c36a 100644 --- a/codex-rs/core/src/tasks/user_shell.rs +++ b/codex-rs/core/src/tasks/user_shell.rs @@ -157,7 +157,7 @@ pub(crate) async fn execute_user_shell_command( }; let shell_snapshot_location = turn_environment.shell_snapshot(&cwd); let mut exec_env_map = create_env( - &turn_context.shell_environment_policy, + &turn_context.config.permissions.shell_environment_policy, Some(session.thread_id), ); if exec_env_map.contains_key(PROXY_ACTIVE_ENV_KEY) { @@ -167,7 +167,11 @@ pub(crate) async fn execute_user_shell_command( &display_command, environment_shell, shell_snapshot_location.as_ref(), - &turn_context.shell_environment_policy.r#set, + &turn_context + .config + .permissions + .shell_environment_policy + .r#set, &mut exec_env_map, ); @@ -294,7 +298,7 @@ pub(crate) async fn execute_user_shell_command( duration: output.duration, formatted_output: format_exec_output_str( &output, - turn_context.truncation_policy, + turn_context.model_info.truncation_policy.into(), ), status: if output.exit_code == 0 { ExecCommandStatus::Completed @@ -339,7 +343,7 @@ pub(crate) async fn execute_user_shell_command( duration: exec_output.duration, formatted_output: format_exec_output_str( &exec_output, - turn_context.truncation_policy, + turn_context.model_info.truncation_policy.into(), ), status: ExecCommandStatus::Failed, }), diff --git a/codex-rs/core/src/tools/code_mode/mod.rs b/codex-rs/core/src/tools/code_mode/mod.rs index 92bb5a8ad..09918033e 100644 --- a/codex-rs/core/src/tools/code_mode/mod.rs +++ b/codex-rs/core/src/tools/code_mode/mod.rs @@ -26,6 +26,7 @@ use crate::tools::ToolRouter; use crate::tools::context::FunctionToolOutput; use crate::tools::context::SharedTurnDiffTracker; use crate::tools::context::ToolPayload; +use crate::tools::effective_tool_mode; use crate::tools::parallel::ToolCallRuntime; use crate::tools::router::ToolCall; use crate::tools::router::ToolCallSource; @@ -116,7 +117,8 @@ impl CodeModeService { router: Arc, tracker: SharedTurnDiffTracker, ) -> Option { - if !matches!(turn.tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) + let tool_mode = effective_tool_mode(turn); + if !matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) || self.session.is_none() { return None; diff --git a/codex-rs/core/src/tools/events.rs b/codex-rs/core/src/tools/events.rs index 2751780d5..c30cee84c 100644 --- a/codex-rs/core/src/tools/events.rs +++ b/codex-rs/core/src/tools/events.rs @@ -346,7 +346,7 @@ impl ToolEmitter { output: &ExecToolCallOutput, ctx: ToolEventCtx<'_>, ) -> String { - super::format_exec_output_for_model(output, ctx.turn.truncation_policy) + super::format_exec_output_for_model(output, ctx.turn.model_info.truncation_policy.into()) } pub async fn finish( @@ -495,7 +495,10 @@ async fn emit_exec_stage( aggregated_output: output.aggregated_output.text.clone(), exit_code: output.exit_code, duration: output.duration, - formatted_output: format_exec_output_str(&output, ctx.turn.truncation_policy), + formatted_output: format_exec_output_str( + &output, + ctx.turn.model_info.truncation_policy.into(), + ), status: if output.exit_code == 0 { ExecCommandStatus::Completed } else { diff --git a/codex-rs/core/src/tools/handlers/apply_patch.rs b/codex-rs/core/src/tools/handlers/apply_patch.rs index 2ce9119e3..9004eb992 100644 --- a/codex-rs/core/src/tools/handlers/apply_patch.rs +++ b/codex-rs/core/src/tools/handlers/apply_patch.rs @@ -82,7 +82,11 @@ impl ToolArgumentDiffConsumer for ApplyPatchArgumentDiffConsumer { call_id: String, diff: &str, ) -> Option { - if !turn.features.enabled(Feature::ApplyPatchStreamingEvents) { + if !turn + .config + .features + .enabled(Feature::ApplyPatchStreamingEvents) + { return None; } diff --git a/codex-rs/core/src/tools/handlers/extension_tools.rs b/codex-rs/core/src/tools/handlers/extension_tools.rs index b59bfaec0..ae79ba405 100644 --- a/codex-rs/core/src/tools/handlers/extension_tools.rs +++ b/codex-rs/core/src/tools/handlers/extension_tools.rs @@ -143,7 +143,7 @@ async fn to_extension_call(invocation: &ToolInvocation) -> ExtensionToolCall { call_id: invocation.call_id.clone(), tool_name: invocation.tool_name.clone(), model: invocation.turn.model_info.slug.clone(), - truncation_policy: invocation.turn.truncation_policy, + truncation_policy: invocation.turn.model_info.truncation_policy.into(), conversation_history, turn_item_emitter: Arc::new(CoreTurnItemEmitter { session: Arc::downgrade(&invocation.session), @@ -315,7 +315,7 @@ mod tests { let weak_turn = Arc::downgrade(&turn); let turn_id = turn.sub_id.clone(); let model = turn.model_info.slug.clone(); - let truncation_policy = turn.truncation_policy; + let truncation_policy = turn.model_info.truncation_policy.into(); let expected_sandbox_cwds = turn .environments .turn_environments diff --git a/codex-rs/core/src/tools/handlers/mcp.rs b/codex-rs/core/src/tools/handlers/mcp.rs index efb32aab9..016a0344c 100644 --- a/codex-rs/core/src/tools/handlers/mcp.rs +++ b/codex-rs/core/src/tools/handlers/mcp.rs @@ -156,7 +156,7 @@ impl McpHandler { tool_input: result.tool_input, wall_time: started.elapsed(), original_image_detail_supported: can_request_original_image_detail(&turn.model_info), - truncation_policy: turn.truncation_policy, + truncation_policy: turn.model_info.truncation_policy.into(), })) } } diff --git a/codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resource_templates.rs b/codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resource_templates.rs index 80b183dab..2af409cf2 100644 --- a/codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resource_templates.rs +++ b/codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resource_templates.rs @@ -115,9 +115,10 @@ impl ListMcpResourceTemplatesHandler { } } .await; + let truncation_policy = turn.model_info.truncation_policy.into(); match payload_result { - Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) { + Ok(payload) => match serialize_function_output(payload, truncation_policy) { Ok(output) => { let content = function_call_output_content_items_to_text(&output.body) .unwrap_or_default(); diff --git a/codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resources.rs b/codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resources.rs index a77d617b9..1a6a6e201 100644 --- a/codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resources.rs +++ b/codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resources.rs @@ -113,9 +113,10 @@ impl ListMcpResourcesHandler { } } .await; + let truncation_policy = turn.model_info.truncation_policy.into(); match payload_result { - Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) { + Ok(payload) => match serialize_function_output(payload, truncation_policy) { Ok(output) => { let content = function_call_output_content_items_to_text(&output.body) .unwrap_or_default(); diff --git a/codex-rs/core/src/tools/handlers/mcp_resource/read_mcp_resource.rs b/codex-rs/core/src/tools/handlers/mcp_resource/read_mcp_resource.rs index 9ca15718a..f1ee6b462 100644 --- a/codex-rs/core/src/tools/handlers/mcp_resource/read_mcp_resource.rs +++ b/codex-rs/core/src/tools/handlers/mcp_resource/read_mcp_resource.rs @@ -96,9 +96,10 @@ impl ReadMcpResourceHandler { }) } .await; + let truncation_policy = turn.model_info.truncation_policy.into(); match payload_result { - Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) { + Ok(payload) => match serialize_function_output(payload, truncation_policy) { Ok(output) => { let content = function_call_output_content_items_to_text(&output.body) .unwrap_or_default(); diff --git a/codex-rs/core/src/tools/handlers/multi_agents_common.rs b/codex-rs/core/src/tools/handlers/multi_agents_common.rs index 595889215..8f91ce5b8 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_common.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_common.rs @@ -228,7 +228,6 @@ fn build_agent_shared_config(turn: &TurnContext) -> Result Result Result ToolMode { + turn_context.model_info.tool_mode.unwrap_or_else(|| { + if turn_context.config.features.enabled(Feature::CodeModeOnly) { + ToolMode::CodeModeOnly + } else if turn_context.config.features.enabled(Feature::CodeMode) { + ToolMode::CodeMode + } else { + ToolMode::Direct + } + }) +} + /// Format the combined exec output for sending back to the model. /// Includes exit code and duration metadata; truncates large bodies safely. pub fn format_exec_output_for_model( diff --git a/codex-rs/core/src/tools/orchestrator.rs b/codex-rs/core/src/tools/orchestrator.rs index 7e97e4ad5..14c6ad74d 100644 --- a/codex-rs/core/src/tools/orchestrator.rs +++ b/codex-rs/core/src/tools/orchestrator.rs @@ -237,7 +237,7 @@ impl ToolOrchestrator { }; // Platform-specific flag gating is handled by SandboxManager::select_initial. - let use_legacy_landlock = turn_ctx.features.use_legacy_landlock(); + let use_legacy_landlock = turn_ctx.config.features.use_legacy_landlock(); #[allow(deprecated)] let sandbox_cwd = tool.sandbox_cwd(req).unwrap_or(&turn_ctx.cwd); let sandbox_policy_cwd = PathUri::from_abs_path(sandbox_cwd); @@ -249,7 +249,7 @@ impl ToolOrchestrator { manager: &self.sandbox, sandbox_cwd: &sandbox_policy_cwd, workspace_roots: workspace_roots.as_slice(), - codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(), + codex_linux_sandbox_exe: turn_ctx.config.codex_linux_sandbox_exe.as_ref(), use_legacy_landlock, windows_sandbox_level: turn_ctx.windows_sandbox_level, windows_sandbox_private_desktop: turn_ctx @@ -413,7 +413,7 @@ impl ToolOrchestrator { let retry_codex_linux_sandbox_exe = if unsandboxed_allowed { None } else { - turn_ctx.codex_linux_sandbox_exe.as_ref() + turn_ctx.config.codex_linux_sandbox_exe.as_ref() }; let retry_attempt = SandboxAttempt { sandbox: retry_sandbox, diff --git a/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs b/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs index ea53e9a55..f2df49708 100644 --- a/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs +++ b/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs @@ -185,8 +185,8 @@ pub(super) async fn try_run_zsh_fork( arg0, sandbox_policy_cwd, windows_sandbox_workspace_roots, - codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(), - use_legacy_landlock: ctx.turn.features.use_legacy_landlock(), + codex_linux_sandbox_exe: ctx.turn.config.codex_linux_sandbox_exe.clone(), + use_legacy_landlock: ctx.turn.config.features.use_legacy_landlock(), }; let main_execve_wrapper_exe = ctx .session @@ -286,8 +286,8 @@ pub(crate) async fn prepare_unified_exec_zsh_fork( arg0: exec_request.arg0.clone(), sandbox_policy_cwd: exec_request.windows_sandbox_policy_cwd.clone(), windows_sandbox_workspace_roots: exec_request.windows_sandbox_workspace_roots.clone(), - codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(), - use_legacy_landlock: ctx.turn.features.use_legacy_landlock(), + codex_linux_sandbox_exe: ctx.turn.config.codex_linux_sandbox_exe.clone(), + use_legacy_landlock: ctx.turn.config.features.use_legacy_landlock(), }; let escalation_policy = CoreShellActionProvider { policy: Arc::clone(&exec_policy), diff --git a/codex-rs/core/src/tools/spec_plan.rs b/codex-rs/core/src/tools/spec_plan.rs index 9269aa379..7680e5d4f 100644 --- a/codex-rs/core/src/tools/spec_plan.rs +++ b/codex-rs/core/src/tools/spec_plan.rs @@ -3,6 +3,7 @@ use crate::agent::next_thread_spawn_depth; use crate::session::turn_context::TurnContext; use crate::tools::code_mode::execute_spec::create_code_mode_tool; use crate::tools::context::ToolInvocation; +use crate::tools::effective_tool_mode; use crate::tools::handlers::ApplyPatchHandler; use crate::tools::handlers::CodeModeExecuteHandler; use crate::tools::handlers::CodeModeWaitHandler; @@ -243,10 +244,9 @@ fn spec_for_model_request( tool_name: &ToolName, spec: ToolSpec, ) -> ToolSpec { - if matches!( - turn_context.tool_mode, - ToolMode::CodeMode | ToolMode::CodeModeOnly - ) && exposure != ToolExposure::DirectModelOnly + let tool_mode = effective_tool_mode(turn_context); + if matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) + && exposure != ToolExposure::DirectModelOnly && !is_excluded_from_code_mode(turn_context, tool_name) && codex_code_mode::is_code_mode_nested_tool(spec.name()) { @@ -298,7 +298,7 @@ pub(crate) fn search_tool_enabled(turn_context: &TurnContext) -> bool { } pub(crate) fn tool_suggest_enabled(turn_context: &TurnContext) -> bool { - let features = turn_context.features.get(); + let features = turn_context.config.features.get(); features.enabled(Feature::ToolSuggest) && features.enabled(Feature::Apps) && features.enabled(Feature::Plugins) @@ -324,7 +324,12 @@ fn collab_tools_enabled(turn_context: &TurnContext) -> bool { } fn agent_jobs_tools_enabled(turn_context: &TurnContext) -> bool { - turn_context.features.get().enabled(Feature::SpawnCsv) && collab_tools_enabled(turn_context) + turn_context + .config + .features + .get() + .enabled(Feature::SpawnCsv) + && collab_tools_enabled(turn_context) } fn agent_jobs_worker_tools_enabled(turn_context: &TurnContext) -> bool { @@ -339,6 +344,7 @@ fn agent_jobs_worker_tools_enabled(turn_context: &TurnContext) -> bool { fn image_generation_tool_enabled(turn_context: &TurnContext) -> bool { image_generation_runtime_enabled(turn_context) && turn_context + .config .features .get() .enabled(Feature::ImageGeneration) @@ -365,7 +371,11 @@ fn standalone_image_generation_model_visible(turn_context: &TurnContext) -> bool return true; } - turn_context.features.get().enabled(Feature::ImageGenExt) + turn_context + .config + .features + .get() + .enabled(Feature::ImageGenExt) } fn standalone_image_generation_available( @@ -412,7 +422,8 @@ fn is_hidden_by_code_mode_only( tool_name: &ToolName, exposure: ToolExposure, ) -> bool { - turn_context.tool_mode == ToolMode::CodeModeOnly + let tool_mode = effective_tool_mode(turn_context); + tool_mode == ToolMode::CodeModeOnly && exposure != ToolExposure::DirectModelOnly && codex_code_mode::is_code_mode_nested_tool(&codex_tools::code_mode_name_for_tool_name( tool_name, @@ -433,10 +444,8 @@ fn build_code_mode_executors( turn_context: &TurnContext, executors: &[Arc], ) -> Vec> { - if !matches!( - turn_context.tool_mode, - ToolMode::CodeMode | ToolMode::CodeModeOnly - ) { + let tool_mode = effective_tool_mode(turn_context); + if !matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) { return vec![]; } @@ -482,7 +491,7 @@ fn build_code_mode_executors( create_code_mode_tool( &enabled_tools, &namespace_descriptions, - turn_context.tool_mode == ToolMode::CodeModeOnly, + tool_mode == ToolMode::CodeModeOnly, deferred_tools_available, ), code_mode_nested_tool_specs, @@ -577,6 +586,7 @@ fn standalone_web_search_enabled(turn_context: &TurnContext) -> bool { namespace_tools_enabled(turn_context) && (turn_context.model_info.use_responses_lite || turn_context + .config .features .get() .enabled(Feature::StandaloneWebSearch)) @@ -584,7 +594,7 @@ fn standalone_web_search_enabled(turn_context: &TurnContext) -> bool { fn add_shell_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut PlannedTools) { let turn_context = context.turn_context; - let features = turn_context.features.get(); + let features = turn_context.config.features.get(); let environment_mode = turn_context.tool_environment_mode(); if !environment_mode.has_environment() { return; @@ -643,7 +653,7 @@ fn add_mcp_resource_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut PlannedTools) { let turn_context = context.turn_context; - let features = turn_context.features.get(); + let features = turn_context.config.features.get(); let environment_mode = turn_context.tool_environment_mode(); planned_tools.add(PlanHandler); @@ -915,10 +925,8 @@ fn append_extension_tool_executors( .iter() .map(|executor| executor.tool_name()) .collect::>(); - if matches!( - turn_context.tool_mode, - ToolMode::CodeMode | ToolMode::CodeModeOnly - ) { + let tool_mode = effective_tool_mode(turn_context); + if matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) { reserved_tool_names.insert(ToolName::plain(codex_code_mode::PUBLIC_TOOL_NAME)); reserved_tool_names.insert(ToolName::plain(codex_code_mode::WAIT_TOOL_NAME)); } diff --git a/codex-rs/core/src/tools/spec_plan_tests.rs b/codex-rs/core/src/tools/spec_plan_tests.rs index 8a324dcc8..43bcf03a6 100644 --- a/codex-rs/core/src/tools/spec_plan_tests.rs +++ b/codex-rs/core/src/tools/spec_plan_tests.rs @@ -197,16 +197,6 @@ async fn probe(configure_turn: impl FnOnce(&mut TurnContext)) -> ToolPlanProbe { } fn set_feature(turn: &mut TurnContext, feature: Feature, enabled: bool) { - if enabled { - turn.features - .enable(feature) - .expect("test feature should be enableable"); - } else { - turn.features - .disable(feature) - .expect("test feature should be disableable"); - } - let mut config = (*turn.config).clone(); if enabled { config @@ -221,15 +211,6 @@ fn set_feature(turn: &mut TurnContext, feature: Feature, enabled: bool) { } turn.multi_agent_version = config.multi_agent_version_from_features(); turn.config = Arc::new(config); - turn.tool_mode = turn.model_info.tool_mode.unwrap_or_else(|| { - if turn.config.features.enabled(Feature::CodeModeOnly) { - ToolMode::CodeModeOnly - } else if turn.config.features.enabled(Feature::CodeMode) { - ToolMode::CodeMode - } else { - ToolMode::Direct - } - }); } fn set_features(turn: &mut TurnContext, features: &[Feature]) { @@ -1196,7 +1177,6 @@ async fn tool_mode_selector_overrides_feature_flags() { let direct = probe(|turn| { set_features(turn, &[Feature::CodeMode, Feature::CodeModeOnly]); turn.model_info.tool_mode = Some(ToolMode::Direct); - turn.tool_mode = ToolMode::Direct; }) .await; direct.assert_visible_lacks(&[ diff --git a/codex-rs/core/src/unified_exec/mod_tests.rs b/codex-rs/core/src/unified_exec/mod_tests.rs index ad39d62b9..dce521f6f 100644 --- a/codex-rs/core/src/unified_exec/mod_tests.rs +++ b/codex-rs/core/src/unified_exec/mod_tests.rs @@ -190,7 +190,7 @@ async fn exec_command_with_tty( chunk_id: generate_chunk_id(), wall_time, raw_output: collected, - truncation_policy: turn.truncation_policy, + truncation_policy: turn.model_info.truncation_policy.into(), max_output_tokens: None, process_id: response_process_id, exit_code, diff --git a/codex-rs/core/src/unified_exec/process_manager.rs b/codex-rs/core/src/unified_exec/process_manager.rs index 702a339e2..e683df963 100644 --- a/codex-rs/core/src/unified_exec/process_manager.rs +++ b/codex-rs/core/src/unified_exec/process_manager.rs @@ -602,7 +602,7 @@ impl UnifiedExecProcessManager { chunk_id, wall_time, raw_output: collected, - truncation_policy: context.turn.truncation_policy, + truncation_policy: context.turn.model_info.truncation_policy.into(), max_output_tokens: request.max_output_tokens, process_id: response_process_id, exit_code, @@ -1030,7 +1030,7 @@ impl UnifiedExecProcessManager { context: &UnifiedExecContext, ) -> Result<(UnifiedExecProcess, Option), UnifiedExecError> { let local_policy_env = create_env( - &context.turn.shell_environment_policy, + &context.turn.config.permissions.shell_environment_policy, /*thread_id*/ None, ); let mut env = local_policy_env.clone(); @@ -1040,7 +1040,9 @@ impl UnifiedExecProcessManager { ); let env = apply_unified_exec_env(env); let exec_server_env_config = ExecServerEnvConfig { - policy: exec_env_policy_from_shell_policy(&context.turn.shell_environment_policy), + policy: exec_env_policy_from_shell_policy( + &context.turn.config.permissions.shell_environment_policy, + ), local_policy_env, }; let mut orchestrator = ToolOrchestrator::new(); @@ -1072,7 +1074,13 @@ impl UnifiedExecProcessManager { turn_environment: request.turn_environment.clone(), env, exec_server_env_config: Some(exec_server_env_config), - explicit_env_overrides: context.turn.shell_environment_policy.r#set.clone(), + explicit_env_overrides: context + .turn + .config + .permissions + .shell_environment_policy + .r#set + .clone(), network: request.network.clone(), tty: request.tty, sandbox_permissions: request.sandbox_permissions, diff --git a/codex-rs/core/src/user_shell_command.rs b/codex-rs/core/src/user_shell_command.rs index cf034faca..e64cf64cd 100644 --- a/codex-rs/core/src/user_shell_command.rs +++ b/codex-rs/core/src/user_shell_command.rs @@ -11,7 +11,10 @@ fn user_shell_command_fragment( exec_output: &ExecToolCallOutput, turn_context: &TurnContext, ) -> UserShellCommand { - let output = format_exec_output_str(exec_output, turn_context.truncation_policy); + let output = format_exec_output_str( + exec_output, + turn_context.model_info.truncation_policy.into(), + ); UserShellCommand::new(command, exec_output.exit_code, exec_output.duration, output) }