memory-presentation.ts
1 import type { MemoryEntry } from '@/types' 2 3 export type MemoryTier = 'working' | 'durable' | 'archive' 4 export type MemoryScopeBadge = 'global' | 'agent' | 'shared' | 'session' | 'project' 5 6 const WORKING_CATEGORIES = new Set(['execution', 'working', 'scratch', 'breadcrumb']) 7 const ARCHIVE_CATEGORIES = new Set(['session_archive']) 8 9 function hasProjectRoot(entry: Pick<MemoryEntry, 'metadata' | 'references' | 'filePaths'>): boolean { 10 const metadataRoot = typeof entry.metadata?.projectRoot === 'string' ? entry.metadata.projectRoot.trim() : '' 11 if (metadataRoot) return true 12 13 if (Array.isArray(entry.references)) { 14 for (const ref of entry.references) { 15 if (typeof ref.projectRoot === 'string' && ref.projectRoot.trim()) return true 16 if ((ref.type === 'project' || ref.type === 'folder' || ref.type === 'file') && typeof ref.path === 'string' && ref.path.trim()) { 17 return true 18 } 19 } 20 } 21 22 if (Array.isArray(entry.filePaths)) { 23 for (const ref of entry.filePaths) { 24 if (typeof ref.projectRoot === 'string' && ref.projectRoot.trim()) return true 25 if (typeof ref.path === 'string' && ref.path.trim()) return true 26 } 27 } 28 29 return false 30 } 31 32 export function getMemoryTierForCategory(category: unknown): MemoryTier { 33 const normalized = typeof category === 'string' ? category.trim().toLowerCase() : '' 34 if (ARCHIVE_CATEGORIES.has(normalized)) return 'archive' 35 if (WORKING_CATEGORIES.has(normalized)) return 'working' 36 return 'durable' 37 } 38 39 export function getMemoryTier(entry: Pick<MemoryEntry, 'category' | 'metadata'>): MemoryTier { 40 const metadataTier = typeof entry.metadata?.tier === 'string' ? entry.metadata.tier.trim().toLowerCase() : '' 41 if (metadataTier === 'working' || metadataTier === 'durable' || metadataTier === 'archive') { 42 return metadataTier 43 } 44 if (metadataTier === 'session_archive') return 'archive' 45 return getMemoryTierForCategory(entry.category) 46 } 47 48 export function deriveMemoryScope(entry: Pick<MemoryEntry, 'agentId' | 'sessionId' | 'sharedWith' | 'metadata' | 'references' | 'filePaths'>): MemoryScopeBadge { 49 if (entry.sessionId) return 'session' 50 if (hasProjectRoot(entry)) return 'project' 51 if (entry.agentId && Array.isArray(entry.sharedWith) && entry.sharedWith.length > 0) return 'shared' 52 if (entry.agentId) return 'agent' 53 return 'global' 54 } 55 56 export function getMemoryScopeLabel(scope: MemoryScopeBadge): string { 57 if (scope === 'agent') return 'private' 58 return scope 59 }