mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
fix(plugins): default bundled discovery to allowlist
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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([]);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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 [
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
|
||||
@@ -445,7 +445,7 @@ describe("resolvePluginWebSearchProviders", () => {
|
||||
const providers = resolvePluginWebSearchProviders({
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["perplexity"],
|
||||
allow: ["brave"],
|
||||
},
|
||||
},
|
||||
mode: "setup",
|
||||
|
||||
Reference in New Issue
Block a user