Spaces:
Paused
Paused
| /** | |
| * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| * β 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<string, Goal> = new Map(); | |
| private plans: Map<string, Plan> = new Map(); | |
| private decisions: Map<string, Decision> = 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<Goal> { | |
| 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<Goal | null> { | |
| 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<Goal | null> { | |
| 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<Goal | null> { | |
| 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<Goal | null> { | |
| 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<PlanStep, 'id' | 'status' | 'completedAt'>[]; | |
| estimatedEffort: string; | |
| risks?: Omit<PlanRisk, 'id'>[]; | |
| }): Promise<Plan> { | |
| 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<Plan | null> { | |
| 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<Plan | null> { | |
| 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<DecisionOption, 'id' | 'score'>[]; | |
| }): Promise<Decision> { | |
| 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<DecisionOption, 'id' | 'score'>): 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<Decision | null> { | |
| 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<void> { | |
| 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<void> { | |
| 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<void> { | |
| 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<void> { | |
| 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(); | |