Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/bundle-desktop-windows.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: "Bundle Desktop (Windows)"

on:
workflow_dispatch:
workflow_call:
inputs:
version:
Expand Down
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ resolver = "2"

[workspace.package]
edition = "2021"
version = "1.24.0"
version = "1.25.0"
authors = ["Block <ai-oss-tools@block.xyz>"]
license = "Apache-2.0"
repository = "https://github.com/block/goose"
Expand Down
8 changes: 4 additions & 4 deletions crates/goose-mcp/src/developer/rmcp_developer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use rmcp::{
const WORKING_DIR_HEADER: &str = "agent-working-dir";
const SESSION_ID_HEADER: &str = "agent-session-id";

pub const WORKING_DIR_PLACEHOLDER: &str = "{{WORKING_DIR}}";

fn extract_working_dir_from_meta(meta: &Meta) -> Option<PathBuf> {
meta.0
.get(WORKING_DIR_HEADER)
Expand Down Expand Up @@ -242,8 +244,6 @@ pub struct DeveloperServer {
impl ServerHandler for DeveloperServer {
#[allow(clippy::too_many_lines)]
fn get_info(&self) -> ServerInfo {
// Get base instructions and working directory
let cwd = std::env::current_dir().expect("should have a current working dir");
let os = std::env::consts::OS;
let in_container = Self::is_definitely_container();

Expand All @@ -268,7 +268,7 @@ impl ServerHandler for DeveloperServer {
{container_info}
"#,
os=os,
cwd=cwd.to_string_lossy(),
cwd=WORKING_DIR_PLACEHOLDER,
container_info=if in_container { "container: true" } else { "" },
},
_ => {
Expand All @@ -295,7 +295,7 @@ impl ServerHandler for DeveloperServer {
{container_info}
"#,
os=os,
cwd=cwd.to_string_lossy(),
cwd=WORKING_DIR_PLACEHOLDER,
shell=shell_info,
container_info=if in_container { "container: true" } else { "" },
}
Expand Down
1 change: 1 addition & 0 deletions crates/goose-mcp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod tutorial;
pub use autovisualiser::AutoVisualiserRouter;
pub use computercontroller::ComputerControllerServer;
pub use developer::rmcp_developer::DeveloperServer;
pub use developer::rmcp_developer::WORKING_DIR_PLACEHOLDER;
pub use memory::MemoryServer;
pub use tutorial::TutorialServer;

Expand Down
32 changes: 28 additions & 4 deletions crates/goose-server/src/routes/recipe_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use crate::state::AppState;
use anyhow::Result;
use axum::http::StatusCode;
use goose::agents::Agent;
use goose::recipe::build_recipe::{build_recipe_from_template, RecipeError};
use goose::recipe::build_recipe::{
build_recipe_from_template, resolve_sub_recipe_path, RecipeError,
};
use goose::recipe::local_recipes::{get_recipe_library_dir, list_local_recipes};
use goose::recipe::validate_recipe::validate_recipe_template_from_content;
use goose::recipe::Recipe;
Expand Down Expand Up @@ -44,13 +46,23 @@ pub fn short_id_from_path(path: &str) -> String {
pub fn get_all_recipes_manifests() -> Result<Vec<RecipeManifest>> {
let recipes_with_path = list_local_recipes()?;
let mut recipe_manifests_with_path = Vec::new();
for (file_path, recipe) in recipes_with_path {
for (file_path, mut recipe) in recipes_with_path {
let Ok(last_modified) = fs::metadata(file_path.clone())
.map(|m| chrono::DateTime::<chrono::Utc>::from(m.modified().unwrap()).to_rfc3339())
else {
continue;
};

if let Some(recipe_dir) = file_path.parent() {
if let Some(ref mut sub_recipes) = recipe.sub_recipes {
for sr in sub_recipes.iter_mut() {
if let Ok(resolved) = resolve_sub_recipe_path(&sr.path, recipe_dir) {
sr.path = resolved;
}
}
}
}

let manifest_with_path = RecipeManifest {
id: short_id_from_path(file_path.to_string_lossy().as_ref()),
recipe,
Expand Down Expand Up @@ -126,10 +138,22 @@ pub async fn get_recipe_file_path_by_id(
pub async fn load_recipe_by_id(state: &AppState, id: &str) -> Result<Recipe, ErrorResponse> {
let path = get_recipe_file_path_by_id(state, id).await?;

Recipe::from_file_path(&path).map_err(|err| ErrorResponse {
let mut recipe = Recipe::from_file_path(&path).map_err(|err| ErrorResponse {
message: format!("Failed to load recipe: {}", err),
status: StatusCode::INTERNAL_SERVER_ERROR,
})
})?;

if let Some(recipe_dir) = path.parent() {
if let Some(ref mut sub_recipes) = recipe.sub_recipes {
for sr in sub_recipes.iter_mut() {
if let Ok(resolved) = resolve_sub_recipe_path(&sr.path, recipe_dir) {
sr.path = resolved;
}
}
}
}

Ok(recipe)
}

pub async fn build_recipe_with_parameter_values(
Expand Down
55 changes: 41 additions & 14 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ impl Agent {
}

match agent_ref
.add_extension(config_clone, &session_id_clone)
.add_extension_inner(config_clone, &session_id_clone)
.await
{
Ok(_) => ExtensionLoadResult {
Expand All @@ -668,13 +668,43 @@ impl Agent {
})
.collect::<Vec<_>>();

futures::future::join_all(extension_futures).await
let results = futures::future::join_all(extension_futures).await;

// Persist once after all extensions are loaded
if results.iter().any(|r| r.success) {
if let Err(e) = self.persist_extension_state(&session_id).await {
warn!("Failed to persist extension state after bulk load: {}", e);
}
}

results
}

pub async fn add_extension(
&self,
extension: ExtensionConfig,
session_id: &str,
) -> ExtensionResult<()> {
self.add_extension_inner(extension, session_id).await?;

// Persist extension state after successful add
self.persist_extension_state(session_id)
.await
.map_err(|e| {
error!("Failed to persist extension state: {}", e);
crate::agents::extension::ExtensionError::SetupError(format!(
"Failed to persist extension state: {}",
e
))
})?;

Ok(())
}

async fn add_extension_inner(
&self,
extension: ExtensionConfig,
session_id: &str,
) -> ExtensionResult<()> {
let session = self
.config
Expand Down Expand Up @@ -728,17 +758,6 @@ impl Agent {
}
}

// Persist extension state after successful add
self.persist_extension_state(session_id)
.await
.map_err(|e| {
error!("Failed to persist extension state: {}", e);
crate::agents::extension::ExtensionError::SetupError(format!(
"Failed to persist extension state: {}",
e
))
})?;

Ok(())
}

Expand Down Expand Up @@ -1695,7 +1714,15 @@ impl Agent {
) -> Result<Recipe> {
tracing::info!("Starting recipe creation with {} messages", messages.len());

let extensions_info = self.extension_manager.get_extensions_info().await;
let session = self
.config
.session_manager
.get_session(session_id, false)
.await?;
let extensions_info = self
.extension_manager
.get_extensions_info(&session.working_dir)
.await;
tracing::debug!("Retrieved {} extensions info", extensions_info.len());
let (extension_count, tool_count) = self
.extension_manager
Expand Down
12 changes: 6 additions & 6 deletions crates/goose/src/agents/extension_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,17 +716,17 @@ impl ExtensionManager {
}

/// Get extensions info for building the system prompt
pub async fn get_extensions_info(&self) -> Vec<ExtensionInfo> {
pub async fn get_extensions_info(&self, working_dir: &std::path::Path) -> Vec<ExtensionInfo> {
let working_dir_str = working_dir.to_string_lossy();
self.extensions
.lock()
.await
.iter()
.map(|(name, ext)| {
ExtensionInfo::new(
name,
ext.get_instructions().unwrap_or_default().as_str(),
ext.supports_resources(),
)
let instructions = ext.get_instructions().unwrap_or_default();
let instructions =
instructions.replace(goose_mcp::WORKING_DIR_PLACEHOLDER, &working_dir_str);
ExtensionInfo::new(name, &instructions, ext.supports_resources())
})
.collect()
}
Expand Down
5 changes: 4 additions & 1 deletion crates/goose/src/agents/reply_parts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ impl Agent {
tools.sort_by(|a, b| a.name.cmp(&b.name));

// Prepare system prompt
let extensions_info = self.extension_manager.get_extensions_info().await;
let extensions_info = self
.extension_manager
.get_extensions_info(working_dir)
.await;
let (extension_count, tool_count) = self
.extension_manager
.get_extension_and_tool_counts(session_id)
Expand Down
Loading
Loading