diff --git a/src/plugins/config-activation-shared.ts b/src/plugins/config-activation-shared.ts index 2de75808037..e0cf4401d50 100644 --- a/src/plugins/config-activation-shared.ts +++ b/src/plugins/config-activation-shared.ts @@ -12,6 +12,298 @@ type EnableStateParamsLike = { type PluginKindLike = string | readonly string[] | undefined; +export type PluginActivationSource = "disabled" | "explicit" | "auto" | "default"; + +export type PluginExplicitSelectionCause = + | "enabled-in-config" + | "bundled-channel-enabled-in-config" + | "selected-memory-slot" + | "selected-context-engine-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 PluginActivationStateLike = { + enabled: boolean; + activated: boolean; + explicitlyEnabled: boolean; + source: PluginActivationSource; + reason?: string; +}; + +export type PluginActivationDecision = PluginActivationStateLike & { + cause?: PluginActivationCause; +}; + +type PluginActivationConfigLike = { + enabled: boolean; + allow: readonly string[]; + deny: readonly string[]; + slots: { + memory?: string | null; + contextEngine?: string | null; + }; + entries: Record; +}; + +export type PluginActivationConfigSourceLike = { + plugins: PluginActivationConfigLike; + rootConfig?: TRootConfig; +}; + +export 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-context-engine-slot": "selected context engine 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)", +}; + +export function resolvePluginActivationReason( + cause?: PluginActivationCause, + reason?: string, +): string | undefined { + if (reason) { + return reason; + } + return cause ? PLUGIN_ACTIVATION_REASON_BY_CAUSE[cause] : undefined; +} + +export function toPluginActivationState( + decision: PluginActivationDecision, +): PluginActivationStateLike { + return { + enabled: decision.enabled, + activated: decision.activated, + explicitlyEnabled: decision.explicitlyEnabled, + source: decision.source, + reason: resolvePluginActivationReason(decision.cause, decision.reason), + }; +} + +function resolveExplicitPluginSelectionShared(params: { + id: string; + origin: string; + config: PluginActivationConfigLike; + rootConfig?: TRootConfig; + isBundledChannelEnabledByChannelConfig: ( + rootConfig: TRootConfig | undefined, + pluginId: string, + ) => boolean; +}): { explicitlyEnabled: boolean; cause?: PluginExplicitSelectionCause } { + if (params.config.entries[params.id]?.enabled === true) { + return { explicitlyEnabled: true, cause: "enabled-in-config" }; + } + if ( + params.origin === "bundled" && + params.isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id) + ) { + return { explicitlyEnabled: true, cause: "bundled-channel-enabled-in-config" }; + } + if (params.config.slots.memory === params.id) { + return { explicitlyEnabled: true, cause: "selected-memory-slot" }; + } + if (params.config.slots.contextEngine === params.id) { + return { explicitlyEnabled: true, cause: "selected-context-engine-slot" }; + } + if (params.origin !== "bundled" && params.config.allow.includes(params.id)) { + return { explicitlyEnabled: true, cause: "selected-in-allowlist" }; + } + return { explicitlyEnabled: false }; +} + +export function resolvePluginActivationDecisionShared(params: { + id: string; + origin: string; + config: PluginActivationConfigLike; + rootConfig?: TRootConfig; + enabledByDefault?: boolean; + activationSource?: PluginActivationConfigSourceLike; + autoEnabledReason?: string; + allowBundledChannelExplicitBypassesAllowlist?: boolean; + isBundledChannelEnabledByChannelConfig: ( + rootConfig: TRootConfig | undefined, + pluginId: string, + ) => boolean; +}): PluginActivationDecision { + const activationSource = params.activationSource ?? { + plugins: params.config, + rootConfig: params.rootConfig, + }; + const explicitSelection = resolveExplicitPluginSelectionShared({ + id: params.id, + origin: params.origin, + config: activationSource.plugins, + rootConfig: activationSource.rootConfig, + isBundledChannelEnabledByChannelConfig: params.isBundledChannelEnabledByChannelConfig, + }); + + if (!params.config.enabled) { + return { + enabled: false, + activated: false, + explicitlyEnabled: explicitSelection.explicitlyEnabled, + source: "disabled", + cause: "plugins-disabled", + }; + } + if (params.config.deny.includes(params.id)) { + return { + enabled: false, + activated: false, + explicitlyEnabled: explicitSelection.explicitlyEnabled, + source: "disabled", + cause: "blocked-by-denylist", + }; + } + const entry = params.config.entries[params.id]; + if (entry?.enabled === false) { + return { + enabled: false, + activated: false, + explicitlyEnabled: explicitSelection.explicitlyEnabled, + source: "disabled", + cause: "disabled-in-config", + }; + } + const explicitlyAllowed = params.config.allow.includes(params.id); + if ( + params.origin === "workspace" && + !explicitlyAllowed && + entry?.enabled !== true && + explicitSelection.cause !== "selected-context-engine-slot" + ) { + return { + enabled: false, + activated: false, + explicitlyEnabled: explicitSelection.explicitlyEnabled, + source: "disabled", + cause: "workspace-disabled-by-default", + }; + } + if (params.config.slots.memory === params.id) { + return { + enabled: true, + activated: true, + explicitlyEnabled: true, + source: "explicit", + cause: "selected-memory-slot", + }; + } + if (params.config.slots.contextEngine === params.id) { + return { + enabled: true, + activated: true, + explicitlyEnabled: true, + source: "explicit", + cause: "selected-context-engine-slot", + }; + } + if ( + params.allowBundledChannelExplicitBypassesAllowlist === true && + explicitSelection.cause === "bundled-channel-enabled-in-config" + ) { + return { + enabled: true, + activated: true, + explicitlyEnabled: true, + source: "explicit", + cause: explicitSelection.cause, + }; + } + if (params.config.allow.length > 0 && !explicitlyAllowed) { + return { + enabled: false, + activated: false, + explicitlyEnabled: explicitSelection.explicitlyEnabled, + source: "disabled", + cause: "not-in-allowlist", + }; + } + if (explicitSelection.explicitlyEnabled) { + return { + enabled: true, + activated: true, + explicitlyEnabled: true, + source: "explicit", + cause: explicitSelection.cause, + }; + } + if (params.autoEnabledReason) { + return { + enabled: true, + activated: true, + explicitlyEnabled: false, + source: "auto", + reason: params.autoEnabledReason, + }; + } + if (entry?.enabled === true) { + return { + enabled: true, + activated: true, + explicitlyEnabled: false, + source: "auto", + cause: "enabled-by-effective-config", + }; + } + if ( + params.origin === "bundled" && + params.isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id) + ) { + return { + enabled: true, + activated: true, + explicitlyEnabled: false, + source: "auto", + cause: "bundled-channel-configured", + }; + } + if (params.origin === "bundled" && params.enabledByDefault === true) { + return { + enabled: true, + activated: true, + explicitlyEnabled: false, + source: "default", + cause: "bundled-default-enablement", + }; + } + if (params.origin === "bundled") { + return { + enabled: false, + activated: false, + explicitlyEnabled: false, + source: "disabled", + cause: "bundled-disabled-by-default", + }; + } + return { + enabled: true, + activated: true, + explicitlyEnabled: explicitSelection.explicitlyEnabled, + source: "default", + }; +} + export function toEnableStateResult(state: EnableStateLike): { enabled: boolean; reason?: string } { return state.enabled ? { enabled: true } : { enabled: false, reason: state.reason }; } @@ -30,6 +322,29 @@ export function resolveEnableStateShared( return resolveEnableStateResult(params, resolveState); } +export function createPluginEnableStateResolver( + resolveState: (params: { + id: string; + origin: TOrigin; + config: TConfig; + enabledByDefault?: boolean; + }) => EnableStateLike, +): ( + id: string, + origin: TOrigin, + config: TConfig, + enabledByDefault?: boolean, +) => { enabled: boolean; reason?: string } { + return (id, origin, config, enabledByDefault) => + resolveEnableStateResult({ id, origin, config, enabledByDefault }, resolveState); +} + +export function createEffectiveEnableStateResolver( + resolveState: (params: TParams) => EnableStateLike, +): (params: TParams) => { enabled: boolean; reason?: string } { + return (params) => resolveEnableStateResult(params, resolveState); +} + function hasKind(kind: PluginKindLike, target: string): boolean { if (!kind) { return false; diff --git a/src/plugins/config-policy.ts b/src/plugins/config-policy.ts index ddc7030e239..5d706517047 100644 --- a/src/plugins/config-policy.ts +++ b/src/plugins/config-policy.ts @@ -1,8 +1,12 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; import { + createEffectiveEnableStateResolver, + createPluginEnableStateResolver, resolveMemorySlotDecisionShared, - resolveEnableStateShared, - resolveEnableStateResult, + resolvePluginActivationDecisionShared, + toPluginActivationState, + type PluginActivationSource, + type PluginActivationStateLike, } from "./config-activation-shared.js"; import { hasExplicitPluginConfig as hasExplicitPluginConfigShared, @@ -15,15 +19,8 @@ import { import type { PluginKind } from "./plugin-kind.types.js"; import type { PluginOrigin } from "./plugin-origin.types.js"; -export type PluginActivationSource = "disabled" | "explicit" | "auto" | "default"; - -export type PluginActivationState = { - enabled: boolean; - activated: boolean; - explicitlyEnabled: boolean; - source: PluginActivationSource; - reason?: string; -}; +export type { PluginActivationSource }; +export type PluginActivationState = PluginActivationStateLike; export type NormalizedPluginsConfig = SharedNormalizedPluginsConfig; @@ -34,33 +31,6 @@ export function normalizePluginsConfigWithResolver( return normalizePluginsConfigWithResolverShared(config, normalizePluginId); } -function resolveExplicitPluginSelection(params: { - id: string; - origin: PluginOrigin; - config: NormalizedPluginsConfig; - rootConfig?: OpenClawConfig; -}): { explicitlyEnabled: boolean; reason?: string } { - if (params.config.entries[params.id]?.enabled === true) { - return { explicitlyEnabled: true, reason: "enabled in config" }; - } - if ( - params.origin === "bundled" && - isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id) - ) { - return { explicitlyEnabled: true, reason: "channel enabled in config" }; - } - if (params.config.slots.memory === params.id) { - return { explicitlyEnabled: true, reason: "selected memory slot" }; - } - if (params.config.slots.contextEngine === params.id) { - return { explicitlyEnabled: true, reason: "selected context engine slot" }; - } - if (params.origin !== "bundled" && params.config.allow.includes(params.id)) { - return { explicitlyEnabled: true, reason: "selected in allowlist" }; - } - return { explicitlyEnabled: false }; -} - export function resolvePluginActivationState(params: { id: string; origin: PluginOrigin; @@ -71,170 +41,27 @@ export function resolvePluginActivationState(params: { sourceRootConfig?: OpenClawConfig; autoEnabledReason?: string; }): PluginActivationState { - const explicitSelection = resolveExplicitPluginSelection({ - id: params.id, - origin: params.origin, - config: params.sourceConfig ?? params.config, - rootConfig: params.sourceRootConfig ?? params.rootConfig, - }); - - if (!params.config.enabled) { - return { - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - reason: "plugins disabled", - }; - } - if (params.config.deny.includes(params.id)) { - return { - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - reason: "blocked by denylist", - }; - } - const entry = params.config.entries[params.id]; - if (entry?.enabled === false) { - return { - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - reason: "disabled in config", - }; - } - const explicitlyAllowed = params.config.allow.includes(params.id); - if ( - params.origin === "workspace" && - !explicitlyAllowed && - entry?.enabled !== true && - explicitSelection.reason !== "selected context engine slot" - ) { - return { - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - reason: "workspace plugin (disabled by default)", - }; - } - if (params.config.slots.memory === params.id) { - return { - enabled: true, - activated: true, - explicitlyEnabled: true, - source: "explicit", - reason: "selected memory slot", - }; - } - if (params.config.slots.contextEngine === params.id) { - return { - enabled: true, - activated: true, - explicitlyEnabled: true, - source: "explicit", - reason: "selected context engine slot", - }; - } - if (params.config.allow.length > 0 && !explicitlyAllowed) { - return { - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - reason: "not in allowlist", - }; - } - if (explicitSelection.explicitlyEnabled) { - return { - enabled: true, - activated: true, - explicitlyEnabled: true, - source: "explicit", - reason: explicitSelection.reason, - }; - } - if (params.autoEnabledReason) { - return { - enabled: true, - activated: true, - explicitlyEnabled: false, - source: "auto", - reason: params.autoEnabledReason, - }; - } - if (entry?.enabled === true) { - return { - enabled: true, - activated: true, - explicitlyEnabled: false, - source: "auto", - reason: "enabled by effective config", - }; - } - if ( - params.origin === "bundled" && - isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id) - ) { - return { - enabled: true, - activated: true, - explicitlyEnabled: false, - source: "auto", - reason: "channel configured", - }; - } - if (params.origin === "bundled" && params.enabledByDefault === true) { - return { - enabled: true, - activated: true, - explicitlyEnabled: false, - source: "default", - reason: "bundled default enablement", - }; - } - if (params.origin === "bundled") { - return { - enabled: false, - activated: false, - explicitlyEnabled: false, - source: "disabled", - reason: "bundled (disabled by default)", - }; - } - return { - enabled: true, - activated: true, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "default", - }; -} -export function hasExplicitPluginConfig(plugins?: OpenClawConfig["plugins"]): boolean { - return hasExplicitPluginConfigShared(plugins); -} -export function resolveEnableState( - id: string, - origin: PluginOrigin, - config: NormalizedPluginsConfig, - enabledByDefault?: boolean, -): { enabled: boolean; reason?: string } { - return resolveEnableStateShared( - { id, origin, config, enabledByDefault }, - resolvePluginActivationState, + return toPluginActivationState( + resolvePluginActivationDecisionShared({ + ...params, + activationSource: { + plugins: params.sourceConfig ?? params.config, + rootConfig: params.sourceRootConfig ?? params.rootConfig, + }, + isBundledChannelEnabledByChannelConfig, + }), ); } +export const hasExplicitPluginConfig = hasExplicitPluginConfigShared; -export function isBundledChannelEnabledByChannelConfig( - cfg: OpenClawConfig | undefined, - pluginId: string, -): boolean { - return isBundledChannelEnabledByChannelConfigShared(cfg, pluginId); -} +export const resolveEnableState = createPluginEnableStateResolver< + NormalizedPluginsConfig, + PluginOrigin +>(resolvePluginActivationState); -export function resolveEffectiveEnableState(params: { +export const isBundledChannelEnabledByChannelConfig = isBundledChannelEnabledByChannelConfigShared; + +type PolicyEffectiveActivationParams = { id: string; origin: PluginOrigin; config: NormalizedPluginsConfig; @@ -243,20 +70,16 @@ export function resolveEffectiveEnableState(params: { sourceConfig?: NormalizedPluginsConfig; sourceRootConfig?: OpenClawConfig; autoEnabledReason?: string; -}): { enabled: boolean; reason?: string } { - return resolveEnableStateResult(params, resolveEffectivePluginActivationState); -} +}; -export function resolveEffectivePluginActivationState(params: { - id: string; - origin: PluginOrigin; - config: NormalizedPluginsConfig; - rootConfig?: OpenClawConfig; - enabledByDefault?: boolean; - sourceConfig?: NormalizedPluginsConfig; - sourceRootConfig?: OpenClawConfig; - autoEnabledReason?: string; -}): PluginActivationState { +export const resolveEffectiveEnableState = + createEffectiveEnableStateResolver( + resolveEffectivePluginActivationState, + ); + +export function resolveEffectivePluginActivationState( + params: PolicyEffectiveActivationParams, +): PluginActivationState { return resolvePluginActivationState(params); } diff --git a/src/plugins/config-state.ts b/src/plugins/config-state.ts index 1ab246b3413..c74af6c1e51 100644 --- a/src/plugins/config-state.ts +++ b/src/plugins/config-state.ts @@ -4,9 +4,14 @@ import { normalizeOptionalString, } from "../shared/string-coerce.js"; import { + createEffectiveEnableStateResolver, + createPluginEnableStateResolver, resolveMemorySlotDecisionShared, - resolveEnableStateShared, - resolveEnableStateResult, + resolvePluginActivationDecisionShared, + toPluginActivationState, + type PluginActivationConfigSourceLike, + type PluginActivationSource, + type PluginActivationStateLike, } from "./config-activation-shared.js"; import { hasExplicitPluginConfig as hasExplicitPluginConfigShared, @@ -17,48 +22,13 @@ import { import { loadPluginManifestRegistry } from "./manifest-registry.js"; import type { PluginOrigin } from "./plugin-origin.types.js"; -export type PluginActivationSource = "disabled" | "explicit" | "auto" | "default"; - -export type PluginExplicitSelectionCause = - | "enabled-in-config" - | "bundled-channel-enabled-in-config" - | "selected-memory-slot" - | "selected-context-engine-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; - explicitlyEnabled: boolean; - source: PluginActivationSource; - reason?: string; -}; - -type PluginActivationDecision = { - enabled: boolean; - activated: boolean; - explicitlyEnabled: boolean; - source: PluginActivationSource; - cause?: PluginActivationCause; - reason?: string; -}; +export type { PluginActivationSource }; +export type PluginActivationState = PluginActivationStateLike; export type PluginActivationConfigSource = { plugins: NormalizedPluginsConfig; rootConfig?: OpenClawConfig; -}; +} & PluginActivationConfigSourceLike; export type NormalizedPluginsConfig = SharedNormalizedPluginsConfig; @@ -101,43 +71,6 @@ export function normalizePluginId(id: string): string { return getBundledPluginAliasLookup().get(normalized) ?? 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-context-engine-slot": "selected context engine 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), - }; -} - export const normalizePluginsConfig = ( config?: OpenClawConfig["plugins"], ): NormalizedPluginsConfig => { @@ -215,33 +148,6 @@ export function isTestDefaultMemorySlotDisabled( return true; } -function resolveExplicitPluginSelection(params: { - id: string; - origin: PluginOrigin; - config: NormalizedPluginsConfig; - rootConfig?: OpenClawConfig; -}): { explicitlyEnabled: boolean; cause?: PluginExplicitSelectionCause } { - if (params.config.entries[params.id]?.enabled === true) { - return { explicitlyEnabled: true, cause: "enabled-in-config" }; - } - if ( - params.origin === "bundled" && - isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id) - ) { - return { explicitlyEnabled: true, cause: "bundled-channel-enabled-in-config" }; - } - if (params.config.slots.memory === params.id) { - return { explicitlyEnabled: true, cause: "selected-memory-slot" }; - } - if (params.config.slots.contextEngine === params.id) { - return { explicitlyEnabled: true, cause: "selected-context-engine-slot" }; - } - if (params.origin !== "bundled" && params.config.allow.includes(params.id)) { - return { explicitlyEnabled: true, cause: "selected-in-allowlist" }; - } - return { explicitlyEnabled: false }; -} - export function resolvePluginActivationState(params: { id: string; origin: PluginOrigin; @@ -251,200 +157,49 @@ export function resolvePluginActivationState(params: { activationSource?: PluginActivationConfigSource; autoEnabledReason?: string; }): PluginActivationState { - const activationSource = - params.activationSource ?? - createPluginActivationSource({ - config: params.rootConfig, - plugins: params.config, - }); - const explicitSelection = resolveExplicitPluginSelection({ - id: params.id, - origin: params.origin, - config: activationSource.plugins, - rootConfig: activationSource.rootConfig, - }); - - if (!params.config.enabled) { - return toPluginActivationState({ - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - cause: "plugins-disabled", - }); - } - if (params.config.deny.includes(params.id)) { - return toPluginActivationState({ - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - cause: "blocked-by-denylist", - }); - } - const entry = params.config.entries[params.id]; - if (entry?.enabled === false) { - return toPluginActivationState({ - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - cause: "disabled-in-config", - }); - } - const explicitlyAllowed = params.config.allow.includes(params.id); - if ( - params.origin === "workspace" && - !explicitlyAllowed && - entry?.enabled !== true && - explicitSelection.cause !== "selected-context-engine-slot" - ) { - return toPluginActivationState({ - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - cause: "workspace-disabled-by-default", - }); - } - if (params.config.slots.memory === params.id) { - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: true, - source: "explicit", - cause: "selected-memory-slot", - }); - } - if (params.config.slots.contextEngine === params.id) { - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: true, - source: "explicit", - cause: "selected-context-engine-slot", - }); - } - if (explicitSelection.cause === "bundled-channel-enabled-in-config") { - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: true, - source: "explicit", - cause: explicitSelection.cause, - }); - } - if (params.config.allow.length > 0 && !explicitlyAllowed) { - return toPluginActivationState({ - enabled: false, - activated: false, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "disabled", - cause: "not-in-allowlist", - }); - } - if (explicitSelection.explicitlyEnabled) { - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: true, - source: "explicit", - cause: explicitSelection.cause, - }); - } - if (params.autoEnabledReason) { - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: false, - source: "auto", - reason: params.autoEnabledReason, - }); - } - if (entry?.enabled === true) { - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: false, - source: "auto", - cause: "enabled-by-effective-config", - }); - } - if ( - params.origin === "bundled" && - isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id) - ) { - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: false, - source: "auto", - cause: "bundled-channel-configured", - }); - } - if (params.origin === "bundled" && params.enabledByDefault === true) { - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: false, - source: "default", - cause: "bundled-default-enablement", - }); - } - if (params.origin === "bundled") { - return toPluginActivationState({ - enabled: false, - activated: false, - explicitlyEnabled: false, - source: "disabled", - cause: "bundled-disabled-by-default", - }); - } - return toPluginActivationState({ - enabled: true, - activated: true, - explicitlyEnabled: explicitSelection.explicitlyEnabled, - source: "default", - }); -} - -export function resolveEnableState( - id: string, - origin: PluginOrigin, - config: NormalizedPluginsConfig, - enabledByDefault?: boolean, -): { enabled: boolean; reason?: string } { - return resolveEnableStateShared( - { id, origin, config, enabledByDefault }, - resolvePluginActivationState, + return toPluginActivationState( + resolvePluginActivationDecisionShared({ + ...params, + activationSource: + params.activationSource ?? + createPluginActivationSource({ + config: params.rootConfig, + plugins: params.config, + }), + allowBundledChannelExplicitBypassesAllowlist: true, + isBundledChannelEnabledByChannelConfig, + }), ); } -export function isBundledChannelEnabledByChannelConfig( - cfg: OpenClawConfig | undefined, - pluginId: string, -): boolean { - return isBundledChannelEnabledByChannelConfigShared(cfg, pluginId); -} +export const resolveEnableState = createPluginEnableStateResolver< + NormalizedPluginsConfig, + PluginOrigin +>(resolvePluginActivationState); -export function resolveEffectiveEnableState(params: { +export const isBundledChannelEnabledByChannelConfig = isBundledChannelEnabledByChannelConfigShared; + +type EffectiveActivationParams = { id: string; origin: PluginOrigin; config: NormalizedPluginsConfig; rootConfig?: OpenClawConfig; enabledByDefault?: boolean; activationSource?: PluginActivationConfigSource; -}): { enabled: boolean; reason?: string } { - return resolveEnableStateResult(params, resolveEffectivePluginActivationState); -} +}; + +export const resolveEffectiveEnableState = + createEffectiveEnableStateResolver( + resolveEffectivePluginActivationState, + ); export function resolveEffectivePluginActivationState(params: { - id: string; - origin: PluginOrigin; - config: NormalizedPluginsConfig; - rootConfig?: OpenClawConfig; - enabledByDefault?: boolean; - activationSource?: PluginActivationConfigSource; + id: EffectiveActivationParams["id"]; + origin: EffectiveActivationParams["origin"]; + config: EffectiveActivationParams["config"]; + rootConfig?: EffectiveActivationParams["rootConfig"]; + enabledByDefault?: EffectiveActivationParams["enabledByDefault"]; + activationSource?: EffectiveActivationParams["activationSource"]; autoEnabledReason?: string; }): PluginActivationState { return resolvePluginActivationState(params);