From 38941b1d26ac5c2fd27892669c62fbe190cbe5ec Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 21 Apr 2026 16:26:49 -0400 Subject: [PATCH] merge goose-acp crate into goose (#8726) --- AGENTS.md | 1 - CUSTOM_DISTROS.md | 2 +- Cargo.lock | 42 +---- Justfile | 6 +- crates/goose-acp/Cargo.toml | 67 ------- crates/goose-acp/src/lib.rs | 9 - crates/goose-cli/Cargo.toml | 5 +- crates/goose-cli/src/cli.rs | 7 +- crates/goose/Cargo.toml | 14 +- crates/{goose-acp => goose}/acp-meta.json | 0 crates/{goose-acp => goose}/acp-schema.json | 0 .../src => goose/src/acp}/adapters.rs | 0 crates/{goose-acp/src => goose/src/acp}/fs.rs | 26 +-- crates/goose/src/acp/mod.rs | 7 + .../src => goose/src/acp}/server.rs | 167 +++++++++--------- .../src => goose/src/acp}/server_factory.rs | 11 +- .../{goose-acp/src => goose/src/acp}/tools.rs | 0 .../src => goose/src/acp}/transport/http.rs | 7 +- .../src/acp/transport/mod.rs} | 2 +- .../src/acp}/transport/websocket.rs | 7 +- .../src/bin/generate_acp_schema.rs | 2 +- crates/goose/src/lib.rs | 1 + .../tests/acp_common_tests}/mod.rs | 76 ++++---- .../tests/acp_custom_requests_test.rs} | 7 +- .../tests/acp_fixtures}/mod.rs | 4 +- .../tests/acp_fixtures}/provider.rs | 0 .../tests/acp_fixtures}/server.rs | 0 .../tests/acp_provider_test.rs} | 1 + .../tests/acp_server_test.rs} | 1 + .../tests/acp_test_data}/openai_basic.txt | 0 .../acp_test_data}/openai_builtin_execute.txt | 0 .../acp_test_data}/openai_builtin_final.txt | 0 .../acp_test_data}/openai_builtin_search.txt | 0 .../openai_fs_read_tool_call.txt | 0 .../openai_fs_read_tool_result.txt | 0 .../openai_fs_write_tool_call.txt | 0 .../openai_fs_write_tool_result.txt | 0 .../openai_image_attachment.txt | 0 .../acp_test_data}/openai_image_tool_call.txt | 0 .../openai_image_tool_result.txt | 0 .../tests/acp_test_data}/openai_models.json | 0 .../acp_test_data}/openai_shell_tool_call.txt | 0 .../openai_shell_tool_result.txt | 0 .../tests/acp_test_data}/openai_tool_call.txt | 0 .../acp_test_data}/openai_tool_result.txt | 0 documentation/docs/guides/acp-clients.md | 4 +- ui/sdk/README.md | 2 +- ui/sdk/generate-schema.ts | 4 +- 48 files changed, 196 insertions(+), 286 deletions(-) delete mode 100644 crates/goose-acp/Cargo.toml delete mode 100644 crates/goose-acp/src/lib.rs rename crates/{goose-acp => goose}/acp-meta.json (100%) rename crates/{goose-acp => goose}/acp-schema.json (100%) rename crates/{goose-acp/src => goose/src/acp}/adapters.rs (100%) rename crates/{goose-acp/src => goose/src/acp}/fs.rs (96%) rename crates/{goose-acp/src => goose/src/acp}/server.rs (97%) rename crates/{goose-acp/src => goose/src/acp}/server_factory.rs (80%) rename crates/{goose-acp/src => goose/src/acp}/tools.rs (100%) rename crates/{goose-acp/src => goose/src/acp}/transport/http.rs (97%) rename crates/{goose-acp/src/transport.rs => goose/src/acp/transport/mod.rs} (98%) rename crates/{goose-acp/src => goose/src/acp}/transport/websocket.rs (96%) rename crates/{goose-acp => goose}/src/bin/generate_acp_schema.rs (99%) rename crates/{goose-acp/tests/common_tests => goose/tests/acp_common_tests}/mod.rs (94%) rename crates/{goose-acp/tests/custom_requests_test.rs => goose/tests/acp_custom_requests_test.rs} (97%) rename crates/{goose-acp/tests/fixtures => goose/tests/acp_fixtures}/mod.rs (99%) rename crates/{goose-acp/tests/fixtures => goose/tests/acp_fixtures}/provider.rs (100%) rename crates/{goose-acp/tests/fixtures => goose/tests/acp_fixtures}/server.rs (100%) rename crates/{goose-acp/tests/provider_test.rs => goose/tests/acp_provider_test.rs} (99%) rename crates/{goose-acp/tests/server_test.rs => goose/tests/acp_server_test.rs} (99%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_basic.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_builtin_execute.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_builtin_final.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_builtin_search.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_fs_read_tool_call.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_fs_read_tool_result.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_fs_write_tool_call.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_fs_write_tool_result.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_image_attachment.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_image_tool_call.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_image_tool_result.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_models.json (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_shell_tool_call.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_shell_tool_result.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_tool_call.txt (100%) rename crates/{goose-acp/tests/test_data => goose/tests/acp_test_data}/openai_tool_result.txt (100%) diff --git a/AGENTS.md b/AGENTS.md index cb036c9eac..ca6db72ed9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,7 +47,6 @@ git commit -s # required for DCO sign-off ``` crates/ ├── goose # core logic -├── goose-acp # Agent Client Protocol ├── goose-acp-macros # ACP proc macros ├── goose-cli # CLI entry ├── goose-server # backend (binary: goosed) diff --git a/CUSTOM_DISTROS.md b/CUSTOM_DISTROS.md index c9f08a2241..d4f7c15ecb 100644 --- a/CUSTOM_DISTROS.md +++ b/CUSTOM_DISTROS.md @@ -416,7 +416,7 @@ For the full ACP specification, see the [Agent Client Protocol documentation](ht - API client example: `ui/desktop/src/api/` (generated TypeScript client) **ACP**: -- ACP server implementation: `crates/goose-acp/src/server.rs` +- ACP server implementation: `crates/goose/src/acp/server.rs` - CLI integration: `crates/goose-cli/src/cli.rs` (Command::Acp) - Protocol library: `sacp` crate (Rust implementation of ACP) - Test client example: `test_acp_client.py` diff --git a/Cargo.lock b/Cargo.lock index c799a881e9..328768afa8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4358,9 +4358,12 @@ dependencies = [ "fs-err", "fs2", "futures", + "goose-acp-macros", + "goose-mcp", "goose-sdk", "goose-test-support", "http 1.4.0", + "http-body-util", "ignore", "include_dir", "indexmap 2.13.0", @@ -4419,6 +4422,7 @@ dependencies = [ "tokio-cron-scheduler", "tokio-stream", "tokio-util", + "tower-http", "tracing", "tracing-futures", "tracing-opentelemetry", @@ -4446,43 +4450,6 @@ dependencies = [ "zip 8.4.0", ] -[[package]] -name = "goose-acp" -version = "1.31.0" -dependencies = [ - "agent-client-protocol-schema", - "anyhow", - "async-stream", - "async-trait", - "axum", - "base64 0.22.1", - "fs-err", - "futures", - "goose", - "goose-acp-macros", - "goose-mcp", - "goose-sdk", - "goose-test-support", - "http-body-util", - "regex", - "rmcp", - "sacp", - "schemars 1.2.1", - "serde", - "serde_json", - "sqlx", - "strum 0.27.2", - "tempfile", - "test-case", - "tokio", - "tokio-util", - "tower-http", - "tracing", - "url", - "uuid", - "wiremock", -] - [[package]] name = "goose-acp-macros" version = "1.31.0" @@ -4513,7 +4480,6 @@ dependencies = [ "etcetera 0.11.0", "futures", "goose", - "goose-acp", "goose-mcp", "indicatif", "open", diff --git a/Justfile b/Justfile index fd9a43e9e4..3e7a220929 100644 --- a/Justfile +++ b/Justfile @@ -205,7 +205,7 @@ check-acp-schema: generate-acp-types #!/usr/bin/env bash set -e echo "🔍 Checking ACP schema and generated types are up-to-date..." - if ! git diff --exit-code crates/goose-acp/acp-schema.json crates/goose-acp/acp-meta.json ui/sdk/src/generated/; then + if ! git diff --exit-code crates/goose/acp-schema.json crates/goose/acp-meta.json ui/sdk/src/generated/; then echo "" echo "❌ ACP generated files are out of date!" echo "" @@ -217,8 +217,8 @@ check-acp-schema: generate-acp-types # Generate ACP JSON schema from Rust types generate-acp-schema: @echo "Generating ACP schema..." - cd crates/goose-acp && cargo run --bin generate-acp-schema - @echo "ACP schema generated: crates/goose-acp/acp-schema.json, crates/goose-acp/acp-meta.json" + cd crates/goose && cargo run --bin generate-acp-schema + @echo "ACP schema generated: crates/goose/acp-schema.json, crates/goose/acp-meta.json" # Generate ACP TypeScript types from JSON schema (requires generate-acp-schema first) generate-acp-types: generate-acp-schema diff --git a/crates/goose-acp/Cargo.toml b/crates/goose-acp/Cargo.toml deleted file mode 100644 index 7a93e340f1..0000000000 --- a/crates/goose-acp/Cargo.toml +++ /dev/null @@ -1,67 +0,0 @@ -[package] -name = "goose-acp" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -authors.workspace = true -license.workspace = true -repository.workspace = true -description.workspace = true - -[[bin]] -name = "generate-acp-schema" -path = "src/bin/generate_acp_schema.rs" - -[features] -default = ["code-mode", "rustls-tls"] -code-mode = ["goose/code-mode"] -local-inference = ["goose/local-inference"] -rustls-tls = ["goose/rustls-tls", "goose-mcp/rustls-tls"] -native-tls = ["goose/native-tls", "goose-mcp/native-tls"] - -[lints] -workspace = true - -[dependencies] -goose = { path = "../goose", default-features = false } -goose-mcp = { path = "../goose-mcp", default-features = false } -rmcp = { workspace = true } -sacp = { workspace = true, features = ["unstable"] } -agent-client-protocol-schema = { workspace = true } -async-trait = { workspace = true } -anyhow = { workspace = true } -tokio = { workspace = true } -tokio-util = { workspace = true, features = ["compat", "rt"] } -tracing = { workspace = true } -serde_json = { workspace = true } -futures = { workspace = true } -regex = { workspace = true } -fs-err = "3" -strum = { workspace = true } -url = { workspace = true } - -# HTTP server dependencies -axum = { workspace = true, features = ["ws"] } -serde = { workspace = true, features = ["derive"] } -tower-http = { workspace = true, features = ["cors"] } -async-stream = { workspace = true } -http-body-util = "0.1.3" -uuid = { workspace = true, features = ["v7"] } -schemars = { workspace = true, features = ["derive"] } -goose-acp-macros = { path = "../goose-acp-macros" } -goose-sdk = { path = "../goose-sdk" } -base64 = { workspace = true } - -[dev-dependencies] -async-trait = { workspace = true } -goose-test-support = { path = "../goose-test-support" } -wiremock = { workspace = true } -tempfile = { workspace = true } -test-case = { workspace = true } -axum = { workspace = true } -rmcp = { workspace = true, features = ["transport-streamable-http-server"] } -sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio-rustls", "sqlite"] } - -[package.metadata.cargo-machete] -# Used to provide extras imports for sacp -ignored = ["agent-client-protocol-schema"] diff --git a/crates/goose-acp/src/lib.rs b/crates/goose-acp/src/lib.rs deleted file mode 100644 index d1bddef8c5..0000000000 --- a/crates/goose-acp/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![recursion_limit = "256"] - -mod adapters; -pub use goose_sdk::custom_requests; -mod fs; -pub mod server; -pub mod server_factory; -pub(crate) mod tools; -pub mod transport; diff --git a/crates/goose-cli/Cargo.toml b/crates/goose-cli/Cargo.toml index 79a1aeb36b..7c801cdf6f 100644 --- a/crates/goose-cli/Cargo.toml +++ b/crates/goose-cli/Cargo.toml @@ -22,7 +22,6 @@ path = "src/bin/generate_manpages.rs" [dependencies] clap_mangen = "0.2.31" goose = { path = "../goose", default-features = false } -goose-acp = { path = "../goose-acp", default-features = false } goose-mcp = { path = "../goose-mcp" } rmcp = { workspace = true } clap = { workspace = true } @@ -71,8 +70,8 @@ winapi = { workspace = true } [features] default = ["code-mode", "local-inference", "aws-providers", "telemetry", "otel", "rustls-tls"] -code-mode = ["goose/code-mode", "goose-acp/code-mode"] -local-inference = ["goose/local-inference", "goose-acp/local-inference"] +code-mode = ["goose/code-mode"] +local-inference = ["goose/local-inference"] aws-providers = ["goose/aws-providers"] cuda = ["goose/cuda", "local-inference"] telemetry = ["goose/telemetry"] diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index 5999e31961..0a5efeb75c 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -1064,8 +1064,9 @@ async fn handle_mcp_command(server: McpCommand) -> Result<()> { } async fn handle_serve_command(host: String, port: u16, builtins: Vec) -> Result<()> { + use goose::acp::server_factory::{AcpServer, AcpServerFactoryConfig}; + use goose::acp::transport::create_router; use goose::config::paths::Paths; - use goose_acp::server_factory::{AcpServer, AcpServerFactoryConfig}; use std::net::SocketAddr; use std::sync::Arc; use tracing::info; @@ -1081,7 +1082,7 @@ async fn handle_serve_command(host: String, port: u16, builtins: Vec) -> data_dir: Paths::data_dir(), config_dir: Paths::config_dir(), })); - let router = goose_acp::transport::create_router(server); + let router = create_router(server); let addr: SocketAddr = format!("{}:{}", host, port).parse()?; info!("Starting ACP server on {}", addr); @@ -1766,7 +1767,7 @@ pub async fn cli() -> anyhow::Result<()> { Some(Command::Doctor {}) => crate::commands::doctor::handle_doctor().await, Some(Command::Info { verbose }) => handle_info(verbose), Some(Command::Mcp { server }) => handle_mcp_command(server).await, - Some(Command::Acp { builtins }) => goose_acp::server::run(builtins).await, + Some(Command::Acp { builtins }) => goose::acp::server::run(builtins).await, Some(Command::Serve { host, port, diff --git a/crates/goose/Cargo.toml b/crates/goose/Cargo.toml index be69196353..c118bedc1e 100644 --- a/crates/goose/Cargo.toml +++ b/crates/goose/Cargo.toml @@ -82,7 +82,7 @@ serde = { workspace = true } serde_json = { workspace = true } serde_urlencoded = "0.7" jsonschema = "0.30.0" -uuid = { workspace = true } +uuid = { workspace = true, features = ["v7"] } regex = { workspace = true } async-trait = { workspace = true } async-stream = { workspace = true } @@ -96,7 +96,7 @@ nanoid = "0.4" sha2 = { workspace = true } base64 = { workspace = true } url = { workspace = true } -axum = { workspace = true } +axum = { workspace = true, features = ["ws"] } webbrowser = { workspace = true } lazy_static = "1.5.0" tracing = { workspace = true } @@ -190,6 +190,9 @@ pem = { version = "3", optional = true } pkcs1 = { version = "0.7", default-features = false, features = ["pkcs8"], optional = true } pkcs8 = { version = "0.10", default-features = false, features = ["alloc"], optional = true } sec1 = { version = "0.7", default-features = false, features = ["der", "pkcs8"], optional = true } +goose-acp-macros = { version = "1.31.0", path = "../goose-acp-macros" } +tower-http = { workspace = true, features = ["cors"] } +http-body-util = "0.1.3" [target.'cfg(target_os = "windows")'.dependencies] @@ -222,6 +225,7 @@ opentelemetry_sdk = { workspace = true, features = ["testing"] } goose-test-support = { path = "../goose-test-support" } bytes.workspace = true http.workspace = true +goose-mcp = { path = "../goose-mcp" } [[example]] name = "agent" @@ -244,6 +248,10 @@ path = "src/bin/analyze_cli.rs" name = "build_canonical_models" path = "src/providers/canonical/build_canonical_models.rs" +[[bin]] +name = "generate-acp-schema" +path = "src/bin/generate_acp_schema.rs" + [package.metadata.cargo-machete] ignored = [ @@ -251,4 +259,6 @@ ignored = [ "winapi", # Used to provide extras imports for sacp "agent-client-protocol-schema", + # Used via http transport + "http-body-util", ] diff --git a/crates/goose-acp/acp-meta.json b/crates/goose/acp-meta.json similarity index 100% rename from crates/goose-acp/acp-meta.json rename to crates/goose/acp-meta.json diff --git a/crates/goose-acp/acp-schema.json b/crates/goose/acp-schema.json similarity index 100% rename from crates/goose-acp/acp-schema.json rename to crates/goose/acp-schema.json diff --git a/crates/goose-acp/src/adapters.rs b/crates/goose/src/acp/adapters.rs similarity index 100% rename from crates/goose-acp/src/adapters.rs rename to crates/goose/src/acp/adapters.rs diff --git a/crates/goose-acp/src/fs.rs b/crates/goose/src/acp/fs.rs similarity index 96% rename from crates/goose-acp/src/fs.rs rename to crates/goose/src/acp/fs.rs index 7677deae3f..89381a252f 100644 --- a/crates/goose-acp/src/fs.rs +++ b/crates/goose/src/acp/fs.rs @@ -1,13 +1,13 @@ -use crate::tools::AcpAwareToolMeta; +use crate::acp::tools::AcpAwareToolMeta; +use crate::agents::mcp_client::{Error as McpError, McpClientTrait}; +use crate::agents::platform_extensions::developer::edit::{ + resolve_path, string_replace, FileEditParams, FileReadParams, FileWriteParams, +}; +use crate::agents::platform_extensions::developer::shell::{ShellParams, OUTPUT_LIMIT_BYTES}; +use crate::agents::platform_extensions::developer::DeveloperClient; use agent_client_protocol_schema::TerminalId; use async_trait::async_trait; use fs_err as fs; -use goose::agents::mcp_client::{Error as McpError, McpClientTrait}; -use goose::agents::platform_extensions::developer::edit::{ - resolve_path, string_replace, FileEditParams, FileReadParams, FileWriteParams, -}; -use goose::agents::platform_extensions::developer::shell::{ShellParams, OUTPUT_LIMIT_BYTES}; -use goose::agents::platform_extensions::developer::DeveloperClient; use rmcp::model::{CallToolResult, Content as RmcpContent, Tool, ToolAnnotations}; use sacp::schema::{ CreateTerminalRequest, Diff, KillTerminalRequest, ReadTextFileRequest, ReleaseTerminalRequest, @@ -93,7 +93,7 @@ fn read_tool() -> Tool { } impl AcpTools { - fn update_tool_call(&self, ctx: &goose::agents::ToolCallContext, fields: ToolCallUpdateFields) { + fn update_tool_call(&self, ctx: &crate::agents::ToolCallContext, fields: ToolCallUpdateFields) { if let Some(ref req_id) = ctx.tool_call_request_id { let _ = self .cx @@ -125,7 +125,7 @@ impl AcpTools { async fn acp_read( &self, arguments: Option, - ctx: &goose::agents::ToolCallContext, + ctx: &crate::agents::ToolCallContext, ) -> Result { let params: FileReadParams = match Self::parse_args(arguments) { Ok(p) => p, @@ -150,7 +150,7 @@ impl AcpTools { async fn acp_write( &self, arguments: Option, - ctx: &goose::agents::ToolCallContext, + ctx: &crate::agents::ToolCallContext, ) -> Result { let params: FileWriteParams = match Self::parse_args(arguments) { Ok(p) => p, @@ -187,7 +187,7 @@ impl AcpTools { async fn acp_edit( &self, arguments: Option, - ctx: &goose::agents::ToolCallContext, + ctx: &crate::agents::ToolCallContext, ) -> Result { let params: FileEditParams = match Self::parse_args(arguments) { Ok(p) => p, @@ -240,7 +240,7 @@ impl AcpTools { async fn acp_shell( &self, arguments: Option, - ctx: &goose::agents::ToolCallContext, + ctx: &crate::agents::ToolCallContext, ) -> Result { let params: ShellParams = match Self::parse_args(arguments) { Ok(p) => p, @@ -404,7 +404,7 @@ impl McpClientTrait for AcpTools { async fn call_tool( &self, - ctx: &goose::agents::ToolCallContext, + ctx: &crate::agents::ToolCallContext, name: &str, arguments: Option, cancellation_token: CancellationToken, diff --git a/crates/goose/src/acp/mod.rs b/crates/goose/src/acp/mod.rs index 6e995948f1..3887b45b28 100644 --- a/crates/goose/src/acp/mod.rs +++ b/crates/goose/src/acp/mod.rs @@ -1,7 +1,14 @@ +mod adapters; mod common; +pub(crate) mod fs; mod provider; +pub mod server; +pub mod server_factory; +pub(crate) mod tools; +pub mod transport; pub use common::{map_permission_response, PermissionDecision}; +pub use goose_sdk::custom_requests; pub use provider::{ extension_configs_to_mcp_servers, AcpProvider, AcpProviderConfig, ACP_CURRENT_MODEL, }; diff --git a/crates/goose-acp/src/server.rs b/crates/goose/src/acp/server.rs similarity index 97% rename from crates/goose-acp/src/server.rs rename to crates/goose/src/acp/server.rs index 2c62d17ec2..1b8048703b 100644 --- a/crates/goose-acp/src/server.rs +++ b/crates/goose/src/acp/server.rs @@ -1,37 +1,36 @@ -use crate::custom_requests::*; -use crate::fs::AcpTools; -use crate::tools::AcpAwareToolMeta; -use anyhow::Result; -use fs_err as fs; -use futures::future::BoxFuture; -use goose::acp::{PermissionDecision, ACP_CURRENT_MODEL}; -use goose::agents::extension::{Envs, PLATFORM_EXTENSIONS}; -use goose::agents::mcp_client::McpClientTrait; -use goose::agents::platform_extensions::developer::DeveloperClient; -use goose::agents::{Agent, AgentConfig, ExtensionConfig, GoosePlatform, SessionConfig}; -use goose::builtin_extension::register_builtin_extensions; -use goose::config::base::CONFIG_YAML_NAME; -use goose::config::extensions::get_enabled_extensions_with_config; -use goose::config::paths::Paths; -use goose::config::permission::PermissionManager; -use goose::config::{Config, GooseMode}; -use goose::conversation::message::{ActionRequiredData, Message, MessageContent}; +use crate::acp::custom_requests::*; +use crate::acp::fs::AcpTools; +use crate::acp::tools::AcpAwareToolMeta; +use crate::acp::{PermissionDecision, ACP_CURRENT_MODEL}; +use crate::agents::extension::{Envs, PLATFORM_EXTENSIONS}; +use crate::agents::mcp_client::McpClientTrait; +use crate::agents::platform_extensions::developer::DeveloperClient; +use crate::agents::{Agent, AgentConfig, ExtensionConfig, GoosePlatform, SessionConfig}; +use crate::config::base::CONFIG_YAML_NAME; +use crate::config::extensions::get_enabled_extensions_with_config; +use crate::config::paths::Paths; +use crate::config::permission::PermissionManager; +use crate::config::{Config, GooseMode}; +use crate::conversation::message::{ActionRequiredData, Message, MessageContent}; #[cfg(feature = "local-inference")] -use goose::dictation::providers::transcribe_local; -use goose::dictation::providers::{ +use crate::dictation::providers::transcribe_local; +use crate::dictation::providers::{ all_providers, is_configured, transcribe_with_provider, DictationProvider, }; #[cfg(feature = "local-inference")] -use goose::dictation::whisper; -use goose::mcp_utils::ToolResult; -use goose::permission::permission_confirmation::PrincipalType; -use goose::permission::{Permission, PermissionConfirmation}; -use goose::providers::base::Provider; -use goose::providers::inventory::{ +use crate::dictation::whisper; +use crate::mcp_utils::ToolResult; +use crate::permission::permission_confirmation::PrincipalType; +use crate::permission::{Permission, PermissionConfirmation}; +use crate::providers::base::Provider; +use crate::providers::inventory::{ ProviderInventoryEntry, ProviderInventoryService, RefreshSkipReason, }; -use goose::session::session_manager::SessionType; -use goose::session::{EnabledExtensionsState, Session, SessionManager}; +use crate::session::session_manager::SessionType; +use crate::session::{EnabledExtensionsState, Session, SessionManager}; +use anyhow::Result; +use fs_err as fs; +use futures::future::BoxFuture; use goose_acp_macros::custom_methods; use rmcp::model::{CallToolResult, RawContent, ResourceContents, Role}; use sacp::schema::{ @@ -69,7 +68,7 @@ use url::Url; pub type AcpProviderFactory = Arc< dyn Fn( String, - goose::model::ModelConfig, + crate::model::ModelConfig, Vec, ) -> BoxFuture<'static, Result>> + Send @@ -106,7 +105,7 @@ const ELEVENLABS_TRANSCRIPTION_MODEL: &str = "scribe_v1"; struct GooseAcpSession { agent: AgentHandle, internal_session_id: String, - tool_requests: HashMap, + tool_requests: HashMap, cancel_token: Option, /// Working directory set while the agent was still loading. /// Applied once the agent becomes ready. @@ -127,7 +126,7 @@ struct AgentSetupRequest { mcp_servers: Vec, /// Pre-resolved provider name + model config (from config, no network). /// When present the spawn skips re-deriving these from config. - resolved_provider: Option<(String, goose::model::ModelConfig)>, + resolved_provider: Option<(String, crate::model::ModelConfig)>, /// Pre-instantiated provider reused from synchronous session initialization. prebuilt_provider: Option>, } @@ -140,7 +139,7 @@ pub struct GooseAcpAgent { client_terminal: OnceCell, config_dir: std::path::PathBuf, session_manager: Arc, - thread_manager: Arc, + thread_manager: Arc, permission_manager: Arc, goose_mode: GooseMode, disable_session_naming: bool, @@ -220,7 +219,7 @@ fn is_developer_file_tool(tool_name: &str) -> bool { } fn extract_locations_from_meta( - tool_response: &goose::conversation::message::ToolResponse, + tool_response: &crate::conversation::message::ToolResponse, ) -> Option> { let result = tool_response.tool_result.as_ref().ok()?; let meta = result.meta.as_ref()?; @@ -242,8 +241,8 @@ fn extract_locations_from_meta( } fn extract_tool_locations( - tool_request: &goose::conversation::message::ToolRequest, - tool_response: &goose::conversation::message::ToolResponse, + tool_request: &crate::conversation::message::ToolRequest, + tool_response: &crate::conversation::message::ToolResponse, ) -> Vec { let mut locations = Vec::new(); @@ -385,7 +384,7 @@ fn summarize_tool_call(tool_name: &str, arguments: Option<&serde_json::Value>) - if !s.is_empty() { let first_line = s.lines().next().unwrap_or(&s); if first_line.len() > 60 { - return Some(format!("{}…", goose::utils::safe_truncate(first_line, 57))); + return Some(format!("{}…", crate::utils::safe_truncate(first_line, 57))); } return Some(first_line.to_string()); } @@ -458,7 +457,7 @@ fn inventory_entry_to_dto(entry: ProviderInventoryEntry) -> ProviderInventoryEnt } } -fn provider_config_key_to_dto(key: goose::providers::base::ConfigKey) -> ProviderConfigKey { +fn provider_config_key_to_dto(key: crate::providers::base::ConfigKey) -> ProviderConfigKey { ProviderConfigKey { name: key.name, required: key.required, @@ -494,7 +493,7 @@ struct ProviderOptionEntry { } async fn list_provider_entries(current_provider: Option<&str>) -> Vec { - let mut providers = goose::providers::providers() + let mut providers = crate::providers::providers() .await .into_iter() .map(|(metadata, _)| ProviderOptionEntry { @@ -548,7 +547,7 @@ fn session_provider_selection(session: &Session) -> &str { async fn resolve_provider_and_model_from_config( config: &Config, goose_session: &Session, -) -> Result<(String, goose::model::ModelConfig), String> { +) -> Result<(String, crate::model::ModelConfig), String> { let global_provider = config.get_goose_provider().ok(); let provider_override = goose_session .provider_name @@ -563,17 +562,17 @@ async fn resolve_provider_and_model_from_config( let model_config = match &goose_session.model_config { Some(mc) => mc.clone(), None if explicitly_switched => { - let entry = goose::providers::get_from_registry(&provider_name) + let entry = crate::providers::get_from_registry(&provider_name) .await .map_err(|e| e.to_string())?; let default_model = &entry.metadata().default_model; - goose::model::ModelConfig::new(default_model) + crate::model::ModelConfig::new(default_model) .map_err(|e| e.to_string())? .with_canonical_limits(&provider_name) } None => { let model_id = config.get_goose_model().map_err(|e| e.to_string())?; - goose::model::ModelConfig::new(&model_id) + crate::model::ModelConfig::new(&model_id) .map_err(|e| e.to_string())? .with_canonical_limits(&provider_name) } @@ -586,7 +585,7 @@ async fn resolve_provider_and_model_from_config( async fn resolve_provider_and_model( config_dir: &std::path::Path, goose_session: &Session, -) -> Result<(String, goose::model::ModelConfig), String> { +) -> Result<(String, crate::model::ModelConfig), String> { let config = Config::new(config_dir.join(CONFIG_YAML_NAME), "goose").map_err(|e| e.to_string())?; resolve_provider_and_model_from_config(&config, goose_session).await @@ -704,7 +703,7 @@ impl GooseAcpAgent { disable_session_naming: bool, ) -> Result { let session_manager = Arc::new(SessionManager::new(data_dir)); - let thread_manager = Arc::new(goose::session::ThreadManager::new( + let thread_manager = Arc::new(crate::session::ThreadManager::new( session_manager.storage().clone(), )); let permission_manager = Arc::new(PermissionManager::new(config_dir.clone())); @@ -733,7 +732,7 @@ impl GooseAcpAgent { async fn create_provider( &self, provider_name: &str, - model_config: goose::model::ModelConfig, + model_config: crate::model::ModelConfig, extensions: Vec, ) -> Result> { (self.provider_factory)(provider_name.to_string(), model_config, extensions).await @@ -741,7 +740,7 @@ impl GooseAcpAgent { async fn prepare_session_init_config( &self, - resolved: &Result<(String, goose::model::ModelConfig), String>, + resolved: &Result<(String, crate::model::ModelConfig), String>, mode_state: &SessionModeState, goose_session: &Session, ) -> ( @@ -1187,7 +1186,7 @@ impl GooseAcpAgent { async fn handle_tool_request( &self, - tool_request: &goose::conversation::message::ToolRequest, + tool_request: &crate::conversation::message::ToolRequest, session_id: &SessionId, session: &mut GooseAcpSession, cx: &ConnectionTo, @@ -1235,7 +1234,7 @@ impl GooseAcpAgent { .map(|a| { let s = serde_json::to_string(a).unwrap_or_default(); if s.len() > 300 { - format!("{}…", goose::utils::safe_truncate(&s, 300)) + format!("{}…", crate::utils::safe_truncate(&s, 300)) } else { s } @@ -1316,7 +1315,7 @@ impl GooseAcpAgent { async fn handle_tool_response( &self, - tool_response: &goose::conversation::message::ToolResponse, + tool_response: &crate::conversation::message::ToolResponse, session_id: &SessionId, session: &mut GooseAcpSession, cx: &ConnectionTo, @@ -1539,7 +1538,7 @@ impl GooseAcpAgent { .map(|s| s.to_string()); // Create the Thread — this IS the ACP session from the client's perspective. - let thread_metadata = goose::session::ThreadMetadata { + let thread_metadata = crate::session::ThreadMetadata { provider_id: requested_provider.clone(), mode: Some(self.goose_mode.to_string()), ..Default::default() @@ -1665,7 +1664,7 @@ impl GooseAcpAgent { builder = builder.provider_name(provider); } if let Some(model) = model_name { - if let Ok(mc) = goose::model::ModelConfig::new(model) { + if let Ok(mc) = crate::model::ModelConfig::new(model) { builder = builder.model_config(mc); } } @@ -1820,7 +1819,7 @@ impl GooseAcpAgent { // so that handle_tool_response can extract file locations from the // matching request. No GooseAcpSession required. let mut replay_tool_requests = - HashMap::::new(); + HashMap::::new(); let t_replay = std::time::Instant::now(); let mut replay_notifications: u32 = 0; @@ -2090,7 +2089,7 @@ impl GooseAcpAgent { } match event { - Ok(goose::agents::AgentEvent::Message(message)) => { + Ok(crate::agents::AgentEvent::Message(message)) => { self.thread_manager .append_message(&thread_id, Some(&internal_session_id), &message) .await @@ -2217,7 +2216,7 @@ impl GooseAcpAgent { let provider_name = current_provider.get_name().to_string(); let extensions = EnabledExtensionsState::for_session(&self.session_manager, &internal_id, &config).await; - let model_config = goose::model::ModelConfig::new(model_id) + let model_config = crate::model::ModelConfig::new(model_id) .map_err(|e| { sacp::Error::invalid_params().data(format!("Invalid model config: {}", e)) })? @@ -2279,7 +2278,7 @@ impl GooseAcpAgent { async fn update_thread_metadata( &self, thread_id: &str, - f: impl FnOnce(&mut goose::session::ThreadMetadata), + f: impl FnOnce(&mut crate::session::ThreadMetadata), ) -> Result<(), sacp::Error> { self.thread_manager .update_metadata(thread_id, f) @@ -2418,7 +2417,7 @@ impl GooseAcpAgent { current_model }; let model = model_name.unwrap_or(&default_model); - let model_config = goose::model::ModelConfig::new(model) + let model_config = crate::model::ModelConfig::new(model) .map_err(|e| { sacp::Error::invalid_params().data(format!("Invalid model config: {}", e)) })? @@ -2765,8 +2764,8 @@ impl GooseAcpAgent { #[custom_method(GetExtensionsRequest)] async fn on_get_extensions(&self) -> Result { - let extensions = goose::config::extensions::get_all_extensions(); - let warnings = goose::config::extensions::get_warnings(); + let extensions = crate::config::extensions::get_all_extensions(); + let warnings = crate::config::extensions::get_warnings(); let extensions_json = extensions .into_iter() .map(|e| serde_json::to_value(&e)) @@ -2792,7 +2791,7 @@ impl GooseAcpAgent { let extensions = EnabledExtensionsState::extensions_or_default( Some(&session.extension_data), - goose::config::Config::global(), + crate::config::Config::global(), ); let extensions_json = extensions @@ -2838,9 +2837,9 @@ impl GooseAcpAgent { let provider_id = provider_id.clone(); tokio::spawn(async move { let result = async { - let metadata = goose::providers::get_from_registry(&provider_id).await?; + let metadata = crate::providers::get_from_registry(&provider_id).await?; let model_config = - goose::model::ModelConfig::new(&metadata.metadata().default_model)? + crate::model::ModelConfig::new(&metadata.metadata().default_model)? .with_canonical_limits(&provider_id); let provider = provider_factory(provider_id.clone(), model_config, Vec::new()).await?; @@ -2894,7 +2893,7 @@ impl GooseAcpAgent { })?; let response = match config.get_param::(&req.key) { Ok(value) => ReadConfigResponse { value }, - Err(goose::config::ConfigError::NotFound(_)) => ReadConfigResponse { + Err(crate::config::ConfigError::NotFound(_)) => ReadConfigResponse { value: serde_json::Value::Null, }, Err(e) => return Err(sacp::Error::internal_error().data(e.to_string())), @@ -3076,7 +3075,7 @@ impl GooseAcpAgent { &self, req: CreateSourceRequest, ) -> Result { - let source = goose::sources::create_source( + let source = crate::sources::create_source( req.source_type, &req.name, &req.description, @@ -3092,7 +3091,7 @@ impl GooseAcpAgent { &self, req: ListSourcesRequest, ) -> Result { - let sources = goose::sources::list_sources(req.source_type, req.project_dir.as_deref())?; + let sources = crate::sources::list_sources(req.source_type, req.project_dir.as_deref())?; Ok(ListSourcesResponse { sources }) } @@ -3101,7 +3100,7 @@ impl GooseAcpAgent { &self, req: UpdateSourceRequest, ) -> Result { - let source = goose::sources::update_source( + let source = crate::sources::update_source( req.source_type, &req.name, &req.description, @@ -3117,7 +3116,7 @@ impl GooseAcpAgent { &self, req: DeleteSourceRequest, ) -> Result { - goose::sources::delete_source( + crate::sources::delete_source( req.source_type, &req.name, req.global, @@ -3131,7 +3130,7 @@ impl GooseAcpAgent { &self, req: ExportSourceRequest, ) -> Result { - let (json, filename) = goose::sources::export_source( + let (json, filename) = crate::sources::export_source( req.source_type, &req.name, req.global, @@ -3146,7 +3145,7 @@ impl GooseAcpAgent { req: ImportSourcesRequest, ) -> Result { let sources = - goose::sources::import_sources(&req.data, req.global, req.project_dir.as_deref())?; + crate::sources::import_sources(&req.data, req.global, req.project_dir.as_deref())?; Ok(ImportSourcesResponse { sources }) } @@ -3156,7 +3155,7 @@ impl GooseAcpAgent { req: DictationTranscribeRequest, ) -> Result { use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; - let config = goose::config::Config::global(); + let config = crate::config::Config::global(); #[cfg(not(feature = "local-inference"))] if req.provider == "local" { @@ -3245,7 +3244,7 @@ impl GooseAcpAgent { &self, _req: DictationConfigRequest, ) -> Result { - let config = goose::config::Config::global(); + let config = crate::config::Config::global(); let mut providers = std::collections::HashMap::new(); for def in all_providers() { @@ -3294,7 +3293,7 @@ impl GooseAcpAgent { ) -> Result { #[cfg(feature = "local-inference")] { - use goose::download_manager::{get_download_manager, DownloadStatus}; + use crate::download_manager::{get_download_manager, DownloadStatus}; let manager = get_download_manager(); let models = whisper::available_models() @@ -3326,7 +3325,7 @@ impl GooseAcpAgent { ) -> Result { #[cfg(feature = "local-inference")] { - use goose::download_manager::get_download_manager; + use crate::download_manager::get_download_manager; let model = whisper::get_model(&_req.model_id) .ok_or_else(|| sacp::Error::invalid_params().data("Unknown model id"))?; @@ -3339,7 +3338,7 @@ impl GooseAcpAgent { model.url.to_string(), model.local_path(), Some(Box::new(move || { - let config = goose::config::Config::global(); + let config = crate::config::Config::global(); // Only auto-select this model if the user has no model // currently selected. This prevents silently switching // the active model mid-session when a user downloads an @@ -3381,7 +3380,7 @@ impl GooseAcpAgent { ) -> Result { #[cfg(feature = "local-inference")] { - use goose::download_manager::get_download_manager; + use crate::download_manager::get_download_manager; let manager = get_download_manager(); let progress = @@ -3412,7 +3411,7 @@ impl GooseAcpAgent { ) -> Result { #[cfg(feature = "local-inference")] { - use goose::download_manager::get_download_manager; + use crate::download_manager::get_download_manager; let manager = get_download_manager(); manager @@ -3485,7 +3484,7 @@ impl GooseAcpAgent { } }; - goose::config::Config::global() + crate::config::Config::global() .set_param(key, req.model_id) .map_err(|e| sacp::Error::internal_error().data(e.to_string()))?; @@ -3875,18 +3874,18 @@ where } pub async fn run(builtins: Vec) -> Result<()> { - register_builtin_extensions(goose_mcp::BUILTIN_EXTENSIONS.clone()); info!("listening on stdio"); let outgoing = tokio::io::stdout().compat_write(); let incoming = tokio::io::stdin().compat(); - let server = - crate::server_factory::AcpServer::new(crate::server_factory::AcpServerFactoryConfig { + let server = crate::acp::server_factory::AcpServer::new( + crate::acp::server_factory::AcpServerFactoryConfig { builtins, data_dir: Paths::data_dir(), config_dir: Paths::config_dir(), - }); + }, + ); let agent = server.create_agent().await?; serve(agent, incoming, outgoing).await } @@ -3894,7 +3893,7 @@ pub async fn run(builtins: Vec) -> Result<()> { #[cfg(test)] mod tests { use super::*; - use goose::conversation::message::{ToolRequest, ToolResponse}; + use crate::conversation::message::{ToolRequest, ToolResponse}; use rmcp::model::{CallToolRequestParams, Content as RmcpContent}; use sacp::schema::{ EnvVariable, HttpHeader, McpServer, McpServerHttp, McpServerSse, McpServerStdio, @@ -4109,14 +4108,14 @@ print(\"hello, world\") description: "Mock".to_string(), default_model: "unused".to_string(), configured: true, - provider_type: goose::providers::base::ProviderType::Builtin, + provider_type: crate::providers::base::ProviderType::Builtin, config_keys: vec![], setup_steps: vec![], supports_refresh: true, refreshing: false, models: models .into_iter() - .map(|id| goose::providers::inventory::InventoryModel { + .map(|id| crate::providers::inventory::InventoryModel { name: id.clone(), id, family: None, @@ -4287,7 +4286,7 @@ print(\"hello, world\") session_type: SessionType::Acp, created_at: Default::default(), updated_at: Default::default(), - extension_data: goose::session::ExtensionData::default(), + extension_data: crate::session::ExtensionData::default(), total_tokens, input_tokens, output_tokens, diff --git a/crates/goose-acp/src/server_factory.rs b/crates/goose/src/acp/server_factory.rs similarity index 80% rename from crates/goose-acp/src/server_factory.rs rename to crates/goose/src/acp/server_factory.rs index 5c63d0175c..dbbc9d1a07 100644 --- a/crates/goose-acp/src/server_factory.rs +++ b/crates/goose/src/acp/server_factory.rs @@ -1,9 +1,8 @@ +use crate::acp::server::{AcpProviderFactory, GooseAcpAgent}; use anyhow::Result; use std::sync::Arc; use tracing::info; -use crate::server::{AcpProviderFactory, GooseAcpAgent}; - pub struct AcpServerFactoryConfig { pub builtins: Vec, pub data_dir: std::path::PathBuf, @@ -23,18 +22,18 @@ impl AcpServer { let config_path = self .config .config_dir - .join(goose::config::base::CONFIG_YAML_NAME); - let config = goose::config::Config::new(&config_path, "goose")?; + .join(crate::config::base::CONFIG_YAML_NAME); + let config = crate::config::Config::new(&config_path, "goose")?; let goose_mode = config .get_goose_mode() - .unwrap_or(goose::config::GooseMode::Auto); + .unwrap_or(crate::config::GooseMode::Auto); let disable_session_naming = config.get_goose_disable_session_naming().unwrap_or(false); let provider_factory: AcpProviderFactory = Arc::new(move |provider_name, model_config, extensions| { Box::pin(async move { - goose::providers::create(&provider_name, model_config, extensions).await + crate::providers::create(&provider_name, model_config, extensions).await }) }); diff --git a/crates/goose-acp/src/tools.rs b/crates/goose/src/acp/tools.rs similarity index 100% rename from crates/goose-acp/src/tools.rs rename to crates/goose/src/acp/tools.rs diff --git a/crates/goose-acp/src/transport/http.rs b/crates/goose/src/acp/transport/http.rs similarity index 97% rename from crates/goose-acp/src/transport/http.rs rename to crates/goose/src/acp/transport/http.rs index 608009e459..4359a21c9d 100644 --- a/crates/goose-acp/src/transport/http.rs +++ b/crates/goose/src/acp/transport/http.rs @@ -13,8 +13,8 @@ use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; use tracing::{error, info}; use super::*; -use crate::adapters::{ReceiverToAsyncRead, SenderToAsyncWrite}; -use crate::server_factory::AcpServer; +use crate::acp::adapters::{ReceiverToAsyncRead, SenderToAsyncWrite}; +use crate::acp::server_factory::AcpServer; pub(crate) struct HttpState { server: Arc, @@ -43,7 +43,8 @@ impl HttpState { let read_stream = ReceiverToAsyncRead::new(to_agent_rx); let write_stream = SenderToAsyncWrite::new(from_agent_tx); - let fut = crate::server::serve(agent, read_stream.compat(), write_stream.compat_write()); + let fut = + crate::acp::server::serve(agent, read_stream.compat(), write_stream.compat_write()); let handle = tokio::spawn(async move { if let Err(e) = fut.await { error!("ACP session error: {}", e); diff --git a/crates/goose-acp/src/transport.rs b/crates/goose/src/acp/transport/mod.rs similarity index 98% rename from crates/goose-acp/src/transport.rs rename to crates/goose/src/acp/transport/mod.rs index dc6e8d0d43..3cb1c67a4e 100644 --- a/crates/goose-acp/src/transport.rs +++ b/crates/goose/src/acp/transport/mod.rs @@ -18,7 +18,7 @@ use serde_json::Value; use tokio::sync::{mpsc, Mutex}; use tower_http::cors::{Any, CorsLayer}; -use crate::server_factory::AcpServer; +use crate::acp::server_factory::AcpServer; pub(crate) const HEADER_SESSION_ID: &str = "Acp-Session-Id"; pub(crate) const EVENT_STREAM_MIME_TYPE: &str = "text/event-stream"; diff --git a/crates/goose-acp/src/transport/websocket.rs b/crates/goose/src/acp/transport/websocket.rs similarity index 96% rename from crates/goose-acp/src/transport/websocket.rs rename to crates/goose/src/acp/transport/websocket.rs index 65a84737ad..2a548630e6 100644 --- a/crates/goose-acp/src/transport/websocket.rs +++ b/crates/goose/src/acp/transport/websocket.rs @@ -11,8 +11,8 @@ use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; use tracing::{debug, error, info, warn}; use super::{TransportSession, HEADER_SESSION_ID}; -use crate::adapters::{ReceiverToAsyncRead, SenderToAsyncWrite}; -use crate::server_factory::AcpServer; +use crate::acp::adapters::{ReceiverToAsyncRead, SenderToAsyncWrite}; +use crate::acp::server_factory::AcpServer; pub(crate) struct WsState { server: Arc, @@ -38,7 +38,8 @@ impl WsState { let read_stream = ReceiverToAsyncRead::new(to_agent_rx); let write_stream = SenderToAsyncWrite::new(from_agent_tx); - let fut = crate::server::serve(agent, read_stream.compat(), write_stream.compat_write()); + let fut = + crate::acp::server::serve(agent, read_stream.compat(), write_stream.compat_write()); let handle = tokio::spawn(async move { if let Err(e) = fut.await { error!("ACP WebSocket session error: {}", e); diff --git a/crates/goose-acp/src/bin/generate_acp_schema.rs b/crates/goose/src/bin/generate_acp_schema.rs similarity index 99% rename from crates/goose-acp/src/bin/generate_acp_schema.rs rename to crates/goose/src/bin/generate_acp_schema.rs index e50afdaa8b..070d9d74e7 100644 --- a/crates/goose-acp/src/bin/generate_acp_schema.rs +++ b/crates/goose/src/bin/generate_acp_schema.rs @@ -1,4 +1,4 @@ -use goose_acp::server::GooseAcpAgent; +use goose::acp::server::GooseAcpAgent; use schemars::SchemaGenerator; use serde_json::{json, Map, Value}; use std::collections::{BTreeSet, HashMap}; diff --git a/crates/goose/src/lib.rs b/crates/goose/src/lib.rs index def1d186d0..154e048b62 100644 --- a/crates/goose/src/lib.rs +++ b/crates/goose/src/lib.rs @@ -5,6 +5,7 @@ compile_error!("At least one of `rustls-tls` or `native-tls` features must be en compile_error!("Features `rustls-tls` and `native-tls` are mutually exclusive"); pub mod acp; +pub use goose_sdk::custom_requests; pub mod action_required_manager; pub mod agents; pub mod builtin_extension; diff --git a/crates/goose-acp/tests/common_tests/mod.rs b/crates/goose/tests/acp_common_tests/mod.rs similarity index 94% rename from crates/goose-acp/tests/common_tests/mod.rs rename to crates/goose/tests/acp_common_tests/mod.rs index 1edb3f4678..16d9ca2a14 100644 --- a/crates/goose-acp/tests/common_tests/mod.rs +++ b/crates/goose/tests/acp_common_tests/mod.rs @@ -2,16 +2,16 @@ #![recursion_limit = "256"] #![allow(unused_attributes)] -#[path = "../fixtures/mod.rs"] +#[path = "../acp_fixtures/mod.rs"] pub mod fixtures; use fixtures::{ assert_notifications, Connection, FsFixture, Notification, OpenAiFixture, PermissionDecision, Session, SessionData, TerminalCall, TerminalFixture, TestConnectionConfig, }; use fs_err as fs; +use goose::acp::server::AcpProviderFactory; use goose::config::base::CONFIG_YAML_NAME; use goose::config::GooseMode; -use goose_acp::server::AcpProviderFactory; use goose_test_support::{McpFixture, FAKE_CODE, TEST_IMAGE_B64, TEST_MODEL}; use sacp::schema::{ ListSessionsResponse, McpServer, McpServerHttp, ModelId, SessionInfo, SessionModeId, @@ -32,7 +32,7 @@ async fn new_basic_session(config: TestConnectionConfig) -> Basic let openai = OpenAiFixture::new( vec![( r#"\nwhat is 1+1""#.into(), - include_str!("../test_data/openai_basic.txt"), + include_str!("../acp_test_data/openai_basic.txt"), )], expected_session_id.clone(), ) @@ -147,11 +147,11 @@ pub async fn run_config_mcp() { vec![ ( prompt.to_string(), - include_str!("../test_data/openai_tool_call.txt"), + include_str!("../acp_test_data/openai_tool_call.txt"), ), ( format!(r#""content":"{FAKE_CODE}""#), - include_str!("../test_data/openai_tool_result.txt"), + include_str!("../acp_test_data/openai_tool_result.txt"), ), ], expected_session_id.clone(), @@ -198,11 +198,11 @@ pub async fn run_fs_read_text_file_true() { vec![ ( prompt.to_string(), - include_str!("../test_data/openai_fs_read_tool_call.txt"), + include_str!("../acp_test_data/openai_fs_read_tool_call.txt"), ), ( r#""content":"test-read-content-12345""#.into(), - include_str!("../test_data/openai_fs_read_tool_result.txt"), + include_str!("../acp_test_data/openai_fs_read_tool_result.txt"), ), ], expected_session_id.clone(), @@ -247,11 +247,11 @@ pub async fn run_fs_write_text_file_false() { vec![ ( prompt.to_string(), - include_str!("../test_data/openai_fs_write_tool_call.txt"), + include_str!("../acp_test_data/openai_fs_write_tool_call.txt"), ), ( r#"Created /tmp/test_acp_write.txt"#.into(), - include_str!("../test_data/openai_fs_write_tool_result.txt"), + include_str!("../acp_test_data/openai_fs_write_tool_result.txt"), ), ], expected_session_id.clone(), @@ -295,11 +295,11 @@ pub async fn run_fs_write_text_file_true() { vec![ ( prompt.to_string(), - include_str!("../test_data/openai_fs_write_tool_call.txt"), + include_str!("../acp_test_data/openai_fs_write_tool_call.txt"), ), ( r#"Created /tmp/test_acp_write.txt"#.into(), - include_str!("../test_data/openai_fs_write_tool_result.txt"), + include_str!("../acp_test_data/openai_fs_write_tool_result.txt"), ), ], expected_session_id.clone(), @@ -366,11 +366,11 @@ pub async fn run_load_mode() { vec![ ( prompt.to_string(), - include_str!("../test_data/openai_tool_call.txt"), + include_str!("../acp_test_data/openai_tool_call.txt"), ), ( format!(r#""content":"{FAKE_CODE}""#), - include_str!("../test_data/openai_tool_result.txt"), + include_str!("../acp_test_data/openai_tool_result.txt"), ), ], expected_session_id.clone(), @@ -426,7 +426,7 @@ pub async fn run_load_model() { let openai = OpenAiFixture::new( vec![( r#""model":"o4-mini""#.into(), - include_str!("../test_data/openai_basic.txt"), + include_str!("../acp_test_data/openai_basic.txt"), )], expected_session_id.clone(), ) @@ -460,19 +460,19 @@ pub async fn run_load_session_mcp() { vec![ ( prompt.to_string(), - include_str!("../test_data/openai_tool_call.txt"), + include_str!("../acp_test_data/openai_tool_call.txt"), ), ( format!(r#""content":"{FAKE_CODE}""#), - include_str!("../test_data/openai_tool_result.txt"), + include_str!("../acp_test_data/openai_tool_result.txt"), ), ( prompt.to_string(), - include_str!("../test_data/openai_tool_call.txt"), + include_str!("../acp_test_data/openai_tool_call.txt"), ), ( format!(r#""content":"{FAKE_CODE}""#), - include_str!("../test_data/openai_tool_result.txt"), + include_str!("../acp_test_data/openai_tool_result.txt"), ), ], expected_session_id.clone(), @@ -602,11 +602,11 @@ async fn run_mode_set_impl(via: SetModeVia) { vec![ ( prompt.to_string(), - include_str!("../test_data/openai_tool_call.txt"), + include_str!("../acp_test_data/openai_tool_call.txt"), ), ( format!(r#""content":"{FAKE_CODE}""#), - include_str!("../test_data/openai_tool_result.txt"), + include_str!("../acp_test_data/openai_tool_result.txt"), ), ], expected_session_id.clone(), @@ -779,12 +779,12 @@ async fn run_model_set_impl(via: SetModelVia) { // Session B prompt with switched model ( r#""model":"o4-mini""#.into(), - include_str!("../test_data/openai_basic.txt"), + include_str!("../acp_test_data/openai_basic.txt"), ), // Session A prompt with default model ( format!(r#""model":"{TEST_MODEL}""#), - include_str!("../test_data/openai_basic.txt"), + include_str!("../acp_test_data/openai_basic.txt"), ), ], expected_session_id.clone(), @@ -919,11 +919,11 @@ pub async fn run_permission_persistence() { vec![ ( prompt.to_string(), - include_str!("../test_data/openai_tool_call.txt"), + include_str!("../acp_test_data/openai_tool_call.txt"), ), ( format!(r#""content":"{FAKE_CODE}""#), - include_str!("../test_data/openai_tool_result.txt"), + include_str!("../acp_test_data/openai_tool_result.txt"), ), ], expected_session_id.clone(), @@ -961,7 +961,7 @@ pub async fn run_prompt_basic() { let openai = OpenAiFixture::new( vec![( r#"\nwhat is 1+1""#.into(), - include_str!("../test_data/openai_basic.txt"), + include_str!("../acp_test_data/openai_basic.txt"), )], expected_session_id.clone(), ) @@ -989,15 +989,15 @@ pub async fn run_prompt_codemode() { vec![ ( format!(r#"\n{prompt}""#), - include_str!("../test_data/openai_builtin_search.txt"), + include_str!("../acp_test_data/openai_builtin_search.txt"), ), ( r#"export async function getCode"#.into(), - include_str!("../test_data/openai_builtin_execute.txt"), + include_str!("../acp_test_data/openai_builtin_execute.txt"), ), ( r#"Created /tmp/result.txt"#.into(), - include_str!("../test_data/openai_builtin_final.txt"), + include_str!("../acp_test_data/openai_builtin_final.txt"), ), ], expected_session_id.clone(), @@ -1037,11 +1037,11 @@ pub async fn run_prompt_image() { ( r#"\nUse the get_image tool and describe what you see in its result.""# .into(), - include_str!("../test_data/openai_image_tool_call.txt"), + include_str!("../acp_test_data/openai_image_tool_call.txt"), ), ( r#""type":"image_url""#.into(), - include_str!("../test_data/openai_image_tool_result.txt"), + include_str!("../acp_test_data/openai_image_tool_result.txt"), ), ], expected_session_id.clone(), @@ -1081,7 +1081,7 @@ pub async fn run_prompt_image_attachment() { let openai = OpenAiFixture::new( vec![( r#""type":"image_url""#.into(), - include_str!("../test_data/openai_image_attachment.txt"), + include_str!("../acp_test_data/openai_image_attachment.txt"), )], expected_session_id.clone(), ) @@ -1112,11 +1112,11 @@ pub async fn run_prompt_mcp() { vec![ ( r#"\nUse the get_code tool and output only its result.""#.into(), - include_str!("../test_data/openai_tool_call.txt"), + include_str!("../acp_test_data/openai_tool_call.txt"), ), ( format!(r#""content":"{FAKE_CODE}""#), - include_str!("../test_data/openai_tool_result.txt"), + include_str!("../acp_test_data/openai_tool_result.txt"), ), ], expected_session_id.clone(), @@ -1178,7 +1178,7 @@ pub async fn run_prompt_skill() { let openai = OpenAiFixture::new( vec![( "skill-loaded-in-acp-session".to_string(), - include_str!("../test_data/openai_basic.txt"), + include_str!("../acp_test_data/openai_basic.txt"), )], expected_session_id.clone(), ) @@ -1209,11 +1209,11 @@ pub async fn run_shell_terminal_false() { vec![ ( prompt.clone(), - include_str!("../test_data/openai_shell_tool_call.txt"), + include_str!("../acp_test_data/openai_shell_tool_call.txt"), ), ( SHELL_TEST_CONTENT.into(), - include_str!("../test_data/openai_shell_tool_result.txt"), + include_str!("../acp_test_data/openai_shell_tool_result.txt"), ), ], expected_session_id.clone(), @@ -1252,11 +1252,11 @@ pub async fn run_shell_terminal_true() { vec![ ( prompt.clone(), - include_str!("../test_data/openai_shell_tool_call.txt"), + include_str!("../acp_test_data/openai_shell_tool_call.txt"), ), ( SHELL_TEST_CONTENT.into(), - include_str!("../test_data/openai_shell_tool_result.txt"), + include_str!("../acp_test_data/openai_shell_tool_result.txt"), ), ], expected_session_id.clone(), diff --git a/crates/goose-acp/tests/custom_requests_test.rs b/crates/goose/tests/acp_custom_requests_test.rs similarity index 97% rename from crates/goose-acp/tests/custom_requests_test.rs rename to crates/goose/tests/acp_custom_requests_test.rs index c4fe430f1f..b31509fc58 100644 --- a/crates/goose-acp/tests/custom_requests_test.rs +++ b/crates/goose/tests/acp_custom_requests_test.rs @@ -1,4 +1,5 @@ #[allow(dead_code)] +#[path = "acp_common_tests/mod.rs"] mod common_tests; use common_tests::fixtures::server::AcpServerConnection; @@ -6,10 +7,10 @@ use common_tests::fixtures::{ run_test, send_custom, Connection, PermissionDecision, Session, SessionData, TestConnectionConfig, }; +use goose::acp::server::AcpProviderFactory; use goose::model::ModelConfig; use goose::providers::base::{MessageStream, Provider}; use goose::providers::errors::ProviderError; -use goose_acp::server::AcpProviderFactory; use goose_test_support::{EnforceSessionId, IgnoreSessionId}; use std::sync::{Arc, Mutex}; @@ -238,11 +239,11 @@ fn test_developer_fs_requests_use_acp_session_id() { ( "Use the read tool to read /tmp/test_acp_read.txt and output only its contents." .to_string(), - include_str!("test_data/openai_fs_read_tool_call.txt"), + include_str!("acp_test_data/openai_fs_read_tool_call.txt"), ), ( r#""content":"test-read-content-12345""#.into(), - include_str!("test_data/openai_fs_read_tool_result.txt"), + include_str!("acp_test_data/openai_fs_read_tool_result.txt"), ), ], Arc::new(IgnoreSessionId), diff --git a/crates/goose-acp/tests/fixtures/mod.rs b/crates/goose/tests/acp_fixtures/mod.rs similarity index 99% rename from crates/goose-acp/tests/fixtures/mod.rs rename to crates/goose/tests/acp_fixtures/mod.rs index 4fc0c970a2..ab763edce9 100644 --- a/crates/goose-acp/tests/fixtures/mod.rs +++ b/crates/goose/tests/acp_fixtures/mod.rs @@ -3,6 +3,7 @@ use async_trait::async_trait; use fs_err as fs; +use goose::acp::server::{serve, AcpProviderFactory, GooseAcpAgent}; pub use goose::acp::{map_permission_response, PermissionDecision}; use goose::builtin_extension::register_builtin_extensions; use goose::config::paths::Paths; @@ -11,7 +12,6 @@ use goose::providers::api_client::{ApiClient, AuthMethod as ApiAuthMethod}; use goose::providers::base::Provider; use goose::providers::openai::OpenAiProvider; use goose::session_context::SESSION_ID_HEADER; -use goose_acp::server::{serve, AcpProviderFactory, GooseAcpAgent}; use goose_test_support::{ExpectedSessionId, TEST_MODEL}; use sacp::schema::{ CreateTerminalResponse, KillTerminalResponse, ListSessionsResponse, McpServer, @@ -52,7 +52,7 @@ impl OpenAiFixture { .respond_with( ResponseTemplate::new(200) .insert_header("content-type", "application/json") - .set_body_string(include_str!("../test_data/openai_models.json")), + .set_body_string(include_str!("../acp_test_data/openai_models.json")), ) .mount(&mock_server) .await; diff --git a/crates/goose-acp/tests/fixtures/provider.rs b/crates/goose/tests/acp_fixtures/provider.rs similarity index 100% rename from crates/goose-acp/tests/fixtures/provider.rs rename to crates/goose/tests/acp_fixtures/provider.rs diff --git a/crates/goose-acp/tests/fixtures/server.rs b/crates/goose/tests/acp_fixtures/server.rs similarity index 100% rename from crates/goose-acp/tests/fixtures/server.rs rename to crates/goose/tests/acp_fixtures/server.rs diff --git a/crates/goose-acp/tests/provider_test.rs b/crates/goose/tests/acp_provider_test.rs similarity index 99% rename from crates/goose-acp/tests/provider_test.rs rename to crates/goose/tests/acp_provider_test.rs index 9a1b1baf68..5e2e65db39 100644 --- a/crates/goose-acp/tests/provider_test.rs +++ b/crates/goose/tests/acp_provider_test.rs @@ -1,6 +1,7 @@ #![recursion_limit = "256"] #[allow(dead_code)] +#[path = "acp_common_tests/mod.rs"] mod common_tests; use common_tests::fixtures::provider::AcpProviderConnection; use common_tests::fixtures::run_test; diff --git a/crates/goose-acp/tests/server_test.rs b/crates/goose/tests/acp_server_test.rs similarity index 99% rename from crates/goose-acp/tests/server_test.rs rename to crates/goose/tests/acp_server_test.rs index a57aa40e6b..fd44ec32e0 100644 --- a/crates/goose-acp/tests/server_test.rs +++ b/crates/goose/tests/acp_server_test.rs @@ -1,4 +1,5 @@ #[allow(dead_code)] +#[path = "acp_common_tests/mod.rs"] mod common_tests; use common_tests::fixtures::run_test; use common_tests::fixtures::server::AcpServerConnection; diff --git a/crates/goose-acp/tests/test_data/openai_basic.txt b/crates/goose/tests/acp_test_data/openai_basic.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_basic.txt rename to crates/goose/tests/acp_test_data/openai_basic.txt diff --git a/crates/goose-acp/tests/test_data/openai_builtin_execute.txt b/crates/goose/tests/acp_test_data/openai_builtin_execute.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_builtin_execute.txt rename to crates/goose/tests/acp_test_data/openai_builtin_execute.txt diff --git a/crates/goose-acp/tests/test_data/openai_builtin_final.txt b/crates/goose/tests/acp_test_data/openai_builtin_final.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_builtin_final.txt rename to crates/goose/tests/acp_test_data/openai_builtin_final.txt diff --git a/crates/goose-acp/tests/test_data/openai_builtin_search.txt b/crates/goose/tests/acp_test_data/openai_builtin_search.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_builtin_search.txt rename to crates/goose/tests/acp_test_data/openai_builtin_search.txt diff --git a/crates/goose-acp/tests/test_data/openai_fs_read_tool_call.txt b/crates/goose/tests/acp_test_data/openai_fs_read_tool_call.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_fs_read_tool_call.txt rename to crates/goose/tests/acp_test_data/openai_fs_read_tool_call.txt diff --git a/crates/goose-acp/tests/test_data/openai_fs_read_tool_result.txt b/crates/goose/tests/acp_test_data/openai_fs_read_tool_result.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_fs_read_tool_result.txt rename to crates/goose/tests/acp_test_data/openai_fs_read_tool_result.txt diff --git a/crates/goose-acp/tests/test_data/openai_fs_write_tool_call.txt b/crates/goose/tests/acp_test_data/openai_fs_write_tool_call.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_fs_write_tool_call.txt rename to crates/goose/tests/acp_test_data/openai_fs_write_tool_call.txt diff --git a/crates/goose-acp/tests/test_data/openai_fs_write_tool_result.txt b/crates/goose/tests/acp_test_data/openai_fs_write_tool_result.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_fs_write_tool_result.txt rename to crates/goose/tests/acp_test_data/openai_fs_write_tool_result.txt diff --git a/crates/goose-acp/tests/test_data/openai_image_attachment.txt b/crates/goose/tests/acp_test_data/openai_image_attachment.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_image_attachment.txt rename to crates/goose/tests/acp_test_data/openai_image_attachment.txt diff --git a/crates/goose-acp/tests/test_data/openai_image_tool_call.txt b/crates/goose/tests/acp_test_data/openai_image_tool_call.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_image_tool_call.txt rename to crates/goose/tests/acp_test_data/openai_image_tool_call.txt diff --git a/crates/goose-acp/tests/test_data/openai_image_tool_result.txt b/crates/goose/tests/acp_test_data/openai_image_tool_result.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_image_tool_result.txt rename to crates/goose/tests/acp_test_data/openai_image_tool_result.txt diff --git a/crates/goose-acp/tests/test_data/openai_models.json b/crates/goose/tests/acp_test_data/openai_models.json similarity index 100% rename from crates/goose-acp/tests/test_data/openai_models.json rename to crates/goose/tests/acp_test_data/openai_models.json diff --git a/crates/goose-acp/tests/test_data/openai_shell_tool_call.txt b/crates/goose/tests/acp_test_data/openai_shell_tool_call.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_shell_tool_call.txt rename to crates/goose/tests/acp_test_data/openai_shell_tool_call.txt diff --git a/crates/goose-acp/tests/test_data/openai_shell_tool_result.txt b/crates/goose/tests/acp_test_data/openai_shell_tool_result.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_shell_tool_result.txt rename to crates/goose/tests/acp_test_data/openai_shell_tool_result.txt diff --git a/crates/goose-acp/tests/test_data/openai_tool_call.txt b/crates/goose/tests/acp_test_data/openai_tool_call.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_tool_call.txt rename to crates/goose/tests/acp_test_data/openai_tool_call.txt diff --git a/crates/goose-acp/tests/test_data/openai_tool_result.txt b/crates/goose/tests/acp_test_data/openai_tool_result.txt similarity index 100% rename from crates/goose-acp/tests/test_data/openai_tool_result.txt rename to crates/goose/tests/acp_test_data/openai_tool_result.txt diff --git a/documentation/docs/guides/acp-clients.md b/documentation/docs/guides/acp-clients.md index 397e9fe59d..ffa6518c87 100644 --- a/documentation/docs/guides/acp-clients.md +++ b/documentation/docs/guides/acp-clients.md @@ -177,7 +177,7 @@ npm install **Option 1: Auto-launch server (recommended)** -The TUI will automatically start the goose-acp-server if you have it installed: +The TUI will automatically start the goose acp server if you have it installed: ```bash npm start @@ -191,7 +191,7 @@ For servers that support the draft standard ACP over Streamable HTTP https://git npm start -- --server http://HOST:PORT # example server -cargo run -p goose-acp --bin goose-acp-server +cargo run -p goose-cli --bin goose -- serve ``` ### Single Prompt Mode diff --git a/ui/sdk/README.md b/ui/sdk/README.md index 53d997b475..ce845fbf6e 100644 --- a/ui/sdk/README.md +++ b/ui/sdk/README.md @@ -57,7 +57,7 @@ npm link @aaif/goose-sdk ### Schema Generation -The TypeScript types are generated from Rust schemas defined in `crates/goose-acp`. +The TypeScript types are generated from Rust schemas defined in `crates/goose`. The build process: 1. Builds the `generate-acp-schema` Rust binary diff --git a/ui/sdk/generate-schema.ts b/ui/sdk/generate-schema.ts index 81939a5a85..870e7eda80 100644 --- a/ui/sdk/generate-schema.ts +++ b/ui/sdk/generate-schema.ts @@ -16,8 +16,8 @@ import * as prettier from "prettier"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const ROOT = resolve(__dirname, "../.."); -const SCHEMA_PATH = resolve(ROOT, "crates/goose-acp/acp-schema.json"); -const META_PATH = resolve(ROOT, "crates/goose-acp/acp-meta.json"); +const SCHEMA_PATH = resolve(ROOT, "crates/goose/acp-schema.json"); +const META_PATH = resolve(ROOT, "crates/goose/acp-meta.json"); const OUTPUT_DIR = resolve(__dirname, "src/generated"); // Export the main function so it can be imported by build-schema.ts