UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

299 lines (298 loc) 12.7 kB
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js"; import { a as normalizeAnyChannelId } from "./registry-9YoS2BJP.js"; import { t as getActivePluginChannelRegistry } from "./runtime-B2ROiq5l.js"; import { h as stringifyRouteThreadId } from "./channel-route-D7X5briz.js"; import { a as normalizeChannelId, n as getLoadedChannelPlugin } from "./registry-MkxNn1Ue.js"; import "./plugins-t2ejWcVy.js"; import { c as normalizeConversationTargetRef } from "./session-binding-service-cfUL2BWM.js"; import { i as stripTargetTopicSuffix, n as stripTargetKindPrefix, r as stripTargetProviderPrefix, t as resolveTargetPrefixedChannel } from "./channel-target-prefix-l64LTfQ6.js"; import { t as resolveExplicitDeliveryTargetCompat } from "./target-parsing-loaded-S7wa4iaI.js"; import { t as resolveConversationIdFromTargets } from "./conversation-id-CK1z1CKA.js"; import { n as resolveBundledChannelThreadBindingInboundConversation, t as resolveBundledChannelThreadBindingDefaultPlacement } from "./thread-binding-api-sqehZw7Q.js"; //#region src/channels/conversation-resolution.ts /** * Canonical conversation resolution for command and inbound channel flows. * This module turns channel targets, thread ids, aliases, and plugin hooks into stable binding ids. */ const CANONICAL_TARGET_PREFIXES = ["user:", "spaces/"]; function resolveChannelId(raw) { const normalizedRaw = normalizeOptionalString(raw); if (!normalizedRaw) return null; return normalizeAnyChannelId(normalizedRaw) ?? normalizeChannelId(normalizedRaw) ?? normalizeOptionalLowercaseString(normalizedRaw) ?? null; } function getActiveRegistryChannelPlugin(rawChannel) { const normalized = normalizeAnyChannelId(rawChannel) ?? normalizeOptionalString(rawChannel); if (!normalized) return; return getActivePluginChannelRegistry()?.channels.find((entry) => entry.plugin.id === normalized)?.plugin; } function getRuntimeChannelPluginCandidates(channel) { const candidates = [getActiveRegistryChannelPlugin(channel), getLoadedChannelPlugin(channel)].filter((plugin) => Boolean(plugin)); return [...new Map(candidates.map((plugin) => [plugin.id, plugin])).values()]; } function resolveRuntimeChannelPlugin(channel) { return getRuntimeChannelPluginCandidates(channel)[0]; } function shouldDefaultParentConversationToSelf(plugin) { return plugin?.bindings?.selfParentConversationByDefault === true; } function normalizeResolutionTarget(params) { const conversationId = normalizeOptionalString(params.conversation?.conversationId); if (!conversationId) return null; const parentConversationId = normalizeOptionalString(params.conversation?.parentConversationId); const defaultParentToSelf = shouldDefaultParentConversationToSelf(params.plugin) && !params.threadId && !parentConversationId; const normalized = normalizeConversationTargetRef({ conversationId, parentConversationId: defaultParentToSelf ? conversationId : parentConversationId }); const normalizedParentConversationId = defaultParentToSelf ? normalized.conversationId : normalized.parentConversationId; const placementHint = params.includePlacementHint === false ? void 0 : resolveChannelDefaultBindingPlacement(params.channel); return { canonical: { channel: params.channel, accountId: params.accountId, conversationId: normalized.conversationId, ...normalizedParentConversationId ? { parentConversationId: normalizedParentConversationId } : {} }, ...params.threadId ? { threadId: params.threadId } : {}, ...placementHint ? { placementHint } : {}, source: params.source }; } function resolveBindingAccountId(params) { return normalizeOptionalString(params.rawAccountId) || normalizeOptionalString(params.plugin?.config.defaultAccountId?.(params.cfg)) || "default"; } function resolveFallbackConversationTargetId(params) { const { allowNumericTopicShorthand = false } = params; const target = normalizeOptionalString(params.rawTarget); if (!target) return; const withoutKind = stripTargetKindPrefix(target); const withoutTopic = params.preserveExplicitTopicSuffix && /:topic:/iu.test(withoutKind) ? withoutKind : stripTargetTopicSuffix(withoutKind, { allowNumericShorthand: allowNumericTopicShorthand }); return resolveConversationIdFromTargets({ targets: [withoutTopic] }) ?? (withoutTopic !== target ? withoutTopic : void 0) ?? resolveConversationIdFromTargets({ targets: [target] }); } function resolveChannelTargetId(params) { const target = normalizeOptionalString(params.target); if (!target) return; const lower = normalizeLowercaseStringOrEmpty(target); const channelPrefix = `${params.channel}:`; if (lower.startsWith(channelPrefix)) return resolveChannelTargetId({ channel: params.channel, target: target.slice(channelPrefix.length), preserveExplicitTopicSuffix: params.preserveExplicitTopicSuffix }); if (CANONICAL_TARGET_PREFIXES.some((prefix) => lower.startsWith(prefix))) return target; const prefixedChannel = resolveTargetPrefixedChannel(target); if (!prefixedChannel || prefixedChannel !== params.channel) { const explicitConversationId = resolveFallbackConversationTargetId({ rawTarget: target, allowNumericTopicShorthand: params.channel === "telegram", preserveExplicitTopicSuffix: params.preserveExplicitTopicSuffix }); if (explicitConversationId) return explicitConversationId; } const normalizedTarget = normalizeOptionalString(resolveRuntimeChannelPlugin(params.channel)?.messaging?.normalizeTarget?.(target)); if (normalizedTarget) { const withoutProvider = stripTargetProviderPrefix(normalizedTarget, params.channel); return resolveFallbackConversationTargetId({ rawTarget: withoutProvider, allowNumericTopicShorthand: params.channel === "telegram", preserveExplicitTopicSuffix: params.preserveExplicitTopicSuffix }) || withoutProvider || normalizedTarget; } const parsedTarget = resolveExplicitDeliveryTargetCompat({ channel: params.channel, rawTarget: target }); if (parsedTarget?.to) return resolveConversationIdFromTargets({ targets: [parsedTarget.to] }) ?? parsedTarget.to; return target; } function buildThreadingContext(params) { const to = normalizeOptionalString(params.originatingTo) ?? normalizeOptionalString(params.fallbackTo); return { ...to ? { To: to } : {}, ...params.from ? { From: params.from } : {}, ...params.chatType ? { ChatType: params.chatType } : {}, ...params.threadId ? { MessageThreadId: params.threadId } : {}, ...params.nativeChannelId ? { NativeChannelId: params.nativeChannelId } : {} }; } /** * Resolves whether top-level bindings default to the current conversation or a child thread. */ function resolveChannelDefaultBindingPlacement(rawChannel) { const channel = resolveChannelId(rawChannel); if (!channel) return; return resolveRuntimeChannelPlugin(channel)?.conversationBindings?.defaultTopLevelPlacement ?? resolveBundledChannelThreadBindingDefaultPlacement(channel); } /** * Resolves command context into a canonical channel/account/conversation tuple. */ function resolveCommandConversationResolution(params) { const channel = resolveChannelId(params.channel); if (!channel) return null; const plugin = resolveRuntimeChannelPlugin(channel); const accountId = resolveBindingAccountId({ rawAccountId: params.accountId, plugin, cfg: params.cfg }); const threadId = stringifyRouteThreadId(params.threadId); const commandParams = { accountId, threadId, threadParentId: normalizeOptionalString(params.threadParentId), senderId: normalizeOptionalString(params.senderId), sessionKey: normalizeOptionalString(params.sessionKey), parentSessionKey: normalizeOptionalString(params.parentSessionKey), from: normalizeOptionalString(params.from), chatType: normalizeOptionalString(params.chatType), originatingTo: params.originatingTo ?? void 0, commandTo: params.commandTo ?? void 0, fallbackTo: params.fallbackTo ?? void 0 }; const resolvedByProvider = plugin?.bindings?.resolveCommandConversation?.(commandParams); const providerResolution = normalizeResolutionTarget({ channel, accountId, conversation: resolvedByProvider, source: "command-provider", threadId, plugin, includePlacementHint: params.includePlacementHint }); if (providerResolution) return providerResolution; const focusedBinding = plugin?.threading?.resolveFocusedBinding?.({ cfg: params.cfg, accountId, context: buildThreadingContext({ fallbackTo: params.fallbackTo ?? void 0, originatingTo: params.originatingTo ?? void 0, threadId, from: normalizeOptionalString(params.from), chatType: normalizeOptionalString(params.chatType), nativeChannelId: normalizeOptionalString(params.nativeChannelId) }) }); const focusedResolution = normalizeResolutionTarget({ channel, accountId, conversation: focusedBinding, source: "focused-binding", threadId, plugin, includePlacementHint: params.includePlacementHint }); if (focusedResolution) return focusedResolution; const baseConversationId = resolveChannelTargetId({ channel, target: params.originatingTo }) ?? resolveChannelTargetId({ channel, target: params.commandTo }) ?? resolveChannelTargetId({ channel, target: params.fallbackTo }); const parentConversationId = resolveChannelTargetId({ channel, target: params.threadParentId }) ?? (threadId && baseConversationId && baseConversationId !== threadId ? baseConversationId : void 0); const conversationId = threadId || baseConversationId; if (!conversationId) return null; return normalizeResolutionTarget({ channel, accountId, conversation: { conversationId, parentConversationId }, source: "command-fallback", threadId, plugin, includePlacementHint: params.includePlacementHint }); } /** * Resolves inbound message context into the canonical binding conversation tuple. */ function resolveInboundConversationResolution(params) { const channel = resolveChannelId(params.channel); if (!channel) return null; const plugin = resolveRuntimeChannelPlugin(channel); const accountId = resolveBindingAccountId({ rawAccountId: params.accountId, plugin, cfg: params.cfg }); const threadId = stringifyRouteThreadId(params.threadId); const resolverParams = { from: normalizeOptionalString(params.from), to: normalizeOptionalString(params.to), conversationId: normalizeOptionalString(params.conversationId) ?? normalizeOptionalString(params.groupId) ?? normalizeOptionalString(params.to), threadId, threadParentId: stringifyRouteThreadId(params.threadParentId), isGroup: params.isGroup ?? true }; const providerConversation = plugin?.messaging?.resolveInboundConversation?.(resolverParams); const providerResolution = normalizeResolutionTarget({ channel, accountId, conversation: providerConversation, source: "inbound-provider", threadId, plugin }); if (providerResolution || providerConversation === null) return providerResolution; const artifactConversation = resolveBundledChannelThreadBindingInboundConversation({ channelId: channel, ...resolverParams }); const artifactResolution = normalizeResolutionTarget({ channel, accountId, conversation: artifactConversation, source: "inbound-bundled-artifact", threadId, plugin }); if (artifactResolution || artifactConversation === null) return artifactResolution; const parentConversationId = resolveChannelTargetId({ channel, target: params.threadParentId == null ? void 0 : String(params.threadParentId), preserveExplicitTopicSuffix: threadId == null }) ?? resolveChannelTargetId({ channel, target: params.to, preserveExplicitTopicSuffix: threadId == null }) ?? resolveChannelTargetId({ channel, target: params.conversationId, preserveExplicitTopicSuffix: threadId == null }) ?? resolveChannelTargetId({ channel, target: params.groupId, preserveExplicitTopicSuffix: threadId == null }); const genericConversationId = threadId ?? resolveChannelTargetId({ channel, target: params.conversationId, preserveExplicitTopicSuffix: threadId == null }) ?? resolveChannelTargetId({ channel, target: params.groupId, preserveExplicitTopicSuffix: threadId == null }) ?? parentConversationId; if (!genericConversationId) return null; return normalizeResolutionTarget({ channel, accountId, conversation: { conversationId: genericConversationId, parentConversationId: threadId != null ? parentConversationId : void 0 }, source: "inbound-fallback", threadId, plugin }); } //#endregion export { resolveCommandConversationResolution as n, resolveInboundConversationResolution as r, resolveChannelDefaultBindingPlacement as t };