openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
406 lines (405 loc) • 15.5 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { n as normalizeAccountId } from "./account-id-Df9e41E6.js";
import { o as channelRouteTargetsMatchExact } from "./channel-route-D7X5briz.js";
import { t as matchesApprovalRequestFilters } from "./approval-request-filters-W2XYHus7.js";
import { p as getExecApprovalReplyMetadata } from "./exec-approval-reply-BXMsffTv.js";
import { a as doesApprovalRequestMatchChannelAccount, r as resolveApprovalRequestSessionTarget, t as resolveApprovalRequestOriginTarget } from "./exec-approval-session-target-DfDihhuU.js";
//#region src/plugin-sdk/approval-native-helpers.ts
/** Compare channel-native approval targets with the same normalization used by outbound routes. */
function nativeApprovalTargetsMatch(params) {
return channelRouteTargetsMatchExact({
left: {
channel: params.channel,
to: params.left.to,
accountId: params.left.accountId,
threadId: params.left.threadId
},
right: {
channel: params.channel,
to: params.right.to,
accountId: params.right.accountId,
threadId: params.right.threadId
}
});
}
/** Decide whether a channel-native exec approval route replaces the local text prompt. */
function shouldSuppressLocalNativeExecApprovalPrompt(params) {
if (params.hint?.kind !== "approval-pending" || params.hint.approvalKind !== "exec") return false;
if (params.hint.nativeRouteActive !== true) return false;
const metadata = getExecApprovalReplyMetadata(params.payload);
if (!metadata || metadata.approvalKind !== "exec") return false;
if (!(params.isNativeDeliveryEnabled ?? params.isTransportEnabled)?.({
cfg: params.cfg,
accountId: params.accountId
})) return false;
const config = params.resolveApprovalConfig?.({
cfg: params.cfg,
accountId: params.accountId,
metadata
}) ?? params.cfg.approvals?.exec;
if ((params.requireApprovalConfigEnabled ?? params.resolveApprovalConfig === void 0) && !config?.enabled) return false;
if (params.enforceForwardingMode ?? params.resolveApprovalConfig === void 0) {
const mode = config?.mode ?? "session";
if (mode !== "session" && mode !== "both" && !params.hasExactTargetProof) return false;
}
if (params.isSessionRouteEligible && !params.isSessionRouteEligible({
cfg: params.cfg,
accountId: params.accountId,
metadata
})) return false;
return matchesApprovalRequestFilters({
request: {
agentId: metadata.agentId,
sessionKey: metadata.sessionKey
},
agentFilter: config?.agentFilter,
sessionFilter: config?.sessionFilter,
fallbackAgentIdFromSessionKey: params.fallbackAgentIdFromSessionKey ?? true
});
}
function isNativeApprovalTarget(value) {
return Boolean(value && typeof value === "object" && typeof value.to === "string");
}
function nativeApprovalTargetMatcher(channel) {
return (left, right) => isNativeApprovalTarget(left) && isNativeApprovalTarget(right) && nativeApprovalTargetsMatch({
channel,
left,
right
});
}
/** Infer approval family from the request shape unless the caller already knows it. */
function resolveApprovalKind(request, approvalKind) {
if (approvalKind) return approvalKind;
return "command" in request.request ? "exec" : "plugin";
}
function resolveApprovalForwardingConfig(params) {
return params.approvalKind === "plugin" ? params.cfg.approvals?.plugin : params.cfg.approvals?.exec;
}
function normalizeApprovalForwardingMode(mode) {
return mode ?? "session";
}
function approvalModeIncludesSession(mode) {
return mode === "session" || mode === "both";
}
function approvalModeIncludesTargets(mode) {
return mode === "targets" || mode === "both";
}
function matchesForwardingFilters(params) {
return matchesApprovalRequestFilters({
request: params.request.request,
agentFilter: params.config.agentFilter,
sessionFilter: params.config.sessionFilter,
fallbackAgentIdFromSessionKey: true
});
}
function resolveActiveApprovalForwarding(params) {
if (!params.isTransportEnabled(params)) return null;
const config = resolveApprovalForwardingConfig(params);
if (!config?.enabled) return null;
return {
config,
mode: params.resolveMode(config)
};
}
function canApprovalPotentiallyRoute(params) {
const forwarding = resolveActiveApprovalForwarding(params);
if (!forwarding) return false;
if (approvalModeIncludesSession(forwarding.mode)) return true;
if (params.nativeSessionOnly) return false;
return approvalModeIncludesTargets(forwarding.mode) && params.hasMatchingTarget({
cfg: params.cfg,
config: forwarding.config,
accountId: params.accountId
});
}
function isSessionApprovalEligibleViaForwarding(params) {
const forwarding = resolveActiveApprovalForwarding(params);
if (!forwarding) return false;
if (!approvalModeIncludesSession(forwarding.mode)) return false;
if (!matchesForwardingFilters({
config: forwarding.config,
request: params.request
})) return false;
if (!doesApprovalRequestMatchChannelAccount({
cfg: params.cfg,
request: params.request,
channel: params.channel,
accountId: params.accountId
})) return false;
return params.hasOriginOrSessionTarget({
cfg: params.cfg,
accountId: params.accountId,
request: params.request
});
}
function isExplicitTargetApprovalEligibleViaForwarding(params) {
const forwarding = resolveActiveApprovalForwarding(params);
if (!forwarding) return false;
if (!approvalModeIncludesTargets(forwarding.mode)) return false;
if (!matchesForwardingFilters({
config: forwarding.config,
request: params.request
})) return false;
return params.hasMatchingTarget({
cfg: params.cfg,
config: forwarding.config,
accountId: params.accountId,
target: params.target
});
}
/** Build reusable forwarding gates for channels with custom target matching logic. */
function createChannelApprovalForwardingEvaluator(params) {
const resolveForwardingMode = (config) => normalizeApprovalForwardingMode(config.mode);
const isPotentialRoute = (input) => {
return canApprovalPotentiallyRoute({
...input,
isTransportEnabled: params.isTransportEnabled,
resolveMode: resolveForwardingMode,
hasMatchingTarget: params.hasMatchingTarget
});
};
const isSessionEligible = (input) => {
return isSessionApprovalEligibleViaForwarding({
...input,
channel: params.channel,
isTransportEnabled: params.isTransportEnabled,
resolveMode: resolveForwardingMode,
hasOriginOrSessionTarget: params.hasOriginOrSessionTarget
});
};
const isExplicitTargetEligible = (input) => {
return isExplicitTargetApprovalEligibleViaForwarding({
...input,
isTransportEnabled: params.isTransportEnabled,
resolveMode: resolveForwardingMode,
hasMatchingTarget: params.hasMatchingTarget
});
};
const canAnyPotentiallyRoute = (input) => isPotentialRoute({
...input,
approvalKind: "exec"
}) || isPotentialRoute({
...input,
approvalKind: "plugin"
});
const shouldHandleRequest = (input) => isSessionEligible({
...input,
approvalKind: resolveApprovalKind(input.request, input.approvalKind)
});
return {
canAnyPotentiallyRoute,
isExplicitTargetEligible,
isPotentialRoute,
isSessionEligible,
shouldHandleRequest
};
}
function normalizeApprovalForwardingModeWithDefault(params) {
return params.config.mode ?? params.defaultForwardingMode;
}
/** Create the standard route gates for native channel approval forwarding. */
function createNativeApprovalChannelRouteGates(params) {
const resolveForwardingMode = (config) => normalizeApprovalForwardingModeWithDefault({
config,
defaultForwardingMode: params.defaultForwardingMode
});
const targetsMatch = params.targetsMatch ?? ((left, right) => nativeApprovalTargetsMatch({
channel: params.channel,
left,
right
}));
const targetAccountMatchesChannelAccount = (input) => {
const targetAccountId = normalizeOptionalString(input.targetAccountId);
const accountId = normalizeOptionalString(input.accountId);
if (targetAccountId) return !accountId || normalizeAccountId(targetAccountId) === normalizeAccountId(accountId);
if (!accountId) return true;
const normalizedAccountId = normalizeAccountId(accountId);
if (normalizedAccountId === normalizeAccountId(params.resolveDefaultAccountId(input.cfg))) return true;
const enabledAccountIds = params.listAccountIds(input.cfg).filter((candidateAccountId) => params.isTransportEnabled({
cfg: input.cfg,
accountId: candidateAccountId
})).map((candidateAccountId) => normalizeAccountId(candidateAccountId));
return enabledAccountIds.length === 1 && enabledAccountIds[0] === normalizedAccountId;
};
const hasMatchingChannelTarget = (input) => {
const candidateTarget = input.target ? params.normalizeForwardTarget(input.target) : null;
return (input.config.targets ?? []).some((target) => {
const configuredTarget = params.normalizeForwardTarget(target);
if (!configuredTarget) return false;
if (!targetAccountMatchesChannelAccount({
cfg: input.cfg,
targetAccountId: configuredTarget.accountId,
accountId: input.accountId
})) return false;
if (!candidateTarget) return true;
return targetsMatch(configuredTarget, candidateTarget);
});
};
const hasChannelOriginOrSessionTarget = (input) => {
if (params.resolveTurnSourceTarget(input.request)) return true;
const sessionTarget = resolveApprovalRequestSessionTarget({
cfg: input.cfg,
request: input.request
});
return normalizeLowercaseStringOrEmpty(sessionTarget?.channel) === params.channel && targetAccountMatchesChannelAccount({
cfg: input.cfg,
targetAccountId: sessionTarget?.accountId,
accountId: input.accountId
});
};
const canApprovalPotentiallyRouteToChannel = (input) => {
return canApprovalPotentiallyRoute({
...input,
isTransportEnabled: params.isTransportEnabled,
resolveMode: resolveForwardingMode,
hasMatchingTarget: hasMatchingChannelTarget
});
};
const canAnyApprovalPotentiallyRouteToChannel = (input) => canApprovalPotentiallyRouteToChannel({
...input,
approvalKind: "exec"
}) || canApprovalPotentiallyRouteToChannel({
...input,
approvalKind: "plugin"
});
const isSessionApprovalEligible = (input) => {
return isSessionApprovalEligibleViaForwarding({
...input,
channel: params.channel,
isTransportEnabled: params.isTransportEnabled,
resolveMode: resolveForwardingMode,
hasOriginOrSessionTarget: hasChannelOriginOrSessionTarget
});
};
const isExplicitTargetEligible = (input) => {
return isExplicitTargetApprovalEligibleViaForwarding({
...input,
isTransportEnabled: params.isTransportEnabled,
resolveMode: resolveForwardingMode,
hasMatchingTarget: hasMatchingChannelTarget
});
};
const shouldHandleApprovalRequest = (input) => isSessionApprovalEligible({
...input,
approvalKind: resolveApprovalKind(input.request, input.approvalKind)
});
return {
canApprovalPotentiallyRouteToChannel,
canAnyApprovalPotentiallyRouteToChannel,
isNativeApprovalHandlerConfigured: (input) => canAnyApprovalPotentiallyRouteToChannel({
...input,
nativeSessionOnly: true
}),
isSessionApprovalEligible,
isExplicitTargetEligible,
shouldHandleApprovalRequest
};
}
function normalizeOptionalAccountId(value) {
return value?.trim() || void 0;
}
/** Create a fallback suppressor that avoids duplicate approval prompts after native delivery. */
function createNativeApprovalForwardingFallbackSuppressor(params) {
const targetsMatch = params.targetsMatch ?? ((left, right) => nativeApprovalTargetsMatch({
channel: params.channel,
left,
right
}));
return (input) => {
const forwardingTarget = params.normalizeForwardTarget(input.target);
if (!forwardingTarget) return false;
const accountId = normalizeOptionalAccountId(params.resolveAccountId?.({
forwardingTarget,
target: input.target,
request: input.request
})) ?? normalizeOptionalAccountId(forwardingTarget.accountId) ?? normalizeOptionalAccountId(input.request.request.turnSourceAccountId);
const approvalKind = params.resolveApprovalKind?.({
approvalKind: input.approvalKind,
request: input.request
}) ?? resolveApprovalKind(input.request, input.approvalKind);
if (!(input.target.source === "target" ? params.isExplicitTargetEligible?.({
cfg: input.cfg,
accountId,
approvalKind,
request: input.request,
target: input.target
}) ?? false : params.isSessionRouteEligible({
cfg: input.cfg,
accountId,
approvalKind,
request: input.request
}))) return false;
const forwardingTargetForMatch = params.resolveForwardingTargetForMatch?.({
forwardingTarget,
accountId,
target: input.target,
approvalKind,
request: input.request
}) ?? forwardingTarget;
if (!forwardingTargetForMatch) return false;
const originTarget = params.resolveOriginTarget({
cfg: input.cfg,
accountId,
approvalKind,
request: input.request
});
if (originTarget && targetsMatch(forwardingTargetForMatch, originTarget)) return true;
return params.resolveApproverDmTargets({
cfg: input.cfg,
accountId,
approvalKind,
request: input.request
}).some((approverTarget) => targetsMatch(forwardingTargetForMatch, approverTarget));
};
}
function createOriginTargetResolver(params) {
return (input) => {
if (params.shouldHandleRequest && !params.shouldHandleRequest(input)) return null;
const normalizeTarget = (target) => {
if (!target) return null;
return params.normalizeTarget ? params.normalizeTarget(target, input.request) ?? null : target;
};
const normalizeTargetForMatch = (target) => params.normalizeTargetForMatch?.(target, input.request) ?? target;
return resolveApprovalRequestOriginTarget({
cfg: input.cfg,
request: input.request,
channel: params.channel,
accountId: input.accountId,
resolveTurnSourceTarget: (request) => normalizeTarget(params.resolveTurnSourceTarget(request)),
resolveSessionTarget: (sessionTarget) => normalizeTarget(params.resolveSessionTarget(sessionTarget, input.request)),
targetsMatch: (left, right) => {
const normalizedLeft = normalizeTargetForMatch(left);
const normalizedRight = normalizeTargetForMatch(right);
return Boolean(normalizedLeft && normalizedRight && params.targetsMatch(normalizedLeft, normalizedRight));
},
resolveFallbackTarget: params.resolveFallbackTarget ? (request) => normalizeTarget(params.resolveFallbackTarget?.(request) ?? null) : void 0
});
};
}
function hasCustomTargetsMatch(params) {
return typeof params.targetsMatch === "function";
}
function createChannelNativeOriginTargetResolver(params) {
if (hasCustomTargetsMatch(params)) return createOriginTargetResolver(params);
return createOriginTargetResolver({
...params,
targetsMatch: nativeApprovalTargetMatcher(params.channel)
});
}
/** Create a resolver for configured approver DM targets. */
function createChannelApproverDmTargetResolver(params) {
return (input) => {
if (params.shouldHandleRequest && !params.shouldHandleRequest(input)) return [];
const targets = [];
for (const approver of params.resolveApprovers({
cfg: input.cfg,
accountId: input.accountId
})) {
const target = params.mapApprover(approver, input);
if (target) targets.push(target);
}
return targets;
};
}
//#endregion
export { createNativeApprovalForwardingFallbackSuppressor as a, shouldSuppressLocalNativeExecApprovalPrompt as c, createNativeApprovalChannelRouteGates as i, createChannelApproverDmTargetResolver as n, nativeApprovalTargetsMatch as o, createChannelNativeOriginTargetResolver as r, resolveApprovalKind as s, createChannelApprovalForwardingEvaluator as t };