Files
openclaw/src/plugin-sdk/approval-delivery-helpers.ts
scoootscooob 9ff57ac479 refactor(exec): unify channel approvals and restore routing/auth (#57838)
* fix(exec): add shared approval runtime

* fix(exec): harden shared approval runtime

* fix(exec): guard approval expiration callbacks

* fix(exec): handle approval runtime races

* fix(exec): clean up failed approval deliveries

* fix(exec): restore channel approval routing

* fix(exec): scope telegram legacy approval fallback

* refactor(exec): centralize native approval delivery

* fix(exec): harden approval auth and account routing

* test(exec): align telegram approval auth assertions

* fix(exec): align approval rebase followups

* fix(exec): clarify plugin approval not-found errors

* fix(exec): fall back to session-bound telegram accounts

* fix(exec): detect structured telegram approval misses

* test(exec): align discord approval auth coverage

* fix(exec): ignore discord dm origin channel routes

* fix(telegram): skip self-authored message echoes

* fix(exec): keep implicit approval auth non-explicit
2026-03-30 15:49:02 -07:00

156 lines
5.8 KiB
TypeScript

import type { ExecApprovalRequest } from "../infra/exec-approvals.js";
import type { PluginApprovalRequest } from "../infra/plugin-approvals.js";
import type { OpenClawConfig } from "./config-runtime.js";
import { normalizeMessageChannel } from "./routing.js";
type ApprovalKind = "exec" | "plugin";
type NativeApprovalDeliveryMode = "dm" | "channel" | "both";
type NativeApprovalRequest = ExecApprovalRequest | PluginApprovalRequest;
type NativeApprovalTarget = { to: string; threadId?: string | number | null };
type NativeApprovalSurface = "origin" | "approver-dm";
type ApprovalAdapterParams = {
cfg: OpenClawConfig;
accountId?: string | null;
senderId?: string | null;
};
type DeliverySuppressionParams = {
cfg: OpenClawConfig;
target: { channel: string; accountId?: string | null };
request: { request: { turnSourceChannel?: string | null; turnSourceAccountId?: string | null } };
};
export function createApproverRestrictedNativeApprovalAdapter(params: {
channel: string;
channelLabel: string;
listAccountIds: (cfg: OpenClawConfig) => string[];
hasApprovers: (params: ApprovalAdapterParams) => boolean;
isExecAuthorizedSender: (params: ApprovalAdapterParams) => boolean;
isPluginAuthorizedSender?: (params: ApprovalAdapterParams) => boolean;
isNativeDeliveryEnabled: (params: { cfg: OpenClawConfig; accountId?: string | null }) => boolean;
resolveNativeDeliveryMode: (params: {
cfg: OpenClawConfig;
accountId?: string | null;
}) => NativeApprovalDeliveryMode;
requireMatchingTurnSourceChannel?: boolean;
resolveSuppressionAccountId?: (params: DeliverySuppressionParams) => string | undefined;
resolveOriginTarget?: (params: {
cfg: OpenClawConfig;
accountId?: string | null;
approvalKind: ApprovalKind;
request: NativeApprovalRequest;
}) => NativeApprovalTarget | null | Promise<NativeApprovalTarget | null>;
resolveApproverDmTargets?: (params: {
cfg: OpenClawConfig;
accountId?: string | null;
approvalKind: ApprovalKind;
request: NativeApprovalRequest;
}) => NativeApprovalTarget[] | Promise<NativeApprovalTarget[]>;
notifyOriginWhenDmOnly?: boolean;
}) {
const pluginSenderAuth = params.isPluginAuthorizedSender ?? params.isExecAuthorizedSender;
const normalizePreferredSurface = (
mode: NativeApprovalDeliveryMode,
): NativeApprovalSurface | "both" =>
mode === "channel" ? "origin" : mode === "dm" ? "approver-dm" : "both";
return {
auth: {
authorizeActorAction: ({
cfg,
accountId,
senderId,
approvalKind,
}: {
cfg: OpenClawConfig;
accountId?: string | null;
senderId?: string | null;
action: "approve";
approvalKind: ApprovalKind;
}) => {
const authorized =
approvalKind === "plugin"
? pluginSenderAuth({ cfg, accountId, senderId })
: params.isExecAuthorizedSender({ cfg, accountId, senderId });
return authorized
? { authorized: true }
: {
authorized: false,
reason: `❌ You are not authorized to approve ${approvalKind} requests on ${params.channelLabel}.`,
};
},
getActionAvailabilityState: ({
cfg,
accountId,
}: {
cfg: OpenClawConfig;
accountId?: string | null;
action: "approve";
}) =>
params.hasApprovers({ cfg, accountId })
? ({ kind: "enabled" } as const)
: ({ kind: "disabled" } as const),
},
delivery: {
hasConfiguredDmRoute: ({ cfg }: { cfg: OpenClawConfig }) =>
params.listAccountIds(cfg).some((accountId) => {
if (!params.hasApprovers({ cfg, accountId })) {
return false;
}
if (!params.isNativeDeliveryEnabled({ cfg, accountId })) {
return false;
}
const target = params.resolveNativeDeliveryMode({ cfg, accountId });
return target === "dm" || target === "both";
}),
shouldSuppressForwardingFallback: (input: DeliverySuppressionParams) => {
const channel = normalizeMessageChannel(input.target.channel) ?? input.target.channel;
if (channel !== params.channel) {
return false;
}
if (params.requireMatchingTurnSourceChannel) {
const turnSourceChannel = normalizeMessageChannel(
input.request.request.turnSourceChannel,
);
if (turnSourceChannel !== params.channel) {
return false;
}
}
const resolvedAccountId = params.resolveSuppressionAccountId?.(input);
const accountId =
(resolvedAccountId === undefined
? input.target.accountId?.trim()
: resolvedAccountId.trim()) || undefined;
return params.isNativeDeliveryEnabled({ cfg: input.cfg, accountId });
},
},
native:
params.resolveOriginTarget || params.resolveApproverDmTargets
? {
describeDeliveryCapabilities: ({
cfg,
accountId,
}: {
cfg: OpenClawConfig;
accountId?: string | null;
approvalKind: ApprovalKind;
request: NativeApprovalRequest;
}) => ({
enabled:
params.hasApprovers({ cfg, accountId }) &&
params.isNativeDeliveryEnabled({ cfg, accountId }),
preferredSurface: normalizePreferredSurface(
params.resolveNativeDeliveryMode({ cfg, accountId }),
),
supportsOriginSurface: Boolean(params.resolveOriginTarget),
supportsApproverDmSurface: Boolean(params.resolveApproverDmTargets),
notifyOriginWhenDmOnly: params.notifyOriginWhenDmOnly ?? false,
}),
resolveOriginTarget: params.resolveOriginTarget,
resolveApproverDmTargets: params.resolveApproverDmTargets,
}
: undefined,
};
}