diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index fb91d754..7ab954f2 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -255,7 +255,7 @@ impl ChatWidget { self.request_redraw(); } - fn on_task_complete(&mut self) { + fn on_task_complete(&mut self, last_agent_message: Option) { // If a stream is currently active, finalize only that stream to flush any tail // without emitting stray headers for other streams. if self.stream.is_write_cycle_active() { @@ -270,7 +270,9 @@ impl ChatWidget { // If there is a queued user message, send exactly one now to begin the next turn. self.maybe_send_next_queued_input(); // Emit a notification when the turn completes (suppressed if focused). - self.notify(Notification::AgentTurnComplete); + self.notify(Notification::AgentTurnComplete { + response: last_agent_message.unwrap_or_default(), + }); } pub(crate) fn set_token_info(&mut self, info: Option) { @@ -1082,7 +1084,9 @@ impl ChatWidget { } EventMsg::AgentReasoningSectionBreak(_) => self.on_reasoning_section_break(), EventMsg::TaskStarted(_) => self.on_task_started(), - EventMsg::TaskComplete(TaskCompleteEvent { .. }) => self.on_task_complete(), + EventMsg::TaskComplete(TaskCompleteEvent { last_agent_message }) => { + self.on_task_complete(last_agent_message) + } EventMsg::TokenCount(ev) => self.set_token_info(ev.info), EventMsg::Error(ErrorEvent { message }) => self.on_error(message), EventMsg::TurnAborted(ev) => match ev.reason { @@ -1479,7 +1483,7 @@ impl WidgetRef for &ChatWidget { } enum Notification { - AgentTurnComplete, + AgentTurnComplete { response: String }, ExecApprovalRequested { command: String }, EditApprovalRequested { cwd: PathBuf, changes: Vec }, } @@ -1487,7 +1491,10 @@ enum Notification { impl Notification { fn display(&self) -> String { match self { - Notification::AgentTurnComplete => "Agent turn complete".to_string(), + Notification::AgentTurnComplete { response } => { + Notification::agent_turn_preview(response) + .unwrap_or_else(|| "Agent turn complete".to_string()) + } Notification::ExecApprovalRequested { command } => { format!("Approval requested: {}", truncate_text(command, 30)) } @@ -1507,7 +1514,7 @@ impl Notification { fn type_name(&self) -> &str { match self { - Notification::AgentTurnComplete => "agent-turn-complete", + Notification::AgentTurnComplete { .. } => "agent-turn-complete", Notification::ExecApprovalRequested { .. } | Notification::EditApprovalRequested { .. } => "approval-requested", } @@ -1519,8 +1526,26 @@ impl Notification { Notifications::Custom(allowed) => allowed.iter().any(|a| a == self.type_name()), } } + + fn agent_turn_preview(response: &str) -> Option { + let mut normalized = String::new(); + for part in response.split_whitespace() { + if !normalized.is_empty() { + normalized.push(' '); + } + normalized.push_str(part); + } + let trimmed = normalized.trim(); + if trimmed.is_empty() { + None + } else { + Some(truncate_text(trimmed, AGENT_NOTIFICATION_PREVIEW_GRAPHEMES)) + } + } } +const AGENT_NOTIFICATION_PREVIEW_GRAPHEMES: usize = 200; + const EXAMPLE_PROMPTS: [&str; 6] = [ "Explain this codebase", "Summarize recent commits",