feat(acp): add session/set_config and stabilize list, delete and close (#7984)

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Adrian Cole
2026-03-22 18:33:26 -04:00
committed by GitHub
parent 6afaeb8432
commit 1426c8bfa0
32 changed files with 2539 additions and 1100 deletions
+3 -1
View File
@@ -2,4 +2,6 @@ mod common;
mod provider;
pub use common::{map_permission_response, PermissionDecision, PermissionMapping};
pub use provider::{extension_configs_to_mcp_servers, AcpProvider, AcpProviderConfig};
pub use provider::{
extension_configs_to_mcp_servers, AcpProvider, AcpProviderConfig, ACP_CURRENT_MODEL,
};
File diff suppressed because it is too large Load Diff
+1
View File
@@ -8,6 +8,7 @@ use utoipa::ToSchema;
Debug,
Default,
Eq,
Hash,
PartialEq,
Serialize,
Deserialize,
+16 -24
View File
@@ -1,9 +1,11 @@
use anyhow::Result;
use futures::future::BoxFuture;
use std::collections::HashMap;
use std::path::PathBuf;
use crate::acp::{
extension_configs_to_mcp_servers, AcpProvider, AcpProviderConfig, PermissionMapping,
ACP_CURRENT_MODEL,
};
use crate::config::search_path::SearchPaths;
use crate::config::{Config, GooseMode};
@@ -11,7 +13,6 @@ use crate::model::ModelConfig;
use crate::providers::base::{ProviderDef, ProviderMetadata};
const CLAUDE_ACP_PROVIDER_NAME: &str = "claude-acp";
pub const CLAUDE_ACP_DEFAULT_MODEL: &str = "default";
const CLAUDE_ACP_DOC_URL: &str = "https://github.com/zed-industries/claude-agent-acp";
const CLAUDE_ACP_BINARY: &str = "claude-agent-acp";
@@ -25,7 +26,7 @@ impl ProviderDef for ClaudeAcpProvider {
CLAUDE_ACP_PROVIDER_NAME,
"Claude Code",
"ACP wrapper for Anthropic's Claude. Install: npm install -g @zed-industries/claude-agent-acp",
CLAUDE_ACP_DEFAULT_MODEL,
ACP_CURRENT_MODEL,
vec![],
CLAUDE_ACP_DOC_URL,
vec![],
@@ -51,6 +52,17 @@ impl ProviderDef for ClaudeAcpProvider {
rejected_tool_status: sacp::schema::ToolCallStatus::Failed,
};
let mode_mapping = HashMap::from([
// Closest to "autonomous": bypassPermissions skips confirmations.
(GooseMode::Auto, "bypassPermissions".to_string()),
// Claude Code's default matches "ask before risky actions".
(GooseMode::Approve, "default".to_string()),
// acceptEdits auto-accepts file edits but still prompts for risky ops.
(GooseMode::SmartApprove, "acceptEdits".to_string()),
// Plan mode disables tool execution, aligning with chat-only intent.
(GooseMode::Chat, "plan".to_string()),
]);
let provider_config = AcpProviderConfig {
command: resolved_command,
args: vec![],
@@ -59,7 +71,8 @@ impl ProviderDef for ClaudeAcpProvider {
env_remove: vec!["CLAUDECODE".to_string()],
work_dir: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
mcp_servers: extension_configs_to_mcp_servers(&extensions),
session_mode_id: Some(map_goose_mode(goose_mode)),
session_mode_id: Some(mode_mapping[&goose_mode].clone()),
mode_mapping,
permission_mapping,
notification_callback: None,
};
@@ -69,24 +82,3 @@ impl ProviderDef for ClaudeAcpProvider {
})
}
}
fn map_goose_mode(goose_mode: GooseMode) -> String {
match goose_mode {
GooseMode::Auto => {
// Closest to "autonomous": Claude Code's bypassPermissions skips confirmations.
"bypassPermissions".to_string()
}
GooseMode::Approve => {
// Claude Code's default matches "ask before risky actions".
"default".to_string()
}
GooseMode::SmartApprove => {
// Best-effort: acceptEdits auto-accepts file edits but still prompts for risky ops.
"acceptEdits".to_string()
}
GooseMode::Chat => {
// Plan mode disables tool execution, aligning with chat-only intent.
"plan".to_string()
}
}
}
+12 -2
View File
@@ -1,9 +1,11 @@
use anyhow::Result;
use futures::future::BoxFuture;
use std::collections::HashMap;
use std::path::PathBuf;
use crate::acp::{
extension_configs_to_mcp_servers, AcpProvider, AcpProviderConfig, PermissionMapping,
ACP_CURRENT_MODEL,
};
use crate::config::search_path::SearchPaths;
use crate::config::{Config, GooseMode};
@@ -11,7 +13,6 @@ use crate::model::ModelConfig;
use crate::providers::base::{ProviderDef, ProviderMetadata};
const CODEX_ACP_PROVIDER_NAME: &str = "codex-acp";
pub const CODEX_ACP_DEFAULT_MODEL: &str = "gpt-5.2-codex";
const CODEX_ACP_DOC_URL: &str = "https://github.com/zed-industries/codex-acp";
pub struct CodexAcpProvider;
@@ -24,7 +25,7 @@ impl ProviderDef for CodexAcpProvider {
CODEX_ACP_PROVIDER_NAME,
"Codex CLI",
"ACP adapter for OpenAI's coding assistant. Install: npm install -g @zed-industries/codex-acp",
CODEX_ACP_DEFAULT_MODEL,
ACP_CURRENT_MODEL,
vec![],
CODEX_ACP_DOC_URL,
vec![],
@@ -74,6 +75,14 @@ impl ProviderDef for CodexAcpProvider {
rejected_tool_status: sacp::schema::ToolCallStatus::Failed,
};
// Chat and Approve both map to "read-only".
let mode_mapping = HashMap::from([
(GooseMode::Auto, "full-access".to_string()),
(GooseMode::Approve, "read-only".to_string()),
(GooseMode::SmartApprove, "auto".to_string()),
(GooseMode::Chat, "read-only".to_string()),
]);
let provider_config = AcpProviderConfig {
command: resolved_command,
args,
@@ -83,6 +92,7 @@ impl ProviderDef for CodexAcpProvider {
mcp_servers,
// Disabled until https://github.com/zed-industries/codex-acp/issues/179 is fixed.
session_mode_id: None,
mode_mapping,
permission_mapping,
notification_callback: None,
};
+13 -13
View File
@@ -1,9 +1,11 @@
use anyhow::Result;
use futures::future::BoxFuture;
use std::collections::HashMap;
use std::path::PathBuf;
use crate::acp::{
extension_configs_to_mcp_servers, AcpProvider, AcpProviderConfig, PermissionMapping,
ACP_CURRENT_MODEL,
};
use crate::config::search_path::SearchPaths;
use crate::config::{Config, GooseMode};
@@ -11,7 +13,6 @@ use crate::model::ModelConfig;
use crate::providers::base::{ProviderDef, ProviderMetadata};
const GEMINI_ACP_PROVIDER_NAME: &str = "gemini-acp";
pub const GEMINI_ACP_DEFAULT_MODEL: &str = "default";
const GEMINI_ACP_DOC_URL: &str = "https://github.com/google-gemini/gemini-cli";
pub struct GeminiAcpProvider;
@@ -24,7 +25,7 @@ impl ProviderDef for GeminiAcpProvider {
GEMINI_ACP_PROVIDER_NAME,
"Gemini CLI (ACP)",
"ACP provider for Google's Gemini CLI. Install: npm install -g @google/gemini-cli",
GEMINI_ACP_DEFAULT_MODEL,
ACP_CURRENT_MODEL,
vec![],
GEMINI_ACP_DOC_URL,
vec![],
@@ -48,11 +49,18 @@ impl ProviderDef for GeminiAcpProvider {
};
let mut args = vec!["--acp".to_string()];
if model.model_name != "default" {
if model.model_name != ACP_CURRENT_MODEL {
args.push("--model".to_string());
args.push(model.model_name.clone());
}
let mode_mapping = HashMap::from([
(GooseMode::Auto, "yolo".to_string()),
(GooseMode::Approve, "default".to_string()),
(GooseMode::SmartApprove, "auto_edit".to_string()),
(GooseMode::Chat, "plan".to_string()),
]);
let provider_config = AcpProviderConfig {
command: resolved_command,
args,
@@ -60,7 +68,8 @@ impl ProviderDef for GeminiAcpProvider {
env_remove: vec![],
work_dir: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
mcp_servers: extension_configs_to_mcp_servers(&extensions),
session_mode_id: Some(map_goose_mode(goose_mode)),
session_mode_id: Some(mode_mapping[&goose_mode].clone()),
mode_mapping,
permission_mapping,
notification_callback: None,
};
@@ -70,12 +79,3 @@ impl ProviderDef for GeminiAcpProvider {
})
}
}
fn map_goose_mode(goose_mode: GooseMode) -> String {
match goose_mode {
GooseMode::Auto => "yolo".to_string(),
GooseMode::Approve => "default".to_string(),
GooseMode::SmartApprove => "auto_edit".to_string(),
GooseMode::Chat => "plan".to_string(),
}
}
+3 -3
View File
@@ -5,13 +5,13 @@ use crate::model::ModelConfig;
use crate::providers::errors::ProviderError;
use anyhow::{anyhow, Result};
use base64::Engine;
use fs_err::File;
use regex::Regex;
use reqwest::{Response, StatusCode};
use rmcp::model::{AnnotateAble, ImageContent, RawImageContent};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::fmt::Display;
use std::fs::File;
use std::io::{BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
@@ -436,10 +436,10 @@ impl RequestLog {
let log_path = |i| logs_dir.join(format!("llm_request.{}.jsonl", i));
for i in (0..LOGS_TO_KEEP - 1).rev() {
let _ = std::fs::rename(log_path(i), log_path(i + 1));
let _ = fs_err::rename(log_path(i), log_path(i + 1));
}
std::fs::rename(&self.temp_path, log_path(0))?;
fs_err::rename(&self.temp_path, log_path(0))?;
}
Ok(())
}