seen_recent: separate current vs pre-compaction seen sets

Present the two seen sets separately to the surface agent:
- Current: already in context, don't re-surface
- Pre-compaction: context was reset, re-surface if still relevant

This lets the agent re-inject important memories after compaction
instead of treating everything ever surfaced as "already shown."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kent Overstreet 2026-03-22 14:26:56 -04:00
parent 870b87df1b
commit 9512dc0a31

View file

@ -495,36 +495,61 @@ fn resolve_seen_recent() -> String {
}
let state_dir = std::path::PathBuf::from("/tmp/claude-memory-search");
let mut entries: Vec<(String, String)> = Vec::new();
for suffix in ["", "-prev"] {
let parse_seen = |suffix: &str| -> Vec<(String, String)> {
let path = state_dir.join(format!("seen{}-{}", suffix, session_id));
if let Ok(content) = std::fs::read_to_string(&path) {
entries.extend(
std::fs::read_to_string(&path).ok()
.map(|content| {
content.lines()
.filter(|s| !s.is_empty())
.filter_map(|line| {
let (ts, key) = line.split_once('\t')?;
Some((ts.to_string(), key.to_string()))
})
);
}
}
.collect()
})
.unwrap_or_default()
};
if entries.is_empty() {
let current = parse_seen("");
let prev = parse_seen("-prev");
if current.is_empty() && prev.is_empty() {
return "(no memories surfaced yet this session)".to_string();
}
// Sort newest first, dedup
entries.sort_by(|a, b| b.0.cmp(&a.0));
let mut seen = std::collections::HashSet::new();
let recent: Vec<String> = entries.into_iter()
.filter(|(_, key)| seen.insert(key.clone()))
.take(20)
.map(|(ts, key)| format!("- {} (surfaced {})", key, ts))
.collect();
let mut out = String::new();
recent.join("\n")
// Current: already in this context, don't re-surface
if !current.is_empty() {
out.push_str("Already surfaced this context (don't re-surface unless conversation shifted):\n");
let mut seen = std::collections::HashSet::new();
for (ts, key) in &current {
if seen.insert(key.clone()) {
out.push_str(&format!("- {} (surfaced {})\n", key, ts));
}
}
}
// Prev: surfaced before compaction, MAY need re-surfacing
if !prev.is_empty() {
let current_keys: std::collections::HashSet<_> = current.iter()
.map(|(_, k)| k.as_str()).collect();
let prev_only: Vec<_> = prev.iter()
.filter(|(_, k)| !current_keys.contains(k.as_str()))
.collect();
if !prev_only.is_empty() {
out.push_str("\nSurfaced before compaction (context was reset — re-surface if still relevant):\n");
let mut seen = std::collections::HashSet::new();
for (ts, key) in prev_only {
if seen.insert(key.clone()) {
out.push_str(&format!("- {} (pre-compaction, {})\n", key, ts));
}
}
}
}
out.trim_end().to_string()
}
/// Resolve all {{placeholder}} patterns in a prompt template.