Gate default-mode request_user_input behind new feature

This commit is contained in:
Charles Cunningham
2026-02-24 14:52:38 -08:00
parent 473bf8b8f5
commit 0fff60e88b
20 changed files with 157 additions and 87 deletions
@@ -177,6 +177,9 @@ impl MessageProcessor {
auth_manager.clone(),
SessionSource::VSCode,
config.model_catalog.clone(),
config
.features
.enabled(codex_core::features::Feature::DefaultModeRequestUserInput),
));
let cloud_requirements = Arc::new(RwLock::new(cloud_requirements));
let codex_message_processor = CodexMessageProcessor::new(CodexMessageProcessorArgs {
@@ -123,9 +123,6 @@ sandbox_mode = "read-only"
model_provider = "mock_provider"
[features]
collaboration_modes = true
[model_providers.mock_provider]
name = "Mock provider for test"
base_url = "{server_uri}/v1"
@@ -352,7 +352,7 @@ async fn turn_start_accepts_collaboration_mode_override_v2() -> Result<()> {
codex_home.path(),
&server.uri(),
"never",
&BTreeMap::default(),
&BTreeMap::from([(Feature::DefaultModeRequestUserInput, true)]),
)?;
let mut mcp = McpProcess::new(codex_home.path()).await?;
+3
View File
@@ -8059,6 +8059,7 @@ mod tests {
config.codex_home.clone(),
auth_manager.clone(),
None,
false,
));
let model = ModelsManager::get_model_offline_for_tests(config.model.as_deref());
let model_info =
@@ -8134,6 +8135,7 @@ mod tests {
config.codex_home.clone(),
auth_manager.clone(),
None,
false,
));
let agent_control = AgentControl::default();
let exec_policy = ExecPolicyManager::default();
@@ -8289,6 +8291,7 @@ mod tests {
config.codex_home.clone(),
auth_manager.clone(),
None,
false,
));
let agent_control = AgentControl::default();
let exec_policy = ExecPolicyManager::default();
+8
View File
@@ -137,6 +137,8 @@ pub enum Feature {
SkillApproval,
/// Steer feature flag - when enabled, Enter submits immediately instead of queuing.
Steer,
/// Allow request_user_input in Default collaboration mode.
DefaultModeRequestUserInput,
/// Enable collaboration modes (Plan, Default).
/// Kept for config backward compatibility; behavior is always collaboration-modes-enabled.
CollaborationModes,
@@ -635,6 +637,12 @@ pub const FEATURES: &[FeatureSpec] = &[
stage: Stage::Stable,
default_enabled: true,
},
FeatureSpec {
id: Feature::DefaultModeRequestUserInput,
key: "default_mode_request_user_input",
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::CollaborationModes,
key: "collaboration_modes",
@@ -9,8 +9,13 @@ const COLLABORATION_MODE_DEFAULT: &str =
const KNOWN_MODE_NAMES_PLACEHOLDER: &str = "{{KNOWN_MODE_NAMES}}";
const REQUEST_USER_INPUT_AVAILABILITY_PLACEHOLDER: &str = "{{REQUEST_USER_INPUT_AVAILABILITY}}";
pub(crate) fn builtin_collaboration_mode_presets() -> Vec<CollaborationModeMask> {
vec![plan_preset(), default_preset()]
pub(crate) fn builtin_collaboration_mode_presets(
default_mode_request_user_input: bool,
) -> Vec<CollaborationModeMask> {
vec![
plan_preset(),
default_preset(default_mode_request_user_input),
]
}
fn plan_preset() -> CollaborationModeMask {
@@ -23,20 +28,22 @@ fn plan_preset() -> CollaborationModeMask {
}
}
fn default_preset() -> CollaborationModeMask {
fn default_preset(default_mode_request_user_input: bool) -> CollaborationModeMask {
CollaborationModeMask {
name: ModeKind::Default.display_name().to_string(),
mode: Some(ModeKind::Default),
model: None,
reasoning_effort: None,
developer_instructions: Some(Some(default_mode_instructions())),
developer_instructions: Some(Some(default_mode_instructions(
default_mode_request_user_input,
))),
}
}
fn default_mode_instructions() -> String {
fn default_mode_instructions(default_mode_request_user_input: bool) -> String {
let known_mode_names = format_mode_names(&TUI_VISIBLE_COLLABORATION_MODES);
let request_user_input_availability =
request_user_input_availability_message(ModeKind::Default);
request_user_input_availability_message(ModeKind::Default, default_mode_request_user_input);
COLLABORATION_MODE_DEFAULT
.replace(KNOWN_MODE_NAMES_PLACEHOLDER, &known_mode_names)
.replace(
@@ -55,9 +62,14 @@ fn format_mode_names(modes: &[ModeKind]) -> String {
}
}
fn request_user_input_availability_message(mode: ModeKind) -> String {
fn request_user_input_availability_message(
mode: ModeKind,
default_mode_request_user_input: bool,
) -> String {
let mode_name = mode.display_name();
if mode.allows_request_user_input() {
if mode.allows_request_user_input()
|| (default_mode_request_user_input && mode == ModeKind::Default)
{
format!("The `request_user_input` tool is available in {mode_name} mode.")
} else {
format!(
@@ -74,7 +86,7 @@ mod tests {
#[test]
fn preset_names_use_mode_display_names() {
assert_eq!(plan_preset().name, ModeKind::Plan.display_name());
assert_eq!(default_preset().name, ModeKind::Default.display_name());
assert_eq!(default_preset(false).name, ModeKind::Default.display_name());
assert_eq!(
plan_preset().reasoning_effort,
Some(Some(ReasoningEffort::Medium))
@@ -83,7 +95,7 @@ mod tests {
#[test]
fn default_mode_instructions_replace_mode_names_placeholder() {
let default_instructions = default_preset()
let default_instructions = default_preset(true)
.developer_instructions
.expect("default preset should include instructions")
.expect("default instructions should be set");
@@ -96,7 +108,7 @@ mod tests {
assert!(default_instructions.contains(&expected_snippet));
let expected_availability_message =
request_user_input_availability_message(ModeKind::Default);
request_user_input_availability_message(ModeKind::Default, true);
assert!(default_instructions.contains(&expected_availability_message));
}
}
+12 -4
View File
@@ -46,6 +46,7 @@ pub enum RefreshStrategy {
pub struct ModelsManager {
remote_models: RwLock<Vec<ModelInfo>>,
has_custom_model_catalog: bool,
default_mode_request_user_input: bool,
auth_manager: Arc<AuthManager>,
etag: RwLock<Option<String>>,
cache_manager: ModelsCacheManager,
@@ -62,6 +63,7 @@ impl ModelsManager {
codex_home: PathBuf,
auth_manager: Arc<AuthManager>,
model_catalog: Option<ModelsResponse>,
default_mode_request_user_input: bool,
) -> Self {
let cache_path = codex_home.join(MODEL_CACHE_FILE);
let cache_manager = ModelsCacheManager::new(cache_path, DEFAULT_MODEL_CACHE_TTL);
@@ -75,6 +77,7 @@ impl ModelsManager {
Self {
remote_models: RwLock::new(remote_models),
has_custom_model_catalog,
default_mode_request_user_input,
auth_manager,
etag: RwLock::new(None),
cache_manager,
@@ -97,7 +100,7 @@ impl ModelsManager {
///
/// Returns a static set of presets seeded with the configured model.
pub fn list_collaboration_modes(&self) -> Vec<CollaborationModeMask> {
builtin_collaboration_mode_presets()
builtin_collaboration_mode_presets(self.default_mode_request_user_input)
}
/// Attempt to list models without blocking, using the current cached state.
@@ -365,6 +368,7 @@ impl ModelsManager {
.unwrap_or_else(|err| panic!("failed to load bundled models.json: {err}")),
),
has_custom_model_catalog: false,
default_mode_request_user_input: false,
auth_manager,
etag: RwLock::new(None),
cache_manager,
@@ -491,7 +495,8 @@ mod tests {
.expect("load default test config");
let auth_manager =
AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
let manager = ModelsManager::new(codex_home.path().to_path_buf(), auth_manager, None);
let manager =
ModelsManager::new(codex_home.path().to_path_buf(), auth_manager, None, false);
let known_slug = manager
.get_remote_models()
.await
@@ -528,6 +533,7 @@ mod tests {
Some(ModelsResponse {
models: vec![remote_model("gpt-overlay", "Overlay", 0)],
}),
false,
);
let model_info = manager
@@ -551,7 +557,8 @@ mod tests {
.expect("load default test config");
let auth_manager =
AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
let manager = ModelsManager::new(codex_home.path().to_path_buf(), auth_manager, None);
let manager =
ModelsManager::new(codex_home.path().to_path_buf(), auth_manager, None, false);
let known_slug = manager
.get_remote_models()
.await
@@ -577,7 +584,8 @@ mod tests {
.expect("load default test config");
let auth_manager =
AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
let manager = ModelsManager::new(codex_home.path().to_path_buf(), auth_manager, None);
let manager =
ModelsManager::new(codex_home.path().to_path_buf(), auth_manager, None, false);
let known_slug = manager
.get_remote_models()
.await
+1 -1
View File
@@ -85,5 +85,5 @@ pub fn all_model_presets() -> &'static Vec<ModelPreset> {
}
pub fn builtin_collaboration_mode_presets() -> Vec<CollaborationModeMask> {
collaboration_mode_presets::builtin_collaboration_mode_presets()
collaboration_mode_presets::builtin_collaboration_mode_presets(false)
}
+2
View File
@@ -143,6 +143,7 @@ impl ThreadManager {
auth_manager: Arc<AuthManager>,
session_source: SessionSource,
model_catalog: Option<ModelsResponse>,
default_mode_request_user_input: bool,
) -> Self {
let (thread_created_tx, _) = broadcast::channel(THREAD_CREATED_CHANNEL_CAPACITY);
let skills_manager = Arc::new(SkillsManager::new(codex_home.clone()));
@@ -155,6 +156,7 @@ impl ThreadManager {
codex_home,
auth_manager.clone(),
model_catalog,
default_mode_request_user_input,
)),
skills_manager,
file_watcher,
@@ -12,10 +12,15 @@ use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::TUI_VISIBLE_COLLABORATION_MODES;
use codex_protocol::request_user_input::RequestUserInputArgs;
fn format_allowed_modes() -> String {
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| mode.allows_request_user_input())
.filter(|mode| request_user_input_is_available(*mode, default_mode_request_user_input))
.map(ModeKind::display_name)
.collect();
@@ -27,8 +32,11 @@ fn format_allowed_modes() -> String {
}
}
pub(crate) fn request_user_input_unavailable_message(mode: ModeKind) -> Option<String> {
if mode.allows_request_user_input() {
pub(crate) fn request_user_input_unavailable_message(
mode: ModeKind,
default_mode_request_user_input: bool,
) -> Option<String> {
if request_user_input_is_available(mode, default_mode_request_user_input) {
None
} else {
let mode_name = mode.display_name();
@@ -38,14 +46,16 @@ pub(crate) fn request_user_input_unavailable_message(mode: ModeKind) -> Option<S
}
}
pub(crate) fn request_user_input_tool_description() -> String {
let allowed_modes = format_allowed_modes();
pub(crate) fn request_user_input_tool_description(default_mode_request_user_input: bool) -> String {
let allowed_modes = format_allowed_modes(default_mode_request_user_input);
format!(
"Request user input for one to three short questions and wait for the response. This tool is only available in {allowed_modes}."
)
}
pub struct RequestUserInputHandler;
pub struct RequestUserInputHandler {
pub default_mode_request_user_input: bool,
}
#[async_trait]
impl ToolHandler for RequestUserInputHandler {
@@ -72,7 +82,9 @@ impl ToolHandler for RequestUserInputHandler {
};
let mode = session.collaboration_mode().await.mode;
if let Some(message) = request_user_input_unavailable_message(mode) {
if let Some(message) =
request_user_input_unavailable_message(mode, self.default_mode_request_user_input)
{
return Err(FunctionCallError::RespondToModel(message));
}
@@ -117,34 +129,45 @@ mod tests {
use pretty_assertions::assert_eq;
#[test]
fn request_user_input_mode_availability_includes_default_mode() {
fn request_user_input_mode_availability_defaults_to_plan_only() {
assert!(ModeKind::Plan.allows_request_user_input());
assert!(ModeKind::Default.allows_request_user_input());
assert!(!ModeKind::Default.allows_request_user_input());
assert!(!ModeKind::Execute.allows_request_user_input());
assert!(!ModeKind::PairProgramming.allows_request_user_input());
}
#[test]
fn request_user_input_unavailable_messages_use_default_name_for_default_modes() {
assert_eq!(request_user_input_unavailable_message(ModeKind::Plan), None);
fn request_user_input_unavailable_messages_respect_default_mode_feature_flag() {
assert_eq!(
request_user_input_unavailable_message(ModeKind::Default),
request_user_input_unavailable_message(ModeKind::Plan, false),
None
);
assert_eq!(
request_user_input_unavailable_message(ModeKind::Execute),
request_user_input_unavailable_message(ModeKind::Default, false),
Some("request_user_input is unavailable in Default mode".to_string())
);
assert_eq!(
request_user_input_unavailable_message(ModeKind::Default, true),
None
);
assert_eq!(
request_user_input_unavailable_message(ModeKind::Execute, false),
Some("request_user_input is unavailable in Execute mode".to_string())
);
assert_eq!(
request_user_input_unavailable_message(ModeKind::PairProgramming),
request_user_input_unavailable_message(ModeKind::PairProgramming, false),
Some("request_user_input is unavailable in Pair Programming mode".to_string())
);
}
#[test]
fn request_user_input_tool_description_mentions_default_and_plan() {
fn request_user_input_tool_description_mentions_available_modes() {
assert_eq!(
request_user_input_tool_description(),
request_user_input_tool_description(false),
"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(true),
"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()
);
}
+22 -34
View File
@@ -54,7 +54,7 @@ pub(crate) struct ToolsConfig {
pub js_repl_enabled: bool,
pub js_repl_tools_only: bool,
pub collab_tools: bool,
pub collaboration_modes_tools: bool,
pub default_mode_request_user_input: bool,
pub experimental_supported_tools: Vec<String>,
pub agent_jobs_tools: bool,
pub agent_jobs_worker_tools: bool,
@@ -80,7 +80,8 @@ impl ToolsConfig {
let include_js_repl_tools_only =
include_js_repl && features.enabled(Feature::JsReplToolsOnly);
let include_collab_tools = features.enabled(Feature::Collab);
let include_collaboration_modes_tools = features.enabled(Feature::CollaborationModes);
let include_default_mode_request_user_input =
features.enabled(Feature::DefaultModeRequestUserInput);
let include_search_tool = features.enabled(Feature::Apps);
let include_agent_jobs = include_collab_tools && features.enabled(Feature::Sqlite);
let request_permission_enabled = features.enabled(Feature::RequestPermissions);
@@ -137,7 +138,7 @@ impl ToolsConfig {
js_repl_enabled: include_js_repl,
js_repl_tools_only: include_js_repl_tools_only,
collab_tools: include_collab_tools,
collaboration_modes_tools: include_collaboration_modes_tools,
default_mode_request_user_input: include_default_mode_request_user_input,
experimental_supported_tools: model_info.experimental_supported_tools.clone(),
agent_jobs_tools: include_agent_jobs,
agent_jobs_worker_tools,
@@ -865,7 +866,7 @@ fn create_wait_tool() -> ToolSpec {
})
}
fn create_request_user_input_tool() -> ToolSpec {
fn create_request_user_input_tool(default_mode_request_user_input: bool) -> ToolSpec {
let mut option_props = BTreeMap::new();
option_props.insert(
"label".to_string(),
@@ -936,7 +937,7 @@ fn create_request_user_input_tool() -> ToolSpec {
ToolSpec::Function(ResponsesApiTool {
name: "request_user_input".to_string(),
description: request_user_input_tool_description(),
description: request_user_input_tool_description(default_mode_request_user_input),
strict: false,
parameters: JsonSchema::Object {
properties,
@@ -1657,7 +1658,9 @@ pub(crate) fn build_specs(
let mcp_handler = Arc::new(McpHandler);
let mcp_resource_handler = Arc::new(McpResourceHandler);
let shell_command_handler = Arc::new(ShellCommandHandler::from(config.shell_command_backend));
let request_user_input_handler = Arc::new(RequestUserInputHandler);
let request_user_input_handler = Arc::new(RequestUserInputHandler {
default_mode_request_user_input: config.default_mode_request_user_input,
});
let search_tool_handler = Arc::new(SearchToolBm25Handler);
let js_repl_handler = Arc::new(JsReplHandler);
let js_repl_reset_handler = Arc::new(JsReplResetHandler);
@@ -1720,10 +1723,10 @@ pub(crate) fn build_specs(
builder.register_handler("js_repl_reset", js_repl_reset_handler);
}
if config.collaboration_modes_tools {
builder.push_spec(create_request_user_input_tool());
builder.register_handler("request_user_input", request_user_input_handler);
}
builder.push_spec(create_request_user_input_tool(
config.default_mode_request_user_input,
));
builder.register_handler("request_user_input", request_user_input_handler);
if config.search_tool
&& let Some(app_tools) = app_tools
@@ -2017,7 +2020,6 @@ mod tests {
let model_info = model_info_from_models_json("gpt-5-codex");
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::CollaborationModes);
let config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
@@ -2048,7 +2050,7 @@ mod tests {
create_exec_command_tool(true, false),
create_write_stdin_tool(),
PLAN_TOOL.clone(),
create_request_user_input_tool(),
create_request_user_input_tool(false),
create_apply_patch_freeform_tool(),
ToolSpec::WebSearch {
external_web_access: Some(true),
@@ -2080,8 +2082,6 @@ mod tests {
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::Collab);
features.enable(Feature::CollaborationModes);
features.enable(Feature::Sqlite);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
@@ -2108,7 +2108,6 @@ mod tests {
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::Collab);
features.enable(Feature::CollaborationModes);
features.enable(Feature::Sqlite);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
@@ -2328,8 +2327,7 @@ mod tests {
let config = test_config();
let model_info =
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::CollaborationModes);
let features = Features::with_defaults();
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
@@ -2352,8 +2350,7 @@ mod tests {
let config = test_config();
let model_info =
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::CollaborationModes);
let features = Features::with_defaults();
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
@@ -2374,8 +2371,7 @@ mod tests {
#[test]
fn test_build_specs_gpt5_codex_default() {
let mut features = Features::with_defaults();
features.enable(Feature::CollaborationModes);
let features = Features::with_defaults();
assert_default_model_tools(
"gpt-5-codex",
&features,
@@ -2393,8 +2389,7 @@ mod tests {
#[test]
fn test_build_specs_gpt51_codex_default() {
let mut features = Features::with_defaults();
features.enable(Feature::CollaborationModes);
let features = Features::with_defaults();
assert_default_model_tools(
"gpt-5.1-codex",
&features,
@@ -2414,7 +2409,6 @@ mod tests {
fn test_build_specs_gpt5_codex_unified_exec_web_search() {
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::CollaborationModes);
assert_model_tools(
"gpt-5-codex",
&features,
@@ -2435,7 +2429,6 @@ mod tests {
fn test_build_specs_gpt51_codex_unified_exec_web_search() {
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::CollaborationModes);
assert_model_tools(
"gpt-5.1-codex",
&features,
@@ -2454,8 +2447,7 @@ mod tests {
#[test]
fn test_gpt_5_1_codex_max_defaults() {
let mut features = Features::with_defaults();
features.enable(Feature::CollaborationModes);
let features = Features::with_defaults();
assert_default_model_tools(
"gpt-5.1-codex-max",
&features,
@@ -2473,8 +2465,7 @@ mod tests {
#[test]
fn test_codex_5_1_mini_defaults() {
let mut features = Features::with_defaults();
features.enable(Feature::CollaborationModes);
let features = Features::with_defaults();
assert_default_model_tools(
"gpt-5.1-codex-mini",
&features,
@@ -2492,8 +2483,7 @@ mod tests {
#[test]
fn test_gpt_5_defaults() {
let mut features = Features::with_defaults();
features.enable(Feature::CollaborationModes);
let features = Features::with_defaults();
assert_default_model_tools(
"gpt-5",
&features,
@@ -2510,8 +2500,7 @@ mod tests {
#[test]
fn test_gpt_5_1_defaults() {
let mut features = Features::with_defaults();
features.enable(Feature::CollaborationModes);
let features = Features::with_defaults();
assert_default_model_tools(
"gpt-5.1",
&features,
@@ -2531,7 +2520,6 @@ mod tests {
fn test_gpt_5_1_codex_max_unified_exec_web_search() {
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::CollaborationModes);
assert_model_tools(
"gpt-5.1-codex-max",
&features,
+3
View File
@@ -584,6 +584,9 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
auth_manager,
SessionSource::Exec,
config.model_catalog.clone(),
config
.features
.enabled(Feature::DefaultModeRequestUserInput),
);
let NewThread { thread: codex, .. } = thread_manager
.start_thread(config)
@@ -12,7 +12,7 @@ async fn offline_model_info_without_tool_output_override() {
let auth_manager = codex_core::test_support::auth_manager_from_auth(
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
);
let manager = ModelsManager::new(config.codex_home.clone(), auth_manager, None);
let manager = ModelsManager::new(config.codex_home.clone(), auth_manager, None, false);
let model_info = manager.get_model_info("gpt-5.1", &config).await;
@@ -30,7 +30,7 @@ async fn offline_model_info_with_tool_output_override() {
let auth_manager = codex_core::test_support::auth_manager_from_auth(
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
);
let manager = ModelsManager::new(config.codex_home.clone(), auth_manager, None);
let manager = ModelsManager::new(config.codex_home.clone(), auth_manager, None, false);
let model_info = manager.get_model_info("gpt-5.1-codex", &config).await;
-2
View File
@@ -1,6 +1,5 @@
#![allow(clippy::unwrap_used)]
use codex_core::features::Feature;
use codex_protocol::config_types::WebSearchMode;
use core_test_support::responses;
use core_test_support::responses::start_mock_server;
@@ -40,7 +39,6 @@ async fn collect_tool_identifiers_for_model(model: &str) -> Vec<String> {
.web_search_mode
.set(WebSearchMode::Cached)
.expect("test web_search_mode should satisfy constraints");
config.features.enable(Feature::CollaborationModes);
});
let test = builder
.build(&server)
+20 -10
View File
@@ -86,8 +86,10 @@ async fn request_user_input_round_trip_for_mode(mode: ModeKind) -> anyhow::Resul
session_configured,
..
} = builder
.with_config(|config| {
config.features.enable(Feature::CollaborationModes);
.with_config(move |config| {
if mode == ModeKind::Default {
config.features.enable(Feature::DefaultModeRequestUserInput);
}
})
.build(&server)
.await?;
@@ -198,18 +200,13 @@ where
let server = start_mock_server().await;
let builder = test_codex();
let mut builder = test_codex();
let TestCodex {
codex,
cwd,
session_configured,
..
} = builder
.with_config(|config| {
config.features.enable(Feature::CollaborationModes);
})
.build(&server)
.await?;
} = builder.build(&server).await?;
let mode_slug = mode_name.to_lowercase().replace(' ', "-");
let call_id = format!("user-input-{mode_slug}-call");
@@ -290,7 +287,20 @@ async fn request_user_input_rejected_in_execute_mode_alias() -> anyhow::Result<(
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn request_user_input_round_trip_in_default_mode() -> anyhow::Result<()> {
async fn request_user_input_rejected_in_default_mode_by_default() -> anyhow::Result<()> {
assert_request_user_input_rejected("Default", |model| CollaborationMode {
mode: ModeKind::Default,
settings: Settings {
model,
reasoning_effort: None,
developer_instructions: None,
},
})
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn request_user_input_round_trip_in_default_mode_with_feature() -> anyhow::Result<()> {
request_user_input_round_trip_for_mode(ModeKind::Default).await
}
+3
View File
@@ -396,6 +396,9 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
auth_manager.clone(),
SessionSource::Exec,
config.model_catalog.clone(),
config
.features
.enabled(codex_core::features::Feature::DefaultModeRequestUserInput),
));
let default_model = thread_manager
.get_models_manager()
@@ -62,6 +62,9 @@ impl MessageProcessor {
auth_manager,
SessionSource::Mcp,
config.model_catalog.clone(),
config
.features
.enabled(codex_core::features::Feature::DefaultModeRequestUserInput),
));
Self {
outgoing,
+1 -1
View File
@@ -210,7 +210,7 @@ impl ModeKind {
}
pub const fn allows_request_user_input(self) -> bool {
matches!(self, Self::Plan | Self::Default)
matches!(self, Self::Plan)
}
}
+3
View File
@@ -1247,6 +1247,9 @@ impl App {
auth_manager.clone(),
SessionSource::Cli,
config.model_catalog.clone(),
config
.features
.enabled(codex_core::features::Feature::DefaultModeRequestUserInput),
));
let mut model = thread_manager
.get_models_manager()
+7 -1
View File
@@ -1633,7 +1633,12 @@ async fn make_chatwidget_manual(
let auth_manager =
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("test"));
let codex_home = cfg.codex_home.clone();
let models_manager = Arc::new(ModelsManager::new(codex_home, auth_manager.clone(), None));
let models_manager = Arc::new(ModelsManager::new(
codex_home,
auth_manager.clone(),
None,
false,
));
let reasoning_effort = None;
let base_mode = CollaborationMode {
mode: ModeKind::Default,
@@ -1759,6 +1764,7 @@ fn set_chatgpt_auth(chat: &mut ChatWidget) {
chat.config.codex_home.clone(),
chat.auth_manager.clone(),
None,
false,
));
}