From e1690e406616d921335182a2caa5ef40d3258d17 Mon Sep 17 00:00:00 2001 From: Tushar Date: Tue, 14 Apr 2026 18:14:27 +0530 Subject: [PATCH 1/4] feat(config): add deep_research feature flag to gate sage agent --- crates/forge_config/.forge.toml | 1 + crates/forge_config/src/config.rs | 7 +++++++ crates/forge_main/src/ui.rs | 6 +++++- crates/forge_repo/src/agent.rs | 18 +++++++++++++++--- forge.schema.json | 5 +++++ 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/crates/forge_config/.forge.toml b/crates/forge_config/.forge.toml index 45a08e9231..de3fbd1b23 100644 --- a/crates/forge_config/.forge.toml +++ b/crates/forge_config/.forge.toml @@ -28,6 +28,7 @@ tool_timeout_secs = 300 top_k = 30 top_p = 0.8 verify_todos = true +deep_research = false [retry] backoff_factor = 2 diff --git a/crates/forge_config/src/config.rs b/crates/forge_config/src/config.rs index 6b9baaa213..f989bbcb58 100644 --- a/crates/forge_config/src/config.rs +++ b/crates/forge_config/src/config.rs @@ -281,6 +281,13 @@ pub struct ForgeConfig { /// when a task ends and reminds the LLM about them. #[serde(default)] pub verify_todos: bool, + + /// Whether the deep research agent is available. + /// + /// When set to `true`, the Sage agent is added to the agent list and + /// the `:sage` app command is enabled. Defaults to `false`. + #[serde(default)] + pub deep_research: bool, } impl ForgeConfig { diff --git a/crates/forge_main/src/ui.rs b/crates/forge_main/src/ui.rs index aa68c7318e..11752a9320 100644 --- a/crates/forge_main/src/ui.rs +++ b/crates/forge_main/src/ui.rs @@ -2007,7 +2007,11 @@ impl A + Send + Sync> UI self.on_agent_change(AgentId::MUSE).await?; } AppCommand::Sage => { - self.on_agent_change(AgentId::SAGE).await?; + if !self.config.deep_research { + self.writeln("Sage agent is disabled. Set `deep_research = true` in .forge.toml to enable it.")?; + } else { + self.on_agent_change(AgentId::SAGE).await?; + } } AppCommand::Help => { let info = Info::from(self.command.as_ref()); diff --git a/crates/forge_repo/src/agent.rs b/crates/forge_repo/src/agent.rs index 2e225e8eb9..c1797ebb24 100644 --- a/crates/forge_repo/src/agent.rs +++ b/crates/forge_repo/src/agent.rs @@ -162,16 +162,17 @@ impl + DirectoryReader AgentRepository for ForgeAgentRepository { async fn get_agents(&self) -> anyhow::Result> { + let config = self.infra.get_config()?; let agent_defs = self.load_agents().await?; - let session = self - .infra - .get_config()? + let session = config .session + .clone() .ok_or(forge_domain::Error::NoDefaultSession)?; Ok(agent_defs .into_iter() + .filter(|def| filter_agent(def, &config)) .map(|def| { def.into_agent( ProviderId::from(session.provider_id.clone()), @@ -182,9 +183,11 @@ impl + DirectoryReader } async fn get_agent_infos(&self) -> anyhow::Result> { + let config = self.infra.get_config()?; let agent_defs = self.load_agents().await?; Ok(agent_defs .into_iter() + .filter(|def| filter_agent(def, &config)) .map(|def| forge_domain::AgentInfo { id: def.id, title: def.title, @@ -194,6 +197,15 @@ impl + DirectoryReader } } +/// Returns `false` for agents that are disabled by a feature flag in the +/// configuration, `true` for all others. +fn filter_agent(def: &AgentDefinition, config: &ForgeConfig) -> bool { + if def.id.as_str() == forge_domain::AgentId::SAGE.as_str() && !config.deep_research { + return false; + } + true +} + #[cfg(test)] mod tests { use pretty_assertions::assert_eq; diff --git a/forge.schema.json b/forge.schema.json index 43cc190609..725800746b 100644 --- a/forge.schema.json +++ b/forge.schema.json @@ -66,6 +66,11 @@ "null" ] }, + "deep_research": { + "description": "Whether the deep research agent is available.\n\nWhen set to `true`, the Sage agent is added to the agent list and\nthe `:sage` app command is enabled. Defaults to `false`.", + "type": "boolean", + "default": false + }, "http": { "description": "HTTP client settings including proxy, TLS, and timeout configuration.", "anyOf": [ From d7d0f384af2ad86ee288010aa72e16b398d62e00 Mon Sep 17 00:00:00 2001 From: Tushar Date: Tue, 14 Apr 2026 19:24:54 +0530 Subject: [PATCH 2/4] refactor(config): rename deep_research field to research_subagent --- crates/forge_config/.forge.toml | 2 +- crates/forge_config/src/config.rs | 2 +- crates/forge_main/src/ui.rs | 2 +- crates/forge_repo/src/agent.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/forge_config/.forge.toml b/crates/forge_config/.forge.toml index de3fbd1b23..8742b1c283 100644 --- a/crates/forge_config/.forge.toml +++ b/crates/forge_config/.forge.toml @@ -28,7 +28,7 @@ tool_timeout_secs = 300 top_k = 30 top_p = 0.8 verify_todos = true -deep_research = false +research_subagent = false [retry] backoff_factor = 2 diff --git a/crates/forge_config/src/config.rs b/crates/forge_config/src/config.rs index f989bbcb58..4bd19b099c 100644 --- a/crates/forge_config/src/config.rs +++ b/crates/forge_config/src/config.rs @@ -287,7 +287,7 @@ pub struct ForgeConfig { /// When set to `true`, the Sage agent is added to the agent list and /// the `:sage` app command is enabled. Defaults to `false`. #[serde(default)] - pub deep_research: bool, + pub research_subagent: bool, } impl ForgeConfig { diff --git a/crates/forge_main/src/ui.rs b/crates/forge_main/src/ui.rs index 11752a9320..fbca879d79 100644 --- a/crates/forge_main/src/ui.rs +++ b/crates/forge_main/src/ui.rs @@ -2007,7 +2007,7 @@ impl A + Send + Sync> UI self.on_agent_change(AgentId::MUSE).await?; } AppCommand::Sage => { - if !self.config.deep_research { + if !self.config.research_subagent { self.writeln("Sage agent is disabled. Set `deep_research = true` in .forge.toml to enable it.")?; } else { self.on_agent_change(AgentId::SAGE).await?; diff --git a/crates/forge_repo/src/agent.rs b/crates/forge_repo/src/agent.rs index c1797ebb24..cc24938206 100644 --- a/crates/forge_repo/src/agent.rs +++ b/crates/forge_repo/src/agent.rs @@ -200,7 +200,7 @@ impl + DirectoryReader /// Returns `false` for agents that are disabled by a feature flag in the /// configuration, `true` for all others. fn filter_agent(def: &AgentDefinition, config: &ForgeConfig) -> bool { - if def.id.as_str() == forge_domain::AgentId::SAGE.as_str() && !config.deep_research { + if def.id.as_str() == forge_domain::AgentId::SAGE.as_str() && !config.research_subagent { return false; } true From 44a4afd05401d54737f747e8e38d83595908502b Mon Sep 17 00:00:00 2001 From: Tushar Date: Tue, 14 Apr 2026 19:27:35 +0530 Subject: [PATCH 3/4] fix(ui): correct config key name in sage disabled message --- crates/forge_main/src/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge_main/src/ui.rs b/crates/forge_main/src/ui.rs index fbca879d79..8918c4b732 100644 --- a/crates/forge_main/src/ui.rs +++ b/crates/forge_main/src/ui.rs @@ -2008,7 +2008,7 @@ impl A + Send + Sync> UI } AppCommand::Sage => { if !self.config.research_subagent { - self.writeln("Sage agent is disabled. Set `deep_research = true` in .forge.toml to enable it.")?; + self.writeln("Sage agent is disabled. Set `research_subagent = true` in .forge.toml to enable it.")?; } else { self.on_agent_change(AgentId::SAGE).await?; } From 2eb5f1745cbc648ffa1f300910bc565850dcf836 Mon Sep 17 00:00:00 2001 From: Tushar Date: Tue, 14 Apr 2026 19:28:07 +0530 Subject: [PATCH 4/4] refactor(schema): rename deep_research field to research_subagent --- forge.schema.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/forge.schema.json b/forge.schema.json index 725800746b..00409d79b3 100644 --- a/forge.schema.json +++ b/forge.schema.json @@ -66,11 +66,6 @@ "null" ] }, - "deep_research": { - "description": "Whether the deep research agent is available.\n\nWhen set to `true`, the Sage agent is added to the agent list and\nthe `:sage` app command is enabled. Defaults to `false`.", - "type": "boolean", - "default": false - }, "http": { "description": "HTTP client settings including proxy, TLS, and timeout configuration.", "anyOf": [ @@ -246,6 +241,11 @@ } ] }, + "research_subagent": { + "description": "Whether the deep research agent is available.\n\nWhen set to `true`, the Sage agent is added to the agent list and\nthe `:sage` app command is enabled. Defaults to `false`.", + "type": "boolean", + "default": false + }, "restricted": { "description": "Whether restricted mode is active; when enabled, tool execution requires\nexplicit permission grants.", "type": "boolean",