diff --git a/poc-memory/agents/observation.agent b/poc-memory/agents/observation.agent index 56b63ec..3267a3e 100644 --- a/poc-memory/agents/observation.agent +++ b/poc-memory/agents/observation.agent @@ -65,6 +65,8 @@ If there's nothing worth extracting, just say so. - **Specific over vague.** - **Don't force it.** "Nothing new here" is valid output. +{{HUBS}} + ## Conversation fragments to mine {{CONVERSATIONS}} diff --git a/poc-memory/src/agents/defs.rs b/poc-memory/src/agents/defs.rs index 6bcab3d..80c02fb 100644 --- a/poc-memory/src/agents/defs.rs +++ b/poc-memory/src/agents/defs.rs @@ -271,6 +271,34 @@ fn resolve( }) } + "hubs" => { + // Top hub nodes by degree, spread apart (skip neighbors of already-selected hubs) + let mut hubs: Vec<(String, usize)> = store.nodes.iter() + .filter(|(k, n)| !n.deleted && !k.starts_with('_')) + .map(|(k, _)| { + let degree = graph.neighbors(k).len(); + (k.clone(), degree) + }) + .collect(); + hubs.sort_by(|a, b| b.1.cmp(&a.1)); + + let mut selected = Vec::new(); + let mut seen: std::collections::HashSet = std::collections::HashSet::new(); + for (key, degree) in &hubs { + if seen.contains(key) { continue; } + selected.push(format!(" - {} (degree {})", key, degree)); + // Mark neighbors as seen so we pick far-apart hubs + for (nbr, _) in graph.neighbors(key) { + seen.insert(nbr.clone()); + } + seen.insert(key.clone()); + if selected.len() >= 20 { break; } + } + + let text = format!("## Hub nodes (link targets)\n\n{}", selected.join("\n")); + Some(Resolved { text, keys: vec![] }) + } + // node:KEY — inline a node's content by key other if other.starts_with("node:") => { let key = &other[5..];