fix: butler audit critical fixes — pain detection, proposal trigger, URI + data flow
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

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)
This commit is contained in:
iven
2026-04-07 10:23:54 +08:00
parent 58703492e1
commit a5b887051d
6 changed files with 69 additions and 12 deletions

View File

@@ -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 <span className={`px-1.5 py-0.5 rounded text-xs ${c.bg} ${c.text}`}>{c.label}</span>;
}
export function InsightsSection({ painPoints }: InsightsSectionProps) {
export function InsightsSection({ painPoints, onGenerate }: InsightsSectionProps) {
const [expandedId, setExpandedId] = useState<string | null>(null);
const [generating, setGenerating] = useState<string | null>(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) {
<span>|</span>
<span>: {new Date(pp.last_seen).toLocaleDateString()}</span>
</div>
{(pp.status === 'confirmed' || pp.status === 'detected') && pp.confidence >= 0.7 && (
<div className="pt-1">
<button
className="flex items-center gap-1 px-3 py-1.5 rounded-md text-xs font-medium bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50"
onClick={() => handleGenerateSolution(pp.id)}
disabled={generating === pp.id}
>
{generating === pp.id ? (
<Loader2 className="w-3.5 h-3.5 animate-spin" />
) : (
<Lightbulb className="w-3.5 h-3.5" />
)}
</button>
</div>
)}
</div>
)}
</div>

View File

@@ -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[]);
})

View File

@@ -37,7 +37,7 @@ export function ButlerPanel({ agentId }: ButlerPanelProps) {
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-2">
</h3>
<InsightsSection painPoints={painPoints} />
<InsightsSection painPoints={painPoints} onGenerate={refresh} />
</div>
{/* Proposals section */}

View File

@@ -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]);