From 5fbafa7e47c96255b7577f2d63963b0017d00976 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 22 Apr 2026 11:49:25 -0700 Subject: [PATCH] fix(hooks): prefer shared outbound conversation context --- extensions/thread-ownership/index.test.ts | 24 +++++++++++++++++++++++ extensions/thread-ownership/index.ts | 6 +++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/extensions/thread-ownership/index.test.ts b/extensions/thread-ownership/index.test.ts index 651cc57823c..e1e973bd8f6 100644 --- a/extensions/thread-ownership/index.test.ts +++ b/extensions/thread-ownership/index.test.ts @@ -88,6 +88,30 @@ describe("thread-ownership plugin", () => { ); }); + it("prefers shared conversationId over non-canonical Slack target shapes", async () => { + vi.mocked(globalThis.fetch).mockResolvedValue( + new Response(JSON.stringify({ owner: "test-agent" }), { status: 200 }), + ); + + const result = await hooks.message_sending( + { + content: "hello", + replyToId: "1234.5678", + to: "channel:C123", + }, + { channelId: "slack", conversationId: "C123" }, + ); + + expect(result).toBeUndefined(); + expect(globalThis.fetch).toHaveBeenCalledWith( + "http://localhost:8750/api/v1/ownership/C123/1234.5678", + expect.objectContaining({ + method: "POST", + body: JSON.stringify({ agent_id: "test-agent" }), + }), + ); + }); + it("cancels when thread owned by another agent", async () => { vi.mocked(globalThis.fetch).mockResolvedValue( new Response(JSON.stringify({ owner: "other-agent" }), { status: 409 }), diff --git a/extensions/thread-ownership/index.ts b/extensions/thread-ownership/index.ts index d0f0b81d9e2..4fb6cd96a10 100644 --- a/extensions/thread-ownership/index.ts +++ b/extensions/thread-ownership/index.ts @@ -104,7 +104,11 @@ export default definePluginEntry({ resolveThreadToken(event.threadId) || resolveThreadToken(event.metadata?.threadId) || resolveThreadToken(event.metadata?.threadTs); - const channelId = (event.metadata?.channelId as string) ?? event.to; + const channelId = + normalizeOptionalString(ctx.conversationId) || + normalizeOptionalString(event.metadata?.channelId) || + normalizeOptionalString(event.to) || + ""; if (!threadTs) { return undefined; }