Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/apps/cli/src/acp_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ impl ExternalAcpClient {
Self::Opencode => ("opencode", vec!["acp"]),
Self::ClaudeCode => (
"npx",
vec!["--yes", "@zed-industries/claude-code-acp@latest"],
vec!["--yes", "@agentclientprotocol/claude-agent-acp@latest"],
),
Self::Codex => (
"npx",
vec!["--yes", "@agentclientprotocol/codex-acp@latest"],
),
Self::Codex => ("npx", vec!["--yes", "@zed-industries/codex-acp@latest"]),
};
AcpClientConfig {
name: Some(self.display_name().to_string()),
Expand Down
30 changes: 29 additions & 1 deletion src/apps/desktop/src/api/acp_client_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use crate::startup_trace::DesktopStartupTrace;
use bitfun_acp::client::{
AcpAvailableCommand, AcpClientInfo, AcpClientPermissionResponse, AcpClientRequirementProbe,
AcpClientStreamEvent, AcpSessionOptions, CreateAcpFlowSessionRecordResponse,
SetAcpSessionModelRequest, SubmitAcpPermissionResponseRequest,
SetAcpSessionConfigOptionRequest, SetAcpSessionModelRequest,
SubmitAcpPermissionResponseRequest,
};
use serde::{Deserialize, Serialize};
use std::time::Instant;
Expand Down Expand Up @@ -662,6 +663,33 @@ pub async fn set_acp_session_model(
.map_err(|e| e.to_string())
}

#[tauri::command]
pub async fn set_acp_session_config_option(
state: State<'_, AppState>,
request: SetAcpSessionConfigOptionRequest,
) -> Result<AcpSessionOptions, String> {
let service = state
.acp_client_service
.as_ref()
.ok_or_else(|| "ACP client service not initialized".to_string())?;
let session_storage_path = match request.workspace_path.as_deref() {
Some(workspace_path) => Some(
desktop_effective_session_storage_path(
&state,
workspace_path,
request.remote_connection_id.as_deref(),
request.remote_ssh_host.as_deref(),
)
.await,
),
None => None,
};
service
.set_session_config_option(request, session_storage_path)
.await
.map_err(|e| e.to_string())
}

#[tauri::command]
pub async fn stop_acp_client(
state: State<'_, AppState>,
Expand Down
4 changes: 4 additions & 0 deletions src/apps/desktop/src/api/remote_workspace_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,10 @@ pub const REMOTE_WORKSPACE_COMMAND_POLICIES: &[(&str, RemoteWorkspacePolicy)] =
RemoteWorkspacePolicy::LegacyUnaudited,
),
("send_system_notification", RemoteWorkspacePolicy::LocalOnly),
(
"set_acp_session_config_option",
RemoteWorkspacePolicy::RemoteRouted,
),
(
"set_acp_session_model",
RemoteWorkspacePolicy::LegacyUnaudited,
Expand Down
1 change: 1 addition & 0 deletions src/apps/desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,7 @@ pub async fn run() {
get_acp_session_options,
get_acp_session_commands,
set_acp_session_model,
set_acp_session_config_option,
lsp_initialize,
lsp_start_server_for_file,
lsp_stop_server,
Expand Down
112 changes: 105 additions & 7 deletions src/crates/interfaces/acp/src/client/builtin_clients.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use std::collections::HashMap;

use super::config::{AcpClientConfig, AcpClientPermissionMode};
use super::config::{AcpClientConfig, AcpClientConfigFile, AcpClientPermissionMode};

const CLAUDE_ACP_PACKAGE: &str = "@agentclientprotocol/claude-agent-acp";
const CLAUDE_ACP_ARGS: &[&str] = &["--yes", "@agentclientprotocol/claude-agent-acp@latest"];
const LEGACY_CLAUDE_ACP_ARGS: &[&str] = &["--yes", "@zed-industries/claude-code-acp@latest"];
const CODEX_ACP_PACKAGE: &str = "@agentclientprotocol/codex-acp";
const CODEX_ACP_ARGS: &[&str] = &["--yes", "@agentclientprotocol/codex-acp@latest"];
const LEGACY_CODEX_ACP_ARGS: &[&str] = &["--yes", "@zed-industries/codex-acp@latest"];

pub(crate) struct BuiltinAcpClientPreset {
pub(crate) id: &'static str,
Expand Down Expand Up @@ -43,19 +50,19 @@ const BUILTIN_ACP_CLIENT_PRESETS: &[BuiltinAcpClientPreset] = &[
BuiltinAcpClientPreset {
id: "claude-code",
command: "npx",
args: &["--yes", "@zed-industries/claude-code-acp@latest"],
args: CLAUDE_ACP_ARGS,
tool_command: "claude",
install_package: Some("@anthropic-ai/claude-code"),
adapter_package: Some("@zed-industries/claude-code-acp"),
adapter_bin: Some("claude-code-acp"),
adapter_package: Some(CLAUDE_ACP_PACKAGE),
adapter_bin: Some("claude-agent-acp"),
},
BuiltinAcpClientPreset {
id: "codex",
command: "npx",
args: &["--yes", "@zed-industries/codex-acp@latest"],
args: CODEX_ACP_ARGS,
tool_command: "codex",
install_package: Some("@openai/codex"),
adapter_package: Some("@zed-industries/codex-acp"),
adapter_package: Some(CODEX_ACP_PACKAGE),
adapter_bin: Some("codex-acp"),
},
];
Expand Down Expand Up @@ -89,6 +96,29 @@ pub(crate) fn default_config_for_builtin_client(client_id: &str) -> Option<AcpCl
})
}

pub(crate) fn migrate_legacy_builtin_client_configs(config_file: &mut AcpClientConfigFile) {
for (client_id, legacy_args, current_args) in [
("claude-code", LEGACY_CLAUDE_ACP_ARGS, CLAUDE_ACP_ARGS),
("codex", LEGACY_CODEX_ACP_ARGS, CODEX_ACP_ARGS),
] {
let Some(config) = config_file.acp_clients.get_mut(client_id) else {
continue;
};
let uses_legacy_preset = config.command == "npx"
&& config
.args
.iter()
.map(String::as_str)
.eq(legacy_args.iter().copied());
if uses_legacy_preset {
config.args = current_args
.iter()
.map(|value| (*value).to_string())
.collect();
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -100,7 +130,7 @@ mod tests {
assert_eq!(config.command, "npx");
assert_eq!(
config.args,
vec!["--yes", "@zed-industries/claude-code-acp@latest"]
vec!["--yes", "@agentclientprotocol/claude-agent-acp@latest"]
);
}

Expand All @@ -121,4 +151,72 @@ mod tests {
assert_eq!(config.command, "omp");
assert_eq!(config.args, vec!["acp"]);
}

#[test]
fn migrates_only_exact_legacy_builtin_commands() {
let mut config_file = AcpClientConfigFile {
acp_clients: HashMap::from([
(
"claude-code".to_string(),
AcpClientConfig {
name: Some("Claude Code".to_string()),
command: "npx".to_string(),
args: LEGACY_CLAUDE_ACP_ARGS
.iter()
.map(|value| (*value).to_string())
.collect(),
env: HashMap::new(),
enabled: true,
readonly: false,
permission_mode: AcpClientPermissionMode::Ask,
},
),
(
"codex".to_string(),
AcpClientConfig {
name: Some("Codex".to_string()),
command: "npx".to_string(),
args: LEGACY_CODEX_ACP_ARGS
.iter()
.map(|value| (*value).to_string())
.collect(),
env: HashMap::new(),
enabled: true,
readonly: false,
permission_mode: AcpClientPermissionMode::Ask,
},
),
(
"custom-codex".to_string(),
AcpClientConfig {
name: Some("Pinned Codex".to_string()),
command: "npx".to_string(),
args: vec![
"--yes".to_string(),
"@zed-industries/codex-acp@0.16.0".to_string(),
],
env: HashMap::new(),
enabled: true,
readonly: false,
permission_mode: AcpClientPermissionMode::Ask,
},
),
]),
};

migrate_legacy_builtin_client_configs(&mut config_file);

assert_eq!(
config_file.acp_clients["claude-code"].args,
vec!["--yes", "@agentclientprotocol/claude-agent-acp@latest"]
);
assert_eq!(
config_file.acp_clients["codex"].args,
vec!["--yes", "@agentclientprotocol/codex-acp@latest"]
);
assert_eq!(
config_file.acp_clients["custom-codex"].args,
vec!["--yes", "@zed-industries/codex-acp@0.16.0"]
);
}
}
Loading
Loading