From 0dafafc534e3efe442d84e1e2eccb4c691d05122 Mon Sep 17 00:00:00 2001 From: Yetibot Date: Sun, 7 Jun 2026 18:42:35 +0000 Subject: [PATCH 1/3] fix(agent): restrict channel context to thread channels only --- src/yetibot/core/commands/agent.clj | 19 +++++++++++-------- test/yetibot/core/test/commands/agent.clj | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/yetibot/core/commands/agent.clj b/src/yetibot/core/commands/agent.clj index 16e6e5e9..fcd28c5b 100644 --- a/src/yetibot/core/commands/agent.clj +++ b/src/yetibot/core/commands/agent.clj @@ -573,14 +573,17 @@ whole thread lets a follow-up like \"retry\" resolve to the original ask." [channel-id] (try - (let [topic (try (:name @(discord/get-channel! (rest-conn) channel-id)) - (catch Exception _ nil)) - lines (->> (all-channel-messages channel-id) - (sort-by :timestamp) - (map (fn [m] (str (get-in m [:author :username]) ": " (:content m)))) - (remove string/blank?))] - (string/join "\n" (cond->> lines - (not (string/blank? topic)) (cons (str "[thread topic] " topic))))) + (let [channel @(discord/get-channel! (rest-conn) channel-id) + type (:type channel)] + (if (not (#{10 11 12} type)) + "" + (let [topic (:name channel) + lines (->> (all-channel-messages channel-id) + (sort-by :timestamp) + (map (fn [m] (str (get-in m [:author :username]) ": " (:content m)))) + (remove string/blank?))] + (string/join "\n" (cond->> lines + (not (string/blank? topic)) (cons (str "[thread topic] " topic))))))) (catch Exception e (debug "thread-context failed:" (.getMessage e)) ""))) ;; --------------------------------------------------------------------------- diff --git a/test/yetibot/core/test/commands/agent.clj b/test/yetibot/core/test/commands/agent.clj index a1d0ece1..7d48ec77 100644 --- a/test/yetibot/core/test/commands/agent.clj +++ b/test/yetibot/core/test/commands/agent.clj @@ -184,6 +184,21 @@ (fact "keeps the original request text" (agent/resume-request "add a bagif command") => (contains "add a bagif command"))) +(facts "about thread-context" + (fact "returns empty string if the channel is not a thread (type is not 10, 11, or 12)" + (#'agent/thread-context "channel-id-123") => "" + (provided + (#'agent/rest-conn) => "mock-conn" + (discljord.messaging/get-channel! "mock-conn" "channel-id-123") => (atom {:type 0 :name "general"}))) + + (fact "returns thread context if the channel is a thread (type 11)" + (#'agent/thread-context "thread-id-456") => "[thread topic] cool-thread\nalice: hello\nbob: world" + (provided + (#'agent/rest-conn) => "mock-conn" + (discljord.messaging/get-channel! "mock-conn" "thread-id-456") => (atom {:type 11 :name "cool-thread"}) + (#'agent/all-channel-messages "thread-id-456") => [{:author {:username "alice"} :content "hello" :timestamp 1} + {:author {:username "bob"} :content "world" :timestamp 2}]))) + (facts "about agent subcommands" (fact "agent-list-commands-cmd returns available commands in JSON" (agent/agent-list-commands-cmd {}) => {:result/value "{\"commands\":[\"cmd1\",\"cmd2\"]}"} From 46396a3f55b4c6a151d8bcce3a188495a57e90b9 Mon Sep 17 00:00:00 2001 From: Yetibot Date: Sun, 7 Jun 2026 18:49:47 +0000 Subject: [PATCH 2/3] fix: restrict thread context injection to threads with multiple messages --- src/yetibot/core/commands/agent.clj | 10 ++++++---- test/yetibot/core/test/commands/agent.clj | 11 +++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/yetibot/core/commands/agent.clj b/src/yetibot/core/commands/agent.clj index fcd28c5b..381be11d 100644 --- a/src/yetibot/core/commands/agent.clj +++ b/src/yetibot/core/commands/agent.clj @@ -577,13 +577,15 @@ type (:type channel)] (if (not (#{10 11 12} type)) "" - (let [topic (:name channel) - lines (->> (all-channel-messages channel-id) + (let [lines (->> (all-channel-messages channel-id) (sort-by :timestamp) (map (fn [m] (str (get-in m [:author :username]) ": " (:content m)))) (remove string/blank?))] - (string/join "\n" (cond->> lines - (not (string/blank? topic)) (cons (str "[thread topic] " topic))))))) + (if (<= (count lines) 1) + "" + (let [topic (:name channel)] + (string/join "\n" (cond->> lines + (not (string/blank? topic)) (cons (str "[thread topic] " topic))))))))) (catch Exception e (debug "thread-context failed:" (.getMessage e)) ""))) ;; --------------------------------------------------------------------------- diff --git a/test/yetibot/core/test/commands/agent.clj b/test/yetibot/core/test/commands/agent.clj index 7d48ec77..c4a2896e 100644 --- a/test/yetibot/core/test/commands/agent.clj +++ b/test/yetibot/core/test/commands/agent.clj @@ -191,13 +191,20 @@ (#'agent/rest-conn) => "mock-conn" (discljord.messaging/get-channel! "mock-conn" "channel-id-123") => (atom {:type 0 :name "general"}))) - (fact "returns thread context if the channel is a thread (type 11)" + (fact "returns thread context if the channel is a thread (type 11) with multiple messages" (#'agent/thread-context "thread-id-456") => "[thread topic] cool-thread\nalice: hello\nbob: world" (provided (#'agent/rest-conn) => "mock-conn" (discljord.messaging/get-channel! "mock-conn" "thread-id-456") => (atom {:type 11 :name "cool-thread"}) (#'agent/all-channel-messages "thread-id-456") => [{:author {:username "alice"} :content "hello" :timestamp 1} - {:author {:username "bob"} :content "world" :timestamp 2}]))) + {:author {:username "bob"} :content "world" :timestamp 2}])) + + (fact "returns empty string if the channel is a thread but has 1 or fewer messages (first message/prompt only)" + (#'agent/thread-context "thread-id-789") => "" + (provided + (#'agent/rest-conn) => "mock-conn" + (discljord.messaging/get-channel! "mock-conn" "thread-id-789") => (atom {:type 11 :name "cool-thread"}) + (#'agent/all-channel-messages "thread-id-789") => [{:author {:username "alice"} :content "hello" :timestamp 1}]))) (facts "about agent subcommands" (fact "agent-list-commands-cmd returns available commands in JSON" From 3b5072681a2ac37902df9872e9a6a91a316ae46b Mon Sep 17 00:00:00 2001 From: Yetibot Date: Sun, 7 Jun 2026 18:52:37 +0000 Subject: [PATCH 3/3] feat(agent): encourage Gemini to search entire channel in system prompt --- src/yetibot/core/commands/agent.clj | 7 ++++++- test/yetibot/core/test/commands/agent.clj | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/yetibot/core/commands/agent.clj b/src/yetibot/core/commands/agent.clj index 381be11d..51735b44 100644 --- a/src/yetibot/core/commands/agent.clj +++ b/src/yetibot/core/commands/agent.clj @@ -347,7 +347,12 @@ "code is not a request to audit CI.\n" "- If you can't tell what the request refers to, reply briefly asking for " "the missing detail. NEVER invent work or report an unrelated summary like " - "\"checked all repos, everything green\".\n\n" + "\"checked all repos, everything green\".\n" + "- If you need more background context than what is provided in the thread " + "context, you are highly encouraged to search the entire channel's history " + "using the `yetibot` tool with the `history` command (e.g., `history` or " + "`history | grep keyword`). It is your job as an autonomous agent to " + "perform this extra search when needed!\n\n" "The codebase (so you can go straight to the right place — don't rediscover " "it). The org is `yetibot`:\n" "- `yetibot/core` — the library: chat commands, adapters, and most logic.\n" diff --git a/test/yetibot/core/test/commands/agent.clj b/test/yetibot/core/test/commands/agent.clj index c4a2896e..8975e554 100644 --- a/test/yetibot/core/test/commands/agent.clj +++ b/test/yetibot/core/test/commands/agent.clj @@ -52,7 +52,9 @@ (fact "gives the bot an identity" (agent/build-agent-prompt "do x" nil nil) => (contains "Yetibot")) (fact "tells gemini to use yetibot tool to run yetibot commands" - (agent/build-agent-prompt "do x" nil nil) => (contains "yetibot"))) + (agent/build-agent-prompt "do x" nil nil) => (contains "yetibot")) + (fact "encourages searching entire channel if needed" + (agent/build-agent-prompt "do x" nil nil) => (contains "search the entire channel's history"))) (facts "about parse-json-response" (fact "pulls the response field"