fix: actually set the working dir for extensions from session (#6612)
This commit is contained in:
@@ -634,6 +634,7 @@ impl Agent {
|
|||||||
|
|
||||||
/// Load extensions from session into the agent
|
/// Load extensions from session into the agent
|
||||||
/// Skips extensions that are already loaded
|
/// Skips extensions that are already loaded
|
||||||
|
/// Uses the session's working_dir for extension initialization
|
||||||
pub async fn load_extensions_from_session(
|
pub async fn load_extensions_from_session(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
@@ -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
|
let extension_futures = enabled_configs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|config| {
|
.map(|config| {
|
||||||
let config_clone = config.clone();
|
let config_clone = config.clone();
|
||||||
let agent_ref = self.clone();
|
let agent_ref = self.clone();
|
||||||
|
let working_dir_clone = working_dir.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let name = config_clone.name().to_string();
|
let name = config_clone.name().to_string();
|
||||||
@@ -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 {
|
Ok(_) => ExtensionLoadResult {
|
||||||
name,
|
name,
|
||||||
success: true,
|
success: true,
|
||||||
@@ -698,6 +706,14 @@ impl Agent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_extension(&self, extension: ExtensionConfig) -> ExtensionResult<()> {
|
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 {
|
match &extension {
|
||||||
ExtensionConfig::Frontend {
|
ExtensionConfig::Frontend {
|
||||||
tools,
|
tools,
|
||||||
@@ -726,7 +742,7 @@ impl Agent {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.extension_manager
|
self.extension_manager
|
||||||
.add_extension(extension.clone())
|
.add_extension_with_working_dir(extension.clone(), working_dir)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -466,13 +466,6 @@ impl ExtensionManager {
|
|||||||
&self.context
|
&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 {
|
pub async fn supports_resources(&self) -> bool {
|
||||||
self.extensions
|
self.extensions
|
||||||
.lock()
|
.lock()
|
||||||
@@ -481,7 +474,14 @@ impl ExtensionManager {
|
|||||||
.any(|ext| ext.supports_resources())
|
.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 config_name = config.key().to_string();
|
||||||
let sanitized_name = normalize(&config_name);
|
let sanitized_name = normalize(&config_name);
|
||||||
|
|
||||||
@@ -489,8 +489,9 @@ impl ExtensionManager {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve working_dir: session > current_dir
|
// Resolve working_dir: explicit > current_dir
|
||||||
let effective_working_dir = self.resolve_working_dir().await;
|
let effective_working_dir =
|
||||||
|
working_dir.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
|
||||||
|
|
||||||
let mut temp_dir = None;
|
let mut temp_dir = None;
|
||||||
|
|
||||||
@@ -555,6 +556,17 @@ impl ExtensionManager {
|
|||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
ExtensionError::ConfigError(format!("Unknown builtin extension: {}", name))
|
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 (server_read, client_write) = tokio::io::duplex(65536);
|
||||||
let (client_read, server_write) = tokio::io::duplex(65536);
|
let (client_read, server_write) = tokio::io::duplex(65536);
|
||||||
(def.spawn_server)(server_read, server_write);
|
(def.spawn_server)(server_read, server_write);
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ impl ExtensionManagerClient {
|
|||||||
};
|
};
|
||||||
|
|
||||||
extension_manager
|
extension_manager
|
||||||
.add_extension(config)
|
.add_extension_with_working_dir(config, None)
|
||||||
.await
|
.await
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
vec![Content::text(format!(
|
vec![Content::text(format!(
|
||||||
|
|||||||
@@ -264,7 +264,9 @@ async fn test_replayed_session(
|
|||||||
|
|
||||||
#[allow(clippy::redundant_closure_call)]
|
#[allow(clippy::redundant_closure_call)]
|
||||||
let result = (async || -> Result<(), Box<dyn std::error::Error>> {
|
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();
|
let mut results = Vec::new();
|
||||||
for tool_call in tool_calls {
|
for tool_call in tool_calls {
|
||||||
let tool_call = CallToolRequestParam {
|
let tool_call = CallToolRequestParam {
|
||||||
|
|||||||
Reference in New Issue
Block a user