import type { OpenClawConfig } from "../config/config.js"; import { defaultSlotIdForKey } from "../plugins/slots.js"; import type { ContextEngine } from "./types.js"; /** * A factory that creates a ContextEngine instance. * Supports async creation for engines that need DB connections etc. */ export type ContextEngineFactory = () => ContextEngine | Promise; // --------------------------------------------------------------------------- // Registry (module-level singleton) // --------------------------------------------------------------------------- const CONTEXT_ENGINE_REGISTRY_STATE = Symbol.for("openclaw.contextEngineRegistryState"); type ContextEngineRegistryState = { engines: Map; }; // Keep context-engine registrations process-global so duplicated dist chunks // still share one registry map at runtime. function getContextEngineRegistryState(): ContextEngineRegistryState { const globalState = globalThis as typeof globalThis & { [CONTEXT_ENGINE_REGISTRY_STATE]?: ContextEngineRegistryState; }; if (!globalState[CONTEXT_ENGINE_REGISTRY_STATE]) { globalState[CONTEXT_ENGINE_REGISTRY_STATE] = { engines: new Map(), }; } return globalState[CONTEXT_ENGINE_REGISTRY_STATE]; } /** * Register a context engine implementation under the given id. */ export function registerContextEngine(id: string, factory: ContextEngineFactory): void { getContextEngineRegistryState().engines.set(id, factory); } /** * Return the factory for a registered engine, or undefined. */ export function getContextEngineFactory(id: string): ContextEngineFactory | undefined { return getContextEngineRegistryState().engines.get(id); } /** * List all registered engine ids. */ export function listContextEngineIds(): string[] { return [...getContextEngineRegistryState().engines.keys()]; } // --------------------------------------------------------------------------- // Resolution // --------------------------------------------------------------------------- /** * Resolve which ContextEngine to use based on plugin slot configuration. * * Resolution order: * 1. `config.plugins.slots.contextEngine` (explicit slot override) * 2. Default slot value ("legacy") * * Throws if the resolved engine id has no registered factory. */ export async function resolveContextEngine(config?: OpenClawConfig): Promise { const slotValue = config?.plugins?.slots?.contextEngine; const engineId = typeof slotValue === "string" && slotValue.trim() ? slotValue.trim() : defaultSlotIdForKey("contextEngine"); const factory = getContextEngineRegistryState().engines.get(engineId); if (!factory) { throw new Error( `Context engine "${engineId}" is not registered. ` + `Available engines: ${listContextEngineIds().join(", ") || "(none)"}`, ); } return factory(); }