From a5b887051db7f4ad852e16bd820e2235a07355ae Mon Sep 17 00:00:00 2001 From: iven Date: Tue, 7 Apr 2026 10:23:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20butler=20audit=20critical=20fixes=20?= =?UTF-8?q?=E2=80=94=20pain=20detection,=20proposal=20trigger,=20URI=20+?= =?UTF-8?q?=20data=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5 fixes from focused audit: - Connect analyze_for_pain_signals() to post_conversation_hook (pain points now auto-created) - Add "generate solution" button in InsightsSection for high-confidence pain points (>=0.7) - Fix Memory URI mismatch: viking://agents/ → viking://agent/ (singular) - Remove duplicate .then() chain in useButlerInsights (was destructuring undefined) - Update stale director.rs doc comment (multi-agent now enabled by default) --- crates/zclaw-kernel/src/director.rs | 7 ++-- desktop/src-tauri/src/intelligence_hooks.rs | 31 ++++++++++++++++ .../ButlerPanel/InsightsSection.tsx | 35 +++++++++++++++++-- .../components/ButlerPanel/MemorySection.tsx | 2 +- desktop/src/components/ButlerPanel/index.tsx | 2 +- desktop/src/hooks/useButlerInsights.ts | 4 --- 6 files changed, 69 insertions(+), 12 deletions(-) diff --git a/crates/zclaw-kernel/src/director.rs b/crates/zclaw-kernel/src/director.rs index 5af0cb4..88eeef3 100644 --- a/crates/zclaw-kernel/src/director.rs +++ b/crates/zclaw-kernel/src/director.rs @@ -6,10 +6,9 @@ //! - Supporting multiple scheduling strategies //! - Coordinating agent responses //! -//! **Status**: This module is fully implemented but gated behind the `multi-agent` feature. -//! The desktop build does not currently enable this feature. When multi-agent support -//! is ready for production, add Tauri commands to create and interact with the Director, -//! and enable the feature in `desktop/src-tauri/Cargo.toml`. +//! **Status**: This module is enabled by default via the `multi-agent` feature in the +//! desktop build. The Director orchestrates butler delegation, task decomposition, and +//! expert agent assignment through `butler_delegate()`. use std::sync::Arc; use serde::{Deserialize, Serialize}; diff --git a/desktop/src-tauri/src/intelligence_hooks.rs b/desktop/src-tauri/src/intelligence_hooks.rs index 856e0b0..de244d6 100644 --- a/desktop/src-tauri/src/intelligence_hooks.rs +++ b/desktop/src-tauri/src/intelligence_hooks.rs @@ -78,6 +78,37 @@ pub async fn post_conversation_hook( } } + // Step 1.6: Detect pain signals from user message + if !_user_message.is_empty() { + let messages = vec![zclaw_types::Message::user(_user_message)]; + if let Some(analysis) = crate::intelligence::pain_aggregator::analyze_for_pain_signals(&messages) { + let severity_str = match analysis.severity { + crate::intelligence::pain_aggregator::PainSeverity::High => "high", + crate::intelligence::pain_aggregator::PainSeverity::Medium => "medium", + crate::intelligence::pain_aggregator::PainSeverity::Low => "low", + }; + match crate::intelligence::pain_aggregator::butler_record_pain_point( + agent_id.to_string(), + "default_user".to_string(), + analysis.summary, + analysis.category, + severity_str.to_string(), + _user_message.to_string(), + analysis.evidence, + ).await { + Ok(pain) => { + debug!( + "[intelligence_hooks] Pain point recorded: {} (confidence: {:.2}, count: {})", + pain.summary, pain.confidence, pain.occurrence_count + ); + } + Err(e) => { + warn!("[intelligence_hooks] Failed to record pain point: {}", e); + } + } + } + } + // Step 2: Record conversation for reflection let mut engine = reflection_state.lock().await; diff --git a/desktop/src/components/ButlerPanel/InsightsSection.tsx b/desktop/src/components/ButlerPanel/InsightsSection.tsx index 4601a31..9e07adc 100644 --- a/desktop/src/components/ButlerPanel/InsightsSection.tsx +++ b/desktop/src/components/ButlerPanel/InsightsSection.tsx @@ -1,9 +1,11 @@ import { useState } from 'react'; -import { ChevronDown, ChevronUp, AlertTriangle, TrendingUp, Info } from 'lucide-react'; +import { ChevronDown, ChevronUp, AlertTriangle, TrendingUp, Info, Lightbulb, Loader2 } from 'lucide-react'; import type { ButlerPainPoint } from '../../lib/viking-client'; +import { generateButlerSolution } from '../../lib/viking-client'; interface InsightsSectionProps { painPoints: ButlerPainPoint[]; + onGenerate?: () => void; } function SeverityBadge({ severity }: { severity: ButlerPainPoint['severity'] }) { @@ -28,8 +30,21 @@ function StatusBadge({ status }: { status: ButlerPainPoint['status'] }) { return {c.label}; } -export function InsightsSection({ painPoints }: InsightsSectionProps) { +export function InsightsSection({ painPoints, onGenerate }: InsightsSectionProps) { const [expandedId, setExpandedId] = useState(null); + const [generating, setGenerating] = useState(null); + + const handleGenerateSolution = async (painId: string) => { + setGenerating(painId); + try { + await generateButlerSolution(painId); + onGenerate?.(); + } catch { + onGenerate?.(); + } finally { + setGenerating(null); + } + }; if (painPoints.length === 0) { return ( @@ -112,6 +127,22 @@ export function InsightsSection({ painPoints }: InsightsSectionProps) { | 最近: {new Date(pp.last_seen).toLocaleDateString()} + {(pp.status === 'confirmed' || pp.status === 'detected') && pp.confidence >= 0.7 && ( +
+ +
+ )} )} diff --git a/desktop/src/components/ButlerPanel/MemorySection.tsx b/desktop/src/components/ButlerPanel/MemorySection.tsx index ce7dea7..2ef9545 100644 --- a/desktop/src/components/ButlerPanel/MemorySection.tsx +++ b/desktop/src/components/ButlerPanel/MemorySection.tsx @@ -20,7 +20,7 @@ export function MemorySection({ agentId }: MemorySectionProps) { if (!agentId) return; setLoading(true); - listVikingResources(`viking://agents/${agentId}/memories/`) + listVikingResources(`viking://agent/${agentId}/memories/`) .then((entries) => { setMemories(entries as MemoryEntry[]); }) diff --git a/desktop/src/components/ButlerPanel/index.tsx b/desktop/src/components/ButlerPanel/index.tsx index ee2c500..0052b0d 100644 --- a/desktop/src/components/ButlerPanel/index.tsx +++ b/desktop/src/components/ButlerPanel/index.tsx @@ -37,7 +37,7 @@ export function ButlerPanel({ agentId }: ButlerPanelProps) {

我最近在关注

- + {/* Proposals section */} diff --git a/desktop/src/hooks/useButlerInsights.ts b/desktop/src/hooks/useButlerInsights.ts index 0ad9e99..8e1d0fd 100644 --- a/desktop/src/hooks/useButlerInsights.ts +++ b/desktop/src/hooks/useButlerInsights.ts @@ -45,10 +45,6 @@ export function useButlerInsights(agentId: string | undefined): ButlerInsightsSt setError(errors.join('; ')); } }) - .then(([pains, props]) => { - setPainPoints(pains); - setProposals(props); - }) .finally(() => setLoading(false)); }, [agentId]);