From b96a75c95b54694c8a7e2e8f1204d1bb69a4691f Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 26 Apr 2026 18:54:49 -0700 Subject: [PATCH] fix(gateway): scope memory runtime plugin loading --- src/plugins/memory-runtime.test.ts | 58 ++++++++++++++++++++++++++++++ src/plugins/memory-runtime.ts | 15 +++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/plugins/memory-runtime.test.ts b/src/plugins/memory-runtime.test.ts index 754be95e60c..f02b6a19cd1 100644 --- a/src/plugins/memory-runtime.test.ts +++ b/src/plugins/memory-runtime.test.ts @@ -60,6 +60,7 @@ function expectMemoryRuntimeLoaded(rawConfig: unknown, autoEnabledConfig: unknow expect.objectContaining({ config: autoEnabledConfig, activationSourceConfig: rawConfig, + onlyPluginIds: ["memory-core"], }), ); } @@ -159,6 +160,63 @@ describe("memory runtime auto-enable loading", () => { await expectAutoEnabledMemoryRuntimeCase({ run, expectedResult }); }); + it("loads only the configured memory slot plugin", async () => { + const rawConfig = { + plugins: { + slots: { + memory: "memory-lancedb", + }, + }, + }; + const runtime = createMemoryRuntimeFixture(); + applyPluginAutoEnableMock.mockReturnValue({ + config: rawConfig, + changes: [], + autoEnabledReasons: {}, + }); + getMemoryRuntimeMock.mockReturnValueOnce(undefined).mockReturnValue(runtime); + + await getActiveMemorySearchManager({ + cfg: rawConfig as never, + agentId: "main", + }); + + expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith( + expect.objectContaining({ + onlyPluginIds: ["memory-lancedb"], + }), + ); + }); + + it("does not fall back to broad plugin loading when the memory slot is disabled", async () => { + const rawConfig = { + plugins: { + slots: { + memory: "none", + }, + }, + }; + applyPluginAutoEnableMock.mockReturnValue({ + config: rawConfig, + changes: [], + autoEnabledReasons: {}, + }); + getMemoryRuntimeMock.mockReturnValue(undefined); + + await expect( + getActiveMemorySearchManager({ + cfg: rawConfig as never, + agentId: "main", + }), + ).resolves.toEqual({ manager: null, error: "memory plugin unavailable" }); + + expect(applyPluginAutoEnableMock).toHaveBeenCalledWith({ + config: rawConfig, + env: process.env, + }); + expect(resolveRuntimePluginRegistryMock).not.toHaveBeenCalled(); + }); + it.each([ { name: "does not bootstrap the memory runtime just to close managers", diff --git a/src/plugins/memory-runtime.ts b/src/plugins/memory-runtime.ts index f8fd9566a92..cc5a6fabf8c 100644 --- a/src/plugins/memory-runtime.ts +++ b/src/plugins/memory-runtime.ts @@ -1,4 +1,5 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; +import { normalizePluginsConfig } from "./config-state.js"; import { resolveRuntimePluginRegistry } from "./loader.js"; import { getMemoryRuntime } from "./memory-state.js"; import { @@ -6,13 +7,25 @@ import { resolvePluginRuntimeLoadContext, } from "./runtime/load-context.js"; +function resolveMemoryRuntimePluginIds(config: OpenClawConfig): string[] { + const memorySlot = normalizePluginsConfig(config.plugins).slots.memory; + return typeof memorySlot === "string" && memorySlot.trim().length > 0 ? [memorySlot] : []; +} + function ensureMemoryRuntime(cfg?: OpenClawConfig) { const current = getMemoryRuntime(); if (current || !cfg) { return current; } + const context = resolvePluginRuntimeLoadContext({ config: cfg }); + const onlyPluginIds = resolveMemoryRuntimePluginIds(context.config); + if (onlyPluginIds.length === 0) { + return getMemoryRuntime(); + } resolveRuntimePluginRegistry( - buildPluginRuntimeLoadOptions(resolvePluginRuntimeLoadContext({ config: cfg })), + buildPluginRuntimeLoadOptions(context, { + onlyPluginIds, + }), ); return getMemoryRuntime(); }