diff --git a/codex-rs/app-server/src/request_processors/thread_processor.rs b/codex-rs/app-server/src/request_processors/thread_processor.rs index b78c53a383..9c0b7072fe 100644 --- a/codex-rs/app-server/src/request_processors/thread_processor.rs +++ b/codex-rs/app-server/src/request_processors/thread_processor.rs @@ -2448,6 +2448,8 @@ impl ThreadRequestProcessor { SortDirection::Asc => StoreSortDirection::Asc, SortDirection::Desc => StoreSortDirection::Desc, }, + after_updated_at: None, + through_updated_at: None, }) .await .map_err(|err| match err { diff --git a/codex-rs/thread-store/src/error.rs b/codex-rs/thread-store/src/error.rs index bc5e8ed7c4..3ce4019bc4 100644 --- a/codex-rs/thread-store/src/error.rs +++ b/codex-rs/thread-store/src/error.rs @@ -46,6 +46,13 @@ pub enum ThreadStoreError { operation: &'static str, }, + /// The store cannot return the requested result within a required resource limit. + #[error("thread-store resource exhausted: {message}")] + ResourceExhausted { + /// User-facing explanation of the exhausted resource. + message: String, + }, + /// Catch-all for implementation failures that do not fit a more specific category. #[error("thread-store internal error: {message}")] Internal { diff --git a/codex-rs/thread-store/src/in_memory.rs b/codex-rs/thread-store/src/in_memory.rs index cb3f198ff3..0c9b2c60f5 100644 --- a/codex-rs/thread-store/src/in_memory.rs +++ b/codex-rs/thread-store/src/in_memory.rs @@ -90,6 +90,8 @@ mod tests { cursor: None, page_size: 10, sort_direction: SortDirection::Asc, + after_updated_at: None, + through_updated_at: None, }) .await .expect_err("default list_items should be unsupported"); diff --git a/codex-rs/thread-store/src/store.rs b/codex-rs/thread-store/src/store.rs index 67ee603f49..8ae27ba8c6 100644 --- a/codex-rs/thread-store/src/store.rs +++ b/codex-rs/thread-store/src/store.rs @@ -111,7 +111,9 @@ pub trait ThreadStore: Any + Send + Sync { }) } - /// Lists persisted items within a stored thread, optionally filtered to a turn. + /// Lists persisted items within a stored thread, optionally filtered to a turn or update + /// interval. Implementations must return [`ThreadStoreError::Unsupported`] rather than ignore + /// timestamp bounds they cannot apply. fn list_items(&self, _params: ListItemsParams) -> ThreadStoreFuture<'_, ItemPage> { Box::pin(async { Err(ThreadStoreError::Unsupported { diff --git a/codex-rs/thread-store/src/types.rs b/codex-rs/thread-store/src/types.rs index 4e688eca6f..a2dbac6dab 100644 --- a/codex-rs/thread-store/src/types.rs +++ b/codex-rs/thread-store/src/types.rs @@ -387,6 +387,13 @@ pub struct ListItemsParams { pub page_size: usize, /// Sort direction requested by the caller. pub sort_direction: SortDirection, + /// Exclusive lower bound for item updates. Requires `through_updated_at` when set. + #[serde(default)] + pub after_updated_at: Option>, + /// Inclusive upper bound for item updates. Implementations that do not support bounded reads + /// must return [`crate::ThreadStoreError::Unsupported`] instead of ignoring this field. + #[serde(default)] + pub through_updated_at: Option>, } /// A projected app-server `ThreadItem` snapshot within a turn. @@ -408,6 +415,9 @@ pub struct ItemPage { pub next_cursor: Option, /// Opaque cursor for fetching in the opposite direction. pub backwards_cursor: Option, + /// Inclusive update bound applied by the store, echoed for bounded reads. + #[serde(default)] + pub read_through_updated_at: Option>, } /// Store-owned thread metadata used by list/read/resume responses.