mirror of
https://github.com/openai/codex.git
synced 2026-06-02 19:31:59 +00:00
feat(config) experimental_request_user_input toggle (#24541)
## Summary Experimental flag to allow toggling `request_user_input`: ``` tools.experimental_request_user_input = false ``` ## Testing - [x] Added unit tests
This commit is contained in:
@@ -87,6 +87,10 @@ const fn default_hide_agent_reasoning() -> Option<bool> {
|
||||
Some(false)
|
||||
}
|
||||
|
||||
const fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Backward-compatible shape for ChatGPT workspace login restrictions in config.toml.
|
||||
#[derive(Serialize, Debug, Clone, PartialEq, JsonSchema)]
|
||||
#[serde(untagged)]
|
||||
@@ -617,6 +621,14 @@ pub struct ToolsToml {
|
||||
deserialize_with = "deserialize_optional_web_search_tool_config"
|
||||
)]
|
||||
pub web_search: Option<WebSearchToolConfig>,
|
||||
pub experimental_request_user_input: Option<ExperimentalRequestUserInput>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
pub struct ExperimentalRequestUserInput {
|
||||
#[serde(default = "default_true")]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
||||
@@ -763,6 +763,16 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ExperimentalRequestUserInput": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalConfigMigrationPrompts": {
|
||||
"additionalProperties": false,
|
||||
"description": "Settings for notices we display to users via the tui and app-server clients (primarily the Codex IDE extension). NOTE: these are different from notifications - notices are warnings, NUX screens, acknowledgements, etc.",
|
||||
@@ -2712,6 +2722,9 @@
|
||||
"ToolsToml": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"experimental_request_user_input": {
|
||||
"$ref": "#/definitions/ExperimentalRequestUserInput"
|
||||
},
|
||||
"web_search": {
|
||||
"allOf": [
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@ use codex_config::config_toml::AgentRoleToml;
|
||||
use codex_config::config_toml::AgentsToml;
|
||||
use codex_config::config_toml::AutoReviewToml;
|
||||
use codex_config::config_toml::ConfigToml;
|
||||
use codex_config::config_toml::ExperimentalRequestUserInput;
|
||||
use codex_config::config_toml::ProjectConfig;
|
||||
use codex_config::config_toml::RealtimeConfig;
|
||||
use codex_config::config_toml::RealtimeToml;
|
||||
@@ -389,7 +390,13 @@ web_search = true
|
||||
)
|
||||
.expect("TOML deserialization should succeed");
|
||||
|
||||
assert_eq!(cfg.tools, Some(ToolsToml { web_search: None }));
|
||||
assert_eq!(
|
||||
cfg.tools,
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -402,7 +409,72 @@ web_search = false
|
||||
)
|
||||
.expect("TOML deserialization should succeed");
|
||||
|
||||
assert_eq!(cfg.tools, Some(ToolsToml { web_search: None }));
|
||||
assert_eq!(
|
||||
cfg.tools,
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tools_experimental_request_user_input_defaults_to_enabled() {
|
||||
let cfg: ConfigToml = toml::from_str(
|
||||
r#"
|
||||
[tools.experimental_request_user_input]
|
||||
"#,
|
||||
)
|
||||
.expect("TOML deserialization should succeed");
|
||||
|
||||
assert_eq!(
|
||||
cfg.tools,
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: Some(ExperimentalRequestUserInput { enabled: true }),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tools_experimental_request_user_input_can_be_disabled() {
|
||||
let cfg: ConfigToml = toml::from_str(
|
||||
r#"
|
||||
[tools.experimental_request_user_input]
|
||||
enabled = false
|
||||
"#,
|
||||
)
|
||||
.expect("TOML deserialization should succeed");
|
||||
|
||||
assert_eq!(
|
||||
cfg.tools,
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: Some(ExperimentalRequestUserInput { enabled: false }),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_config_resolves_experimental_request_user_input_enabled() -> std::io::Result<()> {
|
||||
let codex_home = tempdir()?;
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml {
|
||||
tools: Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: Some(ExperimentalRequestUserInput {
|
||||
enabled: false,
|
||||
}),
|
||||
}),
|
||||
..ConfigToml::default()
|
||||
},
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert!(!config.experimental_request_user_input_enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -939,6 +939,9 @@ pub struct Config {
|
||||
/// Additional parameters for the web search tool when it is enabled.
|
||||
pub web_search_config: Option<WebSearchConfig>,
|
||||
|
||||
/// Whether to register the experimental request_user_input tool.
|
||||
pub experimental_request_user_input_enabled: bool,
|
||||
|
||||
/// If set to `true`, used only the experimental unified exec tool.
|
||||
pub use_experimental_unified_exec_tool: bool,
|
||||
|
||||
@@ -2205,6 +2208,14 @@ fn resolve_web_search_config(config_toml: &ConfigToml) -> Option<WebSearchConfig
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
fn resolve_experimental_request_user_input_enabled(config_toml: &ConfigToml) -> bool {
|
||||
config_toml
|
||||
.tools
|
||||
.as_ref()
|
||||
.and_then(|tools| tools.experimental_request_user_input.as_ref())
|
||||
.is_none_or(|config| config.enabled)
|
||||
}
|
||||
|
||||
fn resolve_multi_agent_v2_config(config_toml: &ConfigToml) -> MultiAgentV2Config {
|
||||
let base = multi_agent_v2_toml_config(config_toml.features.as_ref());
|
||||
let default = MultiAgentV2Config::default();
|
||||
@@ -2916,6 +2927,8 @@ impl Config {
|
||||
let web_search_mode =
|
||||
resolve_web_search_mode(&cfg, &features).unwrap_or(WebSearchMode::Cached);
|
||||
let web_search_config = resolve_web_search_config(&cfg);
|
||||
let experimental_request_user_input_enabled =
|
||||
resolve_experimental_request_user_input_enabled(&cfg);
|
||||
let multi_agent_v2 = resolve_multi_agent_v2_config(&cfg);
|
||||
let apps_mcp_path_override = if features.enabled(Feature::AppsMcpPathOverride) {
|
||||
let base = apps_mcp_path_override_toml_config(cfg.features.as_ref());
|
||||
@@ -3472,6 +3485,7 @@ impl Config {
|
||||
forced_login_method,
|
||||
web_search_mode: constrained_web_search_mode.value,
|
||||
web_search_config,
|
||||
experimental_request_user_input_enabled,
|
||||
use_experimental_unified_exec_tool,
|
||||
background_terminal_max_timeout,
|
||||
ghost_snapshot,
|
||||
|
||||
@@ -600,9 +600,11 @@ fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut
|
||||
planned_tools.add(UpdateGoalHandler);
|
||||
}
|
||||
|
||||
planned_tools.add(RequestUserInputHandler {
|
||||
available_modes: request_user_input_available_modes(features),
|
||||
});
|
||||
if turn_context.config.experimental_request_user_input_enabled {
|
||||
planned_tools.add(RequestUserInputHandler {
|
||||
available_modes: request_user_input_available_modes(features),
|
||||
});
|
||||
}
|
||||
|
||||
if features.enabled(Feature::RequestPermissionsTool) {
|
||||
planned_tools.add(RequestPermissionsHandler);
|
||||
|
||||
@@ -377,6 +377,22 @@ fn apply_patch_accepts_environment_id(spec: &ToolSpec) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn request_user_input_tool_respects_experimental_config_gate() {
|
||||
let enabled = probe(|_| {}).await;
|
||||
enabled.assert_visible_contains(&["request_user_input"]);
|
||||
enabled.assert_registered_contains(&["request_user_input"]);
|
||||
|
||||
let disabled = probe(|turn| {
|
||||
update_config(turn, |config| {
|
||||
config.experimental_request_user_input_enabled = false;
|
||||
});
|
||||
})
|
||||
.await;
|
||||
disabled.assert_visible_lacks(&["request_user_input"]);
|
||||
disabled.assert_registered_lacks(&["request_user_input"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn shell_family_registers_visible_unified_exec_and_hidden_legacy_shell() {
|
||||
let plan = probe(|turn| {
|
||||
|
||||
@@ -265,6 +265,7 @@ fn new_config(model: Option<String>, arg0_paths: Arg0DispatchPaths) -> anyhow::R
|
||||
forced_login_method: None,
|
||||
web_search_mode: Constrained::allow_any(WebSearchMode::Disabled),
|
||||
web_search_config: None,
|
||||
experimental_request_user_input_enabled: true,
|
||||
use_experimental_unified_exec_tool: false,
|
||||
background_terminal_max_timeout: 300_000,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
|
||||
Reference in New Issue
Block a user