openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
845 lines (844 loc) • 33.7 kB
JavaScript
import { l as toErrorObject } from "./error-coercion-D_-xJ90S.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { N as runWithDiagnosticTraceContext, T as createDiagnosticTraceContextFromActiveScope } from "./diagnostic-events-Cwe92uV3.js";
import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js";
import { o as resolveSessionStorePathCore } from "./paths-CXdaYWF_.js";
import { t as getGlobalHookRunner } from "./hook-runner-global-0kfmMG4T.js";
import { a as getReplyPayloadMetadata, i as copyReplyPayloadMetadata, y as setReplyPayloadMetadata } from "./reply-payload-DahKk-7n.js";
import { c as normalizeDeliveryContext, n as deliveryContextFromSession, r as deliveryContextKey } from "./delivery-context.shared-CXmRgetN.js";
import { s as copyChannelParticipantAdmissionEvidence } from "./admission-evidence-BW_2Woj4.js";
import { xt as updateSessionEntry } from "./session-accessor-YsytfDtG.js";
import { u as loadSessionEntryReadOnly } from "./session-accessor.sqlite-entry-CWk3jL7s.js";
import { f as clearChannelHistoryIfEnabled } from "./history-DLKGD0Dj.js";
import { t as appendAssistantMessageToSessionTranscript } from "./transcript-Dv8-hmIo.js";
import { c as isPlatformMessageNotDispatchedError, l as isPlatformMessageRejectedError, r as PlatformMessageNotDispatchedError } from "./deliver-types-Diy-VQKA.js";
import { r as resolveMessageReceiptPrimaryId } from "./receipt-DdpXkxJo.js";
import { o as settlePendingFinalDelivery } from "./delivery-completion-BrAAYzVK.js";
import { s as applyMessageSendingHook } from "./deliver-prepare-6uVAVf8W.js";
import { n as deriveInboundMessageHookContext, r as resolveInboundReplyHookTarget } from "./message-hook-mappers-oV7hnRsq.js";
import { l as summarizeOutboundPayloadForTransport } from "./payloads-DC1mf-yF.js";
import { f as normalizeEmptyPayloadForDelivery } from "./delivery-queue-reconciliation-kPHpqr5H.js";
import { a as withDispatchProcessedOutcomeSink } from "./dispatch-from-config-Djp-96vr.js";
import { n as suppressPendingFinalDelivery } from "./dispatch-from-config.pending-final-DOlooYOM.js";
import { g as assertReplyPayloadSessionWriterDeliveryAuthorized, l as createMessageSentEmitter } from "./delivery-queue-recovery-CeXGRhn8.js";
import { l as findDeliveryIntentOwner } from "./delivery-queue-storage-BMi71pp-.js";
import { n as getGatewayRecoveryRuntime } from "./server-recovery-runtime-context-Cx7vLPdb.js";
import { a as dispatchInboundMessageWithRoutedChannelDispatcher } from "./dispatch-rmguZY5M.js";
import { t as recordInboundSession } from "./session-CGo-oQbb.js";
import { t as isRecentOutboundMessageIdentity } from "./outbound-echo-Cr6_QlrD.js";
import { a as resolvePairLoopGuardSettings, r as createPairLoopGuard } from "./pair-loop-guard-runtime-Df7mJxV6.js";
import { r as hasVisibleChannelTurnDispatch, t as EMPTY_CHANNEL_TURN_DISPATCH_COUNTS } from "./dispatch-result-B75usq__.js";
import { n as runWithSessionInitConflictRetry } from "./session-init-conflict-retry-C_TqdGN3.js";
import { n as withReplySystemEventContext } from "./system-event-session-key-d4mk05_u.js";
import { t as createChannelReplyPipeline } from "./reply-pipeline-_oYGH8-B.js";
import { i as isChannelPartialDeliveryError, r as createSuppressedChannelDeliveryResult } from "./delivery-result-jWGDGh5u.js";
import { n as isDurableInboundReplyDeliveryHandled, r as throwIfDurableInboundReplyDeliveryFailed, t as deliverInboundReplyWithMessageSendContextCore } from "./durable-delivery-Bh9HHtYS.js";
//#region src/channels/turn/bot-loop-protection.ts
const channelBotPairLoopGuard = createPairLoopGuard({ pruneIntervalMs: 6e4 });
/** Records a bot pair interaction and returns whether the loop guard should suppress it. */
function recordChannelBotPairLoopAndCheckSuppression(params) {
return channelBotPairLoopGuard.recordAndCheck({
scopeId: params.scopeId,
conversationId: params.conversationId,
senderId: params.senderId,
receiverId: params.receiverId,
eventId: params.eventId,
settings: resolvePairLoopGuardSettings({
config: params.config,
defaultsConfig: params.defaultsConfig,
defaultEnabled: params.defaultEnabled
}),
nowMs: params.nowMs
});
}
//#endregion
//#region src/channels/turn/pending-delivery-notice.ts
const PENDING_DELIVERY_NOTICE = "I couldn’t confirm whether my previous reply reached this chat, so I won’t resend it automatically. Please ask for any missing remainder.";
function noticeId(intentId) {
return `main-session-restart-recovery:pending-final:${intentId}`;
}
async function deliverPendingDeliveryNotice(sessionKey, storePath) {
const entry = loadSessionEntryReadOnly({
sessionKey,
storePath,
readConsistency: "latest",
hydrateSkillPromptRefs: false
});
const notice = entry?.pendingDeliveryNotice;
const context = normalizeDeliveryContext(notice?.context);
const runtime = getGatewayRecoveryRuntime();
if (!entry || !runtime || !notice || notice.state !== "owed" || !context?.channel || !context.to || deliveryContextKey(context) !== deliveryContextKey(deliveryContextFromSession(entry))) return;
const idempotencyKey = noticeId(notice.intentId);
let delivered;
try {
delivered = !(await runtime.sendRecoveryNotice({
channel: context.channel,
to: context.to,
accountId: context.accountId,
threadId: context.threadId,
text: PENDING_DELIVERY_NOTICE,
idempotencyKey
})).suppressed;
} catch {
const owner = findDeliveryIntentOwner(idempotencyKey);
if (owner?.status !== "completed" && owner?.status !== "failed") return;
delivered = owner.status === "completed";
}
if (delivered && !(await appendAssistantMessageToSessionTranscript({
sessionKey,
storePath,
expectedSessionId: entry.sessionId,
text: PENDING_DELIVERY_NOTICE,
idempotencyKey
})).ok) return;
await updateSessionEntry({
sessionKey,
storePath
}, (current) => current.sessionId === entry.sessionId && current.pendingDeliveryNotice?.intentId === notice.intentId && current.pendingDeliveryNotice.state !== "acknowledged" ? {
pendingDeliveryNotice: {
...current.pendingDeliveryNotice,
state: delivered ? "acknowledged" : "unresolved"
},
updatedAt: Date.now()
} : null, {
skipMaintenance: true,
takeCacheOwnership: true
});
}
//#endregion
//#region src/channels/turn/execution.ts
const NO_ADDITIONAL_DELIVERY_SIGNALS = {};
const log = createSubsystemLogger("channels/turn/execution");
function emit(params) {
params.log?.({
channel: params.channel,
accountId: params.accountId,
...params.event
});
}
function clearPendingHistoryAfterTurn(params) {
if (!params?.isGroup || !params.historyKey || !params.historyMap || params.limit === void 0) return;
clearChannelHistoryIfEnabled({
historyMap: params.historyMap,
historyKey: params.historyKey,
limit: params.limit
});
}
function resolveObserveOnlyDispatchResult(params) {
return params.observeOnlyDispatchResult ?? {
queuedFinal: false,
counts: EMPTY_CHANNEL_TURN_DISPATCH_COUNTS
};
}
function resolveRecordSessionKey(params) {
const explicitSessionKey = params.record?.sessionKey;
if (explicitSessionKey === void 0) return params.ctxPayload.SessionKey ?? params.routeSessionKey;
const normalizedSessionKey = explicitSessionKey.trim();
if (!normalizedSessionKey) throw new Error("Channel turn record.sessionKey must be non-empty.");
if (normalizedSessionKey !== explicitSessionKey) throw new Error("Channel turn record.sessionKey must not include surrounding whitespace.");
return explicitSessionKey;
}
function maybeWarnZeroCountVisibleDispatch(params) {
if (params.admission?.kind === "observeOnly" || params.ctxPayload.InternalTurnSource !== void 0) return;
const dispatchResult = params.dispatchResult;
if (dispatchResult?.deferredToActiveRun) return;
if (hasVisibleChannelTurnDispatch(dispatchResult, NO_ADDITIONAL_DELIVERY_SIGNALS)) return;
const processed = params.processedOutcome;
const cause = processed ? `${processed.outcome}${processed.reason ? `:${processed.reason}` : ""}` : void 0;
log.warn(`visible channel turn dispatched with no queued reply payloads: channel=${params.channel} messageId=${params.messageId ?? "unknown"} sessionKey=${params.ctxPayload.SessionKey ?? params.routeSessionKey} cause=${cause ?? "unknown"}`);
emit({
...params,
event: {
stage: "dispatch",
event: "warning",
messageId: params.messageId,
sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
admission: params.admission?.kind ?? "dispatch",
reason: "zero-count-visible-dispatch"
}
});
}
function resolveBotLoopProtectionDrop(params) {
if (!params.botLoopProtection) return;
if (!recordChannelBotPairLoopAndCheckSuppression(params.botLoopProtection).suppressed) return;
const admission = {
kind: "drop",
reason: "bot-loop-protection"
};
emit({
...params,
event: {
stage: "authorize",
event: "drop",
messageId: params.messageId,
sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
admission: admission.kind,
reason: admission.reason
}
});
return {
admission,
dispatched: false,
ctxPayload: params.ctxPayload,
routeSessionKey: params.routeSessionKey
};
}
function resolveOutboundEchoDrop(params) {
const conversationId = [params.ctxPayload.NativeChannelId, params.ctxPayload.ChatId].find((value) => typeof value === "string" && value.trim().length > 0);
if (!conversationId) return;
const matchedMessageId = [
params.messageId,
params.ctxPayload.MessageSidFull,
params.ctxPayload.MessageSid
].find((messageId) => typeof messageId === "string" && isRecentOutboundMessageIdentity({
channel: params.channel,
accountId: params.accountId,
conversationId,
messageId
}));
const sourceId = params.outboundEchoSourceId?.trim();
const matchesSource = sourceId ? isRecentOutboundMessageIdentity({
channel: params.channel,
accountId: params.accountId,
conversationId,
sourceId
}) : false;
if (!matchedMessageId && !matchesSource) return;
const admission = {
kind: "drop",
reason: "outbound-echo"
};
emit({
...params,
event: {
stage: "authorize",
event: "drop",
messageId: params.messageId ?? matchedMessageId,
sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
admission: admission.kind,
reason: admission.reason
}
});
return {
admission,
dispatched: false,
ctxPayload: params.ctxPayload,
routeSessionKey: params.routeSessionKey
};
}
async function runPreparedChannelTurnCore(params, options) {
const trace = createDiagnosticTraceContextFromActiveScope();
return await runWithDiagnosticTraceContext(trace, () => runPreparedChannelTurnCoreInTrace(params, options));
}
async function runPreparedChannelTurnCoreInTrace(params, options) {
const admission = params.admission ?? { kind: "dispatch" };
const outboundEchoDrop = resolveOutboundEchoDrop(params);
if (outboundEchoDrop) {
clearPendingHistoryAfterTurn(params.history);
await params.runDispatchLifecycle?.onDispatchSkipped("outboundEcho");
return outboundEchoDrop;
}
const botLoopDrop = resolveBotLoopProtectionDrop(params);
if (botLoopDrop) {
clearPendingHistoryAfterTurn(params.history);
await params.runDispatchLifecycle?.onDispatchSkipped("botLoopProtection");
return botLoopDrop;
}
try {
const recordSessionKey = resolveRecordSessionKey(params);
if (params.ctxPayload.SessionTranscriptContext) {
const { mergeSessionTranscriptContext } = await import("./session-transcript-context.runtime.js");
await mergeSessionTranscriptContext({
agentId: params.ctxPayload.AgentId,
ctx: params.ctxPayload,
sessionKey: recordSessionKey,
storePath: params.storePath
});
}
emit({
...params,
event: {
stage: "record",
event: "start",
messageId: params.messageId,
sessionKey: recordSessionKey,
admission: admission.kind
}
});
try {
await params.recordInboundSession({
storePath: params.storePath,
sessionKey: recordSessionKey,
ctx: params.ctxPayload,
groupResolution: params.record?.groupResolution,
createIfMissing: params.record?.createIfMissing,
updateLastRoute: params.record?.updateLastRoute,
onRecordError: params.record?.onRecordError ?? (() => void 0),
trackSessionMetaTask: params.record?.trackSessionMetaTask
});
emit({
...params,
event: {
stage: "record",
event: "done",
messageId: params.messageId,
sessionKey: recordSessionKey,
admission: admission.kind
}
});
await params.afterRecord?.();
await deliverPendingDeliveryNotice(recordSessionKey, params.storePath);
} catch (err) {
emit({
...params,
event: {
stage: "record",
event: "error",
messageId: params.messageId,
sessionKey: recordSessionKey,
admission: admission.kind,
error: err
}
});
try {
await params.onPreDispatchFailure?.(err);
} catch {}
throw err;
}
emit({
...params,
event: {
stage: "dispatch",
event: "start",
messageId: params.messageId,
sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
admission: admission.kind
}
});
let dispatchResult;
try {
if (admission.kind === "observeOnly" && !options.suppressObserveOnlyDispatch) await params.runDispatch();
else if (admission.kind === "observeOnly") await params.runDispatchLifecycle?.onDispatchSkipped("observeOnly");
let processedOutcome;
if (admission.kind === "observeOnly") dispatchResult = resolveObserveOnlyDispatchResult(params);
else ({result: dispatchResult, processedOutcome} = await withDispatchProcessedOutcomeSink(() => params.runDispatch()));
maybeWarnZeroCountVisibleDispatch({
...params,
admission,
dispatchResult,
processedOutcome
});
} catch (err) {
emit({
...params,
event: {
stage: "dispatch",
event: "error",
messageId: params.messageId,
sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
admission: admission.kind,
error: err
}
});
throw err;
}
emit({
...params,
event: {
stage: "dispatch",
event: "done",
messageId: params.messageId,
sessionKey: params.ctxPayload.SessionKey ?? params.routeSessionKey,
admission: admission.kind
}
});
return {
admission,
dispatched: true,
ctxPayload: params.ctxPayload,
routeSessionKey: params.routeSessionKey,
dispatchResult
};
} finally {
clearPendingHistoryAfterTurn(params.history);
}
}
async function runPreparedChannelTurn(params) {
return await runPreparedChannelTurnCore(params, { suppressObserveOnlyDispatch: true });
}
//#endregion
//#region src/channels/turn/direct-delivery-custody.ts
const NO_PENDING_FINAL_CUSTODY = {
assertPlatformSendAuthorized: () => void 0,
onPlatformSendDispatch: () => Promise.resolve()
};
function resolvePendingFinalCompletion(payload) {
const identity = getReplyPayloadMetadata(payload)?.pendingFinalDeliveryCompletion;
return identity ? {
kind: "pending-final",
...identity
} : void 0;
}
function createDirectPendingFinalCustody(payload, fallbackStorePath) {
const completion = resolvePendingFinalCompletion(payload);
const hasWriterAuthority = Boolean(getReplyPayloadMetadata(payload)?.sessionWriterDeliveryAuthority);
if (!completion && !hasWriterAuthority) return;
const identity = completion ? (({ kind: _kind, ...value }) => value)(completion) : void 0;
let firstDispatch = true;
let admissionTail = Promise.resolve();
return {
bindPendingFinalDelivery: (nextPayload) => identity ? setReplyPayloadMetadata(nextPayload, { pendingFinalDeliveryCompletion: identity }) : nextPayload,
assertPlatformSendAuthorized: () => assertReplyPayloadSessionWriterDeliveryAuthorized(payload, fallbackStorePath),
onPlatformSendDispatch: () => {
const expectedStates = firstDispatch ? ["prepared", "queued"] : ["unknown"];
firstDispatch = false;
const admission = admissionTail.then(async () => {
assertReplyPayloadSessionWriterDeliveryAuthorized(payload, fallbackStorePath);
if (!completion) return;
const result = await settlePendingFinalDelivery(completion, "unknown", expectedStates);
if (result.state !== "unknown") throw new PlatformMessageNotDispatchedError("Pending final delivery ownership changed before platform dispatch", { cause: /* @__PURE__ */ new Error(`pending final delivery is ${result.state}`) });
});
admissionTail = admission.catch(() => void 0);
return admission;
}
};
}
function toCoreManagedDeliveryInfo(info) {
return {
kind: info.kind,
...info.assistantMessageIndex === void 0 ? {} : { assistantMessageIndex: info.assistantMessageIndex }
};
}
//#endregion
//#region src/channels/turn/route-dm-scope.ts
function applyRouteDmScope(context, dmScope) {
if (!dmScope || context.DmScope === dmScope) return context;
const scoped = {
...context,
DmScope: dmScope
};
copyChannelParticipantAdmissionEvidence(context, scoped);
return scoped;
}
//#endregion
//#region src/channels/turn/lifecycle.ts
function resolvePartialChannelDeliveryResult(error) {
return isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
}
function assembleResolvedChannelTurn(value) {
if (!("route" in value)) return value;
if ("runDispatch" in value) {
const { cfg, route, ...turn } = value;
return {
...turn,
ctxPayload: applyRouteDmScope(turn.ctxPayload, route.dmScope),
routeSessionKey: route.sessionKey,
storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: route.agentId }),
recordInboundSession
};
}
const { cfg, route, ...turn } = value;
return {
...turn,
ctxPayload: applyRouteDmScope(turn.ctxPayload, route.dmScope),
cfg,
agentId: route.agentId,
routeSessionKey: route.sessionKey,
storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: route.agentId }),
recordInboundSession
};
}
function resolveAssembledReplyPipeline(params) {
const adoption = params.turnAdoptionLifecycle ?? params.replyOptions?.turnAdoptionLifecycle;
let replyOptions = adoption ? {
...params.replyOptions,
turnAdoptionLifecycle: adoption
} : params.replyOptions;
if (params.routeSessionKey !== params.ctxPayload.SessionKey) replyOptions = withReplySystemEventContext(replyOptions ?? {}, { sessionKey: params.routeSessionKey });
if (!params.replyPipeline) return {
dispatcherOptions: params.dispatcherOptions,
replyOptions
};
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
cfg: params.cfg,
agentId: params.agentId,
channel: params.channel,
accountId: params.accountId,
...params.replyPipeline
});
return {
dispatcherOptions: {
...replyPipeline,
...params.dispatcherOptions
},
replyOptions: {
onModelSelected,
...replyOptions
}
};
}
function isExplicitlyNonVisibleChannelDelivery(result) {
return typeof result === "object" && result !== null && !Array.isArray(result) && result.visibleReplySent === false;
}
function markChannelDeliveryErrorVisible(error) {
if (typeof error === "object" && error !== null && !Array.isArray(error)) try {
Object.assign(error, {
sentBeforeError: true,
visibleReplySent: true
});
return error;
} catch {}
const visibleError = new Error("visible channel reply delivery failed", { cause: error });
Object.assign(visibleError, {
sentBeforeError: true,
visibleReplySent: true
});
return visibleError;
}
async function runChannelDeliveryObserver(params) {
if (!params.onDelivered) return;
try {
await params.onDelivered(params.payload, params.info, params.result);
} catch (error) {
throw isExplicitlyNonVisibleChannelDelivery(params.result) ? error : markChannelDeliveryErrorVisible(error);
}
}
function resolveChannelDeliveryMessageId(result) {
return result?.receipt ? resolveMessageReceiptPrimaryId(result.receipt) : result?.messageIds?.find((messageId) => messageId.trim());
}
async function settleChannelDeliveryAttempts(params) {
let preferredSettlementError;
for (const attempt of params.attempts) try {
const finalized = await settleChannelDeliveryAttempt({
attempt,
onDelivered: params.delivery.onDelivered,
onFinalizationError: async (error) => {
await Promise.resolve(params.delivery.onError?.(error, attempt.info));
},
emitMessageSent: params.emitMessageSent
});
params.onSettled?.(attempt.info, finalized);
} catch (error) {
if (preferredSettlementError === void 0 || resolvePartialChannelDeliveryResult(error) !== void 0 && resolvePartialChannelDeliveryResult(preferredSettlementError) === void 0) preferredSettlementError = error;
}
if (preferredSettlementError !== void 0) throw toErrorObject(preferredSettlementError, "channel delivery settlement failed");
}
async function settleFailedPendingFinalDelivery(payload, error) {
const completion = resolvePendingFinalCompletion(payload);
if (!completion) return;
if (isPlatformMessageRejectedError(error)) await settlePendingFinalDelivery(completion, "suppressed", [
"prepared",
"queued",
"unknown"
]);
else if (isPlatformMessageNotDispatchedError(error)) await settlePendingFinalDelivery(completion, "prepared", ["queued", "unknown"]);
else await settlePendingFinalDelivery(completion, "unknown", ["queued", "unknown"]);
}
async function settleChannelDeliveryAttempt(params) {
const { attempt } = params;
if (attempt.state === "rejected") {
const partial = resolvePartialChannelDeliveryResult(attempt.error);
if (!isPlatformMessageNotDispatchedError(attempt.error)) params.emitMessageSent?.({
success: false,
content: partial?.content ?? attempt.payload.text ?? "",
error: formatErrorMessage(attempt.error),
messageId: resolveChannelDeliveryMessageId(partial)
});
return;
}
let finalized;
try {
const result = attempt.result;
finalized = result ? result.finalization ? {
...result,
...await result.finalization,
finalization: void 0
} : result : void 0;
} catch (error) {
try {
await params.onFinalizationError?.(error);
} catch {}
await settleFailedPendingFinalDelivery(attempt.payload, error);
const partial = resolvePartialChannelDeliveryResult(error);
if (!isPlatformMessageNotDispatchedError(error)) params.emitMessageSent?.({
success: false,
content: partial?.content ?? attempt.payload.text ?? "",
error: formatErrorMessage(error),
messageId: resolveChannelDeliveryMessageId(partial)
});
throw toErrorObject(error, "channel delivery finalization failed");
}
if (!isExplicitlyNonVisibleChannelDelivery(finalized)) params.emitMessageSent?.({
success: true,
content: finalized?.content ?? attempt.payload.text ?? "",
messageId: resolveChannelDeliveryMessageId(finalized)
});
const completion = resolvePendingFinalCompletion(attempt.payload);
if (completion) await settlePendingFinalDelivery(completion, isExplicitlyNonVisibleChannelDelivery(finalized) ? "suppressed" : "delivered");
await runChannelDeliveryObserver({
onDelivered: params.onDelivered,
payload: attempt.payload,
info: attempt.info,
result: finalized
});
return finalized;
}
async function applyRoutedDirectMessageSending(params) {
const hookRunner = getGlobalHookRunner();
const hookCtx = deriveInboundMessageHookContext(params.turn.ctxPayload);
const hookResult = await applyMessageSendingHook({
hookRunner,
enabled: hookRunner?.hasHooks("message_sending") ?? false,
payload: params.payload,
payloadSummary: summarizeOutboundPayloadForTransport(params.payload),
to: resolveInboundReplyHookTarget(params.turn.ctxPayload, hookCtx),
channel: params.turn.channel,
accountId: params.turn.accountId,
replyToId: params.payload.replyToId ?? params.turn.ctxPayload.ReplyToIdFull ?? params.turn.ctxPayload.ReplyToId,
threadId: params.turn.ctxPayload.MessageThreadId,
sessionKey: params.turn.routeSessionKey
});
if (hookResult.cancelled) return {
payload: params.payload,
suppression: createSuppressedChannelDeliveryResult({
reason: "cancelled_by_message_sending_hook",
cancelReason: hookResult.cancelReason,
metadata: hookResult.hookMetadata
})
};
const payload = normalizeEmptyPayloadForDelivery(hookResult.payload);
if (!payload) return {
payload: hookResult.payload,
suppression: createSuppressedChannelDeliveryResult({ reason: hookResult.contentRewritten ? "empty_after_message_sending_hook" : "no_visible_payload" })
};
return { payload: copyReplyPayloadMetadata(params.payload, payload) };
}
function createObserveOnlyDeliveryAdapter() {
return { deliver: async () => ({ visibleReplySent: false }) };
}
async function dispatchChannelTurnWithDeliveryOwner(...args) {
const [params, ownership] = args;
const replyPipeline = resolveAssembledReplyPipeline(params);
const adoption = params.turnAdoptionLifecycle ?? params.replyOptions?.turnAdoptionLifecycle;
const delivery = params.admission?.kind === "observeOnly" ? createObserveOnlyDeliveryAdapter() : params.delivery;
const pendingDeliveryAttempts = [];
const normalizationSuppressionAttempts = [];
let agentRun = [];
const onAgentRunStart = replyPipeline.replyOptions?.onAgentRunStart;
const replyOptions = {
...replyPipeline.replyOptions,
onAgentRunStart: (...runStartArgs) => {
agentRun = [runStartArgs[0], runStartArgs[1]];
return onAgentRunStart?.(...runStartArgs);
}
};
const hookCtx = delivery.observeMessageSent ? deriveInboundMessageHookContext(params.ctxPayload) : void 0;
let messageSentEmitter;
const getMessageSentEmitter = () => {
if (!delivery.observeMessageSent || !hookCtx) return;
messageSentEmitter ??= createMessageSentEmitter({
hookRunner: getGlobalHookRunner(),
channel: params.channel,
to: resolveInboundReplyHookTarget(params.ctxPayload, hookCtx),
accountId: params.accountId,
sessionKeyForInternalHooks: params.routeSessionKey,
runId: agentRun[0],
isGroup: hookCtx.isGroup,
groupId: hookCtx.groupId,
logPrefix: "dispatchAssembledChannelTurn"
});
return messageSentEmitter;
};
return await runPreparedChannelTurnCore({
channel: params.channel,
accountId: params.accountId,
routeSessionKey: params.routeSessionKey,
storePath: params.storePath,
ctxPayload: params.ctxPayload,
recordInboundSession: params.recordInboundSession,
afterRecord: params.afterRecord,
record: params.record,
history: params.history,
admission: params.admission,
botLoopProtection: params.botLoopProtection,
outboundEchoSourceId: params.outboundEchoSourceId,
log: params.log,
messageId: params.messageId,
...adoption ? { runDispatchLifecycle: {
turnAdoptionLifecycle: adoption,
onDispatchSkipped: async () => await adoption.onAdopted()
} } : {},
runDispatch: async () => {
let dispatchResult;
let dispatchError;
try {
dispatchResult = await runWithSessionInitConflictRetry(() => (ownership === "routed-delivery" ? dispatchInboundMessageWithRoutedChannelDispatcher : params.dispatchReplyWithBufferedBlockDispatcher)({
ctx: params.ctxPayload,
cfg: params.cfg,
...ownership === "routed-delivery" ? {
...params.admission?.kind === "observeOnly" ? { suppressOutboundHooks: true } : {},
onReplyPayloadSuppressed: async (payload, info, reason) => {
await suppressPendingFinalDelivery(payload);
await runChannelDeliveryObserver({
onDelivered: delivery.onDelivered,
payload,
info,
result: createSuppressedChannelDeliveryResult({ reason })
});
}
} : {},
dispatcherOptions: {
...replyPipeline.dispatcherOptions,
onSkip: (payload, info) => {
replyPipeline.dispatcherOptions?.onSkip?.(payload, info);
if (info.reason !== "channel_transform") return;
const { reason: _reason, ...deliveryInfo } = info;
normalizationSuppressionAttempts.push({
state: "fulfilled",
payload,
info: deliveryInfo,
result: createSuppressedChannelDeliveryResult({ reason: info.reason })
});
},
deliver: async (payload, info) => {
const preparedPayloadResult = delivery.preparePayload ? await delivery.preparePayload(payload, info) : payload;
const preparedPayload = preparedPayloadResult === null ? null : copyReplyPayloadMetadata(payload, preparedPayloadResult);
if (preparedPayload === null) {
const suppression = createSuppressedChannelDeliveryResult({ reason: "no_visible_payload" });
await suppressPendingFinalDelivery(payload);
await runChannelDeliveryObserver({
onDelivered: delivery.onDelivered,
payload,
info,
result: suppression
});
return suppression;
}
const declaredDurable = "durable" in delivery ? delivery.durable : void 0;
const durableOptions = typeof declaredDurable === "function" ? await declaredDurable(preparedPayload, info) : declaredDurable;
if (durableOptions) {
const durable = await deliverInboundReplyWithMessageSendContextCore({
cfg: params.cfg,
channel: params.channel,
accountId: params.accountId,
agentId: params.agentId,
ctxPayload: params.ctxPayload,
payload: preparedPayload,
info,
executionIdentityToken: agentRun[1],
...durableOptions
});
throwIfDurableInboundReplyDeliveryFailed(durable);
if (isDurableInboundReplyDeliveryHandled(durable)) {
await runChannelDeliveryObserver({
onDelivered: delivery.onDelivered,
payload: preparedPayload,
info,
result: durable.delivery
});
return durable.delivery;
}
}
let effectivePayload = preparedPayload;
let result = void 0;
let directInfo = info;
try {
if (ownership === "routed-delivery" && "deliverWithProviderMessageSending" in delivery && delivery.deliverWithProviderMessageSending) {
const providerInfo = {
...info,
...createDirectPendingFinalCustody(effectivePayload, params.storePath) ?? NO_PENDING_FINAL_CUSTODY
};
directInfo = providerInfo;
result = await delivery.deliverWithProviderMessageSending(effectivePayload, providerInfo);
} else {
if (ownership === "routed-delivery" && params.admission?.kind !== "observeOnly") {
const hook = await applyRoutedDirectMessageSending({
turn: params,
payload: effectivePayload
});
effectivePayload = hook.payload;
if (hook.suppression) result = hook.suppression;
}
if (!result) {
if (!("deliver" in delivery) || !delivery.deliver) throw new Error("channel delivery adapter is missing a direct deliverer");
await createDirectPendingFinalCustody(effectivePayload, params.storePath)?.onPlatformSendDispatch();
result = await delivery.deliver(effectivePayload, toCoreManagedDeliveryInfo(info));
}
}
} catch (error) {
await settleFailedPendingFinalDelivery(effectivePayload, error);
if (delivery.observeMessageSent) await settleChannelDeliveryAttempt({
attempt: {
state: "rejected",
payload: effectivePayload,
info: directInfo,
error
},
onDelivered: delivery.onDelivered,
emitMessageSent: getMessageSentEmitter()?.emitMessageSent
});
throw error;
}
if (result?.finalization) {
result.finalization.catch(() => void 0);
pendingDeliveryAttempts.push({
state: "fulfilled",
payload: effectivePayload,
info: directInfo,
result
});
} else await settleChannelDeliveryAttempt({
attempt: {
state: "fulfilled",
payload: effectivePayload,
info: directInfo,
result
},
onDelivered: delivery.onDelivered,
emitMessageSent: delivery.observeMessageSent ? getMessageSentEmitter()?.emitMessageSent : void 0
});
return result;
},
onError: delivery.onError
},
dispatchReplyFromConfig: params.dispatchReplyFromConfig,
toolsAllow: params.toolsAllow,
replyOptions,
replyResolver: params.replyResolver
}), params.sessionInitRetry ? {
retryDelaysMs: params.sessionInitRetry.delaysMs,
signal: params.sessionInitRetry.signal,
sleep: params.sessionInitRetry.sleep
} : void 0);
} catch (error) {
dispatchError = error;
}
let settlementError;
try {
await settleChannelDeliveryAttempts({
attempts: normalizationSuppressionAttempts,
delivery
});
await settleChannelDeliveryAttempts({
attempts: pendingDeliveryAttempts,
delivery,
emitMessageSent: getMessageSentEmitter()?.emitMessageSent
});
} catch (error) {
settlementError = error;
}
if (settlementError !== void 0 && resolvePartialChannelDeliveryResult(settlementError) !== void 0) throw toErrorObject(settlementError, "channel delivery settlement failed");
if (dispatchError !== void 0) throw toErrorObject(dispatchError, "channel dispatch failed");
if (settlementError !== void 0) throw toErrorObject(settlementError, "channel delivery settlement failed");
return dispatchResult;
}
}, { suppressObserveOnlyDispatch: false });
}
async function dispatchAssembledChannelTurn(params) {
return await dispatchChannelTurnWithDeliveryOwner(params, "legacy-dispatcher");
}
async function dispatchRoutedChannelTurn(params) {
return await dispatchChannelTurnWithDeliveryOwner(assembleResolvedChannelTurn(params), "routed-delivery");
}
//#endregion
export { recordChannelBotPairLoopAndCheckSuppression as a, runPreparedChannelTurn as i, dispatchAssembledChannelTurn as n, dispatchRoutedChannelTurn as r, assembleResolvedChannelTurn as t };