mirror of
https://github.com/openai/codex.git
synced 2026-11-02 16:20:35 +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:
1 parent
00ca857d3f
commit
e0435afb72
7 files changed
+135
-5
No files matched your search
@@ -87,6 +87,10 @@ const fn default_hide_agent_reasoning() -> Option<bool> {
|
|||||||
Some(false)
|
Some(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn default_true() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Backward-compatible shape for ChatGPT workspace login restrictions in config.toml.
|
/// Backward-compatible shape for ChatGPT workspace login restrictions in config.toml.
|
||||||
#[derive(Serialize, Debug, Clone, PartialEq, JsonSchema)]
|
#[derive(Serialize, Debug, Clone, PartialEq, JsonSchema)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
@@ -617,6 +621,14 @@ pub struct ToolsToml {
|
|||||||
deserialize_with = "deserialize_optional_web_search_tool_config"
|
deserialize_with = "deserialize_optional_web_search_tool_config"
|
||||||
)]
|
)]
|
||||||
pub web_search: Option<WebSearchToolConfig>,
|
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)]
|
#[derive(Deserialize)]
|
||||||
|
|||||||
@@ -763,6 +763,16 @@
|
|||||||
},
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"ExperimentalRequestUserInput": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"default": true,
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"ExternalConfigMigrationPrompts": {
|
"ExternalConfigMigrationPrompts": {
|
||||||
"additionalProperties": false,
|
"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.",
|
"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": {
|
"ToolsToml": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"experimental_request_user_input": {
|
||||||
|
"$ref": "#/definitions/ExperimentalRequestUserInput"
|
||||||
|
},
|
||||||
"web_search": {
|
"web_search": {
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use codex_config::config_toml::AgentRoleToml;
|
|||||||
use codex_config::config_toml::AgentsToml;
|
use codex_config::config_toml::AgentsToml;
|
||||||
use codex_config::config_toml::AutoReviewToml;
|
use codex_config::config_toml::AutoReviewToml;
|
||||||
use codex_config::config_toml::ConfigToml;
|
use codex_config::config_toml::ConfigToml;
|
||||||
|
use codex_config::config_toml::ExperimentalRequestUserInput;
|
||||||
use codex_config::config_toml::ProjectConfig;
|
use codex_config::config_toml::ProjectConfig;
|
||||||
use codex_config::config_toml::RealtimeConfig;
|
use codex_config::config_toml::RealtimeConfig;
|
||||||
use codex_config::config_toml::RealtimeToml;
|
use codex_config::config_toml::RealtimeToml;
|
||||||
@@ -389,7 +390,13 @@ web_search = true
|
|||||||
)
|
)
|
||||||
.expect("TOML deserialization should succeed");
|
.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]
|
#[test]
|
||||||
@@ -402,7 +409,72 @@ web_search = false
|
|||||||
)
|
)
|
||||||
.expect("TOML deserialization should succeed");
|
.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]
|
#[test]
|
||||||
|
|||||||
@@ -939,6 +939,9 @@ pub struct Config {
|
|||||||
/// Additional parameters for the web search tool when it is enabled.
|
/// Additional parameters for the web search tool when it is enabled.
|
||||||
pub web_search_config: Option<WebSearchConfig>,
|
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.
|
/// If set to `true`, used only the experimental unified exec tool.
|
||||||
pub use_experimental_unified_exec_tool: bool,
|
pub use_experimental_unified_exec_tool: bool,
|
||||||
|
|
||||||
@@ -2205,6 +2208,14 @@ fn resolve_web_search_config(config_toml: &ConfigToml) -> Option<WebSearchConfig
|
|||||||
.map(Into::into)
|
.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 {
|
fn resolve_multi_agent_v2_config(config_toml: &ConfigToml) -> MultiAgentV2Config {
|
||||||
let base = multi_agent_v2_toml_config(config_toml.features.as_ref());
|
let base = multi_agent_v2_toml_config(config_toml.features.as_ref());
|
||||||
let default = MultiAgentV2Config::default();
|
let default = MultiAgentV2Config::default();
|
||||||
@@ -2916,6 +2927,8 @@ impl Config {
|
|||||||
let web_search_mode =
|
let web_search_mode =
|
||||||
resolve_web_search_mode(&cfg, &features).unwrap_or(WebSearchMode::Cached);
|
resolve_web_search_mode(&cfg, &features).unwrap_or(WebSearchMode::Cached);
|
||||||
let web_search_config = resolve_web_search_config(&cfg);
|
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 multi_agent_v2 = resolve_multi_agent_v2_config(&cfg);
|
||||||
let apps_mcp_path_override = if features.enabled(Feature::AppsMcpPathOverride) {
|
let apps_mcp_path_override = if features.enabled(Feature::AppsMcpPathOverride) {
|
||||||
let base = apps_mcp_path_override_toml_config(cfg.features.as_ref());
|
let base = apps_mcp_path_override_toml_config(cfg.features.as_ref());
|
||||||
@@ -3472,6 +3485,7 @@ impl Config {
|
|||||||
forced_login_method,
|
forced_login_method,
|
||||||
web_search_mode: constrained_web_search_mode.value,
|
web_search_mode: constrained_web_search_mode.value,
|
||||||
web_search_config,
|
web_search_config,
|
||||||
|
experimental_request_user_input_enabled,
|
||||||
use_experimental_unified_exec_tool,
|
use_experimental_unified_exec_tool,
|
||||||
background_terminal_max_timeout,
|
background_terminal_max_timeout,
|
||||||
ghost_snapshot,
|
ghost_snapshot,
|
||||||
|
|||||||
@@ -600,9 +600,11 @@ fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut
|
|||||||
planned_tools.add(UpdateGoalHandler);
|
planned_tools.add(UpdateGoalHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
planned_tools.add(RequestUserInputHandler {
|
if turn_context.config.experimental_request_user_input_enabled {
|
||||||
available_modes: request_user_input_available_modes(features),
|
planned_tools.add(RequestUserInputHandler {
|
||||||
});
|
available_modes: request_user_input_available_modes(features),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if features.enabled(Feature::RequestPermissionsTool) {
|
if features.enabled(Feature::RequestPermissionsTool) {
|
||||||
planned_tools.add(RequestPermissionsHandler);
|
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]
|
#[tokio::test]
|
||||||
async fn shell_family_registers_visible_unified_exec_and_hidden_legacy_shell() {
|
async fn shell_family_registers_visible_unified_exec_and_hidden_legacy_shell() {
|
||||||
let plan = probe(|turn| {
|
let plan = probe(|turn| {
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ fn new_config(model: Option<String>, arg0_paths: Arg0DispatchPaths) -> anyhow::R
|
|||||||
forced_login_method: None,
|
forced_login_method: None,
|
||||||
web_search_mode: Constrained::allow_any(WebSearchMode::Disabled),
|
web_search_mode: Constrained::allow_any(WebSearchMode::Disabled),
|
||||||
web_search_config: None,
|
web_search_config: None,
|
||||||
|
experimental_request_user_input_enabled: true,
|
||||||
use_experimental_unified_exec_tool: false,
|
use_experimental_unified_exec_tool: false,
|
||||||
background_terminal_max_timeout: 300_000,
|
background_terminal_max_timeout: 300_000,
|
||||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||||
|
|||||||
Reference in new issue
Block a user