feat(plugins): narrow explicit provider loads from manifests (#65259)

* feat(plugins): narrow explicit provider loads from manifests

* fix(plugins): preserve setup trust filtering for explicit owners

* fix(plugins): respect runtime owner trust and disablement

* fix(plugins): preserve provider owner policy bounds
This commit is contained in:
Vincent Koc
2026-04-12 10:52:03 +01:00
committed by GitHub
parent 3686255a55
commit 12db6dfc8d
6 changed files with 674 additions and 63 deletions

View File

@@ -527,9 +527,12 @@ actual behavior such as hooks, tools, commands, or provider flows.
Optional manifest `activation` and `setup` blocks stay on the control plane.
They are metadata-only descriptors for activation planning and setup discovery;
they do not replace runtime registration, `register(...)`, or `setupEntry`.
The first activation consumer now uses manifest command hints to narrow CLI
plugin loading when a primary command is known, instead of always loading every
CLI-capable plugin up front.
The first live activation consumers now use manifest command and provider hints
to narrow plugin loading before broader registry materialization:
- CLI loading narrows to plugins that own the requested primary command
- explicit provider setup/runtime resolution narrows to plugins that own the
requested provider id
Setup discovery now prefers descriptor-owned ids such as `setup.providers` and
`setup.cliBackends` to narrow candidate plugins before it falls back to

View File

@@ -222,8 +222,8 @@ should activate it later.
This block is metadata only. It does not register runtime behavior, and it does
not replace `register(...)`, `setupEntry`, or other runtime/plugin entrypoints.
Current consumers use it as a narrowing hint before broader plugin loading, so
missing activation metadata only costs performance; it should not change
correctness.
missing activation metadata usually only costs performance; it should not
change correctness while legacy manifest ownership fallbacks still exist.
```json
{
@@ -245,9 +245,13 @@ correctness.
| `onRoutes` | No | `string[]` | Route kinds that should activate this plugin. |
| `onCapabilities` | No | `Array<"provider" \| "channel" \| "tool" \| "hook">` | Broad capability hints used by control-plane activation planning. |
For command-triggered planning specifically, OpenClaw still falls back to
legacy `commandAliases[].cliCommand` or `commandAliases[].name` when a plugin
has not added explicit `activation.onCommands` metadata yet.
Current live consumers:
- command-triggered CLI planning falls back to legacy
`commandAliases[].cliCommand` or `commandAliases[].name`
- provider-triggered setup/runtime planning falls back to legacy
`providers[]` and top-level `cliBackends[]` ownership when explicit provider
activation metadata is missing
## setup reference

View File

@@ -50,6 +50,8 @@ export type BundledPluginCompatibleActivationInputs = PluginActivationInputs & {
export function withActivatedPluginIds(params: {
config?: OpenClawConfig;
pluginIds: readonly string[];
overrideGlobalDisable?: boolean;
overrideExplicitDisable?: boolean;
}): OpenClawConfig | undefined {
if (params.pluginIds.length === 0) {
return params.config;
@@ -64,12 +66,14 @@ export function withActivatedPluginIds(params: {
continue;
}
allow.add(normalized);
const existingEntry = entries[normalized];
entries[normalized] = {
...entries[normalized],
enabled: true,
...existingEntry,
enabled: existingEntry?.enabled !== false || params.overrideExplicitDisable === true,
};
}
const forcePluginsEnabled = params.config?.plugins?.enabled === false;
const forcePluginsEnabled =
params.overrideGlobalDisable === true && params.config?.plugins?.enabled === false;
return {
...params.config,
plugins: {

View File

@@ -1,5 +1,6 @@
import { withActivatedPluginIds } from "./activation-context.js";
import { resolveBundledPluginCompatibleActivationInputs } from "./activation-context.js";
import { resolveManifestActivationPluginIds } from "./activation-planner.js";
import {
isPluginRegistryLoadInFlight,
loadOpenClawPlugins,
@@ -7,6 +8,8 @@ import {
type PluginLoadOptions,
} from "./loader.js";
import {
resolveActivatableProviderOwnerPluginIds,
resolveDiscoverableProviderOwnerPluginIds,
resolveDiscoveredProviderPluginIds,
resolveEnabledProviderPluginIds,
resolveBundledProviderCompatPluginIds,
@@ -21,6 +24,54 @@ import {
} from "./runtime/load-context.js";
import type { ProviderPlugin } from "./types.js";
function dedupeSortedPluginIds(values: Iterable<string>): string[] {
return [...new Set(values)].toSorted((left, right) => left.localeCompare(right));
}
function resolveExplicitProviderOwnerPluginIds(params: {
providerRefs: readonly string[];
config?: PluginLoadOptions["config"];
workspaceDir?: string;
env?: PluginLoadOptions["env"];
}): string[] {
return dedupeSortedPluginIds(
params.providerRefs.flatMap((provider) => {
const plannedPluginIds = resolveManifestActivationPluginIds({
trigger: {
kind: "provider",
provider,
},
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
if (plannedPluginIds.length > 0) {
return plannedPluginIds;
}
// Keep legacy provider/CLI-backend ownership working until every owner is
// expressible through activation descriptors.
return (
resolveOwningPluginIdsForProvider({
provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
}) ?? []
);
}),
);
}
function mergeExplicitOwnerPluginIds(
providerPluginIds: readonly string[],
explicitOwnerPluginIds: readonly string[],
): string[] {
if (explicitOwnerPluginIds.length === 0) {
return [...providerPluginIds];
}
return dedupeSortedPluginIds([...providerPluginIds, ...explicitOwnerPluginIds]);
}
function resolvePluginProviderLoadBase(params: {
config?: PluginLoadOptions["config"];
workspaceDir?: string;
@@ -32,19 +83,12 @@ function resolvePluginProviderLoadBase(params: {
const env = params.env ?? process.env;
const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDir();
const providerOwnedPluginIds = params.providerRefs?.length
? [
...new Set(
params.providerRefs.flatMap(
(provider) =>
resolveOwningPluginIdsForProvider({
provider,
config: params.config,
workspaceDir,
env,
}) ?? [],
),
),
]
? resolveExplicitProviderOwnerPluginIds({
providerRefs: params.providerRefs,
config: params.config,
workspaceDir,
env,
})
: [];
const modelOwnedPluginIds = params.modelRefs?.length
? resolveOwningPluginIdsForModelRefs({
@@ -68,15 +112,16 @@ function resolvePluginProviderLoadBase(params: {
]),
].toSorted((left, right) => left.localeCompare(right))
: undefined;
const runtimeConfig = withActivatedPluginIds({
config: params.config,
pluginIds: [...providerOwnedPluginIds, ...modelOwnedPluginIds],
});
const explicitOwnerPluginIds = dedupeSortedPluginIds([
...providerOwnedPluginIds,
...modelOwnedPluginIds,
]);
return {
env,
workspaceDir,
requestedPluginIds,
runtimeConfig,
explicitOwnerPluginIds,
rawConfig: params.config,
};
}
@@ -85,29 +130,38 @@ function resolveSetupProviderPluginLoadState(
base: ReturnType<typeof resolvePluginProviderLoadBase>,
) {
const providerPluginIds = resolveDiscoveredProviderPluginIds({
config: base.runtimeConfig,
config: params.config,
workspaceDir: base.workspaceDir,
env: base.env,
onlyPluginIds: base.requestedPluginIds,
includeUntrustedWorkspacePlugins: params.includeUntrustedWorkspacePlugins,
});
if (providerPluginIds.length === 0) {
const explicitOwnerPluginIds = resolveDiscoverableProviderOwnerPluginIds({
pluginIds: base.explicitOwnerPluginIds,
config: params.config,
workspaceDir: base.workspaceDir,
env: base.env,
includeUntrustedWorkspacePlugins: params.includeUntrustedWorkspacePlugins,
});
const setupPluginIds = mergeExplicitOwnerPluginIds(providerPluginIds, explicitOwnerPluginIds);
if (setupPluginIds.length === 0) {
return undefined;
}
const setupConfig = withActivatedPluginIds({
config: base.rawConfig,
pluginIds: setupPluginIds,
});
const loadOptions = buildPluginRuntimeLoadOptionsFromValues(
{
config: withActivatedPluginIds({
config: base.runtimeConfig,
pluginIds: providerPluginIds,
}),
activationSourceConfig: base.runtimeConfig,
config: setupConfig,
activationSourceConfig: setupConfig,
autoEnabledReasons: {},
workspaceDir: base.workspaceDir,
env: base.env,
logger: createPluginRuntimeLoaderLogger(),
},
{
onlyPluginIds: providerPluginIds,
onlyPluginIds: setupPluginIds,
pluginSdkResolution: params.pluginSdkResolution,
cache: params.cache ?? false,
activate: params.activate ?? false,
@@ -120,11 +174,26 @@ function resolveRuntimeProviderPluginLoadState(
params: Parameters<typeof resolvePluginProviders>[0],
base: ReturnType<typeof resolvePluginProviderLoadBase>,
) {
const explicitOwnerPluginIds = resolveActivatableProviderOwnerPluginIds({
pluginIds: base.explicitOwnerPluginIds,
config: base.rawConfig,
workspaceDir: base.workspaceDir,
env: base.env,
includeUntrustedWorkspacePlugins: params.includeUntrustedWorkspacePlugins,
});
const runtimeRequestedPluginIds =
base.requestedPluginIds !== undefined
? dedupeSortedPluginIds([...(params.onlyPluginIds ?? []), ...explicitOwnerPluginIds])
: undefined;
const requestConfig = withActivatedPluginIds({
config: base.rawConfig,
pluginIds: explicitOwnerPluginIds,
});
const activation = resolveBundledPluginCompatibleActivationInputs({
rawConfig: base.runtimeConfig,
rawConfig: requestConfig,
env: base.env,
workspaceDir: base.workspaceDir,
onlyPluginIds: base.requestedPluginIds,
onlyPluginIds: runtimeRequestedPluginIds,
applyAutoEnable: true,
compatMode: {
allowlist: params.bundledProviderAllowlistCompat,
@@ -140,12 +209,15 @@ function resolveRuntimeProviderPluginLoadState(
env: base.env,
})
: activation.config;
const providerPluginIds = resolveEnabledProviderPluginIds({
config,
workspaceDir: base.workspaceDir,
env: base.env,
onlyPluginIds: base.requestedPluginIds,
});
const providerPluginIds = mergeExplicitOwnerPluginIds(
resolveEnabledProviderPluginIds({
config,
workspaceDir: base.workspaceDir,
env: base.env,
onlyPluginIds: runtimeRequestedPluginIds,
}),
explicitOwnerPluginIds,
);
const loadOptions = buildPluginRuntimeLoadOptionsFromValues(
{
config,

View File

@@ -21,7 +21,9 @@ const applyPluginAutoEnableMock = vi.fn<ApplyPluginAutoEnable>();
let resolveOwningPluginIdsForProvider: typeof import("./providers.js").resolveOwningPluginIdsForProvider;
let resolveOwningPluginIdsForModelRef: typeof import("./providers.js").resolveOwningPluginIdsForModelRef;
let resolveActivatableProviderOwnerPluginIds: typeof import("./providers.js").resolveActivatableProviderOwnerPluginIds;
let resolveEnabledProviderPluginIds: typeof import("./providers.js").resolveEnabledProviderPluginIds;
let resolveDiscoverableProviderOwnerPluginIds: typeof import("./providers.js").resolveDiscoverableProviderOwnerPluginIds;
let resolvePluginProviders: typeof import("./providers.runtime.js").resolvePluginProviders;
let setActivePluginRegistry: SetActivePluginRegistry;
@@ -32,6 +34,8 @@ function createManifestProviderPlugin(params: {
origin?: "bundled" | "workspace";
enabledByDefault?: boolean;
modelSupport?: { modelPrefixes?: string[]; modelPatterns?: string[] };
activation?: PluginManifestRecord["activation"];
setup?: PluginManifestRecord["setup"];
}): PluginManifestRecord {
return {
id: params.id,
@@ -40,6 +44,8 @@ function createManifestProviderPlugin(params: {
providers: params.providerIds,
cliBackends: params.cliBackends ?? [],
modelSupport: params.modelSupport,
activation: params.activation,
setup: params.setup,
skills: [],
hooks: [],
origin: params.origin ?? "bundled",
@@ -280,9 +286,11 @@ describe("resolvePluginProviders", () => {
loadPluginManifestRegistryMock(...args),
}));
({
resolveActivatableProviderOwnerPluginIds,
resolveOwningPluginIdsForProvider,
resolveOwningPluginIdsForModelRef,
resolveEnabledProviderPluginIds,
resolveDiscoverableProviderOwnerPluginIds,
} = await import("./providers.js"));
({ resolvePluginProviders } = await import("./providers.runtime.js"));
({ setActivePluginRegistry } = await import("./runtime.js"));
@@ -538,7 +546,7 @@ describe("resolvePluginProviders", () => {
"workspace-provider",
]),
entries: expect.objectContaining({
google: { enabled: true },
google: { enabled: false },
kilocode: { enabled: true },
moonshot: { enabled: true },
"workspace-provider": { enabled: true },
@@ -709,6 +717,414 @@ describe("resolvePluginProviders", () => {
}),
);
});
it("uses activation.onProviders to keep explicit provider owners on the runtime path", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "activation-owned-provider",
providerIds: [],
activation: {
onProviders: ["activation-owned"],
},
}),
]);
resolvePluginProviders({
config: {},
providerRefs: ["activation-owned"],
activate: true,
});
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(
expect.objectContaining({
onlyPluginIds: ["activation-owned-provider"],
activate: true,
config: expect.objectContaining({
plugins: expect.objectContaining({
allow: ["activation-owned-provider"],
entries: {
"activation-owned-provider": { enabled: true },
},
}),
}),
}),
);
});
it("does not activate explicit runtime owners when plugins are globally disabled", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "activation-owned-provider",
providerIds: [],
activation: {
onProviders: ["activation-owned"],
},
}),
]);
expect(
resolveActivatableProviderOwnerPluginIds({
pluginIds: ["activation-owned-provider"],
config: {
plugins: {
enabled: false,
},
},
}),
).toEqual([]);
});
it("does not activate explicit runtime owners disabled in config", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "activation-owned-provider",
providerIds: [],
activation: {
onProviders: ["activation-owned"],
},
}),
]);
expect(
resolveActivatableProviderOwnerPluginIds({
pluginIds: ["activation-owned-provider"],
config: {
plugins: {
entries: {
"activation-owned-provider": { enabled: false },
},
},
},
}),
).toEqual([]);
});
it("does not activate explicit runtime owners outside the allowlist", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "activation-owned-provider",
providerIds: [],
activation: {
onProviders: ["activation-owned"],
},
}),
]);
expect(
resolveActivatableProviderOwnerPluginIds({
pluginIds: ["activation-owned-provider"],
config: {
plugins: {
allow: ["other-plugin"],
},
},
}),
).toEqual([]);
});
it("uses setup.providers to keep explicit provider owners on the setup path", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "setup-owned-provider",
providerIds: [],
setup: {
providers: [{ id: "setup-owned" }],
},
}),
]);
resolvePluginProviders({
config: {},
providerRefs: ["setup-owned"],
activate: true,
mode: "setup",
});
expect(loadOpenClawPluginsMock).toHaveBeenCalledWith(
expect.objectContaining({
onlyPluginIds: ["setup-owned-provider"],
activate: true,
config: expect.objectContaining({
plugins: expect.objectContaining({
allow: ["setup-owned-provider"],
entries: {
"setup-owned-provider": { enabled: true },
},
}),
}),
}),
);
});
it("does not override global plugin disable during setup owner loading", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "setup-owned-provider",
providerIds: [],
setup: {
providers: [{ id: "setup-owned" }],
},
}),
]);
resolvePluginProviders({
config: {
plugins: {
enabled: false,
},
},
providerRefs: ["setup-owned"],
activate: true,
mode: "setup",
});
expect(loadOpenClawPluginsMock).toHaveBeenCalledWith(
expect.objectContaining({
config: expect.objectContaining({
plugins: expect.objectContaining({
enabled: false,
allow: ["setup-owned-provider"],
}),
}),
}),
);
});
it("does not override explicitly disabled setup owners", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "setup-owned-provider",
providerIds: [],
setup: {
providers: [{ id: "setup-owned" }],
},
}),
]);
resolvePluginProviders({
config: {
plugins: {
entries: {
"setup-owned-provider": { enabled: false },
},
},
},
providerRefs: ["setup-owned"],
activate: true,
mode: "setup",
});
expect(loadOpenClawPluginsMock).toHaveBeenCalledWith(
expect.objectContaining({
config: expect.objectContaining({
plugins: expect.objectContaining({
allow: ["setup-owned-provider"],
entries: {
"setup-owned-provider": { enabled: false },
},
}),
}),
}),
);
});
it("filters explicit setup owners through the untrusted workspace discovery gate", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "workspace-activation-owner",
providerIds: [],
origin: "workspace",
activation: {
onProviders: ["workspace-activation"],
},
}),
]);
const providers = resolvePluginProviders({
config: {},
providerRefs: ["workspace-activation"],
activate: true,
mode: "setup",
includeUntrustedWorkspacePlugins: false,
});
expect(providers).toEqual([]);
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
});
it("does not auto-activate untrusted workspace runtime owners when requested", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "workspace-activation-owner",
providerIds: [],
origin: "workspace",
activation: {
onProviders: ["workspace-activation"],
},
}),
]);
resolveRuntimePluginRegistryMock.mockReturnValue(createEmptyPluginRegistry());
const providers = resolvePluginProviders({
config: {},
providerRefs: ["workspace-activation"],
activate: true,
includeUntrustedWorkspacePlugins: false,
});
expect(providers).toEqual([]);
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(
expect.objectContaining({
config: {},
onlyPluginIds: [],
}),
);
});
it("does not auto-activate workspace runtime owners by default", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "workspace-activation-owner",
providerIds: [],
origin: "workspace",
activation: {
onProviders: ["workspace-activation"],
},
}),
]);
resolveRuntimePluginRegistryMock.mockReturnValue(createEmptyPluginRegistry());
const providers = resolvePluginProviders({
config: {},
providerRefs: ["workspace-activation"],
activate: true,
});
expect(providers).toEqual([]);
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(
expect.objectContaining({
config: {},
onlyPluginIds: [],
}),
);
});
it("keeps explicit provider requests scoped when runtime owner activation resolves nothing", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "activation-owned-provider",
providerIds: [],
activation: {
onProviders: ["activation-owned"],
},
}),
]);
resolveRuntimePluginRegistryMock.mockReturnValue(createEmptyPluginRegistry());
const providers = resolvePluginProviders({
config: {
plugins: {
allow: ["other-plugin"],
},
},
providerRefs: ["activation-owned"],
activate: true,
});
expect(providers).toEqual([]);
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(
expect.objectContaining({
config: {
plugins: {
allow: ["other-plugin"],
},
},
onlyPluginIds: [],
}),
);
});
it("keeps explicitly trusted disabled workspace setup owners discoverable", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "workspace-activation-owner",
providerIds: [],
origin: "workspace",
activation: {
onProviders: ["workspace-activation"],
},
}),
]);
expect(
resolveDiscoverableProviderOwnerPluginIds({
pluginIds: ["workspace-activation-owner"],
config: {
plugins: {
enabled: true,
allow: ["workspace-activation-owner"],
entries: {
"workspace-activation-owner": { enabled: false },
},
},
},
includeUntrustedWorkspacePlugins: false,
}),
).toEqual(["workspace-activation-owner"]);
});
it("does not auto-activate explicitly disabled trusted workspace runtime owners", () => {
setManifestPlugins([
createManifestProviderPlugin({
id: "workspace-activation-owner",
providerIds: [],
origin: "workspace",
activation: {
onProviders: ["workspace-activation"],
},
}),
]);
expect(
resolveActivatableProviderOwnerPluginIds({
pluginIds: ["workspace-activation-owner"],
config: {
plugins: {
allow: ["workspace-activation-owner"],
entries: {
"workspace-activation-owner": { enabled: false },
},
},
},
includeUntrustedWorkspacePlugins: false,
}),
).toEqual([]);
});
it("keeps legacy CLI backend ownership as the explicit provider fallback", () => {
setOwningProviderManifestPlugins();
resolvePluginProviders({
config: {},
providerRefs: ["claude-cli"],
activate: true,
});
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(
expect.objectContaining({
onlyPluginIds: ["anthropic"],
activate: true,
config: expect.objectContaining({
plugins: expect.objectContaining({
allow: ["anthropic"],
entries: {
anthropic: { enabled: true },
},
}),
}),
}),
);
});
it.each([
{
provider: "minimax-portal",

View File

@@ -89,33 +89,145 @@ export function resolveDiscoveredProviderPluginIds(params: {
if (!(plugin.providers.length > 0 && (!onlyPluginIdSet || onlyPluginIdSet.has(plugin.id)))) {
return false;
}
if (!shouldFilterUntrustedWorkspacePlugins || plugin.origin !== "workspace") {
return true;
}
const activation = resolveEffectivePluginActivationState({
id: plugin.id,
origin: plugin.origin,
config: normalizedConfig,
return isProviderPluginEligibleForSetupDiscovery({
plugin,
shouldFilterUntrustedWorkspacePlugins,
normalizedConfig,
rootConfig: params.config,
enabledByDefault: plugin.enabledByDefault,
});
if (activation.activated) {
return true;
}
const explicitlyTrustedButDisabled =
normalizedConfig.enabled &&
!normalizedConfig.deny.includes(plugin.id) &&
normalizedConfig.allow.includes(plugin.id) &&
normalizedConfig.entries[plugin.id]?.enabled === false;
return explicitlyTrustedButDisabled;
})
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
}
function isProviderPluginEligibleForSetupDiscovery(params: {
plugin: PluginManifestRecord;
shouldFilterUntrustedWorkspacePlugins: boolean;
normalizedConfig: ReturnType<typeof normalizePluginsConfig>;
rootConfig?: PluginLoadOptions["config"];
}): boolean {
if (!params.shouldFilterUntrustedWorkspacePlugins || params.plugin.origin !== "workspace") {
return true;
}
const activation = resolveEffectivePluginActivationState({
id: params.plugin.id,
origin: params.plugin.origin,
config: params.normalizedConfig,
rootConfig: params.rootConfig,
enabledByDefault: params.plugin.enabledByDefault,
});
if (activation.activated) {
return true;
}
const explicitlyTrustedButDisabled =
params.normalizedConfig.enabled &&
!params.normalizedConfig.deny.includes(params.plugin.id) &&
params.normalizedConfig.allow.includes(params.plugin.id) &&
params.normalizedConfig.entries[params.plugin.id]?.enabled === false;
return explicitlyTrustedButDisabled;
}
export function resolveDiscoverableProviderOwnerPluginIds(params: {
pluginIds: readonly string[];
config?: PluginLoadOptions["config"];
workspaceDir?: string;
env?: PluginLoadOptions["env"];
includeUntrustedWorkspacePlugins?: boolean;
}): string[] {
if (params.pluginIds.length === 0) {
return [];
}
const pluginIdSet = new Set(params.pluginIds);
const registry = loadPluginManifestRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
const shouldFilterUntrustedWorkspacePlugins = params.includeUntrustedWorkspacePlugins === false;
const normalizedConfig = normalizePluginsConfig(params.config?.plugins);
return registry.plugins
.filter(
(plugin) =>
pluginIdSet.has(plugin.id) &&
isProviderPluginEligibleForSetupDiscovery({
plugin,
shouldFilterUntrustedWorkspacePlugins,
normalizedConfig,
rootConfig: params.config,
}),
)
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
}
function isProviderPluginEligibleForRuntimeOwnerActivation(params: {
plugin: PluginManifestRecord;
normalizedConfig: ReturnType<typeof normalizePluginsConfig>;
rootConfig?: PluginLoadOptions["config"];
}): boolean {
if (!params.normalizedConfig.enabled) {
return false;
}
if (params.normalizedConfig.deny.includes(params.plugin.id)) {
return false;
}
if (params.normalizedConfig.entries[params.plugin.id]?.enabled === false) {
return false;
}
if (
params.normalizedConfig.allow.length > 0 &&
!params.normalizedConfig.allow.includes(params.plugin.id)
) {
return false;
}
if (params.plugin.origin !== "workspace") {
return true;
}
return resolveEffectivePluginActivationState({
id: params.plugin.id,
origin: params.plugin.origin,
config: params.normalizedConfig,
rootConfig: params.rootConfig,
enabledByDefault: params.plugin.enabledByDefault,
}).activated;
}
export function resolveActivatableProviderOwnerPluginIds(params: {
pluginIds: readonly string[];
config?: PluginLoadOptions["config"];
workspaceDir?: string;
env?: PluginLoadOptions["env"];
includeUntrustedWorkspacePlugins?: boolean;
}): string[] {
if (params.pluginIds.length === 0) {
return [];
}
const pluginIdSet = new Set(params.pluginIds);
const registry = loadPluginManifestRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
const normalizedConfig = normalizePluginsConfig(params.config?.plugins);
return registry.plugins
.filter(
(plugin) =>
pluginIdSet.has(plugin.id) &&
isProviderPluginEligibleForRuntimeOwnerActivation({
plugin,
normalizedConfig,
rootConfig: params.config,
}),
)
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
}
export const __testing = {
resolveActivatableProviderOwnerPluginIds,
resolveEnabledProviderPluginIds,
resolveDiscoveredProviderPluginIds,
resolveDiscoverableProviderOwnerPluginIds,
resolveBundledProviderCompatPluginIds,
withBundledProviderVitestCompat,
} as const;