From f4bbbcbfb3fae2034b5a6ce3166c1b9f0bf89466 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 22 Apr 2026 11:55:57 -0700 Subject: [PATCH] fix(hooks): canonicalize thread ownership conversation ids --- extensions/thread-ownership/index.test.ts | 23 +++++++++++++++++++++++ extensions/thread-ownership/index.ts | 5 ++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/extensions/thread-ownership/index.test.ts b/extensions/thread-ownership/index.test.ts index e1e973bd8f6..49346e114d1 100644 --- a/extensions/thread-ownership/index.test.ts +++ b/extensions/thread-ownership/index.test.ts @@ -161,6 +161,29 @@ describe("thread-ownership plugin", () => { expect(globalThis.fetch).not.toHaveBeenCalled(); }); + it("tracks mentions under the shared conversationId when inbound metadata is non-canonical", async () => { + await hooks.message_received( + { + content: "Hey @TestBot help me", + threadId: "9999.0002", + metadata: { channelId: "channel:C456" }, + }, + { channelId: "slack", conversationId: "C456" }, + ); + + const result = await hooks.message_sending( + { + content: "Sure!", + replyToId: "9999.0002", + to: "channel:C456", + }, + { channelId: "slack", conversationId: "C456" }, + ); + + expect(result).toBeUndefined(); + expect(globalThis.fetch).not.toHaveBeenCalled(); + }); + it("ignores @-mentions on non-slack channels", async () => { // Use a unique thread key so module-level state from other tests doesn't interfere. await hooks.message_received( diff --git a/extensions/thread-ownership/index.ts b/extensions/thread-ownership/index.ts index 4fb6cd96a10..bb9197fa548 100644 --- a/extensions/thread-ownership/index.ts +++ b/extensions/thread-ownership/index.ts @@ -80,7 +80,10 @@ export default definePluginEntry({ resolveThreadToken(event.threadId) || resolveThreadToken(event.metadata?.threadId) || resolveThreadToken(event.metadata?.threadTs); - const channelId = (event.metadata?.channelId as string) ?? ctx.conversationId ?? ""; + const channelId = + normalizeOptionalString(ctx.conversationId) || + normalizeOptionalString(event.metadata?.channelId) || + ""; if (!threadTs || !channelId) { return; }