From c3187fadf2c24c0826d6d888a77a0d670d80f781 Mon Sep 17 00:00:00 2001 From: Tyler Slaton Date: Mon, 3 Aug 2026 15:31:08 -0700 Subject: [PATCH] fix: ignore bot-authored thread messages --- app/channel.test.ts | 44 +++++++++++++++++++++++++++++++++++++++++--- app/channel.tsx | 5 ++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/app/channel.test.ts b/app/channel.test.ts index 0ed6910..c5e57d8 100644 --- a/app/channel.test.ts +++ b/app/channel.test.ts @@ -178,14 +178,52 @@ describe("createOpenTagChannel", () => { expect((agent as CapturingAgent).calls).toHaveLength(2); }); - it("handles an unmentioned turn already admitted by managed ingress", async () => { + it.each(["bot", "app"] as const)( + "ignores %s-authored messages in a subscribed thread", + async (actorKind) => { + const { adapter, agent, channel } = makeChannel(); + + await channel.ɵruntime.start(); + await adapter.getSink().onTurn({ + conversationKey: "bot-loop-thread", + replyTarget: {}, + userText: "@Kite join us", + platform: "slack", + actor: { id: "U1", kind: "human" }, + operation: { + kind: "created", + logicalMessageId: "m1", + revisionId: "m1", + mentioned: true, + }, + }); + + await adapter.getSink().onTurn({ + conversationKey: "bot-loop-thread", + replyTarget: {}, + userText: "Canonical status confirmed: vibing cat it is.", + platform: "slack", + actor: { id: "B1", kind: actorKind }, + operation: { + kind: "created", + logicalMessageId: "m2", + revisionId: "m2", + mentioned: false, + }, + }); + + expect((agent as CapturingAgent).calls).toHaveLength(1); + }, + ); + + it("ignores an unmentioned turn in an unsubscribed thread", async () => { const { adapter, agent, channel } = makeChannel(); await channel.ɵruntime.start(); await adapter.getSink().onTurn({ conversationKey: "managed-thread", replyTarget: {}, - userText: "This turn passed the managed subscription gate", + userText: "This thread is not subscribed", platform: "slack", actor: { id: "U2", kind: "human" }, operation: { @@ -196,7 +234,7 @@ describe("createOpenTagChannel", () => { }, }); - expect((agent as CapturingAgent).calls).toHaveLength(1); + expect((agent as CapturingAgent).calls).toHaveLength(0); }); it("declares one managed Channel and retains app commands", () => { diff --git a/app/channel.tsx b/app/channel.tsx index bb11fae..512522f 100644 --- a/app/channel.tsx +++ b/app/channel.tsx @@ -74,8 +74,11 @@ export function createOpenTagChannel( }); channel.onMessage(async ({ thread, message }) => { - if(await thread.isSubscribed()) + if (message.actor.kind === "bot" || message.actor.kind === "app") return; + + if (await thread.isSubscribed()) { await runAgentSafely({ thread, message }); + } }); channel.onModalSubmit(FILE_ISSUE_CALLBACK, fileIssueSubmit);