fix(signal): fall back to dataMessage.timestamp for ACK reaction

Mirror the same timestamp fallback already used for read receipts:
  envelope.timestamp ?? dataMessage.timestamp

Messages that carry only dataMessage.timestamp (no envelope.timestamp)
previously skipped the ACK entirely; they now receive one correctly.

Updates the 'timestamp missing' test to require both timestamps absent,
and adds a new test confirming the dataMessage fallback path.

Addresses: https://github.com/openclaw/openclaw/pull/31078#discussion_r2871998637
This commit is contained in:
Dale 2026-03-02 07:38:54 -05:00 committed by root
parent cab7a766fb
commit 8a11d0ea32
2 changed files with 31 additions and 4 deletions

View File

@ -173,13 +173,32 @@ describe("Signal ACK reactions", () => {
expect(sendReactionSignal).not.toHaveBeenCalled();
});
it("does NOT send ack when timestamp is missing", async () => {
it("sends ack using dataMessage.timestamp when envelope.timestamp is missing", async () => {
const deps = makeDeps();
const handler = createSignalEventHandler(deps);
await handler(
makeEvent({
timestamp: undefined,
dataMessage: { message: "hello", timestamp: 1700000000000 },
dataMessage: { message: "hello", timestamp: 1700000001234 },
}),
);
expect(sendReactionSignal).toHaveBeenCalledTimes(1);
expect(sendReactionSignal).toHaveBeenCalledWith(
expect.any(String),
1700000001234,
expect.any(String),
expect.any(Object),
);
});
it("does NOT send ack when both envelope and dataMessage timestamps are missing", async () => {
const deps = makeDeps();
const handler = createSignalEventHandler(deps);
await handler(
makeEvent({
timestamp: undefined,
dataMessage: { message: "hello", timestamp: undefined },
}),
);

View File

@ -708,14 +708,22 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
messageText || dataMessage.attachments?.length || dataMessage.quote?.text?.trim(),
);
// Resolve ACK timestamp — mirror the same fallback used for read receipts.
const ackTimestamp =
typeof envelope.timestamp === "number"
? envelope.timestamp
: typeof dataMessage.timestamp === "number"
? dataMessage.timestamp
: undefined;
// 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 (hasEarlyBody && typeof envelope.timestamp === "number") {
if (hasEarlyBody && ackTimestamp !== undefined) {
maybeSendSignalAckReaction({
cfg: deps.cfg,
agentId: route.agentId,
senderRecipient,
targetTimestamp: envelope.timestamp,
targetTimestamp: ackTimestamp,
isGroup,
groupId,
wasMentioned: effectiveWasMentioned,