UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

236 lines (235 loc) 12.3 kB
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { f as isDiagnosticsEnabled } from "./diagnostic-events-Cwe92uV3.js"; import { o as measureDiagnosticsTimelineSpan, s as measureDiagnosticsTimelineSpanSync } from "./diagnostics-timeline-CRX1LXmg.js"; import { t as normalizeChatType } from "./chat-type-CG0X_HJM.js"; import { c as resolveCommandTurnContext, l as resolveCommandTurnTargetSessionKey } from "./command-turn-context-CmPEYNmV.js"; import { a as logMessageReceived } from "./diagnostic-ryAZMr5N.js"; import { t as finalizeInboundContext } from "./inbound-context-BJXKd4x8.js"; import { a as createReplyDispatcherWithTyping, d as composeReplyDispatchBeforeDeliver, f as markReplyDispatchBeforeDeliverDeadlineOwned, i as createReplyDispatcher } from "./reply-dispatcher-BjdCLv_G.js"; import { c as buildInboundReplyPayloadSendingBeforeDeliver, l as buildLegacyInboundMessageSendingBeforeDeliver, u as buildProjectedInboundMessageSendingBeforeDeliver } from "./deliver-prepare-6uVAVf8W.js"; import { t as createKeyedFifoLeaseRegistry } from "./keyed-fifo-lease-hVhofezd.js"; import { m as withReplyDispatcher } from "./dispatch-from-config.finalize-DcagE2ge.js"; import { n as dispatchReplyFromConfig } from "./dispatch-from-config-Djp-96vr.js"; //#region src/auto-reply/dispatch.ts /** Auto-reply dispatch orchestration, hook composition, and foreground delivery fencing. */ const replyPayloadSendingDispatchers = /* @__PURE__ */ new WeakSet(); const foregroundReplyLeases = createKeyedFifoLeaseRegistry(Symbol.for("openclaw.foregroundReplyFences")); function applyRuntimeToolsAllow(replyOptions, toolsAllow) { if (toolsAllow === void 0) return replyOptions; return { ...replyOptions, toolsAllow }; } function resolveForegroundReplyOrderKey(finalized) { const sessionKey = normalizeOptionalString(finalized.SessionKey); const channel = normalizeOptionalString(finalized.OriginatingChannel) ?? normalizeOptionalString(finalized.Surface) ?? normalizeOptionalString(finalized.Provider); const target = normalizeOptionalString(finalized.OriginatingTo) ?? normalizeOptionalString(finalized.NativeChannelId) ?? normalizeOptionalString(finalized.From) ?? normalizeOptionalString(finalized.To); if (!sessionKey || !channel || !target) return; return JSON.stringify([ "foreground", channel, normalizeOptionalString(finalized.AccountId) ?? "default", sessionKey, normalizeChatType(finalized.ChatType) ?? "unknown", target ]); } function reserveForegroundReplyLease(finalized) { const key = resolveForegroundReplyOrderKey(finalized); return key ? foregroundReplyLeases.reserve([key]) : void 0; } async function runOrderedForegroundReplySettledDeliveries(lease, onSettled, onFreshSettledDelivery) { if (!onSettled && !onFreshSettledDelivery) return; await lease?.wait(); await onSettled?.(); await onFreshSettledDelivery?.(); } function resolveDispatcherSilentReplyContext(ctx, cfg) { const finalized = finalizeInboundContext(ctx); const commandTargetSessionKey = resolveCommandTurnTargetSessionKey(finalized); const policySessionKey = commandTargetSessionKey ?? finalized.SessionKey; const chatType = normalizeChatType(finalized.ChatType); const conversationType = commandTargetSessionKey && commandTargetSessionKey !== finalized.SessionKey ? void 0 : chatType === "direct" ? "direct" : chatType === "group" || chatType === "channel" ? "group" : void 0; return { cfg, sessionKey: policySessionKey, surface: finalized.Surface ?? finalized.Provider, conversationType }; } function bindReplyPayloadRunState(replyOptions, runState) { const onAgentRunStart = replyOptions?.onAgentRunStart; return { ...replyOptions, onAgentRunStart: (...args) => { runState.runId = args[0]; return onAgentRunStart?.(...args); } }; } function installReplyPayloadSendingBeforeDeliver(dispatcher, ctx, runState) { if (replyPayloadSendingDispatchers.has(dispatcher)) return; const beforeDeliver = buildInboundReplyPayloadSendingBeforeDeliver(ctx, runState); if (!beforeDeliver || !dispatcher.appendBeforeDeliver) return; dispatcher.appendBeforeDeliver(beforeDeliver); replyPayloadSendingDispatchers.add(dispatcher); } function markReplyPayloadSendingBeforeDeliverInstalled(dispatcher, beforeDeliver) { if (beforeDeliver) replyPayloadSendingDispatchers.add(dispatcher); } function buildDispatchTimelineAttributes(ctx) { const commandTurn = resolveCommandTurnContext(ctx); return { surface: typeof ctx.Surface === "string" ? ctx.Surface : typeof ctx.Provider === "string" ? ctx.Provider : "unknown", hasSessionKey: typeof ctx.SessionKey === "string" || typeof ctx.CommandTargetSessionKey === "string", commandSource: commandTurn.source }; } /** Dispatches one finalized inbound message through reply resolution and queued delivery. */ async function dispatchInboundMessage(params) { const replyOptions = applyRuntimeToolsAllow(params.replyOptions, params.toolsAllow); const replyPayloadRunState = params.replyPayloadRunState ?? { runId: replyOptions?.runId }; const replyOptionsWithRunState = bindReplyPayloadRunState(replyOptions, replyPayloadRunState); const finalized = measureDiagnosticsTimelineSpanSync("auto_reply.finalize_context", () => finalizeInboundContext(params.ctx), { phase: "agent-turn", config: params.cfg, attributes: buildDispatchTimelineAttributes(params.ctx) }); if (isDiagnosticsEnabled(params.cfg)) logMessageReceived({ sessionKey: finalized.SessionKey, channel: finalized.Surface ?? finalized.Provider, chatId: finalized.To ?? finalized.From, messageId: finalized.MessageSid ?? finalized.MessageSidFirst ?? finalized.MessageSidLast, source: "dispatchInboundMessage" }); if (params.outboundHooks !== "disabled") installReplyPayloadSendingBeforeDeliver(params.dispatcher, finalized, replyPayloadRunState); let settledReceipt; const result = await withReplyDispatcher({ dispatcher: params.dispatcher, onSettled: params.onSettled, run: () => measureDiagnosticsTimelineSpan("auto_reply.dispatch_reply_from_config", () => (params.dispatchReplyFromConfig ?? dispatchReplyFromConfig)({ ctx: finalized, cfg: params.cfg, dispatcher: params.dispatcher, replyOptions: replyOptionsWithRunState, replyResolver: params.replyResolver, onSessionMetadataChanges: params.onSessionMetadataChanges, usePublishedModelRuntime: true }), { phase: "agent-turn", config: params.cfg, attributes: buildDispatchTimelineAttributes(finalized) }), onSettledReceipt: (receipt) => { settledReceipt = receipt; } }); return settledReceipt ? { ...result, settledReceipt } : result; } async function dispatchInboundMessageWithBufferedDispatcherCore(params, ownership) { const finalized = finalizeInboundContext(params.ctx); const foregroundReplyLease = reserveForegroundReplyLease(finalized); const silentReplyContext = resolveDispatcherSilentReplyContext(finalized, params.cfg); const replyPayloadRunState = { runId: params.replyOptions?.runId }; let settledDeliveries = Promise.resolve(); const settleDeliveries = () => settledDeliveries = settledDeliveries.then(() => runOrderedForegroundReplySettledDeliveries(foregroundReplyLease, params.dispatcherOptions.onSettled, params.dispatcherOptions.onFreshSettledDelivery)); const replyPayloadBeforeDeliver = ownership.outboundHooks === "disabled" ? void 0 : buildInboundReplyPayloadSendingBeforeDeliver(finalized, replyPayloadRunState, ownership.onReplyPayloadSuppressed); const globalBeforeDeliver = ownership.messageSending === "dispatcher" ? composeReplyDispatchBeforeDeliver(replyPayloadBeforeDeliver, buildLegacyInboundMessageSendingBeforeDeliver(finalized)) : replyPayloadBeforeDeliver; const configuredBeforeDeliver = params.dispatcherOptions.beforeDeliver ? composeReplyDispatchBeforeDeliver({ hook: params.dispatcherOptions.beforeDeliver, options: params.dispatcherOptions.beforeDeliverOptions }, replyPayloadBeforeDeliver) : globalBeforeDeliver; const beforeDeliver = foregroundReplyLease || configuredBeforeDeliver ? markReplyDispatchBeforeDeliverDeadlineOwned(async (payload, info) => { await foregroundReplyLease?.wait(); return configuredBeforeDeliver ? await configuredBeforeDeliver(payload, info) : payload; }) : void 0; const { dispatcher, replyOptions, markDispatchIdle, markRunComplete } = createReplyDispatcherWithTyping({ ...params.dispatcherOptions, beforeDeliver, onSettled: settleDeliveries, onFreshSettledDelivery: void 0, silentReplyContext: params.dispatcherOptions.silentReplyContext ?? silentReplyContext }); const onTypingController = params.replyOptions?.onTypingController ? (typing) => { replyOptions.onTypingController?.(typing); params.replyOptions?.onTypingController?.(typing); } : replyOptions.onTypingController; markReplyPayloadSendingBeforeDeliverInstalled(dispatcher, replyPayloadBeforeDeliver); try { return await dispatchInboundMessage({ ctx: finalized, cfg: params.cfg, dispatcher, toolsAllow: params.toolsAllow, replyResolver: params.replyResolver, dispatchReplyFromConfig: params.dispatchReplyFromConfig, replyOptions: { ...params.replyOptions, ...replyOptions, onTypingController }, replyPayloadRunState, outboundHooks: ownership.outboundHooks, onSessionMetadataChanges: params.onSessionMetadataChanges }); } finally { try { await settledDeliveries; } finally { foregroundReplyLease?.release(); markRunComplete(); markDispatchIdle(); } } } async function dispatchInboundMessageWithBufferedDispatcher(params) { return await dispatchInboundMessageWithBufferedDispatcherCore(params, { messageSending: "dispatcher" }); } async function dispatchInboundMessageWithRoutedChannelDispatcher(params) { const { onReplyPayloadSuppressed, suppressOutboundHooks, ...dispatcherParams } = params; return await dispatchInboundMessageWithBufferedDispatcherCore(dispatcherParams, { messageSending: "channel-delivery", ...suppressOutboundHooks ? { outboundHooks: "disabled" } : { onReplyPayloadSuppressed } }); } async function dispatchInboundMessageWithPlainDispatcherCore(params, messageSending) { const silentReplyContext = resolveDispatcherSilentReplyContext(params.ctx, params.cfg); const replyPayloadRunState = { runId: params.replyOptions?.runId }; const replyPayloadBeforeDeliver = buildInboundReplyPayloadSendingBeforeDeliver(params.ctx, replyPayloadRunState); const messageSendingBeforeDeliver = messageSending === "projected" ? buildProjectedInboundMessageSendingBeforeDeliver(params.ctx) : buildLegacyInboundMessageSendingBeforeDeliver(params.ctx); const globalBeforeDeliver = composeReplyDispatchBeforeDeliver(replyPayloadBeforeDeliver, messageSendingBeforeDeliver); const composedBeforeDeliver = params.dispatcherOptions.beforeDeliver ? composeReplyDispatchBeforeDeliver({ hook: params.dispatcherOptions.beforeDeliver, options: params.dispatcherOptions.beforeDeliverOptions }, replyPayloadBeforeDeliver) : globalBeforeDeliver; const dispatcher = createReplyDispatcher({ ...params.dispatcherOptions, beforeDeliver: composedBeforeDeliver, silentReplyContext: params.dispatcherOptions.silentReplyContext ?? silentReplyContext }); markReplyPayloadSendingBeforeDeliverInstalled(dispatcher, replyPayloadBeforeDeliver); return await dispatchInboundMessage({ ctx: params.ctx, cfg: params.cfg, dispatcher, toolsAllow: params.toolsAllow, replyResolver: params.replyResolver, replyOptions: params.replyOptions, replyPayloadRunState, onSessionMetadataChanges: params.onSessionMetadataChanges }); } /** Creates a plain dispatcher, installs global send hooks, and dispatches the inbound message. */ async function dispatchInboundMessageWithDispatcher(params) { return await dispatchInboundMessageWithPlainDispatcherCore(params, "legacy"); } /** Creates a core-owned dispatcher whose modifiers fence projected output capture. */ async function dispatchInboundMessageWithProjectedDispatcher(params) { return await dispatchInboundMessageWithPlainDispatcherCore(params, "projected"); } //#endregion export { dispatchInboundMessageWithRoutedChannelDispatcher as a, dispatchInboundMessageWithProjectedDispatcher as i, dispatchInboundMessageWithBufferedDispatcher as n, dispatchInboundMessageWithDispatcher as r, dispatchInboundMessage as t };