mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
Deprecate full-history hydration for paginated threads (#40676)
## Why Paginated thread history should be loaded incrementally through `thread/turns/list` and `thread/items/list` instead of being fully reconstructed in `thread.turns`. ## What changed - Emit `deprecationNotice` when `thread/read`, `thread/resume`, or `thread/fork` requests full history for a paginated thread. - Direct clients to omit `includeTurns` for reads or use `excludeTurns: true` for resumes and forks, then page history through the list APIs. - Document the deprecation in the protocol schemas and app-server README. ## Testing - Cover notices for paginated reads, cold and loaded resumes, and forks. - Verify metadata-only requests, legacy threads, and rejected forks do not emit the notice. GitOrigin-RevId: 0d387b46d73608f90b7960120a54af4ef332e510
This commit is contained in:
@@ -7,6 +7,7 @@ use super::thread_input::ensure_direct_input_allowed;
|
||||
use super::*;
|
||||
use crate::error_code::method_not_found;
|
||||
use codex_app_server_protocol::SelectedCapabilityRoot;
|
||||
use codex_app_server_protocol::ThreadHistoryMode as ApiThreadHistoryMode;
|
||||
use codex_app_server_protocol::ThreadRevertParams;
|
||||
use codex_app_server_protocol::ThreadRevertResponse;
|
||||
use codex_app_server_protocol::ThreadRevertedNotification;
|
||||
@@ -27,6 +28,8 @@ pub(super) const THREAD_LIST_MAX_LIMIT: usize = 100;
|
||||
const CODEX_TUI_CLIENT_NAME: &str = "codex-tui";
|
||||
const THREAD_ROLLBACK_DEPRECATION_SUMMARY: &str =
|
||||
"thread/rollback is deprecated and will be removed soon";
|
||||
const PAGINATED_FULL_HISTORY_DEPRECATION_SUMMARY: &str = "Full-history hydration is deprecated for paginated threads; use `excludeTurns: true`, then page with `thread/turns/list` and `thread/items/list`.";
|
||||
const PAGINATED_THREAD_READ_DEPRECATION_SUMMARY: &str = "Full-history hydration is deprecated for paginated threads; omit `includeTurns` or set it to `false`, then page with `thread/turns/list` and `thread/items/list`.";
|
||||
|
||||
async fn stage_pending_project_metadata(
|
||||
thread_manager: &ThreadManager,
|
||||
@@ -787,20 +790,23 @@ impl ThreadRequestProcessor {
|
||||
app_server_client_name: Option<&str>,
|
||||
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
|
||||
if app_server_client_name != Some(CODEX_TUI_CLIENT_NAME) {
|
||||
self.send_thread_rollback_deprecation_notice(request_id.connection_id)
|
||||
.await;
|
||||
self.send_deprecation_notice(
|
||||
request_id.connection_id,
|
||||
THREAD_ROLLBACK_DEPRECATION_SUMMARY,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
self.thread_rollback_inner(request_id, params)
|
||||
.await
|
||||
.map(|()| None)
|
||||
}
|
||||
|
||||
async fn send_thread_rollback_deprecation_notice(&self, connection_id: ConnectionId) {
|
||||
async fn send_deprecation_notice(&self, connection_id: ConnectionId, summary: &str) {
|
||||
self.outgoing
|
||||
.send_server_notification_to_connections(
|
||||
&[connection_id],
|
||||
ServerNotification::DeprecationNotice(DeprecationNoticeNotification {
|
||||
summary: THREAD_ROLLBACK_DEPRECATION_SUMMARY.to_string(),
|
||||
summary: summary.to_string(),
|
||||
details: None,
|
||||
}),
|
||||
)
|
||||
@@ -845,11 +851,24 @@ impl ThreadRequestProcessor {
|
||||
|
||||
pub(crate) async fn thread_read(
|
||||
&self,
|
||||
request_id: &ConnectionRequestId,
|
||||
params: ThreadReadParams,
|
||||
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
|
||||
self.thread_read_response_inner(params)
|
||||
.await
|
||||
.map(|response| Some(response.into()))
|
||||
let include_turns = params.include_turns;
|
||||
let response = self.thread_read_response_inner(params).await?;
|
||||
if include_turns
|
||||
&& matches!(
|
||||
response.thread.history_mode,
|
||||
ApiThreadHistoryMode::Paginated
|
||||
)
|
||||
{
|
||||
self.send_deprecation_notice(
|
||||
request_id.connection_id,
|
||||
PAGINATED_THREAD_READ_DEPRECATION_SUMMARY,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Ok(Some(response.into()))
|
||||
}
|
||||
|
||||
pub(crate) async fn thread_turns_list(
|
||||
@@ -3636,6 +3655,13 @@ impl ThreadRequestProcessor {
|
||||
matches!(thread.history_mode, ThreadHistoryMode::Paginated).then_some(thread.thread_id)
|
||||
});
|
||||
let paginated_resume = paginated_thread_id.is_some();
|
||||
if paginated_resume && include_turns {
|
||||
self.send_deprecation_notice(
|
||||
request_id.connection_id,
|
||||
PAGINATED_FULL_HISTORY_DEPRECATION_SUMMARY,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Parent-owned V2 children must resume through their owner, not caller configuration.
|
||||
if let InitialHistory::Resumed(resumed_history) = &thread_history
|
||||
@@ -4144,6 +4170,13 @@ impl ThreadRequestProcessor {
|
||||
let redact_resume_payloads =
|
||||
should_redact_thread_resume_payloads(app_server_client_name.as_deref());
|
||||
let include_turns = !params.exclude_turns;
|
||||
if paginated_resume && include_turns {
|
||||
self.send_deprecation_notice(
|
||||
request_id.connection_id,
|
||||
PAGINATED_FULL_HISTORY_DEPRECATION_SUMMARY,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
let needs_history =
|
||||
!paginated_resume && (include_turns || params.initial_turns_page.is_some());
|
||||
if needs_history {
|
||||
@@ -4660,6 +4693,13 @@ impl ThreadRequestProcessor {
|
||||
"ephemeral paginated thread/fork requires `excludeTurns: true`",
|
||||
));
|
||||
}
|
||||
if paginated_source && include_turns {
|
||||
self.send_deprecation_notice(
|
||||
request_id.connection_id,
|
||||
PAGINATED_FULL_HISTORY_DEPRECATION_SUMMARY,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
let source_thread_id = source_thread.thread_id;
|
||||
let source_thread_name = source_thread
|
||||
.name
|
||||
|
||||
Reference in New Issue
Block a user