codex: address PR review feedback (#18290)

This commit is contained in:
Eric Traut
2026-04-17 09:08:59 -07:00
parent 02fee5130e
commit 67fd4bb22f
2 changed files with 72 additions and 2 deletions

View File

@@ -5800,6 +5800,8 @@ impl ChatWidget {
duration_ms,
} = turn;
if matches!(status, TurnStatus::InProgress) {
self.restored_active_turn_id
.get_or_insert_with(|| turn_id.clone());
self.last_turn_id = Some(turn_id.clone());
self.last_non_retry_error = None;
self.on_task_started();
@@ -6212,7 +6214,15 @@ impl ChatWidget {
}
}
ServerNotification::TurnStarted(notification) => {
self.last_turn_id = Some(notification.turn.id);
let turn_id = notification.turn.id;
if from_replay {
if self.restored_active_turn_id.is_none() {
self.restored_active_turn_id = Some(turn_id.clone());
} else if !self.replayed_turn_matches_restored_active_turn(&turn_id) {
return;
}
}
self.last_turn_id = Some(turn_id);
self.last_non_retry_error = None;
if !matches!(replay_kind, Some(ReplayKind::ResumeInitialMessages)) {
self.on_task_started();
@@ -6296,10 +6306,18 @@ impl ChatWidget {
if self.replayed_turn_is_stale_for_restored_input(&notification.turn_id) {
return;
}
if !matches!(
if matches!(
notification.error.codex_error_info,
Some(AppServerCodexErrorInfo::ActiveTurnNotSteerable { .. })
) {
if self.pending_steers.front().is_some_and(|pending| {
pending.turn_id.as_deref().is_none_or(|pending_turn_id| {
pending_turn_id == notification.turn_id
})
}) {
self.enqueue_rejected_steer();
}
} else {
self.queue_unacknowledged_pending_steers_for_turn(&notification.turn_id);
self.finalize_turn();
self.request_redraw();

View File

@@ -532,6 +532,25 @@ async fn replayed_in_progress_turn_is_captured_as_active_turn() {
.capture_thread_input_state()
.expect("expected thread input state");
assert_eq!(input_state.active_turn_id.as_deref(), Some("active-turn"));
assert_eq!(chat.restored_active_turn_id.as_deref(), Some("active-turn"));
chat.handle_server_notification(
ServerNotification::TurnStarted(TurnStartedNotification {
thread_id: "thread-1".to_string(),
turn: AppServerTurn {
id: "older-turn".to_string(),
items: Vec::new(),
status: AppServerTurnStatus::InProgress,
error: None,
started_at: None,
completed_at: None,
duration_ms: None,
},
}),
Some(ReplayKind::ThreadSnapshot),
);
assert_eq!(chat.last_turn_id.as_deref(), Some("active-turn"));
}
#[tokio::test]
@@ -565,6 +584,39 @@ async fn replayed_user_message_acknowledges_pending_steer_only_for_restored_turn
assert!(chat.pending_steers.is_empty());
}
#[tokio::test]
async fn replayed_steer_rejection_queues_matching_pending_steer() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let mut pending = pending_steer("retry after rejection");
pending.turn_id = Some("active-turn".to_string());
chat.pending_steers.push_back(pending);
chat.restored_active_turn_id = Some("active-turn".to_string());
chat.handle_server_notification(
ServerNotification::Error(ErrorNotification {
error: AppServerTurnError {
message: "cannot steer this turn".to_string(),
codex_error_info: Some(
codex_app_server_protocol::CodexErrorInfo::ActiveTurnNotSteerable {
turn_kind: codex_app_server_protocol::NonSteerableTurnKind::Review,
},
),
additional_details: None,
},
will_retry: false,
thread_id: "thread-1".to_string(),
turn_id: "active-turn".to_string(),
}),
Some(ReplayKind::ThreadSnapshot),
);
assert!(chat.pending_steers.is_empty());
assert_eq!(
chat.queued_user_message_texts(),
vec!["retry after rejection"]
);
}
#[tokio::test]
async fn steer_enter_uses_pending_steers_while_final_answer_stream_is_active() {
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;