Skip to content

Commit 7d74d56

Browse files
张小波张小波
authored andcommitted
fix: persist and restore registered context engines in plugin cache
Fixes issue #77063: lossless-claw selected and enabled but not registered as context engine after plugin cache restore. Root cause: When a cached plugin registry is reused, the cached state restoration restores agent harnesses, commands, compaction providers, and memory embedding providers, but NOT registered context engines. This causes context engines registered by plugins (like lossless-claw) to be lost when the plugin loader uses cached state. Changes: - src/context-engine/registry.ts: Add RegisteredContextEngineEntry type, listRegisteredContextEngines(), and restoreRegisteredContextEngines() functions for cache snapshot/restore support - src/plugins/loader.ts: Add contextEngines field to CachedPluginState, call restoreRegisteredContextEngines() when using cached state, and save contextEngines to cache when storing registry
1 parent 412ce1b commit 7d74d56

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

src/context-engine/registry.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,49 @@ export function clearContextEnginesForOwner(owner: string): void {
424424
}
425425
}
426426

427+
/**
428+
* A serializable snapshot of a registered context engine for cache restore.
429+
*/
430+
export type RegisteredContextEngineEntry = {
431+
id: string;
432+
factory: ContextEngineFactory;
433+
owner: string;
434+
};
435+
436+
/**
437+
* List all registered context engines with their factories and owners.
438+
* Used for caching and restoring plugin state.
439+
*/
440+
export function listRegisteredContextEngines(): RegisteredContextEngineEntry[] {
441+
const entries: RegisteredContextEngineEntry[] = [];
442+
for (const [id, entry] of getContextEngineRegistryState().engines.entries()) {
443+
entries.push({ id, factory: entry.factory, owner: entry.owner });
444+
}
445+
return entries;
446+
}
447+
448+
/**
449+
* Restore previously cached context engines. Clears existing plugin-owned engines
450+
* first, then re-registers all entries from the cache.
451+
*/
452+
export function restoreRegisteredContextEngines(entries: RegisteredContextEngineEntry[]): void {
453+
// Clear all plugin-owned context engines first
454+
const registry = getContextEngineRegistryState().engines;
455+
for (const [id, entry] of registry.entries()) {
456+
if (entry.owner.startsWith("plugin:")) {
457+
registry.delete(id);
458+
}
459+
}
460+
// Restore all cached entries
461+
for (const { id, factory, owner } of entries) {
462+
if (id === defaultSlotIdForKey("contextEngine") && owner !== CORE_CONTEXT_ENGINE_OWNER) {
463+
// Skip restoring the default engine slot with a non-core owner
464+
continue;
465+
}
466+
registry.set(id, { factory, owner });
467+
}
468+
}
469+
427470
function describeResolvedContextEngineContractError(
428471
engineId: string,
429472
engine: unknown,

src/plugins/loader.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import {
88
} from "../agents/harness/registry.js";
99
import type { OpenClawConfig } from "../config/types.openclaw.js";
1010
import type { PluginInstallRecord } from "../config/types.plugins.js";
11+
import {
12+
listRegisteredContextEngines,
13+
restoreRegisteredContextEngines,
14+
type RegisteredContextEngineEntry,
15+
} from "../context-engine/registry.js";
1116
import type { GatewayRequestHandler } from "../gateway/server-methods/types.js";
1217
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
1318
import { createSubsystemLogger } from "../logging/subsystem.js";
@@ -244,6 +249,7 @@ type CachedPluginState = {
244249
compactionProviders: ReturnType<typeof listRegisteredCompactionProviders>;
245250
memoryEmbeddingProviders: ReturnType<typeof listRegisteredMemoryEmbeddingProviders>;
246251
memoryPromptSupplements: ReturnType<typeof listMemoryPromptSupplements>;
252+
contextEngines: RegisteredContextEngineEntry[];
247253
};
248254

249255
const MAX_PLUGIN_REGISTRY_CACHE_ENTRIES = 128;
@@ -1482,6 +1488,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
14821488
);
14831489
restorePluginInteractiveHandlers(cached.state.interactiveHandlers ?? []);
14841490
restoreRegisteredMemoryEmbeddingProviders(cached.state.memoryEmbeddingProviders);
1491+
restoreRegisteredContextEngines(cached.state.contextEngines);
14851492
restoreMemoryPluginState({
14861493
capability: cached.state.memoryCapability,
14871494
corpusSupplements: cached.state.memoryCorpusSupplements,
@@ -2400,6 +2407,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
24002407
compactionProviders: listRegisteredCompactionProviders(),
24012408
memoryEmbeddingProviders: listRegisteredMemoryEmbeddingProviders(),
24022409
memoryPromptSupplements: listMemoryPromptSupplements(),
2410+
contextEngines: listRegisteredContextEngines(),
24032411
},
24042412
onlyPluginIds,
24052413
);

0 commit comments

Comments
 (0)