Extract update_plan tool spec into codex-tools

This commit is contained in:
Michael Bolin
2026-04-01 14:58:53 -07:00
parent 1b711a5501
commit 8a98693d9c
9 changed files with 64 additions and 59 deletions

View File

@@ -157,6 +157,7 @@ fn strip_total_output_header(output: &str) -> Option<(&str, u32)> {
}
pub(crate) mod tools {
#[cfg(test)]
pub(crate) use codex_tools::ResponsesApiTool;
pub(crate) use codex_tools::ToolSearchOutputTool;
pub(crate) use codex_tools::ToolSpec;

View File

@@ -147,7 +147,7 @@ fn tool_search_payloads_roundtrip_as_tool_search_outputs() {
description: String::new(),
strict: false,
defer_loading: Some(true),
parameters: crate::tools::spec::JsonSchema::Object {
parameters: codex_tools::JsonSchema::Object {
properties: Default::default(),
required: None,
additional_properties: None,

View File

@@ -22,7 +22,6 @@ use codex_sandboxing::policy_transforms::intersect_permission_profiles;
use codex_sandboxing::policy_transforms::merge_permission_profiles;
use codex_sandboxing::policy_transforms::normalize_additional_permissions;
use codex_utils_absolute_path::AbsolutePathBufGuard;
pub use plan::PLAN_TOOL;
use serde::Deserialize;
use serde_json::Value;
use std::path::Path;

View File

@@ -1,5 +1,3 @@
use crate::client_common::tools::ResponsesApiTool;
use crate::client_common::tools::ToolSpec;
use crate::codex::Session;
use crate::codex::TurnContext;
use crate::function_tool::FunctionCallError;
@@ -8,7 +6,6 @@ use crate::tools::context::ToolOutput;
use crate::tools::context::ToolPayload;
use crate::tools::registry::ToolHandler;
use crate::tools::registry::ToolKind;
use crate::tools::spec::JsonSchema;
use async_trait::async_trait;
use codex_protocol::config_types::ModeKind;
use codex_protocol::models::FunctionCallOutputPayload;
@@ -16,8 +13,6 @@ use codex_protocol::models::ResponseInputItem;
use codex_protocol::plan_tool::UpdatePlanArgs;
use codex_protocol::protocol::EventMsg;
use serde_json::Value as JsonValue;
use std::collections::BTreeMap;
use std::sync::LazyLock;
pub struct PlanHandler;
@@ -49,50 +44,6 @@ impl ToolOutput for PlanToolOutput {
}
}
pub static PLAN_TOOL: LazyLock<ToolSpec> = LazyLock::new(|| {
let mut plan_item_props = BTreeMap::new();
plan_item_props.insert("step".to_string(), JsonSchema::String { description: None });
plan_item_props.insert(
"status".to_string(),
JsonSchema::String {
description: Some("One of: pending, in_progress, completed".to_string()),
},
);
let plan_items_schema = JsonSchema::Array {
description: Some("The list of steps".to_string()),
items: Box::new(JsonSchema::Object {
properties: plan_item_props,
required: Some(vec!["step".to_string(), "status".to_string()]),
additional_properties: Some(false.into()),
}),
};
let mut properties = BTreeMap::new();
properties.insert(
"explanation".to_string(),
JsonSchema::String { description: None },
);
properties.insert("plan".to_string(), plan_items_schema);
ToolSpec::Function(ResponsesApiTool {
name: "update_plan".to_string(),
description: r#"Updates the task plan.
Provide an optional explanation and a list of plan items, each with a step and status.
At most one step can be in_progress at a time.
"#
.to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::Object {
properties,
required: Some(vec!["plan".to_string()]),
additional_properties: Some(false.into()),
},
output_schema: None,
})
});
#[async_trait]
impl ToolHandler for PlanHandler {
type Output = PlanToolOutput;

View File

@@ -105,7 +105,7 @@ fn serialize_tool_search_output_tools_groups_results_by_namespace() {
description: "Create a calendar event.".to_string(),
strict: false,
defer_loading: Some(true),
parameters: crate::tools::spec::JsonSchema::Object {
parameters: codex_tools::JsonSchema::Object {
properties: Default::default(),
required: None,
additional_properties: None,
@@ -117,7 +117,7 @@ fn serialize_tool_search_output_tools_groups_results_by_namespace() {
description: "List calendar events.".to_string(),
strict: false,
defer_loading: Some(true),
parameters: crate::tools::spec::JsonSchema::Object {
parameters: codex_tools::JsonSchema::Object {
properties: Default::default(),
required: None,
additional_properties: None,
@@ -134,7 +134,7 @@ fn serialize_tool_search_output_tools_groups_results_by_namespace() {
description: "Read an email.".to_string(),
strict: false,
defer_loading: Some(true),
parameters: crate::tools::spec::JsonSchema::Object {
parameters: codex_tools::JsonSchema::Object {
properties: Default::default(),
required: None,
additional_properties: None,
@@ -187,7 +187,7 @@ fn serialize_tool_search_output_tools_falls_back_to_connector_name_description()
description: "Read multiple emails.".to_string(),
strict: false,
defer_loading: Some(true),
parameters: crate::tools::spec::JsonSchema::Object {
parameters: codex_tools::JsonSchema::Object {
properties: Default::default(),
required: None,
additional_properties: None,

View File

@@ -5,7 +5,6 @@ use crate::shell::Shell;
use crate::shell::ShellType;
use crate::tools::code_mode::PUBLIC_TOOL_NAME;
use crate::tools::code_mode::WAIT_TOOL_NAME;
use crate::tools::handlers::PLAN_TOOL;
use crate::tools::handlers::TOOL_SEARCH_DEFAULT_LIMIT;
use crate::tools::handlers::TOOL_SEARCH_TOOL_NAME;
use crate::tools::handlers::TOOL_SUGGEST_TOOL_NAME;
@@ -59,6 +58,7 @@ use codex_tools::create_spawn_agents_on_csv_tool;
use codex_tools::create_test_sync_tool;
use codex_tools::create_tool_search_tool;
use codex_tools::create_tool_suggest_tool;
use codex_tools::create_update_plan_tool;
use codex_tools::create_view_image_tool;
use codex_tools::create_wait_agent_tool_v1;
use codex_tools::create_wait_agent_tool_v2;
@@ -71,7 +71,6 @@ use codex_tools::request_user_input_tool_description;
use codex_tools::tool_spec_to_code_mode_tool_definition;
use std::collections::HashMap;
pub type JsonSchema = codex_tools::JsonSchema;
pub use codex_tools::ShellCommandBackendConfig;
pub use codex_tools::ToolsConfig;
pub use codex_tools::ToolsConfigParams;
@@ -318,7 +317,7 @@ pub(crate) fn build_specs_with_discoverable_tools(
push_tool_spec(
&mut builder,
PLAN_TOOL.clone(),
create_update_plan_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);

View File

@@ -24,6 +24,7 @@ use codex_tools::ConfiguredToolSpec;
use codex_tools::DiscoverablePluginInfo;
use codex_tools::DiscoverableTool;
use codex_tools::FreeformTool;
use codex_tools::JsonSchema;
use codex_tools::ResponsesApiTool;
use codex_tools::ResponsesApiWebSearchFilters;
use codex_tools::ResponsesApiWebSearchUserLocation;
@@ -41,6 +42,7 @@ use codex_tools::create_send_input_tool_v1;
use codex_tools::create_send_message_tool;
use codex_tools::create_spawn_agent_tool_v1;
use codex_tools::create_spawn_agent_tool_v2;
use codex_tools::create_update_plan_tool;
use codex_tools::create_view_image_tool;
use codex_tools::create_wait_agent_tool_v1;
use codex_tools::create_wait_agent_tool_v2;
@@ -331,7 +333,7 @@ fn test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search() {
exec_permission_approvals_enabled: false,
}),
create_write_stdin_tool(),
PLAN_TOOL.clone(),
create_update_plan_tool(),
request_user_input_tool_spec(/*default_mode_request_user_input*/ false),
create_apply_patch_freeform_tool(),
ToolSpec::WebSearch {

View File

@@ -12,6 +12,7 @@ mod json_schema;
mod local_tool;
mod mcp_resource_tool;
mod mcp_tool;
mod plan_tool;
mod request_user_input_tool;
mod responses_api;
mod tool_config;
@@ -64,6 +65,7 @@ pub use mcp_resource_tool::create_list_mcp_resources_tool;
pub use mcp_resource_tool::create_read_mcp_resource_tool;
pub use mcp_tool::mcp_call_tool_result_output_schema;
pub use mcp_tool::parse_mcp_tool;
pub use plan_tool::create_update_plan_tool;
pub use request_user_input_tool::create_request_user_input_tool;
pub use request_user_input_tool::request_user_input_tool_description;
pub use request_user_input_tool::request_user_input_unavailable_message;

View File

@@ -0,0 +1,51 @@
use crate::JsonSchema;
use crate::ResponsesApiTool;
use crate::ToolSpec;
use std::collections::BTreeMap;
pub fn create_update_plan_tool() -> ToolSpec {
let plan_item_properties = BTreeMap::from([
("step".to_string(), JsonSchema::String { description: None }),
(
"status".to_string(),
JsonSchema::String {
description: Some("One of: pending, in_progress, completed".to_string()),
},
),
]);
let properties = BTreeMap::from([
(
"explanation".to_string(),
JsonSchema::String { description: None },
),
(
"plan".to_string(),
JsonSchema::Array {
description: Some("The list of steps".to_string()),
items: Box::new(JsonSchema::Object {
properties: plan_item_properties,
required: Some(vec!["step".to_string(), "status".to_string()]),
additional_properties: Some(false.into()),
}),
},
),
]);
ToolSpec::Function(ResponsesApiTool {
name: "update_plan".to_string(),
description: r#"Updates the task plan.
Provide an optional explanation and a list of plan items, each with a step and status.
At most one step can be in_progress at a time.
"#
.to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::Object {
properties,
required: Some(vec!["plan".to_string()]),
additional_properties: Some(false.into()),
},
output_schema: None,
})
}