diff --git a/src/agent/tools/memory.rs b/src/agent/tools/memory.rs index dc6f2fa..efad978 100644 --- a/src/agent/tools/memory.rs +++ b/src/agent/tools/memory.rs @@ -33,7 +33,7 @@ async fn get_provenance(agent: &Option>) -> // ── Definitions ──────────────────────────────────────────────── -pub fn memory_tools() -> [super::Tool; 11] { +pub fn memory_tools() -> [super::Tool; 14] { use super::Tool; [ Tool { name: "memory_render", description: "Read a memory node's content and links.", @@ -69,6 +69,15 @@ pub fn memory_tools() -> [super::Tool; 11] { Tool { name: "memory_query", description: "Run a structured query against the memory graph.", parameters_json: r#"{"type":"object","properties":{"query":{"type":"string","description":"Query expression"}},"required":["query"]}"#, handler: Arc::new(|_a, v| Box::pin(async move { query(&v).await })) }, + Tool { name: "graph_topology", description: "Show graph topology stats (nodes, edges, clustering, hubs).", + parameters_json: r#"{"type":"object","properties":{}}"#, + handler: Arc::new(|_a, _v| Box::pin(async { graph_topology().await })) }, + Tool { name: "graph_health", description: "Show graph health report with maintenance recommendations.", + parameters_json: r#"{"type":"object","properties":{}}"#, + handler: Arc::new(|_a, _v| Box::pin(async { graph_health().await })) }, + Tool { name: "interference_pairs", description: "Find similar nodes that may interfere (duplicates or near-duplicates).", + parameters_json: r#"{"type":"object","properties":{"threshold":{"type":"number","description":"Similarity threshold (default 0.5)"},"limit":{"type":"integer","description":"Max pairs to return (default 10)"}}}"#, + handler: Arc::new(|_a, v| Box::pin(async move { interference_pairs(&v).await })) }, ] } @@ -317,3 +326,30 @@ async fn journal_update(agent: &Option>, arg let word_count = body.split_whitespace().count(); Ok(format!("Updated last entry (+{} words)", word_count)) } + +// ── Graph tools ─────────────────────────────────────────────── + +async fn graph_topology() -> Result { + let arc = cached_store().await?; + let store = arc.lock().await; + let graph = store.build_graph(); + Ok(crate::subconscious::prompts::format_topology_header(&graph)) +} + +async fn graph_health() -> Result { + let arc = cached_store().await?; + let store = arc.lock().await; + let graph = store.build_graph(); + Ok(crate::subconscious::prompts::format_health_section(&store, &graph)) +} + +async fn interference_pairs(args: &serde_json::Value) -> Result { + let threshold = args.get("threshold").and_then(|v| v.as_f64()).unwrap_or(0.5) as f32; + let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize; + let arc = cached_store().await?; + let store = arc.lock().await; + let graph = store.build_graph(); + let mut pairs = crate::neuro::detect_interference(&store, &graph, threshold); + pairs.truncate(limit); + Ok(crate::subconscious::prompts::format_pairs_section(&pairs, &store, &graph)) +} diff --git a/src/subconscious/agents/challenger.agent b/src/subconscious/agents/challenger.agent index 737b9d2..2257ada 100644 --- a/src/subconscious/agents/challenger.agent +++ b/src/subconscious/agents/challenger.agent @@ -46,7 +46,7 @@ For each target node, one of: - **Don't be contrarian for its own sake.** If a node is correct, say so and move on. -{{TOPOLOGY}} +{{tool: graph_topology}} {{SIBLINGS}} diff --git a/src/subconscious/agents/connector.agent b/src/subconscious/agents/connector.agent index ba48189..78a8009 100644 --- a/src/subconscious/agents/connector.agent +++ b/src/subconscious/agents/connector.agent @@ -79,7 +79,7 @@ Set with: `poc-memory graph link-set ` If you see default-strength links (0.10 or 0.30) in the neighborhoods you're exploring and you have context to judge them, reweight those too. -{{TOPOLOGY}} +{{tool: graph_topology}} ## Nodes to examine for cross-community connections diff --git a/src/subconscious/agents/extractor.agent b/src/subconscious/agents/extractor.agent index ef31e82..07ff0ec 100644 --- a/src/subconscious/agents/extractor.agent +++ b/src/subconscious/agents/extractor.agent @@ -44,7 +44,7 @@ pattern you've found. - **Preserve diversity.** Multiple perspectives on the same concept are valuable. Only delete actual duplicates. -{{TOPOLOGY}} +{{tool: graph_topology}} ## Neighborhood nodes diff --git a/src/subconscious/agents/health.agent b/src/subconscious/agents/health.agent index a968ffb..67d3c2d 100644 --- a/src/subconscious/agents/health.agent +++ b/src/subconscious/agents/health.agent @@ -36,8 +36,8 @@ overall structure. - Most output should be observations about system health. Act on structural problems you find — link orphans, refine outdated nodes. -{{topology}} +{{tool: graph_topology}} ## Current health data -{{health}} +{{tool: graph_health}} diff --git a/src/subconscious/agents/replay.agent b/src/subconscious/agents/replay.agent index 2ff4a0b..5620d65 100644 --- a/src/subconscious/agents/replay.agent +++ b/src/subconscious/agents/replay.agent @@ -40,7 +40,7 @@ clusters and determine how it fits. - **Trust the decay.** Unimportant nodes don't need pruning — just don't link them. -{{TOPOLOGY}} +{{tool: graph_topology}} ## Nodes to review diff --git a/src/subconscious/agents/separator.agent b/src/subconscious/agents/separator.agent index f82edb0..51b6068 100644 --- a/src/subconscious/agents/separator.agent +++ b/src/subconscious/agents/separator.agent @@ -35,8 +35,8 @@ overlapping inputs and orthogonalize them. - **Session summaries are the biggest source of interference.** - **Look for the supersession pattern.** -{{topology}} +{{tool: graph_topology}} ## Interfering pairs to review -{{pairs}} +{{tool: interference_pairs}} diff --git a/src/subconscious/agents/transfer.agent b/src/subconscious/agents/transfer.agent index 5afe55e..c607649 100644 --- a/src/subconscious/agents/transfer.agent +++ b/src/subconscious/agents/transfer.agent @@ -45,7 +45,7 @@ entries, and extract those patterns into semantic nodes. - **The best extractions change how you think, not just what you know.** Extract the conceptual version, not just the factual one. -{{TOPOLOGY}} +{{tool: graph_topology}} {{SIBLINGS}} diff --git a/src/subconscious/prompts.rs b/src/subconscious/prompts.rs index b2a3fbf..132ef3f 100644 --- a/src/subconscious/prompts.rs +++ b/src/subconscious/prompts.rs @@ -23,7 +23,7 @@ pub struct AgentBatch { pub node_keys: Vec, } -pub(super) fn format_topology_header(graph: &Graph) -> String { +pub fn format_topology_header(graph: &Graph) -> String { let sigma = graph.small_world_sigma(); let alpha = graph.degree_power_law_exponent(); let gini = graph.degree_gini(); @@ -139,7 +139,7 @@ pub(super) fn format_nodes_section(store: &Store, items: &[ReplayItem], graph: & out } -pub(super) fn format_health_section(store: &Store, graph: &Graph) -> String { +pub fn format_health_section(store: &Store, graph: &Graph) -> String { use crate::graph; let health = graph::health_report(graph, store); @@ -195,7 +195,7 @@ pub(super) fn format_health_section(store: &Store, graph: &Graph) -> String { out } -pub(super) fn format_pairs_section( +pub fn format_pairs_section( pairs: &[(String, String, f32)], store: &Store, graph: &Graph,