From 2c32ba91dc2d241270b8f798f68045042dcb8fc2 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Thu, 11 Jun 2026 20:05:47 +1000 Subject: [PATCH] fix: inherit login-shell PATH in spawned subagents Subagents built their AgentConfig with use_login_shell_path unset, which defaults to false for GooseCli (the platform subagents run under). As a result the subagent ShellTool never resolved the user's login-shell PATH, so commands like npm/node failed with 'command not found' even when the main agent could find them. Thread the parent context's use_login_shell_path into both subagent AgentConfig builders so subagents inherit the same PATH resolution. Closes #9648 Assisted-by: mic and goose Signed-off-by: Michael Neale --- crates/goose/src/agents/agent.rs | 36 +++++++++++++++++-- .../src/agents/platform_extensions/summon.rs | 6 ++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index 540fe9edb003..756b0f04d063 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -221,6 +221,14 @@ impl AgentConfig { self.use_login_shell_path = Some(use_login_shell_path); self } + + fn resolve_use_login_shell_path(&self) -> bool { + resolve_use_login_shell_path(self.use_login_shell_path, &self.goose_platform) + } +} + +fn resolve_use_login_shell_path(explicit: Option, platform: &GoosePlatform) -> bool { + explicit.unwrap_or(matches!(platform, GoosePlatform::GooseDesktop)) } /// The main goose Agent @@ -336,9 +344,7 @@ impl Agent { .unwrap_or_else(|| goose_platform.to_string()); let session_manager = Arc::clone(&config.session_manager); let permission_manager = Arc::clone(&config.permission_manager); - let use_login_shell_path = config - .use_login_shell_path - .unwrap_or(matches!(goose_platform, GoosePlatform::GooseDesktop)); + let use_login_shell_path = config.resolve_use_login_shell_path(); Self { provider: provider.clone(), config, @@ -3063,6 +3069,30 @@ mod tests { use std::sync::atomic::{AtomicUsize, Ordering}; use tempfile::TempDir; + #[test] + fn resolve_use_login_shell_path_defaults_by_platform() { + assert!(resolve_use_login_shell_path( + None, + &GoosePlatform::GooseDesktop + )); + assert!(!resolve_use_login_shell_path( + None, + &GoosePlatform::GooseCli + )); + } + + #[test] + fn resolve_use_login_shell_path_explicit_overrides_platform() { + assert!(resolve_use_login_shell_path( + Some(true), + &GoosePlatform::GooseCli + )); + assert!(!resolve_use_login_shell_path( + Some(false), + &GoosePlatform::GooseDesktop + )); + } + struct ActionRequiredProvider { handled: tokio::sync::Mutex>, } diff --git a/crates/goose/src/agents/platform_extensions/summon.rs b/crates/goose/src/agents/platform_extensions/summon.rs index 320d77fbb1fc..b047a423393f 100644 --- a/crates/goose/src/agents/platform_extensions/summon.rs +++ b/crates/goose/src/agents/platform_extensions/summon.rs @@ -1090,7 +1090,8 @@ impl SummonClient { GooseMode::Auto, true, // disable session naming for subagents crate::agents::GoosePlatform::GooseCli, - ); + ) + .with_use_login_shell_path(self.context.use_login_shell_path); let subagent_session = self .context @@ -1593,7 +1594,8 @@ impl SummonClient { GooseMode::Auto, true, // disable session naming for subagents crate::agents::GoosePlatform::GooseCli, - ); + ) + .with_use_login_shell_path(self.context.use_login_shell_path); let subagent_session = self .context