diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 41ecbfe56..3322f24d8 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -1205,7 +1205,7 @@ impl MessageProcessor { } ClientRequest::ThreadRollback { params, .. } => { self.thread_processor - .thread_rollback(&request_id, params) + .thread_rollback(&request_id, params, app_server_client_name.as_deref()) .await } ClientRequest::ThreadList { params, .. } => { 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 7c6b0aad7..0fc37d1a4 100644 --- a/codex-rs/app-server/src/request_processors/thread_processor.rs +++ b/codex-rs/app-server/src/request_processors/thread_processor.rs @@ -8,6 +8,7 @@ use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE; const THREAD_LIST_DEFAULT_LIMIT: usize = 25; 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"; @@ -634,9 +635,12 @@ impl ThreadRequestProcessor { &self, request_id: &ConnectionRequestId, params: ThreadRollbackParams, + app_server_client_name: Option<&str>, ) -> Result, JSONRPCErrorError> { - self.send_thread_rollback_deprecation_notice(request_id.connection_id) - .await; + if app_server_client_name != Some(CODEX_TUI_CLIENT_NAME) { + self.send_thread_rollback_deprecation_notice(request_id.connection_id) + .await; + } self.thread_rollback_inner(request_id, params) .await .map(|()| None) diff --git a/codex-rs/app-server/tests/suite/v2/thread_rollback.rs b/codex-rs/app-server/tests/suite/v2/thread_rollback.rs index 9481f6923..328ae529c 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_rollback.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_rollback.rs @@ -3,6 +3,7 @@ use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; +use codex_app_server_protocol::ClientInfo; use codex_app_server_protocol::DeprecationNoticeNotification; use codex_app_server_protocol::JSONRPCMessage; use codex_app_server_protocol::JSONRPCResponse; @@ -24,6 +25,48 @@ use tokio::time::timeout; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); +#[tokio::test] +async fn thread_rollback_does_not_emit_deprecation_notice_to_codex_tui() -> Result<()> { + let codex_home = TempDir::new()?; + let mut mcp = TestAppServer::new_with_auto_env(codex_home.path()).await?; + let initialized = timeout( + DEFAULT_READ_TIMEOUT, + mcp.initialize_with_client_info(ClientInfo { + name: "codex-tui".to_string(), + title: None, + version: "0.1.0".to_string(), + }), + ) + .await??; + let JSONRPCMessage::Response(_) = initialized else { + panic!("expected initialize response, got {initialized:?}"); + }; + mcp.clear_message_buffer(); + + let rollback_id = mcp + .send_thread_rollback_request(ThreadRollbackParams { + thread_id: "00000000-0000-0000-0000-000000000001".to_string(), + num_turns: 1, + }) + .await?; + loop { + let message = timeout(DEFAULT_READ_TIMEOUT, mcp.read_next_message()).await??; + match message { + JSONRPCMessage::Notification(notification) => { + assert_ne!(notification.method, "deprecationNotice"); + } + JSONRPCMessage::Error(error) if error.id == RequestId::Integer(rollback_id) => { + break; + } + message => { + panic!("expected rollback error response, got {message:?}"); + } + } + } + + Ok(()) +} + #[tokio::test] async fn thread_rollback_drops_last_turns_and_persists_to_rollout() -> Result<()> { // Three Codex turns hit the mock model (session start + two turn/start calls).