diff --git a/src/infra/outbound/channel-selection.test.ts b/src/infra/outbound/channel-selection.test.ts index fdb4ecd4b6f..666593fcf24 100644 --- a/src/infra/outbound/channel-selection.test.ts +++ b/src/infra/outbound/channel-selection.test.ts @@ -143,6 +143,24 @@ describe("resolveMessageChannelSelection", () => { }); }); + it("skips configured-channel scanning when includeConfigured is false", async () => { + const isConfigured = vi.fn(async () => true); + mocks.listChannelPlugins.mockReturnValue([makePlugin({ id: "whatsapp", isConfigured })]); + + const selection = await resolveMessageChannelSelection({ + cfg: {} as never, + channel: "telegram", + includeConfigured: false, + }); + + expect(selection).toEqual({ + channel: "telegram", + configured: [], + source: "explicit", + }); + expect(isConfigured).not.toHaveBeenCalled(); + }); + it("falls back to tool context channel when explicit channel is unknown", async () => { const selection = await resolveMessageChannelSelection({ cfg: {} as never, diff --git a/src/infra/outbound/channel-selection.ts b/src/infra/outbound/channel-selection.ts index 0e87a8e4950..dc6f358eb23 100644 --- a/src/infra/outbound/channel-selection.ts +++ b/src/infra/outbound/channel-selection.ts @@ -146,11 +146,15 @@ export async function resolveMessageChannelSelection(params: { cfg: OpenClawConfig; channel?: string | null; fallbackChannel?: string | null; + includeConfigured?: boolean; }): Promise<{ channel: MessageChannelId; configured: MessageChannelId[]; source: MessageChannelSelectionSource; }> { + const includeConfigured = params.includeConfigured !== false; + const resolveConfigured = async () => + includeConfigured ? await listConfiguredMessageChannels(params.cfg) : []; const normalized = normalizeMessageChannel(params.channel); if (normalized) { const availableExplicit = resolveAvailableKnownChannel({ @@ -165,7 +169,7 @@ export async function resolveMessageChannelSelection(params: { if (fallback) { return { channel: fallback, - configured: await listConfiguredMessageChannels(params.cfg), + configured: await resolveConfigured(), source: "tool-context-fallback", }; } @@ -176,7 +180,7 @@ export async function resolveMessageChannelSelection(params: { } return { channel: availableExplicit, - configured: await listConfiguredMessageChannels(params.cfg), + configured: await resolveConfigured(), source: "explicit", }; } @@ -188,12 +192,12 @@ export async function resolveMessageChannelSelection(params: { if (fallback) { return { channel: fallback, - configured: await listConfiguredMessageChannels(params.cfg), + configured: await resolveConfigured(), source: "tool-context-fallback", }; } - const configured = await listConfiguredMessageChannels(params.cfg); + const configured = await resolveConfigured(); if (configured.length === 1) { return { channel: configured[0], configured, source: "single-configured" }; } diff --git a/src/infra/outbound/message-action-runner.ts b/src/infra/outbound/message-action-runner.ts index 318699c1042..dda0a35cd06 100644 --- a/src/infra/outbound/message-action-runner.ts +++ b/src/infra/outbound/message-action-runner.ts @@ -226,6 +226,7 @@ async function resolveChannel( cfg, channel: readStringParam(params, "channel"), fallbackChannel: toolContext?.currentChannelProvider, + includeConfigured: false, }); if (selection.source === "tool-context-fallback") { params.channel = selection.channel; @@ -318,14 +319,13 @@ async function handleBroadcastAction( throw new Error("Broadcast requires at least one target in --targets."); } const channelHint = readStringParam(params, "channel"); - const configured = await listConfiguredMessageChannels(input.cfg); - if (configured.length === 0) { - throw new Error("Broadcast requires at least one configured channel."); - } const targetChannels = channelHint && channelHint.trim().toLowerCase() !== "all" ? [await resolveChannel(input.cfg, { channel: channelHint }, input.toolContext)] - : configured; + : await listConfiguredMessageChannels(input.cfg); + if (targetChannels.length === 0) { + throw new Error("Broadcast requires at least one configured channel."); + } const results: Array<{ channel: ChannelId; to: string; diff --git a/src/infra/outbound/message.ts b/src/infra/outbound/message.ts index 852b9eef9fd..9df211960f8 100644 --- a/src/infra/outbound/message.ts +++ b/src/infra/outbound/message.ts @@ -136,6 +136,7 @@ async function resolveRequiredChannel(params: { await resolveMessageChannelSelection({ cfg: params.cfg, channel: params.channel, + includeConfigured: false, }) ).channel; }