fix: drop overlong slack command values

This commit is contained in:
Peter Steinberger
2026-04-30 04:04:27 +01:00
parent d25cfda54c
commit 8672737f81
3 changed files with 60 additions and 15 deletions

View File

@@ -52,6 +52,7 @@ Docs: https://docs.openclaw.ai
- Slack/commands: truncate native command argument-menu confirmation text to Slack's dialog limit, so long plugin arg names no longer make fallback buttons render invalid Block Kit payloads. Thanks @slackapi.
- Slack/exec approvals: cap native approval metadata context to Slack's element and text limits, so large approval details no longer make Slack reject the approval card. Thanks @slackapi.
- Slack/commands: cap native command argument-menu fallback rows to Slack's message block limit, so large plugin choice lists no longer make Slack reject the generated menu. Thanks @slackapi.
- Slack/commands: drop fallback command argument buttons whose encoded values exceed Slack's button-value limit, so one oversized plugin choice no longer makes Slack reject the whole menu. Thanks @slackapi.
- Channels/WhatsApp: require Baileys outbound message ids before marking auto-replies delivered, so transcript text and ack reactions no longer make failed group replies look sent. Fixes #49225. Thanks @TinyTb.
- CLI/update: scope packaged Node compile caches by OpenClaw version and install metadata, so global installs no longer reuse stale compiled chunks after package updates. Thanks @pashpashpash.
- Channels/Voice call: keep pre-auth webhook in-flight limiting active when socket remote address metadata is missing, so slow-body requests from stripped-IP proxy paths still share the fallback bucket. (#74453) Thanks @davidangularme.

View File

@@ -9,6 +9,7 @@ vi.mock("./slash-commands.runtime.js", () => {
const reportLongCommand = { key: "reportlong", nativeName: "reportlong" };
const reportLongButtonCommand = { key: "reportlongbutton", nativeName: "reportlongbutton" };
const reportHugeButtonCommand = { key: "reporthugebutton", nativeName: "reporthugebutton" };
const reportHugeValueCommand = { key: "reporthugevalue", nativeName: "reporthugevalue" };
const unsafeConfirmCommand = { key: "unsafeconfirm", nativeName: "unsafeconfirm" };
const longConfirmCommand = { key: "longconfirm", nativeName: "longconfirm" };
const statusAliasCommand = { key: "status", nativeName: "status" };
@@ -80,6 +81,9 @@ vi.mock("./slash-commands.runtime.js", () => {
if (normalized === "reporthugebutton") {
return reportHugeButtonCommand;
}
if (normalized === "reporthugevalue") {
return reportHugeValueCommand;
}
if (normalized === "unsafeconfirm") {
return unsafeConfirmCommand;
}
@@ -134,6 +138,12 @@ vi.mock("./slash-commands.runtime.js", () => {
acceptsArgs: true,
args: [],
},
{
name: "reporthugevalue",
description: "ReportHugeValue",
acceptsArgs: true,
args: [],
},
{
name: "unsafeconfirm",
description: "UnsafeConfirm",
@@ -187,6 +197,12 @@ vi.mock("./slash-commands.runtime.js", () => {
})),
);
}
if (params.command?.key === "reporthugevalue") {
return resolvePeriodMenu(params, [
{ value: "valid", label: "Valid" },
{ value: "x".repeat(2500), label: "Overlong" },
]);
}
if (params.command?.key === "reportcompact") {
return resolvePeriodMenu(params, baseReportPeriodChoices);
}
@@ -466,6 +482,7 @@ describe("Slack native command argument menus", () => {
let reportLongHandler: (args: unknown) => Promise<void>;
let reportLongButtonHandler: (args: unknown) => Promise<void>;
let reportHugeButtonHandler: (args: unknown) => Promise<void>;
let reportHugeValueHandler: (args: unknown) => Promise<void>;
let unsafeConfirmHandler: (args: unknown) => Promise<void>;
let longConfirmHandler: (args: unknown) => Promise<void>;
let agentStatusHandler: (args: unknown) => Promise<void>;
@@ -490,6 +507,11 @@ describe("Slack native command argument menus", () => {
"/reporthugebutton",
"/reporthugebutton",
);
reportHugeValueHandler = requireHandler(
harness.commands,
"/reporthugevalue",
"/reporthugevalue",
);
unsafeConfirmHandler = requireHandler(harness.commands, "/unsafeconfirm", "/unsafeconfirm");
longConfirmHandler = requireHandler(harness.commands, "/longconfirm", "/longconfirm");
agentStatusHandler = requireHandler(harness.commands, "/agentstatus", "/agentstatus");
@@ -638,6 +660,24 @@ describe("Slack native command argument menus", () => {
expect(actionBlocks.at(-1)?.elements).toHaveLength(5);
});
it("drops fallback buttons whose encoded values exceed Slack's button value limit", async () => {
const { respond } = await runCommandHandler(reportHugeValueHandler);
expect(respond).toHaveBeenCalledTimes(1);
const payload = respond.mock.calls[0]?.[0] as {
blocks?: Array<{
type: string;
elements?: Array<{ text?: { text?: string }; value?: string }>;
}>;
};
const actionBlocks = (payload.blocks ?? []).filter((block) => block.type === "actions");
expect(actionBlocks).toHaveLength(1);
expect(actionBlocks[0]?.elements).toHaveLength(1);
expect(actionBlocks[0]?.elements?.[0]).toMatchObject({
text: { text: "Valid" },
});
expect(actionBlocks[0]?.elements?.[0]?.value?.length).toBeLessThanOrEqual(2000);
});
it("shows an overflow menu when choices fit compact range", async () => {
const element = await getFirstActionElementFromCommand(reportCompactHandler);
expect(element?.type).toBe("overflow");

View File

@@ -56,6 +56,7 @@ const SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX = 100;
const SLACK_COMMAND_ARG_SELECT_OPTION_TEXT_MAX = 75;
const SLACK_COMMAND_ARG_SELECT_OPTION_VALUE_MAX = 75;
const SLACK_COMMAND_ARG_BUTTON_TEXT_MAX = 75;
const SLACK_COMMAND_ARG_BUTTON_VALUE_MAX = 2000;
const SLACK_COMMAND_ARG_CONFIRM_TEXT_MAX = 300;
const SLACK_HEADER_TEXT_MAX = 150;
const SLACK_COMMAND_ARG_CHROME_BLOCKS = 3;
@@ -299,21 +300,24 @@ function buildSlackCommandArgMenuBlocks(params: {
},
]
: encodedChoices.length <= SLACK_COMMAND_ARG_BUTTON_ROW_SIZE || !canUseStaticSelect
? chunkItems(encodedChoices, SLACK_COMMAND_ARG_BUTTON_ROW_SIZE).map(
(choices, rowIndex) => ({
type: "actions",
elements: choices.map((choice, colIndex) => ({
type: "button",
action_id: `${SLACK_COMMAND_ARG_ACTION_ID}_${rowIndex}_${colIndex}`,
text: {
type: "plain_text",
text: truncateSlackText(choice.label, SLACK_COMMAND_ARG_BUTTON_TEXT_MAX),
},
value: choice.value,
confirm: buildSlackArgMenuConfirm({ command: params.command, arg: params.arg }),
})),
}),
)
? chunkItems(
encodedChoices.filter(
(choice) => choice.value.length <= SLACK_COMMAND_ARG_BUTTON_VALUE_MAX,
),
SLACK_COMMAND_ARG_BUTTON_ROW_SIZE,
).map((choices, rowIndex) => ({
type: "actions",
elements: choices.map((choice, colIndex) => ({
type: "button",
action_id: `${SLACK_COMMAND_ARG_ACTION_ID}_${rowIndex}_${colIndex}`,
text: {
type: "plain_text",
text: truncateSlackText(choice.label, SLACK_COMMAND_ARG_BUTTON_TEXT_MAX),
},
value: choice.value,
confirm: buildSlackArgMenuConfirm({ command: params.command, arg: params.arg }),
})),
}))
: chunkItems(encodedChoices, SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX).map(
(choices, index) => ({
type: "actions",