feat: persist GooseMode per-session via session DB (#7854)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -137,6 +137,7 @@ impl AgentConfig {
|
||||
pub struct Agent {
|
||||
pub(super) provider: SharedProvider,
|
||||
pub config: AgentConfig,
|
||||
pub(super) current_goose_mode: Mutex<GooseMode>,
|
||||
|
||||
pub extension_manager: Arc<ExtensionManager>,
|
||||
pub(super) final_output_tool: Arc<Mutex<Option<FinalOutputTool>>>,
|
||||
@@ -203,14 +204,13 @@ where
|
||||
|
||||
impl Agent {
|
||||
pub fn new() -> Self {
|
||||
let config = Config::global();
|
||||
Self::with_config(AgentConfig::new(
|
||||
Arc::new(SessionManager::instance()),
|
||||
PermissionManager::instance(),
|
||||
None,
|
||||
Config::global().get_goose_mode().unwrap_or(GooseMode::Auto),
|
||||
Config::global()
|
||||
.get_goose_disable_session_naming()
|
||||
.unwrap_or(false),
|
||||
config.get_goose_mode().unwrap_or_default(),
|
||||
config.get_goose_disable_session_naming().unwrap_or(false),
|
||||
GoosePlatform::GooseCli,
|
||||
))
|
||||
}
|
||||
@@ -220,6 +220,7 @@ impl Agent {
|
||||
let provider = Arc::new(Mutex::new(None));
|
||||
|
||||
let goose_platform = config.goose_platform.clone();
|
||||
let initial_mode = config.goose_mode;
|
||||
let capabilities = match config.goose_platform {
|
||||
GoosePlatform::GooseDesktop => ExtensionManagerCapabilities { mcpui: true },
|
||||
GoosePlatform::GooseCli => ExtensionManagerCapabilities { mcpui: false },
|
||||
@@ -229,6 +230,7 @@ impl Agent {
|
||||
Self {
|
||||
provider: provider.clone(),
|
||||
config,
|
||||
current_goose_mode: Mutex::new(initial_mode),
|
||||
extension_manager: Arc::new(ExtensionManager::new(
|
||||
provider.clone(),
|
||||
session_manager,
|
||||
@@ -351,7 +353,9 @@ impl Agent {
|
||||
.prepare_tools_and_prompt(session_id, working_dir)
|
||||
.await?;
|
||||
|
||||
if self.config.goose_mode == GooseMode::SmartApprove {
|
||||
let goose_mode = *self.current_goose_mode.lock().await;
|
||||
|
||||
if goose_mode == GooseMode::SmartApprove {
|
||||
self.tool_inspection_manager.apply_tool_annotations(&tools);
|
||||
}
|
||||
|
||||
@@ -360,7 +364,7 @@ impl Agent {
|
||||
tools,
|
||||
toolshim_tools,
|
||||
system_prompt,
|
||||
goose_mode: self.config.goose_mode,
|
||||
goose_mode,
|
||||
tool_call_cut_off: Config::global()
|
||||
.get_param::<usize>("GOOSE_TOOL_CALL_CUTOFF")
|
||||
.unwrap_or(10),
|
||||
@@ -1721,6 +1725,28 @@ impl Agent {
|
||||
.context("Failed to persist provider config to session")
|
||||
}
|
||||
|
||||
pub async fn update_goose_mode(&self, mode: GooseMode, session_id: &str) -> Result<()> {
|
||||
if let Some(provider) = self.provider.lock().await.as_ref() {
|
||||
provider
|
||||
.update_mode(session_id, mode)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Provider rejected mode update: {e}"))?;
|
||||
}
|
||||
*self.current_goose_mode.lock().await = mode;
|
||||
self.config
|
||||
.session_manager
|
||||
.clone()
|
||||
.update(session_id)
|
||||
.goose_mode(mode)
|
||||
.apply()
|
||||
.await
|
||||
.context("Failed to persist goose_mode to session")
|
||||
}
|
||||
|
||||
pub async fn goose_mode(&self) -> GooseMode {
|
||||
*self.current_goose_mode.lock().await
|
||||
}
|
||||
|
||||
/// Restore the provider from session data or fall back to global config
|
||||
/// This is used when resuming a session to restore the provider state
|
||||
pub async fn restore_provider_from_session(&self, session: &Session) -> Result<()> {
|
||||
@@ -1752,7 +1778,16 @@ impl Agent {
|
||||
.await
|
||||
.map_err(|e| anyhow!("Could not create provider: {}", e))?;
|
||||
|
||||
self.update_provider(provider, &session.id).await
|
||||
self.update_provider(provider, &session.id).await?;
|
||||
// Propagate session mode to the new provider
|
||||
if let Some(provider) = self.provider.lock().await.as_ref() {
|
||||
provider
|
||||
.update_mode(&session.id, session.goose_mode)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to propagate mode to provider: {}", e))?;
|
||||
}
|
||||
*self.current_goose_mode.lock().await = session.goose_mode;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Override the system prompt with a custom template
|
||||
@@ -1864,12 +1899,14 @@ impl Agent {
|
||||
let model_name = &model_config.model_name;
|
||||
tracing::debug!("Using model: {}", model_name);
|
||||
|
||||
let goose_mode = *self.current_goose_mode.lock().await;
|
||||
let prompt_manager = self.prompt_manager.lock().await;
|
||||
let system_prompt = prompt_manager
|
||||
.builder()
|
||||
.with_extensions(extensions_info.into_iter())
|
||||
.with_frontend_instructions(self.frontend_instructions.lock().await.clone())
|
||||
.with_extension_and_tool_counts(extension_count, tool_count)
|
||||
.with_goose_mode(goose_mode)
|
||||
.build();
|
||||
|
||||
let recipe_prompt = prompt_manager.get_recipe_prompt().await;
|
||||
@@ -2220,7 +2257,10 @@ mod tests {
|
||||
);
|
||||
|
||||
let prompt_manager = agent.prompt_manager.lock().await;
|
||||
let system_prompt = prompt_manager.builder().build();
|
||||
let system_prompt = prompt_manager
|
||||
.builder()
|
||||
.with_goose_mode(GooseMode::default())
|
||||
.build();
|
||||
|
||||
let final_output_tool_ref = agent.final_output_tool.lock().await;
|
||||
let final_output_tool_system_prompt =
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::agents::subagent_task_config::{TaskConfig, DEFAULT_SUBAGENT_MAX_TURNS
|
||||
use crate::agents::tool_execution::ToolCallContext;
|
||||
use crate::agents::AgentConfig;
|
||||
use crate::config::paths::Paths;
|
||||
use crate::config::Config;
|
||||
use crate::config::{Config, GooseMode};
|
||||
use crate::providers;
|
||||
use crate::recipe::build_recipe::build_recipe_from_template;
|
||||
use crate::recipe::local_recipes::load_local_recipe_file;
|
||||
@@ -1243,11 +1243,14 @@ impl SummonClient {
|
||||
.await
|
||||
.map_err(|e| format!("Failed to build task config: {}", e))?;
|
||||
|
||||
// Subagents must use Auto until get_agent_messages forwards
|
||||
// ActionRequired messages to the parent. Until then, any mode
|
||||
// that requires approval will hang on the subagent's confirmation_rx.
|
||||
let agent_config = AgentConfig::new(
|
||||
self.context.session_manager.clone(),
|
||||
crate::config::permission::PermissionManager::instance(),
|
||||
None,
|
||||
crate::config::GooseMode::Auto,
|
||||
GooseMode::Auto,
|
||||
true, // disable session naming for subagents
|
||||
crate::agents::GoosePlatform::GooseCli,
|
||||
);
|
||||
@@ -1259,6 +1262,7 @@ impl SummonClient {
|
||||
working_dir,
|
||||
"Delegated task".to_string(),
|
||||
SessionType::SubAgent,
|
||||
GooseMode::Auto,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to create subagent session: {}", e))?;
|
||||
@@ -1704,11 +1708,14 @@ impl SummonClient {
|
||||
|
||||
let description = truncate(&Self::get_task_description(¶ms), 40);
|
||||
|
||||
// Subagents must use Auto until get_agent_messages forwards
|
||||
// ActionRequired messages to the parent. Until then, any mode
|
||||
// that requires approval will hang on the subagent's confirmation_rx.
|
||||
let agent_config = AgentConfig::new(
|
||||
self.context.session_manager.clone(),
|
||||
crate::config::permission::PermissionManager::instance(),
|
||||
None,
|
||||
crate::config::GooseMode::Auto,
|
||||
GooseMode::Auto,
|
||||
true, // disable session naming for subagents
|
||||
crate::agents::GoosePlatform::GooseCli,
|
||||
);
|
||||
@@ -1716,7 +1723,12 @@ impl SummonClient {
|
||||
let subagent_session = self
|
||||
.context
|
||||
.session_manager
|
||||
.create_session(working_dir, description.clone(), SessionType::SubAgent)
|
||||
.create_session(
|
||||
working_dir,
|
||||
description.clone(),
|
||||
SessionType::SubAgent,
|
||||
GooseMode::Auto,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to create subagent session: {}", e))?;
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ pub struct SystemPromptBuilder<'a, M> {
|
||||
subagents_enabled: bool,
|
||||
hints: Option<String>,
|
||||
code_execution_mode: bool,
|
||||
goose_mode: Option<GooseMode>,
|
||||
}
|
||||
|
||||
impl<'a> SystemPromptBuilder<'a, PromptManager> {
|
||||
@@ -106,6 +107,11 @@ impl<'a> SystemPromptBuilder<'a, PromptManager> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_goose_mode(mut self, mode: GooseMode) -> Self {
|
||||
self.goose_mode = Some(mode);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> String {
|
||||
let mut extensions_info = self.extensions_info;
|
||||
|
||||
@@ -128,8 +134,9 @@ impl<'a> SystemPromptBuilder<'a, PromptManager> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let config = Config::global();
|
||||
let goose_mode = config.get_goose_mode().unwrap_or(GooseMode::Auto);
|
||||
let goose_mode = self
|
||||
.goose_mode
|
||||
.unwrap_or_else(|| Config::global().get_goose_mode().unwrap_or_default());
|
||||
|
||||
let extension_tool_limits = self
|
||||
.extension_tool_count
|
||||
@@ -250,6 +257,7 @@ impl PromptManager {
|
||||
subagents_enabled: false,
|
||||
hints: None,
|
||||
code_execution_mode: false,
|
||||
goose_mode: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +411,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_all_platform_extensions() {
|
||||
use crate::agents::platform_extensions::{PlatformExtensionContext, PLATFORM_EXTENSIONS};
|
||||
use crate::config::GooseMode;
|
||||
use crate::session::SessionManager;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -413,6 +422,7 @@ mod tests {
|
||||
tmp_dir.path().to_path_buf(),
|
||||
"test session".to_owned(),
|
||||
crate::session::SessionType::Hidden,
|
||||
GooseMode::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -181,6 +181,8 @@ impl Agent {
|
||||
let provider = self.provider().await?;
|
||||
let model_config = provider.get_model_config();
|
||||
|
||||
let goose_mode = *self.current_goose_mode.lock().await;
|
||||
|
||||
let prompt_manager = self.prompt_manager.lock().await;
|
||||
let mut system_prompt = prompt_manager
|
||||
.builder()
|
||||
@@ -189,6 +191,7 @@ impl Agent {
|
||||
.with_extension_and_tool_counts(extension_count, tool_count)
|
||||
.with_code_execution_mode(code_execution_active)
|
||||
.with_hints(working_dir)
|
||||
.with_goose_mode(goose_mode)
|
||||
.build();
|
||||
|
||||
// Handle toolshim if enabled
|
||||
@@ -435,6 +438,7 @@ impl Agent {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::GooseMode;
|
||||
use crate::conversation::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::base::{Provider, ProviderUsage, Usage};
|
||||
@@ -483,6 +487,7 @@ mod tests {
|
||||
std::env::current_dir().unwrap(),
|
||||
"test-prepare-tools".to_string(),
|
||||
SessionType::Hidden,
|
||||
GooseMode::default(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user