Files
codex/codex-rs/tools/src/tool_spec_tests.rs
pakrym-oai 83decfa300 [codex] Remove unused legacy shell tools (#22246)
## Why

Recent session history showed no active use of the raw `shell`,
`local_shell`, or `container.exec` execution surfaces. Keeping those
handlers/specs wired into core leaves duplicate shell execution paths
alongside the supported `shell_command` and unified exec tools.

## What changed

- Removed the raw `shell` handler/spec and its `ShellToolCallParams`
protocol helper.
- Removed the legacy `local_shell` and `container.exec` handler/spec
plumbing while preserving persisted-history compatibility for old
response items.
- Normalized model/config `default` and `local` shell selections to
`shell_command`.
- Pruned tests that exercised removed raw-shell/local-shell/apply-patch
variants and kept coverage on `shell_command`, unified exec, and
freeform `apply_patch`.

## Verification

- `git diff --check`
- `cargo test -p codex-protocol`
- `cargo test -p codex-tools`
- `cargo test -p codex-core tools::handlers::shell`
- `cargo test -p codex-core tools::spec`
- `cargo test -p codex-core tools::router`
- `cargo test -p codex-core
active_call_preserves_triggering_command_context`
- `cargo test -p codex-core guardian_tests`
- `cargo test -p codex-core --test all shell_serialization`
- `cargo test -p codex-core --test all apply_patch_cli`
- `cargo test -p codex-core --test all shell_command_`
- `cargo test -p codex-core --test all local_shell`
- `cargo test -p codex-core --test all otel::`
- `cargo test -p codex-core --test all hooks::`
- `just fix -p codex-core`
- `just fix -p codex-tools`
2026-05-13 16:43:25 +00:00

269 lines
8.8 KiB
Rust

use super::ResponsesApiNamespace;
use super::ResponsesApiWebSearchFilters;
use super::ResponsesApiWebSearchUserLocation;
use super::ToolSpec;
use crate::AdditionalProperties;
use crate::FreeformTool;
use crate::FreeformToolFormat;
use crate::JsonSchema;
use crate::ResponsesApiNamespaceTool;
use crate::ResponsesApiTool;
use crate::create_tools_json_for_responses_api;
use codex_protocol::config_types::WebSearchContextSize;
use codex_protocol::config_types::WebSearchFilters as ConfigWebSearchFilters;
use codex_protocol::config_types::WebSearchUserLocation as ConfigWebSearchUserLocation;
use codex_protocol::config_types::WebSearchUserLocationType;
use pretty_assertions::assert_eq;
use serde_json::json;
use std::collections::BTreeMap;
#[test]
fn tool_spec_name_covers_all_variants() {
assert_eq!(
ToolSpec::Function(ResponsesApiTool {
name: "lookup_order".to_string(),
description: "Look up an order".to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::object(
BTreeMap::new(),
/*required*/ None,
/*additional_properties*/ None
),
output_schema: None,
})
.name(),
"lookup_order"
);
assert_eq!(
ToolSpec::Namespace(ResponsesApiNamespace {
name: "mcp__demo__".to_string(),
description: "Demo tools".to_string(),
tools: Vec::new(),
})
.name(),
"mcp__demo__"
);
assert_eq!(
ToolSpec::ToolSearch {
execution: "sync".to_string(),
description: "Search for tools".to_string(),
parameters: JsonSchema::object(
BTreeMap::new(),
/*required*/ None,
/*additional_properties*/ None
),
}
.name(),
"tool_search"
);
assert_eq!(
ToolSpec::ImageGeneration {
output_format: "png".to_string(),
}
.name(),
"image_generation"
);
assert_eq!(
ToolSpec::WebSearch {
external_web_access: Some(true),
filters: None,
user_location: None,
search_context_size: None,
search_content_types: None,
}
.name(),
"web_search"
);
assert_eq!(
ToolSpec::Freeform(FreeformTool {
name: "exec".to_string(),
description: "Run a command".to_string(),
format: FreeformToolFormat {
r#type: "grammar".to_string(),
syntax: "lark".to_string(),
definition: "start: \"exec\"".to_string(),
},
})
.name(),
"exec"
);
}
#[test]
fn web_search_config_converts_to_responses_api_types() {
assert_eq!(
ResponsesApiWebSearchFilters::from(ConfigWebSearchFilters {
allowed_domains: Some(vec!["example.com".to_string()]),
}),
ResponsesApiWebSearchFilters {
allowed_domains: Some(vec!["example.com".to_string()]),
}
);
assert_eq!(
ResponsesApiWebSearchUserLocation::from(ConfigWebSearchUserLocation {
r#type: WebSearchUserLocationType::Approximate,
country: Some("US".to_string()),
region: Some("California".to_string()),
city: Some("San Francisco".to_string()),
timezone: Some("America/Los_Angeles".to_string()),
}),
ResponsesApiWebSearchUserLocation {
r#type: WebSearchUserLocationType::Approximate,
country: Some("US".to_string()),
region: Some("California".to_string()),
city: Some("San Francisco".to_string()),
timezone: Some("America/Los_Angeles".to_string()),
}
);
}
#[test]
fn create_tools_json_for_responses_api_includes_top_level_name() {
assert_eq!(
create_tools_json_for_responses_api(&[ToolSpec::Function(ResponsesApiTool {
name: "demo".to_string(),
description: "A demo tool".to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::object(
BTreeMap::from([("foo".to_string(), JsonSchema::string(/*description*/ None),)]),
/*required*/ None,
/*additional_properties*/ None
),
output_schema: None,
})])
.expect("serialize tools"),
vec![json!({
"type": "function",
"name": "demo",
"description": "A demo tool",
"strict": false,
"parameters": {
"type": "object",
"properties": {
"foo": { "type": "string" }
},
},
})]
);
}
#[test]
fn namespace_tool_spec_serializes_expected_wire_shape() {
assert_eq!(
serde_json::to_value(ToolSpec::Namespace(ResponsesApiNamespace {
name: "mcp__demo__".to_string(),
description: "Demo tools".to_string(),
tools: vec![ResponsesApiNamespaceTool::Function(ResponsesApiTool {
name: "lookup_order".to_string(),
description: "Look up an order".to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::object(
BTreeMap::from([(
"order_id".to_string(),
JsonSchema::string(/*description*/ None),
)]),
/*required*/ None,
/*additional_properties*/ None,
),
output_schema: None,
})],
}))
.expect("serialize namespace tool"),
json!({
"type": "namespace",
"name": "mcp__demo__",
"description": "Demo tools",
"tools": [
{
"type": "function",
"name": "lookup_order",
"description": "Look up an order",
"strict": false,
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string" },
},
},
},
],
})
);
}
#[test]
fn web_search_tool_spec_serializes_expected_wire_shape() {
assert_eq!(
serde_json::to_value(ToolSpec::WebSearch {
external_web_access: Some(true),
filters: Some(ResponsesApiWebSearchFilters {
allowed_domains: Some(vec!["example.com".to_string()]),
}),
user_location: Some(ResponsesApiWebSearchUserLocation {
r#type: WebSearchUserLocationType::Approximate,
country: Some("US".to_string()),
region: Some("California".to_string()),
city: Some("San Francisco".to_string()),
timezone: Some("America/Los_Angeles".to_string()),
}),
search_context_size: Some(WebSearchContextSize::High),
search_content_types: Some(vec!["text".to_string(), "image".to_string()]),
})
.expect("serialize web_search"),
json!({
"type": "web_search",
"external_web_access": true,
"filters": {
"allowed_domains": ["example.com"],
},
"user_location": {
"type": "approximate",
"country": "US",
"region": "California",
"city": "San Francisco",
"timezone": "America/Los_Angeles",
},
"search_context_size": "high",
"search_content_types": ["text", "image"],
})
);
}
#[test]
fn tool_search_tool_spec_serializes_expected_wire_shape() {
assert_eq!(
serde_json::to_value(ToolSpec::ToolSearch {
execution: "sync".to_string(),
description: "Search app tools".to_string(),
parameters: JsonSchema::object(
BTreeMap::from([(
"query".to_string(),
JsonSchema::string(Some("Tool search query".to_string()),),
)]),
Some(vec!["query".to_string()]),
Some(AdditionalProperties::Boolean(false))
),
})
.expect("serialize tool_search"),
json!({
"type": "tool_search",
"execution": "sync",
"description": "Search app tools",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Tool search query",
}
},
"required": ["query"],
"additionalProperties": false,
},
})
);
}