feat(ui): '我眼中的你' 双源渲染 — 静态Clone + 动态UserProfile
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

- RightPanel 增加 userProfile state + fetch 逻辑
- 对话结束后通过 CustomEvent 触发画像刷新
- UserProfile 字段: 行业/角色/沟通偏好/近期话题
桥接 identity 系统 → 前端面板。
This commit is contained in:
iven
2026-04-11 12:51:28 +08:00
parent d50d1ab882
commit b3f7328778
2 changed files with 51 additions and 0 deletions

View File

@@ -8,6 +8,8 @@ import { useConfigStore } from '../store/configStore';
import { toChatAgent, useChatStore, type CodeBlock } from '../store/chatStore'; import { toChatAgent, useChatStore, type CodeBlock } from '../store/chatStore';
import { useConversationStore } from '../store/chat/conversationStore'; import { useConversationStore } from '../store/chat/conversationStore';
import { intelligenceClient, type IdentitySnapshot } from '../lib/intelligence-client'; import { intelligenceClient, type IdentitySnapshot } from '../lib/intelligence-client';
import { invoke } from '@tauri-apps/api/core';
import type { AgentInfo } from '../lib/kernel-types';
import { import {
Wifi, WifiOff, Bot, BarChart3, Plug, RefreshCw, Wifi, WifiOff, Bot, BarChart3, Plug, RefreshCw,
MessageSquare, Cpu, FileText, User, Activity, Brain, MessageSquare, Cpu, FileText, User, Activity, Brain,
@@ -143,6 +145,9 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) {
const [restoringSnapshotId, setRestoringSnapshotId] = useState<string | null>(null); const [restoringSnapshotId, setRestoringSnapshotId] = useState<string | null>(null);
const [confirmRestoreId, setConfirmRestoreId] = useState<string | null>(null); const [confirmRestoreId, setConfirmRestoreId] = useState<string | null>(null);
// UserProfile from memory store (dynamic, learned from conversations)
const [userProfile, setUserProfile] = useState<Record<string, unknown> | null>(null);
const connected = connectionState === 'connected'; const connected = connectionState === 'connected';
const selectedClone = useMemo( const selectedClone = useMemo(
() => clones.find((clone) => clone.id === currentAgent?.id), () => clones.find((clone) => clone.id === currentAgent?.id),
@@ -166,6 +171,28 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) {
} }
}, [connected]); }, [connected]);
// Fetch UserProfile from agent data (includes memory-learned profile)
useEffect(() => {
if (!currentAgent?.id) return;
invoke<AgentInfo | null>('agent_get', { agentId: currentAgent.id })
.then(data => setUserProfile(data?.userProfile ?? null))
.catch(() => setUserProfile(null));
}, [currentAgent?.id]);
// Listen for profile updates after conversations
useEffect(() => {
const handler = (e: Event) => {
const detail = (e as CustomEvent).detail;
if (detail?.agentId === currentAgent?.id && currentAgent?.id) {
invoke<AgentInfo | null>('agent_get', { agentId: currentAgent.id })
.then(data => setUserProfile(data?.userProfile ?? null))
.catch(() => {});
}
};
window.addEventListener('zclaw:agent-profile-updated', handler);
return () => window.removeEventListener('zclaw:agent-profile-updated', handler);
}, [currentAgent?.id]);
const handleReconnect = () => { const handleReconnect = () => {
connect().catch(silentErrorHandler('RightPanel')); connect().catch(silentErrorHandler('RightPanel'));
}; };
@@ -552,6 +579,24 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) {
<AgentRow label="已解析" value={selectedClone?.workspaceResolvedPath || workspaceInfo?.resolvedPath || '-'} /> <AgentRow label="已解析" value={selectedClone?.workspaceResolvedPath || workspaceInfo?.resolvedPath || '-'} />
<AgentRow label="文件限制" value={selectedClone?.restrictFiles ? '已开启' : '已关闭'} /> <AgentRow label="文件限制" value={selectedClone?.restrictFiles ? '已开启' : '已关闭'} />
<AgentRow label="隐私计划" value={selectedClone?.privacyOptIn ? '已加入' : '未加入'} /> <AgentRow label="隐私计划" value={selectedClone?.privacyOptIn ? '已加入' : '未加入'} />
{/* Dynamic: UserProfile data (from conversation learning) */}
{userProfile && (
<div className="mt-3 pt-3 border-t border-gray-100 dark:border-gray-800">
<div className="text-xs text-gray-400 mb-2"></div>
{userProfile.industry ? (
<AgentRow label="行业" value={String(userProfile.industry)} />
) : null}
{userProfile.role ? (
<AgentRow label="角色" value={String(userProfile.role)} />
) : null}
{userProfile.communicationStyle ? (
<AgentRow label="沟通偏好" value={String(userProfile.communicationStyle)} />
) : null}
{Array.isArray(userProfile.recentTopics) && (userProfile.recentTopics as string[]).length > 0 ? (
<AgentRow label="近期话题" value={(userProfile.recentTopics as string[]).slice(0, 5).join(', ')} />
) : null}
</div>
)}
</div> </div>
)} )}
</motion.div> </motion.div>

View File

@@ -487,6 +487,12 @@ export const useStreamStore = create<StreamState>()(
getMemoryExtractor().extractFromConversation(filtered, agentId, convId ?? undefined).catch(err => { getMemoryExtractor().extractFromConversation(filtered, agentId, convId ?? undefined).catch(err => {
log.warn('Memory extraction failed:', err); log.warn('Memory extraction failed:', err);
}); });
// Notify RightPanel to refresh UserProfile after memory extraction
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('zclaw:agent-profile-updated', {
detail: { agentId }
}));
}
intelligenceClient.reflection.recordConversation().catch(err => { intelligenceClient.reflection.recordConversation().catch(err => {
log.warn('Recording conversation failed:', err); log.warn('Recording conversation failed:', err);
}); });