From 205d37a20f742b0bf8e191622bd07c43f567ea49 Mon Sep 17 00:00:00 2001 From: rka-oai Date: Thu, 23 Jul 2026 16:14:42 +0000 Subject: [PATCH] Keep the sleep tool outside code mode (#34969) ## What changed - Mark `clock.sleep` as `DirectModelOnly` so it remains directly callable in code-mode-only sessions without appearing in the nested code-mode tool surface. - Cover both code mode configurations with a tool-plan test that verifies the sleep tool's exposure and visibility. GitOrigin-RevId: 7ef67bea0678f27fdb17d88004368e5f1d475125 --- codex-rs/core/src/tools/handlers/sleep.rs | 5 +++ codex-rs/core/src/tools/spec_plan_tests.rs | 49 ++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/codex-rs/core/src/tools/handlers/sleep.rs b/codex-rs/core/src/tools/handlers/sleep.rs index ecc55c2e1511..c3068e6e8dc2 100644 --- a/codex-rs/core/src/tools/handlers/sleep.rs +++ b/codex-rs/core/src/tools/handlers/sleep.rs @@ -13,6 +13,7 @@ use codex_tools::JsonSchema; use codex_tools::ResponsesApiNamespace; use codex_tools::ResponsesApiNamespaceTool; use codex_tools::ResponsesApiTool; +use codex_tools::ToolExposure; use codex_tools::ToolName; use codex_tools::ToolSpec; use serde::Deserialize; @@ -68,6 +69,10 @@ impl ToolExecutor for SleepHandler { create_sleep_tool() } + fn exposure(&self) -> ToolExposure { + ToolExposure::DirectModelOnly + } + fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> { Box::pin(async move { let ToolInvocation { diff --git a/codex-rs/core/src/tools/spec_plan_tests.rs b/codex-rs/core/src/tools/spec_plan_tests.rs index 556979d443e4..c6e9ad51ffb8 100644 --- a/codex-rs/core/src/tools/spec_plan_tests.rs +++ b/codex-rs/core/src/tools/spec_plan_tests.rs @@ -726,6 +726,55 @@ async fn sleep_tool_follows_current_time_config() { ); } +#[tokio::test] +async fn sleep_tool_stays_direct_and_outside_code_mode() { + for code_mode_only in [false, true] { + let plan = probe(|turn| { + set_features( + turn, + &[ + Feature::CodeMode, + Feature::CurrentTimeReminder, + Feature::MultiAgentV2, + ], + ); + if code_mode_only { + set_feature(turn, Feature::CodeModeOnly, /*enabled*/ true); + } + update_config(turn, |config| { + config.current_time_reminder = Some(CurrentTimeReminderConfig { + sleep_tool: true, + ..CurrentTimeReminderConfig::default() + }); + config.multi_agent_v2.wait_agent_enabled = false; + }); + }) + .await; + + assert!( + plan.namespace_function_names("clock") + .iter() + .any(|name| name == "sleep") + ); + let sleep_tool_name = ToolName::namespaced("clock", "sleep").to_string(); + let wait_agent_tool_name = + ToolName::namespaced(MULTI_AGENT_V2_NAMESPACE, "wait_agent").to_string(); + assert_eq!( + plan.exposure(&sleep_tool_name), + ToolExposure::DirectModelOnly + ); + plan.assert_registered_lacks(&[wait_agent_tool_name.as_str()]); + + let ToolSpec::Freeform(exec) = plan.visible_spec(codex_code_mode::PUBLIC_TOOL_NAME) else { + panic!("expected code mode exec tool"); + }; + if code_mode_only { + assert!(exec.description.contains("clock__curr_time")); + } + assert!(!exec.description.contains("clock__sleep")); + } +} + #[tokio::test] async fn mcp_and_tool_search_follow_direct_and_deferred_tool_exposure() { let direct_mcp = probe_with(