Use agent reply text in turn notifications (#3756)

Instead of "Agent turn complete", turn-complete notifications now
include the first handful of chars from the agent's final message.
This commit is contained in:
Jeremy Rose
2025-09-17 11:23:46 -07:00
committed by GitHub
parent 208089e58e
commit 530382db05

View File

@@ -255,7 +255,7 @@ impl ChatWidget {
self.request_redraw(); self.request_redraw();
} }
fn on_task_complete(&mut self) { fn on_task_complete(&mut self, last_agent_message: Option<String>) {
// If a stream is currently active, finalize only that stream to flush any tail // If a stream is currently active, finalize only that stream to flush any tail
// without emitting stray headers for other streams. // without emitting stray headers for other streams.
if self.stream.is_write_cycle_active() { 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. // If there is a queued user message, send exactly one now to begin the next turn.
self.maybe_send_next_queued_input(); self.maybe_send_next_queued_input();
// Emit a notification when the turn completes (suppressed if focused). // 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<TokenUsageInfo>) { pub(crate) fn set_token_info(&mut self, info: Option<TokenUsageInfo>) {
@@ -1082,7 +1084,9 @@ impl ChatWidget {
} }
EventMsg::AgentReasoningSectionBreak(_) => self.on_reasoning_section_break(), EventMsg::AgentReasoningSectionBreak(_) => self.on_reasoning_section_break(),
EventMsg::TaskStarted(_) => self.on_task_started(), 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::TokenCount(ev) => self.set_token_info(ev.info),
EventMsg::Error(ErrorEvent { message }) => self.on_error(message), EventMsg::Error(ErrorEvent { message }) => self.on_error(message),
EventMsg::TurnAborted(ev) => match ev.reason { EventMsg::TurnAborted(ev) => match ev.reason {
@@ -1479,7 +1483,7 @@ impl WidgetRef for &ChatWidget {
} }
enum Notification { enum Notification {
AgentTurnComplete, AgentTurnComplete { response: String },
ExecApprovalRequested { command: String }, ExecApprovalRequested { command: String },
EditApprovalRequested { cwd: PathBuf, changes: Vec<PathBuf> }, EditApprovalRequested { cwd: PathBuf, changes: Vec<PathBuf> },
} }
@@ -1487,7 +1491,10 @@ enum Notification {
impl Notification { impl Notification {
fn display(&self) -> String { fn display(&self) -> String {
match self { 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 } => { Notification::ExecApprovalRequested { command } => {
format!("Approval requested: {}", truncate_text(command, 30)) format!("Approval requested: {}", truncate_text(command, 30))
} }
@@ -1507,7 +1514,7 @@ impl Notification {
fn type_name(&self) -> &str { fn type_name(&self) -> &str {
match self { match self {
Notification::AgentTurnComplete => "agent-turn-complete", Notification::AgentTurnComplete { .. } => "agent-turn-complete",
Notification::ExecApprovalRequested { .. } Notification::ExecApprovalRequested { .. }
| Notification::EditApprovalRequested { .. } => "approval-requested", | Notification::EditApprovalRequested { .. } => "approval-requested",
} }
@@ -1519,8 +1526,26 @@ impl Notification {
Notifications::Custom(allowed) => allowed.iter().any(|a| a == self.type_name()), Notifications::Custom(allowed) => allowed.iter().any(|a| a == self.type_name()),
} }
} }
fn agent_turn_preview(response: &str) -> Option<String> {
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] = [ const EXAMPLE_PROMPTS: [&str; 6] = [
"Explain this codebase", "Explain this codebase",
"Summarize recent commits", "Summarize recent commits",