openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
224 lines (223 loc) • 10.5 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { a as getPluginRuntimeGatewayRequestScope, u as withPluginRuntimeRegistryScope } from "./gateway-request-scope-BSmCVVpP.mjs";
import { _ as isPluginRegistryGatewayViewOf, d as getPluginRegistryGatewayOwner, u as getPluginRegistryGatewayChannelRegistration } from "./registry-lifecycle-CZIcWb1R.mjs";
import { t as runOutsidePluginRuntimeGenerationScope } from "./generation-scope-tH9UXyLm.mjs";
import { i as getPluginRuntimeLoadContext } from "./load-context-wgCwX0mF.mjs";
import { s as getGroupThreadDispatchContext } from "./group-thread-context-Dq9sbt-L.mjs";
import { _ as isReplyPayloadTargetSuppressed } from "./reply-payload-NzglYOOT.mjs";
import { r as PlatformMessageNotDispatchedError } from "./deliver-types-Diy-VQKA.mjs";
import { c as isProvenDeliveryNotSentError } from "./delivery-recovery.shared-AM7vMnRv.mjs";
import { s as resolveOutboundDurableFinalDeliverySupport } from "./deliver-prepare-eSoKByIL.mjs";
import { t as buildOutboundSessionContext } from "./session-context-Kov7ff_A.mjs";
import { n as sendDurableMessageBatchCore, r as sendStructuredDurableMessageBatchCore, t as durableMessageBatchMayHaveReachedRecipient } from "./send-CqAjSzNt.mjs";
import { t as deriveDurableFinalDeliveryRequirements } from "./capabilities-CZr1Vdri.mjs";
import { t as normalizeDeliverableOutboundChannel } from "./channel-resolution-D9vhAPIJ.mjs";
import "./deliver-ByaMG-sT.mjs";
import { c as createChannelDeliveryResultFromReceipt, u as createChannelPartialDeliveryError } from "./live-TWzvpgza.mjs";
import { isDeepStrictEqual } from "node:util";
//#region src/channels/turn/durable-delivery-runtime.ts
/** Final delivery is a new operation of the admitting Gateway, not of the completed model turn. */
function withDurableDeliveryRuntime(input, deliver) {
const registry = getPluginRuntimeGatewayRequestScope()?.pluginRegistry;
const owner = registry && getPluginRegistryGatewayOwner(registry);
if (!registry || !owner) return deliver(input.cfg);
const current = owner.current();
const reject = (message) => {
throw new PlatformMessageNotDispatchedError(message, {
cause: new Error(message),
retryable: false
});
};
if (!current) return reject("The Gateway that admitted this reply is closing.");
const assertCurrent = () => {
if (owner.current() !== current) reject("The reply delivery runtime changed before sending.");
};
if (current === registry || isPluginRegistryGatewayViewOf(registry, current)) return runOutsidePluginRuntimeGenerationScope(() => withPluginRuntimeRegistryScope(current, () => deliver(input.cfg, assertCurrent)));
const cfg = getPluginRuntimeLoadContext(current)?.rawConfig;
const channel = current.channels.find((entry) => entry.plugin.id === input.channel);
const prepareRuntimeHandoff = input.prepareRuntimeHandoff;
const admittedChannel = getPluginRegistryGatewayChannelRegistration(registry, input.channel);
const retainedChannel = channel && admittedChannel?.pluginId === channel.pluginId && admittedChannel.plugin === channel.plugin;
if (!cfg || !isDeepStrictEqual(cfg.channels?.[input.channel], input.cfg.channels?.[input.channel]) || !isDeepStrictEqual(cfg.channels?.defaults, input.cfg.channels?.defaults) || !channel || !retainedChannel || !isDeepStrictEqual(cfg.plugins?.entries?.[channel.pluginId], input.cfg.plugins?.entries?.[channel.pluginId])) return reject("The reply channel changed; delivery was not started.");
return runOutsidePluginRuntimeGenerationScope(() => withPluginRuntimeRegistryScope(current, () => {
const preparedCfg = prepareRuntimeHandoff ? prepareRuntimeHandoff(cfg) : cfg;
assertCurrent();
return deliver(preparedCfg, assertCurrent);
}));
}
//#endregion
//#region src/channels/turn/durable-delivery.ts
function resolveDeliveryTarget(params) {
return normalizeOptionalString(params.to) ?? normalizeOptionalString(params.ctxPayload.OriginatingTo) ?? normalizeOptionalString(params.ctxPayload.To);
}
function resolveDurableInboundReplyToId(params) {
if (isReplyPayloadTargetSuppressed(params.payload)) return null;
if (params.replyToId === null || params.payload.replyToId === null) return null;
return normalizeOptionalString(params.replyToId) ?? normalizeOptionalString(params.payload.replyToId) ?? normalizeOptionalString(params.ctxPayload.ReplyToIdFull) ?? normalizeOptionalString(params.ctxPayload.ReplyToId);
}
function toDeliveryIntent(intent) {
return {
id: intent.id,
kind: "outbound_queue",
queuePolicy: intent.queuePolicy
};
}
function resolveDurableSuppression(send) {
const hookEffect = send.payloadOutcomes?.find((outcome) => outcome.status === "suppressed")?.hookEffect;
return {
reason: send.reason,
...hookEffect?.cancelReason ? { cancelReason: hookEffect.cancelReason } : {},
...hookEffect?.metadata ? { metadata: hookEffect.metadata } : {}
};
}
function isDurableInboundReplyDeliveryHandled(result) {
return result.status === "handled_visible" || result.status === "handled_no_send";
}
function throwIfDurableInboundReplyDeliveryFailed(result) {
if (result.status === "failed") throw result.error;
}
function resolveAcceptedVisibleContent(results) {
return results.map((result) => result.meta?.visibleText).filter((value) => typeof value === "string").join("") || void 0;
}
async function deliverInboundReplyWithMessageSendContextCore(params) {
return await deliverInboundReplyWithMessageSendContext(params, sendDurableMessageBatchCore);
}
/** Delivers a prepared final reply through the same durable owner without parsing its text. */
async function deliverStructuredInboundReplyWithMessageSendContextCore(params) {
const { plan, ...context } = params;
return await deliverInboundReplyWithMessageSendContext({
...context,
payload: plan.payload
}, ({ payloads: _payloads, ...sendParams }) => sendStructuredDurableMessageBatchCore({
...sendParams,
plan: [plan]
}));
}
async function deliverInboundReplyWithMessageSendContext(input, sendBatch) {
if (input.info.kind !== "final") return {
status: "not_applicable",
reason: "non_final"
};
try {
return await withDurableDeliveryRuntime(input, (cfg, assertCurrent) => deliverAdmittedInboundReply({
...input,
cfg
}, sendBatch, assertCurrent));
} catch (error) {
return {
status: "failed",
error
};
}
}
async function deliverAdmittedInboundReply(input, sendBatch, assertCurrent) {
const group = getGroupThreadDispatchContext();
const params = group ? {
...input,
agentId: group.ctx.AgentId ?? input.agentId,
ctxPayload: group.ctx,
runId: group.runState.runId,
executionIdentityToken: group.runState.executionIdentityToken
} : input;
const channel = normalizeDeliverableOutboundChannel(params.channel);
const to = resolveDeliveryTarget(params);
if (!channel) return {
status: "unsupported",
reason: "missing_channel"
};
if (!to) return {
status: "unsupported",
reason: "missing_target"
};
const replyToId = resolveDurableInboundReplyToId(params);
const threadId = "threadId" in params ? params.threadId : params.ctxPayload.MessageThreadId;
const requiredCapabilities = params.requiredCapabilities ?? deriveDurableFinalDeliveryRequirements({
payload: params.payload,
replyToId,
threadId,
silent: params.silent
});
const durability = requiredCapabilities.reconcileUnknownSend === true ? "required" : "best_effort";
const support = await resolveOutboundDurableFinalDeliverySupport({
cfg: params.cfg,
agentId: params.agentId,
channel,
requirements: requiredCapabilities
});
if (!support.ok) return {
status: "unsupported",
reason: support.reason,
...support.capability ? { capability: support.capability } : {}
};
const session = buildOutboundSessionContext({
cfg: params.cfg,
sessionKey: params.ctxPayload.SessionKey,
policySessionKey: params.ctxPayload.RuntimePolicySessionKey,
conversationType: params.ctxPayload.ChatType,
agentId: params.agentId,
requesterAccountId: params.accountId ?? params.ctxPayload.AccountId,
requesterSenderId: params.ctxPayload.SenderId ?? params.ctxPayload.From,
requesterSenderName: params.ctxPayload.SenderName,
requesterSenderUsername: params.ctxPayload.SenderUsername,
requesterSenderE164: params.ctxPayload.SenderE164
});
assertCurrent?.();
const send = await sendBatch({
assertDirectAdapterHandoff: assertCurrent,
cfg: params.cfg,
channel,
to,
accountId: params.accountId,
payloads: [params.payload],
...params.runId ?? params.executionIdentityToken?.runId ? { runId: params.runId ?? params.executionIdentityToken?.runId } : {},
...params.executionIdentityToken ? { executionIdentityToken: params.executionIdentityToken } : {},
threadId,
replyToId,
replyToMode: params.replyToMode,
formatting: params.formatting,
identity: params.identity,
deps: params.deps,
mediaAccess: params.mediaAccess,
silent: params.silent,
durability,
...requiredCapabilities.reconcileUnknownSend === true ? { requireUnknownSendReconciliation: true } : {},
session,
gatewayClientScopes: params.ctxPayload.GatewayClientScopes ?? []
});
if (send.status === "failed") return {
status: "failed",
error: send.error,
...!durableMessageBatchMayHaveReachedRecipient(send) && isProvenDeliveryNotSentError(send.error) ? { sentBeforeError: false } : {}
};
const content = send.status === "partial_failed" ? resolveAcceptedVisibleContent(send.results) : void 0;
const receiptDelivery = createChannelDeliveryResultFromReceipt({
receipt: send.receipt,
threadId: threadId == null ? void 0 : String(threadId),
...replyToId ? { replyToId } : {},
visibleReplySent: send.status !== "suppressed",
...content ? { content } : {},
...send.deliveryIntent ? { deliveryIntent: toDeliveryIntent(send.deliveryIntent) } : {}
});
if (send.status === "partial_failed") return {
status: "failed",
error: createChannelPartialDeliveryError(send.error, {
...receiptDelivery,
visibleReplySent: true
}),
sentBeforeError: true
};
if (send.status === "suppressed") return {
status: "handled_no_send",
reason: "no_visible_result",
delivery: {
...receiptDelivery,
suppression: resolveDurableSuppression(send)
}
};
return {
status: "handled_visible",
delivery: receiptDelivery
};
}
//#endregion
export { throwIfDurableInboundReplyDeliveryFailed as i, deliverStructuredInboundReplyWithMessageSendContextCore as n, isDurableInboundReplyDeliveryHandled as r, deliverInboundReplyWithMessageSendContextCore as t };