mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-14 13:58:34 +00:00
refactor(outbound): unify channel selection and action input normalization
This commit is contained in:
91
src/infra/outbound/channel-selection.test.ts
Normal file
91
src/infra/outbound/channel-selection.test.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => ({
|
||||||
|
listChannelPlugins: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../../channels/plugins/index.js", () => ({
|
||||||
|
listChannelPlugins: mocks.listChannelPlugins,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { resolveMessageChannelSelection } from "./channel-selection.js";
|
||||||
|
|
||||||
|
describe("resolveMessageChannelSelection", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.listChannelPlugins.mockReset();
|
||||||
|
mocks.listChannelPlugins.mockReturnValue([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps explicit known channels and marks source explicit", async () => {
|
||||||
|
const selection = await resolveMessageChannelSelection({
|
||||||
|
cfg: {} as never,
|
||||||
|
channel: "telegram",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(selection).toEqual({
|
||||||
|
channel: "telegram",
|
||||||
|
configured: [],
|
||||||
|
source: "explicit",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to tool context channel when explicit channel is unknown", async () => {
|
||||||
|
const selection = await resolveMessageChannelSelection({
|
||||||
|
cfg: {} as never,
|
||||||
|
channel: "channel:C123",
|
||||||
|
fallbackChannel: "slack",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(selection).toEqual({
|
||||||
|
channel: "slack",
|
||||||
|
configured: [],
|
||||||
|
source: "tool-context-fallback",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses fallback channel when explicit channel is omitted", async () => {
|
||||||
|
const selection = await resolveMessageChannelSelection({
|
||||||
|
cfg: {} as never,
|
||||||
|
fallbackChannel: "signal",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(selection).toEqual({
|
||||||
|
channel: "signal",
|
||||||
|
configured: [],
|
||||||
|
source: "tool-context-fallback",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("selects single configured channel when no explicit/fallback channel exists", async () => {
|
||||||
|
mocks.listChannelPlugins.mockReturnValue([
|
||||||
|
{
|
||||||
|
id: "discord",
|
||||||
|
config: {
|
||||||
|
listAccountIds: () => ["default"],
|
||||||
|
resolveAccount: () => ({}),
|
||||||
|
isConfigured: async () => true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const selection = await resolveMessageChannelSelection({
|
||||||
|
cfg: {} as never,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(selection).toEqual({
|
||||||
|
channel: "discord",
|
||||||
|
configured: ["discord"],
|
||||||
|
source: "single-configured",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws unknown channel when explicit and fallback channels are both invalid", async () => {
|
||||||
|
await expect(
|
||||||
|
resolveMessageChannelSelection({
|
||||||
|
cfg: {} as never,
|
||||||
|
channel: "channel:C123",
|
||||||
|
fallbackChannel: "not-a-channel",
|
||||||
|
}),
|
||||||
|
).rejects.toThrow("Unknown channel: channel:c123");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,10 +4,15 @@ import type { OpenClawConfig } from "../../config/config.js";
|
|||||||
import {
|
import {
|
||||||
listDeliverableMessageChannels,
|
listDeliverableMessageChannels,
|
||||||
type DeliverableMessageChannel,
|
type DeliverableMessageChannel,
|
||||||
|
isDeliverableMessageChannel,
|
||||||
normalizeMessageChannel,
|
normalizeMessageChannel,
|
||||||
} from "../../utils/message-channel.js";
|
} from "../../utils/message-channel.js";
|
||||||
|
|
||||||
export type MessageChannelId = DeliverableMessageChannel;
|
export type MessageChannelId = DeliverableMessageChannel;
|
||||||
|
export type MessageChannelSelectionSource =
|
||||||
|
| "explicit"
|
||||||
|
| "tool-context-fallback"
|
||||||
|
| "single-configured";
|
||||||
|
|
||||||
const getMessageChannels = () => listDeliverableMessageChannels();
|
const getMessageChannels = () => listDeliverableMessageChannels();
|
||||||
|
|
||||||
@@ -15,6 +20,20 @@ function isKnownChannel(value: string): boolean {
|
|||||||
return getMessageChannels().includes(value as MessageChannelId);
|
return getMessageChannels().includes(value as MessageChannelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveKnownChannel(value?: string | null): MessageChannelId | undefined {
|
||||||
|
const normalized = normalizeMessageChannel(value);
|
||||||
|
if (!normalized) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!isDeliverableMessageChannel(normalized)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!isKnownChannel(normalized)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return normalized as MessageChannelId;
|
||||||
|
}
|
||||||
|
|
||||||
function isAccountEnabled(account: unknown): boolean {
|
function isAccountEnabled(account: unknown): boolean {
|
||||||
if (!account || typeof account !== "object") {
|
if (!account || typeof account !== "object") {
|
||||||
return true;
|
return true;
|
||||||
@@ -67,21 +86,44 @@ export async function listConfiguredMessageChannels(
|
|||||||
export async function resolveMessageChannelSelection(params: {
|
export async function resolveMessageChannelSelection(params: {
|
||||||
cfg: OpenClawConfig;
|
cfg: OpenClawConfig;
|
||||||
channel?: string | null;
|
channel?: string | null;
|
||||||
}): Promise<{ channel: MessageChannelId; configured: MessageChannelId[] }> {
|
fallbackChannel?: string | null;
|
||||||
|
}): Promise<{
|
||||||
|
channel: MessageChannelId;
|
||||||
|
configured: MessageChannelId[];
|
||||||
|
source: MessageChannelSelectionSource;
|
||||||
|
}> {
|
||||||
const normalized = normalizeMessageChannel(params.channel);
|
const normalized = normalizeMessageChannel(params.channel);
|
||||||
if (normalized) {
|
if (normalized) {
|
||||||
if (!isKnownChannel(normalized)) {
|
if (!isKnownChannel(normalized)) {
|
||||||
|
const fallback = resolveKnownChannel(params.fallbackChannel);
|
||||||
|
if (fallback) {
|
||||||
|
return {
|
||||||
|
channel: fallback,
|
||||||
|
configured: await listConfiguredMessageChannels(params.cfg),
|
||||||
|
source: "tool-context-fallback",
|
||||||
|
};
|
||||||
|
}
|
||||||
throw new Error(`Unknown channel: ${String(normalized)}`);
|
throw new Error(`Unknown channel: ${String(normalized)}`);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
channel: normalized as MessageChannelId,
|
channel: normalized as MessageChannelId,
|
||||||
configured: await listConfiguredMessageChannels(params.cfg),
|
configured: await listConfiguredMessageChannels(params.cfg),
|
||||||
|
source: "explicit",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallback = resolveKnownChannel(params.fallbackChannel);
|
||||||
|
if (fallback) {
|
||||||
|
return {
|
||||||
|
channel: fallback,
|
||||||
|
configured: await listConfiguredMessageChannels(params.cfg),
|
||||||
|
source: "tool-context-fallback",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const configured = await listConfiguredMessageChannels(params.cfg);
|
const configured = await listConfiguredMessageChannels(params.cfg);
|
||||||
if (configured.length === 1) {
|
if (configured.length === 1) {
|
||||||
return { channel: configured[0], configured };
|
return { channel: configured[0], configured, source: "single-configured" };
|
||||||
}
|
}
|
||||||
if (configured.length === 0) {
|
if (configured.length === 0) {
|
||||||
throw new Error("Channel is required (no configured channels detected).");
|
throw new Error("Channel is required (no configured channels detected).");
|
||||||
|
|||||||
68
src/infra/outbound/message-action-normalization.test.ts
Normal file
68
src/infra/outbound/message-action-normalization.test.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { normalizeMessageActionInput } from "./message-action-normalization.js";
|
||||||
|
|
||||||
|
describe("normalizeMessageActionInput", () => {
|
||||||
|
it("prefers explicit target and clears legacy target fields", () => {
|
||||||
|
const normalized = normalizeMessageActionInput({
|
||||||
|
action: "send",
|
||||||
|
args: {
|
||||||
|
target: "channel:C1",
|
||||||
|
to: "legacy",
|
||||||
|
channelId: "legacy-channel",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(normalized.target).toBe("channel:C1");
|
||||||
|
expect(normalized.to).toBe("channel:C1");
|
||||||
|
expect("channelId" in normalized).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("maps legacy target fields into canonical target", () => {
|
||||||
|
const normalized = normalizeMessageActionInput({
|
||||||
|
action: "send",
|
||||||
|
args: {
|
||||||
|
to: "channel:C1",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(normalized.target).toBe("channel:C1");
|
||||||
|
expect(normalized.to).toBe("channel:C1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("infers target from tool context when required", () => {
|
||||||
|
const normalized = normalizeMessageActionInput({
|
||||||
|
action: "send",
|
||||||
|
args: {},
|
||||||
|
toolContext: {
|
||||||
|
currentChannelId: "channel:C1",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(normalized.target).toBe("channel:C1");
|
||||||
|
expect(normalized.to).toBe("channel:C1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("infers channel from tool context provider", () => {
|
||||||
|
const normalized = normalizeMessageActionInput({
|
||||||
|
action: "send",
|
||||||
|
args: {
|
||||||
|
target: "channel:C1",
|
||||||
|
},
|
||||||
|
toolContext: {
|
||||||
|
currentChannelId: "C1",
|
||||||
|
currentChannelProvider: "slack",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(normalized.channel).toBe("slack");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws when required target remains unresolved", () => {
|
||||||
|
expect(() =>
|
||||||
|
normalizeMessageActionInput({
|
||||||
|
action: "send",
|
||||||
|
args: {},
|
||||||
|
}),
|
||||||
|
).toThrow(/requires a target/);
|
||||||
|
});
|
||||||
|
});
|
||||||
70
src/infra/outbound/message-action-normalization.ts
Normal file
70
src/infra/outbound/message-action-normalization.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import type {
|
||||||
|
ChannelMessageActionName,
|
||||||
|
ChannelThreadingToolContext,
|
||||||
|
} from "../../channels/plugins/types.js";
|
||||||
|
import {
|
||||||
|
isDeliverableMessageChannel,
|
||||||
|
normalizeMessageChannel,
|
||||||
|
} from "../../utils/message-channel.js";
|
||||||
|
import { applyTargetToParams } from "./channel-target.js";
|
||||||
|
import { actionHasTarget, actionRequiresTarget } from "./message-action-spec.js";
|
||||||
|
|
||||||
|
export function normalizeMessageActionInput(params: {
|
||||||
|
action: ChannelMessageActionName;
|
||||||
|
args: Record<string, unknown>;
|
||||||
|
toolContext?: ChannelThreadingToolContext;
|
||||||
|
}): Record<string, unknown> {
|
||||||
|
const normalizedArgs = { ...params.args };
|
||||||
|
const { action, toolContext } = params;
|
||||||
|
|
||||||
|
const explicitTarget =
|
||||||
|
typeof normalizedArgs.target === "string" ? normalizedArgs.target.trim() : "";
|
||||||
|
const hasLegacyTarget =
|
||||||
|
(typeof normalizedArgs.to === "string" && normalizedArgs.to.trim().length > 0) ||
|
||||||
|
(typeof normalizedArgs.channelId === "string" && normalizedArgs.channelId.trim().length > 0);
|
||||||
|
|
||||||
|
if (explicitTarget && hasLegacyTarget) {
|
||||||
|
delete normalizedArgs.to;
|
||||||
|
delete normalizedArgs.channelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!explicitTarget &&
|
||||||
|
!hasLegacyTarget &&
|
||||||
|
actionRequiresTarget(action) &&
|
||||||
|
!actionHasTarget(action, normalizedArgs)
|
||||||
|
) {
|
||||||
|
const inferredTarget = toolContext?.currentChannelId?.trim();
|
||||||
|
if (inferredTarget) {
|
||||||
|
normalizedArgs.target = inferredTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!explicitTarget && actionRequiresTarget(action) && hasLegacyTarget) {
|
||||||
|
const legacyTo = typeof normalizedArgs.to === "string" ? normalizedArgs.to.trim() : "";
|
||||||
|
const legacyChannelId =
|
||||||
|
typeof normalizedArgs.channelId === "string" ? normalizedArgs.channelId.trim() : "";
|
||||||
|
const legacyTarget = legacyTo || legacyChannelId;
|
||||||
|
if (legacyTarget) {
|
||||||
|
normalizedArgs.target = legacyTarget;
|
||||||
|
delete normalizedArgs.to;
|
||||||
|
delete normalizedArgs.channelId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const explicitChannel =
|
||||||
|
typeof normalizedArgs.channel === "string" ? normalizedArgs.channel.trim() : "";
|
||||||
|
if (!explicitChannel) {
|
||||||
|
const inferredChannel = normalizeMessageChannel(toolContext?.currentChannelProvider);
|
||||||
|
if (inferredChannel && isDeliverableMessageChannel(inferredChannel)) {
|
||||||
|
normalizedArgs.channel = inferredChannel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTargetToParams({ action, args: normalizedArgs });
|
||||||
|
if (actionRequiresTarget(action) && !actionHasTarget(action, normalizedArgs)) {
|
||||||
|
throw new Error(`Action ${action} requires a target.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedArgs;
|
||||||
|
}
|
||||||
@@ -16,19 +16,14 @@ import type { OpenClawConfig } from "../../config/config.js";
|
|||||||
import { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js";
|
import { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js";
|
||||||
import { buildChannelAccountBindings } from "../../routing/bindings.js";
|
import { buildChannelAccountBindings } from "../../routing/bindings.js";
|
||||||
import { normalizeAgentId } from "../../routing/session-key.js";
|
import { normalizeAgentId } from "../../routing/session-key.js";
|
||||||
import {
|
import { type GatewayClientMode, type GatewayClientName } from "../../utils/message-channel.js";
|
||||||
isDeliverableMessageChannel,
|
|
||||||
normalizeMessageChannel,
|
|
||||||
type GatewayClientMode,
|
|
||||||
type GatewayClientName,
|
|
||||||
} from "../../utils/message-channel.js";
|
|
||||||
import { throwIfAborted } from "./abort.js";
|
import { throwIfAborted } from "./abort.js";
|
||||||
import {
|
import {
|
||||||
listConfiguredMessageChannels,
|
listConfiguredMessageChannels,
|
||||||
resolveMessageChannelSelection,
|
resolveMessageChannelSelection,
|
||||||
} from "./channel-selection.js";
|
} from "./channel-selection.js";
|
||||||
import { applyTargetToParams } from "./channel-target.js";
|
|
||||||
import type { OutboundSendDeps } from "./deliver.js";
|
import type { OutboundSendDeps } from "./deliver.js";
|
||||||
|
import { normalizeMessageActionInput } from "./message-action-normalization.js";
|
||||||
import {
|
import {
|
||||||
hydrateAttachmentParamsForAction,
|
hydrateAttachmentParamsForAction,
|
||||||
normalizeSandboxMediaList,
|
normalizeSandboxMediaList,
|
||||||
@@ -41,7 +36,6 @@ import {
|
|||||||
resolveSlackAutoThreadId,
|
resolveSlackAutoThreadId,
|
||||||
resolveTelegramAutoThreadId,
|
resolveTelegramAutoThreadId,
|
||||||
} from "./message-action-params.js";
|
} from "./message-action-params.js";
|
||||||
import { actionHasTarget, actionRequiresTarget } from "./message-action-spec.js";
|
|
||||||
import type { MessagePollResult, MessageSendResult } from "./message.js";
|
import type { MessagePollResult, MessageSendResult } from "./message.js";
|
||||||
import {
|
import {
|
||||||
applyCrossContextDecoration,
|
applyCrossContextDecoration,
|
||||||
@@ -222,23 +216,15 @@ async function resolveChannel(
|
|||||||
params: Record<string, unknown>,
|
params: Record<string, unknown>,
|
||||||
toolContext?: { currentChannelProvider?: string },
|
toolContext?: { currentChannelProvider?: string },
|
||||||
) {
|
) {
|
||||||
const channelHint = readStringParam(params, "channel");
|
|
||||||
try {
|
|
||||||
const selection = await resolveMessageChannelSelection({
|
const selection = await resolveMessageChannelSelection({
|
||||||
cfg,
|
cfg,
|
||||||
channel: channelHint,
|
channel: readStringParam(params, "channel"),
|
||||||
|
fallbackChannel: toolContext?.currentChannelProvider,
|
||||||
});
|
});
|
||||||
|
if (selection.source === "tool-context-fallback") {
|
||||||
|
params.channel = selection.channel;
|
||||||
|
}
|
||||||
return selection.channel;
|
return selection.channel;
|
||||||
} catch (error) {
|
|
||||||
if (channelHint && toolContext?.currentChannelProvider) {
|
|
||||||
const fallback = normalizeMessageChannel(toolContext.currentChannelProvider);
|
|
||||||
if (fallback && isDeliverableMessageChannel(fallback)) {
|
|
||||||
params.channel = fallback;
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveActionTarget(params: {
|
async function resolveActionTarget(params: {
|
||||||
@@ -710,7 +696,7 @@ export async function runMessageAction(
|
|||||||
input: RunMessageActionParams,
|
input: RunMessageActionParams,
|
||||||
): Promise<MessageActionRunResult> {
|
): Promise<MessageActionRunResult> {
|
||||||
const cfg = input.cfg;
|
const cfg = input.cfg;
|
||||||
const params = { ...input.params };
|
let params = { ...input.params };
|
||||||
const resolvedAgentId =
|
const resolvedAgentId =
|
||||||
input.agentId ??
|
input.agentId ??
|
||||||
(input.sessionKey
|
(input.sessionKey
|
||||||
@@ -724,50 +710,11 @@ export async function runMessageAction(
|
|||||||
if (action === "broadcast") {
|
if (action === "broadcast") {
|
||||||
return handleBroadcastAction(input, params);
|
return handleBroadcastAction(input, params);
|
||||||
}
|
}
|
||||||
|
params = normalizeMessageActionInput({
|
||||||
const explicitTarget = typeof params.target === "string" ? params.target.trim() : "";
|
action,
|
||||||
const hasLegacyTarget =
|
args: params,
|
||||||
(typeof params.to === "string" && params.to.trim().length > 0) ||
|
toolContext: input.toolContext,
|
||||||
(typeof params.channelId === "string" && params.channelId.trim().length > 0);
|
});
|
||||||
if (explicitTarget && hasLegacyTarget) {
|
|
||||||
delete params.to;
|
|
||||||
delete params.channelId;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!explicitTarget &&
|
|
||||||
!hasLegacyTarget &&
|
|
||||||
actionRequiresTarget(action) &&
|
|
||||||
!actionHasTarget(action, params)
|
|
||||||
) {
|
|
||||||
const inferredTarget = input.toolContext?.currentChannelId?.trim();
|
|
||||||
if (inferredTarget) {
|
|
||||||
params.target = inferredTarget;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!explicitTarget && actionRequiresTarget(action) && hasLegacyTarget) {
|
|
||||||
const legacyTo = typeof params.to === "string" ? params.to.trim() : "";
|
|
||||||
const legacyChannelId = typeof params.channelId === "string" ? params.channelId.trim() : "";
|
|
||||||
const legacyTarget = legacyTo || legacyChannelId;
|
|
||||||
if (legacyTarget) {
|
|
||||||
params.target = legacyTarget;
|
|
||||||
delete params.to;
|
|
||||||
delete params.channelId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const explicitChannel = typeof params.channel === "string" ? params.channel.trim() : "";
|
|
||||||
if (!explicitChannel) {
|
|
||||||
const inferredChannel = normalizeMessageChannel(input.toolContext?.currentChannelProvider);
|
|
||||||
if (inferredChannel && isDeliverableMessageChannel(inferredChannel)) {
|
|
||||||
params.channel = inferredChannel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applyTargetToParams({ action, args: params });
|
|
||||||
if (actionRequiresTarget(action)) {
|
|
||||||
if (!actionHasTarget(action, params)) {
|
|
||||||
throw new Error(`Action ${action} requires a target.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const channel = await resolveChannel(cfg, params, input.toolContext);
|
const channel = await resolveChannel(cfg, params, input.toolContext);
|
||||||
let accountId = readStringParam(params, "accountId") ?? input.defaultAccountId;
|
let accountId = readStringParam(params, "accountId") ?? input.defaultAccountId;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const mocks = vi.hoisted(() => ({
|
|||||||
vi.mock("../../channels/plugins/index.js", () => ({
|
vi.mock("../../channels/plugins/index.js", () => ({
|
||||||
normalizeChannelId: (channel?: string) => channel?.trim().toLowerCase() ?? undefined,
|
normalizeChannelId: (channel?: string) => channel?.trim().toLowerCase() ?? undefined,
|
||||||
getChannelPlugin: mocks.getChannelPlugin,
|
getChannelPlugin: mocks.getChannelPlugin,
|
||||||
|
listChannelPlugins: () => [],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../../agents/agent-scope.js", () => ({
|
vi.mock("../../agents/agent-scope.js", () => ({
|
||||||
|
|||||||
@@ -9,10 +9,7 @@ import {
|
|||||||
type GatewayClientMode,
|
type GatewayClientMode,
|
||||||
type GatewayClientName,
|
type GatewayClientName,
|
||||||
} from "../../utils/message-channel.js";
|
} from "../../utils/message-channel.js";
|
||||||
import {
|
import { resolveOutboundChannelPlugin } from "./channel-resolution.js";
|
||||||
normalizeDeliverableOutboundChannel,
|
|
||||||
resolveOutboundChannelPlugin,
|
|
||||||
} from "./channel-resolution.js";
|
|
||||||
import { resolveMessageChannelSelection } from "./channel-selection.js";
|
import { resolveMessageChannelSelection } from "./channel-selection.js";
|
||||||
import {
|
import {
|
||||||
deliverOutboundPayloads,
|
deliverOutboundPayloads,
|
||||||
@@ -111,14 +108,12 @@ async function resolveRequiredChannel(params: {
|
|||||||
cfg: OpenClawConfig;
|
cfg: OpenClawConfig;
|
||||||
channel?: string;
|
channel?: string;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
if (params.channel?.trim()) {
|
return (
|
||||||
const normalized = normalizeDeliverableOutboundChannel(params.channel);
|
await resolveMessageChannelSelection({
|
||||||
if (!normalized) {
|
cfg: params.cfg,
|
||||||
throw new Error(`Unknown channel: ${params.channel}`);
|
channel: params.channel,
|
||||||
}
|
})
|
||||||
return normalized;
|
).channel;
|
||||||
}
|
|
||||||
return (await resolveMessageChannelSelection({ cfg: params.cfg })).channel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveRequiredPlugin(channel: string, cfg: OpenClawConfig) {
|
function resolveRequiredPlugin(channel: string, cfg: OpenClawConfig) {
|
||||||
|
|||||||
Reference in New Issue
Block a user