diff --git a/src/infra/outbound/targets-resolve-shared.ts b/src/infra/outbound/targets-resolve-shared.ts index 646e288de5c..aa26b005bf2 100644 --- a/src/infra/outbound/targets-resolve-shared.ts +++ b/src/infra/outbound/targets-resolve-shared.ts @@ -3,8 +3,8 @@ import type { ChannelPlugin } from "../../channels/plugins/types.plugin.js"; import type { ChannelOutboundTargetMode } from "../../channels/plugins/types.public.js"; import { formatCliCommand } from "../../cli/command-format.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; +import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel-constants.js"; import type { GatewayMessageChannel } from "../../utils/message-channel.js"; -import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js"; import { missingTargetError } from "./target-errors.js"; export type OutboundTargetResolution = { ok: true; to: string } | { ok: false; error: Error }; diff --git a/src/infra/outbound/targets-session.ts b/src/infra/outbound/targets-session.ts index 6964ba49a08..5f08cc04838 100644 --- a/src/infra/outbound/targets-session.ts +++ b/src/infra/outbound/targets-session.ts @@ -9,11 +9,11 @@ import { deliveryContextFromSession } from "../../utils/delivery-context.shared. import type { DeliverableMessageChannel, GatewayMessageChannel, -} from "../../utils/message-channel.js"; +} from "../../utils/message-channel-normalize.js"; import { isDeliverableMessageChannel, normalizeMessageChannel, -} from "../../utils/message-channel.js"; +} from "../../utils/message-channel-normalize.js"; export type SessionDeliveryTarget = { channel?: DeliverableMessageChannel; diff --git a/src/utils/delivery-context.shared.ts b/src/utils/delivery-context.shared.ts index 09c3dc82390..e477f2caac2 100644 --- a/src/utils/delivery-context.shared.ts +++ b/src/utils/delivery-context.shared.ts @@ -1,7 +1,7 @@ import { normalizeOptionalString } from "../shared/string-coerce.js"; import { normalizeAccountId } from "./account-id.js"; import type { DeliveryContext, DeliveryContextSessionSource } from "./delivery-context.types.js"; -import { normalizeMessageChannel } from "./message-channel.js"; +import { normalizeMessageChannel } from "./message-channel-normalize.js"; export type { DeliveryContext, DeliveryContextSessionSource } from "./delivery-context.types.js"; export function normalizeDeliveryContext(context?: DeliveryContext): DeliveryContext | undefined { diff --git a/src/utils/message-channel-constants.ts b/src/utils/message-channel-constants.ts new file mode 100644 index 00000000000..7c3e8af3e82 --- /dev/null +++ b/src/utils/message-channel-constants.ts @@ -0,0 +1,2 @@ +export const INTERNAL_MESSAGE_CHANNEL = "webchat" as const; +export type InternalMessageChannel = typeof INTERNAL_MESSAGE_CHANNEL; diff --git a/src/utils/message-channel-normalize.ts b/src/utils/message-channel-normalize.ts new file mode 100644 index 00000000000..0bfb2ca99ed --- /dev/null +++ b/src/utils/message-channel-normalize.ts @@ -0,0 +1,87 @@ +import { + CHANNEL_IDS, + listChatChannelAliases, + listRegisteredChannelPluginAliases, + listRegisteredChannelPluginIds, + normalizeAnyChannelId, + normalizeChatChannelId, +} from "../channels/registry.js"; +import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; +import { + INTERNAL_MESSAGE_CHANNEL, + type InternalMessageChannel, +} from "./message-channel-constants.js"; + +type ChannelId = string & { readonly __openclawChannelIdBrand?: never }; + +export type DeliverableMessageChannel = ChannelId; + +export type GatewayMessageChannel = DeliverableMessageChannel; + +export type GatewayAgentChannelHint = GatewayMessageChannel; + +const listPluginChannelIds = (): string[] => { + return listRegisteredChannelPluginIds(); +}; + +const listPluginChannelAliases = (): string[] => { + return listRegisteredChannelPluginAliases(); +}; + +export function normalizeMessageChannel(raw?: string | null): string | undefined { + const normalized = normalizeOptionalLowercaseString(raw); + if (!normalized) { + return undefined; + } + if (normalized === INTERNAL_MESSAGE_CHANNEL) { + return INTERNAL_MESSAGE_CHANNEL; + } + const builtIn = normalizeChatChannelId(normalized); + if (builtIn) { + return builtIn; + } + return normalizeAnyChannelId(normalized) ?? normalized; +} + +export const listDeliverableMessageChannels = (): ChannelId[] => + Array.from(new Set([...CHANNEL_IDS, ...listPluginChannelIds()])); + +export const listGatewayMessageChannels = (): GatewayMessageChannel[] => [ + ...listDeliverableMessageChannels(), + INTERNAL_MESSAGE_CHANNEL, +]; + +export const listGatewayAgentChannelAliases = (): string[] => + Array.from(new Set([...listChatChannelAliases(), ...listPluginChannelAliases()])); + +export const listGatewayAgentChannelValues = (): string[] => + Array.from( + new Set([...listGatewayMessageChannels(), "last", ...listGatewayAgentChannelAliases()]), + ); + +export function isGatewayMessageChannel(value: string): value is GatewayMessageChannel { + return listGatewayMessageChannels().includes(value as GatewayMessageChannel); +} + +export function isDeliverableMessageChannel(value: string): value is DeliverableMessageChannel { + return listDeliverableMessageChannels().includes(value as DeliverableMessageChannel); +} + +export function resolveGatewayMessageChannel( + raw?: string | null, +): GatewayMessageChannel | undefined { + const normalized = normalizeMessageChannel(raw); + if (!normalized) { + return undefined; + } + return isGatewayMessageChannel(normalized) ? normalized : undefined; +} + +export function resolveMessageChannel( + primary?: string | null, + fallback?: string | null, +): string | undefined { + return normalizeMessageChannel(primary) ?? normalizeMessageChannel(fallback); +} + +export type { InternalMessageChannel }; diff --git a/src/utils/message-channel.ts b/src/utils/message-channel.ts index f5951d5f891..43061de241c 100644 --- a/src/utils/message-channel.ts +++ b/src/utils/message-channel.ts @@ -1,13 +1,5 @@ import { getChatChannelMeta } from "../channels/chat-meta.js"; -import { - CHANNEL_IDS, - getRegisteredChannelPluginMeta, - listRegisteredChannelPluginAliases, - listRegisteredChannelPluginIds, - listChatChannelAliases, - normalizeChatChannelId, - normalizeAnyChannelId, -} from "../channels/registry.js"; +import { getRegisteredChannelPluginMeta, normalizeChatChannelId } from "../channels/registry.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, @@ -16,12 +8,32 @@ import { normalizeGatewayClientMode, normalizeGatewayClientName, } from "../gateway/protocol/client-info.js"; -import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; - -type ChannelId = string & { readonly __openclawChannelIdBrand?: never }; - -export const INTERNAL_MESSAGE_CHANNEL = "webchat" as const; -export type InternalMessageChannel = typeof INTERNAL_MESSAGE_CHANNEL; +export { + isDeliverableMessageChannel, + isGatewayMessageChannel, + listDeliverableMessageChannels, + listGatewayAgentChannelAliases, + listGatewayAgentChannelValues, + listGatewayMessageChannels, + normalizeMessageChannel, + resolveGatewayMessageChannel, + resolveMessageChannel, + type DeliverableMessageChannel, + type GatewayAgentChannelHint, + type GatewayMessageChannel, +} from "./message-channel-normalize.js"; +export { + INTERNAL_MESSAGE_CHANNEL, + type InternalMessageChannel, +} from "./message-channel-constants.js"; +import { + INTERNAL_MESSAGE_CHANNEL, + type InternalMessageChannel, +} from "./message-channel-constants.js"; +import { + normalizeMessageChannel, + type DeliverableMessageChannel, +} from "./message-channel-normalize.js"; export { GATEWAY_CLIENT_NAMES, GATEWAY_CLIENT_MODES }; export type { GatewayClientName, GatewayClientMode }; @@ -58,76 +70,6 @@ export function isWebchatClient(client?: GatewayClientInfoLike | null): boolean return normalizeGatewayClientName(client?.id) === GATEWAY_CLIENT_NAMES.WEBCHAT_UI; } -export function normalizeMessageChannel(raw?: string | null): string | undefined { - const normalized = normalizeOptionalLowercaseString(raw); - if (!normalized) { - return undefined; - } - if (normalized === INTERNAL_MESSAGE_CHANNEL) { - return INTERNAL_MESSAGE_CHANNEL; - } - const builtIn = normalizeChatChannelId(normalized); - if (builtIn) { - return builtIn; - } - return normalizeAnyChannelId(normalized) ?? normalized; -} - -const listPluginChannelIds = (): string[] => { - return listRegisteredChannelPluginIds(); -}; - -const listPluginChannelAliases = (): string[] => { - return listRegisteredChannelPluginAliases(); -}; - -export const listDeliverableMessageChannels = (): ChannelId[] => - Array.from(new Set([...CHANNEL_IDS, ...listPluginChannelIds()])); - -export type DeliverableMessageChannel = ChannelId; - -export type GatewayMessageChannel = DeliverableMessageChannel; - -export const listGatewayMessageChannels = (): GatewayMessageChannel[] => [ - ...listDeliverableMessageChannels(), - INTERNAL_MESSAGE_CHANNEL, -]; - -export const listGatewayAgentChannelAliases = (): string[] => - Array.from(new Set([...listChatChannelAliases(), ...listPluginChannelAliases()])); - -export type GatewayAgentChannelHint = GatewayMessageChannel; - -export const listGatewayAgentChannelValues = (): string[] => - Array.from( - new Set([...listGatewayMessageChannels(), "last", ...listGatewayAgentChannelAliases()]), - ); - -export function isGatewayMessageChannel(value: string): value is GatewayMessageChannel { - return listGatewayMessageChannels().includes(value as GatewayMessageChannel); -} - -export function isDeliverableMessageChannel(value: string): value is DeliverableMessageChannel { - return listDeliverableMessageChannels().includes(value as DeliverableMessageChannel); -} - -export function resolveGatewayMessageChannel( - raw?: string | null, -): GatewayMessageChannel | undefined { - const normalized = normalizeMessageChannel(raw); - if (!normalized) { - return undefined; - } - return isGatewayMessageChannel(normalized) ? normalized : undefined; -} - -export function resolveMessageChannel( - primary?: string | null, - fallback?: string | null, -): string | undefined { - return normalizeMessageChannel(primary) ?? normalizeMessageChannel(fallback); -} - export function isMarkdownCapableMessageChannel(raw?: string | null): boolean { const channel = normalizeMessageChannel(raw); if (!channel) {