diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index 286c87b226..aa5784d4fe 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -60,7 +60,6 @@ pub use codex_mcp::SandboxState; mod mcp_openai_file; mod mcp_tool_call; pub(crate) mod mention_syntax; -mod tool_mode; pub(crate) mod utils; pub use mention_syntax::PLUGIN_TEXT_MENTION_SIGIL; pub use mention_syntax::TOOL_MENTION_SIGIL; diff --git a/codex-rs/core/src/session/review.rs b/codex-rs/core/src/session/review.rs index ddd9585eb4..42583b042c 100644 --- a/codex-rs/core/src/session/review.rs +++ b/codex-rs/core/src/session/review.rs @@ -1,5 +1,4 @@ use super::*; -use crate::tool_mode::resolve_tool_mode; use std::sync::atomic::AtomicBool; /// Spawn a review thread using the given prompt. @@ -48,7 +47,15 @@ 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 = resolve_tool_mode(&model_info, &per_turn_config.features); + 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 + } + }); 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!( diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index ee67e915c2..638bb91198 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -2,7 +2,6 @@ use super::*; use crate::SkillLoadOutcome; use crate::config::GhostSnapshotConfig; use crate::environment_selection::ResolvedTurnEnvironments; -use crate::tool_mode::resolve_tool_mode; use codex_model_provider::SharedModelProvider; use codex_model_provider::create_model_provider; use codex_protocol::SessionId; @@ -165,14 +164,6 @@ impl TurnContext { self.goal_tools_supported && self.features.get().enabled(Feature::Goals) } - pub(crate) fn code_mode_enabled(&self) -> bool { - matches!(self.tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) - } - - pub(crate) fn code_mode_only_enabled(&self) -> bool { - self.tool_mode == ToolMode::CodeModeOnly - } - pub(crate) async fn with_model( &self, model: String, @@ -183,7 +174,15 @@ impl TurnContext { let model_info = models_manager .get_model_info(model.as_str(), &config.to_models_manager_config()) .await; - let tool_mode = resolve_tool_mode(&model_info, &config.features); + 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 @@ -488,7 +487,15 @@ impl Session { ); let mut per_turn_config = per_turn_config; - let tool_mode = resolve_tool_mode(&model_info, &per_turn_config.features); + 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), diff --git a/codex-rs/core/src/tool_mode.rs b/codex-rs/core/src/tool_mode.rs deleted file mode 100644 index 168cc6e8af..0000000000 --- a/codex-rs/core/src/tool_mode.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::config::ManagedFeatures; -use codex_features::Feature; -use codex_protocol::openai_models::ModelInfo; -use codex_protocol::openai_models::ToolMode; - -pub(crate) fn resolve_tool_mode(model_info: &ModelInfo, features: &ManagedFeatures) -> ToolMode { - model_info - .tool_mode - .unwrap_or_else(|| tool_mode_from_features(features)) -} - -fn tool_mode_from_features(features: &ManagedFeatures) -> ToolMode { - if features.enabled(Feature::CodeModeOnly) { - ToolMode::CodeModeOnly - } else if features.enabled(Feature::CodeMode) { - ToolMode::CodeMode - } else { - ToolMode::Direct - } -} - -#[cfg(test)] -mod tests { - use super::*; - use codex_features::Features; - - fn features(enabled: &[Feature]) -> ManagedFeatures { - let mut features = Features::default(); - for feature in enabled { - features.enable(*feature); - } - features.into() - } - - fn model_info(tool_mode: Option) -> ModelInfo { - let mut model_info = codex_models_manager::model_info::model_info_from_slug("test-model"); - model_info.tool_mode = tool_mode; - model_info - } - - #[test] - fn omitted_selector_follows_feature_flags() { - let tool_mode = resolve_tool_mode( - &model_info(/*tool_mode*/ None), - &features(&[Feature::CodeModeOnly]), - ); - - assert_eq!(tool_mode, ToolMode::CodeModeOnly); - } - - #[test] - fn explicit_selector_overrides_feature_flags() { - let tool_mode = resolve_tool_mode( - &model_info(Some(ToolMode::Direct)), - &features(&[Feature::CodeModeOnly]), - ); - - assert_eq!(tool_mode, ToolMode::Direct); - } -} diff --git a/codex-rs/core/src/tools/code_mode/mod.rs b/codex-rs/core/src/tools/code_mode/mod.rs index 1a8d0bbd50..ff005646c2 100644 --- a/codex-rs/core/src/tools/code_mode/mod.rs +++ b/codex-rs/core/src/tools/code_mode/mod.rs @@ -30,6 +30,7 @@ use crate::tools::parallel::ToolCallRuntime; use crate::tools::router::ToolCall; use crate::tools::router::ToolCallSource; use crate::unified_exec::resolve_max_tokens; +use codex_protocol::openai_models::ToolMode; use codex_tools::ToolName; use codex_utils_output_truncation::TruncationPolicy; use codex_utils_output_truncation::formatted_truncate_text_content_items_with_policy; @@ -90,7 +91,7 @@ impl CodeModeService { router: Arc, tracker: SharedTurnDiffTracker, ) -> Option { - if !turn.code_mode_enabled() { + if !matches!(turn.tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) { return None; } diff --git a/codex-rs/core/src/tools/spec_plan.rs b/codex-rs/core/src/tools/spec_plan.rs index c3e078433a..9aaccbbd0f 100644 --- a/codex-rs/core/src/tools/spec_plan.rs +++ b/codex-rs/core/src/tools/spec_plan.rs @@ -60,6 +60,7 @@ use codex_mcp::ToolInfo; use codex_protocol::dynamic_tools::DynamicToolSpec; use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::InputModality; +use codex_protocol::openai_models::ToolMode; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::SubAgentSource; use codex_tools::DiscoverableTool; @@ -230,8 +231,10 @@ fn spec_for_model_request( exposure: ToolExposure, spec: ToolSpec, ) -> ToolSpec { - if code_mode_enabled(turn_context) - && exposure != ToolExposure::DirectModelOnly + if matches!( + turn_context.tool_mode, + ToolMode::CodeMode | ToolMode::CodeModeOnly + ) && exposure != ToolExposure::DirectModelOnly && codex_code_mode::is_code_mode_nested_tool(spec.name()) { codex_tools::augment_tool_spec_for_code_mode(spec) @@ -282,14 +285,6 @@ fn namespace_tools_enabled(turn_context: &TurnContext) -> bool { turn_context.provider.capabilities().namespace_tools } -fn code_mode_enabled(turn_context: &TurnContext) -> bool { - turn_context.code_mode_enabled() -} - -fn code_mode_only_enabled(turn_context: &TurnContext) -> bool { - turn_context.code_mode_only_enabled() -} - fn multi_agent_v2_enabled(turn_context: &TurnContext) -> bool { turn_context.features.get().enabled(Feature::MultiAgentV2) } @@ -398,7 +393,7 @@ fn is_hidden_by_code_mode_only( tool_name: &ToolName, exposure: ToolExposure, ) -> bool { - code_mode_only_enabled(turn_context) + 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, @@ -410,7 +405,10 @@ fn build_code_mode_executors( executors: &[Arc], deferred_tools_available: bool, ) -> Vec> { - if !code_mode_enabled(turn_context) { + if !matches!( + turn_context.tool_mode, + ToolMode::CodeMode | ToolMode::CodeModeOnly + ) { return vec![]; } @@ -444,7 +442,7 @@ fn build_code_mode_executors( create_code_mode_tool( &enabled_tools, &namespace_descriptions, - code_mode_only_enabled(turn_context), + turn_context.tool_mode == ToolMode::CodeModeOnly, deferred_tools_available, ), code_mode_nested_tool_specs, @@ -847,7 +845,10 @@ fn append_extension_tool_executors( .iter() .map(|executor| executor.tool_name()) .collect::>(); - if code_mode_enabled(turn_context) { + if matches!( + turn_context.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 c2e41b7ad1..e059b5f4af 100644 --- a/codex-rs/core/src/tools/spec_plan_tests.rs +++ b/codex-rs/core/src/tools/spec_plan_tests.rs @@ -32,7 +32,6 @@ use serde_json::json; use crate::session::tests::make_session_and_context; use crate::session::turn_context::TurnContext; -use crate::tool_mode::resolve_tool_mode; use crate::tools::handlers::multi_agents_spec::MULTI_AGENT_V1_NAMESPACE; use crate::tools::router::ToolRouter; use crate::tools::router::ToolRouterParams; @@ -217,7 +216,15 @@ fn set_feature(turn: &mut TurnContext, feature: Feature, enabled: bool) { .expect("test feature should be disableable in config"); } turn.config = Arc::new(config); - resolve_tool_mode_for_turn(turn); + 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]) { @@ -232,10 +239,6 @@ fn update_config(turn: &mut TurnContext, update: impl FnOnce(&mut crate::config: turn.config = Arc::new(config); } -fn resolve_tool_mode_for_turn(turn: &mut TurnContext) { - turn.tool_mode = resolve_tool_mode(&turn.model_info, &turn.config.features); -} - fn set_web_search_mode(turn: &mut TurnContext, mode: WebSearchMode) { update_config(turn, |config| { config @@ -809,7 +812,7 @@ 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); - resolve_tool_mode_for_turn(turn); + turn.tool_mode = ToolMode::Direct; }) .await; direct.assert_visible_lacks(&[