From dd40e60bc1e841eee9b3d04b238e05e0b9ff2539 Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:05:29 +0900 Subject: [PATCH] fix: reset MessageTool.sentInRound per processing round ResetSentInRound() was never called by the agent loop, so once the LLM used the message tool in any round the flag stayed true forever. This caused all subsequent direct-answer responses to be suppressed by the alreadySent check in llmWorker, leaving only the "Thinking..." placeholder visible on Telegram. Co-Authored-By: Claude Opus 4.6 --- pkg/agent/loop.go | 10 ++++++++++ pkg/tools/message_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 89037d8dc..2117e2f39 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -584,6 +584,16 @@ func (al *AgentLoop) llmWorker(ctx context.Context, queue <-chan bus.InboundMess return } + // Reset per-round message-tool state so a previous round's + // tool-sent flag does not suppress this round's response. + if defaultAgent := al.registry.GetDefaultAgent(); defaultAgent != nil { + if tool, ok := defaultAgent.Tools.Get("message"); ok { + if mt, ok := tool.(*tools.MessageTool); ok { + mt.ResetSentInRound() + } + } + } + response, err := al.processMessage(ctx, msg) if err != nil { response = fmt.Sprintf("Error processing message: %v", err) diff --git a/pkg/tools/message_test.go b/pkg/tools/message_test.go index 05630972e..abd34448a 100644 --- a/pkg/tools/message_test.go +++ b/pkg/tools/message_test.go @@ -252,3 +252,29 @@ func TestMessageTool_Parameters(t *testing.T) { t.Error("Expected chat_id type to be 'string'") } } + +func TestMessageTool_ResetSentInRound(t *testing.T) { + tool := NewMessageTool() + tool.SetSendCallback(func(channel, chatID, content string) error { + return nil + }) + + ctx := WithToolContext(context.Background(), "ch", "cid") + + // First round: tool sends a message + tool.Execute(ctx, map[string]any{"content": "hello"}) + if !tool.HasSentInRound() { + t.Fatal("expected sentInRound=true after Execute") + } + + // Reset for second round + tool.ResetSentInRound() + if tool.HasSentInRound() { + t.Fatal("expected sentInRound=false after ResetSentInRound") + } + + // Second round: tool is NOT used (direct answer) → flag stays false + if tool.HasSentInRound() { + t.Error("expected sentInRound=false when tool was not used in this round") + } +}