mirror of
https://github.com/openai/codex.git
synced 2026-05-24 04:54:52 +00:00
## 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](22dd9ad392/codex-rs/tools/src/tool_config.rs (L97)), [stored on each `TurnContext`](22dd9ad392/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`
147 lines
4.3 KiB
Rust
147 lines
4.3 KiB
Rust
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::openai_models::ConfigShellToolType;
|
|
use codex_protocol::openai_models::ModelInfo;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
pub enum ShellCommandBackendConfig {
|
|
Classic,
|
|
ZshFork,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
pub enum ToolUserShellType {
|
|
Zsh,
|
|
Bash,
|
|
PowerShell,
|
|
Sh,
|
|
Cmd,
|
|
}
|
|
|
|
pub fn request_user_input_available_modes(features: &Features) -> Vec<ModeKind> {
|
|
TUI_VISIBLE_COLLABORATION_MODES
|
|
.into_iter()
|
|
.filter(|mode| {
|
|
mode.allows_request_user_input()
|
|
|| (features.enabled(Feature::DefaultModeRequestUserInput)
|
|
&& *mode == ModeKind::Default)
|
|
})
|
|
.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,
|
|
ZshFork(ZshForkConfig),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub struct ZshForkConfig {
|
|
pub shell_zsh_path: AbsolutePathBuf,
|
|
pub main_execve_wrapper_exe: AbsolutePathBuf,
|
|
}
|
|
|
|
impl UnifiedExecShellMode {
|
|
pub fn for_session(
|
|
shell_command_backend: ShellCommandBackendConfig,
|
|
user_shell_type: ToolUserShellType,
|
|
shell_zsh_path: Option<&PathBuf>,
|
|
main_execve_wrapper_exe: Option<&PathBuf>,
|
|
) -> Self {
|
|
if cfg!(unix)
|
|
&& shell_command_backend == ShellCommandBackendConfig::ZshFork
|
|
&& matches!(user_shell_type, ToolUserShellType::Zsh)
|
|
&& let (Some(shell_zsh_path), Some(main_execve_wrapper_exe)) =
|
|
(shell_zsh_path, main_execve_wrapper_exe)
|
|
&& let (Ok(shell_zsh_path), Ok(main_execve_wrapper_exe)) = (
|
|
AbsolutePathBuf::try_from(shell_zsh_path.as_path()).inspect_err(|err| {
|
|
tracing::warn!(
|
|
"Failed to convert shell_zsh_path `{shell_zsh_path:?}`: {err:?}"
|
|
)
|
|
}),
|
|
AbsolutePathBuf::try_from(main_execve_wrapper_exe.as_path()).inspect_err(
|
|
|err| {
|
|
tracing::warn!(
|
|
"Failed to convert main_execve_wrapper_exe `{main_execve_wrapper_exe:?}`: {err:?}"
|
|
)
|
|
},
|
|
),
|
|
)
|
|
{
|
|
Self::ZshFork(ZshForkConfig {
|
|
shell_zsh_path,
|
|
main_execve_wrapper_exe,
|
|
})
|
|
} else {
|
|
Self::Direct
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum ToolEnvironmentMode {
|
|
None,
|
|
Single,
|
|
Multiple,
|
|
}
|
|
|
|
impl ToolEnvironmentMode {
|
|
pub fn from_count(count: usize) -> Self {
|
|
match count {
|
|
0 => Self::None,
|
|
1 => Self::Single,
|
|
_ => Self::Multiple,
|
|
}
|
|
}
|
|
|
|
pub fn has_environment(self) -> bool {
|
|
!matches!(self, Self::None)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "tool_config_tests.rs"]
|
|
mod tests;
|