fix(plugins): default bundled discovery to allowlist

This commit is contained in:
Peter Steinberger
2026-05-04 22:46:39 +01:00
parent 55df2d4598
commit b2096d19ec
19 changed files with 185 additions and 77 deletions

View File

@@ -121,7 +121,7 @@ Docs: https://docs.openclaw.ai
- Plugins/packages: reject blank `openclaw.runtimeExtensions` entries instead of silently ignoring them and falling back to inferred TypeScript runtime entries. Thanks @vincentkoc.
- Doctor/plugins: remove stale managed npm plugin shadow entries from the managed package lock as well as `package.json` and `node_modules`, so future npm operations do not keep referencing repaired bundled-plugin shadows. Thanks @vincentkoc.
- Plugins/runtime state: keep the key being registered when namespace eviction runs in the same millisecond as existing entries, so `register` and `registerIfAbsent` do not report success while evicting their own fresh value. Thanks @vincentkoc.
- Plugins/providers: add `plugins.bundledDiscovery: "allowlist"` so restrictive `plugins.allow` deployments can keep bundled provider and web-search provider discovery from auto-loading omitted bundled plugins. Thanks @dougbtv.
- Plugins/providers: make bundled provider discovery honor restrictive `plugins.allow` by default for new configs, while doctor migrates legacy restrictive allowlist configs to `plugins.bundledDiscovery: "compat"` to preserve upgrade behavior. Thanks @dougbtv.
- Control UI/Talk: make failed Talk startup errors dismissable and clear the stale Talk error state when dismissed, so missing realtime voice provider configuration does not leave a permanent chat banner. Fixes #77071. Thanks @ijoshdavis.
- Control UI/Talk: stop and clear failed realtime Talk sessions when dismissing runtime error banners, so the next Talk click starts a fresh session instead of only stopping the stale one. Thanks @vincentkoc.
- Control UI/Talk: retry from a failed realtime Talk session on the next Talk click instead of requiring a separate stale-session stop click first. Thanks @vincentkoc.

View File

@@ -1,4 +1,4 @@
0fc1b2f75e34ab92274067cdd6bfcfeac01c5b898a3aa355cfa1c3a5ec18bd5d config-baseline.json
0a6a2493d90ffe6204be807a8bc12dcf34f854602cc1c5e3c4917fed902d310e config-baseline.core.json
02987f4cecb64a98170b61c925fd7b16a22b276abfb261f9281b42f613ded923 config-baseline.json
de5a6f65ef09dc23453a2e12512e41c133c941519e0ebef7f2946e4a24265d17 config-baseline.core.json
cd7c0c7fb1435bc7e59099e9ac334462d5ad444016e9ab4512aae63a238f78dc config-baseline.channel.json
9832b30a696930a3da7efccf38073137571e1b66cae84e54d747b733fdafcc54 config-baseline.plugin.json

View File

