// Context-engine registry owns engine registration, resolution, compatibility, and quarantine. import { sanitizeForLog } from "../../packages/terminal-core/src/ansi.js"; import type { OpenClawConfig } from "../config/types.js"; import { runPluginCleanup } from "../plugins/plugin-instance-scope.js"; import type { ContextEngineFactory, ContextEngineFactoryContext, ContextEngineRegistration, ContextEngineRegistrationLifecycle, } from "../plugins/registry-contribution-types.js"; import type { PluginRegistry } from "../plugins/registry-types.js"; import { getActivePluginRegistry, requireActivePluginRegistry } from "../plugins/runtime.js"; import { defaultSlotIdForKey } from "../plugins/slots.js"; import { getAsyncWorkSignal } from "../shared/async-work-scope.js"; import { createDeferredCore } from "../shared/deferred.js"; import { resolveGlobalSingleton } from "../shared/global-singleton.js"; import { inheritRuntimeCompactionDelegate, markRuntimeCompactionDelegate, } from "./compaction-watchdog.js"; import { contextEngineAbortSignal, isContextEngineAbortRejection } from "./context-engine-abort.js"; import { clearPersistedContextEngineQuarantineForProcess, listPersistedContextEngineQuarantines, recordPersistedContextEngineQuarantine, } from "./quarantine-health.js"; import { pluginIdFromContextEngineOwner } from "./registry-adoption.js"; import { describeResolvedContextEngineContractError, projectContextEngineHostParams, } from "./registry-contract.js"; import { resolveEffectiveContextEngineId } from "./registry-selection.js"; import { recordContextEngineRegistrationSource, createContextEngineWithResources, disposeContextEngineSources, resolveContextEngineFactory, retainLogicalTurnContextEngineSources, runContextEngineFactoryResolution, type ContextEngineFactoryResources, } from "./registry.resources.js"; import type { BootstrapResult, ContextEngine, ContextEngineMaintenanceResult, IngestBatchResult, IngestResult, } from "./types.js"; export type { ContextEngineFactory } from "../plugins/registry-contribution-types.js"; /** * Runtime context passed to context engine factories during resolution. * Provides config and path information so plugins can initialize engines * without fragile workarounds. */ type ContextEngineRegistrationResult = { ok: true } | { ok: false; existingOwner: string }; type RegisterContextEngineForOwnerOptions = { allowSameOwnerRefresh?: boolean; lifecycle?: ContextEngineRegistrationLifecycle; }; type GuardedContextEngineMethodName = Exclude; type GuardedContextEngineMethod = (...args: never[]) => unknown; const GUARDED_CONTEXT_ENGINE_METHODS = new Set( "bootstrap maintain ingest ingestBatch afterTurn commitTurn assemble compact prepareSubagentSpawn onSubagentEnded".split( " ", ), ); type ResolvedContextEngineMetadata = { owner: string; engineId: string; sourceEngine?: ContextEngine; source?: ContextEngineFactoryResources; ownsSource?: boolean; }; const resolvedEngineMetadata = new WeakMap(); function inheritCompactionWatchdogOwnership( property: PropertyKey, source: GuardedContextEngineMethod, wrapped: GuardedContextEngineMethod, ): GuardedContextEngineMethod { if (property !== "compact") { return wrapped; } // SAFETY: the compact property narrows both functions to the ContextEngine compact contract. const compact = source as ContextEngine["compact"]; // SAFETY: guarded compact wrappers preserve the source method's single-parameter contract. const wrappedCompact = wrapped as ContextEngine["compact"]; return inheritRuntimeCompactionDelegate(compact, wrappedCompact); } function wrapResolvedContextEngine( rawEngine: ContextEngine, metadata: ResolvedContextEngineMetadata & { factory: ContextEngineFactory; defaultEngineId?: string; factoryCtx?: ContextEngineFactoryContext; }, ): ContextEngine { let disposal: Promise | undefined; const source = metadata.source; const engine = source?.wrap(rawEngine) ?? rawEngine; const fallback = metadata.defaultEngineId && metadata.factoryCtx && metadata.engineId !== metadata.defaultEngineId ? { defaultEngineId: metadata.defaultEngineId, factoryCtx: metadata.factoryCtx } : undefined; let fallbackEnginePromise: Promise | undefined; let resolvedFallbackEngine: ContextEngine | undefined; const getFallbackEngine = fallback ? async () => { if (disposal) { throw new Error("Context engine has been disposed"); } const resolve = () => resolveDefaultContextEngine(fallback.defaultEngineId, fallback.factoryCtx); // Failed factories return before cleanup; capture that work in this engine's source owner. return await (fallbackEnginePromise ??= (source ? source.run(resolve) : resolve()).then( (resolved) => { resolvedFallbackEngine = resolved; return resolved; }, )); } : undefined; const disposeOwned = () => { if (disposal) { return disposal; } const completion = createDeferredCore(); disposal = completion.promise; void (async () => { // Join only a fallback already admitted before closure; disposal must never create one. const fallbackResult: PromiseSettledResult = fallbackEnginePromise && !resolvedFallbackEngine ? (await Promise.allSettled([fallbackEnginePromise]))[0]! : { status: "fulfilled", value: resolvedFallbackEngine }; const fallbackEngine = fallbackResult.status === "fulfilled" ? fallbackResult.value : undefined; const sources = metadata.ownsSource && source ? [source] : []; const shared = fallbackEngine && hasSameContextEngineInstance(wrapped, fallbackEngine); const fallbackSource = fallbackEngine && resolvedEngineMetadata.get(fallbackEngine)?.source; if (shared && fallbackSource) { sources.push(fallbackSource); } // The fallback is a child of this factory; start its disposer before closing the parent signal. const fallbackCleanup = (async () => { if (!shared) { await fallbackEngine?.dispose?.(); } })(); // Shared raw engines dispose once with both source claims held; independent cleanup all runs. const results = await Promise.allSettled([ (async () => { if (source && !metadata.ownsSource) { await source.runCleanup(() => rawEngine.dispose?.()); } else { await disposeContextEngineSources(rawEngine, sources, () => source ? rawEngine.dispose?.() : runPluginCleanup(metadata.factory, () => rawEngine.dispose?.()), ); } })(), fallbackCleanup, ]); for (const result of [...results, fallbackResult]) { if (result.status === "rejected") { throw result.reason; } } })().then(completion.resolve, completion.reject); return disposal; }; // A fresh target keeps Proxy invariants compatible with frozen engines and private getters. const wrapped = new Proxy( Object.create(engine, { info: { get: () => engine.info } }) as ContextEngine, { get(_target, property) { if (property === "dispose" && (source || fallback)) { return disposeOwned; } if (property === "info") { if (!fallback || !getContextEngineQuarantine(metadata.engineId)) { return engine.info; } return ( resolvedFallbackEngine?.info ?? { id: fallback.defaultEngineId, name: fallback.defaultEngineId === "legacy" ? "Legacy Context Engine" : `${fallback.defaultEngineId} Context Engine`, } ); } // Disposal keeps its registered owner while ordinary methods retain their normal fences. const invokeMember = (run: () => T): T => property === "dispose" ? runPluginCleanup(metadata.factory, run) : run(); const method = invokeMember(() => Reflect.get(engine, property, engine)); if (typeof method !== "function") { return method; } if (!GUARDED_CONTEXT_ENGINE_METHODS.has(property)) { return (...args: unknown[]) => invokeMember(() => Reflect.apply(method, engine, args)); } const methodName = property as GuardedContextEngineMethodName; if (!fallback || !getFallbackEngine) { const invoke = (params: Record) => method.call(engine, projectContextEngineHostParams(engine, methodName, params)); return inheritCompactionWatchdogOwnership(property, method, invoke); } const invokeFallback = async (methodParams: Record) => { contextEngineAbortSignal(methodParams); return await invokeFallbackContextEngineMethod({ getFallbackEngine, methodName, methodParams, }); }; if (getContextEngineQuarantine(metadata.engineId)) { return methodName === "compact" ? markRuntimeCompactionDelegate(invokeFallback as ContextEngine["compact"]) // SAFETY: compact keeps this parameter contract. : invokeFallback; } const invoke = async (methodParams: Record) => { const abortSignal = contextEngineAbortSignal(methodParams); if (getContextEngineQuarantine(metadata.engineId)) { // Runtime failures downgrade future guarded calls for this process. return await invokeFallback(methodParams); } try { return await method.call( engine, projectContextEngineHostParams(engine, methodName, methodParams), ); } catch (error) { if (isContextEngineAbortRejection(error, abortSignal)) { // Abort is caller intent, not engine instability; never quarantine for it. throw error; } recordContextEngineQuarantine({ engineId: metadata.engineId, owner: metadata.owner, operation: methodName, error, defaultEngineId: fallback.defaultEngineId, }); if (methodName === "compact" || methodName === "prepareSubagentSpawn") { throw error; } return await invokeFallback(methodParams).catch(() => { throw error; }); } }; return inheritCompactionWatchdogOwnership(property, method, invoke); }, }, ); resolvedEngineMetadata.set(wrapped, { ...metadata, sourceEngine: metadata.sourceEngine ?? resolvedEngineMetadata.get(engine)?.sourceEngine ?? engine, }); return wrapped; } // --------------------------------------------------------------------------- // Registry (module-level singleton) // --------------------------------------------------------------------------- const CONTEXT_ENGINE_REGISTRY_STATE = Symbol.for("openclaw.contextEngineRegistryState"); const CORE_CONTEXT_ENGINE_OWNER = "core"; type ContextEngineRuntimeQuarantine = { engineId: string; owner?: string; operation: string; reason: string; failedAt: Date; }; type ContextEngineRegistryState = { quarantinedEngines: Map; }; // Keep context-engine registrations process-global so duplicated dist chunks // still share one registry map at runtime. const contextEngineRegistryState = resolveGlobalSingleton( CONTEXT_ENGINE_REGISTRY_STATE, () => ({ quarantinedEngines: new Map(), }), ); const getContextEngines = () => requireActivePluginRegistry().contextEngines; function requireContextEngineOwner(owner: string): string { const normalizedOwner = owner.trim(); if (!normalizedOwner) { throw new Error( `registerContextEngineForOwner: owner must be a non-empty string, got ${JSON.stringify(owner)}`, ); } return normalizedOwner; } function recordContextEngineQuarantine(params: { engineId: string; owner?: string; operation: string; error: unknown; defaultEngineId: string; }): ContextEngineRuntimeQuarantine { const existing = contextEngineRegistryState.quarantinedEngines.get(params.engineId); if (existing) { // First failure wins so logs and diagnostics point at the root cause, not follow-on fallback use. return existing; } const quarantine: ContextEngineRuntimeQuarantine = { engineId: params.engineId, operation: params.operation, reason: params.error instanceof Error ? params.error.message : String(params.error), failedAt: new Date(), ...(params.owner ? { owner: params.owner } : {}), }; contextEngineRegistryState.quarantinedEngines.set(params.engineId, quarantine); try { recordPersistedContextEngineQuarantine(quarantine); } catch { // Quarantine behavior must not depend on the best-effort health mirror. } const ownerSuffix = params.owner ? ` owner=${sanitizeForLog(params.owner)}` : ""; console.error( `[context-engine] Context engine "${sanitizeForLog(params.engineId)}"${ownerSuffix} failed during ${sanitizeForLog(params.operation)}: ` + `${sanitizeForLog(quarantine.reason)}; quarantining it for this process and falling back to default engine "${params.defaultEngineId}".`, ); return quarantine; } function getContextEngineQuarantine(engineId: string): ContextEngineRuntimeQuarantine | undefined { return contextEngineRegistryState.quarantinedEngines.get(engineId); } export function listContextEngineQuarantines(): ContextEngineRuntimeQuarantine[] { const quarantines = Array.from( contextEngineRegistryState.quarantinedEngines.values(), ({ failedAt, ...quarantine }) => ({ ...quarantine, failedAt: new Date(failedAt) }), ); const seenEngineIds = new Set(quarantines.map((entry) => entry.engineId)); return quarantines.concat( listPersistedContextEngineQuarantines().filter(({ engineId }) => !seenEngineIds.has(engineId)), ); } function clearContextEngineRuntimeQuarantine(engineId: string): void { contextEngineRegistryState.quarantinedEngines.delete(engineId); clearPersistedContextEngineQuarantineForProcess(engineId, process.pid); } /** * Register a context engine implementation under an explicit trusted owner. */ export function registerContextEngineForOwner( id: string, factory: ContextEngineFactory, owner: string, opts?: RegisterContextEngineForOwnerOptions, ): ContextEngineRegistrationResult { const targetRegistry = requireActivePluginRegistry(); const result = registerContextEngineInRegistry(targetRegistry, id, factory, owner, opts); if ( result.ok && (opts?.lifecycle ?? "runtime") === "runtime" && getActivePluginRegistry() === targetRegistry ) { clearContextEngineRuntimeQuarantine(id); } return result; } /** Registers an engine in a registry value while that value is being assembled. */ export function registerContextEngineInRegistry( pluginRegistry: PluginRegistry, id: string, factory: ContextEngineFactory, owner: string, opts?: RegisterContextEngineForOwnerOptions, ): ContextEngineRegistrationResult { const normalizedOwner = requireContextEngineOwner(owner); const lifecycle = opts?.lifecycle ?? "runtime"; const registry = pluginRegistry.contextEngines; const existing = registry.get(id); if ( id === defaultSlotIdForKey("contextEngine") && normalizedOwner !== CORE_CONTEXT_ENGINE_OWNER ) { // The default fallback id is core-owned; plugins can select other ids through slots. return { ok: false, existingOwner: CORE_CONTEXT_ENGINE_OWNER }; } if (existing && existing.owner !== normalizedOwner) { return { ok: false, existingOwner: existing.owner }; } if (existing?.lifecycle === "runtime" && lifecycle === "readOnlyDiscovery") { // Read-only discovery may re-run after live activation. It can collect metadata, but it must // not replace the runtime-safe factory with a closure that captured a read-only plugin mode. return { ok: true }; } if (existing && opts?.allowSameOwnerRefresh !== true) { return { ok: false, existingOwner: existing.owner }; } const registration = { factory, owner: normalizedOwner, lifecycle }; recordContextEngineRegistrationSource(registration, pluginRegistry); registry.set(id, registration); return { ok: true }; } export { adoptRuntimeContextEngineRegistrations } from "./registry-adoption.js"; /** Clear runtime quarantine only after a complete builder-local registry becomes active. */ export function activateContextEngineRegistrations(pluginRegistry: PluginRegistry): void { for (const [id, registration] of pluginRegistry.contextEngines) { if (registration.lifecycle === "runtime") { clearContextEngineRuntimeQuarantine(id); } } } /** Returns registration metadata so callers can distinguish discovery snapshots from runtime entries. */ export function getContextEngineRegistration(id: string): ContextEngineRegistration | undefined { return getContextEngines().get(id); } const listContextEngineIds = () => [...getContextEngines().keys()].toSorted(); /** * Return the trusted plugin id that registered a resolved context engine. * Downgraded engines intentionally report no plugin owner. */ export function resolveContextEngineOwnerPluginId( engine: ContextEngine | undefined | null, ): string | undefined { const metadata = engine ? resolvedEngineMetadata.get(engine) : undefined; // Downgraded work belongs to its core-owned fallback, never the disabled plugin. const owner = metadata && !getContextEngineQuarantine(metadata.engineId) ? metadata.owner : undefined; return owner ? pluginIdFromContextEngineOwner(owner) : undefined; } export const hasSameContextEngineInstance = (left: ContextEngine, right: ContextEngine): boolean => (resolvedEngineMetadata.get(left)?.sourceEngine ?? left) === (resolvedEngineMetadata.get(right)?.sourceEngine ?? right); const CONTEXT_ENGINE_FALLBACK_RESULTS = { bootstrap: { bootstrapped: false, reason: "context engine downgraded to legacy" }, maintain: { changed: false, bytesFreed: 0, rewrittenEntries: 0, reason: "context engine downgraded to legacy", }, ingest: { ingested: false }, ingestBatch: { ingestedCount: 0 }, } as const satisfies { bootstrap: BootstrapResult; maintain: ContextEngineMaintenanceResult; ingest: IngestResult; ingestBatch: IngestBatchResult; }; export { isContextEngineAbortRejection }; async function invokeFallbackContextEngineMethod(params: { getFallbackEngine: () => Promise; methodName: GuardedContextEngineMethodName; methodParams: unknown; }): Promise { const fallbackEngine = await params.getFallbackEngine(); const fallbackMethod = fallbackEngine[params.methodName] as | ((methodParams: unknown) => unknown) | undefined; if (typeof fallbackMethod === "function") { return await fallbackMethod.call(fallbackEngine, params.methodParams); } if (params.methodName === "assemble" || params.methodName === "compact") { throw new Error(`No legacy fallback result for ${params.methodName}`); } const fallbackResult = CONTEXT_ENGINE_FALLBACK_RESULTS[ params.methodName as keyof typeof CONTEXT_ENGINE_FALLBACK_RESULTS ]; return fallbackResult ? { ...fallbackResult } : undefined; } // --------------------------------------------------------------------------- // Resolution // --------------------------------------------------------------------------- /** * Options for {@link resolveContextEngine}. */ export type ResolveContextEngineOptions = { agentDir?: string; workspaceDir?: string; onCleanupFailure?: () => void; }; export type ResolvedContextEngineRef = Readonly<{ engine: ContextEngine; registeredId: string; ownerPluginId?: string; }>; export type LogicalTurnContextEngineResolution = { configured: ResolvedContextEngineRef; configuredId: string; configuredFailure?: string; fallback: ResolvedContextEngineRef; sourceResources?: ReadonlyMap; }; function resolvedContextEngineRef(params: { engine: ContextEngine; registeredId: string; owner: string; }): ResolvedContextEngineRef { const pluginId = pluginIdFromContextEngineOwner(params.owner); return Object.freeze({ engine: params.engine, registeredId: params.registeredId, ...(pluginId ? { ownerPluginId: pluginId } : {}), }); } async function createOwnedContextEngine( engineId: string, entry: ContextEngineRegistration, factoryCtx: ContextEngineFactoryContext, options: { defaultEngineId?: string; onValidation?: () => void; contractErrorPrefix?: string; source?: ContextEngineFactoryResources; ownsSource?: boolean; } = {}, ): Promise { let engine: ContextEngine | undefined; try { engine = await entry.factory(factoryCtx); options.onValidation?.(); const contractError = describeResolvedContextEngineContractError(engineId, engine); if (contractError) { throw new Error(`${options.contractErrorPrefix ?? ""}${contractError}`); } return wrapResolvedContextEngine(engine, { sourceEngine: resolvedEngineMetadata.get(engine)?.sourceEngine ?? engine, source: options.source, ownsSource: options.ownsSource, engineId, owner: entry.owner, factory: entry.factory, defaultEngineId: options.defaultEngineId, factoryCtx, }); } catch (error) { const dispose = () => engine?.dispose?.(); await Promise.resolve() .then(() => options.source ? options.source.runCleanup(dispose) : runPluginCleanup(entry.factory, dispose), ) .catch(() => undefined); throw error; } } async function resolveRawContextEngineRef( engineId: string, factoryCtx: ContextEngineFactoryContext, entry: ContextEngineRegistration | undefined, source: ContextEngineFactoryResources | undefined, ): Promise { if (!entry) { throw new Error( `Context engine "${engineId}" is not registered. ` + `Available engines: ${listContextEngineIds().join(", ") || "(none)"}`, ); } return resolvedContextEngineRef({ engine: await createOwnedContextEngine(engineId, entry, factoryCtx, { source }), registeredId: engineId, owner: entry.owner, }); } /** * Resolve fresh engines for one logical turn without consulting or mutating * process quarantine. A failed configured engine is retried by the next turn. */ export async function resolveLogicalTurnContextEngines( config?: OpenClawConfig, options?: ResolveContextEngineOptions, ): Promise { return await runContextEngineFactoryResolution(async (abandon) => { const defaultEngineId = defaultSlotIdForKey("contextEngine"); const configuredEngineId = resolveEffectiveContextEngineId(config, getContextEngines()); const factoryCtx: ContextEngineFactoryContext = { config, agentDir: options?.agentDir, workspaceDir: options?.workspaceDir, }; const registry = requireActivePluginRegistry(); const entries = registry.contextEngines; const fallbackEntry = entries.get(defaultEngineId); const configuredEntry = entries.get(configuredEngineId); const sources = retainLogicalTurnContextEngineSources( registry, fallbackEntry, configuredEngineId === defaultEngineId ? undefined : configuredEntry, abandon, ); const sourceResources = new Map(); let fallback: ResolvedContextEngineRef; try { fallback = await resolveContextEngineFactory(sources.fallback, sourceResources, () => resolveRawContextEngineRef(defaultEngineId, factoryCtx, fallbackEntry, sources.fallback), ); } catch (error) { abandon(sources.fallback); abandon(sources.configured); throw error; } if (configuredEngineId === defaultEngineId) { return { configured: fallback, configuredId: configuredEngineId, fallback, sourceResources }; } if (!configuredEntry || configuredEntry.lifecycle === "readOnlyDiscovery") { return { configured: fallback, configuredId: configuredEngineId, configuredFailure: !configuredEntry ? `context engine "${configuredEngineId}" is not registered` : `context engine "${configuredEngineId}" is available for discovery only`, fallback, sourceResources, }; } try { if (sources.configuredFailure) { throw sources.configuredFailure.error; } const configured = await resolveContextEngineFactory( sources.configured, sourceResources, () => resolveRawContextEngineRef( configuredEngineId, factoryCtx, configuredEntry, sources.configured, ), ); return { configured, configuredId: configuredEngineId, fallback, sourceResources }; } catch (error) { abandon(sources.configured); return { configured: fallback, configuredId: configuredEngineId, configuredFailure: error instanceof Error ? error.message : String(error), fallback, sourceResources, }; } }, options?.onCleanupFailure); } /** * Resolve which ContextEngine to use based on plugin slot configuration. * * Resolution order: * 1. `config.plugins.slots.contextEngine` when its plugin policy permits it * 2. Default slot value ("legacy") * * When `config` is provided it is forwarded to the factory as part of a * {@link ContextEngineFactoryContext}. Additional runtime paths can be * supplied via `options`. Existing no-arg factories continue to work * because JavaScript permits extra arguments at call sites. * * Non-default engines that fail (unregistered, factory throw, or contract * violation) are logged and silently replaced by the default engine. * Host admission/resource failures and owner cancellation propagate without quarantine. * Default-engine failures also propagate. */ export async function resolveContextEngine( config?: OpenClawConfig, options?: ResolveContextEngineOptions, ): Promise { const defaultEngineId = defaultSlotIdForKey("contextEngine"); const engineId = resolveEffectiveContextEngineId(config, getContextEngines()); const isDefaultEngine = engineId === defaultEngineId; const factoryCtx: ContextEngineFactoryContext = { config, agentDir: options?.agentDir, workspaceDir: options?.workspaceDir, }; const quarantine = !isDefaultEngine ? getContextEngineQuarantine(engineId) : undefined; if (quarantine) { // Previously failed custom engines stay downgraded until explicit quarantine clear/restart. return resolveDefaultContextEngine(defaultEngineId, factoryCtx); } const entry = getContextEngines().get(engineId); if (!entry) { if (isDefaultEngine) { throw new Error( `Context engine "${engineId}" is not registered. ` + `Available engines: ${listContextEngineIds().join(", ") || "(none)"}`, ); } recordContextEngineQuarantine({ engineId, operation: "resolve", error: "not registered", defaultEngineId, }); return resolveDefaultContextEngine(defaultEngineId, factoryCtx); } if (!isDefaultEngine && entry.lifecycle === "readOnlyDiscovery") { console.warn( `[context-engine] Context engine "${engineId}" owner=${entry.owner} is registered for read-only discovery only; falling back to default engine "${defaultEngineId}" without quarantine until runtime activation registers it.`, ); return resolveDefaultContextEngine(defaultEngineId, factoryCtx); } const abortSignal = getAsyncWorkSignal(); let operation: "factory" | "contract-validation" | undefined; try { return await createContextEngineWithResources( requireActivePluginRegistry(), entry, (source) => { // Admission and source retention belong to the host, not the plugin factory. operation = "factory"; return createOwnedContextEngine(engineId, entry, factoryCtx, { source, ownsSource: true, defaultEngineId, onValidation: () => { operation = "contract-validation"; }, }); }, ); } catch (error) { if (isDefaultEngine || !operation || isContextEngineAbortRejection(error, abortSignal)) { throw error; } recordContextEngineQuarantine({ engineId, owner: entry.owner, operation, error, defaultEngineId, }); return resolveDefaultContextEngine(defaultEngineId, factoryCtx); } } /** Default-engine failures propagate; they cannot select another fallback. */ async function resolveDefaultContextEngine( defaultEngineId: string, factoryCtx: ContextEngineFactoryContext, ): Promise { const defaultEntry = getContextEngines().get(defaultEngineId); if (!defaultEntry) { throw new Error( `[context-engine] fallback failed: default engine "${defaultEngineId}" is not registered. ` + `Available engines: ${listContextEngineIds().join(", ") || "(none)"}`, ); } return await createContextEngineWithResources( requireActivePluginRegistry(), defaultEntry, (source) => createOwnedContextEngine(defaultEngineId, defaultEntry, factoryCtx, { source, ownsSource: true, contractErrorPrefix: "[context-engine] ", }), ); }