From b6abd68a290855635a59b18d1b4397a6f39b1f83 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 13 Apr 2026 18:21:59 +0100 Subject: [PATCH] perf(channels): split hot-path message channel normalization --- src/channels/registry-normalize.ts | 35 ++++++++++++++++++++++++++ src/infra/outbound/targets-session.ts | 8 +++--- src/utils/delivery-context.shared.ts | 2 +- src/utils/message-channel-core.ts | 33 ++++++++++++++++++++++++ src/utils/message-channel-normalize.ts | 26 +++++-------------- 5 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 src/channels/registry-normalize.ts create mode 100644 src/utils/message-channel-core.ts diff --git a/src/channels/registry-normalize.ts b/src/channels/registry-normalize.ts new file mode 100644 index 00000000000..0693361e615 --- /dev/null +++ b/src/channels/registry-normalize.ts @@ -0,0 +1,35 @@ +import type { ActivePluginChannelRegistration } from "../plugins/channel-registry-state.types.js"; +import { getActivePluginChannelRegistryFromState } from "../plugins/runtime-channel-state.js"; +import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; +import { normalizeChatChannelId, type ChatChannelId } from "./ids.js"; +import type { ChannelId } from "./plugins/channel-id.types.js"; + +function listRegisteredChannelPluginEntries(): ActivePluginChannelRegistration[] { + const channelRegistry = getActivePluginChannelRegistryFromState(); + if (channelRegistry?.channels && channelRegistry.channels.length > 0) { + return channelRegistry.channels; + } + return []; +} + +export function normalizeChannelId(raw?: string | null): ChatChannelId | null { + return normalizeChatChannelId(raw); +} + +export function normalizeAnyChannelId(raw?: string | null): ChannelId | null { + const key = normalizeOptionalLowercaseString(raw); + if (!key) { + return null; + } + return ( + listRegisteredChannelPluginEntries().find((entry) => { + const id = normalizeOptionalLowercaseString(entry.plugin.id ?? "") ?? ""; + if (id && id === key) { + return true; + } + return (entry.plugin.meta?.aliases ?? []).some( + (alias) => normalizeOptionalLowercaseString(alias) === key, + ); + })?.plugin.id ?? null + ); +} diff --git a/src/infra/outbound/targets-session.ts b/src/infra/outbound/targets-session.ts index 5f08cc04838..a33265a5367 100644 --- a/src/infra/outbound/targets-session.ts +++ b/src/infra/outbound/targets-session.ts @@ -6,13 +6,13 @@ import { import type { ChannelOutboundTargetMode } from "../../channels/plugins/types.public.js"; import type { SessionEntry } from "../../config/sessions.js"; import { deliveryContextFromSession } from "../../utils/delivery-context.shared.js"; -import type { - DeliverableMessageChannel, - GatewayMessageChannel, -} from "../../utils/message-channel-normalize.js"; import { isDeliverableMessageChannel, normalizeMessageChannel, +} from "../../utils/message-channel-core.js"; +import type { + DeliverableMessageChannel, + GatewayMessageChannel, } from "../../utils/message-channel-normalize.js"; export type SessionDeliveryTarget = { diff --git a/src/utils/delivery-context.shared.ts b/src/utils/delivery-context.shared.ts index e477f2caac2..7a29712b593 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-normalize.js"; +import { normalizeMessageChannel } from "./message-channel-core.js"; export type { DeliveryContext, DeliveryContextSessionSource } from "./delivery-context.types.js"; export function normalizeDeliveryContext(context?: DeliveryContext): DeliveryContext | undefined { diff --git a/src/utils/message-channel-core.ts b/src/utils/message-channel-core.ts new file mode 100644 index 00000000000..fc8122436d1 --- /dev/null +++ b/src/utils/message-channel-core.ts @@ -0,0 +1,33 @@ +import { normalizeChatChannelId } from "../channels/ids.js"; +import { normalizeAnyChannelId } from "../channels/registry-normalize.js"; +import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; +import { INTERNAL_MESSAGE_CHANNEL } from "./message-channel-constants.js"; + +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 function isDeliverableMessageChannel(value: string): boolean { + const normalized = normalizeMessageChannel(value); + return ( + normalized !== undefined && normalized !== INTERNAL_MESSAGE_CHANNEL && normalized === value + ); +} + +export function resolveMessageChannel( + primary?: string | null, + fallback?: string | null, +): string | undefined { + return normalizeMessageChannel(primary) ?? normalizeMessageChannel(fallback); +} diff --git a/src/utils/message-channel-normalize.ts b/src/utils/message-channel-normalize.ts index 0bfb2ca99ed..4c98646c5b0 100644 --- a/src/utils/message-channel-normalize.ts +++ b/src/utils/message-channel-normalize.ts @@ -1,16 +1,13 @@ +import { CHANNEL_IDS, listChatChannelAliases } from "../channels/ids.js"; 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"; +import { normalizeMessageChannel as normalizeMessageChannelCore } from "./message-channel-core.js"; type ChannelId = string & { readonly __openclawChannelIdBrand?: never }; @@ -20,6 +17,10 @@ export type GatewayMessageChannel = DeliverableMessageChannel; export type GatewayAgentChannelHint = GatewayMessageChannel; +export function normalizeMessageChannel(raw?: string | null): string | undefined { + return normalizeMessageChannelCore(raw); +} + const listPluginChannelIds = (): string[] => { return listRegisteredChannelPluginIds(); }; @@ -28,21 +29,6 @@ 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()]));