From 35f5d02464ade52d2341dd7a40fd65aae439adfa Mon Sep 17 00:00:00 2001 From: rka-oai Date: Wed, 24 Jun 2026 17:49:00 -0700 Subject: [PATCH] [codex] nest sleep config under current time reminder (#29910) ## Summary - move sleep tool enablement from top-level `[features].sleep_tool` to `[features.current_time_reminder].sleep_tool` - remove the standalone `Feature::SleepTool` flag and gate `clock.sleep` from resolved current-time configuration - update config schema, config-lock materialization, and existing sleep coverage Stacked on #29907. --- codex-rs/app-server/tests/suite/v2/sleep.rs | 3 ++- codex-rs/core/config.schema.json | 10 ++++----- codex-rs/core/src/config/config_tests.rs | 2 ++ codex-rs/core/src/config/mod.rs | 8 ++++++- codex-rs/core/src/session/config_lock.rs | 1 + codex-rs/core/src/tools/spec_plan.rs | 12 +++++++---- codex-rs/core/src/tools/spec_plan_tests.rs | 21 +++++++++++++------ codex-rs/core/tests/suite/code_mode.rs | 1 + .../core/tests/suite/current_time_reminder.rs | 1 + codex-rs/core/tests/suite/pending_input.rs | 9 ++++++-- codex-rs/features/src/feature_configs.rs | 3 +++ codex-rs/features/src/lib.rs | 8 ------- 12 files changed, 51 insertions(+), 28 deletions(-) diff --git a/codex-rs/app-server/tests/suite/v2/sleep.rs b/codex-rs/app-server/tests/suite/v2/sleep.rs index 063823324..872983f76 100644 --- a/codex-rs/app-server/tests/suite/v2/sleep.rs +++ b/codex-rs/app-server/tests/suite/v2/sleep.rs @@ -167,7 +167,8 @@ wire_api = "responses" request_max_retries = 0 stream_max_retries = 0 -[features] +[features.current_time_reminder] +enabled = true sleep_tool = true "# ), diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index e3e4959f0..32478b81d 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -643,9 +643,6 @@ "skill_mcp_dependency_install": { "type": "boolean" }, - "sleep_tool": { - "type": "boolean" - }, "sqlite": { "type": "boolean" }, @@ -820,6 +817,10 @@ "format": "uint64", "minimum": 1.0, "type": "integer" + }, + "sleep_tool": { + "description": "Expose the input-interruptible `clock.sleep` tool.", + "type": "boolean" } }, "type": "object" @@ -4951,9 +4952,6 @@ "skill_mcp_dependency_install": { "type": "boolean" }, - "sleep_tool": { - "type": "boolean" - }, "sqlite": { "type": "boolean" }, diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 62d9f792e..e13c91842 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -623,10 +623,12 @@ current_time_reminder = true enabled = true reminder_interval_seconds = 4 clock_source = "external" +sleep_tool = true "#, CurrentTimeReminderConfig { reminder_interval_seconds: 4, clock_source: CurrentTimeSource::External, + sleep_tool: true, }, ), ] { diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index ac79dea39..85ed1ee85 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1037,7 +1037,7 @@ pub struct Config { pub token_budget: Option, /// Shared token budget for the root thread and its sub-agents. pub rollout_budget: Option, - /// Current-time reminder configuration, when enabled. + /// Current-time reminder and clock tool configuration, when enabled. pub current_time_reminder: Option, /// Centralized feature flags; source of truth for feature gating. @@ -1117,6 +1117,8 @@ pub struct RolloutBudgetConfig { pub struct CurrentTimeReminderConfig { pub reminder_interval_seconds: u64, pub clock_source: CurrentTimeSource, + /// Whether to expose the input-interruptible `clock.sleep` tool. + pub sleep_tool: bool, } impl Default for CurrentTimeReminderConfig { @@ -1124,6 +1126,7 @@ impl Default for CurrentTimeReminderConfig { Self { reminder_interval_seconds: 1, clock_source: CurrentTimeSource::System, + sleep_tool: false, } } } @@ -2675,6 +2678,9 @@ fn resolve_current_time_reminder_config( clock_source: base .and_then(|config| config.clock_source) .unwrap_or(default.clock_source), + sleep_tool: base + .and_then(|config| config.sleep_tool) + .unwrap_or(default.sleep_tool), })) } diff --git a/codex-rs/core/src/session/config_lock.rs b/codex-rs/core/src/session/config_lock.rs index 13b6b4824..ec7fd6de7 100644 --- a/codex-rs/core/src/session/config_lock.rs +++ b/codex-rs/core/src/session/config_lock.rs @@ -359,6 +359,7 @@ mod tests { enabled: Some(true), reminder_interval_seconds: Some(1), clock_source: Some(codex_features::CurrentTimeSource::System), + sleep_tool: Some(false), })) ); diff --git a/codex-rs/core/src/tools/spec_plan.rs b/codex-rs/core/src/tools/spec_plan.rs index 78926f5f0..b873f87e2 100644 --- a/codex-rs/core/src/tools/spec_plan.rs +++ b/codex-rs/core/src/tools/spec_plan.rs @@ -744,10 +744,14 @@ fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut if features.enabled(Feature::CurrentTimeReminder) { planned_tools.add(CurrentTimeHandler); - } - - if features.enabled(Feature::SleepTool) { - planned_tools.add(SleepHandler); + if turn_context + .config + .current_time_reminder + .as_ref() + .is_some_and(|config| config.sleep_tool) + { + planned_tools.add(SleepHandler); + } } if tool_suggest_enabled(turn_context) diff --git a/codex-rs/core/src/tools/spec_plan_tests.rs b/codex-rs/core/src/tools/spec_plan_tests.rs index 8c4274302..f89b3e202 100644 --- a/codex-rs/core/src/tools/spec_plan_tests.rs +++ b/codex-rs/core/src/tools/spec_plan_tests.rs @@ -31,6 +31,7 @@ use codex_tools::ToolSpec; use pretty_assertions::assert_eq; use serde_json::json; +use crate::config::CurrentTimeReminderConfig; use crate::session::step_context::StepContext; use crate::session::tests::make_session_and_context; use crate::session::turn_context::TurnContext; @@ -721,19 +722,27 @@ async fn host_context_gates_agent_job_tools() { } #[tokio::test] -async fn sleep_tool_follows_feature_gate() { +async fn sleep_tool_follows_current_time_config() { let disabled = probe(|turn| { - set_feature(turn, Feature::SleepTool, /*enabled*/ false); + set_feature(turn, Feature::CurrentTimeReminder, /*enabled*/ true); }) .await; - disabled.assert_visible_lacks(&["clock"]); + assert_eq!(disabled.namespace_function_names("clock"), ["curr_time"]); let enabled = probe(|turn| { - set_feature(turn, Feature::SleepTool, /*enabled*/ true); + set_feature(turn, Feature::CurrentTimeReminder, /*enabled*/ true); + let mut config = (*turn.config).clone(); + config.current_time_reminder = Some(CurrentTimeReminderConfig { + sleep_tool: true, + ..CurrentTimeReminderConfig::default() + }); + turn.config = Arc::new(config); }) .await; - enabled.assert_visible_contains(&["clock"]); - assert_eq!(enabled.namespace_function_names("clock"), ["sleep"]); + assert_eq!( + enabled.namespace_function_names("clock"), + ["curr_time", "sleep"] + ); } #[tokio::test] diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index 5b1770913..d7d0b9be7 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -883,6 +883,7 @@ text(JSON.stringify(result)); config.current_time_reminder = Some(CurrentTimeReminderConfig { reminder_interval_seconds: 3_000, clock_source: CurrentTimeSource::System, + ..CurrentTimeReminderConfig::default() }); }, ) diff --git a/codex-rs/core/tests/suite/current_time_reminder.rs b/codex-rs/core/tests/suite/current_time_reminder.rs index ec55da431..22634fd8e 100644 --- a/codex-rs/core/tests/suite/current_time_reminder.rs +++ b/codex-rs/core/tests/suite/current_time_reminder.rs @@ -86,6 +86,7 @@ fn enable_current_time_reminder( config.current_time_reminder = Some(CurrentTimeReminderConfig { reminder_interval_seconds: interval, clock_source, + ..CurrentTimeReminderConfig::default() }); } diff --git a/codex-rs/core/tests/suite/pending_input.rs b/codex-rs/core/tests/suite/pending_input.rs index b706798e2..9c9852a27 100644 --- a/codex-rs/core/tests/suite/pending_input.rs +++ b/codex-rs/core/tests/suite/pending_input.rs @@ -2,6 +2,7 @@ use core_test_support::test_codex::local_selections; use std::sync::Arc; use codex_core::CodexThread; +use codex_core::config::CurrentTimeReminderConfig; use codex_features::Feature; use codex_protocol::AgentPath; use codex_protocol::items::SleepItem; @@ -390,8 +391,12 @@ async fn any_new_input_interrupts_sleep() { .with_config(|config| { config .features - .enable(Feature::SleepTool) - .expect("test config should allow feature update"); + .enable(Feature::CurrentTimeReminder) + .expect("test config should allow current-time reminders"); + config.current_time_reminder = Some(CurrentTimeReminderConfig { + sleep_tool: true, + ..CurrentTimeReminderConfig::default() + }); }) .build_with_streaming_server(&server) .await diff --git a/codex-rs/features/src/feature_configs.rs b/codex-rs/features/src/feature_configs.rs index be06bd157..efb3580a5 100644 --- a/codex-rs/features/src/feature_configs.rs +++ b/codex-rs/features/src/feature_configs.rs @@ -147,6 +147,9 @@ pub struct CurrentTimeReminderConfigToml { pub reminder_interval_seconds: Option, #[serde(skip_serializing_if = "Option::is_none")] pub clock_source: Option, + /// Expose the input-interruptible `clock.sleep` tool. + #[serde(skip_serializing_if = "Option::is_none")] + pub sleep_tool: Option, } impl FeatureConfig for CurrentTimeReminderConfigToml { diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs index 9d3d3e1b4..151e13f61 100644 --- a/codex-rs/features/src/lib.rs +++ b/codex-rs/features/src/lib.rs @@ -219,8 +219,6 @@ pub enum Feature { RolloutBudget, /// Add current-time reminders to model-visible context. CurrentTimeReminder, - /// Expose an input-interruptible sleep tool. - SleepTool, /// Route MCP tool approval prompts through the MCP elicitation request path. ToolCallMcpElicitation, /// Prompt Codex Apps connector auth failures through MCP URL elicitations. @@ -1243,12 +1241,6 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::UnderDevelopment, default_enabled: false, }, - FeatureSpec { - id: Feature::SleepTool, - key: "sleep_tool", - stage: Stage::UnderDevelopment, - default_enabled: false, - }, FeatureSpec { id: Feature::CollaborationModes, key: "collaboration_modes",