import { CHANNEL_IDS, listChatChannelAliases, normalizeChatChannelId, } from "../channels/registry.js"; import type { ChannelId } from "../channels/plugins/types.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, type GatewayClientMode, type GatewayClientName, normalizeGatewayClientMode, normalizeGatewayClientName, } from "../gateway/protocol/client-info.js"; import { getActivePluginRegistry } from "../plugins/runtime.js"; export const INTERNAL_MESSAGE_CHANNEL = "webchat" as const; export type InternalMessageChannel = typeof INTERNAL_MESSAGE_CHANNEL; const MARKDOWN_CAPABLE_CHANNELS = new Set([ "slack", "telegram", "signal", "discord", "tui", INTERNAL_MESSAGE_CHANNEL, ]); export { GATEWAY_CLIENT_NAMES, GATEWAY_CLIENT_MODES }; export type { GatewayClientName, GatewayClientMode }; export { normalizeGatewayClientName, normalizeGatewayClientMode }; type GatewayClientInfoLike = { mode?: string | null; id?: string | null; }; export function isGatewayCliClient(client?: GatewayClientInfoLike | null): boolean { return normalizeGatewayClientMode(client?.mode) === GATEWAY_CLIENT_MODES.CLI; } export function isInternalMessageChannel(raw?: string | null): raw is InternalMessageChannel { return normalizeMessageChannel(raw) === INTERNAL_MESSAGE_CHANNEL; } export function isWebchatClient(client?: GatewayClientInfoLike | null): boolean { const mode = normalizeGatewayClientMode(client?.mode); if (mode === GATEWAY_CLIENT_MODES.WEBCHAT) return true; return normalizeGatewayClientName(client?.id) === GATEWAY_CLIENT_NAMES.WEBCHAT_UI; } export function normalizeMessageChannel(raw?: string | null): string | undefined { const normalized = raw?.trim().toLowerCase(); if (!normalized) return undefined; if (normalized === INTERNAL_MESSAGE_CHANNEL) return INTERNAL_MESSAGE_CHANNEL; const builtIn = normalizeChatChannelId(normalized); if (builtIn) return builtIn; const registry = getActivePluginRegistry(); const pluginMatch = registry?.channels.find((entry) => { if (entry.plugin.id.toLowerCase() === normalized) return true; return (entry.plugin.meta.aliases ?? []).some( (alias) => alias.trim().toLowerCase() === normalized, ); }); return pluginMatch?.plugin.id ?? normalized; } const listPluginChannelIds = (): string[] => { const registry = getActivePluginRegistry(); if (!registry) return []; return registry.channels.map((entry) => entry.plugin.id); }; const listPluginChannelAliases = (): string[] => { const registry = getActivePluginRegistry(); if (!registry) return []; return registry.channels.flatMap((entry) => entry.plugin.meta.aliases ?? []); }; export const listDeliverableMessageChannels = (): ChannelId[] => Array.from(new Set([...CHANNEL_IDS, ...listPluginChannelIds()])); export type DeliverableMessageChannel = ChannelId; export type GatewayMessageChannel = DeliverableMessageChannel | InternalMessageChannel; export const listGatewayMessageChannels = (): GatewayMessageChannel[] => [ ...listDeliverableMessageChannels(), INTERNAL_MESSAGE_CHANNEL, ]; export const listGatewayAgentChannelAliases = (): string[] => Array.from(new Set([...listChatChannelAliases(), ...listPluginChannelAliases()])); export type GatewayAgentChannelHint = GatewayMessageChannel | "last"; 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) return false; return MARKDOWN_CAPABLE_CHANNELS.has(channel); }