feat(FN-3313): add plugin slot registry, planning mode rewind, and docker n
This merge lands five features spanning the Fusion stack: research settings key renaming (FN-3313, Steps 2–7) across types, defaults, CLI, and engine packages; static plugin slot-host rendering contract documentation (FN-3260); Docker node provisioning routes with a planning modal (FN-3116); plannin Fusion-Task-Id: FN-3313
This commit is contained in:
@@ -824,7 +824,7 @@ describe("createResearchTools", () => {
|
||||
researchGlobalMaxConcurrentRuns: 2,
|
||||
researchGlobalDefaultTimeout: 30_000,
|
||||
researchGlobalMaxSynthesisRounds: 2,
|
||||
researchWebSearchProvider: "none",
|
||||
researchGlobalWebSearchProvider: "none",
|
||||
researchSettings: { enabled: true },
|
||||
};
|
||||
|
||||
@@ -897,7 +897,7 @@ describe("createResearchTools", () => {
|
||||
const tools = createResearchTools({
|
||||
store: store as any,
|
||||
rootDir: process.cwd(),
|
||||
getSettings: async () => ({ ...baseSettings, researchTavilyApiKey: "key", researchWebSearchProvider: "tavily" } as any),
|
||||
getSettings: async () => ({ ...baseSettings, researchGlobalTavilyApiKey: "key", researchGlobalWebSearchProvider: "tavily" } as any),
|
||||
});
|
||||
|
||||
const runTool = tools.find((tool) => tool.name === "fn_research_run")!;
|
||||
|
||||
@@ -11,19 +11,19 @@ describe("ResearchProviderRegistry", () => {
|
||||
});
|
||||
|
||||
it("detects search backend from credentials", () => {
|
||||
const tavily = new ResearchProviderRegistry({ researchTavilyApiKey: "key" }, process.cwd());
|
||||
const tavily = new ResearchProviderRegistry({ researchGlobalTavilyApiKey: "key" }, process.cwd());
|
||||
expect(tavily.isProviderAvailable("web-search")).toBe(true);
|
||||
|
||||
const searx = new ResearchProviderRegistry({ researchSearxngUrl: "https://sx.local" }, process.cwd());
|
||||
const searx = new ResearchProviderRegistry({ researchGlobalSearxngUrl: "https://sx.local" }, process.cwd());
|
||||
expect(searx.isProviderAvailable("web-search")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns only configured providers in available list", () => {
|
||||
const registry = new ResearchProviderRegistry(
|
||||
{
|
||||
researchWebSearchProvider: "brave",
|
||||
researchBraveApiKey: "token",
|
||||
researchGitHubEnabled: true,
|
||||
researchGlobalWebSearchProvider: "brave",
|
||||
researchGlobalBraveApiKey: "token",
|
||||
researchGlobalGitHubEnabled: true,
|
||||
},
|
||||
process.cwd(),
|
||||
);
|
||||
@@ -34,15 +34,15 @@ describe("ResearchProviderRegistry", () => {
|
||||
});
|
||||
|
||||
it("refreshes providers after settings changes", () => {
|
||||
const registry = new ResearchProviderRegistry({ researchWebSearchProvider: "none" }, process.cwd());
|
||||
const registry = new ResearchProviderRegistry({ researchGlobalWebSearchProvider: "none" }, process.cwd());
|
||||
expect(registry.isProviderAvailable("web-search")).toBe(false);
|
||||
|
||||
registry.refreshSettings({ researchWebSearchProvider: "tavily", researchTavilyApiKey: "key" });
|
||||
registry.refreshSettings({ researchGlobalWebSearchProvider: "tavily", researchGlobalTavilyApiKey: "key" });
|
||||
expect(registry.isProviderAvailable("web-search")).toBe(true);
|
||||
});
|
||||
|
||||
it("gracefully degrades disabled providers", () => {
|
||||
const registry = new ResearchProviderRegistry({ researchGitHubEnabled: false, researchLocalDocsEnabled: false }, process.cwd());
|
||||
const registry = new ResearchProviderRegistry({ researchGlobalGitHubEnabled: false, researchGlobalLocalDocsEnabled: false }, process.cwd());
|
||||
expect(registry.isProviderAvailable("github")).toBe(false);
|
||||
expect(registry.isProviderAvailable("local-docs")).toBe(false);
|
||||
});
|
||||
|
||||
@@ -44,30 +44,30 @@ export class ResearchProviderRegistry {
|
||||
|
||||
private instantiateProviders(): void {
|
||||
const backend = this.resolveSearchBackend();
|
||||
const maxResults = Number(this.settings.researchMaxSearchResults ?? 10);
|
||||
const fetchTimeoutMs = Number(this.settings.researchFetchTimeoutMs ?? 30_000);
|
||||
const userAgent = this.settings.researchUserAgent ?? "FusionResearchBot/1.0";
|
||||
const maxResults = Number(this.settings.researchGlobalMaxSearchResults ?? 10);
|
||||
const fetchTimeoutMs = Number(this.settings.researchGlobalFetchTimeoutMs ?? 30_000);
|
||||
const userAgent = this.settings.researchGlobalUserAgent ?? "FusionResearchBot/1.0";
|
||||
|
||||
this.providers = new Map<ResearchProviderType, ResearchProvider>([
|
||||
[
|
||||
"web-search",
|
||||
new WebSearchProvider({
|
||||
backend,
|
||||
searxngUrl: this.settings.researchSearxngUrl,
|
||||
braveApiKey: this.settings.researchBraveApiKey,
|
||||
googleApiKey: this.settings.researchGoogleSearchApiKey,
|
||||
googleCx: this.settings.researchGoogleSearchCx,
|
||||
tavilyApiKey: this.settings.researchTavilyApiKey,
|
||||
searxngUrl: this.settings.researchGlobalSearxngUrl,
|
||||
braveApiKey: this.settings.researchGlobalBraveApiKey,
|
||||
googleApiKey: this.settings.researchGlobalGoogleSearchApiKey,
|
||||
googleCx: this.settings.researchGlobalGoogleSearchCx,
|
||||
tavilyApiKey: this.settings.researchGlobalTavilyApiKey,
|
||||
maxResults,
|
||||
timeoutMs: fetchTimeoutMs,
|
||||
userAgent,
|
||||
}),
|
||||
],
|
||||
["page-fetch", new PageFetchProvider({ timeoutMs: fetchTimeoutMs, userAgent })],
|
||||
["github", this.settings.researchGitHubEnabled ? new GitHubProvider() : new DisabledProvider("github")],
|
||||
["github", this.settings.researchGlobalGitHubEnabled ? new GitHubProvider() : new DisabledProvider("github")],
|
||||
[
|
||||
"local-docs",
|
||||
this.settings.researchLocalDocsEnabled === false
|
||||
this.settings.researchGlobalLocalDocsEnabled === false
|
||||
? new DisabledProvider("local-docs")
|
||||
: new LocalDocsProvider({ projectRoot: this.projectRoot, timeoutMs: fetchTimeoutMs, maxResults }),
|
||||
],
|
||||
@@ -78,13 +78,13 @@ export class ResearchProviderRegistry {
|
||||
}
|
||||
|
||||
private resolveSearchBackend(): WebSearchBackend {
|
||||
const explicit = this.settings.researchWebSearchProvider;
|
||||
const explicit = this.settings.researchGlobalWebSearchProvider;
|
||||
if (explicit && explicit !== "none") return explicit;
|
||||
|
||||
if (this.settings.researchSearxngUrl) return "searxng";
|
||||
if (this.settings.researchTavilyApiKey) return "tavily";
|
||||
if (this.settings.researchBraveApiKey) return "brave";
|
||||
if (this.settings.researchGoogleSearchApiKey && this.settings.researchGoogleSearchCx) return "google";
|
||||
if (this.settings.researchGlobalSearxngUrl) return "searxng";
|
||||
if (this.settings.researchGlobalTavilyApiKey) return "tavily";
|
||||
if (this.settings.researchGlobalBraveApiKey) return "brave";
|
||||
if (this.settings.researchGlobalGoogleSearchApiKey && this.settings.researchGlobalGoogleSearchCx) return "google";
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user