From df5f2cb77f0666f0db1578e956331f49bd51ba0f Mon Sep 17 00:00:00 2001 From: Jari Mustonen Date: Mon, 9 Mar 2026 15:42:51 +0200 Subject: [PATCH] fix(plugins): restore memory prompt builder on cached plugin loads --- src/memory/prompt-section.ts | 10 ++++++++++ src/plugins/loader.ts | 29 +++++++++++++++++++++-------- src/plugins/registry.ts | 2 +- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/memory/prompt-section.ts b/src/memory/prompt-section.ts index e1fe0470b8d..a130340cfea 100644 --- a/src/memory/prompt-section.ts +++ b/src/memory/prompt-section.ts @@ -23,6 +23,16 @@ export function buildMemoryPromptSection(params: { return _builder?.(params) ?? []; } +/** Return the current builder (used by the plugin cache to snapshot state). */ +export function getMemoryPromptSectionBuilder(): MemoryPromptSectionBuilder | undefined { + return _builder; +} + +/** Restore a previously-snapshotted builder (used on plugin cache hits). */ +export function restoreMemoryPromptSection(builder: MemoryPromptSectionBuilder | undefined): void { + _builder = builder; +} + /** Clear the registered builder (called on plugin reload and in tests). */ export function clearMemoryPromptSection(): void { _builder = undefined; diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index b4b9e8edb1e..e0d5374a6d8 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -9,8 +9,12 @@ import type { PluginInstallRecord } from "../config/types.plugins.js"; import type { GatewayRequestHandler } from "../gateway/server-methods/types.js"; import { openBoundaryFileSync } from "../infra/boundary-file-read.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; +import { + clearMemoryPromptSection, + getMemoryPromptSectionBuilder, + restoreMemoryPromptSection, +} from "../memory/prompt-section.js"; import { resolveUserPath } from "../utils.js"; -import { clearMemoryPromptSection } from "../memory/prompt-section.js"; import { inspectBundleMcpRuntimeSupport } from "./bundle-mcp.js"; import { clearPluginCommands } from "./commands.js"; import { @@ -91,8 +95,13 @@ export class PluginLoadFailureError extends Error { } } +type CachedPluginState = { + registry: PluginRegistry; + memoryPromptBuilder: ReturnType; +}; + const MAX_PLUGIN_REGISTRY_CACHE_ENTRIES = 128; -const registryCache = new Map(); +const registryCache = new Map(); const openAllowlistWarningCache = new Set(); const LAZY_RUNTIME_REFLECTION_KEYS = [ "version", @@ -180,7 +189,7 @@ export const __testing = { maxPluginRegistryCacheEntries: MAX_PLUGIN_REGISTRY_CACHE_ENTRIES, }; -function getCachedPluginRegistry(cacheKey: string): PluginRegistry | undefined { +function getCachedPluginRegistry(cacheKey: string): CachedPluginState | undefined { const cached = registryCache.get(cacheKey); if (!cached) { return undefined; @@ -191,11 +200,11 @@ function getCachedPluginRegistry(cacheKey: string): PluginRegistry | undefined { return cached; } -function setCachedPluginRegistry(cacheKey: string, registry: PluginRegistry): void { +function setCachedPluginRegistry(cacheKey: string, state: CachedPluginState): void { if (registryCache.has(cacheKey)) { registryCache.delete(cacheKey); } - registryCache.set(cacheKey, registry); + registryCache.set(cacheKey, state); while (registryCache.size > MAX_PLUGIN_REGISTRY_CACHE_ENTRIES) { const oldestKey = registryCache.keys().next().value; if (!oldestKey) { @@ -734,10 +743,11 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi if (cacheEnabled) { const cached = getCachedPluginRegistry(cacheKey); if (cached) { + restoreMemoryPromptSection(cached.memoryPromptBuilder); if (shouldActivate) { - activatePluginRegistry(cached, cacheKey); + activatePluginRegistry(cached.registry, cacheKey); } - return cached; + return cached.registry; } } @@ -1289,7 +1299,10 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi maybeThrowOnPluginLoadError(registry, options.throwOnLoadError); if (cacheEnabled) { - setCachedPluginRegistry(cacheKey, registry); + setCachedPluginRegistry(cacheKey, { + registry, + memoryPromptBuilder: getMemoryPromptSectionBuilder(), + }); } if (shouldActivate) { activatePluginRegistry(registry, cacheKey); diff --git a/src/plugins/registry.ts b/src/plugins/registry.ts index b4007ff6067..04154435d30 100644 --- a/src/plugins/registry.ts +++ b/src/plugins/registry.ts @@ -2,13 +2,13 @@ import path from "node:path"; import type { AnyAgentTool } from "../agents/tools/common.js"; import type { ChannelPlugin } from "../channels/plugins/types.js"; import { registerContextEngineForOwner } from "../context-engine/registry.js"; -import { registerMemoryPromptSection } from "../memory/prompt-section.js"; import type { GatewayRequestHandler, GatewayRequestHandlers, } from "../gateway/server-methods/types.js"; import { registerInternalHook } from "../hooks/internal-hooks.js"; import type { HookEntry } from "../hooks/types.js"; +import { registerMemoryPromptSection } from "../memory/prompt-section.js"; import { resolveUserPath } from "../utils.js"; import { registerPluginCommand, validatePluginCommandDefinition } from "./commands.js"; import { normalizePluginHttpPath } from "./http-path.js";