Detailed changes
@@ -78,10 +78,10 @@ use crate::user_slash_command::{
};
use crate::{
AgentDiffPane, AgentPanel, AllowAlways, AllowOnce, AuthorizeToolCall, ClearMessageQueue,
- CycleFavoriteModels, CycleModeSelector, EditFirstQueuedMessage, ExpandMessageEditor, Follow,
- KeepAll, NewThread, OpenAddContextMenu, OpenAgentDiff, OpenHistory, RejectAll, RejectOnce,
- RemoveFirstQueuedMessage, SelectPermissionGranularity, SendImmediately, SendNextQueuedMessage,
- ToggleProfileSelector, ToggleThinkingMode,
+ CycleFavoriteModels, CycleModeSelector, EditFirstQueuedMessage, ExpandMessageEditor,
+ ExternalAgentInitialContent, Follow, KeepAll, NewThread, OpenAddContextMenu, OpenAgentDiff,
+ OpenHistory, RejectAll, RejectOnce, RemoveFirstQueuedMessage, SelectPermissionGranularity,
+ SendImmediately, SendNextQueuedMessage, ToggleProfileSelector, ToggleThinkingMode,
};
const STOPWATCH_THRESHOLD: Duration = Duration::from_secs(30);
@@ -388,7 +388,7 @@ impl AcpThreadView {
pub fn new(
agent: Rc<dyn AgentServer>,
resume_thread: Option<AgentSessionInfo>,
- summarize_thread: Option<AgentSessionInfo>,
+ initial_content: Option<ExternalAgentInitialContent>,
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
thread_store: Option<Entity<ThreadStore>>,
@@ -430,8 +430,19 @@ impl AcpThreadView {
window,
cx,
);
- if let Some(entry) = summarize_thread {
- editor.insert_thread_summary(entry, window, cx);
+ if let Some(content) = initial_content {
+ match content {
+ ExternalAgentInitialContent::ThreadSummary(entry) => {
+ editor.insert_thread_summary(entry, window, cx);
+ }
+ ExternalAgentInitialContent::Text(prompt) => {
+ editor.set_message(
+ vec![acp::ContentBlock::Text(acp::TextContent::new(prompt))],
+ window,
+ cx,
+ );
+ }
+ }
}
editor
});
@@ -30,7 +30,10 @@ use crate::{
acp::{AcpThreadHistory, ThreadHistoryEvent},
text_thread_history::{TextThreadHistory, TextThreadHistoryEvent},
};
-use crate::{ExternalAgent, NewExternalAgentThread, NewNativeAgentThreadFromSummary};
+use crate::{
+ ExternalAgent, ExternalAgentInitialContent, NewExternalAgentThread,
+ NewNativeAgentThreadFromSummary,
+};
use agent_settings::AgentSettings;
use ai_onboarding::AgentPanelOnboarding;
use anyhow::{Result, anyhow};
@@ -742,7 +745,7 @@ impl AgentPanel {
self.external_thread(
Some(ExternalAgent::NativeAgent),
None,
- Some(thread),
+ Some(ExternalAgentInitialContent::ThreadSummary(thread)),
window,
cx,
);
@@ -795,7 +798,7 @@ impl AgentPanel {
&mut self,
agent_choice: Option<crate::ExternalAgent>,
resume_thread: Option<AgentSessionInfo>,
- summarize_thread: Option<AgentSessionInfo>,
+ initial_content: Option<ExternalAgentInitialContent>,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -857,7 +860,7 @@ impl AgentPanel {
agent_panel._external_thread(
server,
resume_thread,
- summarize_thread,
+ initial_content,
workspace,
project,
ext_agent,
@@ -1414,6 +1417,21 @@ impl AgentPanel {
}
}
+ pub fn new_external_thread_with_text(
+ &mut self,
+ initial_text: Option<String>,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ self.external_thread(
+ None,
+ None,
+ initial_text.map(ExternalAgentInitialContent::Text),
+ window,
+ cx,
+ );
+ }
+
pub fn new_agent_thread(
&mut self,
agent: AgentType,
@@ -1476,7 +1494,7 @@ impl AgentPanel {
&mut self,
server: Rc<dyn AgentServer>,
resume_thread: Option<AgentSessionInfo>,
- summarize_thread: Option<AgentSessionInfo>,
+ initial_content: Option<ExternalAgentInitialContent>,
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
ext_agent: ExternalAgent,
@@ -1498,7 +1516,7 @@ impl AgentPanel {
crate::acp::AcpThreadView::new(
server,
resume_thread,
- summarize_thread,
+ initial_content,
workspace.clone(),
project,
thread_store,
@@ -217,6 +217,12 @@ impl ExternalAgent {
}
}
+/// Content to initialize new external agent with.
+pub enum ExternalAgentInitialContent {
+ ThreadSummary(acp_thread::AgentSessionInfo),
+ Text(String),
+}
+
/// Opens the profile management interface for configuring agent tools and settings.
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
#[action(namespace = agent)]
@@ -846,13 +846,15 @@ fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut
})
.detach_and_log_err(cx);
}
- OpenRequestKind::AgentPanel => {
+ OpenRequestKind::AgentPanel { initial_prompt } => {
cx.spawn(async move |cx| {
let workspace =
workspace::get_any_active_workspace(app_state, cx.clone()).await?;
workspace.update(cx, |workspace, window, cx| {
- if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
- panel.focus_handle(cx).focus(window, cx);
+ if let Some(panel) = workspace.focus_panel::<AgentPanel>(window, cx) {
+ panel.update(cx, |panel, cx| {
+ panel.new_external_thread_with_text(initial_prompt, window, cx);
+ });
}
})
})
@@ -48,7 +48,9 @@ pub enum OpenRequestKind {
Extension {
extension_id: String,
},
- AgentPanel,
+ AgentPanel {
+ initial_prompt: Option<String>,
+ },
SharedAgentThread {
session_id: String,
},
@@ -108,8 +110,8 @@ impl OpenRequest {
this.kind = Some(OpenRequestKind::Extension {
extension_id: extension_id.to_string(),
});
- } else if url == "zed://agent" {
- this.kind = Some(OpenRequestKind::AgentPanel);
+ } else if let Some(agent_path) = url.strip_prefix("zed://agent") {
+ this.parse_agent_url(agent_path)
} else if let Some(session_id_str) = url.strip_prefix("zed://agent/shared/") {
if uuid::Uuid::parse_str(session_id_str).is_ok() {
this.kind = Some(OpenRequestKind::SharedAgentThread {
@@ -160,6 +162,17 @@ impl OpenRequest {
}
}
+ fn parse_agent_url(&mut self, agent_path: &str) {
+ // Format: "" or "?prompt=<text>"
+ let initial_prompt = agent_path.strip_prefix('?').and_then(|query| {
+ url::form_urlencoded::parse(query.as_bytes())
+ .find_map(|(key, value)| (key == "prompt").then_some(value))
+ .filter(|s| !s.is_empty())
+ .map(|s| s.into_owned())
+ });
+ self.kind = Some(OpenRequestKind::AgentPanel { initial_prompt });
+ }
+
fn parse_git_clone_url(&mut self, clone_path: &str) -> Result<()> {
// Format: /?repo=<url> or ?repo=<url>
let clone_path = clone_path.strip_prefix('/').unwrap_or(clone_path);