fix: preserve approval surface parity

This commit is contained in:
Gustavo Madeira Santana
2026-04-01 12:55:38 -04:00
parent 39989b3b8a
commit 9ec34d1c2d
4 changed files with 42 additions and 11 deletions

View File

@@ -1,3 +1,7 @@
import {
isTelegramExecApprovalAuthorizedSender,
isTelegramExecApprovalClientEnabled,
} from "../../../extensions/telegram/api.js";
import { callGateway } from "../../gateway/call.js";
import { ErrorCodes } from "../../gateway/protocol/index.js";
import { logVerbose } from "../../globals.js";
@@ -70,6 +74,17 @@ function buildResolvedByLabel(params: Parameters<CommandHandler>[0]): string {
return `${channel}:${sender}`;
}
function isAuthorizedTelegramExecSender(params: Parameters<CommandHandler>[0]): boolean {
if (params.command.channel !== "telegram") {
return false;
}
return isTelegramExecApprovalAuthorizedSender({
cfg: params.cfg,
accountId: params.ctx.AccountId,
senderId: params.command.senderId,
});
}
function readErrorCode(value: unknown): string | null {
return typeof value === "string" && value.trim() ? value : null;
}
@@ -159,6 +174,8 @@ export const handleApproveCommand: CommandHandler = async (params, allowTextComm
return { shouldContinue: false, reply: { text: parsed.error } };
}
const isPluginId = parsed.id.startsWith("plugin:");
const telegramExecAuthorizedSender = isAuthorizedTelegramExecSender(params);
const execApprovalAuthorization = resolveApprovalCommandAuthorization({
cfg: params.cfg,
channel: params.command.channel,
@@ -183,6 +200,18 @@ export const handleApproveCommand: CommandHandler = async (params, allowTextComm
return { shouldContinue: false };
}
if (
params.command.channel === "telegram" &&
!isPluginId &&
!telegramExecAuthorizedSender &&
!isTelegramExecApprovalClientEnabled({ cfg: params.cfg, accountId: params.ctx.AccountId })
) {
return {
shouldContinue: false,
reply: { text: "❌ Telegram exec approvals are not enabled for this bot account." },
};
}
const missingScope = requireGatewayClientScopeForInternalChannel(params, {
label: "/approve",
allowedScopes: ["operator.approvals", "operator.admin"],

View File

@@ -993,7 +993,7 @@ describe("/approve command", () => {
expect(callGatewayMock).not.toHaveBeenCalled();
});
it("accepts Telegram /approve from exec target recipients even when native approvals are disabled", async () => {
it("ignores Telegram /approve from exec target recipients when native approvals are disabled", async () => {
const cfg = {
commands: { text: true },
approvals: {
@@ -1020,13 +1020,8 @@ describe("/approve command", () => {
const result = await handleCommands(params);
expect(result.shouldContinue).toBe(false);
expect(result.reply?.text).toContain("Approval allow-once submitted");
expect(callGatewayMock).toHaveBeenCalledWith(
expect.objectContaining({
method: "exec.approval.resolve",
params: { id: "abc12345", decision: "allow-once" },
}),
);
expect(result.reply).toBeUndefined();
expect(callGatewayMock).not.toHaveBeenCalled();
});
it("requires configured Discord approvers for exec approvals", async () => {
@@ -1261,7 +1256,7 @@ describe("/approve command", () => {
expectGatewayCalls: 2,
},
{
name: "telegram disabled native delivery now reports plain approval auth denial",
name: "telegram disabled native delivery reports the channel-disabled message",
cfg: createTelegramApproveCfg(null),
commandBody: "/approve abc12345 allow-once",
ctx: {
@@ -1270,7 +1265,7 @@ describe("/approve command", () => {
SenderId: "123",
},
setup: undefined,
expectedText: "not authorized to approve exec requests on Telegram",
expectedText: "Telegram exec approvals are not enabled",
expectGatewayCalls: 0,
},
{

View File

@@ -100,6 +100,13 @@ describe("createApproverRestrictedNativeApprovalAdapter", () => {
action: "approve",
}),
).toEqual({ kind: "disabled" });
expect(
getActionAvailabilityState({
cfg: {} as never,
accountId: "disabled",
action: "approve",
}),
).toEqual({ kind: "disabled" });
expect(hasConfiguredDmRoute.hasConfiguredDmRoute({ cfg: {} as never })).toBe(true);
expect(nativeCapabilities).toEqual({
enabled: true,

View File

@@ -92,7 +92,7 @@ function buildApproverRestrictedNativeApprovalCapability(
accountId?: string | null;
action: "approve";
}) =>
params.hasApprovers({ cfg, accountId })
params.hasApprovers({ cfg, accountId }) && params.isNativeDeliveryEnabled({ cfg, accountId })
? ({ kind: "enabled" } as const)
: ({ kind: "disabled" } as const),
approvals: {