feat(FN-4135): add always-on web search with locked provider setting

Adds always-on web search capability to the research system, locking the web search provider in settings UI and updating the ResearchView to surface web search as a persistent toggle with agent tool integration, plus corresponding docs and test coverage.

Fusion-Task-Id: FN-4135
This commit is contained in:
Fusion
2026-05-12 12:20:10 -07:00
committed by gsxdsm
parent 917ffa5b74
commit 2d250e6067
16 changed files with 92 additions and 88 deletions

View File

@@ -962,7 +962,7 @@ describe("createResearchTools", () => {
researchGlobalMaxConcurrentRuns: 2,
researchGlobalDefaultTimeout: 30_000,
researchGlobalMaxSynthesisRounds: 2,
researchGlobalWebSearchProvider: "none",
researchGlobalWebSearchProvider: "builtin",
researchSettings: { enabled: true },
};

View File

@@ -11,11 +11,6 @@ describe("ResearchProviderRegistry", () => {
expect(registry.isProviderAvailable("github")).toBe(false);
});
it("honors explicit none by disabling web-search", () => {
const registry = new ResearchProviderRegistry({ researchGlobalWebSearchProvider: "none" }, process.cwd());
expect(registry.isProviderAvailable("web-search")).toBe(false);
});
it("requires credentials for explicit external providers", () => {
const registry = new ResearchProviderRegistry({ researchGlobalWebSearchProvider: "tavily" }, process.cwd());
expect(registry.isProviderAvailable("web-search")).toBe(false);
@@ -37,7 +32,7 @@ describe("ResearchProviderRegistry", () => {
});
it("refreshes providers after settings changes", () => {
const registry = new ResearchProviderRegistry({ researchGlobalWebSearchProvider: "none" }, process.cwd());
const registry = new ResearchProviderRegistry({ researchGlobalWebSearchProvider: "tavily" }, process.cwd());
expect(registry.isProviderAvailable("web-search")).toBe(false);
registry.refreshSettings({ researchGlobalWebSearchProvider: "tavily", researchGlobalTavilyApiKey: "key" });

View File

@@ -28,7 +28,6 @@ describe("WebSearchProvider", () => {
it("validates configuration for each backend", () => {
expect(new WebSearchProvider({ backend: "builtin", projectRoot: process.cwd() }).isConfigured()).toBe(true);
expect(new WebSearchProvider({ backend: "none" }).isConfigured()).toBe(false);
expect(new WebSearchProvider({ backend: "searxng", searxngUrl: "https://sx" }).isConfigured()).toBe(true);
expect(new WebSearchProvider({ backend: "brave", braveApiKey: "k" }).isConfigured()).toBe(true);
expect(new WebSearchProvider({ backend: "google", googleApiKey: "k", googleCx: "cx" }).isConfigured()).toBe(true);

View File

@@ -35,7 +35,6 @@ export class WebSearchProvider implements ResearchProvider {
isConfigured(): boolean {
const backend = this.options.backend ?? "builtin";
if (backend === "builtin") return true;
if (backend === "none") return false;
if (backend === "searxng") return Boolean(this.options.searxngUrl);
if (backend === "brave") return Boolean(this.options.braveApiKey);
if (backend === "google") return Boolean(this.options.googleApiKey && this.options.googleCx);