mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat: land unified_exec (#10641)
Land `unified_exec` for all non-windows OS
This commit is contained in:
@@ -3862,7 +3862,7 @@ model_verbosity = "high"
|
|||||||
forced_login_method: None,
|
forced_login_method: None,
|
||||||
include_apply_patch_tool: false,
|
include_apply_patch_tool: false,
|
||||||
web_search_mode: None,
|
web_search_mode: None,
|
||||||
use_experimental_unified_exec_tool: false,
|
use_experimental_unified_exec_tool: !cfg!(windows),
|
||||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||||
features: Features::with_defaults(),
|
features: Features::with_defaults(),
|
||||||
suppress_unstable_features_warning: false,
|
suppress_unstable_features_warning: false,
|
||||||
@@ -3947,7 +3947,7 @@ model_verbosity = "high"
|
|||||||
forced_login_method: None,
|
forced_login_method: None,
|
||||||
include_apply_patch_tool: false,
|
include_apply_patch_tool: false,
|
||||||
web_search_mode: None,
|
web_search_mode: None,
|
||||||
use_experimental_unified_exec_tool: false,
|
use_experimental_unified_exec_tool: !cfg!(windows),
|
||||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||||
features: Features::with_defaults(),
|
features: Features::with_defaults(),
|
||||||
suppress_unstable_features_warning: false,
|
suppress_unstable_features_warning: false,
|
||||||
@@ -4047,7 +4047,7 @@ model_verbosity = "high"
|
|||||||
forced_login_method: None,
|
forced_login_method: None,
|
||||||
include_apply_patch_tool: false,
|
include_apply_patch_tool: false,
|
||||||
web_search_mode: None,
|
web_search_mode: None,
|
||||||
use_experimental_unified_exec_tool: false,
|
use_experimental_unified_exec_tool: !cfg!(windows),
|
||||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||||
features: Features::with_defaults(),
|
features: Features::with_defaults(),
|
||||||
suppress_unstable_features_warning: false,
|
suppress_unstable_features_warning: false,
|
||||||
@@ -4133,7 +4133,7 @@ model_verbosity = "high"
|
|||||||
forced_login_method: None,
|
forced_login_method: None,
|
||||||
include_apply_patch_tool: false,
|
include_apply_patch_tool: false,
|
||||||
web_search_mode: None,
|
web_search_mode: None,
|
||||||
use_experimental_unified_exec_tool: false,
|
use_experimental_unified_exec_tool: !cfg!(windows),
|
||||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||||
features: Features::with_defaults(),
|
features: Features::with_defaults(),
|
||||||
suppress_unstable_features_warning: false,
|
suppress_unstable_features_warning: false,
|
||||||
|
|||||||
@@ -406,6 +406,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
|||||||
stage: Stage::Stable,
|
stage: Stage::Stable,
|
||||||
default_enabled: true,
|
default_enabled: true,
|
||||||
},
|
},
|
||||||
|
FeatureSpec {
|
||||||
|
id: Feature::UnifiedExec,
|
||||||
|
key: "unified_exec",
|
||||||
|
stage: Stage::Stable,
|
||||||
|
default_enabled: !cfg!(windows),
|
||||||
|
},
|
||||||
FeatureSpec {
|
FeatureSpec {
|
||||||
id: Feature::WebSearchRequest,
|
id: Feature::WebSearchRequest,
|
||||||
key: "web_search_request",
|
key: "web_search_request",
|
||||||
@@ -419,16 +425,6 @@ pub const FEATURES: &[FeatureSpec] = &[
|
|||||||
default_enabled: false,
|
default_enabled: false,
|
||||||
},
|
},
|
||||||
// Experimental program. Rendered in the `/experimental` menu for users.
|
// Experimental program. Rendered in the `/experimental` menu for users.
|
||||||
FeatureSpec {
|
|
||||||
id: Feature::UnifiedExec,
|
|
||||||
key: "unified_exec",
|
|
||||||
stage: Stage::Experimental {
|
|
||||||
name: "Background terminal",
|
|
||||||
menu_description: "Run long-running terminal commands in the background.",
|
|
||||||
announcement: "NEW! Try Background terminals for long-running commands. Enable in /experimental!",
|
|
||||||
},
|
|
||||||
default_enabled: false,
|
|
||||||
},
|
|
||||||
FeatureSpec {
|
FeatureSpec {
|
||||||
id: Feature::ShellSnapshot,
|
id: Feature::ShellSnapshot,
|
||||||
key: "shell_snapshot",
|
key: "shell_snapshot",
|
||||||
|
|||||||
@@ -1687,6 +1687,22 @@ mod tests {
|
|||||||
assert_eq!(&tool_names, &expected_tools,);
|
assert_eq!(&tool_names, &expected_tools,);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_default_model_tools(
|
||||||
|
model_slug: &str,
|
||||||
|
features: &Features,
|
||||||
|
web_search_mode: Option<WebSearchMode>,
|
||||||
|
shell_tool: &'static str,
|
||||||
|
expected_tail: &[&str],
|
||||||
|
) {
|
||||||
|
let mut expected = if features.enabled(Feature::UnifiedExec) {
|
||||||
|
vec!["exec_command", "write_stdin"]
|
||||||
|
} else {
|
||||||
|
vec![shell_tool]
|
||||||
|
};
|
||||||
|
expected.extend(expected_tail);
|
||||||
|
assert_model_tools(model_slug, features, web_search_mode, &expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn web_search_mode_cached_sets_external_web_access_false() {
|
fn web_search_mode_cached_sets_external_web_access_false() {
|
||||||
let config = test_config();
|
let config = test_config();
|
||||||
@@ -1735,12 +1751,12 @@ mod tests {
|
|||||||
fn test_build_specs_gpt5_codex_default() {
|
fn test_build_specs_gpt5_codex_default() {
|
||||||
let mut features = Features::with_defaults();
|
let mut features = Features::with_defaults();
|
||||||
features.enable(Feature::CollaborationModes);
|
features.enable(Feature::CollaborationModes);
|
||||||
assert_model_tools(
|
assert_default_model_tools(
|
||||||
"gpt-5-codex",
|
"gpt-5-codex",
|
||||||
&features,
|
&features,
|
||||||
Some(WebSearchMode::Cached),
|
Some(WebSearchMode::Cached),
|
||||||
|
"shell_command",
|
||||||
&[
|
&[
|
||||||
"shell_command",
|
|
||||||
"list_mcp_resources",
|
"list_mcp_resources",
|
||||||
"list_mcp_resource_templates",
|
"list_mcp_resource_templates",
|
||||||
"read_mcp_resource",
|
"read_mcp_resource",
|
||||||
@@ -1757,12 +1773,12 @@ mod tests {
|
|||||||
fn test_build_specs_gpt51_codex_default() {
|
fn test_build_specs_gpt51_codex_default() {
|
||||||
let mut features = Features::with_defaults();
|
let mut features = Features::with_defaults();
|
||||||
features.enable(Feature::CollaborationModes);
|
features.enable(Feature::CollaborationModes);
|
||||||
assert_model_tools(
|
assert_default_model_tools(
|
||||||
"gpt-5.1-codex",
|
"gpt-5.1-codex",
|
||||||
&features,
|
&features,
|
||||||
Some(WebSearchMode::Cached),
|
Some(WebSearchMode::Cached),
|
||||||
|
"shell_command",
|
||||||
&[
|
&[
|
||||||
"shell_command",
|
|
||||||
"list_mcp_resources",
|
"list_mcp_resources",
|
||||||
"list_mcp_resource_templates",
|
"list_mcp_resource_templates",
|
||||||
"read_mcp_resource",
|
"read_mcp_resource",
|
||||||
@@ -1827,12 +1843,12 @@ mod tests {
|
|||||||
fn test_codex_mini_defaults() {
|
fn test_codex_mini_defaults() {
|
||||||
let mut features = Features::with_defaults();
|
let mut features = Features::with_defaults();
|
||||||
features.enable(Feature::CollaborationModes);
|
features.enable(Feature::CollaborationModes);
|
||||||
assert_model_tools(
|
assert_default_model_tools(
|
||||||
"codex-mini-latest",
|
"codex-mini-latest",
|
||||||
&features,
|
&features,
|
||||||
Some(WebSearchMode::Cached),
|
Some(WebSearchMode::Cached),
|
||||||
|
"local_shell",
|
||||||
&[
|
&[
|
||||||
"local_shell",
|
|
||||||
"list_mcp_resources",
|
"list_mcp_resources",
|
||||||
"list_mcp_resource_templates",
|
"list_mcp_resource_templates",
|
||||||
"read_mcp_resource",
|
"read_mcp_resource",
|
||||||
@@ -1848,12 +1864,12 @@ mod tests {
|
|||||||
fn test_codex_5_1_mini_defaults() {
|
fn test_codex_5_1_mini_defaults() {
|
||||||
let mut features = Features::with_defaults();
|
let mut features = Features::with_defaults();
|
||||||
features.enable(Feature::CollaborationModes);
|
features.enable(Feature::CollaborationModes);
|
||||||
assert_model_tools(
|
assert_default_model_tools(
|
||||||
"gpt-5.1-codex-mini",
|
"gpt-5.1-codex-mini",
|
||||||
&features,
|
&features,
|
||||||
Some(WebSearchMode::Cached),
|
Some(WebSearchMode::Cached),
|
||||||
|
"shell_command",
|
||||||
&[
|
&[
|
||||||
"shell_command",
|
|
||||||
"list_mcp_resources",
|
"list_mcp_resources",
|
||||||
"list_mcp_resource_templates",
|
"list_mcp_resource_templates",
|
||||||
"read_mcp_resource",
|
"read_mcp_resource",
|
||||||
@@ -1870,12 +1886,12 @@ mod tests {
|
|||||||
fn test_gpt_5_defaults() {
|
fn test_gpt_5_defaults() {
|
||||||
let mut features = Features::with_defaults();
|
let mut features = Features::with_defaults();
|
||||||
features.enable(Feature::CollaborationModes);
|
features.enable(Feature::CollaborationModes);
|
||||||
assert_model_tools(
|
assert_default_model_tools(
|
||||||
"gpt-5",
|
"gpt-5",
|
||||||
&features,
|
&features,
|
||||||
Some(WebSearchMode::Cached),
|
Some(WebSearchMode::Cached),
|
||||||
|
"shell",
|
||||||
&[
|
&[
|
||||||
"shell",
|
|
||||||
"list_mcp_resources",
|
"list_mcp_resources",
|
||||||
"list_mcp_resource_templates",
|
"list_mcp_resource_templates",
|
||||||
"read_mcp_resource",
|
"read_mcp_resource",
|
||||||
@@ -1891,12 +1907,12 @@ mod tests {
|
|||||||
fn test_gpt_5_1_defaults() {
|
fn test_gpt_5_1_defaults() {
|
||||||
let mut features = Features::with_defaults();
|
let mut features = Features::with_defaults();
|
||||||
features.enable(Feature::CollaborationModes);
|
features.enable(Feature::CollaborationModes);
|
||||||
assert_model_tools(
|
assert_default_model_tools(
|
||||||
"gpt-5.1",
|
"gpt-5.1",
|
||||||
&features,
|
&features,
|
||||||
Some(WebSearchMode::Cached),
|
Some(WebSearchMode::Cached),
|
||||||
|
"shell_command",
|
||||||
&[
|
&[
|
||||||
"shell_command",
|
|
||||||
"list_mcp_resources",
|
"list_mcp_resources",
|
||||||
"list_mcp_resource_templates",
|
"list_mcp_resource_templates",
|
||||||
"read_mcp_resource",
|
"read_mcp_resource",
|
||||||
|
|||||||
@@ -50,6 +50,16 @@ async fn collect_tool_identifiers_for_model(model: &str) -> Vec<String> {
|
|||||||
tool_identifiers(&body)
|
tool_identifiers(&body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expected_default_tools(shell_tool: &str, tail: &[&str]) -> Vec<String> {
|
||||||
|
let mut tools = if cfg!(windows) {
|
||||||
|
vec![shell_tool.to_string()]
|
||||||
|
} else {
|
||||||
|
vec!["exec_command".to_string(), "write_stdin".to_string()]
|
||||||
|
};
|
||||||
|
tools.extend(tail.iter().map(|tool| (*tool).to_string()));
|
||||||
|
tools
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn model_selects_expected_tools() {
|
async fn model_selects_expected_tools() {
|
||||||
skip_if_no_network!();
|
skip_if_no_network!();
|
||||||
@@ -58,83 +68,93 @@ async fn model_selects_expected_tools() {
|
|||||||
let codex_tools = collect_tool_identifiers_for_model("codex-mini-latest").await;
|
let codex_tools = collect_tool_identifiers_for_model("codex-mini-latest").await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
codex_tools,
|
codex_tools,
|
||||||
vec![
|
expected_default_tools(
|
||||||
"local_shell".to_string(),
|
"local_shell",
|
||||||
"list_mcp_resources".to_string(),
|
&[
|
||||||
"list_mcp_resource_templates".to_string(),
|
"list_mcp_resources",
|
||||||
"read_mcp_resource".to_string(),
|
"list_mcp_resource_templates",
|
||||||
"update_plan".to_string(),
|
"read_mcp_resource",
|
||||||
"request_user_input".to_string(),
|
"update_plan",
|
||||||
"web_search".to_string(),
|
"request_user_input",
|
||||||
"view_image".to_string()
|
"web_search",
|
||||||
],
|
"view_image",
|
||||||
|
],
|
||||||
|
),
|
||||||
"codex-mini-latest should expose the local shell tool",
|
"codex-mini-latest should expose the local shell tool",
|
||||||
);
|
);
|
||||||
|
|
||||||
let gpt5_codex_tools = collect_tool_identifiers_for_model("gpt-5-codex").await;
|
let gpt5_codex_tools = collect_tool_identifiers_for_model("gpt-5-codex").await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
gpt5_codex_tools,
|
gpt5_codex_tools,
|
||||||
vec![
|
expected_default_tools(
|
||||||
"shell_command".to_string(),
|
"shell_command",
|
||||||
"list_mcp_resources".to_string(),
|
&[
|
||||||
"list_mcp_resource_templates".to_string(),
|
"list_mcp_resources",
|
||||||
"read_mcp_resource".to_string(),
|
"list_mcp_resource_templates",
|
||||||
"update_plan".to_string(),
|
"read_mcp_resource",
|
||||||
"request_user_input".to_string(),
|
"update_plan",
|
||||||
"apply_patch".to_string(),
|
"request_user_input",
|
||||||
"web_search".to_string(),
|
"apply_patch",
|
||||||
"view_image".to_string()
|
"web_search",
|
||||||
],
|
"view_image",
|
||||||
|
],
|
||||||
|
),
|
||||||
"gpt-5-codex should expose the apply_patch tool",
|
"gpt-5-codex should expose the apply_patch tool",
|
||||||
);
|
);
|
||||||
|
|
||||||
let gpt51_codex_tools = collect_tool_identifiers_for_model("gpt-5.1-codex").await;
|
let gpt51_codex_tools = collect_tool_identifiers_for_model("gpt-5.1-codex").await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
gpt51_codex_tools,
|
gpt51_codex_tools,
|
||||||
vec![
|
expected_default_tools(
|
||||||
"shell_command".to_string(),
|
"shell_command",
|
||||||
"list_mcp_resources".to_string(),
|
&[
|
||||||
"list_mcp_resource_templates".to_string(),
|
"list_mcp_resources",
|
||||||
"read_mcp_resource".to_string(),
|
"list_mcp_resource_templates",
|
||||||
"update_plan".to_string(),
|
"read_mcp_resource",
|
||||||
"request_user_input".to_string(),
|
"update_plan",
|
||||||
"apply_patch".to_string(),
|
"request_user_input",
|
||||||
"web_search".to_string(),
|
"apply_patch",
|
||||||
"view_image".to_string()
|
"web_search",
|
||||||
],
|
"view_image",
|
||||||
|
],
|
||||||
|
),
|
||||||
"gpt-5.1-codex should expose the apply_patch tool",
|
"gpt-5.1-codex should expose the apply_patch tool",
|
||||||
);
|
);
|
||||||
|
|
||||||
let gpt5_tools = collect_tool_identifiers_for_model("gpt-5").await;
|
let gpt5_tools = collect_tool_identifiers_for_model("gpt-5").await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
gpt5_tools,
|
gpt5_tools,
|
||||||
vec![
|
expected_default_tools(
|
||||||
"shell".to_string(),
|
"shell",
|
||||||
"list_mcp_resources".to_string(),
|
&[
|
||||||
"list_mcp_resource_templates".to_string(),
|
"list_mcp_resources",
|
||||||
"read_mcp_resource".to_string(),
|
"list_mcp_resource_templates",
|
||||||
"update_plan".to_string(),
|
"read_mcp_resource",
|
||||||
"request_user_input".to_string(),
|
"update_plan",
|
||||||
"web_search".to_string(),
|
"request_user_input",
|
||||||
"view_image".to_string()
|
"web_search",
|
||||||
],
|
"view_image",
|
||||||
|
],
|
||||||
|
),
|
||||||
"gpt-5 should expose the apply_patch tool",
|
"gpt-5 should expose the apply_patch tool",
|
||||||
);
|
);
|
||||||
|
|
||||||
let gpt51_tools = collect_tool_identifiers_for_model("gpt-5.1").await;
|
let gpt51_tools = collect_tool_identifiers_for_model("gpt-5.1").await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
gpt51_tools,
|
gpt51_tools,
|
||||||
vec![
|
expected_default_tools(
|
||||||
"shell_command".to_string(),
|
"shell_command",
|
||||||
"list_mcp_resources".to_string(),
|
&[
|
||||||
"list_mcp_resource_templates".to_string(),
|
"list_mcp_resources",
|
||||||
"read_mcp_resource".to_string(),
|
"list_mcp_resource_templates",
|
||||||
"update_plan".to_string(),
|
"read_mcp_resource",
|
||||||
"request_user_input".to_string(),
|
"update_plan",
|
||||||
"apply_patch".to_string(),
|
"request_user_input",
|
||||||
"web_search".to_string(),
|
"apply_patch",
|
||||||
"view_image".to_string()
|
"web_search",
|
||||||
],
|
"view_image",
|
||||||
|
],
|
||||||
|
),
|
||||||
"gpt-5.1 should expose the apply_patch tool",
|
"gpt-5.1 should expose the apply_patch tool",
|
||||||
);
|
);
|
||||||
let exp_tools = collect_tool_identifiers_for_model("exp-5.1").await;
|
let exp_tools = collect_tool_identifiers_for_model("exp-5.1").await;
|
||||||
|
|||||||
@@ -136,8 +136,12 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||||
|
|
||||||
let expected_tools_names = vec![
|
let mut expected_tools_names = if cfg!(windows) {
|
||||||
"shell_command",
|
vec!["shell_command"]
|
||||||
|
} else {
|
||||||
|
vec!["exec_command", "write_stdin"]
|
||||||
|
};
|
||||||
|
expected_tools_names.extend([
|
||||||
"list_mcp_resources",
|
"list_mcp_resources",
|
||||||
"list_mcp_resource_templates",
|
"list_mcp_resource_templates",
|
||||||
"read_mcp_resource",
|
"read_mcp_resource",
|
||||||
@@ -146,7 +150,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
|
|||||||
"apply_patch",
|
"apply_patch",
|
||||||
"web_search",
|
"web_search",
|
||||||
"view_image",
|
"view_image",
|
||||||
];
|
]);
|
||||||
let body0 = req1.single_request().body_json();
|
let body0 = req1.single_request().body_json();
|
||||||
|
|
||||||
let expected_instructions = if expected_tools_names.contains(&"apply_patch") {
|
let expected_instructions = if expected_tools_names.contains(&"apply_patch") {
|
||||||
|
|||||||
Reference in New Issue
Block a user