From 87a211d3099bcd86f902c239d65e32cdaea4a444 Mon Sep 17 00:00:00 2001 From: "clawsweeper[bot]" <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:05:18 -0700 Subject: [PATCH] fix(slack): cap approval update fallback text Co-authored-by: openclaw-clawsweeper[bot] <280122609+openclaw-clawsweeper[bot]@users.noreply.github.com> --- .../src/approval-handler.runtime.test.ts | 53 ++++++++++++++----- .../slack/src/approval-handler.runtime.ts | 4 +- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/extensions/slack/src/approval-handler.runtime.test.ts b/extensions/slack/src/approval-handler.runtime.test.ts index 90741f63c22..84f347dc4ab 100644 --- a/extensions/slack/src/approval-handler.runtime.test.ts +++ b/extensions/slack/src/approval-handler.runtime.test.ts @@ -5,7 +5,7 @@ type SlackPayload = { text: string; blocks?: unknown; }; -const SLACK_TEXT_LIMIT = 8000; +const SLACK_CHAT_UPDATE_TEXT_LIMIT = 4000; function findSlackActionsBlock(blocks: Array<{ type?: string; elements?: unknown[] }>) { return blocks.find((block) => block.type === "actions"); @@ -115,7 +115,7 @@ describe("slackApprovalNativeRuntime", () => { ).toBe(false); }); - it("caps resolved update fallback text while preserving approval blocks", async () => { + it("caps resolved update fallback text to Slack chat.update limits while preserving blocks", async () => { const blocks = [ { type: "section", @@ -126,31 +126,56 @@ describe("slackApprovalNativeRuntime", () => { }, ]; const chatUpdate = vi.fn(async (_payload: { text: string; blocks: typeof blocks }) => ({})); + const context = { + app: { + client: { + chat: { + update: chatUpdate, + }, + }, + }, + config: {}, + } as never; await slackApprovalNativeRuntime.transport.updateEntry?.({ cfg: {} as never, accountId: "default", - context: { - app: { - client: { - chat: { - update: chatUpdate, - }, - }, - }, - config: {}, - } as never, + context, entry: { channelId: "C123", messageTs: "1712345678.999999", }, payload: { - text: `*Exec approval: Allowed once*\n\n*Command*\n${"a".repeat(9000)}`, + text: "a".repeat(SLACK_CHAT_UPDATE_TEXT_LIMIT), blocks, }, phase: "resolved", }); + await slackApprovalNativeRuntime.transport.updateEntry?.({ + cfg: {} as never, + accountId: "default", + context, + entry: { + channelId: "C123", + messageTs: "1712345678.999999", + }, + payload: { + text: "a".repeat(5000), + blocks, + }, + phase: "resolved", + }); + + expect(chatUpdate).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + channel: "C123", + ts: "1712345678.999999", + text: "a".repeat(SLACK_CHAT_UPDATE_TEXT_LIMIT), + blocks, + }), + ); expect(chatUpdate).toHaveBeenCalledWith( expect.objectContaining({ channel: "C123", @@ -159,7 +184,7 @@ describe("slackApprovalNativeRuntime", () => { blocks, }), ); - expect(chatUpdate.mock.calls[0]?.[0].text).toHaveLength(SLACK_TEXT_LIMIT); + expect(chatUpdate.mock.calls[1]?.[0].text).toHaveLength(SLACK_CHAT_UPDATE_TEXT_LIMIT); }); it("keeps pending metadata context within Slack Block Kit limits", async () => { diff --git a/extensions/slack/src/approval-handler.runtime.ts b/extensions/slack/src/approval-handler.runtime.ts index dad73c9cde6..116a6fa492f 100644 --- a/extensions/slack/src/approval-handler.runtime.ts +++ b/extensions/slack/src/approval-handler.runtime.ts @@ -17,7 +17,6 @@ import { shouldHandleSlackExecApprovalRequest, normalizeSlackApproverId, } from "./exec-approvals.js"; -import { SLACK_TEXT_LIMIT } from "./limits.js"; import { resolveSlackReplyBlocks } from "./reply-blocks.js"; import { sendMessageSlack } from "./send.js"; import { truncateSlackText } from "./truncate.js"; @@ -33,6 +32,7 @@ type SlackPendingDelivery = { }; const SLACK_CONTEXT_ELEMENTS_MAX = 10; +const SLACK_CHAT_UPDATE_TEXT_LIMIT = 4000; const SLACK_TEXT_OBJECT_MAX = 3000; type SlackExecApprovalConfig = NonNullable< @@ -231,7 +231,7 @@ async function updateMessage(params: { await params.app.client.chat.update({ channel: params.channelId, ts: params.messageTs, - text: truncateSlackText(params.text, SLACK_TEXT_LIMIT), + text: truncateSlackText(params.text, SLACK_CHAT_UPDATE_TEXT_LIMIT), blocks: params.blocks, }); } catch (err) {