diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 223ef3091..52e248a37 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -3841,9 +3841,9 @@ pub struct ThreadStartParams { #[experimental("thread/start.experimentalRawEvents")] #[serde(default)] pub experimental_raw_events: bool, - /// If true, persist additional EventMsg variants to the rollout file. - /// However, `thread/read`, `thread/resume`, and `thread/fork` still only - /// return the limited form of thread history for scalability reasons. + /// Deprecated and ignored by app-server. Kept only so older clients can + /// continue sending the field while rollout persistence always uses the + /// limited history policy. #[experimental("thread/start.persistFullHistory")] #[serde(default)] pub persist_extended_history: bool, @@ -3973,9 +3973,9 @@ pub struct ThreadResumeParams { #[experimental("thread/resume.excludeTurns")] #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub exclude_turns: bool, - /// If true, persist additional EventMsg variants to the rollout file. - /// However, `thread/read`, `thread/resume`, and `thread/fork` still only - /// return the limited form of thread history for scalability reasons. + /// Deprecated and ignored by app-server. Kept only so older clients can + /// continue sending the field while rollout persistence always uses the + /// limited history policy. #[experimental("thread/resume.persistFullHistory")] #[serde(default)] pub persist_extended_history: bool, @@ -4079,9 +4079,9 @@ pub struct ThreadForkParams { #[experimental("thread/fork.excludeTurns")] #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub exclude_turns: bool, - /// If true, persist additional EventMsg variants to the rollout file. - /// However, `thread/read`, `thread/resume`, and `thread/fork` still only - /// return the limited form of thread history for scalability reasons. + /// Deprecated and ignored by app-server. Kept only so older clients can + /// continue sending the field while rollout persistence always uses the + /// limited history policy. #[experimental("thread/fork.persistFullHistory")] #[serde(default)] pub persist_extended_history: bool, diff --git a/codex-rs/app-server/src/request_processors.rs b/codex-rs/app-server/src/request_processors.rs index 0154b6173..8e4b9ca3b 100644 --- a/codex-rs/app-server/src/request_processors.rs +++ b/codex-rs/app-server/src/request_processors.rs @@ -52,6 +52,7 @@ use codex_app_server_protocol::CommandExecWriteParams; use codex_app_server_protocol::ConfigWarningNotification; use codex_app_server_protocol::ConversationGitInfo; use codex_app_server_protocol::ConversationSummary; +use codex_app_server_protocol::DeprecationNoticeNotification; use codex_app_server_protocol::DynamicToolSpec as ApiDynamicToolSpec; use codex_app_server_protocol::ExperimentalFeature as ApiExperimentalFeature; use codex_app_server_protocol::ExperimentalFeatureListParams; 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 3d8746092..2dc5fd3e1 100644 --- a/codex-rs/app-server/src/request_processors/thread_processor.rs +++ b/codex-rs/app-server/src/request_processors/thread_processor.rs @@ -2,6 +2,10 @@ use super::*; const THREAD_LIST_DEFAULT_LIMIT: usize = 25; const THREAD_LIST_MAX_LIMIT: usize = 100; +const PERSIST_EXTENDED_HISTORY_DEPRECATION_SUMMARY: &str = + "persistExtendedHistory is deprecated and ignored"; +const PERSIST_EXTENDED_HISTORY_DEPRECATION_DETAILS: &str = + "Remove this parameter. App-server always uses limited history persistence."; struct ThreadListFilters { model_providers: Option>, @@ -121,12 +125,6 @@ fn collect_resume_override_mismatches( "developerInstructions override was provided and ignored while running".to_string(), ); } - if request.persist_extended_history { - mismatch_details.push( - "persistExtendedHistory override was provided and ignored while running".to_string(), - ); - } - mismatch_details } @@ -750,6 +748,10 @@ impl ThreadRequestProcessor { "`permissions` cannot be combined with `sandbox`", )); } + if persist_extended_history { + self.send_persist_extended_history_deprecation_notice(request_id.connection_id) + .await; + } let environment_selections = self.parse_environment_selections(environments)?; let mut typesafe_overrides = self.build_thread_config_overrides( model, @@ -792,7 +794,6 @@ impl ThreadRequestProcessor { dynamic_tools, session_start_source, environment_selections, - persist_extended_history, service_name, experimental_raw_events, request_trace, @@ -841,6 +842,18 @@ impl ThreadRequestProcessor { self.outgoing.request_trace_context(request_id).await } + async fn send_persist_extended_history_deprecation_notice(&self, connection_id: ConnectionId) { + self.outgoing + .send_server_notification_to_connections( + &[connection_id], + ServerNotification::DeprecationNotice(DeprecationNoticeNotification { + summary: PERSIST_EXTENDED_HISTORY_DEPRECATION_SUMMARY.to_string(), + details: Some(PERSIST_EXTENDED_HISTORY_DEPRECATION_DETAILS.to_string()), + }), + ) + .await; + } + async fn submit_core_op( &self, request_id: &ConnectionRequestId, @@ -864,7 +877,6 @@ impl ThreadRequestProcessor { dynamic_tools: Option>, session_start_source: Option, environments: Option>, - persist_extended_history: bool, service_name: Option, experimental_raw_events: bool, request_trace: Option, @@ -981,7 +993,7 @@ impl ThreadRequestProcessor { }, session_source: None, dynamic_tools: core_dynamic_tools, - persist_extended_history, + persist_extended_history: false, metrics_service_name: service_name, parent_trace: request_trace, environments, @@ -990,7 +1002,7 @@ impl ThreadRequestProcessor { "app_server.thread_start.create_thread", otel.name = "app_server.thread_start.create_thread", thread_start.dynamic_tool_count = core_dynamic_tool_count, - thread_start.persist_extended_history = persist_extended_history, + thread_start.persist_extended_history = false, )) .await .map_err(|err| match err { @@ -2202,6 +2214,10 @@ impl ThreadRequestProcessor { .await; return Ok(()); } + if params.persist_extended_history { + self.send_persist_extended_history_deprecation_notice(request_id.connection_id) + .await; + } let _thread_list_state_permit = match self.acquire_thread_list_state_permit().await { Ok(permit) => permit, @@ -2236,7 +2252,7 @@ impl ThreadRequestProcessor { developer_instructions, personality, exclude_turns, - persist_extended_history, + persist_extended_history: _persist_extended_history, } = params; let include_turns = !exclude_turns; @@ -2300,7 +2316,7 @@ impl ThreadRequestProcessor { config.clone(), thread_history, self.auth_manager.clone(), - persist_extended_history, + /*persist_extended_history*/ false, self.request_trace_context(&request_id).await, ) .await @@ -2836,6 +2852,10 @@ impl ThreadRequestProcessor { "`permissions` cannot be combined with `sandbox`", )); } + if persist_extended_history { + self.send_persist_extended_history_deprecation_notice(request_id.connection_id) + .await; + } let source_thread = self .read_stored_thread_for_resume(&thread_id, path.as_ref(), /*include_history*/ true) @@ -2913,7 +2933,7 @@ impl ThreadRequestProcessor { history: history_items.clone(), rollout_path: source_thread.rollout_path.clone(), }), - persist_extended_history, + /*persist_extended_history*/ false, self.request_trace_context(&request_id).await, ) .await diff --git a/codex-rs/app-server/tests/suite/v2/thread_start.rs b/codex-rs/app-server/tests/suite/v2/thread_start.rs index d8a50b88a..2eba33477 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_start.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_start.rs @@ -6,6 +6,7 @@ use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use codex_app_server_protocol::AskForApproval; +use codex_app_server_protocol::DeprecationNoticeNotification; use codex_app_server_protocol::JSONRPCError; use codex_app_server_protocol::JSONRPCMessage; use codex_app_server_protocol::JSONRPCResponse; @@ -50,6 +51,46 @@ use super::analytics::wait_for_analytics_payload; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); const INVALID_REQUEST_ERROR_CODE: i64 = -32600; +#[tokio::test] +async fn thread_start_deprecates_persist_extended_history_true() -> Result<()> { + let server = create_mock_responses_server_repeating_assistant("Done").await; + let codex_home = TempDir::new()?; + create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let req_id = mcp + .send_thread_start_request(ThreadStartParams { + persist_extended_history: true, + ..Default::default() + }) + .await?; + + let notification = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_notification_message("deprecationNotice"), + ) + .await??; + let notice: DeprecationNoticeNotification = serde_json::from_value( + notification + .params + .expect("deprecationNotice params should be present"), + )?; + assert_eq!( + notice.summary, + "persistExtendedHistory is deprecated and ignored" + ); + + timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(req_id)), + ) + .await??; + + Ok(()) +} + #[tokio::test] async fn thread_start_creates_thread_and_emits_started() -> Result<()> { // Provide a mock server and config so model wiring is valid.