fix(signal): gate ACK reaction on non-empty early body check

Before sending the ACK reaction, verify the message has at least one
of: non-empty trimmed text, an attachment, or a quote. Without this
guard a whitespace-only message (bodyText === '' after trim) would
receive an ACK even though it is dropped by the later bodyText guard,
giving users a false 'message accepted' signal.

Adds two test cases:
- whitespace-only message → no ACK
- empty text but has attachment → ACK sent

Addresses: https://github.com/openclaw/openclaw/pull/31078#discussion_r2871918100
This commit is contained in:
Dale 2026-03-02 06:41:48 -05:00 committed by root
parent 8105516b30
commit cab7a766fb
2 changed files with 35 additions and 1 deletions

View File

@ -186,6 +186,34 @@ describe("Signal ACK reactions", () => {
expect(sendReactionSignal).not.toHaveBeenCalled();
});
it("does NOT send ack when message body is empty (whitespace-only text, no attachment, no quote)", async () => {
const deps = makeDeps();
const handler = createSignalEventHandler(deps);
await handler(
makeEvent({
dataMessage: { message: " ", timestamp: 1700000000000 },
}),
);
expect(sendReactionSignal).not.toHaveBeenCalled();
});
it("sends ack when message has no text but has an attachment", async () => {
const deps = makeDeps();
const handler = createSignalEventHandler(deps);
await handler(
makeEvent({
dataMessage: {
message: "",
timestamp: 1700000000000,
attachments: [{ id: "att1", contentType: "image/png", size: 1024 }],
},
}),
);
expect(sendReactionSignal).toHaveBeenCalledTimes(1);
});
it("sends ack BEFORE dispatch", async () => {
const callOrder: string[] = [];
vi.mocked(sendReactionSignal).mockImplementation(async () => {

View File

@ -702,9 +702,15 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
return;
}
// Early body guard: if the message has no text, no attachment, and no quote it will be
// dropped at the bodyText check below — don't send a false ACK in that case.
const hasEarlyBody = Boolean(
messageText || dataMessage.attachments?.length || dataMessage.quote?.text?.trim(),
);
// Send ACK reaction immediately — before any awaited I/O (attachment fetch, read receipt)
// so the user gets instant visual feedback that their message was received.
if (typeof envelope.timestamp === "number") {
if (hasEarlyBody && typeof envelope.timestamp === "number") {
maybeSendSignalAckReaction({
cfg: deps.cfg,
agentId: route.agentId,