

























@@ -44,9 +44,16 @@ export type ContextEngineFactory = (
4444ctx: ContextEngineFactoryContext,
4545) => ContextEngine | Promise<ContextEngine>;
4646export type ContextEngineRegistrationResult = { ok: true } | { ok: false; existingOwner: string };
47+export type ContextEngineRegistrationLifecycle = "runtime" | "readOnlyDiscovery";
48+export type ContextEngineRegistration = {
49+factory: ContextEngineFactory;
50+owner: string;
51+lifecycle: ContextEngineRegistrationLifecycle;
52+};
47534854type RegisterContextEngineForOwnerOptions = {
4955allowSameOwnerRefresh?: boolean;
56+lifecycle?: ContextEngineRegistrationLifecycle;
5057};
51585259const LEGACY_SESSION_KEY_COMPAT = Symbol.for("openclaw.contextEngine.sessionKeyCompat");
@@ -389,13 +396,7 @@ export type ContextEngineRuntimeQuarantine = {
389396};
390397391398type ContextEngineRegistryState = {
392-engines: Map<
393-string,
394-{
395-factory: ContextEngineFactory;
396-owner: string;
397-}
398->;
399+engines: Map<string, ContextEngineRegistration>;
399400quarantinedEngines: Map<string, ContextEngineRuntimeQuarantine>;
400401};
401402@@ -512,6 +513,7 @@ export function registerContextEngineForOwner(
512513opts?: RegisterContextEngineForOwnerOptions,
513514): ContextEngineRegistrationResult {
514515const normalizedOwner = requireContextEngineOwner(owner);
516+const lifecycle = opts?.lifecycle ?? "runtime";
515517const registry = getContextEngineRegistryState().engines;
516518const existing = registry.get(id);
517519if (
@@ -524,11 +526,18 @@ export function registerContextEngineForOwner(
524526if (existing && existing.owner !== normalizedOwner) {
525527return { ok: false, existingOwner: existing.owner };
526528}
529+if (existing?.lifecycle === "runtime" && lifecycle === "readOnlyDiscovery") {
530+// Read-only discovery may re-run after live activation. It can collect metadata, but it must
531+// not replace the runtime-safe factory with a closure that captured a read-only plugin mode.
532+return { ok: true };
533+}
527534if (existing && opts?.allowSameOwnerRefresh !== true) {
528535return { ok: false, existingOwner: existing.owner };
529536}
530-registry.set(id, { factory, owner: normalizedOwner });
531-clearContextEngineRuntimeQuarantine(id);
537+registry.set(id, { factory, owner: normalizedOwner, lifecycle });
538+if (lifecycle === "runtime") {
539+clearContextEngineRuntimeQuarantine(id);
540+}
532541return { ok: true };
533542}
534543@@ -550,7 +559,13 @@ export function registerContextEngine(
550559 * Return the factory for a registered engine, or undefined.
551560 */
552561export function getContextEngineFactory(id: string): ContextEngineFactory | undefined {
553-return getContextEngineRegistryState().engines.get(id)?.factory;
562+const registration = getContextEngineRegistration(id);
563+return registration?.lifecycle === "runtime" ? registration.factory : undefined;
564+}
565+566+/** Returns registration metadata so callers can distinguish discovery snapshots from runtime entries. */
567+export function getContextEngineRegistration(id: string): ContextEngineRegistration | undefined {
568+return getContextEngineRegistryState().engines.get(id);
554569}
555570556571/**
@@ -945,6 +960,13 @@ export async function resolveContextEngine(
945960return resolveDefaultContextEngine(defaultEngineId, factoryCtx);
946961}
947962963+if (!isDefaultEngine && entry.lifecycle === "readOnlyDiscovery") {
964+console.warn(
965+`[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.`,
966+);
967+return resolveDefaultContextEngine(defaultEngineId, factoryCtx);
968+}
969+948970let engine: ContextEngine;
949971try {
950972engine = await entry.factory(factoryCtx);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。