// Configured hook helpers combine config and install records into active hooks. import type { HookInstallRecord } from "../config/types.hooks.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { readConfigMachineState } from "../state/config-machine-state.js"; import { shouldIncludeHook } from "./config.js"; import { resolveHookKey } from "./frontmatter.js"; import type { HookPolicyEntry } from "./types.js"; /** Capture discovery and explicit selection from one config/install snapshot. */ export function resolveInternalHookSelection(config: OpenClawConfig): { configured: boolean; names: Set | null; declaredNames: Set; } { const internal = config.hooks?.internal; const installs = readConfigMachineState>("hooks.internal.installs"); const names = new Set(); const declaredNames = new Set(); let open = (internal?.load?.extraDirs ?? []).some((dir) => dir.trim().length > 0); for (const [name, entry] of Object.entries(internal?.entries ?? {})) { const trimmed = name.trim(); if (trimmed) { declaredNames.add(trimmed); if (entry?.enabled !== false) { names.add(trimmed); } } } for (const [installId, install] of Object.entries(installs ?? {})) { const hookNames = install.hooks ?? []; open ||= hookNames.length === 0 && Boolean(installId.trim()); for (const name of hookNames) { const trimmed = name.trim(); if (trimmed) { declaredNames.add(trimmed); names.add(trimmed); } } } const configured = internal?.enabled !== false && (internal?.enabled === true || Object.values(internal?.entries ?? {}).some((entry) => entry?.enabled !== false) || open || Object.keys(installs ?? {}).length > 0); return { configured, names: internal?.enabled === false ? new Set() : open || (declaredNames.size === 0 && internal?.enabled === true) ? null : names, declaredNames, }; } /** True when an explicit selection names this entry, by hook name or resolved hook key. */ export function isHookNameSelected( names: Set | undefined, entry: HookPolicyEntry, ): boolean { return Boolean(names?.has(entry.hook.name) || names?.has(resolveHookKey(entry.hook.name, entry))); } /** Shared selection and eligibility gate; importing handlers remains the loader's job. */ export function isHookLoadable(params: { entry: HookPolicyEntry; config: OpenClawConfig; names: Set | null; }): boolean { return ( (!params.names || isHookNameSelected(params.names, params.entry)) && shouldIncludeHook({ entry: params.entry, config: params.config }) ); }