From 1e3e1d48e48a8f910435a7eba788398b8eb74d51 Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Sun, 22 Feb 2026 04:34:16 +0900 Subject: [PATCH] fix: render status code fences as HTML in Telegram and reduce allocations - Add statusToHTML() to convert backtick fences to
 tags with HTML escaping
- Apply ParseMode=HTML to EditStatus/EditTaskStatus so code blocks render properly
- Replace escapeHTML's chained ReplaceAll with single-pass strings.Replacer
- Extract statusSeparator const to deduplicate separator literals
- Replace fmt.Fprintf with strconv.Itoa+WriteString to avoid reflection
- Remove intermediate strings.Builder for latest entry prefix

Co-Authored-By: Claude Opus 4.6 
---
 pkg/agent/loop.go        | 26 +++++++++++++++---------
 pkg/channels/telegram.go | 44 +++++++++++++++++++++++++++++++++-------
 2 files changed, 53 insertions(+), 17 deletions(-)

diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go
index 14061b966..1d37732a2 100644
--- a/pkg/agent/loop.go
+++ b/pkg/agent/loop.go
@@ -1060,6 +1060,7 @@ const (
 	displayPastEntries = 3  // number of compact 1-line past entries
 	displayErrorLines  = 3  // content lines inside the error code block
 	maxLatestWidth     = 70 // rune limit for the latest entry command (~2 Telegram lines)
+	statusSeparator    = "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"
 )
 
 // buildRichStatus builds a fixed-height terminal-like status display.
@@ -1088,7 +1089,11 @@ func buildRichStatus(task *activeTask, isBackground bool, workspace string) stri
 	defer task.mu.Unlock()
 
 	var sb strings.Builder
-	fmt.Fprintf(&sb, "\U0001F504 Task in progress (%d/%d)\n", task.Iteration, task.MaxIter)
+	sb.WriteString("\U0001F504 Task in progress (")
+	sb.WriteString(strconv.Itoa(task.Iteration))
+	sb.WriteByte('/')
+	sb.WriteString(strconv.Itoa(task.MaxIter))
+	sb.WriteString(")\n")
 	if workspace != "" {
 		project := workspace
 		if idx := strings.LastIndex(workspace, "/"); idx >= 0 {
@@ -1097,10 +1102,12 @@ func buildRichStatus(task *activeTask, isBackground bool, workspace string) stri
 			project = workspace[idx+1:]
 		}
 		if project != "" {
-			fmt.Fprintf(&sb, "\U0001F4C2 %s\n", project)
+			sb.WriteString("\U0001F4C2 ")
+			sb.WriteString(project)
+			sb.WriteByte('\n')
 		}
 	}
-	sb.WriteString("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n")
+	sb.WriteString(statusSeparator)
 
 	// --- Task entries region (displayPastEntries + 4 lines) ---
 	entries := task.toolLog
