Fix approval workflow (#1696)

(Hopefully) temporary solution to the invisible approvals problem -
prints commands to history when they need approval and then also prints
the result of the approval. In the near future we should be able to do
some fancy stuff with updating commands before writing them to permanent
history.

Also, ctr-c while in the approval modal now acts as esc (aborts command)
and puts the TUI in the state where one additional ctr-c will exit.
This commit is contained in:
easong-openai
2025-07-28 12:00:06 -07:00
committed by GitHub
parent 19bef7659f
commit 80c19ea77c
5 changed files with 168 additions and 13 deletions

View File

@@ -34,8 +34,10 @@ use crate::app_event::AppEvent;
use crate::app_event_sender::AppEventSender;
use crate::bottom_pane::BottomPane;
use crate::bottom_pane::BottomPaneParams;
use crate::bottom_pane::CancellationEvent;
use crate::bottom_pane::InputResult;
use crate::conversation_history_widget::ConversationHistoryWidget;
use crate::exec_command::strip_bash_lc_and_escape;
use crate::history_cell::PatchEventType;
use crate::user_approval_widget::ApprovalRequest;
use codex_file_search::FileMatch;
@@ -301,6 +303,20 @@ impl ChatWidget<'_> {
cwd,
reason,
}) => {
// Print the command to the history so it is visible in the
// transcript *before* the modal asks for approval.
let cmdline = strip_bash_lc_and_escape(&command);
let text = format!(
"command requires approval:\n$ {cmdline}{reason}",
reason = reason
.as_ref()
.map(|r| format!("\n{r}"))
.unwrap_or_default()
);
self.conversation_history.add_background_event(text);
self.emit_last_history_entry();
self.conversation_history.scroll_to_bottom();
let request = ApprovalRequest::Exec {
id,
command,
@@ -308,6 +324,7 @@ impl ChatWidget<'_> {
reason,
};
self.bottom_pane.push_approval_request(request);
self.request_redraw();
}
EventMsg::ApplyPatchApprovalRequest(ApplyPatchApprovalRequestEvent {
call_id: _,
@@ -453,21 +470,25 @@ impl ChatWidget<'_> {
}
/// Handle Ctrl-C key press.
/// Returns true if the key press was handled, false if it was not.
/// If the key press was not handled, the caller should handle it (likely by exiting the process).
pub(crate) fn on_ctrl_c(&mut self) -> bool {
/// Returns CancellationEvent::Handled if the event was consumed by the UI, or
/// CancellationEvent::Ignored if the caller should handle it (e.g. exit).
pub(crate) fn on_ctrl_c(&mut self) -> CancellationEvent {
match self.bottom_pane.on_ctrl_c() {
CancellationEvent::Handled => return CancellationEvent::Handled,
CancellationEvent::Ignored => {}
}
if self.bottom_pane.is_task_running() {
self.bottom_pane.clear_ctrl_c_quit_hint();
self.submit_op(Op::Interrupt);
self.answer_buffer.clear();
self.reasoning_buffer.clear();
false
CancellationEvent::Ignored
} else if self.bottom_pane.ctrl_c_quit_hint_visible() {
self.submit_op(Op::Shutdown);
true
CancellationEvent::Handled
} else {
self.bottom_pane.show_ctrl_c_quit_hint();
false
CancellationEvent::Ignored
}
}