fix(discord): Implement character-aware message splitting and soften memory warnings

This commit is contained in:
Andrew 2026-02-16 16:57:39 +00:00
parent 5ac9a46f59
commit e60728cf96
2 changed files with 71 additions and 67 deletions

View file

@ -740,7 +740,7 @@ func (al *AgentLoop) maybeSummarize(sessionKey, channel, chatID string) {
al.bus.PublishOutbound(bus.OutboundMessage{
Channel: channel,
ChatID: chatID,
Content: "⚠️ Memory threshold reached. Optimizing conversation history...",
Content: "🔄 Context optimized. (Memory threshold reached)",
})
}
al.summarizeSession(sessionKey)

View file

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"os"
"strings"
"time"
"github.com/bwmarrin/discordgo"
@ -120,97 +119,102 @@ func (c *DiscordChannel) Send(ctx context.Context, msg bus.OutboundMessage) erro
// splitMessage splits long messages into chunks, preserving code block integrity
// Uses natural boundaries (newlines, spaces) and extends messages slightly to avoid breaking code blocks
func splitMessage(content string, limit int) []string {
var messages []string
if limit > 1900 {
limit = 1900
}
runes := []rune(content)
if len(runes) <= limit {
return []string{content}
}
for len(content) > 0 {
if len(content) <= limit {
messages = append(messages, content)
var chunks []string
for len(runes) > 0 {
if len(runes) <= limit {
chunks = append(chunks, string(runes))
break
}
msgEnd := limit
splitAt := limit
// Find natural split point within the limit
msgEnd = findLastNewline(content[:limit], 200)
if msgEnd <= 0 {
msgEnd = findLastSpace(content[:limit], 100)
}
if msgEnd <= 0 {
msgEnd = limit
// Look for natural split points (newlines) in a window
window := 300
if splitAt < window {
window = splitAt
}
// Check if this would end with an incomplete code block
candidate := content[:msgEnd]
unclosedIdx := findLastUnclosedCodeBlock(candidate)
foundNatural := false
for i := splitAt - 1; i >= splitAt-window; i-- {
if runes[i] == '\n' {
splitAt = i + 1
foundNatural = true
break
}
}
if unclosedIdx >= 0 {
// Message would end with incomplete code block
// Try to extend to include the closing ``` (with some buffer)
extendedLimit := limit + 500 // Allow 500 char buffer for code blocks
if len(content) > extendedLimit {
closingIdx := findNextClosingCodeBlock(content, msgEnd)
if closingIdx > 0 && closingIdx <= extendedLimit {
// Extend to include the closing ```
msgEnd = closingIdx
} else {
// Can't find closing, split before the code block
msgEnd = findLastNewline(content[:unclosedIdx], 200)
if msgEnd <= 0 {
msgEnd = findLastSpace(content[:unclosedIdx], 100)
}
if msgEnd <= 0 {
msgEnd = unclosedIdx
if !foundNatural {
for i := splitAt - 1; i >= splitAt-window/2; i-- {
if runes[i] == ' ' || runes[i] == '\t' {
splitAt = i + 1
foundNatural = true
break
}
}
}
// Check for unclosed code blocks
chunkCandidate := runes[:splitAt]
if isInsideCodeBlock(chunkCandidate) {
// Try to find the closing code block within extended limit
extendedLimit := limit + 300
if extendedLimit > 2000 {
extendedLimit = 2000
}
foundClosing := false
for i := splitAt; i < len(runes)-2 && i < extendedLimit-3; i++ {
if runes[i] == '`' && runes[i+1] == '`' && runes[i+2] == '`' {
splitAt = i + 3
foundClosing = true
break
}
}
if !foundClosing {
// Can't find closing within reasonable limit, split before the block
for i := splitAt - 1; i >= 0; i-- {
if i+2 < len(runes) && runes[i] == '`' && runes[i+1] == '`' && runes[i+2] == '`' {
if i > 0 {
splitAt = i
}
break
}
}
} else {
// Remaining content fits within extended limit
msgEnd = len(content)
}
}
if msgEnd <= 0 {
msgEnd = limit
if splitAt <= 0 {
splitAt = limit
}
messages = append(messages, content[:msgEnd])
content = strings.TrimSpace(content[msgEnd:])
chunks = append(chunks, string(runes[:splitAt]))
runes = runes[splitAt:]
}
return messages
return chunks
}
// findLastUnclosedCodeBlock finds the last opening ``` that doesn't have a closing ```
// Returns the position of the opening ``` or -1 if all code blocks are complete
func findLastUnclosedCodeBlock(text string) int {
func isInsideCodeBlock(runes []rune) bool {
count := 0
lastOpenIdx := -1
for i := 0; i < len(text); i++ {
if i+2 < len(text) && text[i] == '`' && text[i+1] == '`' && text[i+2] == '`' {
if count == 0 {
lastOpenIdx = i
}
for i := 0; i < len(runes)-2; i++ {
if runes[i] == '`' && runes[i+1] == '`' && runes[i+2] == '`' {
count++
i += 2
}
}
// If odd number of ``` markers, last one is unclosed
if count%2 == 1 {
return lastOpenIdx
}
return -1
return count%2 != 0
}
// findNextClosingCodeBlock finds the next closing ``` starting from a position
// Returns the position after the closing ``` or -1 if not found
func findNextClosingCodeBlock(text string, startIdx int) int {
for i := startIdx; i < len(text); i++ {
if i+2 < len(text) && text[i] == '`' && text[i+1] == '`' && text[i+2] == '`' {
return i + 3
}
}
return -1
func _unused_marker_() {
}
// findLastNewline finds the last newline character within the last N characters