@@ -1135,13 +1142,10 @@ func buildRichStatus(task *activeTask, isBackground bool, workspace string) stri
 	// Latest entry: always exactly 4 lines
 	if latest != nil {
 		// Line 1-2: command (truncated to ~2 Telegram lines)
-		var lb strings.Builder
-		lb.WriteString(latest.Name)
+		prefix := latest.Name
 		if latest.ArgsSnip != "" {
-			lb.WriteByte(' ')
-			lb.WriteString(latest.ArgsSnip)
+			prefix += " " + latest.ArgsSnip
 		}
-		prefix := lb.String()
 		if runes := []rune(prefix); len(runes) > maxLatestWidth {
 			sb.WriteString(string(runes[:maxLatestWidth-1]))
 			sb.WriteString("\u2026\n")
@@ -1150,7 +1154,9 @@ func buildRichStatus(task *activeTask, isBackground bool, workspace string) stri
 			sb.WriteByte('\n')
 		}
 		// Line 3: result
-		fmt.Fprintf(&sb, "  %s\n", latest.Result)
+		sb.WriteString("  ")
+		sb.WriteString(latest.Result)
+		sb.WriteByte('\n')
 	} else {
 		sb.WriteString("\u23F3 waiting...\n")
 		sb.WriteString("\u2800\n")
@@ -1160,7 +1166,7 @@ func buildRichStatus(task *activeTask, isBackground bool, workspace string) stri
 	sb.WriteString("\u2800\n")
 
 	// --- Error region (separator + code fence with displayErrorLines) ---
-	sb.WriteString("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n")
+	sb.WriteString(statusSeparator)
 	sb.WriteString("```\n")
 
 	errEntry := task.lastError
diff --git a/pkg/channels/telegram.go b/pkg/channels/telegram.go
index 62bbb1419..c66d6598b 100644
--- a/pkg/channels/telegram.go
+++ b/pkg/channels/telegram.go
@@ -179,7 +179,8 @@ func (c *TelegramChannel) EditStatus(ctx context.Context, msg bus.OutboundMessag
 	if !ok {
 		return nil // no placeholder → nothing to edit
 	}
-	editMsg := tu.EditMessageText(tu.ID(chatID), pID.(int), msg.Content)
+	editMsg := tu.EditMessageText(tu.ID(chatID), pID.(int), statusToHTML(msg.Content))
+	editMsg.ParseMode = telego.ModeHTML
 	_, err = c.bot.EditMessageText(ctx, editMsg)
 	return err
 }
@@ -196,7 +197,8 @@ func (c *TelegramChannel) EditTaskStatus(ctx context.Context, msg bus.OutboundMe
 	// Check if we already have a message for this task
 	if existingMsgID, ok := c.taskStatuses.Load(msg.TaskID); ok {
 		// Edit existing task status message
-		editMsg := tu.EditMessageText(tu.ID(chatID), existingMsgID.(int), msg.Content)
+		editMsg := tu.EditMessageText(tu.ID(chatID), existingMsgID.(int), statusToHTML(msg.Content))
+		editMsg.ParseMode = telego.ModeHTML
 		_, err = c.bot.EditMessageText(ctx, editMsg)
 		if err != nil {
 			logger.DebugCF("telegram", "EditTaskStatus edit failed", map[string]interface{}{
@@ -208,7 +210,8 @@ func (c *TelegramChannel) EditTaskStatus(ctx context.Context, msg bus.OutboundMe
 	}
 
 	// First task status message: send a new message and track it
-	tgMsg := tu.Message(tu.ID(chatID), msg.Content)
+	tgMsg := tu.Message(tu.ID(chatID), statusToHTML(msg.Content))
+	tgMsg.ParseMode = telego.ModeHTML
 	sent, err := c.bot.SendMessage(ctx, tgMsg)
 	if err != nil {
 		return err
@@ -982,9 +985,36 @@ func wrapByDisplayWidth(s string, maxWidth int) []string {
 	return lines
 }
 
+var htmlEscaper = strings.NewReplacer("&", "&", "<", "<", ">", ">")
+
 func escapeHTML(text string) string {
-	text = strings.ReplaceAll(text, "&", "&")
-	text = strings.ReplaceAll(text, "<", "<")
-	text = strings.ReplaceAll(text, ">", ">")
-	return text
+	return htmlEscaper.Replace(text)
+}
+
+// statusToHTML converts status message content to Telegram HTML.
+// It HTML-escapes the text and converts backtick code fences to 
 blocks.
+func statusToHTML(content string) string {
+	parts := strings.Split(content, "```")
+	if len(parts) < 3 {
+		return escapeHTML(content)
+	}
+	var sb strings.Builder
+	for i, part := range parts {
+		if i%2 == 0 {
+			sb.WriteString(escapeHTML(part))
+		} else {
+			// Strip optional language tag on the opening line
+			body := part
+			if nl := strings.Index(body, "\n"); nl >= 0 {
+				tag := strings.TrimSpace(body[:nl])
+				if tag == "" || !strings.ContainsAny(tag, " \t") {
+					body = body[nl+1:]
+				}
+			}
+			sb.WriteString("
")
+			sb.WriteString(escapeHTML(body))
+			sb.WriteString("
") + } + } + return sb.String() }