Skip to content

Commit 5e8659d

Browse files
authored
chore: undo nits (#5631)
1 parent 2338294 commit 5e8659d

File tree

7 files changed

+66
-18
lines changed

7 files changed

+66
-18
lines changed

codex-rs/core/src/codex/compact.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::sync::Arc;
22

33
use super::Session;
44
use super::TurnContext;
5+
use super::filter_model_visible_history;
56
use super::get_last_assistant_message_from_turn;
67
use crate::Prompt;
78
use crate::client_common::ResponseEvent;
@@ -86,8 +87,9 @@ async fn run_compact_task_inner(
8687

8788
loop {
8889
let turn_input = history.get_history();
90+
let prompt_input = filter_model_visible_history(turn_input.clone());
8991
let prompt = Prompt {
90-
input: turn_input.clone(),
92+
input: prompt_input.clone(),
9193
..Default::default()
9294
};
9395
let attempt_result = drain_to_completed(&sess, turn_context.as_ref(), &prompt).await;
@@ -109,7 +111,7 @@ async fn run_compact_task_inner(
109111
return;
110112
}
111113
Err(e @ CodexErr::ContextWindowExceeded) => {
112-
if turn_input.len() > 1 {
114+
if prompt_input.len() > 1 {
113115
// Trim from the beginning to preserve cache (prefix-based) and keep recent messages intact.
114116
error!(
115117
"Context window exceeded while compacting; removing oldest history item. Error: {e}"
@@ -152,7 +154,13 @@ async fn run_compact_task_inner(
152154
let summary_text = get_last_assistant_message_from_turn(&history_snapshot).unwrap_or_default();
153155
let user_messages = collect_user_messages(&history_snapshot);
154156
let initial_context = sess.build_initial_context(turn_context.as_ref());
155-
let new_history = build_compacted_history(initial_context, &user_messages, &summary_text);
157+
let mut new_history = build_compacted_history(initial_context, &user_messages, &summary_text);
158+
let ghost_snapshots: Vec<ResponseItem> = history_snapshot
159+
.iter()
160+
.filter(|item| matches!(item, ResponseItem::GhostSnapshot { .. }))
161+
.cloned()
162+
.collect();
163+
new_history.extend(ghost_snapshots);
156164
sess.replace_history(new_history).await;
157165

158166
let rollout_item = RolloutItem::Compacted(CompactedItem {

codex-rs/exec/src/event_processor_with_human_output.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ impl EventProcessor for EventProcessorWithHumanOutput {
499499
| EventMsg::GetHistoryEntryResponse(_)
500500
| EventMsg::McpListToolsResponse(_)
501501
| EventMsg::ListCustomPromptsResponse(_)
502+
| EventMsg::RawResponseItem(_)
502503
| EventMsg::UserMessage(_)
503504
| EventMsg::EnteredReviewMode(_)
504505
| EventMsg::ExitedReviewMode(_)
@@ -507,7 +508,6 @@ impl EventProcessor for EventProcessorWithHumanOutput {
507508
| EventMsg::AgentReasoningRawContentDelta(_)
508509
| EventMsg::ItemStarted(_)
509510
| EventMsg::ItemCompleted(_)
510-
| EventMsg::RawResponseItem(_)
511511
| EventMsg::UndoCompleted(_)
512512
| EventMsg::UndoStarted(_) => {}
513513
}

codex-rs/git-tooling/src/ghost_commits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,13 @@ pub fn restore_ghost_commit(repo_path: &Path, commit: &GhostCommit) -> Result<()
159159
let repo_prefix = repo_subdir(repo_root.as_path(), repo_path);
160160
let current_untracked =
161161
capture_existing_untracked(repo_root.as_path(), repo_prefix.as_deref())?;
162+
restore_to_commit_inner(repo_root.as_path(), repo_prefix.as_deref(), commit.id())?;
162163
remove_new_untracked(
163164
repo_root.as_path(),
164165
commit.preexisting_untracked_files(),
165166
commit.preexisting_untracked_dirs(),
166167
current_untracked,
167-
)?;
168-
restore_to_commit_inner(repo_root.as_path(), repo_prefix.as_deref(), commit.id())
168+
)
169169
}
170170

171171
/// Restore the working tree to match the given commit ID.

codex-rs/tui/src/bottom_pane/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ impl BottomPane {
348348
));
349349
}
350350
if let Some(status) = self.status.as_mut() {
351+
status.set_interrupt_hint_visible(true);
351352
status.set_queued_messages(self.queued_user_messages.clone());
352353
}
353354
self.request_redraw();
@@ -374,6 +375,13 @@ impl BottomPane {
374375
}
375376
}
376377

378+
pub(crate) fn set_interrupt_hint_visible(&mut self, visible: bool) {
379+
if let Some(status) = self.status.as_mut() {
380+
status.set_interrupt_hint_visible(visible);
381+
self.request_redraw();
382+
}
383+
}
384+
377385
pub(crate) fn set_context_window_percent(&mut self, percent: Option<i64>) {
378386
if self.context_window_percent == percent {
379387
return;

codex-rs/tui/src/chatwidget.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,6 @@ impl ChatWidget {
304304
}
305305

306306
fn set_status_header(&mut self, header: String) {
307-
if self.current_status_header == header {
308-
return;
309-
}
310307
self.current_status_header = header.clone();
311308
self.bottom_pane.update_status_header(header);
312309
}
@@ -429,6 +426,7 @@ impl ChatWidget {
429426
self.bottom_pane.clear_ctrl_c_quit_hint();
430427
self.bottom_pane.set_task_running(true);
431428
self.retry_status_header = None;
429+
self.bottom_pane.set_interrupt_hint_visible(true);
432430
self.set_status_header(String::from("Working"));
433431
self.full_reasoning_buffer.clear();
434432
self.reasoning_buffer.clear();
@@ -666,6 +664,7 @@ impl ChatWidget {
666664

667665
fn on_undo_started(&mut self, event: UndoStartedEvent) {
668666
self.bottom_pane.ensure_status_indicator();
667+
self.bottom_pane.set_interrupt_hint_visible(false);
669668
let message = event
670669
.message
671670
.unwrap_or_else(|| "Undo in progress...".to_string());

codex-rs/tui/src/chatwidget/tests.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,25 @@ fn undo_failure_events_render_error_message() {
933933
);
934934
}
935935

936+
#[test]
937+
fn undo_started_hides_interrupt_hint() {
938+
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
939+
940+
chat.handle_codex_event(Event {
941+
id: "turn-hint".to_string(),
942+
msg: EventMsg::UndoStarted(UndoStartedEvent { message: None }),
943+
});
944+
945+
let status = chat
946+
.bottom_pane
947+
.status_widget()
948+
.expect("status indicator should be active");
949+
assert!(
950+
!status.interrupt_hint_visible(),
951+
"undo should hide the interrupt hint because the operation cannot be cancelled"
952+
);
953+
}
954+
936955
/// The commit picker shows only commit subjects (no timestamps).
937956
#[test]
938957
fn review_commit_picker_shows_subjects_without_timestamps() {

codex-rs/tui/src/status_indicator_widget.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub(crate) struct StatusIndicatorWidget {
2525
header: String,
2626
/// Queued user messages to display under the status line.
2727
queued_messages: Vec<String>,
28+
/// Whether to show the interrupt hint (Esc).
29+
show_interrupt_hint: bool,
2830

2931
elapsed_running: Duration,
3032
last_resume_at: Instant,
@@ -55,6 +57,7 @@ impl StatusIndicatorWidget {
5557
Self {
5658
header: String::from("Working"),
5759
queued_messages: Vec::new(),
60+
show_interrupt_hint: true,
5861
elapsed_running: Duration::ZERO,
5962
last_resume_at: Instant::now(),
6063
is_paused: false,
@@ -98,16 +101,23 @@ impl StatusIndicatorWidget {
98101

99102
/// Update the animated header label (left of the brackets).
100103
pub(crate) fn update_header(&mut self, header: String) {
101-
if self.header != header {
102-
self.header = header;
103-
}
104+
self.header = header;
105+
}
106+
107+
pub(crate) fn set_interrupt_hint_visible(&mut self, visible: bool) {
108+
self.show_interrupt_hint = visible;
104109
}
105110

106111
#[cfg(test)]
107112
pub(crate) fn header(&self) -> &str {
108113
&self.header
109114
}
110115

116+
#[cfg(test)]
117+
pub(crate) fn interrupt_hint_visible(&self) -> bool {
118+
self.show_interrupt_hint
119+
}
120+
111121
/// Replace the queued messages displayed beneath the header.
112122
pub(crate) fn set_queued_messages(&mut self, queued: Vec<String>) {
113123
self.queued_messages = queued;
@@ -175,12 +185,16 @@ impl WidgetRef for StatusIndicatorWidget {
175185
spans.push(spinner(Some(self.last_resume_at)));
176186
spans.push(" ".into());
177187
spans.extend(shimmer_spans(&self.header));
178-
spans.extend(vec![
179-
" ".into(),
180-
format!("({pretty_elapsed} • ").dim(),
181-
key_hint::plain(KeyCode::Esc).into(),
182-
" to interrupt)".dim(),
183-
]);
188+
spans.push(" ".into());
189+
if self.show_interrupt_hint {
190+
spans.extend(vec![
191+
format!("({pretty_elapsed} • ").dim(),
192+
key_hint::plain(KeyCode::Esc).into(),
193+
" to interrupt)".dim(),
194+
]);
195+
} else {
196+
spans.push(format!("({pretty_elapsed})").dim());
197+
}
184198

185199
// Build lines: status, then queued messages, then spacer.
186200
let mut lines: Vec<Line<'static>> = Vec::new();

0 commit comments

Comments
 (0)