From cee5f960b5273ec170e01d78799f36c962339aa8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 4 Apr 2026 00:04:16 +0900 Subject: [PATCH] fix(gateway): preserve raw activation source for startup plugin loads --- src/gateway/server-plugin-bootstrap.ts | 5 ++-- src/gateway/server-plugins.test.ts | 38 ++++++++++++++++++++++++++ src/gateway/server.impl.ts | 1 + 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/gateway/server-plugin-bootstrap.ts b/src/gateway/server-plugin-bootstrap.ts index 023d382db30..b3f75912325 100644 --- a/src/gateway/server-plugin-bootstrap.ts +++ b/src/gateway/server-plugin-bootstrap.ts @@ -20,6 +20,7 @@ type GatewayPluginBootstrapLog = { type GatewayPluginBootstrapParams = { cfg: ReturnType; + activationSourceConfig?: ReturnType; workspaceDir: string; log: GatewayPluginBootstrapLog; coreGatewayHandlers: Record; @@ -59,14 +60,14 @@ function logGatewayPluginDiagnostics(params: { export function prepareGatewayPluginLoad(params: GatewayPluginBootstrapParams) { const autoEnabled = applyPluginAutoEnable({ - config: params.cfg, + config: params.activationSourceConfig ?? params.cfg, env: process.env, }); const resolvedConfig = autoEnabled.config; installGatewayPluginRuntimeEnvironment(resolvedConfig); const loaded = loadGatewayPlugins({ cfg: resolvedConfig, - activationSourceConfig: params.cfg, + activationSourceConfig: params.activationSourceConfig ?? params.cfg, autoEnabledReasons: autoEnabled.autoEnabledReasons, workspaceDir: params.workspaceDir, log: params.log, diff --git a/src/gateway/server-plugins.test.ts b/src/gateway/server-plugins.test.ts index bb278e89f9c..cf27ba8b0da 100644 --- a/src/gateway/server-plugins.test.ts +++ b/src/gateway/server-plugins.test.ts @@ -278,6 +278,44 @@ describe("loadGatewayPlugins", () => { ); }); + test("keeps the raw activation source when a precomputed startup scope is reused", async () => { + const rawConfig = { channels: { slack: { botToken: "x" } } }; + const resolvedConfig = { + channels: { slack: { botToken: "x", enabled: true } }, + autoEnabled: true, + }; + applyPluginAutoEnable.mockReturnValue({ + config: resolvedConfig, + changes: [], + autoEnabledReasons: { + slack: ["slack configured"], + }, + }); + loadOpenClawPlugins.mockReturnValue(createRegistry([])); + + loadGatewayStartupPluginsForTest({ + cfg: resolvedConfig, + activationSourceConfig: rawConfig, + pluginIds: ["slack"], + }); + + expect(resolveGatewayStartupPluginIds).not.toHaveBeenCalled(); + expect(applyPluginAutoEnable).toHaveBeenCalledWith({ + config: rawConfig, + env: process.env, + }); + expect(loadOpenClawPlugins).toHaveBeenCalledWith( + expect.objectContaining({ + config: resolvedConfig, + activationSourceConfig: rawConfig, + onlyPluginIds: ["slack"], + autoEnabledReasons: { + slack: ["slack configured"], + }, + }), + ); + }); + test("treats an empty startup scope as no plugin load instead of an unscoped load", async () => { resolveGatewayStartupPluginIds.mockReturnValue([]); diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index 7cfd42d26f6..c28b538aa08 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -613,6 +613,7 @@ export async function startGatewayServer( if (!minimalTestGateway) { ({ pluginRegistry, gatewayMethods: baseGatewayMethods } = loadGatewayStartupPlugins({ cfg: gatewayPluginConfigAtStart, + activationSourceConfig: cfgAtStart, workspaceDir: defaultWorkspaceDir, log, coreGatewayHandlers,