fix: start configured web search providers

This commit is contained in:
joshavant
2026-05-15 19:55:32 -05:00
committed by Peter Steinberger
parent 4d2acd9481
commit 37f8507b4b
2 changed files with 197 additions and 0 deletions

View File

@@ -183,6 +183,17 @@ function createManifestRegistryFixture(): PluginManifestRegistry {
musicGenerationProviders: ["google"],
},
},
{
id: "brave",
channels: [],
origin: "global",
enabledByDefault: undefined,
providers: [],
cliBackends: [],
contracts: {
webSearchProviders: ["brave"],
},
},
{
id: "codex",
channels: [],
@@ -819,6 +830,75 @@ describe("resolveGatewayStartupPluginIds", () => {
} as OpenClawConfig,
["browser", "memory-core"],
],
[
"includes explicitly selected external web search providers at startup",
{
channels: {},
tools: {
web: {
search: {
enabled: true,
provider: "brave",
},
},
},
plugins: {
allow: ["brave"],
entries: {
brave: {
enabled: true,
},
},
},
} as OpenClawConfig,
["brave"],
],
[
"honors disabled web search when selecting startup providers",
{
channels: {},
tools: {
web: {
search: {
enabled: false,
provider: "brave",
},
},
},
plugins: {
allow: ["brave"],
entries: {
brave: {
enabled: true,
},
},
},
} as OpenClawConfig,
[],
],
[
"honors explicit plugin disablement for configured web search providers",
{
channels: {},
tools: {
web: {
search: {
enabled: true,
provider: "brave",
},
},
},
plugins: {
allow: ["brave"],
entries: {
brave: {
enabled: false,
},
},
},
} as OpenClawConfig,
[],
],
[
"keeps configured generation providers behind restrictive allowlists",
{
@@ -884,6 +964,40 @@ describe("resolveGatewayStartupPluginIds", () => {
});
});
it("includes auto-enabled external web search providers at startup", () => {
const rawConfig = {
channels: {},
tools: {
web: {
search: {
enabled: true,
provider: "brave",
},
},
},
plugins: {
allow: ["browser"],
},
} as OpenClawConfig;
const effectiveConfig = {
...rawConfig,
plugins: {
allow: ["browser", "brave"],
entries: {
brave: {
enabled: true,
},
},
},
} as OpenClawConfig;
expectStartupPluginIdsCase({
config: effectiveConfig,
activationSourceConfig: rawConfig,
expected: ["browser", "brave"],
});
});
it("does not let runtime-default plugin entries bypass the authored startup allowlist", () => {
const activationSourceConfig = {
channels: {},

View File

@@ -214,6 +214,28 @@ function manifestOwnsConfiguredSpeechProvider(params: {
});
}
function collectConfiguredWebSearchProviderIds(config: OpenClawConfig): ReadonlySet<string> {
const search = config.tools?.web?.search;
if (search?.enabled === false || typeof search?.provider !== "string") {
return new Set();
}
const providerId = normalizeOptionalLowercaseString(search.provider);
return providerId ? new Set([providerId]) : new Set();
}
function manifestOwnsConfiguredWebSearchProvider(params: {
manifest: PluginManifestRecord | undefined;
configuredWebSearchProviderIds: ReadonlySet<string>;
}): boolean {
if (params.configuredWebSearchProviderIds.size === 0) {
return false;
}
return (params.manifest?.contracts?.webSearchProviders ?? []).some((providerId) => {
const normalized = normalizeOptionalLowercaseString(providerId);
return normalized ? params.configuredWebSearchProviderIds.has(normalized) : false;
});
}
function listModelProviderRefs(value: unknown): string[] {
if (typeof value === "string") {
return [value];
@@ -433,6 +455,52 @@ function canStartConfiguredSpeechProviderPlugin(params: {
return activationState.enabled && activationState.explicitlyEnabled;
}
function canStartConfiguredWebSearchProviderPlugin(params: {
plugin: InstalledPluginIndexRecord;
manifest: PluginManifestRecord | undefined;
config: OpenClawConfig;
pluginsConfig: ReturnType<typeof normalizePluginsConfigWithRegistry>;
activationSource: {
plugins: ReturnType<typeof normalizePluginsConfigWithRegistry>;
rootConfig?: OpenClawConfig;
};
configuredWebSearchProviderIds: ReadonlySet<string>;
platform?: NodeJS.Platform;
}): boolean {
if (
!manifestOwnsConfiguredWebSearchProvider({
manifest: params.manifest,
configuredWebSearchProviderIds: params.configuredWebSearchProviderIds,
})
) {
return false;
}
if (!params.pluginsConfig.enabled || !params.activationSource.plugins.enabled) {
return false;
}
if (
params.pluginsConfig.deny.includes(params.plugin.pluginId) ||
params.activationSource.plugins.deny.includes(params.plugin.pluginId)
) {
return false;
}
if (
params.pluginsConfig.entries[params.plugin.pluginId]?.enabled === false ||
params.activationSource.plugins.entries[params.plugin.pluginId]?.enabled === false
) {
return false;
}
const activationState = resolveEffectivePluginActivationState({
id: params.plugin.pluginId,
origin: params.plugin.origin,
config: params.pluginsConfig,
rootConfig: params.config,
enabledByDefault: isPluginEnabledByDefaultForPlatform(params.plugin, params.platform),
activationSource: params.activationSource,
});
return activationState.enabled;
}
function canStartConfiguredRootPlugin(params: {
plugin: InstalledPluginIndexRecord;
manifest: PluginManifestRecord | undefined;
@@ -693,6 +761,8 @@ export function resolveGatewayStartupPluginPlanFromRegistry(params: {
const startupDreamingPluginIds = resolveGatewayStartupDreamingPluginIds(params.config);
const manifestLookup = createManifestRegistryLookup(params.manifestRegistry);
const configuredSpeechProviderIds = collectConfiguredSpeechProviderIds(activationSourceConfig);
const configuredWebSearchProviderIds =
collectConfiguredWebSearchProviderIds(activationSourceConfig);
const configuredGenerationProviderIds =
collectConfiguredGenerationProviderIds(activationSourceConfig);
const normalizePluginId = createPluginRegistryIdNormalizer(params.index, {
@@ -763,6 +833,19 @@ export function resolveGatewayStartupPluginPlanFromRegistry(params: {
) {
return true;
}
if (
canStartConfiguredWebSearchProviderPlugin({
plugin,
manifest,
config: params.config,
pluginsConfig,
activationSource,
configuredWebSearchProviderIds,
platform: params.platform,
})
) {
return true;
}
if (
canStartConfiguredGenerationProviderPlugin({
plugin,