From f34ca8369be9c2cd5363fa61b951b843814b5fcf Mon Sep 17 00:00:00 2001 From: mosir Date: Sat, 28 Feb 2026 12:47:38 +0800 Subject: [PATCH] fix(message): preserve same-round dedup by resetting only once per round --- pkg/agent/loop.go | 3 +++ pkg/tools/message.go | 7 ++++++- pkg/tools/message_test.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 29827d0b2..14adc11f0 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -879,6 +879,9 @@ func (al *AgentLoop) runLLMIteration( func (al *AgentLoop) updateToolContexts(agent *AgentInstance, channel, chatID string) { // Use ContextualTool interface instead of type assertions if tool, ok := agent.Tools.Get("message"); ok { + if rt, ok := tool.(interface{ BeginRound() }); ok { + rt.BeginRound() + } if mt, ok := tool.(tools.ContextualTool); ok { mt.SetContext(channel, chatID) } diff --git a/pkg/tools/message.go b/pkg/tools/message.go index 50335a88d..c0d76f888 100644 --- a/pkg/tools/message.go +++ b/pkg/tools/message.go @@ -53,7 +53,12 @@ func (t *MessageTool) Parameters() map[string]any { func (t *MessageTool) SetContext(channel, chatID string) { t.defaultChannel = channel t.defaultChatID = chatID - t.sentInRound = false // Reset send tracking for new processing round +} + +// BeginRound resets per-round send tracking. +// AgentLoop calls this once per inbound message before tool iterations begin. +func (t *MessageTool) BeginRound() { + t.sentInRound = false t.lastChannel = "" t.lastChatID = "" t.lastContent = "" diff --git a/pkg/tools/message_test.go b/pkg/tools/message_test.go index 6cc643860..bc53a9692 100644 --- a/pkg/tools/message_test.go +++ b/pkg/tools/message_test.go @@ -226,6 +226,36 @@ func TestMessageTool_Execute_SuppressDuplicateInSameRound(t *testing.T) { } } +func TestMessageTool_Execute_AllowsSameContentAfterBeginRound(t *testing.T) { + tool := NewMessageTool() + tool.SetContext("test-channel", "test-chat-id") + + callCount := 0 + tool.SetSendCallback(func(channel, chatID, content string) error { + callCount++ + return nil + }) + + ctx := context.Background() + args := map[string]any{"content": "same message"} + + first := tool.Execute(ctx, args) + if first.IsError || !first.Silent { + t.Fatalf("first result unexpected: %+v", first) + } + + tool.BeginRound() + tool.SetContext("test-channel", "test-chat-id") + + second := tool.Execute(ctx, args) + if second.IsError || !second.Silent { + t.Fatalf("second result unexpected: %+v", second) + } + if callCount != 2 { + t.Fatalf("send callback call count = %d, want 2", callCount) + } +} + func TestMessageTool_Name(t *testing.T) { tool := NewMessageTool() if tool.Name() != "message" {