/** * ╔═══════════════════════════════════════════════════════════════════════════╗ * ║ PREFRONTAL CORTEX SERVICE ║ * ║═══════════════════════════════════════════════════════════════════════════║ * ║ The system's executive function - strategic planning, goal management, ║ * ║ decision making, and coordinating other cognitive modules ║ * ║ • Goal Management: Define and track objectives ║ * ║ • Strategic Planning: Break goals into actionable plans ║ * ║ • Decision Making: Evaluate options with trade-off analysis ║ * ║ • Executive Control: Coordinate actions across modules ║ * ╚═══════════════════════════════════════════════════════════════════════════╝ */ import { neo4jAdapter } from '../adapters/Neo4jAdapter.js'; import { v4 as uuidv4 } from 'uuid'; // ═══════════════════════════════════════════════════════════════════════════ // Types // ═══════════════════════════════════════════════════════════════════════════ export type GoalStatus = 'DRAFT' | 'ACTIVE' | 'IN_PROGRESS' | 'BLOCKED' | 'COMPLETED' | 'ABANDONED'; export type GoalPriority = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'; export type GoalTimeframe = 'IMMEDIATE' | 'SHORT_TERM' | 'MEDIUM_TERM' | 'LONG_TERM'; export interface Goal { id: string; title: string; description: string; status: GoalStatus; priority: GoalPriority; timeframe: GoalTimeframe; parentGoalId?: string; // For sub-goals successCriteria: string[]; progress: number; // 0-100 blockers: string[]; dependencies: string[]; // Other goal IDs tags: string[]; createdAt: string; updatedAt: string; targetDate?: string; completedAt?: string; } export interface Plan { id: string; goalId: string; title: string; steps: PlanStep[]; status: 'DRAFT' | 'APPROVED' | 'EXECUTING' | 'COMPLETED' | 'FAILED'; estimatedEffort: string; // e.g., "2 hours", "3 days" risks: PlanRisk[]; createdAt: string; approvedAt?: string; approvedBy?: string; } export interface PlanStep { id: string; order: number; description: string; actionType?: string; status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'SKIPPED' | 'FAILED'; dependencies: string[]; // Other step IDs output?: string; completedAt?: string; } export interface PlanRisk { id: string; description: string; probability: 'LOW' | 'MEDIUM' | 'HIGH'; impact: 'LOW' | 'MEDIUM' | 'HIGH'; mitigation: string; } export interface Decision { id: string; question: string; context: string; options: DecisionOption[]; selectedOption?: string; rationale?: string; status: 'PENDING' | 'DECIDED' | 'IMPLEMENTED'; decidedAt?: string; decidedBy?: string; createdAt: string; } export interface DecisionOption { id: string; name: string; description: string; pros: string[]; cons: string[]; risks: string[]; score?: number; // Calculated score } // ═══════════════════════════════════════════════════════════════════════════ // Prefrontal Cortex Service // ═══════════════════════════════════════════════════════════════════════════ class PrefrontalCortexService { private static instance: PrefrontalCortexService; private goals: Map = new Map(); private plans: Map = new Map(); private decisions: Map = new Map(); // Focus tracking private currentFocus: string | null = null; private focusHistory: { goalId: string; startedAt: string; endedAt?: string }[] = []; private constructor() { this.loadFromNeo4j(); } public static getInstance(): PrefrontalCortexService { if (!PrefrontalCortexService.instance) { PrefrontalCortexService.instance = new PrefrontalCortexService(); } return PrefrontalCortexService.instance; } // ═══════════════════════════════════════════════════════════════════════ // Goal Management // ═══════════════════════════════════════════════════════════════════════ /** * Create a new goal */ public async createGoal(params: { title: string; description: string; priority?: GoalPriority; timeframe?: GoalTimeframe; parentGoalId?: string; successCriteria?: string[]; targetDate?: string; tags?: string[]; }): Promise { const goal: Goal = { id: `goal-${uuidv4()}`, title: params.title, description: params.description, status: 'DRAFT', priority: params.priority || 'MEDIUM', timeframe: params.timeframe || 'MEDIUM_TERM', parentGoalId: params.parentGoalId, successCriteria: params.successCriteria || [], progress: 0, blockers: [], dependencies: [], tags: params.tags || [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), targetDate: params.targetDate }; this.goals.set(goal.id, goal); await this.persistGoal(goal); console.error(`[PrefrontalCortex] 🎯 Created goal: ${goal.title}`); return goal; } /** * Activate a goal (move from DRAFT to ACTIVE) */ public async activateGoal(goalId: string): Promise { const goal = this.goals.get(goalId); if (!goal) return null; goal.status = 'ACTIVE'; goal.updatedAt = new Date().toISOString(); await this.persistGoal(goal); return goal; } /** * Update goal progress */ public async updateGoalProgress(goalId: string, progress: number, status?: GoalStatus): Promise { const goal = this.goals.get(goalId); if (!goal) return null; goal.progress = Math.min(100, Math.max(0, progress)); goal.updatedAt = new Date().toISOString(); if (status) { goal.status = status; } else if (progress >= 100) { goal.status = 'COMPLETED'; goal.completedAt = new Date().toISOString(); } else if (progress > 0 && goal.status === 'ACTIVE') { goal.status = 'IN_PROGRESS'; } await this.persistGoal(goal); return goal; } /** * Add blocker to goal */ public async addBlocker(goalId: string, blocker: string): Promise { const goal = this.goals.get(goalId); if (!goal) return null; goal.blockers.push(blocker); goal.status = 'BLOCKED'; goal.updatedAt = new Date().toISOString(); await this.persistGoal(goal); console.error(`[PrefrontalCortex] 🚧 Goal blocked: ${goal.title} - ${blocker}`); return goal; } /** * Remove blocker from goal */ public async removeBlocker(goalId: string, blockerIndex: number): Promise { const goal = this.goals.get(goalId); if (!goal) return null; goal.blockers.splice(blockerIndex, 1); if (goal.blockers.length === 0 && goal.status === 'BLOCKED') { goal.status = goal.progress > 0 ? 'IN_PROGRESS' : 'ACTIVE'; } goal.updatedAt = new Date().toISOString(); await this.persistGoal(goal); return goal; } /** * Get goals by status or priority */ public getGoals(filter?: { status?: GoalStatus; priority?: GoalPriority; timeframe?: GoalTimeframe; parentGoalId?: string; }): Goal[] { let results = Array.from(this.goals.values()); if (filter?.status) { results = results.filter(g => g.status === filter.status); } if (filter?.priority) { results = results.filter(g => g.priority === filter.priority); } if (filter?.timeframe) { results = results.filter(g => g.timeframe === filter.timeframe); } if (filter?.parentGoalId !== undefined) { results = results.filter(g => g.parentGoalId === filter.parentGoalId); } // Sort by priority const priorityOrder = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 }; return results.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]); } /** * Get sub-goals for a goal */ public getSubGoals(parentGoalId: string): Goal[] { return this.getGoals({ parentGoalId }); } // ═══════════════════════════════════════════════════════════════════════ // Strategic Planning // ═══════════════════════════════════════════════════════════════════════ /** * Create a plan for a goal */ public async createPlan(params: { goalId: string; title: string; steps: Omit[]; estimatedEffort: string; risks?: Omit[]; }): Promise { const plan: Plan = { id: `plan-${uuidv4()}`, goalId: params.goalId, title: params.title, steps: params.steps.map((s, i) => ({ ...s, id: `step-${uuidv4()}`, order: i + 1, status: 'PENDING' as const })), status: 'DRAFT', estimatedEffort: params.estimatedEffort, risks: (params.risks || []).map(r => ({ ...r, id: `risk-${uuidv4()}` })), createdAt: new Date().toISOString() }; this.plans.set(plan.id, plan); await this.persistPlan(plan); console.error(`[PrefrontalCortex] 📋 Created plan: ${plan.title} (${plan.steps.length} steps)`); return plan; } /** * Approve a plan */ public async approvePlan(planId: string, approvedBy: string): Promise { const plan = this.plans.get(planId); if (!plan) return null; plan.status = 'APPROVED'; plan.approvedAt = new Date().toISOString(); plan.approvedBy = approvedBy; await this.persistPlan(plan); return plan; } /** * Update plan step status */ public async updatePlanStep(planId: string, stepId: string, status: PlanStep['status'], output?: string): Promise { const plan = this.plans.get(planId); if (!plan) return null; const step = plan.steps.find(s => s.id === stepId); if (!step) return null; step.status = status; step.output = output; if (status === 'COMPLETED' || status === 'SKIPPED' || status === 'FAILED') { step.completedAt = new Date().toISOString(); } // Update plan status const allComplete = plan.steps.every(s => s.status === 'COMPLETED' || s.status === 'SKIPPED'); const anyFailed = plan.steps.some(s => s.status === 'FAILED'); const anyInProgress = plan.steps.some(s => s.status === 'IN_PROGRESS'); if (allComplete) { plan.status = 'COMPLETED'; } else if (anyFailed) { plan.status = 'FAILED'; } else if (anyInProgress) { plan.status = 'EXECUTING'; } // Update goal progress const goal = this.goals.get(plan.goalId); if (goal) { const completedSteps = plan.steps.filter(s => s.status === 'COMPLETED').length; goal.progress = Math.round((completedSteps / plan.steps.length) * 100); await this.persistGoal(goal); } await this.persistPlan(plan); return plan; } /** * Get next step to execute in a plan */ public getNextStep(planId: string): PlanStep | null { const plan = this.plans.get(planId); if (!plan || plan.status !== 'APPROVED' && plan.status !== 'EXECUTING') return null; // Find first pending step whose dependencies are met for (const step of plan.steps) { if (step.status !== 'PENDING') continue; const depsComplete = step.dependencies.every(depId => { const depStep = plan.steps.find(s => s.id === depId); return depStep && (depStep.status === 'COMPLETED' || depStep.status === 'SKIPPED'); }); if (depsComplete) { return step; } } return null; } // ═══════════════════════════════════════════════════════════════════════ // Decision Making // ═══════════════════════════════════════════════════════════════════════ /** * Create a decision to be made */ public async createDecision(params: { question: string; context: string; options: Omit[]; }): Promise { const decision: Decision = { id: `decision-${uuidv4()}`, question: params.question, context: params.context, options: params.options.map(o => ({ ...o, id: `option-${uuidv4()}`, score: this.scoreOption(o) })), status: 'PENDING', createdAt: new Date().toISOString() }; this.decisions.set(decision.id, decision); await this.persistDecision(decision); console.error(`[PrefrontalCortex] ❓ Decision pending: ${decision.question}`); return decision; } /** * Score a decision option based on pros/cons/risks */ private scoreOption(option: Omit): number { let score = 50; // Start neutral // Pros add points score += option.pros.length * 10; // Cons subtract points score -= option.cons.length * 8; // Risks subtract based on severity (assumed from description length as proxy) score -= option.risks.length * 5; return Math.max(0, Math.min(100, score)); } /** * Make a decision */ public async makeDecision(decisionId: string, optionId: string, rationale: string, decidedBy: string): Promise { const decision = this.decisions.get(decisionId); if (!decision) return null; decision.selectedOption = optionId; decision.rationale = rationale; decision.status = 'DECIDED'; decision.decidedAt = new Date().toISOString(); decision.decidedBy = decidedBy; await this.persistDecision(decision); const option = decision.options.find(o => o.id === optionId); console.error(`[PrefrontalCortex] ✅ Decision made: ${option?.name || optionId}`); return decision; } /** * Get pending decisions */ public getPendingDecisions(): Decision[] { return Array.from(this.decisions.values()).filter(d => d.status === 'PENDING'); } /** * Get recommendation for a decision */ public getRecommendation(decisionId: string): { option: DecisionOption; rationale: string } | null { const decision = this.decisions.get(decisionId); if (!decision || decision.options.length === 0) return null; // Sort by score const sorted = [...decision.options].sort((a, b) => (b.score || 0) - (a.score || 0)); const best = sorted[0]; const rationale = `Recommended "${best.name}" based on: ${best.pros.length} pros vs ${best.cons.length} cons. Key advantages: ${best.pros.slice(0, 2).join(', ')}`; return { option: best, rationale }; } // ═══════════════════════════════════════════════════════════════════════ // Executive Control / Focus Management // ═══════════════════════════════════════════════════════════════════════ /** * Set current focus to a goal */ public setFocus(goalId: string): void { if (this.currentFocus) { // End previous focus const lastFocus = this.focusHistory[this.focusHistory.length - 1]; if (lastFocus && !lastFocus.endedAt) { lastFocus.endedAt = new Date().toISOString(); } } this.currentFocus = goalId; this.focusHistory.push({ goalId, startedAt: new Date().toISOString() }); const goal = this.goals.get(goalId); console.error(`[PrefrontalCortex] 🎯 Focus set: ${goal?.title || goalId}`); } /** * Get current focus */ public getCurrentFocus(): Goal | null { if (!this.currentFocus) return null; return this.goals.get(this.currentFocus) || null; } /** * Get executive summary */ public getExecutiveSummary(): { currentFocus: Goal | null; activeGoals: number; blockedGoals: number; pendingDecisions: number; activePlans: number; overallProgress: number; } { const goals = Array.from(this.goals.values()); const plans = Array.from(this.plans.values()); const activeGoals = goals.filter(g => g.status === 'ACTIVE' || g.status === 'IN_PROGRESS'); const totalProgress = activeGoals.reduce((sum, g) => sum + g.progress, 0); return { currentFocus: this.getCurrentFocus(), activeGoals: activeGoals.length, blockedGoals: goals.filter(g => g.status === 'BLOCKED').length, pendingDecisions: this.getPendingDecisions().length, activePlans: plans.filter(p => p.status === 'APPROVED' || p.status === 'EXECUTING').length, overallProgress: activeGoals.length > 0 ? Math.round(totalProgress / activeGoals.length) : 0 }; } // ═══════════════════════════════════════════════════════════════════════ // Persistence // ═══════════════════════════════════════════════════════════════════════ private async persistGoal(goal: Goal): Promise { try { await neo4jAdapter.executeQuery(` MERGE (g:Goal {id: $id}) SET g.title = $title, g.description = $description, g.status = $status, g.priority = $priority, g.timeframe = $timeframe, g.progress = $progress, g.tags = $tags, g.createdAt = $createdAt, g.updatedAt = $updatedAt `, { id: goal.id, title: goal.title, description: goal.description, status: goal.status, priority: goal.priority, timeframe: goal.timeframe, progress: goal.progress, tags: goal.tags, createdAt: goal.createdAt, updatedAt: goal.updatedAt }); } catch (error) { console.warn('[PrefrontalCortex] Failed to persist goal:', error); } } private async persistPlan(plan: Plan): Promise { try { await neo4jAdapter.executeQuery(` MERGE (p:Plan {id: $id}) SET p.goalId = $goalId, p.title = $title, p.status = $status, p.estimatedEffort = $estimatedEffort, p.stepCount = $stepCount, p.createdAt = $createdAt `, { id: plan.id, goalId: plan.goalId, title: plan.title, status: plan.status, estimatedEffort: plan.estimatedEffort, stepCount: plan.steps.length, createdAt: plan.createdAt }); } catch (error) { console.warn('[PrefrontalCortex] Failed to persist plan:', error); } } private async persistDecision(decision: Decision): Promise { try { await neo4jAdapter.executeQuery(` MERGE (d:Decision {id: $id}) SET d.question = $question, d.status = $status, d.optionCount = $optionCount, d.selectedOption = $selectedOption, d.createdAt = $createdAt, d.decidedAt = $decidedAt `, { id: decision.id, question: decision.question, status: decision.status, optionCount: decision.options.length, selectedOption: decision.selectedOption || '', createdAt: decision.createdAt, decidedAt: decision.decidedAt || '' }); } catch (error) { console.warn('[PrefrontalCortex] Failed to persist decision:', error); } } private async loadFromNeo4j(): Promise { try { const goalResults = await neo4jAdapter.executeQuery(` MATCH (g:Goal) WHERE g.status IN ['ACTIVE', 'IN_PROGRESS', 'BLOCKED'] RETURN g ORDER BY g.priority LIMIT 100 `); for (const r of goalResults) { const g = r.g.properties; this.goals.set(g.id, { id: g.id, title: g.title, description: g.description, status: g.status, priority: g.priority, timeframe: g.timeframe, progress: g.progress, successCriteria: [], blockers: [], dependencies: [], tags: g.tags || [], createdAt: g.createdAt, updatedAt: g.updatedAt }); } console.error(`[PrefrontalCortex] 🔄 Loaded ${goalResults.length} active goals`); } catch (error) { console.warn('[PrefrontalCortex] Failed to load from Neo4j:', error); } } // ═══════════════════════════════════════════════════════════════════════ // Status // ═══════════════════════════════════════════════════════════════════════ public getStatus(): { totalGoals: number; activePlans: number; pendingDecisions: number; currentFocus: string | null; } { return { totalGoals: this.goals.size, activePlans: Array.from(this.plans.values()).filter(p => p.status === 'EXECUTING').length, pendingDecisions: this.getPendingDecisions().length, currentFocus: this.currentFocus }; } } export const prefrontalCortex = PrefrontalCortexService.getInstance();