Move sleep items to the extension-owned lifecycle path (#33159)

## What changed

- Define `SleepItem` in `codex-extension-items` and carry `clock.sleep` events through `TurnItem::Extension`.
- Preserve sleep persistence, thread-history reconstruction, and the app-server's `type: "sleep"` schema while reusing the extension-owned type.

## Testing

- Add serialization coverage for the `clock.sleep` extension wire shape and its protocol wrapper.
- Update sleep lifecycle and thread-history tests to exercise the extension-owned item.

GitOrigin-RevId: 34bb33ed9b90a09bf4744aafdb56e019c717a168
This commit is contained in:
pakrym-oai authored and copyberry committed 2026-07-14 21:01:54 +00:00
1 parent 83a4187837
commit 5d325ba223
41 files changed
+141 -55

No files matched your search

@@ -599,8 +599,7 @@ impl ThreadHistoryBuilder {
);
let should_upsert = match item {
codex_protocol::items::TurnItem::Plan(plan) => !plan.text.is_empty(),
codex_protocol::items::TurnItem::Sleep(_)
| codex_protocol::items::TurnItem::HookPrompt(_)
codex_protocol::items::TurnItem::HookPrompt(_)
| codex_protocol::items::TurnItem::CommandExecution(_)
| codex_protocol::items::TurnItem::DynamicToolCall(_)
| codex_protocol::items::TurnItem::CollabAgentToolCall(_)
@@ -1588,6 +1587,7 @@ mod tests {
use super::*;
use crate::protocol::v2::CommandExecutionSource;
use codex_extension_items::ExtensionItem as CoreExtensionItem;
use codex_extension_items::sleep::SleepItem as CoreSleepItem;
use codex_protocol::ThreadId;
use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem as CoreDynamicToolCallOutputContentItem;
use codex_protocol::items::CommandExecutionItem as CoreCommandExecutionItem;
@@ -1595,7 +1595,6 @@ mod tests {
use codex_protocol::items::EnteredReviewModeItem as CoreEnteredReviewModeItem;
use codex_protocol::items::ExitedReviewModeItem as CoreExitedReviewModeItem;
use codex_protocol::items::HookPromptFragment as CoreHookPromptFragment;
use codex_protocol::items::SleepItem as CoreSleepItem;
use codex_protocol::items::TurnItem as CoreTurnItem;
use codex_protocol::items::UserMessageItem as CoreUserMessageItem;
use codex_protocol::items::build_hook_prompt_message;
@@ -1959,10 +1958,10 @@ mod tests {
fn rebuilds_sleep_item_from_persisted_completion() {
let turn_id = "turn-1";
let thread_id = ThreadId::new();
let sleep_item = CoreTurnItem::Sleep(CoreSleepItem {
let sleep_item = CoreTurnItem::Extension(CoreExtensionItem::Sleep(CoreSleepItem {
id: "sleep-1".to_string(),
duration_ms: 1_000,
});
}));
let events = vec![
EventMsg::TurnStarted(TurnStartedEvent {
turn_id: turn_id.to_string(),
@@ -1997,10 +1996,10 @@ mod tests {
assert_eq!(turns.len(), 1);
assert_eq!(
turns[0].items,
vec![ThreadItem::Sleep {
vec![ThreadItem::Sleep(CoreSleepItem {
id: "sleep-1".to_string(),
duration_ms: 1_000,
}]
})]
);
}
@@ -14,6 +14,7 @@ use crate::protocol::item_builders::review_output_text;
use codex_experimental_api_macros::ExperimentalApi;
use codex_extension_items::ExtensionItem;
pub use codex_extension_items::image_generation::ImageGenerationItem;
pub use codex_extension_items::sleep::SleepItem;
pub use codex_extension_items::web_search::WebSearchAction;
pub use codex_extension_items::web_search::WebSearchItem;
use codex_protocol::approvals::GuardianAssessmentAction as CoreGuardianAssessmentAction;
@@ -368,13 +369,7 @@ pub enum ThreadItem {
id: String,
path: LegacyAppPathString,
},
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
Sleep {
id: String,
#[ts(type = "number")]
duration_ms: u64,
},
Sleep(SleepItem),
ImageGeneration(ImageGenerationItem),
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
@@ -430,11 +425,11 @@ impl ThreadItem {
| ThreadItem::CollabAgentToolCall { id, .. }
| ThreadItem::SubAgentActivity { id, .. }
| ThreadItem::ImageView { id, .. }
| ThreadItem::Sleep { id, .. }
| ThreadItem::EnteredReviewMode { id, .. }
| ThreadItem::ExitedReviewMode { id, .. }
| ThreadItem::ContextCompaction { id, .. } => id,
ThreadItem::WebSearch(item) => &item.id,
ThreadItem::Sleep(item) => &item.id,
ThreadItem::ImageGeneration(item) => &item.id,
}
}
@@ -908,12 +903,9 @@ impl From<CoreTurnItem> for ThreadItem {
id: image.id,
path: image.path.into(),
},
CoreTurnItem::Sleep(sleep) => ThreadItem::Sleep {
id: sleep.id,
duration_ms: sleep.duration_ms,
},
CoreTurnItem::Extension(extension) => match extension {
ExtensionItem::ImageGeneration(item) => ThreadItem::ImageGeneration(item),
ExtensionItem::Sleep(item) => ThreadItem::Sleep(item),
ExtensionItem::WebSearch(item) => ThreadItem::WebSearch(item),
},
CoreTurnItem::ImageGeneration(image) => {