@@ -166,7 +166,7 @@ See [MCP](/cli/mcp#openclaw-as-an-mcp-client-registry) and
plugins: {
enabled: true,
allow: ["voice-call"],
bundledDiscovery: "compat",
bundledDiscovery: "allowlist",
deny: [],
load: {
paths: ["~/Projects/oss/voice-call-plugin"],
@@ -188,9 +188,10 @@ See [MCP](/cli/mcp#openclaw-as-an-mcp-client-registry) and
- Discovery accepts native OpenClaw plugins plus compatible Codex bundles and Claude bundles, including manifestless Claude default-layout bundles.
- **Config changes require a gateway restart.**
- `allow`: optional allowlist (only listed plugins load). `deny` wins.
- `bundledDiscovery`: defaults to `"compat"` for legacy bundled provider activation.
Use `"allowlist"` when a non-empty `plugins.allow` should also gate
bundled provider plugins, including web-search runtime providers.
- `bundledDiscovery`: defaults to `"allowlist"` for new configs, so a non-empty
`plugins.allow` also gates bundled provider plugins, including web-search
runtime providers. Doctor writes `"compat"` for migrated legacy allowlist
configs to preserve existing bundled provider behavior until you opt in.
- `plugins.entries.<id>.apiKey`: plugin-level API key convenience field (when supported by the plugin).
- `plugins.entries.<id>.env`: plugin-scoped env var map.
- `plugins.entries.<id>.hooks.allowPromptInjection`: when `false`, core blocks `before_prompt_build` and ignores prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride`. Applies to native plugin hooks and supported bundle-provided hook directories.

View File

@@ -169,9 +169,9 @@ That stages grounded durable candidates into the short-term dreaming store while
Doctor also warns when `plugins.allow` is non-empty and tool policy uses
wildcard or plugin-owned tool entries. `tools.allow: ["*"]` only matches tools
from plugins that actually load; it does not bypass the exclusive plugin
allowlist. If bundled provider discovery is still in legacy compatibility
mode, doctor also points to the stricter `plugins.bundledDiscovery:
"allowlist"` setting.
allowlist. Doctor writes `plugins.bundledDiscovery: "compat"` for migrated
legacy allowlist configs to preserve existing bundled provider behavior, and
then points to the stricter `"allowlist"` setting.
</Accordion>
<Accordion title="2. Legacy config key migrations">

View File

@@ -264,7 +264,7 @@ Looking for third-party plugins? See [Community Plugins](/plugins/community).
| ------------------ | --------------------------------------------------------- |
| `enabled` | Master toggle (default: `true`) |
| `allow` | Plugin allowlist (optional) |
| `bundledDiscovery` | Bundled plugin discovery mode (`compat` by default) |
| `bundledDiscovery` | Bundled plugin discovery mode (`allowlist` by default) |
| `deny` | Plugin denylist (optional; deny wins) |
| `load.paths` | Extra plugin files/directories |
| `slots` | Exclusive slot selectors (e.g. `memory`, `contextEngine`) |
@@ -276,11 +276,12 @@ tool name. If a tool allowlist references plugin tools, add the owning plugin id
to `plugins.allow` or remove `plugins.allow`; `openclaw doctor` warns about this
shape.
`plugins.bundledDiscovery` defaults to `"compat"` so older configs keep legacy
bundled provider behavior. Set it to `"allowlist"` when a restrictive
`plugins.allow` inventory should also block omitted bundled provider plugins,
including runtime web-search provider discovery. An empty `plugins.allow` is
still treated as unset/open.
`plugins.bundledDiscovery` defaults to `"allowlist"` for new configs, so a
restrictive `plugins.allow` inventory also blocks omitted bundled provider
plugins, including runtime web-search provider discovery. Doctor stamps older
restrictive allowlist configs with `"compat"` during migration so upgrades keep
legacy bundled provider behavior until the operator opts into the stricter mode.
An empty `plugins.allow` is still treated as unset/open.
Config changes made through `/plugins enable` or `/plugins disable` trigger an
in-process Gateway plugin reload. New agent turns rebuild their tool list from

View File

@@ -61,7 +61,31 @@ const providerManifestRegistry: PluginManifestRegistry = {
};
describe("implicit provider plugin allowlist compatibility", () => {
it("keeps bundled implicit providers discoverable when plugins.allow is set", () => {
it("keeps bundled implicit providers discoverable in explicit compat mode", () => {
const config = withBundledPluginEnablementCompat({
config: withBundledPluginAllowlistCompat({
config: {
plugins: {
allow: ["openrouter"],
bundledDiscovery: "compat",
},
},
pluginIds: ["kilocode", "moonshot"],
}),
pluginIds: ["kilocode", "moonshot"],
});
expect(
resolveEnabledProviderPluginIds({
config,
registry: providerRegistry,
manifestRegistry: providerManifestRegistry,
onlyPluginIds: PROVIDER_PLUGIN_IDS,
}),
).toEqual(["kilocode", "moonshot", "openrouter"]);
});
it("respects allowlist for bundled plugins by default", () => {
const config = withBundledPluginEnablementCompat({
config: withBundledPluginAllowlistCompat({
config: {
@@ -81,7 +105,7 @@ describe("implicit provider plugin allowlist compatibility", () => {
manifestRegistry: providerManifestRegistry,
onlyPluginIds: PROVIDER_PLUGIN_IDS,
}),
).toEqual(["kilocode", "moonshot", "openrouter"]);
).toEqual(["openrouter"]);
});
it("respects allowlist for bundled plugins when bundledDiscovery is allowlist", () => {
@@ -114,6 +138,7 @@ describe("implicit provider plugin allowlist compatibility", () => {
config: {
plugins: {
allow: ["openrouter"],
bundledDiscovery: "compat",
deny: ["kilocode"],
},
},

View File

@@ -267,6 +267,33 @@ describe("legacy migrate mention routing", () => {
});
});
describe("legacy bundled provider discovery migrate", () => {
it("sets compat mode for existing restrictive plugin allowlists", () => {
const res = migrateLegacyConfigForTest({
plugins: {
allow: ["telegram"],
},
});
expect(res.config?.plugins?.bundledDiscovery).toBe("compat");
expect(res.changes).toContain(
'Set plugins.bundledDiscovery="compat" to preserve legacy bundled provider discovery for this restrictive plugins.allow config.',
);
});
it("does not override explicit bundled discovery mode", () => {
const res = migrateLegacyConfigForTest({
plugins: {
allow: ["telegram"],
bundledDiscovery: "allowlist",
},
});
expect(res.config).toBeNull();
expect(res.changes).toEqual([]);
});
});
describe("legacy migrate sandbox scope aliases", () => {
it("removes legacy agents.defaults.llm timeout config", () => {
const res = migrateLegacyConfigForTest({

View File

@@ -3,6 +3,7 @@ import {
type LegacyConfigMigrationSpec,
type LegacyConfigRule,
} from "../../../config/legacy.shared.js";
import { isRecord } from "./legacy-config-record-shared.js";
import { migrateLegacyXSearchConfig } from "./legacy-x-search-migrate.js";
const X_SEARCH_RULE: LegacyConfigRule = {
@@ -11,7 +12,40 @@ const X_SEARCH_RULE: LegacyConfigRule = {
'tools.web.x_search.apiKey moved to the xAI plugin; use plugins.entries.xai.config.webSearch.apiKey instead. Run "openclaw doctor --fix".',
};
const BUNDLED_DISCOVERY_COMPAT_RULE: LegacyConfigRule = {
path: ["plugins", "allow"],
message:
'plugins.allow now gates bundled provider discovery by default; run "openclaw doctor --fix" to preserve legacy bundled provider compatibility as plugins.bundledDiscovery="compat", or set plugins.bundledDiscovery="allowlist" to keep the stricter behavior.',
requireSourceLiteral: true,
match: (value, root) => {
if (!Array.isArray(value) || value.length === 0) {
return false;
}
const plugins = isRecord(root.plugins) ? root.plugins : undefined;
return plugins?.bundledDiscovery === undefined;
},
};
export const LEGACY_CONFIG_MIGRATIONS_RUNTIME_PROVIDERS: LegacyConfigMigrationSpec[] = [
defineLegacyConfigMigration({
id: "plugins.allow->plugins.bundledDiscovery.compat",
describe: "Preserve legacy bundled provider discovery for existing restrictive allowlists",
legacyRules: [BUNDLED_DISCOVERY_COMPAT_RULE],
apply: (raw, changes) => {
const plugins = isRecord(raw.plugins) ? raw.plugins : undefined;
if (!plugins || plugins.bundledDiscovery !== undefined) {
return;
}
const allow = plugins.allow;
if (!Array.isArray(allow) || allow.length === 0) {
return;
}
plugins.bundledDiscovery = "compat";
changes.push(
'Set plugins.bundledDiscovery="compat" to preserve legacy bundled provider discovery for this restrictive plugins.allow config.',
);
},
}),
defineLegacyConfigMigration({
id: "tools.web.x_search.apiKey->plugins.entries.xai.config.webSearch.apiKey",
describe: "Move legacy x_search auth into the xAI plugin webSearch config",

View File

@@ -113,10 +113,13 @@ describe("collectPluginToolAllowlistWarnings", () => {
expect(warnings).toEqual([]);
});
it("warns when restrictive plugins.allow leaves bundled provider discovery in compat mode", () => {
it("warns when restrictive plugins.allow leaves bundled provider discovery in explicit compat mode", () => {
const warnings = collectBundledProviderAllowlistPolicyWarnings({
cfg: {
plugins: { allow: ["telegram"] },
plugins: {
allow: ["telegram"],
bundledDiscovery: "compat",
},
},
});
@@ -125,16 +128,18 @@ describe("collectPluginToolAllowlistWarnings", () => {
]);
});
it("does not warn when bundled provider discovery follows the allowlist", () => {
const warnings = collectBundledProviderAllowlistPolicyWarnings({
cfg: {
plugins: {
allow: ["telegram"],
bundledDiscovery: "allowlist",
},
},
});
it.each([
{ name: "default", plugins: { allow: ["telegram"] } },
{
name: "explicit allowlist",
plugins: { allow: ["telegram"], bundledDiscovery: "allowlist" },
},
])(
"does not warn when bundled provider discovery follows the allowlist ($name)",
({ plugins }) => {
const warnings = collectBundledProviderAllowlistPolicyWarnings({ cfg: { plugins } });
expect(warnings).toEqual([]);
});
expect(warnings).toEqual([]);
},
);
});

View File

@@ -204,7 +204,7 @@ export function collectBundledProviderAllowlistPolicyWarnings(params: {
if (!Array.isArray(allow) || allow.length === 0) {
return [];
}
if (params.cfg.plugins?.bundledDiscovery === "allowlist") {
if (params.cfg.plugins?.bundledDiscovery !== "compat") {
return [];
}
return [

View File

@@ -24191,7 +24191,7 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
enum: ["compat", "allowlist"],
title: "Bundled Plugin Discovery",
description:
'Controls bundled plugin runtime discovery when plugins.allow is configured. "compat" (default) preserves legacy behavior where bundled provider plugins can be force-loaded on every chat turn. "allowlist" gates bundled provider plugins by plugins.allow like third-party plugins.',
'Controls bundled plugin runtime discovery when plugins.allow is configured. "allowlist" (default) gates bundled provider plugins by plugins.allow like third-party plugins. "compat" preserves legacy behavior where bundled provider plugins can be force-loaded on every chat turn.',
},
},
additionalProperties: false,
@@ -28874,7 +28874,7 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
},
"plugins.bundledDiscovery": {
label: "Bundled Plugin Discovery",
help: 'Controls bundled plugin runtime discovery when plugins.allow is configured. "compat" (default) preserves legacy behavior where bundled provider plugins can be force-loaded on every chat turn. "allowlist" gates bundled provider plugins by plugins.allow like third-party plugins.',
help: 'Controls bundled plugin runtime discovery when plugins.allow is configured. "allowlist" (default) gates bundled provider plugins by plugins.allow like third-party plugins. "compat" preserves legacy behavior where bundled provider plugins can be force-loaded on every chat turn.',
tags: ["advanced"],
},
"plugins.deny": {

View File

@@ -1213,7 +1213,7 @@ export const FIELD_HELP: Record<string, string> = {
"plugins.slots.contextEngine":
"Selects the active context engine plugin by id so one plugin provides context orchestration behavior.",
"plugins.bundledDiscovery":
'Controls bundled plugin runtime discovery when plugins.allow is configured. "compat" (default) preserves legacy behavior where bundled provider plugins can be force-loaded on every chat turn. "allowlist" gates bundled provider plugins by plugins.allow like third-party plugins.',
'Controls bundled plugin runtime discovery when plugins.allow is configured. "allowlist" (default) gates bundled provider plugins by plugins.allow like third-party plugins. "compat" preserves legacy behavior where bundled provider plugins can be force-loaded on every chat turn.',
"plugins.entries":
"Per-plugin settings keyed by plugin ID including enablement and plugin-specific runtime configuration payloads. Use this for scoped plugin tuning without changing global loader policy.",
"plugins.entries.*.enabled":

View File

@@ -55,10 +55,10 @@ export type PluginsConfig = {
* Controls how bundled plugins participate in runtime provider discovery when
* `allow` is configured.
*
* - `"compat"` (default): bundled provider plugins are force-loaded on
* every chat turn regardless of the allowlist (legacy behavior).
* - `"allowlist"`: bundled provider plugins are gated by `allow` and
* `entries.<id>.enabled` like third-party plugins.
* - `"allowlist"` (default): bundled provider plugins are gated by `allow`
* and `entries.<id>.enabled` like third-party plugins.
* - `"compat"`: legacy mode for migrated configs; bundled provider plugins
* can be force-loaded regardless of the allowlist.
*/
bundledDiscovery?: "compat" | "allowlist";
load?: PluginsLoadConfig;

View File

@@ -79,9 +79,9 @@ export function withActivatedPluginIds(params: {
return params.config;
}
const originalAllow = params.config?.plugins?.allow ?? [];
// Empty allowlists are still open; allowlist mode only stops compat from widening configured allowlists.
// Empty allowlists are still open; only explicit compat widens configured allowlists.
const useAllowlistDiscovery =
params.config?.plugins?.bundledDiscovery === "allowlist" && originalAllow.length > 0;
params.config?.plugins?.bundledDiscovery !== "compat" && originalAllow.length > 0;
const originalAllowSet = useAllowlistDiscovery ? new Set(originalAllow) : undefined;
const allow = new Set(originalAllow);
const entries = {

View File

@@ -6,7 +6,7 @@ export function withBundledPluginAllowlistCompat(params: {
config: OpenClawConfig | undefined;
pluginIds: readonly string[];
}): OpenClawConfig | undefined {
if (params.config?.plugins?.bundledDiscovery === "allowlist") {
if (params.config?.plugins?.bundledDiscovery !== "compat") {
return params.config;
}
const allow = params.config?.plugins?.allow;
@@ -42,8 +42,10 @@ export function withBundledPluginEnablementCompat(params: {
}): OpenClawConfig | undefined {
const existingEntries = params.config?.plugins?.entries ?? {};
const forcePluginsEnabled = params.config?.plugins?.enabled === false;
const useAllowlistDiscovery = params.config?.plugins?.bundledDiscovery === "allowlist";
const allowSet = useAllowlistDiscovery ? new Set(params.config?.plugins?.allow ?? []) : undefined;
const useCompatDiscovery = params.config?.plugins?.bundledDiscovery === "compat";
const allow = params.config?.plugins?.allow;
const allowSet =
!useCompatDiscovery && Array.isArray(allow) && allow.length > 0 ? new Set(allow) : undefined;
let changed = false;
const nextEntries: Record<string, PluginEntryConfig> = { ...existingEntries };

View File

@@ -295,6 +295,7 @@ function createBundledProviderCompatOptions(params?: { onlyPluginIds?: readonly
config: {
plugins: {
allow: ["openrouter"],
bundledDiscovery: "compat" as const,
},
},
bundledProviderAllowlistCompat: true,
@@ -593,7 +594,7 @@ describe("resolvePluginProviders", () => {
).toEqual(["legacy-auth-owner"]);
});
it("filters bundled provider plugins by allowlist when bundledDiscovery is allowlist", () => {
it("filters bundled provider plugins by allowlist by default", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "kilocode",
@@ -619,7 +620,6 @@ describe("resolvePluginProviders", () => {
config: {
plugins: {
allow: ["openrouter"],
bundledDiscovery: "allowlist",
},
},
env: {} as NodeJS.ProcessEnv,
@@ -628,7 +628,7 @@ describe("resolvePluginProviders", () => {
expect(discovered).toEqual(["openrouter"]);
});
it("returns all bundled provider plugins in compat mode (default)", () => {
it("returns all bundled provider plugins in explicit compat mode", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "kilocode",
@@ -654,6 +654,7 @@ describe("resolvePluginProviders", () => {
config: {
plugins: {
allow: ["openrouter"],
bundledDiscovery: "compat",
},
},
env: {} as NodeJS.ProcessEnv,
@@ -829,6 +830,7 @@ describe("resolvePluginProviders", () => {
config: {
plugins: {
allow: ["openrouter"],
bundledDiscovery: "compat",
},
},
bundledProviderAllowlistCompat: true,
@@ -842,11 +844,38 @@ describe("resolvePluginProviders", () => {
});
});
it("loads all discovered provider plugins in setup mode", () => {
it("scopes setup provider plugin discovery to the allowlist by default", () => {
resolvePluginProviders({
config: {
plugins: {
allow: ["google"],
},
},
mode: "setup",
includeUntrustedWorkspacePlugins: false,
});
expectLastSetupRegistryLoad({
onlyPluginIds: ["google"],
});
expect(getLastSetupLoadedPluginConfig()).toEqual(
expect.objectContaining({
plugins: expect.objectContaining({
allow: ["google"],
entries: expect.objectContaining({
google: { enabled: true },
}),
}),
}),
);
});
it("loads all discovered provider plugins in setup mode for explicit compat configs", () => {
resolvePluginProviders({
config: {
plugins: {
allow: ["openrouter"],
bundledDiscovery: "compat",
entries: {
google: { enabled: false },
},
@@ -884,6 +913,7 @@ describe("resolvePluginProviders", () => {
config: {
plugins: {
allow: ["openrouter"],
bundledDiscovery: "compat",
},
},
mode: "setup",
@@ -900,6 +930,7 @@ describe("resolvePluginProviders", () => {
config: {
plugins: {
allow: ["openrouter", "workspace-provider"],
bundledDiscovery: "compat",
entries: {
"workspace-provider": { enabled: false },
},
@@ -919,6 +950,7 @@ describe("resolvePluginProviders", () => {
config: {
plugins: {
allow: ["openrouter", "workspace-provider"],
bundledDiscovery: "compat",
deny: ["workspace-provider"],
entries: {
"workspace-provider": { enabled: false },
@@ -948,9 +980,7 @@ describe("resolvePluginProviders", () => {
includeUntrustedWorkspacePlugins: false,
});
expectLastSetupRegistryLoad({
onlyPluginIds: ["google", "kilocode", "moonshot"],
});
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
});
it("loads provider plugins from the auto-enabled config snapshot", () => {
@@ -1242,16 +1272,7 @@ describe("resolvePluginProviders", () => {
mode: "setup",
});
expect(loadOpenClawPluginsMock).toHaveBeenCalledWith(
expect.objectContaining({
config: expect.objectContaining({
plugins: expect.objectContaining({
enabled: false,
allow: ["setup-owned-provider"],
}),
}),
}),
);
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
});
it("does not override explicitly disabled setup owners", () => {
@@ -1278,18 +1299,7 @@ describe("resolvePluginProviders", () => {
mode: "setup",
});
expect(loadOpenClawPluginsMock).toHaveBeenCalledWith(
expect.objectContaining({
config: expect.objectContaining({
plugins: expect.objectContaining({
allow: ["setup-owned-provider"],
entries: {
"setup-owned-provider": { enabled: false },
},
}),
}),
}),
);
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
});
it("filters explicit setup owners through the untrusted workspace discovery gate", () => {

View File

@@ -255,7 +255,7 @@ export function resolveDiscoveredProviderPluginIds(params: {
const { registry, onlyPluginIdSet } = loadScopedProviderRegistry(params);
const providerSurfacePluginIds = resolveProviderSurfacePluginIdSet({ ...params, registry });
const shouldFilterUntrustedWorkspacePlugins = params.includeUntrustedWorkspacePlugins === false;
const shouldFilterBundledByAllowlist = params.config?.plugins?.bundledDiscovery === "allowlist";
const shouldFilterBundledByAllowlist = params.config?.plugins?.bundledDiscovery !== "compat";
const normalizedConfig = normalizePluginsConfigWithRegistry(params.config?.plugins, registry);
return listRegistryPluginIds(registry, (plugin) => {
if (
@@ -298,6 +298,9 @@ function isProviderPluginEligibleForSetupDiscovery(params: {
) {
return false;
}
if (params.plugin.origin === "bundled") {
return true;
}
return isActivatedManifestOwner({
plugin: toManifestOwnerRecord(params.plugin),
normalizedConfig: params.normalizedConfig,
@@ -313,7 +316,7 @@ export function resolveDiscoverableProviderOwnerPluginIds(params: {
includeUntrustedWorkspacePlugins?: boolean;
}): string[] {
const shouldFilterUntrustedWorkspacePlugins = params.includeUntrustedWorkspacePlugins === false;
const shouldFilterBundledByAllowlist = params.config?.plugins?.bundledDiscovery === "allowlist";
const shouldFilterBundledByAllowlist = params.config?.plugins?.bundledDiscovery !== "compat";
return resolveProviderOwnerPluginIds({
...params,
isEligible: (plugin, normalizedConfig) =>

View File

@@ -32,7 +32,7 @@ function filterAllowlistedBundledPluginIds(
) {
const allow = config?.plugins?.allow;
if (
config?.plugins?.bundledDiscovery !== "allowlist" ||
config?.plugins?.bundledDiscovery === "compat" ||
!Array.isArray(allow) ||
allow.length === 0
) {

View File

@@ -445,7 +445,7 @@ describe("resolvePluginWebSearchProviders", () => {
const providers = resolvePluginWebSearchProviders({
config: {
plugins: {
allow: ["perplexity"],
allow: ["brave"],
},
},
mode: "setup",