fix(signal): fall back to toolContext.currentMessageId for reactions

Signal reactions required an explicit messageId parameter, unlike
Telegram which already fell back to toolContext.currentMessageId.
This made agent-initiated reactions fail on Signal because the
inbound message ID was available in tool context but never used.

- Destructure toolContext in Signal action handler
- Fall back to toolContext.currentMessageId when messageId omitted
- Update reaction schema descriptions (not Telegram-specific)
- Add tests for fallback and missing-messageId rejection

Closes #17651
This commit is contained in:
dunamismax
2026-03-02 17:10:40 -05:00
committed by Peter Steinberger
parent f25be781c4
commit d9fdec12ab
3 changed files with 44 additions and 8 deletions

View File

@@ -61,7 +61,11 @@ type SignalActionInput = Parameters<NonNullable<typeof signalMessageActions.hand
async function runSignalAction(
action: SignalActionInput["action"],
params: SignalActionInput["params"],
options?: { cfg?: OpenClawConfig; accountId?: string },
options?: {
cfg?: OpenClawConfig;
accountId?: string;
toolContext?: SignalActionInput["toolContext"];
},
) {
const cfg =
options?.cfg ?? ({ channels: { signal: { account: "+15550001111" } } } as OpenClawConfig);
@@ -75,6 +79,7 @@ async function runSignalAction(
params,
cfg,
accountId: options?.accountId,
toolContext: options?.toolContext,
});
return { cfg };
}
@@ -852,6 +857,33 @@ describe("signalMessageActions", () => {
}
});
it("falls back to toolContext.currentMessageId for reactions when messageId is omitted", async () => {
sendReactionSignal.mockClear();
await runSignalAction(
"react",
{ to: "+15559999999", emoji: "🔥" },
{ toolContext: { currentMessageId: "1737630212345" } },
);
expect(sendReactionSignal).toHaveBeenCalledTimes(1);
expect(sendReactionSignal).toHaveBeenCalledWith(
"+15559999999",
1737630212345,
"🔥",
expect.objectContaining({}),
);
});
it("rejects reaction when neither messageId nor toolContext.currentMessageId is provided", async () => {
const cfg = {
channels: { signal: { account: "+15550001111" } },
} as OpenClawConfig;
await expectSignalActionRejected(
{ to: "+15559999999", emoji: "✅" },
/messageId.*required/,
cfg,
);
});
it("requires targetAuthor for group reactions", async () => {
const cfg = {
channels: { signal: { account: "+15550001111" } },

View File

@@ -90,7 +90,7 @@ export const signalMessageActions: ChannelMessageActionAdapter = {
},
supportsAction: ({ action }) => action !== "send",
handleAction: async ({ action, params, cfg, accountId }) => {
handleAction: async ({ action, params, cfg, accountId, toolContext }) => {
if (action === "send") {
throw new Error("Send should be handled by outbound, not actions handler.");
}
@@ -126,10 +126,14 @@ export const signalMessageActions: ChannelMessageActionAdapter = {
throw new Error("recipient or group required");
}
const messageId = readStringParam(params, "messageId", {
required: true,
label: "messageId (timestamp)",
});
const messageId =
readStringParam(params, "messageId") ??
(toolContext?.currentMessageId != null ? String(toolContext.currentMessageId) : undefined);
if (!messageId) {
throw new Error(
"messageId (timestamp) required. Provide messageId explicitly or react to the current inbound message.",
);
}
const targetAuthor = readStringParam(params, "targetAuthor");
const targetAuthorUuid = readStringParam(params, "targetAuthorUuid");
if (target.groupId && !targetAuthor && !targetAuthorUuid) {