From 5de543e26cf27219b1026948eb97ed7fcd404c99 Mon Sep 17 00:00:00 2001 From: Dino Hensen Date: Wed, 18 Mar 2026 23:18:06 +0100 Subject: [PATCH] fix(cron): publish agent response to user after scheduled job runs ProcessDirectWithChannel called processMessage and discarded the returned response, relying on a wrong assumption that AgentLoop.Run() would publish it. Run() only publishes for messages that go through the inbound bus loop; cron jobs call ProcessDirectWithChannel directly, bypassing it entirely. Add the same HasSentInRound() guard + PublishOutbound call that Run() uses, so the response reaches the user's channel (e.g. Telegram) after a cron job triggers LLM inference. Co-Authored-By: Claude Sonnet 4.6 --- pkg/agent/loop.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 33da33e92..03d8f82e2 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -666,7 +666,34 @@ func (al *AgentLoop) ProcessDirectWithChannel( SessionKey: sessionKey, } - return al.processMessage(ctx, msg) + response, err := al.processMessage(ctx, msg) + if err != nil { + return response, err + } + + // Publish response if non-empty and the message tool hasn't already sent it. + // This mirrors the publish logic in Run() for direct/cron invocations that + // bypass the main message loop. + if response != "" { + alreadySent := false + defaultAgent := al.GetRegistry().GetDefaultAgent() + if defaultAgent != nil { + if tool, ok := defaultAgent.Tools.Get("message"); ok { + if mt, ok := tool.(*tools.MessageTool); ok { + alreadySent = mt.HasSentInRound() + } + } + } + if !alreadySent { + al.bus.PublishOutbound(ctx, bus.OutboundMessage{ + Channel: msg.Channel, + ChatID: msg.ChatID, + Content: response, + }) + } + } + + return response, nil } // ProcessHeartbeat processes a heartbeat request without session history.