diff --git a/src/slack/monitor/exec-approvals.ts b/src/slack/monitor/exec-approvals.ts index 38316eebf..5796fd8ae 100644 --- a/src/slack/monitor/exec-approvals.ts +++ b/src/slack/monitor/exec-approvals.ts @@ -8,7 +8,7 @@ import type { ExecApprovalDecision } from "../../infra/exec-approvals.js"; import { logDebug, logError } from "../../logger.js"; import type { RuntimeEnv } from "../../runtime.js"; -const EXEC_APPROVAL_ACTION_ID = "clawdbot_execapproval"; +const EXEC_APPROVAL_ACTION_ID_PREFIX = "clawdbot_execapproval"; const EXEC_APPROVAL_VALUE_PREFIX = "execapproval"; export type ExecApprovalRequest = { @@ -60,8 +60,12 @@ export function parseApprovalValue( } } -export function getExecApprovalActionId(): string { - return EXEC_APPROVAL_ACTION_ID; +export function getExecApprovalActionIdPrefix(): string { + return EXEC_APPROVAL_ACTION_ID_PREFIX; +} + +export function matchesExecApprovalActionId(actionId: string): boolean { + return actionId.startsWith(EXEC_APPROVAL_ACTION_ID_PREFIX); } function formatApprovalBlocks(request: ExecApprovalRequest) { @@ -121,20 +125,20 @@ function formatApprovalBlocks(request: ExecApprovalRequest) { { type: "button", text: { type: "plain_text", text: "✓ Allow once", emoji: true }, - action_id: EXEC_APPROVAL_ACTION_ID, + action_id: `${EXEC_APPROVAL_ACTION_ID_PREFIX}_allow_once`, value: encodeApprovalValue(request.id, "allow-once"), style: "primary", }, { type: "button", text: { type: "plain_text", text: "✓✓ Always allow", emoji: true }, - action_id: EXEC_APPROVAL_ACTION_ID, + action_id: `${EXEC_APPROVAL_ACTION_ID_PREFIX}_allow_always`, value: encodeApprovalValue(request.id, "allow-always"), }, { type: "button", text: { type: "plain_text", text: "✗ Deny", emoji: true }, - action_id: EXEC_APPROVAL_ACTION_ID, + action_id: `${EXEC_APPROVAL_ACTION_ID_PREFIX}_deny`, value: encodeApprovalValue(request.id, "deny"), style: "danger", }, diff --git a/src/slack/monitor/provider.ts b/src/slack/monitor/provider.ts index 74cc2cead..d1927bcd3 100644 --- a/src/slack/monitor/provider.ts +++ b/src/slack/monitor/provider.ts @@ -28,7 +28,8 @@ import { normalizeAllowList } from "./allow-list.js"; import type { MonitorSlackOpts } from "./types.js"; import { SlackExecApprovalHandler, - getExecApprovalActionId, + getExecApprovalActionIdPrefix, + matchesExecApprovalActionId, parseApprovalValue, } from "./exec-approvals.js"; @@ -234,43 +235,46 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { ( app as unknown as { action: ( - id: string, + id: string | RegExp, handler: (args: { ack: () => Promise; body: { user?: { id?: string } }; - action: { value?: string }; + action: { action_id?: string; value?: string }; respond: (payload: { text: string; response_type?: string }) => Promise; }) => Promise, ) => void; } - ).action(getExecApprovalActionId(), async ({ ack, body, action, respond }) => { - await ack(); - const parsed = parseApprovalValue(action?.value); - if (!parsed) { + ).action( + new RegExp(`^${getExecApprovalActionIdPrefix()}_`), + async ({ ack, body, action, respond }) => { + await ack(); + const parsed = parseApprovalValue(action?.value); + if (!parsed) { + await respond({ + text: "This approval button is no longer valid.", + response_type: "ephemeral", + }); + return; + } + const decisionLabel = + parsed.action === "allow-once" + ? "Allowed (once)" + : parsed.action === "allow-always" + ? "Allowed (always)" + : "Denied"; await respond({ - text: "This approval button is no longer valid.", + text: `Submitting decision: **${decisionLabel}**...`, response_type: "ephemeral", }); - return; - } - const decisionLabel = - parsed.action === "allow-once" - ? "Allowed (once)" - : parsed.action === "allow-always" - ? "Allowed (always)" - : "Denied"; - await respond({ - text: `Submitting decision: **${decisionLabel}**...`, - response_type: "ephemeral", - }); - const ok = await execApprovalHandler!.resolveApproval(parsed.approvalId, parsed.action); - if (!ok) { - await respond({ - text: "Failed to submit approval. The request may have expired or already been resolved.", - response_type: "ephemeral", - }); - } - }); + const ok = await execApprovalHandler!.resolveApproval(parsed.approvalId, parsed.action); + if (!ok) { + await respond({ + text: "Failed to submit approval. The request may have expired or already been resolved.", + response_type: "ephemeral", + }); + } + }, + ); } }