setActiveTab('butler')}
+ icon={}
+ label="管家"
+ />
@@ -377,7 +385,9 @@ export function RightPanel() {
) : activeTab === 'evolution' ? (
- ) : activeTab === 'agent' ? (
+ ) : activeTab === 'butler' ? (
+
+ ) : activeTab === 'agent'? (
void;
+}
+
+export function useButlerInsights(agentId: string | undefined): ButlerInsightsState {
+ const [painPoints, setPainPoints] = useState([]);
+ const [proposals, setProposals] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const refresh = useCallback(() => {
+ if (!agentId) return;
+
+ setLoading(true);
+ setError(null);
+
+ Promise.all([
+ getButlerInsights(agentId).catch((err: unknown) => {
+ const msg = err instanceof Error ? err.message : String(err);
+ setError(msg);
+ return [] as ButlerPainPoint[];
+ }),
+ getButlerProposals(agentId).catch((err: unknown) => {
+ const msg = err instanceof Error ? err.message : String(err);
+ setError(msg);
+ return [] as ButlerProposal[];
+ }),
+ ])
+ .then(([pains, props]) => {
+ setPainPoints(pains);
+ setProposals(props);
+ })
+ .finally(() => setLoading(false));
+ }, [agentId]);
+
+ useEffect(() => {
+ refresh();
+ }, [refresh]);
+
+ return { painPoints, proposals, loading, error, refresh };
+}
diff --git a/desktop/src/lib/viking-client.ts b/desktop/src/lib/viking-client.ts
index 858fe04..5476ffd 100644
--- a/desktop/src/lib/viking-client.ts
+++ b/desktop/src/lib/viking-client.ts
@@ -201,9 +201,116 @@ export async function extractAndStoreMemories(
});
}
+// === Butler Insights Functions ===
+
+export interface ButlerPainPoint {
+ id: string;
+ agent_id: string;
+ user_id: string;
+ summary: string;
+ category: string;
+ severity: 'low' | 'medium' | 'high';
+ evidence: Array<{ when: string; user_said: string; why_flagged: string }>;
+ occurrence_count: number;
+ first_seen: string;
+ last_seen: string;
+ confidence: number;
+ status: 'detected' | 'confirmed' | 'solving' | 'solved' | 'dismissed';
+}
+
+export interface ButlerProposalStep {
+ index: number;
+ action: string;
+ detail: string;
+ skill_hint: string | null;
+}
+
+export interface ButlerProposal {
+ id: string;
+ pain_point_id: string;
+ title: string;
+ description: string;
+ steps: ButlerProposalStep[];
+ status: 'pending' | 'accepted' | 'rejected' | 'completed';
+ evidence_chain: Array<{ when: string; user_said: string; why_flagged: string }>;
+ confidence_at_creation: number;
+ created_at: string;
+ updated_at: string;
+}
+
+export interface ButlerDelegationTask {
+ id: string;
+ description: string;
+ category: string;
+ priority: number;
+ status: 'pending' | 'assigned' | 'in_progress' | 'completed' | 'failed';
+ assigned_expert: { id: string; name: string; role: string } | null;
+}
+
+export interface ButlerDelegationResult {
+ request: string;
+ tasks: ButlerDelegationTask[];
+ success: boolean;
+ summary: string;
+}
+
/**
- * Get butler insights data - pain points and proposals for the but agentId }
+ * Get butler pain points for an agent
+ */
+export async function getButlerInsights(agentId: string): Promise {
return invoke('butler_list_pain_points', { agentId });
}
+/**
+ * Get butler proposals for an agent
+ */
+export async function getButlerProposals(agentId: string): Promise {
+ return invoke('butler_list_proposals', { agentId });
+}
+/**
+ * Record a new pain point from conversation analysis
+ */
+export async function recordButlerPainPoint(
+ agentId: string,
+ userId: string,
+ summary: string,
+ category: string,
+ severity: string,
+ userSaid: string,
+ whyFlagged: string
+): Promise {
+ return invoke('butler_record_pain_point', {
+ agentId,
+ userId,
+ summary,
+ category,
+ severity,
+ userSaid,
+ whyFlagged,
+ });
+}
+
+/**
+ * Generate a solution for a high-confidence pain point
+ */
+export async function generateButlerSolution(painId: string): Promise {
+ return invoke('butler_generate_solution', { painId });
+}
+
+/**
+ * Update the status of a proposal
+ */
+export async function updateButlerProposalStatus(
+ proposalId: string,
+ status: string
+): Promise {
+ return invoke('butler_update_proposal_status', { proposalId, status });
+}
+
+/**
+ * Butler delegates a user request to expert agents
+ */
+export async function butlerDelegateTask(request: string): Promise {
+ return invoke('butler_delegate_task', { request });
+}