From c78defdc2faabb27f49e3e49e9450e79449d562f Mon Sep 17 00:00:00 2001 From: HansY Date: Mon, 6 Apr 2026 12:14:54 +0000 Subject: [PATCH] plugins: exclude runtimeSubagentMode from loader cache key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plugin loader cache key included runtimeSubagentMode, which is derived from allowGatewaySubagentBinding. Since different call sites in the message processing pipeline pass different values for this flag, each call produced a distinct cache key, triggering redundant register() calls (40+ in 24 seconds after startup). runtimeSubagentMode does not affect which plugins are loaded or how they are configured — it is only metadata stored alongside the active registry state. Removing it from the cache key lets all call sites share the same cached registry regardless of their binding mode. Fixes #61756 --- src/plugins/loader.test.ts | 42 ++++++++++++++++++++++++++++++-------- src/plugins/loader.ts | 4 +--- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index 3a7ea349311..61c810ae615 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -1753,20 +1753,20 @@ module.exports = { id: "throws-after-import", register() {} };`, }, }, { - name: "does not reuse cached registries across gateway subagent binding modes", + name: "does not reuse cached registries across different plugin SDK resolution preferences", setup: () => { useNoBundledPlugins(); const plugin = writePlugin({ - id: "cache-gateway-bindable", - filename: "cache-gateway-bindable.cjs", - body: `module.exports = { id: "cache-gateway-bindable", register() {} };`, + id: "cache-sdk-resolution", + filename: "cache-sdk-resolution.cjs", + body: `module.exports = { id: "cache-sdk-resolution", register() {} };`, }); const options = { workspaceDir: plugin.dir, config: { plugins: { - allow: ["cache-gateway-bindable"], + allow: ["cache-sdk-resolution"], load: { paths: [plugin.file], }, @@ -1779,9 +1779,7 @@ module.exports = { id: "throws-after-import", register() {} };`, loadVariant: () => loadOpenClawPlugins({ ...options, - runtimeOptions: { - allowGatewaySubagentBinding: true, - }, + pluginSdkResolution: "workspace" as const, }), }; }, @@ -1790,6 +1788,34 @@ module.exports = { id: "throws-after-import", register() {} };`, expectCacheMissThenHit(setup()); }); + it("reuses cached registry across gateway subagent binding modes", () => { + useNoBundledPlugins(); + const plugin = writePlugin({ + id: "cache-gateway-shared", + filename: "cache-gateway-shared.cjs", + body: `module.exports = { id: "cache-gateway-shared", register() {} };`, + }); + + const options = { + workspaceDir: plugin.dir, + config: { + plugins: { + allow: ["cache-gateway-shared"], + load: { + paths: [plugin.file], + }, + }, + }, + }; + + const first = loadOpenClawPlugins(options); + const second = loadOpenClawPlugins({ + ...options, + runtimeOptions: { allowGatewaySubagentBinding: true }, + }); + expect(second).toBe(first); + }); + it("evicts least recently used registries when the loader cache exceeds its cap", () => { useNoBundledPlugins(); const plugin = writePlugin({ diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index d142edcbbf9..562ade0b999 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -285,7 +285,6 @@ function buildCacheKey(params: { includeSetupOnlyChannelPlugins?: boolean; preferSetupRuntimeForChannelPlugins?: boolean; loadModules?: boolean; - runtimeSubagentMode?: "default" | "explicit" | "gateway-bindable"; pluginSdkResolution?: PluginSdkResolutionPreference; coreGatewayMethodNames?: string[]; }): string { @@ -321,7 +320,7 @@ function buildCacheKey(params: { installs, loadPaths, activationMetadataKey: params.activationMetadataKey ?? "", - })}::${scopeKey}::${setupOnlyKey}::${startupChannelMode}::${moduleLoadMode}::${params.runtimeSubagentMode ?? "default"}::${params.pluginSdkResolution ?? "auto"}::${gatewayMethodsKey}`; + })}::${scopeKey}::${setupOnlyKey}::${startupChannelMode}::${moduleLoadMode}::${params.pluginSdkResolution ?? "auto"}::${gatewayMethodsKey}`; } function normalizeScopedPluginIds(ids?: string[]): string[] | undefined { @@ -434,7 +433,6 @@ function resolvePluginLoadCacheContext(options: PluginLoadOptions = {}) { includeSetupOnlyChannelPlugins, preferSetupRuntimeForChannelPlugins, loadModules: options.loadModules, - runtimeSubagentMode: resolveRuntimeSubagentMode(options.runtimeOptions), pluginSdkResolution: options.pluginSdkResolution, coreGatewayMethodNames, });