From 0b5da95ef4fe519bfee77ce9b465c8a9aa4f04bb Mon Sep 17 00:00:00 2001 From: Sergio Date: Sun, 12 Apr 2026 02:49:03 -0500 Subject: [PATCH] fix(plugins): restore cached memory capability on cache hits --- src/plugins/loader.test.ts | 69 ++++++++++++++++++++++++++++++++++++++ src/plugins/loader.ts | 4 +++ 2 files changed, 73 insertions(+) diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index c6f2fb43a7d..93eafabe015 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -39,7 +39,9 @@ import { } from "./memory-embedding-providers.js"; import { buildMemoryPromptSection, + clearMemoryPluginState, getMemoryRuntime, + listActiveMemoryPublicArtifacts, listMemoryCorpusSupplements, registerMemoryCorpusSupplement, registerMemoryFlushPlanResolver, @@ -1809,6 +1811,73 @@ module.exports = { id: "throws-after-import", register() {} };`, expect(listMemoryEmbeddingProviders()).toEqual([]); }); + it("restores cached memory capability public artifacts on cache hits", async () => { + useNoBundledPlugins(); + const workspaceDir = makeTempDir(); + const absolutePath = path.join(workspaceDir, "MEMORY.md"); + fs.writeFileSync(absolutePath, "# Memory\n"); + const plugin = writePlugin({ + id: "cached-memory-capability", + filename: "cached-memory-capability.cjs", + body: `module.exports = { + id: "cached-memory-capability", + kind: "memory", + register(api) { + api.registerMemoryCapability({ + publicArtifacts: { + async listArtifacts() { + return [{ + kind: "memory-root", + workspaceDir: ${JSON.stringify(workspaceDir)}, + relativePath: "MEMORY.md", + absolutePath: ${JSON.stringify(absolutePath)}, + agentIds: ["main"], + contentType: "markdown", + }]; + }, + }, + }); + }, + };`, + }); + + const options = { + workspaceDir: plugin.dir, + config: { + plugins: { + load: { paths: [plugin.file] }, + allow: ["cached-memory-capability"], + slots: { memory: "cached-memory-capability" }, + }, + }, + onlyPluginIds: ["cached-memory-capability"], + }; + + const expectedArtifacts = [ + { + kind: "memory-root", + workspaceDir, + relativePath: "MEMORY.md", + absolutePath, + agentIds: ["main"], + contentType: "markdown" as const, + }, + ]; + + const first = loadOpenClawPlugins(options); + await expect(listActiveMemoryPublicArtifacts({ cfg: {} as never })).resolves.toEqual( + expectedArtifacts, + ); + + clearMemoryPluginState(); + + const second = loadOpenClawPlugins(options); + expect(second).toBe(first); + await expect(listActiveMemoryPublicArtifacts({ cfg: {} as never })).resolves.toEqual( + expectedArtifacts, + ); + }); + it("throws when activate:false is used without cache:false", () => { expect(() => loadOpenClawPlugins({ activate: false })).toThrow( "activate:false requires cache:false", diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index 5f8f089824d..e172f265e46 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -48,6 +48,7 @@ import { } from "./memory-embedding-providers.js"; import { clearMemoryPluginState, + getMemoryCapabilityRegistration, getMemoryFlushPlanResolver, getMemoryPromptSectionBuilder, getMemoryRuntime, @@ -148,6 +149,7 @@ export class PluginLoadReentryError extends Error { type CachedPluginState = { registry: PluginRegistry; + memoryCapability: ReturnType; memoryCorpusSupplements: ReturnType; agentHarnesses: ReturnType; compactionProviders: ReturnType; @@ -1104,6 +1106,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi restoreRegisteredCompactionProviders(cached.compactionProviders); restoreRegisteredMemoryEmbeddingProviders(cached.memoryEmbeddingProviders); restoreMemoryPluginState({ + capability: cached.memoryCapability, corpusSupplements: cached.memoryCorpusSupplements, promptBuilder: cached.memoryPromptBuilder, promptSupplements: cached.memoryPromptSupplements, @@ -1799,6 +1802,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi if (cacheEnabled) { setCachedPluginRegistry(cacheKey, { + memoryCapability: getMemoryCapabilityRegistration(), memoryCorpusSupplements: listMemoryCorpusSupplements(), registry, agentHarnesses: listRegisteredAgentHarnesses(),