From 9563713df2f748e7bd0792e96bbb2a49b916e66b Mon Sep 17 00:00:00 2001 From: xli-oai Date: Mon, 28 Sep 2026 22:35:20 +0000 Subject: [PATCH] Notify lifecycle extensions of compaction usage limits (#49097) ## Why Usage-limit failures during manual or post-turn compaction did not notify turn lifecycle extensions, leaving them unable to react to these errors. ## What changed Emit the turn error lifecycle notification for `UsageLimitExceeded` in both compaction paths. Preserve the completed answer after post-turn compaction fails, and avoid duplicating the error already emitted by manual compaction. GitOrigin-RevId: f31155e5991d762a0779030921aedbd1f818cfbb --- codex-rs/core/src/session/turn.rs | 6 ++++++ codex-rs/core/src/tasks/compact.rs | 19 +++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/codex-rs/core/src/session/turn.rs b/codex-rs/core/src/session/turn.rs index 996cb33e73..818d44dfac 100644 --- a/codex-rs/core/src/session/turn.rs +++ b/codex-rs/core/src/session/turn.rs @@ -736,6 +736,12 @@ pub(crate) async fn run_turn( ) { return Err(err); } + let error = err.to_codex_protocol_error(); + if matches!(error, CodexErrorInfo::UsageLimitExceeded) { + // Preserve the completed answer while stopping automatic work. + sess.emit_turn_error_lifecycle(turn_context.as_ref(), error) + .await; + } warn!(error = %err, "Post-turn compaction failed; preserving the completed turn"); } break; diff --git a/codex-rs/core/src/tasks/compact.rs b/codex-rs/core/src/tasks/compact.rs index 14913d95c8..85f1a54f91 100644 --- a/codex-rs/core/src/tasks/compact.rs +++ b/codex-rs/core/src/tasks/compact.rs @@ -10,6 +10,7 @@ use crate::state::TaskKind; use codex_features::Feature; use codex_model_provider::RemoteCompactionSupport; use codex_protocol::error::CodexErrorDetails; +use codex_protocol::protocol::CodexErrorInfo; use codex_protocol::user_input::UserInput; use tokio_util::sync::CancellationToken; @@ -45,7 +46,8 @@ impl SessionTask for CompactTask { "remote_v2", /*manual*/ true, ); - crate::compact_remote_v2::run_remote_compact_task(session.clone(), ctx).await + crate::compact_remote_v2::run_remote_compact_task(session.clone(), Arc::clone(&ctx)) + .await } RemoteCompactionSupport::Unsupported => { emit_compact_metric( @@ -63,13 +65,18 @@ impl SessionTask for CompactTask { // Compaction prompt is synthesized; no UI element ranges to preserve. text_elements: Vec::new(), }]; - crate::compact::run_compact_task(session.clone(), ctx, input).await + crate::compact::run_compact_task(session.clone(), Arc::clone(&ctx), input).await } }; - if let Err(err) = result - && matches!(err.details(), CodexErrorDetails::TurnAborted) - { - return Err(err); + if let Err(err) = result { + if matches!(err.details(), CodexErrorDetails::TurnAborted) { + return Err(err); + } + let error = err.to_codex_protocol_error(); + if matches!(error, CodexErrorInfo::UsageLimitExceeded) { + // Compaction already emitted the error; notify extensions without emitting it twice. + session.emit_turn_error_lifecycle(ctx.as_ref(), error).await; + } } Ok(None) }