fix(tui): retry token card after stream shutdown

This commit is contained in:
Felipe Coury
2026-05-30 21:09:19 -03:00
parent 6b8ca2b4d2
commit 02c1eeb1ad
7 changed files with 39 additions and 5 deletions
+6 -3
View File
@@ -215,14 +215,14 @@ impl App {
scrollback_reflow,
deferred_history_cell,
)?;
self.insert_completed_token_activity_output_if_ready(tui);
self.insert_completed_token_activity_output_after_stream_shutdown(tui);
}
AppEvent::ConsolidateProposedPlan(source) => {
if !self.terminal_resize_reflow_enabled() {
if !self.transcript_reflow.history_cell_refresh_requested() {
self.transcript_reflow.clear();
}
self.insert_completed_token_activity_output(tui);
self.insert_completed_token_activity_output_after_stream_shutdown(tui);
return Ok(AppRunControl::Continue);
}
let end = self.transcript_cells.len();
@@ -257,7 +257,7 @@ impl App {
self.maybe_finish_stream_reflow(tui)?;
}
self.insert_completed_token_activity_output_if_ready(tui);
self.insert_completed_token_activity_output_after_stream_shutdown(tui);
}
AppEvent::ApplyThreadRollback { num_turns } => {
if self.apply_non_pending_thread_rollback(num_turns) {
@@ -743,6 +743,9 @@ impl App {
self.insert_completed_token_activity_output_if_ready(tui);
}
}
AppEvent::CommitCompletedTokenActivityOutput => {
self.insert_completed_token_activity_output_after_stream_shutdown(tui);
}
AppEvent::ConnectorsLoaded { result, is_final } => {
self.chat_widget.on_connectors_loaded(result, is_final);
}
+10
View File
@@ -48,6 +48,16 @@ impl App {
}
}
pub(super) fn insert_completed_token_activity_output_after_stream_shutdown(
&mut self,
tui: &mut tui::Tui,
) {
if self.chat_widget.token_activity_history_insertion_blocked() {
return;
}
self.insert_completed_token_activity_output(tui);
}
pub(super) fn open_url_in_browser(&mut self, url: String) {
if let Err(err) = webbrowser::open(&url) {
self.chat_widget
+3
View File
@@ -304,6 +304,9 @@ pub(crate) enum AppEvent {
result: Result<GetAccountTokenUsageResponse, String>,
},
/// Commit a settled token activity card after a stream shutdown barrier.
CommitCompletedTokenActivityOutput,
/// Send a user-confirmed request to notify the workspace owner.
SendAddCreditsNudgeEmail {
credit_type: AddCreditsNudgeCreditType,
+3
View File
@@ -51,6 +51,9 @@ impl ChatWidget {
if had_stream_controller && self.stream_controllers_idle() {
self.app_event_tx.send(AppEvent::StopCommitAnimation);
}
if had_stream_controller {
self.request_completed_token_activity_output_insertion();
}
}
pub(super) fn stream_controllers_idle(&self) -> bool {
@@ -1289,8 +1289,12 @@ async fn completed_token_activity_refresh_waits_for_active_stream() {
Some("/tokens daily\n\n Token activity\n Token activity unavailable\n".to_string()),
);
chat.stream_controller = None;
chat.finalize_turn();
assert!(!chat.token_activity_history_insertion_blocked());
assert!(
std::iter::from_fn(|| rx.try_recv().ok())
.any(|event| matches!(event, AppEvent::CommitCompletedTokenActivityOutput))
);
assert!(chat.take_completed_token_activity_output().is_some());
}
+7
View File
@@ -694,6 +694,13 @@ impl ChatWidget {
Some(output)
}
pub(crate) fn request_completed_token_activity_output_insertion(&self) {
if self.completed_token_activity_output.is_some() {
self.app_event_tx
.send(AppEvent::CommitCompletedTokenActivityOutput);
}
}
pub(crate) fn clear_pending_token_activity_refreshes(&mut self) {
let cleared_refresh = self.refreshing_token_activity_output.take().is_some();
let cleared_completed = self.completed_token_activity_output.take().is_some();
+5 -1
View File
@@ -51,7 +51,9 @@ impl ChatWidget {
self.turn_lifecycle.start(Instant::now());
self.transcript.reset_turn_flags();
self.adaptive_chunking.reset();
self.plan_stream_controller = None;
if self.plan_stream_controller.take().is_some() {
self.request_completed_token_activity_output_insertion();
}
self.turn_runtime_metrics = RuntimeMetricsSummary::default();
self.session_telemetry.reset_runtime_metrics();
self.bottom_pane.clear_quit_shortcut_hint();
@@ -127,6 +129,7 @@ impl ChatWidget {
self.app_event_tx
.send(AppEvent::ConsolidateProposedPlan(source));
}
self.request_completed_token_activity_output_insertion();
}
self.flush_unified_exec_wait_streak();
if !from_replay {
@@ -316,6 +319,7 @@ impl ChatWidget {
self.adaptive_chunking.reset();
self.stream_controller = None;
self.plan_stream_controller = None;
self.request_completed_token_activity_output_insertion();
self.status_state.pending_status_indicator_restore = false;
self.request_status_line_branch_refresh();
self.request_status_line_git_summary_refresh();