From ff348d20636da992d00c2edff6b491779cc49ab3 Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:51:20 -0500 Subject: [PATCH] fix(regression): auto-enable gateway send selection --- src/gateway/server-methods/send.test.ts | 35 +++++++++++++++++++++---- src/gateway/server-methods/send.ts | 6 ++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/gateway/server-methods/send.test.ts b/src/gateway/server-methods/send.test.ts index 4dc9bb1eafe..f38d9e14617 100644 --- a/src/gateway/server-methods/send.test.ts +++ b/src/gateway/server-methods/send.test.ts @@ -14,6 +14,7 @@ const mocks = vi.hoisted(() => ({ sendPoll: vi.fn(async () => ({ messageId: "poll-1" })), getChannelPlugin: vi.fn(), loadOpenClawPlugins: vi.fn(), + applyPluginAutoEnable: vi.fn(), })); vi.mock("../../config/config.js", async () => { @@ -43,10 +44,6 @@ function resolveAgentIdFromSessionKeyForTests(params: { sessionKey?: string }): return "main"; } -function passthroughPluginAutoEnable(config: unknown) { - return { config, changes: [] as unknown[] }; -} - vi.mock("../../agents/agent-scope.js", () => ({ resolveSessionAgentId: ({ sessionKey, @@ -60,7 +57,8 @@ vi.mock("../../agents/agent-scope.js", () => ({ })); vi.mock("../../config/plugin-auto-enable.js", () => ({ - applyPluginAutoEnable: ({ config }: { config: unknown }) => passthroughPluginAutoEnable(config), + applyPluginAutoEnable: ({ config, env }: { config: unknown; env?: unknown }) => + mocks.applyPluginAutoEnable({ config, env }), })); vi.mock("../../plugins/loader.js", () => ({ @@ -166,6 +164,7 @@ describe("gateway send mirroring", () => { vi.clearAllMocks(); registrySeq += 1; setActivePluginRegistry(createTestRegistry([]), `send-test-${registrySeq}`); + mocks.applyPluginAutoEnable.mockImplementation(({ config }) => ({ config, changes: [] })); mocks.resolveOutboundTarget.mockReturnValue({ ok: true, to: "resolved" }); mocks.resolveMessageChannelSelection.mockResolvedValue({ channel: "slack", @@ -303,6 +302,32 @@ describe("gateway send mirroring", () => { ); }); + it("auto-picks the single configured channel from the auto-enabled config snapshot for send", async () => { + const autoEnabledConfig = { channels: { slack: {} }, plugins: { allow: ["slack"] } }; + mocks.applyPluginAutoEnable.mockReturnValue({ config: autoEnabledConfig, changes: [] }); + mockDeliverySuccess("m-single-send-auto"); + + const { respond } = await runSend({ + to: "x", + message: "hi", + idempotencyKey: "idem-missing-channel-auto-enabled", + }); + + expect(mocks.applyPluginAutoEnable).toHaveBeenCalledWith({ + config: {}, + env: process.env, + }); + expect(mocks.resolveMessageChannelSelection).toHaveBeenCalledWith({ + cfg: autoEnabledConfig, + }); + expect(respond).toHaveBeenCalledWith( + true, + expect.objectContaining({ messageId: "m-single-send-auto" }), + undefined, + expect.objectContaining({ channel: "slack" }), + ); + }); + it("returns invalid request when send channel selection is ambiguous", async () => { mocks.resolveMessageChannelSelection.mockRejectedValueOnce( new Error("Channel is required when multiple channels are configured: telegram, slack"), diff --git a/src/gateway/server-methods/send.ts b/src/gateway/server-methods/send.ts index ead034991aa..6703b8347cf 100644 --- a/src/gateway/server-methods/send.ts +++ b/src/gateway/server-methods/send.ts @@ -3,6 +3,7 @@ import { resolveSessionAgentId } from "../../agents/agent-scope.js"; import { normalizeChannelId } from "../../channels/plugins/index.js"; import { createOutboundSendDeps } from "../../cli/deps.js"; import { loadConfig } from "../../config/config.js"; +import { applyPluginAutoEnable } from "../../config/plugin-auto-enable.js"; import { resolveOutboundChannelPlugin } from "../../infra/outbound/channel-resolution.js"; import { resolveMessageChannelSelection } from "../../infra/outbound/channel-selection.js"; import { deliverOutboundPayloads } from "../../infra/outbound/deliver.js"; @@ -76,7 +77,10 @@ async function resolveRequestedChannel(params: { error: errorShape(ErrorCodes.INVALID_REQUEST, params.unsupportedMessage(channelInput)), }; } - const cfg = loadConfig(); + const cfg = applyPluginAutoEnable({ + config: loadConfig(), + env: process.env, + }).config; let channel = normalizedChannel; if (!channel) { try {