fix(identity): 3 项根因级修复 — Agent ID 映射 + user_profile 读取 + 用户画像 fallback
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

Issue 2: IdentityFile 枚举补全 UserProfile 变体
- get_file()/propose_change()/approve_proposal() 补全 match arm
- identity_get_file/identity_propose_change Tauri 命令支持 user_profile

Issue 1: Agent ID 映射机制
- 新增 resolveKernelAgentId() 工具函数 (带缓存)
- ButlerPanel 使用 kernel UUID 替代 SaaS relay "1" 查询 VikingStorage

Issue 3: 用户画像 fallback 注入
- build_system_prompt 改为 async,identity user_profile 为默认值时
  从 VikingStorage preferences 路径查询最近 5 条记忆作为 fallback
- intelligence_hooks 调用处同步加 .await

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-04-16 17:07:38 +08:00
parent 3c01754c40
commit 1e65b56a0f
4 changed files with 98 additions and 9 deletions

View File

@@ -3,6 +3,7 @@ import { useButlerInsights } from '../../hooks/useButlerInsights';
import { useChatStore } from '../../store/chatStore';
import { useIndustryStore } from '../../store/industryStore';
import { extractAndStoreMemories } from '../../lib/viking-client';
import { resolveKernelAgentId } from '../../lib/kernel-agent';
import { InsightsSection } from './InsightsSection';
import { ProposalsSection } from './ProposalsSection';
import { MemorySection } from './MemorySection';
@@ -17,6 +18,18 @@ export function ButlerPanel({ agentId }: ButlerPanelProps) {
const { accountIndustries, configs, lastSynced, isLoading: industryLoading, fetchIndustries } = useIndustryStore();
const [analyzing, setAnalyzing] = useState(false);
const [memoryRefreshKey, setMemoryRefreshKey] = useState(0);
const [resolvedAgentId, setResolvedAgentId] = useState<string | null>(null);
// Resolve SaaS relay agentId ("1") to kernel UUID for VikingStorage queries
useEffect(() => {
if (!agentId) {
setResolvedAgentId(null);
return;
}
resolveKernelAgentId(agentId)
.then(setResolvedAgentId)
.catch(() => setResolvedAgentId(agentId));
}, [agentId]);
// Auto-fetch industry configs once per session
useEffect(() => {
@@ -29,7 +42,7 @@ export function ButlerPanel({ agentId }: ButlerPanelProps) {
const canAnalyze = messageCount >= 2;
const handleAnalyze = useCallback(async () => {
if (!canAnalyze || analyzing || !agentId) return;
if (!canAnalyze || analyzing || !resolvedAgentId) return;
setAnalyzing(true);
try {
// 1. Refresh pain points & proposals
@@ -42,7 +55,7 @@ export function ButlerPanel({ agentId }: ButlerPanelProps) {
role: m.role as 'user' | 'assistant',
content: typeof m.content === 'string' ? m.content : '',
}));
await extractAndStoreMemories(extractionMessages, agentId);
await extractAndStoreMemories(extractionMessages, resolvedAgentId);
// Trigger MemorySection to reload
setMemoryRefreshKey((k) => k + 1);
}
@@ -51,7 +64,7 @@ export function ButlerPanel({ agentId }: ButlerPanelProps) {
} finally {
setAnalyzing(false);
}
}, [canAnalyze, analyzing, agentId, refresh]);
}, [canAnalyze, analyzing, resolvedAgentId, refresh]);
if (!agentId) {
return (
@@ -124,7 +137,7 @@ export function ButlerPanel({ agentId }: ButlerPanelProps) {
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-2">
</h3>
<MemorySection agentId={agentId} refreshKey={memoryRefreshKey} />
<MemorySection agentId={resolvedAgentId || agentId} refreshKey={memoryRefreshKey} />
</div>
{/* Industry section */}

View File

@@ -270,3 +270,42 @@ export function installAgentMethods(ClientClass: { prototype: KernelClient }): v
return { clone };
};
}
// === Agent ID Resolution ===
/**
* Cached kernel default agent UUID.
* The conversationStore's DEFAULT_AGENT has id="1", but VikingStorage
* stores data under kernel UUIDs. This cache bridges the gap.
*/
let _cachedDefaultKernelAgentId: string | null = null;
/**
* Resolve an agent ID to the kernel's actual agent UUID.
* - If already a UUID (8-4-4 hex pattern), return as-is.
* - If "1" or undefined, query agent_list and cache the first kernel agent's UUID.
* - Falls back to the original ID if kernel has no agents.
*/
export async function resolveKernelAgentId(agentId: string | undefined): Promise<string> {
if (agentId && /^[0-9a-f]{8}-[0-9a-f]{4}-/.test(agentId)) {
return agentId;
}
if (_cachedDefaultKernelAgentId) {
return _cachedDefaultKernelAgentId;
}
try {
const agents = await invoke<{ id: string }[]>('agent_list');
if (agents.length > 0) {
_cachedDefaultKernelAgentId = agents[0].id;
return _cachedDefaultKernelAgentId;
}
} catch {
// Kernel may not be available
}
return agentId || '1';
}
/** Invalidate cache when kernel reconnects (new instance may have different UUIDs) */
export function invalidateKernelAgentIdCache(): void {
_cachedDefaultKernelAgentId = null;
}