fix: resolve merge conflicts and fix threading tests

- Update MessageToolOptions type to include Slack threading options
- Remove duplicate threadTs property in slack/actions.ts
- Fix replyThreadTs parameter name in monitor.ts
- Update test to correctly verify 'first' mode threading behavior:
  - 'off' mode: no threading unless already in a thread
  - 'first' mode: first reply starts a thread
- Add new test case for 'first' mode threading
This commit is contained in:
Austin Mudd
2026-01-09 09:17:47 -08:00
committed by Peter Steinberger
parent b4663ed11c
commit 909c14d443
4 changed files with 56 additions and 4 deletions

View File

@@ -134,6 +134,14 @@ const MessageToolSchema = Type.Object({
type MessageToolOptions = { type MessageToolOptions = {
agentAccountId?: string; agentAccountId?: string;
config?: ClawdbotConfig; config?: ClawdbotConfig;
/** Current channel ID for auto-threading (Slack). */
currentChannelId?: string;
/** Current thread timestamp for auto-threading (Slack). */
currentThreadTs?: string;
/** Reply-to mode for Slack auto-threading. */
replyToMode?: "off" | "first" | "all";
/** Mutable ref to track if a reply was sent (for "first" mode). */
hasRepliedRef?: { value: boolean };
}; };
function hasTelegramInlineButtons(cfg: ClawdbotConfig): boolean { function hasTelegramInlineButtons(cfg: ClawdbotConfig): boolean {

View File

@@ -151,7 +151,6 @@ export async function sendSlackMessage(
accountId: opts.accountId, accountId: opts.accountId,
token: opts.token, token: opts.token,
mediaUrl: opts.mediaUrl, mediaUrl: opts.mediaUrl,
threadTs: opts.threadTs,
client: opts.client, client: opts.client,
threadTs: opts.threadTs, threadTs: opts.threadTs,
}); });

View File

@@ -601,8 +601,52 @@ describe("monitorSlackProvider tool results", () => {
expect(ctx.ParentSessionKey).toBe("agent:support:slack:channel:C1"); expect(ctx.ParentSessionKey).toBe("agent:support:slack:channel:C1");
}); });
it("keeps replies in channel root when message is not threaded", async () => { it("keeps replies in channel root when message is not threaded (replyToMode off)", async () => {
replyMock.mockResolvedValue({ text: "root reply" }); replyMock.mockResolvedValue({ text: "root reply" });
config = {
messages: {
responsePrefix: "PFX",
ackReaction: "👀",
ackReactionScope: "group-mentions",
},
slack: {
dm: { enabled: true, policy: "open", allowFrom: ["*"] },
replyToMode: "off",
},
};
const controller = new AbortController();
const run = monitorSlackProvider({
botToken: "bot-token",
appToken: "app-token",
abortSignal: controller.signal,
});
await waitForEvent("message");
const handler = getSlackHandlers()?.get("message");
if (!handler) throw new Error("Slack message handler not registered");
await handler({
event: {
type: "message",
user: "U1",
text: "hello",
ts: "789",
channel: "C1",
channel_type: "im",
},
});
await flush();
controller.abort();
await run;
expect(sendMock).toHaveBeenCalledTimes(1);
expect(sendMock.mock.calls[0][2]).toMatchObject({ threadTs: undefined });
});
it("threads first reply when replyToMode is first and message is not threaded", async () => {
replyMock.mockResolvedValue({ text: "first reply" });
config = { config = {
messages: { messages: {
responsePrefix: "PFX", responsePrefix: "PFX",
@@ -642,7 +686,8 @@ describe("monitorSlackProvider tool results", () => {
await run; await run;
expect(sendMock).toHaveBeenCalledTimes(1); expect(sendMock).toHaveBeenCalledTimes(1);
expect(sendMock.mock.calls[0][2]).toMatchObject({ threadTs: undefined }); // First reply starts a thread under the incoming message
expect(sendMock.mock.calls[0][2]).toMatchObject({ threadTs: "789" });
}); });
it("forces thread replies when replyToId is set", async () => { it("forces thread replies when replyToId is set", async () => {

View File

@@ -1110,7 +1110,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
accountId: account.accountId, accountId: account.accountId,
runtime, runtime,
textLimit, textLimit,
threadTs: effectiveThreadTs, replyThreadTs: effectiveThreadTs,
}); });
hasRepliedRef.value = true; hasRepliedRef.value = true;
}, },