openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
513 lines (512 loc) • 22.5 kB
JavaScript
import { u as toErrorObject } from "./error-coercion-1P2One0X.mjs";
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs";
import { t as formatErrorMessage } from "./errors-BSdPMFao.mjs";
import { l as resolveSessionStorePathCore } from "./paths-CAzSovN1.mjs";
import { t as getGlobalHookRunner } from "./hook-runner-global-BMhafKQZ.mjs";
import { s as getGroupThreadDispatchContext } from "./group-thread-context-Q8iIBktF.mjs";
import { T as setReplyPayloadMetadata, a as copyReplyPayloadMetadata, s as getReplyPayloadMetadata } from "./reply-payload-DBtQZ6PK.mjs";
import { a as copyChannelParticipantAdmissionEvidence } from "./admission-evidence-DswNfzJ7.mjs";
import { c as isPlatformMessageNotDispatchedError, l as isPlatformMessageRejectedError, r as PlatformMessageNotDispatchedError } from "./deliver-types-Diy-VQKA.mjs";
import { i as resolveMessageReceiptPrimaryId } from "./receipt-Cu38Wczp.mjs";
import { c as settlePendingFinalDelivery } from "./delivery-completion-Dbf9XT4U.mjs";
import { n as createStructuredOutboundPayloadPlan, u as summarizeOutboundPayloadForTransport } from "./payloads-Bufmd7PI.mjs";
import { p as isReplyDispatchDeliveryPending } from "./reply-dispatcher-BmqXN2zF.mjs";
import { c as applyMessageSendingHook } from "./deliver-prepare-DDbw7906.mjs";
import { n as deriveInboundMessageHookContext, r as resolveInboundReplyHookTarget } from "./message-hook-mappers-QJ73jWnL.mjs";
import { m as normalizeEmptyPayloadForDelivery } from "./delivery-queue-reconciliation-f1V4Resu.mjs";
import { a as dispatchInboundMessageWithRoutedChannelDispatcher } from "./dispatch-QTvqNYOP.mjs";
import { n as withReplySystemEventContext } from "./system-event-session-key-d4mk05_u.mjs";
import { n as suppressPendingFinalDelivery } from "./dispatch-from-config.pending-final-qh6rgx-m.mjs";
import { b as assertReplyPayloadSessionWriterDeliveryAuthorized, l as createMessageSentEmitter } from "./delivery-queue-recovery-D4z7b9eg.mjs";
import { f as isChannelPartialDeliveryError, u as createSuppressedChannelDeliveryResult } from "./live-B6hIHTTD.mjs";
import { t as recordInboundSession } from "./session-BLJHMJ3b.mjs";
import { n as runWithSessionInitConflictRetry } from "./session-init-conflict-retry-B8fl5aj2.mjs";
import { i as throwIfDurableInboundReplyDeliveryFailed, n as deliverStructuredInboundReplyWithMessageSendContextCore, r as isDurableInboundReplyDeliveryHandled, t as deliverInboundReplyWithMessageSendContextCore } from "./durable-delivery-wrXvD8Iq.mjs";
import { n as runPreparedChannelTurnCore } from "./execution-DiEbCIan.mjs";
import { t as createChannelReplyPipeline } from "./reply-pipeline-BgOuETYW.mjs";
//#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.participant ? { participant: info.participant } : {},
...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;
const { cfg, route } = value;
const routing = {
ctxPayload: applyRouteDmScope(value.ctxPayload, route.dmScope),
routeSessionKey: route.sessionKey,
storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: route.agentId }),
recordInboundSession
};
if ("runDispatch" in value) {
const { cfg: _cfg, route: _route, ...turn } = value;
return {
...turn,
...routing
};
}
const { cfg: _cfg, route: _route, ...turn } = value;
return {
...turn,
...routing,
cfg,
agentId: route.agentId
};
}
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 isRecord(result) && result.visibleReplySent === false;
}
function markChannelDeliveryErrorVisible(error) {
if (isRecord(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 || isReplyDispatchDeliveryPending(params.result)) 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(attempts, delivery) {
let preferredSettlementError;
for (const attempt of attempts) try {
await settleChannelDeliveryAttempt(attempt, delivery.onDelivered, (error) => delivery.onError?.(error, attempt.info));
} 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(attempt, onDelivered, onFinalizationError) {
const emitFailure = (error) => {
const partial = resolvePartialChannelDeliveryResult(error);
if (!isPlatformMessageNotDispatchedError(error)) attempt.emitMessageSent?.({
success: false,
content: partial?.content ?? attempt.payload.text ?? "",
error: formatErrorMessage(error),
messageId: resolveChannelDeliveryMessageId(partial)
});
};
if (attempt.state === "rejected") {
emitFailure(attempt.error);
return;
}
let finalized;
try {
finalized = attempt.result || void 0;
if (finalized?.finalization) finalized = {
...finalized,
...await finalized.finalization,
finalization: void 0
};
} catch (error) {
try {
await onFinalizationError?.(error);
} catch {}
await settleFailedPendingFinalDelivery(attempt.payload, error);
emitFailure(error);
throw toErrorObject(error, "channel delivery finalization failed");
}
const pending = isReplyDispatchDeliveryPending(finalized);
if (!pending && !isExplicitlyNonVisibleChannelDelivery(finalized)) attempt.emitMessageSent?.({
success: true,
content: finalized?.content ?? attempt.payload.text ?? "",
messageId: resolveChannelDeliveryMessageId(finalized)
});
const completion = resolvePendingFinalCompletion(attempt.payload);
if (completion) await settlePendingFinalDelivery(completion, pending ? "unknown" : isExplicitlyNonVisibleChannelDelivery(finalized) ? "suppressed" : "delivered");
await runChannelDeliveryObserver({
onDelivered,
payload: attempt.payload,
info: attempt.info,
result: 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;
const group = getGroupThreadDispatchContext();
if (!group && messageSentEmitter) return messageSentEmitter;
const emitter = createMessageSentEmitter({
hookRunner: getGlobalHookRunner(),
channel: params.channel,
to: resolveInboundReplyHookTarget(params.ctxPayload, hookCtx),
accountId: params.accountId,
sessionKeyForInternalHooks: group?.ctx.SessionKey ?? params.routeSessionKey,
runId: group ? group.runState.runId : agentRun[0],
isGroup: hookCtx.isGroup,
groupId: hookCtx.groupId,
logPrefix: "dispatchAssembledChannelTurn"
});
if (!group) messageSentEmitter = emitter;
return emitter;
};
const suppressReply = async (payload, info, reason) => {
const result = createSuppressedChannelDeliveryResult({ reason });
await suppressPendingFinalDelivery(payload);
await runChannelDeliveryObserver({
onDelivered: delivery.onDelivered,
payload,
info,
result
});
return result;
};
const onReplySuppressed = async (payload, info, reason) => {
await suppressReply(payload, info, reason);
};
const rawDeliver = delivery.deliver?.bind(delivery);
const rawProviderDeliver = "deliverWithProviderMessageSending" in delivery ? delivery.deliverWithProviderMessageSending?.bind(delivery) : void 0;
const preparedDeliver = delivery.deliverPrepared?.bind(delivery);
const preparedProviderDeliver = "deliverPreparedWithProviderMessageSending" in delivery ? delivery.deliverPreparedWithProviderMessageSending?.bind(delivery) : void 0;
const rawOperation = {
prepare: (payload) => ({ payload }),
deliver: rawDeliver ? (value, info) => rawDeliver(value.payload, info) : void 0,
deliverWithProviderMessageSending: rawProviderDeliver ? (value, info) => rawProviderDeliver(value.payload, info) : void 0,
deliverDurable: (value, context) => deliverInboundReplyWithMessageSendContextCore({
...context,
payload: value.payload
})
};
async function deliverReply(payload, info, operation) {
const preparedPayloadResult = delivery.preparePayload ? await delivery.preparePayload(payload, info) : payload;
const preparedValue = preparedPayloadResult === null ? null : operation.prepare(copyReplyPayloadMetadata(payload, preparedPayloadResult));
if (preparedValue === null) return await suppressReply(payload, info, "no_visible_payload");
const preparedPayload = preparedValue.payload;
const declaredDurable = "durable" in delivery ? delivery.durable : void 0;
const durableOptions = typeof declaredDurable === "function" ? await declaredDurable(preparedPayload, info) : declaredDurable;
if (durableOptions) {
const durable = await operation.deliverDurable(preparedValue, {
cfg: params.cfg,
channel: params.channel,
accountId: params.accountId,
agentId: params.agentId,
ctxPayload: params.ctxPayload,
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 effectiveValue = preparedValue;
let effectivePayload = preparedPayload;
let result = void 0;
let directInfo = info;
const emitMessageSentForDelivery = delivery.observeMessageSent ? getGroupThreadDispatchContext() ? getMessageSentEmitter()?.emitMessageSent : (event) => getMessageSentEmitter()?.emitMessageSent(event) : void 0;
try {
if (ownership === "routed-delivery" && operation.deliverWithProviderMessageSending) {
const providerInfo = {
...info,
...createDirectPendingFinalCustody(effectivePayload, params.storePath) ?? NO_PENDING_FINAL_CUSTODY
};
directInfo = providerInfo;
result = await operation.deliverWithProviderMessageSending(effectiveValue, 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;
else {
const nextValue = operation.prepare(effectivePayload);
if (nextValue) {
effectiveValue = nextValue;
effectivePayload = nextValue.payload;
} else result = createSuppressedChannelDeliveryResult({ reason: "no_visible_payload" });
}
}
if (!result) {
if (!operation.deliver) throw new Error("channel delivery adapter is missing a direct deliverer");
await createDirectPendingFinalCustody(effectivePayload, params.storePath)?.onPlatformSendDispatch();
result = await operation.deliver(effectiveValue, toCoreManagedDeliveryInfo(info));
}
}
} catch (error) {
await settleFailedPendingFinalDelivery(effectivePayload, error);
if (delivery.observeMessageSent) await settleChannelDeliveryAttempt({
state: "rejected",
payload: effectivePayload,
info: directInfo,
error,
emitMessageSent: emitMessageSentForDelivery
}, delivery.onDelivered);
throw error;
}
const attempt = {
state: "fulfilled",
payload: effectivePayload,
info: directInfo,
result,
emitMessageSent: emitMessageSentForDelivery
};
if (result?.finalization) {
result.finalization.catch(() => void 0);
pendingDeliveryAttempts.push(attempt);
} else await settleChannelDeliveryAttempt(attempt, delivery.onDelivered);
return result;
}
const deliverPrepared = (plan, info) => deliverReply(plan.payload, info, {
prepare: (payload) => {
const [entry] = createStructuredOutboundPayloadPlan([payload]);
return entry ? {
...entry,
sourceIndex: plan.sourceIndex
} : null;
},
deliver: preparedDeliver ?? rawOperation.deliver,
deliverWithProviderMessageSending: preparedProviderDeliver ?? rawOperation.deliverWithProviderMessageSending,
deliverDurable: (value, context) => deliverStructuredInboundReplyWithMessageSendContextCore({
...context,
plan: value
})
});
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: onReplySuppressed
} : {},
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: (payload, info) => deliverReply(payload, info, rawOperation),
deliverPrepared,
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(normalizationSuppressionAttempts, delivery);
await settleChannelDeliveryAttempts(pendingDeliveryAttempts, delivery);
} 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 { dispatchAssembledChannelTurn as n, dispatchRoutedChannelTurn as r, assembleResolvedChannelTurn as t };