/ src / lib / memory-presentation.ts
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  }