UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

179 lines (178 loc) 6.56 kB
import { i as formatErrorMessage } from "./errors-BXgSefBE.js"; import { t as createSubsystemLogger } from "./subsystem-BzXSmsuh.js"; import { a as createRenderedMessageBatch, n as deliverOutboundPayloadsInternal, s as isOutboundDeliveryError } from "./deliver-BHfQle87.js"; import { c as markLiveMessagePreviewUpdated, t as createLiveMessageState } from "./live-DjttXqqq.js"; import { t as createMessageReceiptFromOutboundResults } from "./receipt-B3SXxhHV.js"; //#region src/channels/message/send.ts const log = createSubsystemLogger("channels/message/send"); const neverAbortedSignal = new AbortController().signal; function toDurableMessageIntent(intent, renderedBatch) { return { id: intent.id, channel: intent.channel, to: intent.to, ...intent.accountId ? { accountId: intent.accountId } : {}, durability: intent.queuePolicy === "required" ? "required" : "best_effort", renderedBatch }; } function toDurablePayloadOutcome(outcome) { return outcome; } function toDurablePayloadOutcomes(outcomes) { return outcomes.map((outcome) => toDurablePayloadOutcome(outcome)); } async function withDurableMessageSendContext(params, run) { let deliveryIntent; const { attempt, durability, onDeleteReceipt, onEditReceipt, onCommitReceipt, onPreviewUpdate, onSendFailure, onPayloadDeliveryOutcome, payloads, preview, previousReceipt, signal, abortSignal, ...deliveryParams } = params; const effectiveSignal = signal ?? abortSignal; const queuePolicy = durability === "best_effort" ? "best_effort" : "required"; let liveState = preview ?? createLiveMessageState(); const ctx = { id: `${params.channel}:${params.to}`, channel: params.channel, to: params.to, ...params.accountId ? { accountId: params.accountId } : {}, durability: durability ?? "required", attempt: attempt ?? 1, signal: effectiveSignal ?? neverAbortedSignal, ...previousReceipt ? { previousReceipt } : {}, preview: liveState, render: async () => createRenderedMessageBatch(payloads), previewUpdate: async (rendered) => { liveState = onPreviewUpdate ? await onPreviewUpdate(rendered, liveState) : markLiveMessagePreviewUpdated(liveState, rendered); ctx.preview = liveState; return liveState; }, send: async (rendered) => { const payloadOutcomes = []; const durablePayloadOutcomes = () => toDurablePayloadOutcomes(payloadOutcomes); try { const results = await deliverOutboundPayloadsInternal({ ...deliveryParams, payloads: rendered.payloads, renderedBatchPlan: rendered.plan, queuePolicy, ...effectiveSignal ? { abortSignal: effectiveSignal } : {}, onPayloadDeliveryOutcome: (outcome) => { payloadOutcomes.push(outcome); onPayloadDeliveryOutcome?.(outcome); }, onDeliveryIntent: (intent) => { deliveryIntent = intent; ctx.intent = toDurableMessageIntent(intent, rendered); } }); const receipt = createMessageReceiptFromOutboundResults({ results, threadId: params.threadId == null ? void 0 : String(params.threadId), replyToId: params.replyToId ?? void 0 }); const failedOutcome = payloadOutcomes.find((outcome) => outcome.status === "failed"); if (failedOutcome) { if (results.length > 0) return { status: "partial_failed", results, receipt, error: failedOutcome.error, sentBeforeError: true, ...deliveryIntent ? { deliveryIntent } : {}, ...payloadOutcomes.length > 0 ? { payloadOutcomes: durablePayloadOutcomes() } : {} }; return { status: "failed", error: failedOutcome.error, stage: failedOutcome.stage, ...payloadOutcomes.length > 0 ? { payloadOutcomes: durablePayloadOutcomes() } : {} }; } if (results.length === 0) return { status: "suppressed", results: [], receipt, ...deliveryIntent ? { deliveryIntent } : {}, reason: payloadOutcomes.find((outcome) => outcome.status === "suppressed")?.reason ?? "no_visible_result", ...payloadOutcomes.length > 0 ? { payloadOutcomes: durablePayloadOutcomes() } : {} }; return { status: "sent", results, receipt, ...deliveryIntent ? { deliveryIntent } : {}, ...payloadOutcomes.length > 0 ? { payloadOutcomes: durablePayloadOutcomes() } : {} }; } catch (error) { if (isOutboundDeliveryError(error)) { if (error.results.length > 0) { const receipt = createMessageReceiptFromOutboundResults({ results: error.results, threadId: params.threadId == null ? void 0 : String(params.threadId), replyToId: params.replyToId ?? void 0 }); return { status: "partial_failed", results: error.results, receipt, error, sentBeforeError: true, ...deliveryIntent ? { deliveryIntent } : {}, ...error.payloadOutcomes.length > 0 ? { payloadOutcomes: toDurablePayloadOutcomes(error.payloadOutcomes) } : {} }; } return { status: "failed", error, stage: error.stage, ...error.payloadOutcomes.length > 0 ? { payloadOutcomes: toDurablePayloadOutcomes(error.payloadOutcomes) } : {} }; } return { status: "failed", error }; } }, edit: async (receipt, rendered) => { if (!onEditReceipt) throw new Error("message send context edit is not configured"); const editedReceipt = await onEditReceipt(receipt, rendered); liveState = { ...liveState, receipt: editedReceipt, lastRendered: rendered }; ctx.preview = liveState; return editedReceipt; }, delete: async (receipt) => { if (!onDeleteReceipt) throw new Error("message send context delete is not configured"); await onDeleteReceipt(receipt); }, commit: async (receipt) => { await onCommitReceipt?.(receipt); }, fail: async (error) => { try { await onSendFailure?.(error); } catch (cleanupError) { log.warn(`message send failure cleanup failed; preserving original send error: ${formatErrorMessage(cleanupError)}`); } } }; try { return await run(ctx); } catch (error) { await ctx.fail(error); throw error; } } async function sendDurableMessageBatch(params) { return await withDurableMessageSendContext(params, async (ctx) => { const rendered = await ctx.render(); const result = await ctx.send(rendered); if (result.status === "sent" || result.status === "suppressed") await ctx.commit(result.receipt); else await ctx.fail(result.error); return result; }); } //#endregion export { withDurableMessageSendContext as n, sendDurableMessageBatch as t };