codex: address PR review feedback (#18290)

This commit is contained in:
Eric Traut
2026-04-17 08:48:03 -07:00
parent d1542245b6
commit 02fee5130e
2 changed files with 51 additions and 30 deletions

View File

@@ -1066,7 +1066,7 @@ impl ThreadComposerState {
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ThreadInputState {
composer: Option<ThreadComposerState>,
pending_steers: VecDeque<UserMessage>,
pending_steers: VecDeque<PendingSteer>,
rejected_steers_queue: VecDeque<UserMessage>,
queued_user_messages: VecDeque<UserMessage>,
active_turn_id: Option<String>,
@@ -1102,6 +1102,7 @@ impl From<&str> for UserMessage {
}
}
#[derive(Clone, Debug, PartialEq)]
struct PendingSteer {
user_message: UserMessage,
compare_key: PendingSteerCompareKey,
@@ -3363,11 +3364,7 @@ impl ChatWidget {
};
Some(ThreadInputState {
composer: composer.has_content().then_some(composer),
pending_steers: self
.pending_steers
.iter()
.map(|pending| pending.user_message.clone())
.collect(),
pending_steers: self.pending_steers.clone(),
rejected_steers_queue: self.rejected_steers_queue.clone(),
queued_user_messages: self.queued_user_messages.clone(),
active_turn_id: self
@@ -3415,19 +3412,7 @@ impl ChatWidget {
);
self.bottom_pane.set_composer_pending_pastes(Vec::new());
}
self.pending_steers = input_state
.pending_steers
.into_iter()
.map(|user_message| PendingSteer {
compare_key: PendingSteerCompareKey {
message: user_message.text.clone(),
image_count: user_message.local_images.len()
+ user_message.remote_image_urls.len(),
},
turn_id: input_state.active_turn_id.clone(),
user_message,
})
.collect();
self.pending_steers = input_state.pending_steers;
self.rejected_steers_queue = input_state.rejected_steers_queue;
self.queued_user_messages = input_state.queued_user_messages;
} else {
@@ -5815,6 +5800,7 @@ impl ChatWidget {
duration_ms,
} = turn;
if matches!(status, TurnStatus::InProgress) {
self.last_turn_id = Some(turn_id.clone());
self.last_non_retry_error = None;
self.on_task_started();
}
@@ -6314,6 +6300,7 @@ impl ChatWidget {
notification.error.codex_error_info,
Some(AppServerCodexErrorInfo::ActiveTurnNotSteerable { .. })
) {
self.queue_unacknowledged_pending_steers_for_turn(&notification.turn_id);
self.finalize_turn();
self.request_redraw();
self.clear_restored_active_turn_if_matches(&notification.turn_id);
@@ -6517,23 +6504,28 @@ impl ChatWidget {
TurnStatus::Interrupted => {
self.last_non_retry_error = None;
if from_replay {
if !replayed_turn_matches_restored_active_turn {
return;
if replayed_turn_matches_restored_active_turn {
self.restore_pending_messages_after_replayed_incomplete_turn();
self.clear_restored_active_turn_if_matches(&turn_id);
} else {
self.finalize_turn();
self.request_redraw();
}
self.restore_pending_messages_after_replayed_incomplete_turn();
self.clear_restored_active_turn_if_matches(&turn_id);
} else {
self.on_interrupted_turn(TurnAbortReason::Interrupted);
}
}
TurnStatus::Failed => {
if from_replay {
if !replayed_turn_matches_restored_active_turn {
return;
if replayed_turn_matches_restored_active_turn {
self.last_non_retry_error = None;
self.restore_pending_messages_after_replayed_incomplete_turn();
self.clear_restored_active_turn_if_matches(&turn_id);
} else {
self.last_non_retry_error = None;
self.finalize_turn();
self.request_redraw();
}
self.last_non_retry_error = None;
self.restore_pending_messages_after_replayed_incomplete_turn();
self.clear_restored_active_turn_if_matches(&turn_id);
return;
}
if let Some(error) = notification.turn.error {

View File

@@ -350,7 +350,9 @@ async fn review_restores_context_window_indicator() {
async fn restore_thread_input_state_restores_pending_steers_without_downgrading_them() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let mut pending_steers = VecDeque::new();
pending_steers.push_back(UserMessage::from("pending steer"));
let mut pending_steer = pending_steer("pending steer");
pending_steer.turn_id = Some("turn-1".to_string());
pending_steers.push_back(pending_steer);
let mut rejected_steers_queue = VecDeque::new();
rejected_steers_queue.push_back(UserMessage::from("already rejected"));
let mut queued_user_messages = VecDeque::new();
@@ -361,7 +363,7 @@ async fn restore_thread_input_state_restores_pending_steers_without_downgrading_
pending_steers,
rejected_steers_queue,
queued_user_messages,
active_turn_id: None,
active_turn_id: Some("turn-1".to_string()),
current_collaboration_mode: chat.current_collaboration_mode.clone(),
active_collaboration_mask: chat.active_collaboration_mask.clone(),
task_running: false,
@@ -377,6 +379,10 @@ async fn restore_thread_input_state_restores_pending_steers_without_downgrading_
chat.pending_steers.front().unwrap().user_message.text,
"pending steer"
);
assert_eq!(
chat.pending_steers.front().unwrap().turn_id.as_deref(),
Some("turn-1")
);
}
#[tokio::test]
@@ -505,6 +511,29 @@ async fn replayed_completion_does_not_retry_pending_steer() {
assert_no_submit_op(&mut op_rx);
}
#[tokio::test]
async fn replayed_in_progress_turn_is_captured_as_active_turn() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.replay_thread_turns(
vec![AppServerTurn {
id: "active-turn".to_string(),
items: Vec::new(),
status: AppServerTurnStatus::InProgress,
error: None,
started_at: None,
completed_at: None,
duration_ms: None,
}],
ReplayKind::ThreadSnapshot,
);
let input_state = chat
.capture_thread_input_state()
.expect("expected thread input state");
assert_eq!(input_state.active_turn_id.as_deref(), Some("active-turn"));
}
#[tokio::test]
async fn replayed_user_message_acknowledges_pending_steer_only_for_restored_turn() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;