Detailed changes
@@ -292,6 +292,8 @@ pub struct AcpThreadView {
resume_thread_metadata: Option<DbThreadMetadata>,
_cancel_task: Option<Task<()>>,
_subscriptions: [Subscription; 5],
+ #[cfg(target_os = "windows")]
+ show_codex_windows_warning: bool,
}
enum ThreadState {
@@ -397,6 +399,10 @@ impl AcpThreadView {
),
];
+ #[cfg(target_os = "windows")]
+ let show_codex_windows_warning = crate::ExternalAgent::parse_built_in(agent.as_ref())
+ == Some(crate::ExternalAgent::Codex);
+
Self {
agent: agent.clone(),
workspace: workspace.clone(),
@@ -439,6 +445,8 @@ impl AcpThreadView {
focus_handle: cx.focus_handle(),
new_server_version_available: None,
resume_thread_metadata: resume_thread,
+ #[cfg(target_os = "windows")]
+ show_codex_windows_warning,
}
}
@@ -5028,6 +5036,49 @@ impl AcpThreadView {
)
}
+ #[cfg(target_os = "windows")]
+ fn render_codex_windows_warning(&self, cx: &mut Context<Self>) -> Option<Callout> {
+ if self.show_codex_windows_warning {
+ Some(
+ Callout::new()
+ .icon(IconName::Warning)
+ .severity(Severity::Warning)
+ .title("Codex on Windows")
+ .description(
+ "For best performance, run Codex in Windows Subsystem for Linux (WSL2)",
+ )
+ .actions_slot(
+ Button::new("open-wsl-modal", "Open in WSL")
+ .icon_size(IconSize::Small)
+ .icon_color(Color::Muted)
+ .on_click(cx.listener({
+ move |_, _, window, cx| {
+ window.dispatch_action(
+ zed_actions::wsl_actions::OpenWsl::default().boxed_clone(),
+ cx,
+ );
+ cx.notify();
+ }
+ })),
+ )
+ .dismiss_action(
+ IconButton::new("dismiss", IconName::Close)
+ .icon_size(IconSize::Small)
+ .icon_color(Color::Muted)
+ .tooltip(Tooltip::text("Dismiss Warning"))
+ .on_click(cx.listener({
+ move |this, _, _, cx| {
+ this.show_codex_windows_warning = false;
+ cx.notify();
+ }
+ })),
+ ),
+ )
+ } else {
+ None
+ }
+ }
+
fn render_thread_error(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Div> {
let content = match self.thread_error.as_ref()? {
ThreadError::Other(error) => self.render_any_thread_error(error.clone(), cx),
@@ -5515,6 +5566,16 @@ impl Render for AcpThreadView {
_ => this,
})
.children(self.render_thread_retry_status_callout(window, cx))
+ .children({
+ #[cfg(target_os = "windows")]
+ {
+ self.render_codex_windows_warning(cx)
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ Vec::<Empty>::new()
+ }
+ })
.children(self.render_thread_error(window, cx))
.when_some(
self.new_server_version_available.as_ref().filter(|_| {
@@ -222,12 +222,11 @@ enum WhichFontSize {
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub enum AgentType {
#[default]
- Zed,
+ NativeAgent,
TextThread,
Gemini,
ClaudeCode,
Codex,
- NativeAgent,
Custom {
name: SharedString,
command: AgentServerCommand,
@@ -237,8 +236,7 @@ pub enum AgentType {
impl AgentType {
fn label(&self) -> SharedString {
match self {
- Self::Zed | Self::TextThread => "Zed Agent".into(),
- Self::NativeAgent => "Agent 2".into(),
+ Self::NativeAgent | Self::TextThread => "Zed Agent".into(),
Self::Gemini => "Gemini CLI".into(),
Self::ClaudeCode => "Claude Code".into(),
Self::Codex => "Codex".into(),
@@ -248,7 +246,7 @@ impl AgentType {
fn icon(&self) -> Option<IconName> {
match self {
- Self::Zed | Self::NativeAgent | Self::TextThread => None,
+ Self::NativeAgent | Self::TextThread => None,
Self::Gemini => Some(IconName::AiGemini),
Self::ClaudeCode => Some(IconName::AiClaude),
Self::Codex => Some(IconName::AiOpenAi),
@@ -813,7 +811,7 @@ impl AgentPanel {
const LAST_USED_EXTERNAL_AGENT_KEY: &str = "agent_panel__last_used_external_agent";
- #[derive(Default, Serialize, Deserialize)]
+ #[derive(Serialize, Deserialize)]
struct LastUsedExternalAgent {
agent: crate::ExternalAgent,
}
@@ -854,18 +852,18 @@ impl AgentPanel {
.and_then(|value| {
serde_json::from_str::<LastUsedExternalAgent>(&value).log_err()
})
- .unwrap_or_default()
- .agent
+ .map(|agent| agent.agent)
+ .unwrap_or(ExternalAgent::NativeAgent)
}
}
};
+ let server = ext_agent.server(fs, history);
+
if !loading {
- telemetry::event!("Agent Thread Started", agent = ext_agent.name());
+ telemetry::event!("Agent Thread Started", agent = server.telemetry_id());
}
- let server = ext_agent.server(fs, history);
-
this.update_in(cx, |this, window, cx| {
let selected_agent = ext_agent.into();
if this.selected_agent != selected_agent {
@@ -1345,15 +1343,6 @@ impl AgentPanel {
cx: &mut Context<Self>,
) {
match agent {
- AgentType::Zed => {
- window.dispatch_action(
- NewThread {
- from_thread_id: None,
- }
- .boxed_clone(),
- cx,
- );
- }
AgentType::TextThread => {
window.dispatch_action(NewTextThread.boxed_clone(), cx);
}
@@ -161,10 +161,9 @@ pub struct NewNativeAgentThreadFromSummary {
}
// TODO unify this with AgentType
-#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
-enum ExternalAgent {
- #[default]
+pub enum ExternalAgent {
Gemini,
ClaudeCode,
Codex,
@@ -184,13 +183,13 @@ fn placeholder_command() -> AgentServerCommand {
}
impl ExternalAgent {
- fn name(&self) -> &'static str {
- match self {
- Self::NativeAgent => "zed",
- Self::Gemini => "gemini-cli",
- Self::ClaudeCode => "claude-code",
- Self::Codex => "codex",
- Self::Custom { .. } => "custom",
+ pub fn parse_built_in(server: &dyn agent_servers::AgentServer) -> Option<Self> {
+ match server.telemetry_id() {
+ "gemini-cli" => Some(Self::Gemini),
+ "claude-code" => Some(Self::ClaudeCode),
+ "codex" => Some(Self::Codex),
+ "zed" => Some(Self::NativeAgent),
+ _ => None,
}
}