fix: bound slack command confirm text

This commit is contained in:
Peter Steinberger
2026-04-30 03:40:15 +01:00
parent eab4024934
commit a6390efeba
3 changed files with 37 additions and 1 deletions

View File

@@ -44,6 +44,7 @@ Docs: https://docs.openclaw.ai
- Agents/Codex: flush accepted debounced steering messages before normal app-server turn cleanup, so inbound follow-ups acknowledged as queued are not dropped when the turn completes before the debounce fires. Thanks @vincentkoc.
- Slack/interactive replies: keep rendered buttons and selects within Slack Block Kit value and count limits, and align command argument select values with Slack's option limit, so overlong agent-authored choices no longer make Slack reject the whole block payload. Thanks @slackapi.
- Slack/interactive replies: drop overlong Block Kit button URLs while preserving valid callback values, so malformed link buttons no longer make Slack reject the whole interactive reply. Thanks @slackapi.
- 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.
- 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 unsafeConfirmCommand = { key: "unsafeconfirm", nativeName: "unsafeconfirm" };
const longConfirmCommand = { key: "longconfirm", nativeName: "longconfirm" };
const statusAliasCommand = { key: "status", nativeName: "status" };
const periodArg = { name: "period", description: "period" };
const baseReportPeriodChoices = [
@@ -78,6 +79,9 @@ vi.mock("./slash-commands.runtime.js", () => {
if (normalized === "unsafeconfirm") {
return unsafeConfirmCommand;
}
if (normalized === "longconfirm") {
return longConfirmCommand;
}
if (normalized === "agentstatus") {
return statusAliasCommand;
}
@@ -126,6 +130,12 @@ vi.mock("./slash-commands.runtime.js", () => {
acceptsArgs: true,
args: [],
},
{
name: "longconfirm",
description: "LongConfirm",
acceptsArgs: true,
args: [],
},
{
name: "agentstatus",
description: "Status",
@@ -179,6 +189,15 @@ vi.mock("./slash-commands.runtime.js", () => {
],
};
}
if (params.command?.key === "longconfirm") {
return {
arg: { name: `mode_${"x".repeat(320)}`, description: "mode" },
choices: [
{ value: "on", label: "on" },
{ value: "off", label: "off" },
],
};
}
if (params.command?.key !== "usage") {
return null;
}
@@ -428,6 +447,7 @@ describe("Slack native command argument menus", () => {
let reportLongHandler: (args: unknown) => Promise<void>;
let reportLongButtonHandler: (args: unknown) => Promise<void>;
let unsafeConfirmHandler: (args: unknown) => Promise<void>;
let longConfirmHandler: (args: unknown) => Promise<void>;
let agentStatusHandler: (args: unknown) => Promise<void>;
let argMenuHandler: (args: unknown) => Promise<void>;
let argMenuOptionsHandler: (args: unknown) => Promise<void>;
@@ -446,6 +466,7 @@ describe("Slack native command argument menus", () => {
"/reportlongbutton",
);
unsafeConfirmHandler = requireHandler(harness.commands, "/unsafeconfirm", "/unsafeconfirm");
longConfirmHandler = requireHandler(harness.commands, "/longconfirm", "/longconfirm");
agentStatusHandler = requireHandler(harness.commands, "/agentstatus", "/agentstatus");
argMenuHandler = requireHandler(harness.actions, /^openclaw_cmdarg/, "arg-menu action");
argMenuOptionsHandler = requireHandler(harness.options, "openclaw_cmdarg", "arg-menu options");
@@ -596,6 +617,16 @@ describe("Slack native command argument menus", () => {
);
});
it("truncates confirm dialog text when long args force button fallback", async () => {
const element = (await getFirstActionElementFromCommand(longConfirmHandler)) as
| { type?: string; confirm?: { text?: { text?: string } } }
| undefined;
const confirmText = element?.confirm?.text?.text;
expect(element?.type).toBe("button");
expect(confirmText).toHaveLength(300);
expect(confirmText?.endsWith("…")).toBe(true);
});
it("dispatches the command when a menu button is clicked", async () => {
await runArgMenuAction(argMenuHandler, {
action: {

View File

@@ -55,6 +55,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_CONFIRM_TEXT_MAX = 300;
const SLACK_HEADER_TEXT_MAX = 150;
let slashCommandsRuntimePromise: Promise<typeof import("./slash-commands.runtime.js")> | null =
null;
@@ -142,7 +143,10 @@ function buildSlackArgMenuConfirm(params: { command: string; arg: string }) {
title: { type: "plain_text", text: "Confirm selection" },
text: {
type: "mrkdwn",
text: `Run */${command}* with *${arg}* set to this value?`,
text: truncateSlackText(
`Run */${command}* with *${arg}* set to this value?`,
SLACK_COMMAND_ARG_CONFIRM_TEXT_MAX,
),
},
confirm: { type: "plain_text", text: "Run command" },
deny: { type: "plain_text", text: "Cancel" },