From 1743f9d079d22887f2b2c4388040227130fa8b8f Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sun, 22 Feb 2026 23:47:30 +0800 Subject: [PATCH 1/3] fix: filter out empty or whitespace-only messages in processMessage --- pkg/agent/loop.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index b36f4a0c4..ac88e8b7d 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -266,6 +266,15 @@ func (al *AgentLoop) ProcessHeartbeat(ctx context.Context, content, channel, cha } func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage) (string, error) { + // Filter out empty or whitespace-only messages + if strings.TrimSpace(msg.Content) == "" { + logger.DebugCF("agent", "Dropped empty message", map[string]any{ + "channel": msg.Channel, + "sender_id": msg.SenderID, + }) + return "", nil + } + // Add message preview to log (show full content for error messages) var logContent string if strings.Contains(msg.Content, "Error:") || strings.Contains(msg.Content, "error") { From 80fa303c2eac38a22104999f505c6f6bbf8f17a3 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 23 Feb 2026 14:08:15 +0800 Subject: [PATCH 2/3] fix: improve access token handling and refresh logic in WeComAppChannel --- pkg/channels/wecom_app.go | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/pkg/channels/wecom_app.go b/pkg/channels/wecom_app.go index 715c48707..cfcfbbb95 100644 --- a/pkg/channels/wecom_app.go +++ b/pkg/channels/wecom_app.go @@ -215,7 +215,15 @@ func (c *WeComAppChannel) Send(ctx context.Context, msg bus.OutboundMessage) err accessToken := c.getAccessToken() if accessToken == "" { - return fmt.Errorf("no valid access token available") + // Token expired or not yet acquired — attempt an on-demand refresh + logger.WarnC("wecom_app", "Access token missing or expired, attempting on-demand refresh") + if err := c.refreshAccessToken(); err != nil { + return fmt.Errorf("access token unavailable and refresh failed: %w", err) + } + accessToken = c.getAccessToken() + if accessToken == "" { + return fmt.Errorf("no valid access token available after refresh") + } } logger.DebugCF("wecom_app", "Sending message", map[string]any{ @@ -453,14 +461,29 @@ func (c *WeComAppChannel) processMessage(ctx context.Context, msg WeComXMLMessag // tokenRefreshLoop periodically refreshes the access token func (c *WeComAppChannel) tokenRefreshLoop() { - ticker := time.NewTicker(5 * time.Minute) - defer ticker.Stop() + const fallbackInterval = 30 * time.Minute + const earlyRefresh = 5 * time.Minute for { + // Calculate sleep duration based on current token expiry + c.tokenMu.RLock() + expiry := c.tokenExpiry + c.tokenMu.RUnlock() + + var sleepDur time.Duration + if expiry.IsZero() { + // Token never successfully acquired + sleepDur = fallbackInterval + } else { + sleepDur = max(time.Until(expiry.Add(-earlyRefresh)), + // minimum 1 minute to avoid tight loop + time.Minute) + } + select { case <-c.ctx.Done(): return - case <-ticker.C: + case <-time.After(sleepDur): if err := c.refreshAccessToken(); err != nil { logger.ErrorCF("wecom_app", "Failed to refresh access token", map[string]any{ "error": err.Error(), From 7be27f2d928ef127c4901e8488aaecd873cdfd2b Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Fri, 27 Feb 2026 16:12:59 +0800 Subject: [PATCH 3/3] fix: enhance message handling by refining tool message sanitization and updating WeCom message sending method --- pkg/agent/context.go | 15 +++++++++++++-- pkg/agent/loop.go | 3 +-- pkg/channels/wecom_app.go | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/agent/context.go b/pkg/agent/context.go index a9db5afdd..7bd55d4ab 100644 --- a/pkg/agent/context.go +++ b/pkg/agent/context.go @@ -229,8 +229,19 @@ func sanitizeHistoryForProvider(history []providers.Message) []providers.Message logger.DebugCF("agent", "Dropping orphaned leading tool message", map[string]any{}) continue } - last := sanitized[len(sanitized)-1] - if last.Role != "assistant" || len(last.ToolCalls) == 0 { + // Walk backwards to find the nearest assistant message, + // skipping over any preceding tool messages (multi-tool-call case). + foundAssistant := false + for i := len(sanitized) - 1; i >= 0; i-- { + if sanitized[i].Role == "tool" { + continue + } + if sanitized[i].Role == "assistant" && len(sanitized[i].ToolCalls) > 0 { + foundAssistant = true + } + break + } + if !foundAssistant { logger.DebugCF("agent", "Dropping orphaned tool message", map[string]any{}) continue } diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index ac88e8b7d..c77271098 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -463,8 +463,7 @@ func (al *AgentLoop) runAgentLoop(ctx context.Context, agent *AgentInstance, opt } // 9. Log response - responsePreview := utils.Truncate(finalContent, 120) - logger.InfoCF("agent", fmt.Sprintf("Response: %s", responsePreview), + logger.InfoCF("agent", fmt.Sprintf("Response: [%s]", finalContent), map[string]any{ "agent_id": agent.ID, "session_key": opts.SessionKey, diff --git a/pkg/channels/wecom_app.go b/pkg/channels/wecom_app.go index cfcfbbb95..fd169879a 100644 --- a/pkg/channels/wecom_app.go +++ b/pkg/channels/wecom_app.go @@ -231,7 +231,7 @@ func (c *WeComAppChannel) Send(ctx context.Context, msg bus.OutboundMessage) err "preview": utils.Truncate(msg.Content, 100), }) - return c.sendTextMessage(ctx, accessToken, msg.ChatID, msg.Content) + return c.sendMarkdownMessage(ctx, accessToken, msg.ChatID, msg.Content) } // handleWebhook handles incoming webhook requests from WeCom