Improve AI provider model configuration
This commit is contained in:
+121
-27
@@ -10,12 +10,109 @@ use agent_client_protocol::AcpAgentConfig;
|
||||
|
||||
use crate::{DenyByDefaultPermissionHandler, PermissionHandler};
|
||||
|
||||
/// Pinned version of the official Codex ACP adapter.
|
||||
pub const CODEX_ACP_NPM_VERSION: &str = "1.1.7";
|
||||
/// Version of the official Codex ACP adapter supported by the built-in setup.
|
||||
pub const CODEX_ACP_NPM_VERSION: &str = "1.1.14";
|
||||
|
||||
/// Pinned version of OpenCode used by the built-in ACP launch preset.
|
||||
pub const OPENCODE_NPM_VERSION: &str = "1.18.9";
|
||||
|
||||
/// A known ACP client that can be selected in Galaxy settings.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct AcpKnownAgent {
|
||||
pub id: &'static str,
|
||||
pub name: &'static str,
|
||||
pub description: &'static str,
|
||||
pub command: &'static str,
|
||||
pub args: &'static [&'static str],
|
||||
}
|
||||
|
||||
/// Curated ACP Registry catalog. Launch commands are intentionally local-only:
|
||||
/// Galaxy never installs or downloads an agent on the user's behalf.
|
||||
pub const KNOWN_ACP_AGENTS: &[AcpKnownAgent] = &[
|
||||
AcpKnownAgent {
|
||||
id: "codex",
|
||||
name: "Codex",
|
||||
description: "OpenAI's coding assistant",
|
||||
command: "codex",
|
||||
args: &[],
|
||||
},
|
||||
AcpKnownAgent {
|
||||
id: "opencode",
|
||||
name: "OpenCode",
|
||||
description: "Open source coding agent",
|
||||
command: "opencode",
|
||||
args: &["acp"],
|
||||
},
|
||||
AcpKnownAgent {
|
||||
id: "claude-acp",
|
||||
name: "Claude Agent",
|
||||
description: "Anthropic's coding agent",
|
||||
command: "claude-agent-acp",
|
||||
args: &[],
|
||||
},
|
||||
AcpKnownAgent {
|
||||
id: "gemini",
|
||||
name: "Gemini CLI",
|
||||
description: "Google's coding agent",
|
||||
command: "gemini",
|
||||
args: &["--acp"],
|
||||
},
|
||||
AcpKnownAgent {
|
||||
id: "cline",
|
||||
name: "Cline",
|
||||
description: "Autonomous coding agent",
|
||||
command: "cline",
|
||||
args: &["--acp"],
|
||||
},
|
||||
AcpKnownAgent {
|
||||
id: "cursor",
|
||||
name: "Cursor",
|
||||
description: "Cursor's coding agent",
|
||||
command: "cursor-agent",
|
||||
args: &["acp"],
|
||||
},
|
||||
AcpKnownAgent {
|
||||
id: "github-copilot-cli",
|
||||
name: "GitHub Copilot",
|
||||
description: "GitHub's AI pair programmer",
|
||||
command: "copilot",
|
||||
args: &["--acp"],
|
||||
},
|
||||
AcpKnownAgent {
|
||||
id: "goose",
|
||||
name: "Goose",
|
||||
description: "Block's open source AI agent",
|
||||
command: "goose",
|
||||
args: &["acp"],
|
||||
},
|
||||
AcpKnownAgent {
|
||||
id: "auggie",
|
||||
name: "Auggie CLI",
|
||||
description: "Augment Code's coding agent",
|
||||
command: "auggie",
|
||||
args: &["--acp"],
|
||||
},
|
||||
];
|
||||
|
||||
pub fn known_acp_agents() -> &'static [AcpKnownAgent] {
|
||||
KNOWN_ACP_AGENTS
|
||||
}
|
||||
|
||||
/// Resolve a registry-listed agent from the local PATH.
|
||||
pub fn resolve_known_acp_agent(agent_id: &str) -> Result<AcpLaunchConfig, String> {
|
||||
let agent = known_acp_agents()
|
||||
.iter()
|
||||
.find(|agent| agent.id.eq_ignore_ascii_case(agent_id.trim()))
|
||||
.ok_or_else(|| format!("Unknown ACP agent: {agent_id:?}"))?;
|
||||
let command = executable_on_path(agent.command).ok_or_else(|| {
|
||||
format!(
|
||||
"{} is not installed or could not be found on PATH (expected `{}`). Install it or choose Custom.",
|
||||
agent.name, agent.command
|
||||
)
|
||||
})?;
|
||||
Ok(AcpLaunchConfig::new(command).args(agent.args.iter().copied()))
|
||||
}
|
||||
|
||||
const DEFAULT_CANCELLATION_GRACE_PERIOD: Duration = Duration::from_secs(5);
|
||||
const DEFAULT_INITIALIZATION_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const DEFAULT_AUTHENTICATION_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
||||
@@ -37,7 +134,7 @@ impl AcpAgentPreset {
|
||||
pub fn launch_config(self) -> AcpLaunchConfig {
|
||||
match self {
|
||||
Self::Codex => AcpLaunchConfig::new("npx")
|
||||
.args(vec![
|
||||
.args([
|
||||
"--yes".to_owned(),
|
||||
format!("@agentclientprotocol/codex-acp@{CODEX_ACP_NPM_VERSION}"),
|
||||
])
|
||||
@@ -61,9 +158,9 @@ impl AcpAgentPreset {
|
||||
|
||||
/// Resolves the best available executable for this preset.
|
||||
///
|
||||
/// OpenCode's native binary is preferred when installed. The Codex adapter
|
||||
/// uses `npx` when available and can run through Bun's Node compatibility
|
||||
/// mode. OpenCode's npm wrapper requires Node during installation.
|
||||
/// OpenCode's native binary is preferred when installed. Codex runs its ACP
|
||||
/// adapter through npx, while CODEX_PATH points at the user's installed
|
||||
/// Codex CLI rather than downloading a second Codex installation.
|
||||
pub fn resolve_launch_config(self) -> Result<AcpLaunchConfig, String> {
|
||||
self.resolve_launch_config_with(executable_on_path)
|
||||
}
|
||||
@@ -74,33 +171,30 @@ impl AcpAgentPreset {
|
||||
) -> Result<AcpLaunchConfig, String> {
|
||||
match self {
|
||||
Self::Codex => {
|
||||
let (command, args) = if let Some(command) = resolve("npx") {
|
||||
(
|
||||
command,
|
||||
vec![
|
||||
"--yes".to_owned(),
|
||||
format!("@agentclientprotocol/codex-acp@{CODEX_ACP_NPM_VERSION}"),
|
||||
],
|
||||
)
|
||||
} else if let Some(command) = resolve("bunx") {
|
||||
(
|
||||
command,
|
||||
vec![
|
||||
"--bun".to_owned(),
|
||||
format!("@agentclientprotocol/codex-acp@{CODEX_ACP_NPM_VERSION}"),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
let Some(codex) = resolve("codex") else {
|
||||
return Err(
|
||||
"Codex ACP requires npx or bunx; install Node.js/npm or Bun, or configure a custom ACP executable"
|
||||
"Codex ACP requires the locally installed codex CLI; install Codex or configure a custom ACP executable"
|
||||
.to_owned(),
|
||||
);
|
||||
};
|
||||
Ok(AcpLaunchConfig::new(command)
|
||||
.args(args)
|
||||
let launch = if let Some(adapter) = resolve("codex-acp") {
|
||||
AcpLaunchConfig::new(adapter)
|
||||
} else if let Some(npx) = resolve("npx") {
|
||||
AcpLaunchConfig::new(npx).args([
|
||||
"--yes".to_owned(),
|
||||
format!("@agentclientprotocol/codex-acp@{CODEX_ACP_NPM_VERSION}"),
|
||||
])
|
||||
} else {
|
||||
return Err(
|
||||
"Codex ACP requires either a local codex-acp executable or npx; install the ACP adapter, install Node.js/npm, or configure a custom ACP executable"
|
||||
.to_owned(),
|
||||
);
|
||||
};
|
||||
Ok(launch
|
||||
.preferred_auth_method("chat-gpt")
|
||||
.env("DEFAULT_AUTH_REQUEST", r#"{"methodId":"chat-gpt"}"#)
|
||||
.env("INITIAL_AGENT_MODE", "read-only"))
|
||||
.env("INITIAL_AGENT_MODE", "read-only")
|
||||
.codex_path(codex))
|
||||
}
|
||||
Self::OpenCode => {
|
||||
if let Some(command) = resolve("opencode") {
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::time::Duration;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn codex_preset_is_version_pinned() {
|
||||
fn codex_preset_uses_the_adapter_with_npx() {
|
||||
let launch = AcpAgentPreset::Codex.launch_config();
|
||||
|
||||
assert_eq!(launch.command, PathBuf::from("npx"));
|
||||
@@ -62,20 +62,28 @@ fn resolved_opencode_prefers_the_native_executable() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolved_codex_falls_back_to_bun_compatibility_mode() {
|
||||
let resolve = |command: &str| (command == "bunx").then(|| PathBuf::from("/opt/bin/bunx"));
|
||||
fn resolved_codex_uses_npx_adapter_and_local_cli() {
|
||||
let resolve = |command: &str| match command {
|
||||
"npx" => Some(PathBuf::from("/opt/bin/npx")),
|
||||
"codex" => Some(PathBuf::from("/opt/homebrew/bin/codex")),
|
||||
_ => None,
|
||||
};
|
||||
let codex = AcpAgentPreset::Codex
|
||||
.resolve_launch_config_with(resolve)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(codex.command, PathBuf::from("/opt/bin/bunx"));
|
||||
assert_eq!(codex.command, PathBuf::from("/opt/bin/npx"));
|
||||
assert_eq!(
|
||||
codex.args,
|
||||
vec![
|
||||
"--bun".to_owned(),
|
||||
"--yes".to_owned(),
|
||||
format!("@agentclientprotocol/codex-acp@{CODEX_ACP_NPM_VERSION}")
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
codex.env.get("CODEX_PATH").map(String::as_str),
|
||||
Some("/opt/homebrew/bin/codex")
|
||||
);
|
||||
assert_eq!(
|
||||
codex.env.get("INITIAL_AGENT_MODE").map(String::as_str),
|
||||
Some("read-only")
|
||||
@@ -89,13 +97,32 @@ fn resolved_codex_falls_back_to_bun_compatibility_mode() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolved_codex_falls_back_to_local_adapter_without_npx() {
|
||||
let resolve = |command: &str| match command {
|
||||
"codex" => Some(PathBuf::from("/opt/homebrew/bin/codex")),
|
||||
"codex-acp" => Some(PathBuf::from("/opt/bin/codex-acp")),
|
||||
_ => None,
|
||||
};
|
||||
let codex = AcpAgentPreset::Codex
|
||||
.resolve_launch_config_with(resolve)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(codex.command, PathBuf::from("/opt/bin/codex-acp"));
|
||||
assert!(codex.args.is_empty());
|
||||
assert_eq!(
|
||||
codex.env.get("CODEX_PATH").map(String::as_str),
|
||||
Some("/opt/homebrew/bin/codex")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolved_presets_explain_missing_launchers() {
|
||||
let error = AcpAgentPreset::Codex
|
||||
.resolve_launch_config_with(|_| None)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(error.contains("requires npx or bunx"));
|
||||
assert!(error.contains("requires the locally installed codex CLI"));
|
||||
|
||||
let opencode_error = AcpAgentPreset::OpenCode
|
||||
.resolve_launch_config_with(|command| {
|
||||
@@ -103,6 +130,13 @@ fn resolved_presets_explain_missing_launchers() {
|
||||
})
|
||||
.unwrap_err();
|
||||
assert!(opencode_error.contains("requires the opencode executable or npx"));
|
||||
|
||||
let codex_adapter_error = AcpAgentPreset::Codex
|
||||
.resolve_launch_config_with(|command| {
|
||||
(command == "codex").then(|| PathBuf::from("/opt/homebrew/bin/codex"))
|
||||
})
|
||||
.unwrap_err();
|
||||
assert!(codex_adapter_error.contains("requires either a local codex-acp executable or npx"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -21,7 +21,8 @@ pub use agent_runtime::{
|
||||
AcpAgentRuntime, AcpAgentRuntimeConfig, AcpRuntimeState, AcpRuntimeStateHandle,
|
||||
};
|
||||
pub use config::{
|
||||
AcpAgentPreset, AcpLaunchConfig, AcpManagerConfig, CODEX_ACP_NPM_VERSION, OPENCODE_NPM_VERSION,
|
||||
known_acp_agents, resolve_known_acp_agent, AcpAgentPreset, AcpKnownAgent, AcpLaunchConfig,
|
||||
AcpManagerConfig, CODEX_ACP_NPM_VERSION, KNOWN_ACP_AGENTS, OPENCODE_NPM_VERSION,
|
||||
};
|
||||
pub use events::AcpEvent;
|
||||
pub use permissions::{
|
||||
|
||||
@@ -159,7 +159,7 @@ impl AgentRuntime for ChatGPTSubscriptionRuntime {
|
||||
request,
|
||||
self.config.max_output_tokens,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
additional_params,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ fn build_completion_request(
|
||||
request,
|
||||
configured_max_output_tokens,
|
||||
supports_system_messages,
|
||||
false,
|
||||
true,
|
||||
Some(serde_json::json!({
|
||||
"stream_options": { "include_usage": true }
|
||||
})),
|
||||
|
||||
Reference in New Issue
Block a user