Reduce the surface of collaboration modes (#20149)

Collaboration modes were slightly invasive both into ThreadManager
construction and ModelProvider
This commit is contained in:
pakrym-oai
2026-04-29 17:22:41 -07:00
committed by GitHub
parent c8abcbf925
commit fedcefe9da
41 changed files with 121 additions and 346 deletions
+1
View File
@@ -83,6 +83,7 @@ pub use plan_tool::create_update_plan_tool;
pub use request_user_input_tool::REQUEST_USER_INPUT_TOOL_NAME;
pub use request_user_input_tool::create_request_user_input_tool;
pub use request_user_input_tool::normalize_request_user_input_args;
pub use request_user_input_tool::request_user_input_available_modes;
pub use request_user_input_tool::request_user_input_tool_description;
pub use request_user_input_tool::request_user_input_unavailable_message;
pub use responses_api::FreeformTool;
+21 -14
View File
@@ -1,6 +1,8 @@
use crate::JsonSchema;
use crate::ResponsesApiTool;
use crate::ToolSpec;
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::request_user_input::RequestUserInputArgs;
@@ -8,6 +10,17 @@ use std::collections::BTreeMap;
pub const REQUEST_USER_INPUT_TOOL_NAME: &str = "request_user_input";
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 create_request_user_input_tool(description: String) -> ToolSpec {
let option_props = BTreeMap::from([
(
@@ -85,9 +98,9 @@ pub fn create_request_user_input_tool(description: String) -> ToolSpec {
pub fn request_user_input_unavailable_message(
mode: ModeKind,
default_mode_request_user_input: bool,
available_modes: &[ModeKind],
) -> Option<String> {
if request_user_input_is_available(mode, default_mode_request_user_input) {
if available_modes.contains(&mode) {
None
} else {
let mode_name = mode.display_name();
@@ -115,23 +128,17 @@ pub fn normalize_request_user_input_args(
Ok(args)
}
pub fn request_user_input_tool_description(default_mode_request_user_input: bool) -> String {
let allowed_modes = format_allowed_modes(default_mode_request_user_input);
pub fn request_user_input_tool_description(available_modes: &[ModeKind]) -> String {
let allowed_modes = format_allowed_modes(available_modes);
format!(
"Request user input for one to three short questions and wait for the response. This tool is only available in {allowed_modes}."
)
}
fn request_user_input_is_available(mode: ModeKind, default_mode_request_user_input: bool) -> bool {
mode.allows_request_user_input()
|| (default_mode_request_user_input && mode == ModeKind::Default)
}
fn format_allowed_modes(default_mode_request_user_input: bool) -> String {
let mode_names: Vec<&str> = TUI_VISIBLE_COLLABORATION_MODES
.into_iter()
.filter(|mode| request_user_input_is_available(*mode, default_mode_request_user_input))
.map(ModeKind::display_name)
fn format_allowed_modes(available_modes: &[ModeKind]) -> String {
let mode_names: Vec<&str> = available_modes
.iter()
.map(|mode| mode.display_name())
.collect();
match mode_names.as_slice() {
@@ -1,9 +1,21 @@
use super::*;
use crate::JsonSchema;
use codex_features::Feature;
use codex_features::Features;
use codex_protocol::config_types::ModeKind;
use pretty_assertions::assert_eq;
use std::collections::BTreeMap;
fn default_mode_enabled_available_modes() -> Vec<ModeKind> {
let mut features = Features::with_defaults();
features.enable(Feature::DefaultModeRequestUserInput);
request_user_input_available_modes(&features)
}
fn default_available_modes() -> Vec<ModeKind> {
request_user_input_available_modes(&Features::with_defaults())
}
#[test]
fn request_user_input_tool_includes_questions_schema() {
assert_eq!(
@@ -92,37 +104,28 @@ fn request_user_input_tool_includes_questions_schema() {
#[test]
fn request_user_input_unavailable_messages_respect_default_mode_feature_flag() {
assert_eq!(
request_user_input_unavailable_message(
ModeKind::Plan,
/*default_mode_request_user_input*/ false
),
request_user_input_unavailable_message(ModeKind::Plan, &default_available_modes()),
None
);
assert_eq!(
request_user_input_unavailable_message(
ModeKind::Default,
/*default_mode_request_user_input*/ false
),
request_user_input_unavailable_message(ModeKind::Default, &default_available_modes()),
Some("request_user_input is unavailable in Default mode".to_string())
);
assert_eq!(
request_user_input_unavailable_message(
ModeKind::Default,
/*default_mode_request_user_input*/ true
&default_mode_enabled_available_modes()
),
None
);
assert_eq!(
request_user_input_unavailable_message(
ModeKind::Execute,
/*default_mode_request_user_input*/ false
),
request_user_input_unavailable_message(ModeKind::Execute, &default_available_modes()),
Some("request_user_input is unavailable in Execute mode".to_string())
);
assert_eq!(
request_user_input_unavailable_message(
ModeKind::PairProgramming,
/*default_mode_request_user_input*/ false
&default_available_modes()
),
Some("request_user_input is unavailable in Pair Programming mode".to_string())
);
@@ -131,11 +134,11 @@ fn request_user_input_unavailable_messages_respect_default_mode_feature_flag() {
#[test]
fn request_user_input_tool_description_mentions_available_modes() {
assert_eq!(
request_user_input_tool_description(/*default_mode_request_user_input*/ false),
request_user_input_tool_description(&default_available_modes()),
"Request user input for one to three short questions and wait for the response. This tool is only available in Plan mode.".to_string()
);
assert_eq!(
request_user_input_tool_description(/*default_mode_request_user_input*/ true),
request_user_input_tool_description(&default_mode_enabled_available_modes()),
"Request user input for one to three short questions and wait for the response. This tool is only available in Default or Plan mode.".to_string()
);
}
+4 -4
View File
@@ -1,6 +1,8 @@
use crate::can_request_original_image_detail;
use crate::request_user_input_available_modes;
use codex_features::Feature;
use codex_features::Features;
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::WebSearchConfig;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
@@ -109,7 +111,7 @@ pub struct ToolsConfig {
pub spawn_agent_usage_hint_text: Option<String>,
pub max_concurrent_threads_per_session: Option<usize>,
pub wait_agent_min_timeout_ms: Option<i64>,
pub default_mode_request_user_input: bool,
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,
@@ -145,8 +147,6 @@ impl ToolsConfig {
let include_goal_tools = features.enabled(Feature::Goals);
let include_multi_agent_v2 = features.enabled(Feature::MultiAgentV2);
let include_agent_jobs = features.enabled(Feature::SpawnCsv);
let include_default_mode_request_user_input =
features.enabled(Feature::DefaultModeRequestUserInput);
let include_search_tool =
model_info.supports_search_tool && features.enabled(Feature::ToolSearch);
let include_tool_suggest = features.enabled(Feature::ToolSuggest)
@@ -228,7 +228,7 @@ impl ToolsConfig {
spawn_agent_usage_hint_text: None,
max_concurrent_threads_per_session: None,
wait_agent_min_timeout_ms: None,
default_mode_request_user_input: include_default_mode_request_user_input,
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,
+5 -2
View File
@@ -155,7 +155,7 @@ fn shell_zsh_fork_prefers_shell_command_over_unified_exec() {
}
#[test]
fn subagents_keep_request_user_input_mode_config_and_agent_jobs_workers_opt_in_by_label() {
fn subagents_keep_request_user_input_config_and_agent_jobs_workers_opt_in_by_label() {
let model_info = model_info();
let mut features = Features::with_defaults();
features.enable(Feature::DefaultModeRequestUserInput);
@@ -175,7 +175,10 @@ fn subagents_keep_request_user_input_mode_config_and_agent_jobs_workers_opt_in_b
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
assert!(tools_config.default_mode_request_user_input);
assert_eq!(
tools_config.request_user_input_available_modes,
request_user_input_available_modes(&features)
);
assert!(tools_config.agent_jobs_tools);
assert!(tools_config.agent_jobs_worker_tools);
}
+1 -1
View File
@@ -241,7 +241,7 @@ pub fn build_tool_registry_plan(
plan.push_spec(
create_request_user_input_tool(request_user_input_tool_description(
config.default_mode_request_user_input,
&config.request_user_input_available_modes,
)),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
@@ -19,9 +19,11 @@ use crate::ToolRegistryPlanMcpTool;
use crate::ToolsConfigParams;
use crate::WaitAgentTimeoutOptions;
use crate::mcp_call_tool_result_output_schema;
use crate::request_user_input_available_modes;
use codex_app_server_protocol::AppInfo;
use codex_features::Feature;
use codex_features::Features;
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::WebSearchConfig;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
@@ -88,7 +90,7 @@ fn test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search() {
}),
create_write_stdin_tool(),
create_update_plan_tool(),
request_user_input_tool_spec(/*default_mode_request_user_input*/ false),
request_user_input_tool_spec(&request_user_input_available_modes(&features)),
create_apply_patch_freeform_tool(),
ToolSpec::WebSearch {
external_web_access: Some(true),
@@ -586,7 +588,7 @@ fn request_user_input_description_reflects_default_mode_feature_flag() {
let request_user_input_tool = find_tool(&tools, REQUEST_USER_INPUT_TOOL_NAME);
assert_eq!(
request_user_input_tool.spec,
request_user_input_tool_spec(/*default_mode_request_user_input*/ false)
request_user_input_tool_spec(&request_user_input_available_modes(&features))
);
features.enable(Feature::DefaultModeRequestUserInput);
@@ -609,7 +611,7 @@ fn request_user_input_description_reflects_default_mode_feature_flag() {
let request_user_input_tool = find_tool(&tools, REQUEST_USER_INPUT_TOOL_NAME);
assert_eq!(
request_user_input_tool.spec,
request_user_input_tool_spec(/*default_mode_request_user_input*/ true)
request_user_input_tool_spec(&request_user_input_available_modes(&features))
);
}
@@ -2357,10 +2359,8 @@ fn assert_lacks_tool_name(tools: &[ConfiguredToolSpec], expected_absent: &str) {
);
}
fn request_user_input_tool_spec(default_mode_request_user_input: bool) -> ToolSpec {
create_request_user_input_tool(request_user_input_tool_description(
default_mode_request_user_input,
))
fn request_user_input_tool_spec(available_modes: &[ModeKind]) -> ToolSpec {
create_request_user_input_tool(request_user_input_tool_description(available_modes))
}
fn spawn_agent_tool_options(config: &ToolsConfig) -> SpawnAgentToolOptions<'_> {