mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-04 13:51:30 +00:00
* 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
156 lines
5.8 KiB
TypeScript
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,
|
|
};
|
|
}
|