widgettdc-api / apps /backend /src /mcp /cognitive /UnifiedMemorySystem.ts
Kraft102's picture
Update backend source
34367da verified
// UnifiedMemorySystem – Phase 1 foundation
// Provides Working, Procedural, Semantic, and Episodic memory layers
// Integrates existing repositories (CMA, SRAG, PAL, Evolution, ProjectMemory)
import { getCognitiveMemory, initCognitiveMemory, CognitiveMemory } from '../memory/CognitiveMemory.js';
import { getDatabase, getSqlJsDatabase } from '../../database/index.js';
import { getDatabaseAdapter } from '../../platform/db/PrismaDatabaseAdapter.js';
import { PostgresStorageAdapter } from '../memory/StorageAdapter.js';
import { MemoryRepository } from '../../services/memory/memoryRepository.js';
import { SragRepository } from '../../services/srag/sragRepository.js';
import { PalRepository } from '../../services/pal/palRepository.js';
import { EvolutionRepository } from '../../services/evolution/evolutionRepository.js';
import { projectMemory } from '../../services/project/ProjectMemory.js';
import { McpContext } from '@widget-tdc/mcp-types';
import { QueryIntent } from '../autonomous/DecisionEngine.js';
import { hybridSearchEngine } from './HybridSearchEngine.js';
import { emotionAwareDecisionEngine } from './EmotionAwareDecisionEngine.js';
/** WorkingMemoryState – transient context for the current request */
export interface WorkingMemoryState {
recentEvents: any[];
recentFeatures: any[];
recentPatterns?: any[];
widgetStates: Record<string, any>; // Live data fra widgets
userMood: {
sentiment: 'positive' | 'neutral' | 'negative' | 'stressed';
arousal: number; // 0-1 (Hvor aktiv er brugeren?)
lastUpdated: number;
};
suggestedLayout?: {
mode: 'focus' | 'discovery' | 'alert';
activeWidgets: string[]; // ID på widgets der bør være fremme
theme?: string;
};
}
/** ProductionRuleEngine – simple procedural memory placeholder */
class ProductionRuleEngine {
constructor(private cognitive: CognitiveMemory) { }
// TODO: implement rule extraction from cognitive patterns
async findRules(_opts: any): Promise<any[]> { return []; }
}
export class UnifiedMemorySystem {
// Existing repositories
private cognitive: CognitiveMemory;
private memoryRepo: MemoryRepository;
private sragRepo: SragRepository;
private palRepo: PalRepository;
private evolutionRepo: EvolutionRepository;
// New memory layers
private workingMemory: Map<string, WorkingMemoryState> = new Map();
private proceduralMemory: ProductionRuleEngine;
constructor() {
// Initialize repositories
this.memoryRepo = new MemoryRepository();
this.sragRepo = new SragRepository();
this.palRepo = new PalRepository();
this.evolutionRepo = new EvolutionRepository();
// Initialize cognitive memory lazily or assume initialized
// We cannot call getDatabase() here because it might not be ready
// The cognitive memory should be passed in or retrieved lazily
this.cognitive = {} as any; // Placeholder, will be set in init() or getter
this.proceduralMemory = new ProductionRuleEngine(this.cognitive);
}
// New init method to be called after DB is ready
public init() {
const dbAdapter = getDatabaseAdapter();
if (dbAdapter.isAvailable()) {
initCognitiveMemory(new PostgresStorageAdapter(dbAdapter));
} else {
const db = getSqlJsDatabase();
// Note: getSqlJsDatabase returns the raw sql.js instance needed for .exec()
// If it returns null, CognitiveMemory handles it (memory-only mode)
initCognitiveMemory(db);
}
this.cognitive = getCognitiveMemory();
this.proceduralMemory = new ProductionRuleEngine(this.cognitive);
}
/** Retrieve or create working memory for a user/org context */
async getWorkingMemory(ctx: McpContext): Promise<WorkingMemoryState> {
const key = `${ctx.orgId}:${ctx.userId}`;
if (!this.workingMemory.has(key)) {
const events = projectMemory.getLifecycleEvents(20);
const features = projectMemory.getFeatures();
this.workingMemory.set(key, {
recentEvents: events,
recentFeatures: features,
widgetStates: {},
userMood: { sentiment: 'neutral', arousal: 0.5, lastUpdated: Date.now() }
});
}
return this.workingMemory.get(key)!;
}
/** Opdater widget state og kør adaptiv analyse */
async updateWidgetState(ctx: McpContext, widgetId: string, state: any): Promise<void> {
const wm = await this.getWorkingMemory(ctx);
wm.widgetStates[widgetId] = { ...state, lastUpdated: Date.now() };
// Trigger holographic analysis when state changes
const patterns = await this.findHolographicPatterns(ctx);
// Opdater adaptivt layout baseret på mønstre
this.updateAdaptiveLayout(wm, patterns);
}
/** Persist result (e.g., tool output) into working memory for future context */
async updateWorkingMemory(ctx: McpContext, result: any): Promise<void> {
const key = `${ctx.orgId}:${ctx.userId}`;
const state = this.workingMemory.get(key);
if (state) {
state.recentEvents = [...(state.recentEvents || []), result];
// Simuleret humør-analyse baseret på interaktion
// Hvis resultatet er en fejl -> stress op
if (result?.error) {
state.userMood.sentiment = 'stressed';
state.userMood.arousal = Math.min(1, state.userMood.arousal + 0.2);
} else {
// Reset langsomt mod neutral
state.userMood.arousal = Math.max(0.2, state.userMood.arousal - 0.05);
}
this.workingMemory.set(key, state);
}
}
/** Enrich an incoming MCPMessage with memory context */
async enrichMCPRequest(message: any, ctx: McpContext): Promise<any> {
const wm = await this.getWorkingMemory(ctx);
return {
...message,
memoryContext: {
recentEvents: wm.recentEvents,
recentFeatures: wm.recentFeatures,
activeWidgets: wm.widgetStates,
systemSuggestion: wm.suggestedLayout
}
};
}
/** Example holographic pattern correlation across subsystems */
async findHolographicPatterns(ctx: McpContext): Promise<any[]> {
const wm = await this.getWorkingMemory(ctx);
const widgetData = Object.values(wm.widgetStates);
const [pal, cma, srag] = await Promise.all([
Promise.resolve(this.palRepo.getRecentEvents(ctx.userId, ctx.orgId, 50)).catch(() => []),
Promise.resolve(this.memoryRepo.searchEntities({ orgId: ctx.orgId, userId: ctx.userId, keywords: [], limit: 50 })).catch(() => []),
Promise.resolve(this.sragRepo.searchDocuments(ctx.orgId, '')).catch(() => []),
]);
// Inkluder widget data i korrelationen
return this.correlateAcrossSystems([pal, cma, srag, widgetData]);
}
/** Opdater layout forslag baseret på mønstre og humør */
private updateAdaptiveLayout(wm: WorkingMemoryState, patterns: any[]) {
// 1. Tjek for kritiske mønstre (Sikkerhed)
const securityPattern = patterns.find(p =>
['threat', 'attack', 'breach', 'password', 'alert'].includes(p.keyword) && p.frequency > 2
);
if (securityPattern) {
wm.suggestedLayout = {
mode: 'alert',
activeWidgets: ['DarkWebMonitorWidget', 'NetworkSpyWidget', 'CybersecurityOverwatchWidget'],
theme: 'red-alert'
};
return;
}
// 2. Tjek brugerens humør (Emotion Aware)
if (wm.userMood.sentiment === 'stressed' || wm.userMood.arousal > 0.8) {
wm.suggestedLayout = {
mode: 'focus',
activeWidgets: ['StatusWidget', 'IntelligentNotesWidget'], // Kun det mest nødvendige
theme: 'calm-blue'
};
return;
}
// 3. Default: Discovery mode hvis mange data-kilder er aktive
if (patterns.length > 5) {
wm.suggestedLayout = {
mode: 'discovery',
activeWidgets: ['VisualizerWidget', 'SearchInterfaceWidget', 'KnowledgeGraphWidget'],
theme: 'default'
};
}
}
/** Cross-correlate patterns across subsystems */
private correlateAcrossSystems(systems: any[]): any[] {
const patterns: any[] = [];
// Simple correlation: find common keywords/topics across systems
const allKeywords = new Map<string, number>();
if (!Array.isArray(systems)) return [];
systems.forEach((system, idx) => {
if (Array.isArray(system)) {
system.forEach((item: any) => {
if (!item) return;
const text = JSON.stringify(item).toLowerCase();
const words = text.match(/\b\w{4,}\b/g) || [];
words.forEach(word => {
allKeywords.set(word, (allKeywords.get(word) || 0) + 1);
});
});
}
});
// Find keywords that appear in multiple systems (holographic pattern)
Array.from(allKeywords.entries())
.filter(([_, count]) => count >= 2)
.forEach(([keyword, count]) => {
patterns.push({
keyword,
frequency: count,
systems: systems.length,
type: 'holographic_pattern'
});
});
return patterns;
}
/** Whole-part system health analysis */
async analyzeSystemHealth(): Promise<SystemHealthReport> {
const wholeSystem = {
globalHealth: await this.calculateGlobalHealth(),
emergentPatterns: await this.detectEmergentBehaviors(),
systemRhythms: await this.detectTemporalCycles()
};
const parts = await Promise.all([
this.componentHealth('pal'),
this.componentHealth('cma'),
this.componentHealth('srag'),
this.componentHealth('evolution'),
this.componentHealth('autonomous-agent')
]);
return this.modelWholePartRelationships(wholeSystem, parts);
}
private async calculateGlobalHealth(): Promise<number> {
try {
const health = await this.cognitive.getSourceHealth('system');
return health?.healthScore || 0.8; // Default to 80% if no data
} catch {
return 0.8;
}
}
private async detectEmergentBehaviors(): Promise<any[]> {
// Placeholder: detect patterns that emerge from system interactions
return [];
}
private async detectTemporalCycles(): Promise<any[]> {
// Placeholder: detect recurring patterns over time
return [];
}
private async componentHealth(component: string): Promise<ComponentHealth> {
try {
if (!this.cognitive || !this.cognitive.getSourceHealth) {
return {
name: component,
healthScore: 0.8, // Default optimistic
latency: 0,
successRate: 0.9
};
}
const health = await this.cognitive.getSourceHealth(component);
return {
name: component,
healthScore: health?.healthScore || 0.8,
latency: health?.latency?.p50 || 0,
successRate: health?.successRate || 0.9
};
} catch {
return {
name: component,
healthScore: 0.8,
latency: 0,
successRate: 0.9
};
}
}
private modelWholePartRelationships(whole: any, parts: ComponentHealth[]): SystemHealthReport {
const avgPartHealth = parts.reduce((sum, p) => sum + p.healthScore, 0) / parts.length;
const wholeHealth = whole.globalHealth;
return {
globalHealth: wholeHealth,
componentHealth: parts,
emergentPatterns: whole.emergentPatterns,
systemRhythms: whole.systemRhythms,
wholePartRatio: wholeHealth / Math.max(avgPartHealth, 0.1), // How whole relates to parts
healthVariance: this.calculateVariance(parts.map(p => p.healthScore))
};
}
private calculateVariance(values: number[]): number {
if (values.length === 0) return 0;
const mean = values.reduce((a, b) => a + b, 0) / values.length;
const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
return variance;
}
}
interface ComponentHealth {
name: string;
healthScore: number;
latency: number;
successRate: number;
}
interface SystemHealthReport {
globalHealth: number;
componentHealth: ComponentHealth[];
emergentPatterns: any[];
systemRhythms: any[];
wholePartRatio: number;
healthVariance: number;
}
export const unifiedMemorySystem = new UnifiedMemorySystem();