Skip to content

Commit ff3ad43

Browse files
authored
working_dir usage more clear in add_extension (aaif-goose#6958)
1 parent 3959805 commit ff3ad43

File tree

3 files changed

+76
-71
lines changed

3 files changed

+76
-71
lines changed

crates/goose/src/agents/extension_manager.rs

Lines changed: 74 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -535,10 +535,6 @@ impl ExtensionManager {
535535
return Ok(());
536536
}
537537

538-
// Resolve working_dir: explicit > current_dir
539-
let effective_working_dir =
540-
working_dir.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
541-
542538
let mut temp_dir = None;
543539

544540
let client: Box<dyn McpClientTrait> = match &config {
@@ -577,63 +573,6 @@ impl ExtensionManager {
577573
)
578574
.await?
579575
}
580-
ExtensionConfig::Stdio {
581-
cmd,
582-
args,
583-
envs,
584-
env_keys,
585-
timeout,
586-
..
587-
} => {
588-
let config = Config::global();
589-
let mut all_envs =
590-
merge_environments(envs, env_keys, &sanitized_name, config).await?;
591-
592-
if let Some(sid) = session_id {
593-
all_envs.insert("AGENT_SESSION_ID".to_string(), sid.to_string());
594-
}
595-
596-
// Check for malicious packages before launching the process
597-
extension_malware_check::deny_if_malicious_cmd_args(cmd, args).await?;
598-
599-
let command = if let Some(container) = container {
600-
let container_id = container.id();
601-
tracing::info!(
602-
container = %container_id,
603-
cmd = %cmd,
604-
"Starting stdio extension inside Docker container"
605-
);
606-
Command::new("docker").configure(|command| {
607-
command.arg("exec").arg("-i");
608-
for (key, value) in &all_envs {
609-
command.arg("-e").arg(format!("{}={}", key, value));
610-
}
611-
command.arg(container_id);
612-
command.arg(cmd);
613-
command.args(args);
614-
})
615-
} else {
616-
let cmd = resolve_command(cmd);
617-
Command::new(cmd).configure(|command| {
618-
command.args(args).envs(all_envs);
619-
})
620-
};
621-
622-
let capabilities = GooseMcpClientCapabilities {
623-
mcpui: self.capabilities.mcpui,
624-
};
625-
let client = child_process_client(
626-
command,
627-
timeout,
628-
self.provider.clone(),
629-
Some(&effective_working_dir),
630-
container.map(|c| c.id().to_string()),
631-
self.client_name.clone(),
632-
capabilities,
633-
)
634-
.await?;
635-
Box::new(client)
636-
}
637576
ExtensionConfig::Builtin { name, timeout, .. } => {
638577
let timeout_duration = Duration::from_secs(timeout.unwrap_or(300));
639578
let normalized_name = name_to_key(name);
@@ -660,9 +599,14 @@ impl ExtensionManager {
660599
.arg(&normalized_name);
661600
});
662601

602+
let effective_working_dir = working_dir
603+
.clone()
604+
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
605+
663606
let capabilities = GooseMcpClientCapabilities {
664607
mcpui: self.capabilities.mcpui,
665608
};
609+
666610
let client = child_process_client(
667611
command,
668612
timeout,
@@ -675,12 +619,16 @@ impl ExtensionManager {
675619
.await?;
676620
Box::new(client)
677621
} else {
622+
// Non-containerized builtin runs in-process via duplex channels.
623+
// Working directory is passed per-request via call_tool metadata, not here.
678624
let (server_read, client_write) = tokio::io::duplex(65536);
679625
let (client_read, server_write) = tokio::io::duplex(65536);
680626
extension_fn(server_read, server_write);
627+
681628
let capabilities = GooseMcpClientCapabilities {
682629
mcpui: self.capabilities.mcpui,
683630
};
631+
684632
Box::new(
685633
McpClient::connect(
686634
(client_read, client_write),
@@ -693,6 +641,66 @@ impl ExtensionManager {
693641
)
694642
}
695643
}
644+
ExtensionConfig::Stdio {
645+
cmd,
646+
args,
647+
envs,
648+
env_keys,
649+
timeout,
650+
..
651+
} => {
652+
let config = Config::global();
653+
let mut all_envs =
654+
merge_environments(envs, env_keys, &sanitized_name, config).await?;
655+
656+
if let Some(sid) = session_id {
657+
all_envs.insert("AGENT_SESSION_ID".to_string(), sid.to_string());
658+
}
659+
660+
// Check for malicious packages before launching the process
661+
extension_malware_check::deny_if_malicious_cmd_args(cmd, args).await?;
662+
663+
let command = if let Some(container) = container {
664+
let container_id = container.id();
665+
tracing::info!(
666+
container = %container_id,
667+
cmd = %cmd,
668+
"Starting stdio extension inside Docker container"
669+
);
670+
Command::new("docker").configure(|command| {
671+
command.arg("exec").arg("-i");
672+
for (key, value) in &all_envs {
673+
command.arg("-e").arg(format!("{}={}", key, value));
674+
}
675+
command.arg(container_id);
676+
command.arg(cmd);
677+
command.args(args);
678+
})
679+
} else {
680+
let cmd = resolve_command(cmd);
681+
Command::new(cmd).configure(|command| {
682+
command.args(args).envs(all_envs);
683+
})
684+
};
685+
686+
let effective_working_dir = working_dir
687+
.clone()
688+
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
689+
let capabilities = GooseMcpClientCapabilities {
690+
mcpui: self.capabilities.mcpui,
691+
};
692+
let client = child_process_client(
693+
command,
694+
timeout,
695+
self.provider.clone(),
696+
Some(&effective_working_dir),
697+
container.map(|c| c.id().to_string()),
698+
self.client_name.clone(),
699+
capabilities,
700+
)
701+
.await?;
702+
Box::new(client)
703+
}
696704
ExtensionConfig::Platform { name, .. } => {
697705
let normalized_key = name_to_key(name);
698706
let def = PLATFORM_EXTENSIONS
@@ -724,6 +732,11 @@ impl ExtensionManager {
724732
command.arg("python").arg(file_path.to_str().unwrap());
725733
});
726734

735+
// Compute working_dir for InlinePython (runs as child process via uvx)
736+
let effective_working_dir = working_dir
737+
.clone()
738+
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
739+
727740
let capabilities = GooseMcpClientCapabilities {
728741
mcpui: self.capabilities.mcpui,
729742
};

ui/desktop/src/components/settings/providers/modal/ProviderConfiguationModal.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ export default function ProviderConfigurationModal({
107107
Object.entries(configValues)
108108
.filter(
109109
([_k, entry]) =>
110-
!!entry.value ||
111-
(entry.serverValue != null && typeof entry.serverValue === 'string')
110+
!!entry.value || (entry.serverValue != null && typeof entry.serverValue === 'string')
112111
)
113112
.map(([k, entry]) => [
114113
k,

ui/desktop/src/sandbox/proxy.test.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,7 @@ describe('checkBlocked', () => {
190190
});
191191

192192
it('does not block loopback by default, blocks when opted in', async () => {
193-
const allowed = await checkBlocked(
194-
'localhost',
195-
8080,
196-
blocked,
197-
noLD,
198-
noLDCache,
199-
defaultOptions
200-
);
193+
const allowed = await checkBlocked('localhost', 8080, blocked, noLD, noLDCache, defaultOptions);
201194
expect(allowed.blocked).toBe(false);
202195

203196
const blocked_ = await checkBlocked('localhost', 8080, blocked, noLD, noLDCache, {

0 commit comments

Comments
 (0)