Cargo.lock 🔗
@@ -60,6 +60,7 @@ dependencies = [
"assistant_slash_commands",
"assistant_tool",
"async-watch",
+ "audio",
"buffer_diff",
"chrono",
"client",
Danilo Leal created
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.
Cargo.lock | 1
assets/settings/default.json | 7 ++
assets/sounds/agent_done.wav | 0
crates/agent/Cargo.toml | 1
crates/agent/src/active_thread.rs | 14 ++++
crates/agent/src/agent_configuration.rs | 40 +++++++++++++++
crates/assistant_settings/src/assistant_settings.rs | 21 +++++++
crates/audio/src/audio.rs | 2
docs/src/ai/agent-panel.md | 12 +++
9 files changed, 93 insertions(+), 5 deletions(-)
@@ -60,6 +60,7 @@ dependencies = [
"assistant_slash_commands",
"assistant_tool",
"async-watch",
+ "audio",
"buffer_diff",
"chrono",
"client",
@@ -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": {
@@ -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
@@ -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>,
@@ -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(
@@ -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,
@@ -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",
}
}
}
@@ -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}