From 5dc696e3c6572462537c2c88f8abb52c85bcabfd Mon Sep 17 00:00:00 2001 From: viettranx Date: Mon, 13 Apr 2026 19:09:29 +0700 Subject: [PATCH] fix(agent): preserve up to 30 media refs during history compaction Both v3 mid-loop compaction and v2 background summarization were dropping MediaRefs when summarizing old messages, making previously shared images/documents permanently inaccessible to the agent. Now collect up to 30 most recent MediaRefs from compacted messages and attach them to the summary/first-kept message so they survive the compaction cycle. --- internal/agent/loop_compact.go | 17 +++++++++++++++-- internal/agent/loop_history_sanitize.go | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/internal/agent/loop_compact.go b/internal/agent/loop_compact.go index 1f8a136e..a7e81119 100644 --- a/internal/agent/loop_compact.go +++ b/internal/agent/loop_compact.go @@ -100,9 +100,22 @@ func (l *Loop) compactMessagesInPlace(ctx context.Context, messages []providers. return nil } + // Collect MediaRefs from compacted messages (keep up to 30 most recent). + const maxPreservedMediaRefs = 30 + var preservedRefs []providers.MediaRef + for i := len(toSummarize) - 1; i >= 0 && len(preservedRefs) < maxPreservedMediaRefs; i-- { + for _, ref := range toSummarize[i].MediaRefs { + preservedRefs = append(preservedRefs, ref) + if len(preservedRefs) >= maxPreservedMediaRefs { + break + } + } + } + summary := providers.Message{ - Role: "user", - Content: "[Summary of earlier conversation]\n" + SanitizeAssistantContent(resp.Content), + Role: "user", + Content: "[Summary of earlier conversation]\n" + SanitizeAssistantContent(resp.Content), + MediaRefs: preservedRefs, } result := make([]providers.Message, 0, 1+keepCount) result = append(result, summary) diff --git a/internal/agent/loop_history_sanitize.go b/internal/agent/loop_history_sanitize.go index 13acea95..e7c880d4 100644 --- a/internal/agent/loop_history_sanitize.go +++ b/internal/agent/loop_history_sanitize.go @@ -292,8 +292,33 @@ func (l *Loop) maybeSummarize(ctx context.Context, sessionKey string) { return } + // Collect MediaRefs from messages about to be truncated (keep up to 30 most recent). + const maxPreservedMediaRefs = 30 + var preservedRefs []providers.MediaRef + for i := len(toSummarize) - 1; i >= 0 && len(preservedRefs) < maxPreservedMediaRefs; i-- { + for _, ref := range toSummarize[i].MediaRefs { + preservedRefs = append(preservedRefs, ref) + if len(preservedRefs) >= maxPreservedMediaRefs { + break + } + } + } + l.sessions.SetSummary(sctx, sessionKey, SanitizeAssistantContent(resp.Content)) l.sessions.TruncateHistory(sctx, sessionKey, keepLast) + + // Inject preserved MediaRefs into the first kept message so they survive truncation. + if len(preservedRefs) > 0 { + kept := l.sessions.GetHistory(sctx, sessionKey) + if len(kept) > 0 { + kept[0].MediaRefs = append(preservedRefs, kept[0].MediaRefs...) + // Cap total refs on this message at maxPreservedMediaRefs. + if len(kept[0].MediaRefs) > maxPreservedMediaRefs { + kept[0].MediaRefs = kept[0].MediaRefs[:maxPreservedMediaRefs] + } + l.sessions.SetHistory(sctx, sessionKey, kept) + } + } l.sessions.IncrementCompaction(sctx, sessionKey) // Mirror SessionMetaKeyLastCompactionAt from the v3 prune/compact path // so the legacy v2 post-turn summarizer also surfaces compaction cadence.