agent: Add sound notification when done generating (#31472)

This PR adds the ability to hear a sound notification when the agent is
done generating and/or needs user input. This setting is turned off by
default and can be used together with the visual notification. The
specific sound I'm using here comes from the [Material Design 2 Sound
Library](https://m2.material.io/design/sound/sound-resources.html#).

Release Notes:

- agent: Added the ability to have a sound notification when the agent
is done generating and/or needs user input.
This commit is contained in:
Danilo Leal 2025-05-26 21:20:41 -03:00 committed by GitHub
parent fe0bcd14d2
commit d211f88d23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 93 additions and 5 deletions

1
Cargo.lock generated
View file

@ -60,6 +60,7 @@ dependencies = [
"assistant_slash_commands",
"assistant_tool",
"async-watch",
"audio",
"buffer_diff",
"chrono",
"client",

View file

@ -822,7 +822,12 @@
// "primary_screen" - Show the notification only on your primary screen (default)
// "all_screens" - Show these notifications on all screens
// "never" - Never show these notifications
"notify_when_agent_waiting": "primary_screen"
"notify_when_agent_waiting": "primary_screen",
// Whether to play a sound when the agent has either completed
// its response, or needs user input.
// Default: false
"play_sound_when_agent_done": false
},
// The settings for slash commands.
"slash_commands": {

BIN
assets/sounds/agent_done.wav Executable file

Binary file not shown.

View file

@ -26,6 +26,7 @@ assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_tool.workspace = true
async-watch.workspace = true
audio.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
client.workspace = true

View file

@ -16,6 +16,7 @@ use crate::ui::{
use anyhow::Context as _;
use assistant_settings::{AssistantSettings, NotifyWhenAgentWaiting};
use assistant_tool::ToolUseStatus;
use audio::{Audio, Sound};
use collections::{HashMap, HashSet};
use editor::actions::{MoveUp, Paste};
use editor::scroll::Autoscroll;
@ -996,9 +997,10 @@ impl ActiveThread {
}
ThreadEvent::Stopped(reason) => match reason {
Ok(StopReason::EndTurn | StopReason::MaxTokens) => {
let thread = self.thread.read(cx);
let used_tools = self.thread.read(cx).used_tools_since_last_user_message();
self.play_notification_sound(cx);
self.show_notification(
if thread.used_tools_since_last_user_message() {
if used_tools {
"Finished running tools"
} else {
"New message"
@ -1011,6 +1013,7 @@ impl ActiveThread {
_ => {}
},
ThreadEvent::ToolConfirmationNeeded => {
self.play_notification_sound(cx);
self.show_notification("Waiting for tool confirmation", IconName::Info, window, cx);
}
ThreadEvent::StreamedAssistantText(message_id, text) => {
@ -1147,6 +1150,13 @@ impl ActiveThread {
cx.notify();
}
fn play_notification_sound(&self, cx: &mut App) {
let settings = AssistantSettings::get_global(cx);
if settings.play_sound_when_agent_done {
Audio::play_sound(Sound::AgentDone, cx);
}
}
fn show_notification(
&mut self,
caption: impl Into<SharedString>,

View file

@ -327,6 +327,45 @@ impl AgentConfiguration {
)
}
fn render_sound_notification(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let play_sound_when_agent_done =
AssistantSettings::get_global(cx).play_sound_when_agent_done;
h_flex()
.gap_4()
.justify_between()
.flex_wrap()
.child(
v_flex()
.gap_0p5()
.max_w_5_6()
.child(Label::new("Play sound when finished generating"))
.child(
Label::new(
"Hear a notification sound when the agent is done generating changes or needs your input.",
)
.color(Color::Muted),
),
)
.child(
Switch::new("play-sound-notification-switch", play_sound_when_agent_done.into())
.color(SwitchColor::Accent)
.on_click({
let fs = self.fs.clone();
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _| {
settings.set_play_sound_when_agent_done(allow);
},
);
}
}),
)
}
fn render_general_settings_section(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
@ -337,6 +376,7 @@ impl AgentConfiguration {
.child(Headline::new("General Settings"))
.child(self.render_command_permission(cx))
.child(self.render_single_file_review(cx))
.child(self.render_sound_notification(cx))
}
fn render_context_servers_section(

View file

@ -105,6 +105,7 @@ pub struct AssistantSettings {
pub profiles: IndexMap<AgentProfileId, AgentProfile>,
pub always_allow_tool_actions: bool,
pub notify_when_agent_waiting: NotifyWhenAgentWaiting,
pub play_sound_when_agent_done: bool,
pub stream_edits: bool,
pub single_file_review: bool,
pub model_parameters: Vec<LanguageModelParameters>,
@ -285,6 +286,7 @@ impl AssistantSettingsContent {
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
},
VersionedAssistantSettingsContent::V2(ref settings) => settings.clone(),
},
@ -317,6 +319,7 @@ impl AssistantSettingsContent {
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
},
None => AssistantSettingsContentV2::default(),
}
@ -517,6 +520,14 @@ impl AssistantSettingsContent {
.ok();
}
pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
self.v2_setting(|setting| {
setting.play_sound_when_agent_done = Some(allow);
Ok(())
})
.ok();
}
pub fn set_single_file_review(&mut self, allow: bool) {
self.v2_setting(|setting| {
setting.single_file_review = Some(allow);
@ -603,6 +614,7 @@ impl Default for VersionedAssistantSettingsContent {
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
})
}
}
@ -659,6 +671,10 @@ pub struct AssistantSettingsContentV2 {
///
/// Default: "primary_screen"
notify_when_agent_waiting: Option<NotifyWhenAgentWaiting>,
/// Whether to play a sound when the agent has either completed its response, or needs user input.
///
/// Default: false
play_sound_when_agent_done: Option<bool>,
/// Whether to stream edits from the agent as they are received.
///
/// Default: false
@ -884,6 +900,10 @@ impl Settings for AssistantSettings {
&mut settings.notify_when_agent_waiting,
value.notify_when_agent_waiting,
);
merge(
&mut settings.play_sound_when_agent_done,
value.play_sound_when_agent_done,
);
merge(&mut settings.stream_edits, value.stream_edits);
merge(&mut settings.single_file_review, value.single_file_review);
merge(&mut settings.default_profile, value.default_profile);
@ -1027,6 +1047,7 @@ mod tests {
default_view: None,
profiles: None,
always_allow_tool_actions: None,
play_sound_when_agent_done: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,

View file

@ -18,6 +18,7 @@ pub enum Sound {
Unmute,
StartScreenshare,
StopScreenshare,
AgentDone,
}
impl Sound {
@ -29,6 +30,7 @@ impl Sound {
Self::Unmute => "unmute",
Self::StartScreenshare => "start_screenshare",
Self::StopScreenshare => "stop_screenshare",
Self::AgentDone => "agent_done",
}
}
}

View file

@ -39,9 +39,17 @@ To follow the agent reading through your codebase and performing edits, click on
### Get Notified {#get-notified}
If you send a prompt to the Agent and then move elsewhere, thus putting Zed in the background, a notification will pop up at the top right of your screen indicating that the Agent has completed its work.
If you send a prompt to the Agent and then move elsewhere, thus putting Zed in the background, you can be notified of whether its response is finished either via:
You can customize the notification behavior, including the option to turn it off entirely, by using the `agent.notify_when_agent_waiting` settings key.
- a visual notification that appears in the top right of your screen
- or a sound notification
You can use both notification methods together or just pick one of them.
For the visual notification, you can customize its behavior, including the option to turn it off entirely, by using the `agent.notify_when_agent_waiting` settings key.
For the sound notification, turn it on or off using the `agent.play_sound_when_agent_done` settings key.
#### Sound Notification
### Reviewing Changes {#reviewing-changes}