mirror of
https://github.com/openai/codex.git
synced 2026-11-13 22:47:20 +00:00
Remove ToolsConfig from tool planning (#22835)
## Why `codex-tools` is meant to hold reusable tool primitives, but `ToolsConfig` had become a second copy of core runtime decisions instead of a small shared contract. It carried provider capabilities, auth/model gates, permission and environment state, web/search/image feature gates, multi-agent settings, and goal availability from core into `codex-tools` ([definition](https://github.com/openai/codex/blob/22dd9ad3929253ed24d7ee4f10f238e95ab25f37/codex-rs/tools/src/tool_config.rs#L97), [stored on each `TurnContext`](https://github.com/openai/codex/blob/22dd9ad3929253ed24d7ee4f10f238e95ab25f37/codex-rs/core/src/session/turn_context.rs#L87)). Every session/context variant then had to build and mutate that snapshot before assembling tools. This PR removes that master object instead of renaming it. Tool planning now reads the live `TurnContext`, where `codex-core` already owns those decisions, while `codex-tools` keeps only reusable primitives and a generic `ToolSetBuilder`/`ToolSet` accumulator. ## What Changed - Removed `ToolsConfig` / `ToolsConfigParams` from `codex-tools`; the crate keeps the shared helpers that still belong there, including request-user-input mode selection, shell backend/type resolution, `UnifiedExecShellMode`, and `ToolEnvironmentMode`. - Replaced config-snapshot planning with `ToolRouter::from_turn_context` and a `spec_plan` pipeline over `CoreToolPlanContext`, deriving provider capabilities, auth gates, model support, feature gates, environment count, goal support, multi-agent options, web search, and image generation from the authoritative turn state. - Added generic `codex_tools::ToolSetBuilder` / `ToolSet`, plus the small core adapter needed to accumulate `CoreToolRuntime` values and hosted model specs. - Added the `tool_family::shell` registration module and moved shell/unified-exec/memory accounting call sites to read the narrow per-turn fields directly. - Narrowed `TurnContext` to the remaining explicit per-turn fields needed by planning: `available_models`, `unified_exec_shell_mode`, and `goal_tools_supported`. - Reworked MCP exposure and tool-search setup so deferred/direct MCP behavior is driven by the current turn rather than a precomputed config snapshot. - Replaced the large expected-spec fixture tests with focused behavior-level coverage for shell tools, environments, goal and agent-job gates, MCP direct/deferred exposure, tool search, request-plugin-install, code mode, multi-agent mode, hosted tools, and extension executor dispatch. ## Verification - `cargo check -p codex-tools` - `cargo check -p codex-core --lib` - `cargo test -p codex-tools` - `cargo test -p codex-core spec_plan --lib` - `cargo test -p codex-core router --lib`
This commit is contained in:
1 parent
ba57aab13a
commit
05e171094d
25 files changed
+1426
-5232
No files matched your search
@@ -1,20 +1,9 @@
|
||||
use crate::can_request_original_image_detail;
|
||||
use codex_features::Feature;
|
||||
use codex_features::Features;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::TUI_VISIBLE_COLLABORATION_MODES;
|
||||
use codex_protocol::config_types::WebSearchConfig;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ApplyPatchToolType;
|
||||
use codex_protocol::openai_models::ConfigShellToolType;
|
||||
use codex_protocol::openai_models::InputModality;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::WebSearchToolType;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -44,6 +33,44 @@ pub fn request_user_input_available_modes(features: &Features) -> Vec<ModeKind>
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn shell_command_backend_for_features(features: &Features) -> ShellCommandBackendConfig {
|
||||
if features.enabled(Feature::ShellTool) && features.enabled(Feature::ShellZshFork) {
|
||||
ShellCommandBackendConfig::ZshFork
|
||||
} else {
|
||||
ShellCommandBackendConfig::Classic
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shell_type_for_model_and_features(
|
||||
model_info: &ModelInfo,
|
||||
features: &Features,
|
||||
) -> ConfigShellToolType {
|
||||
let unified_exec_enabled = features.enabled(Feature::UnifiedExec);
|
||||
let model_shell_type = match model_info.shell_type {
|
||||
ConfigShellToolType::UnifiedExec if !unified_exec_enabled => {
|
||||
ConfigShellToolType::ShellCommand
|
||||
}
|
||||
ConfigShellToolType::Default | ConfigShellToolType::Local => {
|
||||
ConfigShellToolType::ShellCommand
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
|
||||
if !features.enabled(Feature::ShellTool) {
|
||||
ConfigShellToolType::Disabled
|
||||
} else if features.enabled(Feature::ShellZshFork) {
|
||||
ConfigShellToolType::ShellCommand
|
||||
} else if unified_exec_enabled {
|
||||
if codex_utils_pty::conpty_supported() {
|
||||
ConfigShellToolType::UnifiedExec
|
||||
} else {
|
||||
ConfigShellToolType::ShellCommand
|
||||
}
|
||||
} else {
|
||||
model_shell_type
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum UnifiedExecShellMode {
|
||||
Direct,
|
||||
@@ -93,57 +120,6 @@ impl UnifiedExecShellMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ToolsConfig {
|
||||
pub available_models: Vec<ModelPreset>,
|
||||
pub shell_type: ConfigShellToolType,
|
||||
pub shell_command_backend: ShellCommandBackendConfig,
|
||||
pub unified_exec_shell_mode: UnifiedExecShellMode,
|
||||
pub environment_mode: ToolEnvironmentMode,
|
||||
pub allow_login_shell: bool,
|
||||
pub apply_patch_tool_type: Option<ApplyPatchToolType>,
|
||||
pub web_search_mode: Option<WebSearchMode>,
|
||||
pub web_search_config: Option<WebSearchConfig>,
|
||||
pub web_search_tool_type: WebSearchToolType,
|
||||
pub image_gen_tool: bool,
|
||||
pub search_tool: bool,
|
||||
pub namespace_tools: bool,
|
||||
pub tool_suggest: bool,
|
||||
pub exec_permission_approvals_enabled: bool,
|
||||
pub request_permissions_tool_enabled: bool,
|
||||
pub code_mode_enabled: bool,
|
||||
pub code_mode_only_enabled: bool,
|
||||
pub can_request_original_image_detail: bool,
|
||||
pub collab_tools: bool,
|
||||
pub goal_tools: bool,
|
||||
pub multi_agent_v2: bool,
|
||||
pub multi_agent_v2_non_code_mode_only: bool,
|
||||
pub hide_spawn_agent_metadata: bool,
|
||||
pub spawn_agent_usage_hint: bool,
|
||||
pub spawn_agent_usage_hint_text: Option<String>,
|
||||
pub multi_agent_v2_tool_namespace: Option<String>,
|
||||
pub max_concurrent_threads_per_session: Option<usize>,
|
||||
pub wait_agent_min_timeout_ms: Option<i64>,
|
||||
pub wait_agent_max_timeout_ms: Option<i64>,
|
||||
pub wait_agent_default_timeout_ms: Option<i64>,
|
||||
pub request_user_input_available_modes: Vec<ModeKind>,
|
||||
pub experimental_supported_tools: Vec<String>,
|
||||
pub agent_jobs_tools: bool,
|
||||
pub agent_jobs_worker_tools: bool,
|
||||
pub agent_type_description: String,
|
||||
}
|
||||
|
||||
pub struct ToolsConfigParams<'a> {
|
||||
pub model_info: &'a ModelInfo,
|
||||
pub available_models: &'a [ModelPreset],
|
||||
pub features: &'a Features,
|
||||
pub image_generation_tool_auth_allowed: bool,
|
||||
pub web_search_mode: Option<WebSearchMode>,
|
||||
pub session_source: SessionSource,
|
||||
pub permission_profile: &'a PermissionProfile,
|
||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ToolEnvironmentMode {
|
||||
None,
|
||||
@@ -165,262 +141,6 @@ impl ToolEnvironmentMode {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolsConfig {
|
||||
pub fn new(params: &ToolsConfigParams<'_>) -> Self {
|
||||
let ToolsConfigParams {
|
||||
model_info,
|
||||
available_models,
|
||||
features,
|
||||
image_generation_tool_auth_allowed,
|
||||
web_search_mode,
|
||||
session_source,
|
||||
..
|
||||
} = params;
|
||||
let include_code_mode = features.enabled(Feature::CodeMode);
|
||||
let include_code_mode_only = include_code_mode && features.enabled(Feature::CodeModeOnly);
|
||||
let include_goal_tools = features.enabled(Feature::Goals);
|
||||
let include_multi_agent_v2 = features.enabled(Feature::MultiAgentV2);
|
||||
let include_collab_tools = include_multi_agent_v2 || features.enabled(Feature::Collab);
|
||||
let include_agent_jobs = features.enabled(Feature::SpawnCsv);
|
||||
let include_search_tool = model_info.supports_search_tool;
|
||||
let include_tool_suggest = features.enabled(Feature::ToolSuggest)
|
||||
&& features.enabled(Feature::Apps)
|
||||
&& features.enabled(Feature::Plugins);
|
||||
let include_original_image_detail = can_request_original_image_detail(model_info);
|
||||
// API-key auth bypasses Codex backend entitlement/tool normalization, so
|
||||
// callers must confirm ChatGPT auth before exposing the built-in tool.
|
||||
let include_image_gen_tool = *image_generation_tool_auth_allowed
|
||||
&& features.enabled(Feature::ImageGeneration)
|
||||
&& supports_image_generation(model_info);
|
||||
let exec_permission_approvals_enabled = features.enabled(Feature::ExecPermissionApprovals);
|
||||
let request_permissions_tool_enabled = features.enabled(Feature::RequestPermissionsTool);
|
||||
let shell_command_backend =
|
||||
if features.enabled(Feature::ShellTool) && features.enabled(Feature::ShellZshFork) {
|
||||
ShellCommandBackendConfig::ZshFork
|
||||
} else {
|
||||
ShellCommandBackendConfig::Classic
|
||||
};
|
||||
let unified_exec_enabled = features.enabled(Feature::UnifiedExec);
|
||||
let model_shell_type = match model_info.shell_type {
|
||||
ConfigShellToolType::UnifiedExec if !unified_exec_enabled => {
|
||||
ConfigShellToolType::ShellCommand
|
||||
}
|
||||
ConfigShellToolType::Default | ConfigShellToolType::Local => {
|
||||
ConfigShellToolType::ShellCommand
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
let shell_type = if !features.enabled(Feature::ShellTool) {
|
||||
ConfigShellToolType::Disabled
|
||||
} else if features.enabled(Feature::ShellZshFork) {
|
||||
ConfigShellToolType::ShellCommand
|
||||
} else if unified_exec_enabled {
|
||||
if codex_utils_pty::conpty_supported() {
|
||||
ConfigShellToolType::UnifiedExec
|
||||
} else {
|
||||
ConfigShellToolType::ShellCommand
|
||||
}
|
||||
} else {
|
||||
model_shell_type
|
||||
};
|
||||
|
||||
let apply_patch_tool_type = model_info.apply_patch_tool_type.clone();
|
||||
|
||||
let agent_jobs_worker_tools = include_agent_jobs
|
||||
&& matches!(
|
||||
session_source,
|
||||
SessionSource::SubAgent(SubAgentSource::Other(label))
|
||||
if label.starts_with("agent_job:")
|
||||
);
|
||||
|
||||
Self {
|
||||
available_models: available_models.to_vec(),
|
||||
shell_type,
|
||||
shell_command_backend,
|
||||
unified_exec_shell_mode: UnifiedExecShellMode::Direct,
|
||||
environment_mode: ToolEnvironmentMode::Single,
|
||||
allow_login_shell: true,
|
||||
apply_patch_tool_type,
|
||||
web_search_mode: *web_search_mode,
|
||||
web_search_config: None,
|
||||
web_search_tool_type: model_info.web_search_tool_type,
|
||||
image_gen_tool: include_image_gen_tool,
|
||||
search_tool: include_search_tool,
|
||||
namespace_tools: true,
|
||||
tool_suggest: include_tool_suggest,
|
||||
exec_permission_approvals_enabled,
|
||||
request_permissions_tool_enabled,
|
||||
code_mode_enabled: include_code_mode,
|
||||
code_mode_only_enabled: include_code_mode_only,
|
||||
can_request_original_image_detail: include_original_image_detail,
|
||||
collab_tools: include_collab_tools,
|
||||
goal_tools: include_goal_tools,
|
||||
multi_agent_v2: include_multi_agent_v2,
|
||||
multi_agent_v2_non_code_mode_only: false,
|
||||
hide_spawn_agent_metadata: false,
|
||||
spawn_agent_usage_hint: true,
|
||||
spawn_agent_usage_hint_text: None,
|
||||
multi_agent_v2_tool_namespace: None,
|
||||
max_concurrent_threads_per_session: None,
|
||||
wait_agent_min_timeout_ms: None,
|
||||
wait_agent_max_timeout_ms: None,
|
||||
wait_agent_default_timeout_ms: None,
|
||||
request_user_input_available_modes: request_user_input_available_modes(features),
|
||||
experimental_supported_tools: model_info.experimental_supported_tools.clone(),
|
||||
agent_jobs_tools: include_agent_jobs,
|
||||
agent_jobs_worker_tools,
|
||||
agent_type_description: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_agent_type_description(mut self, agent_type_description: String) -> Self {
|
||||
self.agent_type_description = agent_type_description;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_namespace_tools_capability(mut self, namespace_tools: bool) -> Self {
|
||||
if !namespace_tools {
|
||||
self.namespace_tools = false;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_image_generation_capability(mut self, image_generation: bool) -> Self {
|
||||
if !image_generation {
|
||||
self.image_gen_tool = false;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_web_search_capability(mut self, web_search: bool) -> Self {
|
||||
if !web_search {
|
||||
self.web_search_mode = None;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_spawn_agent_usage_hint(mut self, spawn_agent_usage_hint: bool) -> Self {
|
||||
self.spawn_agent_usage_hint = spawn_agent_usage_hint;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_spawn_agent_usage_hint_text(
|
||||
mut self,
|
||||
spawn_agent_usage_hint_text: Option<String>,
|
||||
) -> Self {
|
||||
self.spawn_agent_usage_hint_text = spawn_agent_usage_hint_text;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_hide_spawn_agent_metadata(mut self, hide_spawn_agent_metadata: bool) -> Self {
|
||||
self.hide_spawn_agent_metadata = hide_spawn_agent_metadata;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_multi_agent_v2_tool_namespace(
|
||||
mut self,
|
||||
multi_agent_v2_tool_namespace: Option<String>,
|
||||
) -> Self {
|
||||
self.multi_agent_v2_tool_namespace = multi_agent_v2_tool_namespace;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_multi_agent_v2_non_code_mode_only(
|
||||
mut self,
|
||||
multi_agent_v2_non_code_mode_only: bool,
|
||||
) -> Self {
|
||||
self.multi_agent_v2_non_code_mode_only =
|
||||
self.multi_agent_v2 && multi_agent_v2_non_code_mode_only;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_goal_tools_allowed(mut self, allowed: bool) -> Self {
|
||||
self.goal_tools = self.goal_tools && allowed;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_max_concurrent_threads_per_session(
|
||||
mut self,
|
||||
max_concurrent_threads_per_session: Option<usize>,
|
||||
) -> Self {
|
||||
self.max_concurrent_threads_per_session = max_concurrent_threads_per_session;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_wait_agent_min_timeout_ms(
|
||||
mut self,
|
||||
wait_agent_min_timeout_ms: Option<i64>,
|
||||
) -> Self {
|
||||
self.wait_agent_min_timeout_ms = wait_agent_min_timeout_ms;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_wait_agent_max_timeout_ms(
|
||||
mut self,
|
||||
wait_agent_max_timeout_ms: Option<i64>,
|
||||
) -> Self {
|
||||
self.wait_agent_max_timeout_ms = wait_agent_max_timeout_ms;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_wait_agent_default_timeout_ms(
|
||||
mut self,
|
||||
wait_agent_default_timeout_ms: Option<i64>,
|
||||
) -> Self {
|
||||
self.wait_agent_default_timeout_ms = wait_agent_default_timeout_ms;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_allow_login_shell(mut self, allow_login_shell: bool) -> Self {
|
||||
self.allow_login_shell = allow_login_shell;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_environment_mode(mut self, environment_mode: ToolEnvironmentMode) -> Self {
|
||||
self.environment_mode = environment_mode;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_unified_exec_shell_mode(
|
||||
mut self,
|
||||
unified_exec_shell_mode: UnifiedExecShellMode,
|
||||
) -> Self {
|
||||
self.unified_exec_shell_mode = unified_exec_shell_mode;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_unified_exec_shell_mode_for_session(
|
||||
mut self,
|
||||
user_shell_type: ToolUserShellType,
|
||||
shell_zsh_path: Option<&PathBuf>,
|
||||
main_execve_wrapper_exe: Option<&PathBuf>,
|
||||
) -> Self {
|
||||
self.unified_exec_shell_mode = UnifiedExecShellMode::for_session(
|
||||
self.shell_command_backend,
|
||||
user_shell_type,
|
||||
shell_zsh_path,
|
||||
main_execve_wrapper_exe,
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_web_search_config(mut self, web_search_config: Option<WebSearchConfig>) -> Self {
|
||||
self.web_search_config = web_search_config;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn for_code_mode_nested_tools(&self) -> Self {
|
||||
let mut nested = self.clone();
|
||||
nested.code_mode_enabled = false;
|
||||
nested.code_mode_only_enabled = false;
|
||||
nested
|
||||
}
|
||||
}
|
||||
|
||||
fn supports_image_generation(model_info: &ModelInfo) -> bool {
|
||||
model_info.input_modalities.contains(&InputModality::Image)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "tool_config_tests.rs"]
|
||||
mod tests;
|
||||
Reference in new issue
Block a user