diff --git a/src/config/plugin-auto-enable.ts b/src/config/plugin-auto-enable.ts index d631b3bd5cf..a0e432e007b 100644 --- a/src/config/plugin-auto-enable.ts +++ b/src/config/plugin-auto-enable.ts @@ -26,6 +26,8 @@ type PluginEnableChange = { reason: string; }; +export type PluginAutoEnableCandidate = PluginEnableChange; + export type PluginAutoEnableResult = { config: OpenClawConfig; changes: string[]; @@ -457,8 +459,8 @@ function resolveConfiguredPlugins( cfg: OpenClawConfig, env: NodeJS.ProcessEnv, registry: PluginManifestRegistry, -): PluginEnableChange[] { - const changes: PluginEnableChange[] = []; +): PluginAutoEnableCandidate[] { + const changes: PluginAutoEnableCandidate[] = []; // Build reverse map: channel ID → plugin ID from installed plugin manifests. const channelToPluginId = buildChannelToPluginIdMap(registry); for (const channelId of collectCandidateChannelIds(cfg, env)) { @@ -644,38 +646,45 @@ function formatAutoEnableChange(entry: PluginEnableChange): string { return `${reason}, enabled automatically.`; } -export function applyPluginAutoEnable(params: { +export function detectPluginAutoEnableCandidates(params: { config?: OpenClawConfig; env?: NodeJS.ProcessEnv; - /** Pre-loaded manifest registry. When omitted, the registry is loaded from - * the installed plugins on disk. Pass an explicit registry in tests to - * avoid filesystem access and control what plugins are "installed". */ manifestRegistry?: PluginManifestRegistry; -}): PluginAutoEnableResult { +}): PluginAutoEnableCandidate[] { const env = params.env ?? process.env; const config = params.config ?? ({} as OpenClawConfig); if (!configMayNeedPluginAutoEnable(config, env)) { - return { config, changes: [], autoEnabledReasons: {} }; + return []; } const registry = params.manifestRegistry ?? (configMayNeedPluginManifestRegistry(config) ? loadPluginManifestRegistry({ config, env }) : EMPTY_PLUGIN_MANIFEST_REGISTRY); - const configured = resolveConfiguredPlugins(config, env, registry); - if (configured.length === 0) { - return { config, changes: [], autoEnabledReasons: {} }; - } + return resolveConfiguredPlugins(config, env, registry); +} - let next = config; +export function materializePluginAutoEnableCandidates(params: { + config?: OpenClawConfig; + candidates: readonly PluginAutoEnableCandidate[]; + env?: NodeJS.ProcessEnv; + manifestRegistry?: PluginManifestRegistry; +}): PluginAutoEnableResult { + const env = params.env ?? process.env; + let next = params.config ?? {}; const changes: string[] = []; const autoEnabledReasons = new Map(); + const registry = + params.manifestRegistry ?? + (configMayNeedPluginManifestRegistry(next) + ? loadPluginManifestRegistry({ config: next, env }) + : EMPTY_PLUGIN_MANIFEST_REGISTRY); if (next.plugins?.enabled === false) { return { config: next, changes, autoEnabledReasons: {} }; } - for (const entry of configured) { + for (const entry of params.candidates) { const builtInChannelId = normalizeChatChannelId(entry.pluginId); if (isPluginDenied(next, entry.pluginId)) { continue; @@ -683,7 +692,7 @@ export function applyPluginAutoEnable(params: { if (isPluginExplicitlyDisabled(next, entry.pluginId)) { continue; } - if (shouldSkipPreferredPluginAutoEnable(next, entry, configured, env, registry)) { + if (shouldSkipPreferredPluginAutoEnable(next, entry, [...params.candidates], env, registry)) { continue; } const allow = next.plugins?.allow; @@ -728,3 +737,20 @@ export function applyPluginAutoEnable(params: { return { config: next, changes, autoEnabledReasons: autoEnabledReasonRecord }; } + +export function applyPluginAutoEnable(params: { + config?: OpenClawConfig; + env?: NodeJS.ProcessEnv; + /** Pre-loaded manifest registry. When omitted, the registry is loaded from + * the installed plugins on disk. Pass an explicit registry in tests to + * avoid filesystem access and control what plugins are "installed". */ + manifestRegistry?: PluginManifestRegistry; +}): PluginAutoEnableResult { + const candidates = detectPluginAutoEnableCandidates(params); + return materializePluginAutoEnableCandidates({ + config: params.config, + candidates, + env: params.env, + manifestRegistry: params.manifestRegistry, + }); +} diff --git a/src/plugins/activation-context.ts b/src/plugins/activation-context.ts new file mode 100644 index 00000000000..6cfd624a281 --- /dev/null +++ b/src/plugins/activation-context.ts @@ -0,0 +1,95 @@ +import type { OpenClawConfig } from "../config/config.js"; +import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; +import { + withBundledPluginAllowlistCompat, + withBundledPluginEnablementCompat, + withBundledPluginVitestCompat, +} from "./bundled-compat.js"; +import { + createPluginActivationSource, + normalizePluginsConfig, + type NormalizedPluginsConfig, + type PluginActivationConfigSource, +} from "./config-state.js"; + +export type PluginActivationCompatConfig = { + allowlistPluginIds?: readonly string[]; + enablementPluginIds?: readonly string[]; + vitestPluginIds?: readonly string[]; +}; + +export type PluginActivationInputs = { + rawConfig?: OpenClawConfig; + config?: OpenClawConfig; + normalized: NormalizedPluginsConfig; + activationSourceConfig?: OpenClawConfig; + activationSource: PluginActivationConfigSource; + autoEnabledReasons: Record; +}; + +function applyPluginActivationCompat(params: { + config?: OpenClawConfig; + compat?: PluginActivationCompatConfig; + env: NodeJS.ProcessEnv; +}): OpenClawConfig | undefined { + const allowlistCompat = params.compat?.allowlistPluginIds?.length + ? withBundledPluginAllowlistCompat({ + config: params.config, + pluginIds: params.compat.allowlistPluginIds, + }) + : params.config; + const enablementCompat = params.compat?.enablementPluginIds?.length + ? withBundledPluginEnablementCompat({ + config: allowlistCompat, + pluginIds: params.compat.enablementPluginIds, + }) + : allowlistCompat; + const vitestCompat = params.compat?.vitestPluginIds?.length + ? withBundledPluginVitestCompat({ + config: enablementCompat, + pluginIds: params.compat.vitestPluginIds, + env: params.env, + }) + : enablementCompat; + return vitestCompat; +} + +export function resolvePluginActivationInputs(params: { + rawConfig?: OpenClawConfig; + resolvedConfig?: OpenClawConfig; + autoEnabledReasons?: Record; + env?: NodeJS.ProcessEnv; + compat?: PluginActivationCompatConfig; + applyAutoEnable?: boolean; +}): PluginActivationInputs { + const env = params.env ?? process.env; + const rawConfig = params.rawConfig ?? params.resolvedConfig; + let resolvedConfig = params.resolvedConfig ?? params.rawConfig; + let autoEnabledReasons = params.autoEnabledReasons; + + if (params.applyAutoEnable && rawConfig !== undefined) { + const autoEnabled = applyPluginAutoEnable({ + config: rawConfig, + env, + }); + resolvedConfig = autoEnabled.config; + autoEnabledReasons = autoEnabled.autoEnabledReasons; + } + + const config = applyPluginActivationCompat({ + config: resolvedConfig, + compat: params.compat, + env, + }); + + return { + rawConfig, + config, + normalized: normalizePluginsConfig(config?.plugins), + activationSourceConfig: rawConfig, + activationSource: createPluginActivationSource({ + config: rawConfig, + }), + autoEnabledReasons: autoEnabledReasons ?? {}, + }; +} diff --git a/src/plugins/config-state.ts b/src/plugins/config-state.ts index c26f602574b..a302ccdbccb 100644 --- a/src/plugins/config-state.ts +++ b/src/plugins/config-state.ts @@ -9,6 +9,24 @@ import type { PluginKind, PluginOrigin } from "./types.js"; export type PluginActivationSource = "disabled" | "explicit" | "auto" | "default"; +export type PluginExplicitSelectionCause = + | "enabled-in-config" + | "bundled-channel-enabled-in-config" + | "selected-memory-slot" + | "selected-in-allowlist"; + +export type PluginActivationCause = + | PluginExplicitSelectionCause + | "plugins-disabled" + | "blocked-by-denylist" + | "disabled-in-config" + | "workspace-disabled-by-default" + | "not-in-allowlist" + | "enabled-by-effective-config" + | "bundled-channel-configured" + | "bundled-default-enablement" + | "bundled-disabled-by-default"; + export type PluginActivationState = { enabled: boolean; activated: boolean; @@ -17,6 +35,15 @@ export type PluginActivationState = { reason?: string; }; +type PluginActivationDecision = { + enabled: boolean; + activated: boolean; + explicitlyEnabled: boolean; + source: PluginActivationSource; + cause?: PluginActivationCause; + reason?: string; +}; + export type PluginActivationConfigSource = { plugins: NormalizedPluginsConfig; rootConfig?: OpenClawConfig; @@ -79,6 +106,42 @@ const normalizeSlotValue = (value: unknown): string | null | undefined => { return trimmed; }; +const PLUGIN_ACTIVATION_REASON_BY_CAUSE: Record = { + "enabled-in-config": "enabled in config", + "bundled-channel-enabled-in-config": "channel enabled in config", + "selected-memory-slot": "selected memory slot", + "selected-in-allowlist": "selected in allowlist", + "plugins-disabled": "plugins disabled", + "blocked-by-denylist": "blocked by denylist", + "disabled-in-config": "disabled in config", + "workspace-disabled-by-default": "workspace plugin (disabled by default)", + "not-in-allowlist": "not in allowlist", + "enabled-by-effective-config": "enabled by effective config", + "bundled-channel-configured": "channel configured", + "bundled-default-enablement": "bundled default enablement", + "bundled-disabled-by-default": "bundled (disabled by default)", +}; + +function resolvePluginActivationReason( + cause?: PluginActivationCause, + reason?: string, +): string | undefined { + if (reason) { + return reason; + } + return cause ? PLUGIN_ACTIVATION_REASON_BY_CAUSE[cause] : undefined; +} + +function toPluginActivationState(decision: PluginActivationDecision): PluginActivationState { + return { + enabled: decision.enabled, + activated: decision.activated, + explicitlyEnabled: decision.explicitlyEnabled, + source: decision.source, + reason: resolvePluginActivationReason(decision.cause, decision.reason), + }; +} + const normalizePluginEntries = (entries: unknown): NormalizedPluginsConfig["entries"] => { if (!entries || typeof entries !== "object" || Array.isArray(entries)) { return {}; @@ -265,21 +328,21 @@ function resolveExplicitPluginSelection(params: { origin: PluginOrigin; config: NormalizedPluginsConfig; rootConfig?: OpenClawConfig; -}): { explicitlyEnabled: boolean; reason?: string } { +}): { explicitlyEnabled: boolean; cause?: PluginExplicitSelectionCause } { if (params.config.entries[params.id]?.enabled === true) { - return { explicitlyEnabled: true, reason: "enabled in config" }; + return { explicitlyEnabled: true, cause: "enabled-in-config" }; } if ( params.origin === "bundled" && isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id) ) { - return { explicitlyEnabled: true, reason: "channel enabled in config" }; + return { explicitlyEnabled: true, cause: "bundled-channel-enabled-in-config" }; } if (params.config.slots.memory === params.id) { - return { explicitlyEnabled: true, reason: "selected memory slot" }; + return { explicitlyEnabled: true, cause: "selected-memory-slot" }; } if (params.origin !== "bundled" && params.config.allow.includes(params.id)) { - return { explicitlyEnabled: true, reason: "selected in allowlist" }; + return { explicitlyEnabled: true, cause: "selected-in-allowlist" }; } return { explicitlyEnabled: false }; } @@ -308,127 +371,127 @@ export function resolvePluginActivationState(params: { const explicitlyConfiguredBundledChannel = params.origin === "bundled" && explicitSelection.explicitlyEnabled && - explicitSelection.reason === "channel enabled in config"; + explicitSelection.cause === "bundled-channel-enabled-in-config"; if (!params.config.enabled) { - return { + return toPluginActivationState({ enabled: false, activated: false, explicitlyEnabled: explicitSelection.explicitlyEnabled, source: "disabled", - reason: "plugins disabled", - }; + cause: "plugins-disabled", + }); } if (params.config.deny.includes(params.id)) { - return { + return toPluginActivationState({ enabled: false, activated: false, explicitlyEnabled: explicitSelection.explicitlyEnabled, source: "disabled", - reason: "blocked by denylist", - }; + cause: "blocked-by-denylist", + }); } const entry = params.config.entries[params.id]; if (entry?.enabled === false) { - return { + return toPluginActivationState({ enabled: false, activated: false, explicitlyEnabled: explicitSelection.explicitlyEnabled, source: "disabled", - reason: "disabled in config", - }; + cause: "disabled-in-config", + }); } const explicitlyAllowed = params.config.allow.includes(params.id); if (params.origin === "workspace" && !explicitlyAllowed && entry?.enabled !== true) { - return { + return toPluginActivationState({ enabled: false, activated: false, explicitlyEnabled: explicitSelection.explicitlyEnabled, source: "disabled", - reason: "workspace plugin (disabled by default)", - }; + cause: "workspace-disabled-by-default", + }); } if (params.config.slots.memory === params.id) { - return { + return toPluginActivationState({ enabled: true, activated: true, explicitlyEnabled: true, source: "explicit", - reason: "selected memory slot", - }; + cause: "selected-memory-slot", + }); } if (params.config.allow.length > 0 && !explicitlyAllowed && !explicitlyConfiguredBundledChannel) { - return { + return toPluginActivationState({ enabled: false, activated: false, explicitlyEnabled: explicitSelection.explicitlyEnabled, source: "disabled", - reason: "not in allowlist", - }; + cause: "not-in-allowlist", + }); } if (explicitSelection.explicitlyEnabled) { - return { + return toPluginActivationState({ enabled: true, activated: true, explicitlyEnabled: true, source: "explicit", - reason: explicitSelection.reason, - }; + cause: explicitSelection.cause, + }); } if (params.autoEnabledReason) { - return { + return toPluginActivationState({ enabled: true, activated: true, explicitlyEnabled: false, source: "auto", reason: params.autoEnabledReason, - }; + }); } if (entry?.enabled === true) { - return { + return toPluginActivationState({ enabled: true, activated: true, explicitlyEnabled: false, source: "auto", - reason: "enabled by effective config", - }; + cause: "enabled-by-effective-config", + }); } if ( params.origin === "bundled" && isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id) ) { - return { + return toPluginActivationState({ enabled: true, activated: true, explicitlyEnabled: false, source: "auto", - reason: "channel configured", - }; + cause: "bundled-channel-configured", + }); } if (params.origin === "bundled" && params.enabledByDefault === true) { - return { + return toPluginActivationState({ enabled: true, activated: true, explicitlyEnabled: false, source: "default", - reason: "bundled default enablement", - }; + cause: "bundled-default-enablement", + }); } if (params.origin === "bundled") { - return { + return toPluginActivationState({ enabled: false, activated: false, explicitlyEnabled: false, source: "disabled", - reason: "bundled (disabled by default)", - }; + cause: "bundled-disabled-by-default", + }); } - return { + return toPluginActivationState({ enabled: true, activated: true, explicitlyEnabled: explicitSelection.explicitlyEnabled, source: "default", - }; + }); } export function resolveEnableState( diff --git a/src/plugins/providers.runtime.ts b/src/plugins/providers.runtime.ts index 150ce7b7e09..29f1f12e980 100644 --- a/src/plugins/providers.runtime.ts +++ b/src/plugins/providers.runtime.ts @@ -1,9 +1,5 @@ -import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; -import { - withBundledPluginAllowlistCompat, - withBundledPluginEnablementCompat, -} from "./bundled-compat.js"; +import { resolvePluginActivationInputs } from "./activation-context.js"; import { resolveRuntimePluginRegistry, type PluginLoadOptions } from "./loader.js"; import { createPluginLoaderLogger } from "./logger.js"; import { @@ -28,42 +24,44 @@ export function resolvePluginProviders(params: { pluginSdkResolution?: PluginLoadOptions["pluginSdkResolution"]; }): ProviderPlugin[] { const env = params.env ?? process.env; - const autoEnabled = - params.config !== undefined - ? applyPluginAutoEnable({ - config: params.config, - env, - }) - : undefined; - const autoEnabledConfig = autoEnabled?.config; + const autoEnabled = resolvePluginActivationInputs({ + rawConfig: params.config, + env, + applyAutoEnable: true, + }); const bundledProviderCompatPluginIds = params.bundledProviderAllowlistCompat || params.bundledProviderVitestCompat ? resolveBundledProviderCompatPluginIds({ - config: autoEnabledConfig, + config: autoEnabled.config, workspaceDir: params.workspaceDir, env, onlyPluginIds: params.onlyPluginIds, }) : []; - const maybeAllowlistCompat = params.bundledProviderAllowlistCompat - ? withBundledPluginAllowlistCompat({ - config: autoEnabledConfig, - pluginIds: bundledProviderCompatPluginIds, - }) - : autoEnabledConfig; - const allowlistCompatConfig = params.bundledProviderAllowlistCompat - ? withBundledPluginEnablementCompat({ - config: maybeAllowlistCompat, - pluginIds: bundledProviderCompatPluginIds, - }) - : maybeAllowlistCompat; + const activation = resolvePluginActivationInputs({ + rawConfig: params.config, + resolvedConfig: autoEnabled.config, + autoEnabledReasons: autoEnabled.autoEnabledReasons, + env, + compat: { + allowlistPluginIds: params.bundledProviderAllowlistCompat + ? bundledProviderCompatPluginIds + : undefined, + enablementPluginIds: params.bundledProviderAllowlistCompat + ? bundledProviderCompatPluginIds + : undefined, + vitestPluginIds: params.bundledProviderVitestCompat + ? bundledProviderCompatPluginIds + : undefined, + }, + }); const config = params.bundledProviderVitestCompat ? withBundledProviderVitestCompat({ - config: allowlistCompatConfig, + config: activation.config, pluginIds: bundledProviderCompatPluginIds, env, }) - : allowlistCompatConfig; + : activation.config; const providerPluginIds = resolveEnabledProviderPluginIds({ config, workspaceDir: params.workspaceDir, @@ -72,8 +70,8 @@ export function resolvePluginProviders(params: { }); const registry = resolveRuntimePluginRegistry({ config, - activationSourceConfig: params.config, - autoEnabledReasons: autoEnabled?.autoEnabledReasons, + activationSourceConfig: activation.activationSourceConfig, + autoEnabledReasons: activation.autoEnabledReasons, workspaceDir: params.workspaceDir, env, onlyPluginIds: providerPluginIds, diff --git a/src/plugins/web-fetch-providers.shared.ts b/src/plugins/web-fetch-providers.shared.ts index 6d77d20977c..ef9e9967372 100644 --- a/src/plugins/web-fetch-providers.shared.ts +++ b/src/plugins/web-fetch-providers.shared.ts @@ -1,11 +1,6 @@ -import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; -import { - withBundledPluginAllowlistCompat, - withBundledPluginEnablementCompat, - withBundledPluginVitestCompat, -} from "./bundled-compat.js"; +import { resolvePluginActivationInputs } from "./activation-context.js"; import { resolveBundledWebFetchPluginIds } from "./bundled-web-fetch.js"; -import { normalizePluginsConfig, type NormalizedPluginsConfig } from "./config-state.js"; +import { type NormalizedPluginsConfig } from "./config-state.js"; import type { PluginLoadOptions } from "./loader.js"; import type { PluginWebFetchProviderEntry } from "./types.js"; @@ -58,39 +53,32 @@ export function resolveBundledWebFetchResolutionConfig(params: { activationSourceConfig?: PluginLoadOptions["config"]; autoEnabledReasons: Record; } { - const autoEnabled = - params.config !== undefined - ? applyPluginAutoEnable({ - config: params.config, - env: params.env ?? process.env, - }) - : undefined; - const autoEnabledConfig = autoEnabled?.config; + const autoEnabled = resolvePluginActivationInputs({ + rawConfig: params.config, + env: params.env, + applyAutoEnable: true, + }); const bundledCompatPluginIds = resolveBundledWebFetchCompatPluginIds({ - config: autoEnabledConfig, + config: autoEnabled.config, workspaceDir: params.workspaceDir, env: params.env, }); - const allowlistCompat = params.bundledAllowlistCompat - ? withBundledPluginAllowlistCompat({ - config: autoEnabledConfig, - pluginIds: bundledCompatPluginIds, - }) - : autoEnabledConfig; - const enablementCompat = withBundledPluginEnablementCompat({ - config: allowlistCompat, - pluginIds: bundledCompatPluginIds, - }); - const config = withBundledPluginVitestCompat({ - config: enablementCompat, - pluginIds: bundledCompatPluginIds, + const activation = resolvePluginActivationInputs({ + rawConfig: params.config, + resolvedConfig: autoEnabled.config, + autoEnabledReasons: autoEnabled.autoEnabledReasons, env: params.env, + compat: { + allowlistPluginIds: params.bundledAllowlistCompat ? bundledCompatPluginIds : undefined, + enablementPluginIds: bundledCompatPluginIds, + vitestPluginIds: bundledCompatPluginIds, + }, }); return { - config, - normalized: normalizePluginsConfig(config?.plugins), - activationSourceConfig: params.config, - autoEnabledReasons: autoEnabled?.autoEnabledReasons ?? {}, + config: activation.config, + normalized: activation.normalized, + activationSourceConfig: activation.activationSourceConfig, + autoEnabledReasons: activation.autoEnabledReasons, }; } diff --git a/src/plugins/web-search-providers.shared.ts b/src/plugins/web-search-providers.shared.ts index fd94c48de12..9fd2aae88b8 100644 --- a/src/plugins/web-search-providers.shared.ts +++ b/src/plugins/web-search-providers.shared.ts @@ -1,11 +1,6 @@ -import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; -import { - withBundledPluginAllowlistCompat, - withBundledPluginEnablementCompat, - withBundledPluginVitestCompat, -} from "./bundled-compat.js"; +import { resolvePluginActivationInputs } from "./activation-context.js"; import { resolveBundledWebSearchPluginIds } from "./bundled-web-search.js"; -import { normalizePluginsConfig, type NormalizedPluginsConfig } from "./config-state.js"; +import { type NormalizedPluginsConfig } from "./config-state.js"; import type { PluginLoadOptions } from "./loader.js"; import type { PluginWebSearchProviderEntry } from "./types.js"; @@ -58,39 +53,32 @@ export function resolveBundledWebSearchResolutionConfig(params: { activationSourceConfig?: PluginLoadOptions["config"]; autoEnabledReasons: Record; } { - const autoEnabled = - params.config !== undefined - ? applyPluginAutoEnable({ - config: params.config, - env: params.env ?? process.env, - }) - : undefined; - const autoEnabledConfig = autoEnabled?.config; + const autoEnabled = resolvePluginActivationInputs({ + rawConfig: params.config, + env: params.env, + applyAutoEnable: true, + }); const bundledCompatPluginIds = resolveBundledWebSearchCompatPluginIds({ - config: autoEnabledConfig, + config: autoEnabled.config, workspaceDir: params.workspaceDir, env: params.env, }); - const allowlistCompat = params.bundledAllowlistCompat - ? withBundledPluginAllowlistCompat({ - config: autoEnabledConfig, - pluginIds: bundledCompatPluginIds, - }) - : autoEnabledConfig; - const enablementCompat = withBundledPluginEnablementCompat({ - config: allowlistCompat, - pluginIds: bundledCompatPluginIds, - }); - const config = withBundledPluginVitestCompat({ - config: enablementCompat, - pluginIds: bundledCompatPluginIds, + const activation = resolvePluginActivationInputs({ + rawConfig: params.config, + resolvedConfig: autoEnabled.config, + autoEnabledReasons: autoEnabled.autoEnabledReasons, env: params.env, + compat: { + allowlistPluginIds: params.bundledAllowlistCompat ? bundledCompatPluginIds : undefined, + enablementPluginIds: bundledCompatPluginIds, + vitestPluginIds: bundledCompatPluginIds, + }, }); return { - config, - normalized: normalizePluginsConfig(config?.plugins), - activationSourceConfig: params.config, - autoEnabledReasons: autoEnabled?.autoEnabledReasons ?? {}, + config: activation.config, + normalized: activation.normalized, + activationSourceConfig: activation.activationSourceConfig, + autoEnabledReasons: activation.autoEnabledReasons, }; }