UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

303 lines (302 loc) 11.8 kB
import { j as resolveTimerTimeoutMs } from "./number-coercion-CJQ8TR--.js"; import "./number-coercion-Z7n6tXLk.js"; import { i as GATEWAY_CLIENT_NAMES, r as GATEWAY_CLIENT_MODES } from "./client-info-CcqJJIan.js"; import "./message-channel-BiOeMu0l.js"; import { r as resolveOutboundDurableFinalDeliverySupport } from "./deliver-BHfQle87.js"; import { t as buildOutboundSessionContext } from "./session-context-Bz96-75Y.js"; import { t as deriveDurableFinalDeliveryRequirements } from "./capabilities-tytC94t4.js"; import { r as resolveOutboundChannelPlugin } from "./channel-resolution-azq0u3DK.js"; import { a as projectOutboundPayloadPlanForDelivery, s as projectOutboundPayloadPlanForMirror, t as createOutboundPayloadPlan } from "./payloads-B4DTFTLz.js"; import { t as sendDurableMessageBatch } from "./send-DWoTcOuO.js"; import "./runtime-BiQQNgz2.js"; import { n as normalizePollInput } from "./polls-C-v11_tu.js"; import { r as resolveMessageChannelSelection } from "./channel-selection-MKj8pvsX.js"; import { i as resolveOutboundTarget } from "./targets-C4BNalx6.js"; //#region src/infra/outbound/message-gateway-options.ts /** Normalizes outbound gateway options and fills CLI defaults. */ function resolveOutboundMessageGatewayOptions(gateway) { const clientName = gateway?.clientName ?? GATEWAY_CLIENT_NAMES.CLI; const mode = gateway?.mode ?? GATEWAY_CLIENT_MODES.CLI; return { url: mode === GATEWAY_CLIENT_MODES.BACKEND || clientName === GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT ? void 0 : gateway?.url, token: gateway?.token, timeoutMs: resolveTimerTimeoutMs(gateway?.timeoutMs, 1e4), clientName, clientDisplayName: gateway?.clientDisplayName, mode }; } //#endregion //#region src/infra/outbound/message.ts let messageConfigRuntimePromise = null; let messageGatewayRuntimePromise = null; const SEND_BUFFER_MEDIA_URL = "buffer://message-send/attachment"; function loadMessageConfigRuntime() { messageConfigRuntimePromise ??= import("./message.config.runtime.js"); return messageConfigRuntimePromise; } function loadMessageGatewayRuntime() { messageGatewayRuntimePromise ??= import("./message.gateway.runtime.js"); return messageGatewayRuntimePromise; } function buildMessagePollResult(params) { return { channel: params.channel, to: params.to, question: params.normalized.question, options: params.normalized.options, maxSelections: params.normalized.maxSelections, durationSeconds: params.normalized.durationSeconds ?? null, durationHours: params.normalized.durationHours ?? null, via: "gateway", ...params.dryRun ? { dryRun: true } : { result: params.result } }; } async function resolveRequiredChannel(params) { return (await resolveMessageChannelSelection({ cfg: params.cfg, channel: params.channel })).channel; } function resolveRequiredPlugin(channel, cfg) { const plugin = resolveOutboundChannelPlugin({ channel, cfg }); if (!plugin) throw new Error(`Unknown channel: ${channel}`); return plugin; } function payloadRequiresDurablePayloadTransport(payload) { return payload.presentation !== void 0 || payload.delivery !== void 0 || payload.interactive !== void 0 || payload.channelData !== void 0 && Object.keys(payload.channelData).length > 0; } function mergeDurableRequirements(target, source) { for (const [capability, required] of Object.entries(source)) if (required === true) target[capability] = true; return target; } function deriveRequiredMessageSendCapabilities(params) { const requirements = { reconcileUnknownSend: true }; for (const payload of params.payloads) mergeDurableRequirements(requirements, deriveDurableFinalDeliveryRequirements({ payload, replyToId: params.replyToId, threadId: params.threadId, silent: params.silent, payloadTransport: payloadRequiresDurablePayloadTransport(payload), batch: params.payloads.length > 1, reconcileUnknownSend: true })); return requirements; } async function assertRequiredMessageSendDurability(params) { const support = await resolveOutboundDurableFinalDeliverySupport({ cfg: params.cfg, channel: params.channel, requirements: deriveRequiredMessageSendCapabilities(params) }); if (support.ok) return; const suffix = support.reason === "capability_mismatch" && support.capability ? `missing ${support.capability}` : support.reason; throw new Error(`Required durable message send is unsupported for ${params.channel}: ${suffix}. Use queuePolicy:"best_effort" for best-effort delivery, omit bestEffort:false in message-tool calls, or use a channel with required durable delivery support.`); } function resolveGatewayOptions(opts) { return resolveOutboundMessageGatewayOptions(opts); } async function callMessageGateway(params) { const { callGatewayLeastPrivilege } = await loadMessageGatewayRuntime(); const gateway = resolveGatewayOptions(params.gateway); return await callGatewayLeastPrivilege({ url: gateway.url, token: gateway.token, method: params.method, params: params.params, timeoutMs: gateway.timeoutMs, clientName: gateway.clientName, clientDisplayName: gateway.clientDisplayName, mode: gateway.mode }); } async function resolveMessageConfig(cfg) { if (cfg) return cfg; const { getRuntimeConfig } = await loadMessageConfigRuntime(); return getRuntimeConfig(); } async function resolveGatewayIdempotencyKey(idempotencyKey) { if (idempotencyKey) return idempotencyKey; const { randomIdempotencyKey } = await loadMessageGatewayRuntime(); return randomIdempotencyKey(); } async function sendMessage(params) { const cfg = await resolveMessageConfig(params.cfg); const channel = await resolveRequiredChannel({ cfg, channel: params.channel }); const deliveryMode = resolveRequiredPlugin(channel, cfg).outbound?.deliveryMode ?? "direct"; const hasRealMediaSource = [params.mediaUrl, ...params.mediaUrls ?? []].filter((source) => Boolean(source)).some((source) => source !== SEND_BUFFER_MEDIA_URL); const shouldForwardBuffer = deliveryMode === "gateway" && Boolean(params.buffer) && !hasRealMediaSource; const mediaUrl = params.mediaUrl ?? (shouldForwardBuffer ? SEND_BUFFER_MEDIA_URL : void 0); const mediaUrls = params.mediaUrls ?? (shouldForwardBuffer ? [SEND_BUFFER_MEDIA_URL] : void 0); const outboundPlan = createOutboundPayloadPlan(params.payloads && params.payloads.length > 0 ? params.payloads : [{ text: params.content, mediaUrl, mediaUrls, audioAsVoice: params.asVoice === true }]); const normalizedPayloads = projectOutboundPayloadPlanForDelivery(outboundPlan); const mirrorProjection = projectOutboundPayloadPlanForMirror(outboundPlan); const mirrorText = mirrorProjection.text; const mirrorMediaUrls = mirrorProjection.mediaUrls; const primaryMediaUrl = mirrorMediaUrls[0] ?? mediaUrl ?? null; if (params.dryRun) return { channel, to: params.to, via: deliveryMode === "gateway" ? "gateway" : "direct", mediaUrl: primaryMediaUrl, mediaUrls: mirrorMediaUrls.length ? mirrorMediaUrls : void 0, dryRun: true }; if (deliveryMode !== "gateway") { const outboundChannel = channel; const resolvedTarget = resolveOutboundTarget({ channel: outboundChannel, to: params.to, cfg, accountId: params.accountId, mode: "explicit" }); if (!resolvedTarget.ok) throw resolvedTarget.error; const outboundSession = buildOutboundSessionContext({ cfg, agentId: params.agentId, sessionKey: params.requesterSessionKey ?? params.mirror?.sessionKey, requesterAccountId: params.requesterAccountId ?? params.accountId, requesterSenderId: params.requesterSenderId, requesterSenderName: params.requesterSenderName, requesterSenderUsername: params.requesterSenderUsername, requesterSenderE164: params.requesterSenderE164 }); if (params.queuePolicy === "required") await assertRequiredMessageSendDurability({ cfg, channel: outboundChannel, payloads: normalizedPayloads, replyToId: params.replyToId, threadId: params.threadId, silent: params.silent }); const send = await sendDurableMessageBatch({ cfg, channel: outboundChannel, to: resolvedTarget.to, session: outboundSession, accountId: params.accountId, payloads: normalizedPayloads, replyToId: params.replyToId, threadId: params.threadId, gifPlayback: params.gifPlayback, forceDocument: params.forceDocument, deps: params.deps, bestEffort: params.bestEffort, durability: params.bestEffort || params.queuePolicy === "best_effort" ? "best_effort" : "required", signal: params.abortSignal, silent: params.silent, mediaAccess: params.mediaAccess, formatting: params.parseMode ? { parseMode: params.parseMode } : void 0, mirror: params.mirror ? { ...params.mirror, text: mirrorText || params.content, mediaUrls: mirrorMediaUrls.length ? mirrorMediaUrls : void 0, idempotencyKey: params.mirror.idempotencyKey ?? params.idempotencyKey } : void 0 }); if (!params.bestEffort && (send.status === "failed" || send.status === "partial_failed")) throw send.error; const results = send.status === "sent" || send.status === "partial_failed" ? send.results : []; return { channel, to: params.to, via: "direct", mediaUrl: primaryMediaUrl, mediaUrls: mirrorMediaUrls.length ? mirrorMediaUrls : void 0, result: results.at(-1) }; } const result = await callMessageGateway({ gateway: params.gateway, method: "send", params: { to: params.to, message: params.content, mediaUrl, mediaUrls: mirrorMediaUrls.length ? mirrorMediaUrls : mediaUrls, buffer: shouldForwardBuffer ? params.buffer : void 0, filename: shouldForwardBuffer ? params.filename : void 0, contentType: shouldForwardBuffer ? params.contentType : void 0, asVoice: params.asVoice, gifPlayback: params.gifPlayback, accountId: params.accountId, agentId: params.agentId, channel, replyToId: params.replyToId, threadId: params.threadId != null ? String(params.threadId) : void 0, forceDocument: params.forceDocument, silent: params.silent, parseMode: params.parseMode, sessionKey: params.mirror?.sessionKey, idempotencyKey: await resolveGatewayIdempotencyKey(params.idempotencyKey) } }); return { channel, to: params.to, via: "gateway", mediaUrl: primaryMediaUrl, mediaUrls: mirrorMediaUrls.length ? mirrorMediaUrls : void 0, result }; } async function sendPoll(params) { const cfg = await resolveMessageConfig(params.cfg); const channel = await resolveRequiredChannel({ cfg, channel: params.channel }); const pollInput = { question: params.question, options: params.options, maxSelections: params.maxSelections, durationSeconds: params.durationSeconds, durationHours: params.durationHours }; const outbound = resolveRequiredPlugin(channel, cfg)?.outbound; if (!outbound?.sendPoll) throw new Error(`Unsupported poll channel: ${channel}`); const normalized = outbound.pollMaxOptions ? normalizePollInput(pollInput, { maxOptions: outbound.pollMaxOptions }) : normalizePollInput(pollInput); if (params.dryRun) return buildMessagePollResult({ channel, to: params.to, normalized, dryRun: true }); const result = await callMessageGateway({ gateway: params.gateway, method: "poll", params: { to: params.to, question: normalized.question, options: normalized.options, maxSelections: normalized.maxSelections, durationSeconds: normalized.durationSeconds, durationHours: normalized.durationHours, threadId: params.threadId, silent: params.silent, isAnonymous: params.isAnonymous, channel, accountId: params.accountId, idempotencyKey: await resolveGatewayIdempotencyKey(params.idempotencyKey) } }); return buildMessagePollResult({ channel, to: params.to, normalized, result }); } //#endregion export { sendPoll as n, resolveOutboundMessageGatewayOptions as r, sendMessage as t };