refactor(plugins): separate activation from enablement (#59844)

* refactor(plugins): separate activation from enablement

* fix(cli): sanitize verbose plugin activation reasons
This commit is contained in:
Vincent Koc
2026-04-03 03:22:37 +09:00
committed by GitHub
parent 4aeb0255f3
commit f911bbc353
15 changed files with 148 additions and 42 deletions

View File

@@ -9,7 +9,7 @@ import {
mergeBundlePathLists,
normalizeBundlePathList,
} from "./bundle-manifest.js";
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
import { normalizePluginsConfig, resolveEffectivePluginActivationState } from "./config-state.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
export type ClaudeBundleCommandSpec = {
@@ -179,13 +179,13 @@ export function loadEnabledClaudeBundleCommands(params: {
) {
continue;
}
const enableState = resolveEffectiveEnableState({
const activationState = resolveEffectivePluginActivationState({
id: record.id,
origin: record.origin,
config: normalizedPlugins,
rootConfig: params.cfg,
});
if (!enableState.enabled) {
if (!activationState.activated) {
continue;
}
for (const relativeRoot of resolveClaudeCommandRootDirs(record.rootDir)) {

View File

@@ -4,7 +4,7 @@ import type { OpenClawConfig } from "../config/config.js";
import { applyMergePatch } from "../config/merge-patch.js";
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { isRecord } from "../utils.js";
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
import { normalizePluginsConfig, resolveEffectivePluginActivationState } from "./config-state.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import type { PluginBundleFormat } from "./types.js";
@@ -114,13 +114,13 @@ export function loadEnabledBundleConfig<TConfig, TDiagnostic>(params: {
if (record.format !== "bundle" || !record.bundleFormat) {
continue;
}
const enableState = resolveEffectiveEnableState({
const activationState = resolveEffectivePluginActivationState({
id: record.id,
origin: record.origin,
config: normalizedPlugins,
rootConfig: params.cfg,
});
if (!enableState.enabled) {
if (!activationState.activated) {
continue;
}

View File

@@ -1,6 +1,6 @@
import { normalizeProviderId } from "../agents/provider-id.js";
import { withBundledPluginVitestCompat } from "./bundled-compat.js";
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
import { normalizePluginsConfig, resolveEffectivePluginActivationState } from "./config-state.js";
import type { PluginLoadOptions } from "./loader.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
@@ -53,12 +53,12 @@ export function resolveEnabledProviderPluginIds(params: {
(plugin) =>
plugin.providers.length > 0 &&
(!onlyPluginIdSet || onlyPluginIdSet.has(plugin.id)) &&
resolveEffectiveEnableState({
resolveEffectivePluginActivationState({
id: plugin.id,
origin: plugin.origin,
config: normalizedConfig,
rootConfig: params.config,
}).enabled,
}).activated,
)
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
@@ -111,12 +111,12 @@ export function resolveNonBundledProviderPluginIds(params: {
(plugin) =>
plugin.origin !== "bundled" &&
plugin.providers.length > 0 &&
resolveEffectiveEnableState({
resolveEffectivePluginActivationState({
id: plugin.id,
origin: plugin.origin,
config: normalizedConfig,
rootConfig: params.config,
}).enabled,
}).activated,
)
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
@@ -137,12 +137,12 @@ export function resolveCatalogHookProviderPluginIds(params: {
.filter(
(plugin) =>
plugin.providers.length > 0 &&
resolveEffectiveEnableState({
resolveEffectivePluginActivationState({
id: plugin.id,
origin: plugin.origin,
config: normalizedConfig,
rootConfig: params.config,
}).enabled,
}).activated,
)
.map((plugin) => plugin.id);
const bundledCompatPluginIds = resolveBundledProviderCompatPluginIds(params);

View File

@@ -440,6 +440,47 @@ describe("plugin status reports", () => {
});
});
it("preserves raw config activation context for compatibility-derived reports", () => {
const { rawConfig, autoEnabledConfig } = createAutoEnabledStatusConfig(
{
demo: { enabled: true },
},
{ channels: { demo: { enabled: true } } },
);
applyPluginAutoEnableMock.mockReturnValue({
config: autoEnabledConfig,
changes: [],
autoEnabledReasons: {
demo: ["demo configured"],
},
});
setSinglePluginLoadResult(
createPluginRecord({
id: "demo",
name: "Demo",
description: "Auto-enabled plugin",
origin: "bundled",
hookCount: 1,
}),
{
typedHooks: [createTypedHook({ pluginId: "demo", hookName: "before_agent_start" })],
},
);
expect(buildPluginCompatibilityNotices({ config: rawConfig })).toEqual([
createCompatibilityNotice({ pluginId: "demo", code: "legacy-before-agent-start" }),
createCompatibilityNotice({ pluginId: "demo", code: "hook-only" }),
]);
expectAutoEnabledStatusLoad({
rawConfig,
autoEnabledConfig,
autoEnabledReasons: {
demo: ["demo configured"],
},
});
});
it("normalizes bundled plugin versions to the core base release", () => {
setSinglePluginLoadResult(
createPluginRecord({

View File

@@ -1,5 +1,5 @@
import { listBundledWebSearchProviders as listBundledWebSearchProviderEntries } from "./bundled-web-search.js";
import { resolveEffectiveEnableState } from "./config-state.js";
import { resolveEffectivePluginActivationState } from "./config-state.js";
import type { PluginLoadOptions } from "./loader.js";
import type { PluginWebSearchProviderEntry } from "./types.js";
import {
@@ -26,11 +26,11 @@ export function resolveBundledPluginWebSearchProviders(params: {
if (onlyPluginIdSet && !onlyPluginIdSet.has(provider.pluginId)) {
return false;
}
return resolveEffectiveEnableState({
return resolveEffectivePluginActivationState({
id: provider.pluginId,
origin: "bundled",
config: normalized,
rootConfig: config,
}).enabled;
}).activated;
});
}