/** * Intelligence Domain Store * * Valtio-based state management for intelligence operations. * Wraps intelligence-client with caching and reactive state. */ import { proxy } from 'valtio'; import { intelligenceClient } from '../../lib/intelligence-client'; import { getIntelligenceCache, memorySearchKey, identityKey, } from './cache'; import type { IntelligenceStore, IntelligenceState, MemoryEntry, MemoryType, MemorySource, MemorySearchOptions, MemoryStats, CacheStats, } from './types'; // === Initial State === const initialState: IntelligenceState = { // Memory memories: [], memoryStats: null, isMemoryLoading: false, // Heartbeat heartbeatConfig: null, heartbeatHistory: [], isHeartbeatRunning: false, // Compaction lastCompaction: null, compactionCheck: null, // Reflection reflectionState: null, lastReflection: null, // Identity currentIdentity: null, pendingProposals: [], // Cache cacheStats: { entries: 0, hits: 0, misses: 0, hitRate: 0, }, // General isLoading: false, error: null, }; // === Store Implementation === export const intelligenceStore = proxy({ ...initialState, // === Memory Actions === loadMemories: async (options: MemorySearchOptions): Promise => { const cache = getIntelligenceCache(); const key = memorySearchKey(options as Record); // Check cache first const cached = cache.get(key); if (cached) { intelligenceStore.memories = cached; intelligenceStore.cacheStats = cache.getStats(); return; } intelligenceStore.isMemoryLoading = true; intelligenceStore.error = null; try { const rawMemories = await intelligenceClient.memory.search({ agentId: options.agentId, type: options.type, tags: options.tags, query: options.query, limit: options.limit, minImportance: options.minImportance, }); // Convert to frontend format const memories: MemoryEntry[] = rawMemories.map(m => ({ id: m.id, agentId: m.agentId, content: m.content, type: m.type as MemoryType, importance: m.importance, source: m.source as MemorySource, tags: m.tags, createdAt: m.createdAt, lastAccessedAt: m.lastAccessedAt, accessCount: m.accessCount, conversationId: m.conversationId, })); cache.set(key, memories); intelligenceStore.memories = memories; intelligenceStore.cacheStats = cache.getStats(); } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to load memories'; } finally { intelligenceStore.isMemoryLoading = false; } }, storeMemory: async (entry): Promise => { const cache = getIntelligenceCache(); try { const id = await intelligenceClient.memory.store({ agent_id: entry.agentId, memory_type: entry.type, content: entry.content, importance: entry.importance, source: entry.source, tags: entry.tags, conversation_id: entry.conversationId, }); // Invalidate relevant cache entries cache.delete(memorySearchKey({ agentId: entry.agentId })); return id; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to store memory'; throw err; } }, deleteMemory: async (id: string): Promise => { const cache = getIntelligenceCache(); try { await intelligenceClient.memory.delete(id); // Clear all memory search caches cache.clear(); } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to delete memory'; throw err; } }, loadMemoryStats: async (): Promise => { try { const rawStats = await intelligenceClient.memory.stats(); const stats: MemoryStats = { totalEntries: rawStats.totalEntries, byType: rawStats.byType, byAgent: rawStats.byAgent, oldestEntry: rawStats.oldestEntry, newestEntry: rawStats.newestEntry, }; intelligenceStore.memoryStats = stats; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to load memory stats'; } }, // === Heartbeat Actions === initHeartbeat: async (agentId: string, config?: import('../../lib/intelligence-backend').HeartbeatConfig): Promise => { try { await intelligenceClient.heartbeat.init(agentId, config); if (config) { intelligenceStore.heartbeatConfig = config; } } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to init heartbeat'; } }, startHeartbeat: async (agentId: string): Promise => { try { await intelligenceClient.heartbeat.start(agentId); intelligenceStore.isHeartbeatRunning = true; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to start heartbeat'; } }, stopHeartbeat: async (agentId: string): Promise => { try { await intelligenceClient.heartbeat.stop(agentId); intelligenceStore.isHeartbeatRunning = false; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to stop heartbeat'; } }, tickHeartbeat: async (agentId: string): Promise => { try { const result = await intelligenceClient.heartbeat.tick(agentId); intelligenceStore.heartbeatHistory = [ result, ...intelligenceStore.heartbeatHistory.slice(0, 99), ]; return result; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Heartbeat tick failed'; throw err; } }, // === Compaction Actions === checkCompaction: async (messages: Array<{ id?: string; role: string; content: string; timestamp?: string }>): Promise => { try { const compactableMessages = messages.map(m => ({ id: m.id || `msg_${Date.now()}`, role: m.role, content: m.content, timestamp: m.timestamp, })); const check = await intelligenceClient.compactor.checkThreshold(compactableMessages); intelligenceStore.compactionCheck = check; return check; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Compaction check failed'; throw err; } }, compact: async ( messages: Array<{ id?: string; role: string; content: string; timestamp?: string }>, agentId: string, conversationId?: string ): Promise => { try { const compactableMessages = messages.map(m => ({ id: m.id || `msg_${Date.now()}`, role: m.role, content: m.content, timestamp: m.timestamp, })); const result = await intelligenceClient.compactor.compact( compactableMessages, agentId, conversationId ); intelligenceStore.lastCompaction = result; return result; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Compaction failed'; throw err; } }, // === Reflection Actions === recordConversation: async (): Promise => { try { await intelligenceClient.reflection.recordConversation(); } catch (err) { console.warn('[IntelligenceStore] Failed to record conversation:', err); } }, shouldReflect: async (): Promise => { try { return intelligenceClient.reflection.shouldReflect(); } catch { return false; } }, reflect: async (agentId: string): Promise => { try { // Get memories for reflection const memories = await intelligenceClient.memory.search({ agentId, limit: 50, minImportance: 3, }); const analysisMemories = memories.map(m => ({ id: m.id, memory_type: m.type, content: m.content, importance: m.importance, created_at: m.createdAt, access_count: m.accessCount, tags: m.tags, })); const result = await intelligenceClient.reflection.reflect(agentId, analysisMemories); intelligenceStore.lastReflection = result; // Invalidate caches getIntelligenceCache().clear(); return result; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Reflection failed'; throw err; } }, // === Identity Actions === loadIdentity: async (agentId: string): Promise => { const cache = getIntelligenceCache(); const key = identityKey(agentId); // Check cache const cached = cache.get(key); if (cached) { intelligenceStore.currentIdentity = cached; intelligenceStore.cacheStats = cache.getStats(); return; } try { const identity = await intelligenceClient.identity.get(agentId); cache.set(key, identity, 10 * 60 * 1000); // 10 minute TTL intelligenceStore.currentIdentity = identity; intelligenceStore.cacheStats = cache.getStats(); } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to load identity'; } }, buildPrompt: async (agentId: string, memoryContext?: string): Promise => { try { return intelligenceClient.identity.buildPrompt(agentId, memoryContext); } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to build prompt'; throw err; } }, proposeIdentityChange: async ( agentId: string, file: 'soul' | 'instructions', content: string, reason: string ): Promise => { try { const proposal = await intelligenceClient.identity.proposeChange( agentId, file, content, reason ); intelligenceStore.pendingProposals.push(proposal); return proposal; } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to propose change'; throw err; } }, approveProposal: async (proposalId: string): Promise => { try { const identity = await intelligenceClient.identity.approveProposal(proposalId); intelligenceStore.pendingProposals = intelligenceStore.pendingProposals.filter( p => p.id !== proposalId ); intelligenceStore.currentIdentity = identity; getIntelligenceCache().clear(); } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to approve proposal'; throw err; } }, rejectProposal: async (proposalId: string): Promise => { try { await intelligenceClient.identity.rejectProposal(proposalId); intelligenceStore.pendingProposals = intelligenceStore.pendingProposals.filter( p => p.id !== proposalId ); } catch (err) { intelligenceStore.error = err instanceof Error ? err.message : 'Failed to reject proposal'; throw err; } }, // === Cache Actions === clearCache: (): void => { getIntelligenceCache().clear(); intelligenceStore.cacheStats = getIntelligenceCache().getStats(); }, getCacheStats: (): CacheStats => { return getIntelligenceCache().getStats(); }, // === General Actions === clearError: (): void => { intelligenceStore.error = null; }, reset: (): void => { Object.assign(intelligenceStore, initialState); getIntelligenceCache().clear(); }, }); export type { IntelligenceStore };