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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ impl Agent {

/// Load extensions from session into the agent
/// Skips extensions that are already loaded
/// Uses the session's working_dir for extension initialization
pub async fn load_extensions_from_session(
self: &Arc<Self>,
session: &Session,
Expand All @@ -651,11 +652,15 @@ impl Agent {
}
};

// Capture the session's working_dir to pass to extensions
let working_dir = session.working_dir.clone();

let extension_futures = enabled_configs
.into_iter()
.map(|config| {
let config_clone = config.clone();
let agent_ref = self.clone();
let working_dir_clone = working_dir.clone();

async move {
let name = config_clone.name().to_string();
Expand All @@ -674,7 +679,10 @@ impl Agent {
};
}

match agent_ref.add_extension(config_clone).await {
match agent_ref
.add_extension_with_working_dir(config_clone, Some(working_dir_clone))
.await
{
Ok(_) => ExtensionLoadResult {
name,
success: true,
Expand All @@ -698,6 +706,14 @@ impl Agent {
}

pub async fn add_extension(&self, extension: ExtensionConfig) -> ExtensionResult<()> {
self.add_extension_with_working_dir(extension, None).await
}

pub async fn add_extension_with_working_dir(
&self,
extension: ExtensionConfig,
working_dir: Option<std::path::PathBuf>,
) -> ExtensionResult<()> {
match &extension {
ExtensionConfig::Frontend {
tools,
Expand Down Expand Up @@ -726,7 +742,7 @@ impl Agent {
}
_ => {
self.extension_manager
.add_extension(extension.clone())
.add_extension_with_working_dir(extension.clone(), working_dir)
.await?;
}
}
Expand Down
32 changes: 22 additions & 10 deletions crates/goose/src/agents/extension_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,6 @@ impl ExtensionManager {
&self.context
}

/// Resolve the working directory for an extension.
/// Falls back to current_dir when working_dir is not available.
async fn resolve_working_dir(&self) -> PathBuf {
// Fall back to current_dir - working_dir is passed through the call chain from session
std::env::current_dir().unwrap_or_default()
}

pub async fn supports_resources(&self) -> bool {
self.extensions
.lock()
Expand All @@ -481,16 +474,24 @@ impl ExtensionManager {
.any(|ext| ext.supports_resources())
}

pub async fn add_extension(self: &Arc<Self>, config: ExtensionConfig) -> ExtensionResult<()> {
/// Add an extension with an optional working directory.
/// If working_dir is None, falls back to current_dir.
#[allow(clippy::too_many_lines)]
pub async fn add_extension_with_working_dir(
self: &Arc<Self>,
config: ExtensionConfig,
working_dir: Option<PathBuf>,
) -> ExtensionResult<()> {
let config_name = config.key().to_string();
let sanitized_name = normalize(&config_name);

if self.extensions.lock().await.contains_key(&sanitized_name) {
return Ok(());
}

// Resolve working_dir: session > current_dir
let effective_working_dir = self.resolve_working_dir().await;
// Resolve working_dir: explicit > current_dir
let effective_working_dir =
working_dir.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());

let mut temp_dir = None;

Expand Down Expand Up @@ -555,6 +556,17 @@ impl ExtensionManager {
.ok_or_else(|| {
ExtensionError::ConfigError(format!("Unknown builtin extension: {}", name))
})?;

// Set GOOSE_WORKING_DIR in the current process for builtin extensions
// since they run in-process and read from std::env::var
if effective_working_dir.exists() && effective_working_dir.is_dir() {
std::env::set_var("GOOSE_WORKING_DIR", &effective_working_dir);
tracing::info!(
"Set GOOSE_WORKING_DIR for builtin extension: {:?}",
effective_working_dir
);
}

let (server_read, client_write) = tokio::io::duplex(65536);
let (client_read, server_write) = tokio::io::duplex(65536);
(def.spawn_server)(server_read, server_write);
Expand Down
2 changes: 1 addition & 1 deletion crates/goose/src/agents/extension_manager_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ impl ExtensionManagerClient {
};

extension_manager
.add_extension(config)
.add_extension_with_working_dir(config, None)
.await
.map(|_| {
vec![Content::text(format!(
Expand Down
4 changes: 3 additions & 1 deletion crates/goose/tests/mcp_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,9 @@ async fn test_replayed_session(

#[allow(clippy::redundant_closure_call)]
let result = (async || -> Result<(), Box<dyn std::error::Error>> {
extension_manager.add_extension(extension_config).await?;
extension_manager
.add_extension_with_working_dir(extension_config, None)
.await?;
let mut results = Vec::new();
for tool_call in tool_calls {
let tool_call = CallToolRequestParam {
Expand Down
Loading