fix(slack): mrkdwn + thread edge cases (#464) (thanks @austinm911)

This commit is contained in:
Peter Steinberger
2026-01-09 22:09:02 +01:00
parent 8890fbcf38
commit 84046cbad8
8 changed files with 222 additions and 49 deletions

View File

@@ -197,6 +197,52 @@ describe("handleSlackAction", () => {
);
});
it("replyToMode=first marks hasRepliedRef even when threadTs is explicit", async () => {
const cfg = { slack: { botToken: "tok" } } as ClawdbotConfig;
sendSlackMessage.mockClear();
const hasRepliedRef = { value: false };
const context = {
currentChannelId: "C123",
currentThreadTs: "1111111111.111111",
replyToMode: "first" as const,
hasRepliedRef,
};
await handleSlackAction(
{
action: "sendMessage",
to: "channel:C123",
content: "Explicit",
threadTs: "2222222222.222222",
},
cfg,
context,
);
expect(sendSlackMessage).toHaveBeenLastCalledWith(
"channel:C123",
"Explicit",
{
mediaUrl: undefined,
threadTs: "2222222222.222222",
},
);
expect(hasRepliedRef.value).toBe(true);
await handleSlackAction(
{ action: "sendMessage", to: "channel:C123", content: "Second" },
cfg,
context,
);
expect(sendSlackMessage).toHaveBeenLastCalledWith(
"channel:C123",
"Second",
{
mediaUrl: undefined,
threadTs: undefined,
},
);
});
it("replyToMode=first without hasRepliedRef does not thread", async () => {
const cfg = { slack: { botToken: "tok" } } as ClawdbotConfig;
sendSlackMessage.mockClear();

View File

@@ -152,6 +152,19 @@ export async function handleSlackAction(
mediaUrl: mediaUrl ?? undefined,
threadTs: threadTs ?? undefined,
});
// Keep "first" mode consistent even when the agent explicitly provided
// threadTs: once we send a message to the current channel, consider the
// first reply "used" so later tool calls don't auto-thread again.
if (context?.hasRepliedRef && context.currentChannelId) {
const normalizedTarget = to.startsWith("channel:")
? to.slice("channel:".length)
: to;
if (normalizedTarget === context.currentChannelId) {
context.hasRepliedRef.value = true;
}
}
return jsonResult({ ok: true, result });
}
case "editMessage": {