diff --git a/crates/zclaw-kernel/src/kernel/mod.rs b/crates/zclaw-kernel/src/kernel/mod.rs index ff5c66d..027a3d3 100644 --- a/crates/zclaw-kernel/src/kernel/mod.rs +++ b/crates/zclaw-kernel/src/kernel/mod.rs @@ -365,17 +365,6 @@ impl Kernel { chain.register(Arc::new(mw)); } - // Data masking middleware — DISABLED for desktop single-user scenario. - // The regex-based approach over-matches common Chinese text (e.g. "有一家公司" - // gets masked as a company entity). Response unmask was also missing. - // Re-enable when NLP-based entity detection is available. - // { - // use std::sync::Arc; - // let masker = Arc::new(zclaw_runtime::middleware::data_masking::DataMasker::new()); - // let mw = zclaw_runtime::middleware::data_masking::DataMaskingMiddleware::new(masker); - // chain.register(Arc::new(mw)); - // } - // Growth integration — cached to avoid recreating empty scorer per request let growth = { let mut cached = self.growth.lock().expect("growth lock"); @@ -388,6 +377,12 @@ impl Kernel { if let Some(ref embed_client) = self.embedding_client { g.configure_embedding(embed_client.clone()); } + // Bridge UserProfileStore so extract_combined() can persist profile signals + { + let profile_store = zclaw_memory::UserProfileStore::new(self.memory.pool()); + g = g.with_profile_store(std::sync::Arc::new(profile_store)); + tracing::info!("[Kernel] UserProfileStore bridged to GrowthIntegration"); + } *cached = Some(std::sync::Arc::new(g)); } cached.as_ref().expect("growth present").clone() @@ -567,6 +562,11 @@ impl Kernel { self.viking.clone() } + /// Get a reference to the shared MemoryStore + pub fn memory(&self) -> Arc { + self.memory.clone() + } + /// Set the LLM extraction driver for the Growth system. /// /// Required for `MemoryMiddleware` to extract memories from conversations diff --git a/crates/zclaw-runtime/src/growth.rs b/crates/zclaw-runtime/src/growth.rs index 7b01dc6..ee301f3 100644 --- a/crates/zclaw-runtime/src/growth.rs +++ b/crates/zclaw-runtime/src/growth.rs @@ -330,15 +330,43 @@ impl GrowthIntegration { && combined.experiences.is_empty() && !combined.profile_signals.has_any_signal() { + tracing::debug!( + "[GrowthIntegration] Combined extraction produced nothing for agent {}", + agent_id + ); return Ok(None); } let mem_count = combined.memories.len(); + tracing::info!( + "[GrowthIntegration] Combined extraction for agent {}: {} memories, {} experiences, {} profile signals", + agent_id, + mem_count, + combined.experiences.len(), + combined.profile_signals.signal_count() + ); // Store raw memories - self.extractor + match self.extractor .store_memories(&agent_id.to_string(), &combined.memories) - .await?; + .await + { + Ok(stored) => { + tracing::info!( + "[GrowthIntegration] Stored {} memories for agent {}", + stored, + agent_id + ); + } + Err(e) => { + tracing::error!( + "[GrowthIntegration] Failed to store memories for agent {}: {}", + agent_id, + e + ); + return Err(e); + } + } // Track learning event self.tracker @@ -362,6 +390,11 @@ impl GrowthIntegration { // Update user profile from extraction signals (L1 enhancement) if let Some(profile_store) = &self.profile_store { let updates = self.profile_updater.collect_updates(&combined); + tracing::info!( + "[GrowthIntegration] Applying {} profile updates for agent {}", + updates.len(), + agent_id + ); let user_id = agent_id.to_string(); for update in updates { let result = match update.kind { diff --git a/desktop/src-tauri/src/kernel_commands/agent.rs b/desktop/src-tauri/src/kernel_commands/agent.rs index 2ee12de..01fcd7d 100644 --- a/desktop/src-tauri/src/kernel_commands/agent.rs +++ b/desktop/src-tauri/src/kernel_commands/agent.rs @@ -185,16 +185,23 @@ pub async fn agent_get( let mut info = kernel.get_agent(&id); - // Extend with UserProfile if available + // Extend with UserProfile if available (reads from same MemoryStore pool as middleware writes to) if let Some(ref mut agent_info) = info { - if let Ok(storage) = crate::viking_commands::get_storage().await { - let profile_store = zclaw_memory::UserProfileStore::new(storage.pool().clone()); - if let Ok(Some(profile)) = profile_store.get(&agent_id).await { + let memory_store = kernel.memory(); + let profile_store = zclaw_memory::UserProfileStore::new(memory_store.pool()); + match profile_store.get(&agent_id).await { + Ok(Some(profile)) => { match serde_json::to_value(&profile) { Ok(val) => agent_info.user_profile = Some(val), Err(e) => tracing::warn!("[agent_get] Failed to serialize UserProfile: {}", e), } } + Ok(None) => { + tracing::debug!("[agent_get] No UserProfile found for agent {}", agent_id); + } + Err(e) => { + tracing::warn!("[agent_get] Failed to read UserProfile: {}", e); + } } }