UNPKG

codecrucible-synth

Version:

Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability

204 lines 7.49 kB
/** * Task Memory Database - Persistent task state and context management * Enables autonomous agents to resume work after interruptions */ import { promises as fs } from 'fs'; import { join } from 'path'; import { logger } from './logger.js'; export class TaskMemoryDB { dbPath; tasks = new Map(); initialized = false; constructor(dbPath = '.codecrucible-tasks.json') { this.dbPath = join(process.cwd(), dbPath); } async initialize() { try { const data = await fs.readFile(this.dbPath, 'utf-8'); const taskArray = JSON.parse(data); for (const task of taskArray) { this.tasks.set(task.task_id, task); } logger.info(`TaskMemoryDB initialized with ${this.tasks.size} tasks`); } catch (error) { // File doesn't exist yet, start fresh logger.info('TaskMemoryDB initialized (new database)'); } this.initialized = true; } async createTask(params) { const task_id = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const now = new Date().toISOString(); const task = { task_id, title: params.title, status: 'pending', priority: params.priority, created_at: now, updated_at: now, estimated_duration: params.estimated_duration, current_phase: 1, total_phases: params.total_phases, completed_steps: [], failed_attempts: [], agent_assignments: [], context_data: params.context_data || {}, results: {}, checkpoints: [], }; this.tasks.set(task_id, task); await this.persist(); logger.info(`Created task: ${task_id} - ${params.title}`); return task; } async updateTask(task_id, updates) { const task = this.tasks.get(task_id); if (!task) { logger.error(`Task not found: ${task_id}`); return null; } const updated = { ...task, ...updates, updated_at: new Date().toISOString(), }; this.tasks.set(task_id, updated); await this.persist(); logger.info(`Updated task: ${task_id} - Status: ${updated.status}`); return updated; } async addCheckpoint(task_id, checkpoint) { const task = this.tasks.get(task_id); if (!task) return false; const newCheckpoint = { ...checkpoint, id: `checkpoint_${Date.now()}`, timestamp: new Date().toISOString(), }; task.checkpoints.push(newCheckpoint); task.updated_at = new Date().toISOString(); await this.persist(); logger.info(`Added checkpoint for task ${task_id}: ${checkpoint.description}`); return true; } async recordFailedAttempt(task_id, step, error, agent_id) { const task = this.tasks.get(task_id); if (!task) return false; const existingAttempt = task.failed_attempts.find(a => a.step === step && a.agent_id === agent_id); if (existingAttempt) { existingAttempt.retry_count++; existingAttempt.timestamp = new Date().toISOString(); existingAttempt.error = error; } else { task.failed_attempts.push({ step, error, timestamp: new Date().toISOString(), agent_id, retry_count: 1, }); } task.updated_at = new Date().toISOString(); await this.persist(); logger.warn(`Recorded failed attempt for task ${task_id}, step: ${step}`); return true; } async assignAgent(task_id, agent_id, role, steps) { const task = this.tasks.get(task_id); if (!task) return false; const existingAssignment = task.agent_assignments.find(a => a.agent_id === agent_id); if (existingAssignment) { existingAssignment.assigned_steps = steps; existingAssignment.last_activity = new Date().toISOString(); } else { task.agent_assignments.push({ agent_id, role, status: 'assigned', assigned_steps: steps, last_activity: new Date().toISOString(), }); } task.updated_at = new Date().toISOString(); await this.persist(); logger.info(`Assigned agent ${agent_id} to task ${task_id} with role: ${role}`); return true; } async getTask(task_id) { return this.tasks.get(task_id) || null; } async getAllTasks() { return Array.from(this.tasks.values()); } async getActiveTasks() { return Array.from(this.tasks.values()).filter(task => task.status === 'in_progress' || task.status === 'pending'); } async markStepCompleted(task_id, step, result) { const task = this.tasks.get(task_id); if (!task) return false; if (!task.completed_steps.includes(step)) { task.completed_steps.push(step); } if (result) { task.results[step] = result; } task.updated_at = new Date().toISOString(); await this.persist(); logger.info(`Completed step for task ${task_id}: ${step}`); return true; } async getTaskProgress(task_id) { const task = this.tasks.get(task_id); if (!task) return null; const total_steps = task.completed_steps.length + task.failed_attempts.length; const progress_percentage = total_steps > 0 ? (task.completed_steps.length / total_steps) * 100 : 0; const current_phase_progress = (task.current_phase / task.total_phases) * 100; // Get next steps from agent assignments const next_steps = []; for (const assignment of task.agent_assignments) { if (assignment.status === 'assigned' || assignment.status === 'working') { next_steps.push(...assignment.assigned_steps.filter(s => !task.completed_steps.includes(s))); } } return { task, progress_percentage, current_phase_progress, estimated_remaining: 'Calculating...', // TODO: Implement time estimation next_steps: next_steps.slice(0, 5), // Show next 5 steps }; } async persist() { if (!this.initialized) return; try { const taskArray = Array.from(this.tasks.values()); await fs.writeFile(this.dbPath, JSON.stringify(taskArray, null, 2)); } catch (error) { logger.error('Failed to persist TaskMemoryDB:', error); } } async cleanup() { // Remove completed tasks older than 7 days const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); for (const [task_id, task] of this.tasks) { if (task.status === 'completed' && new Date(task.updated_at) < oneWeekAgo) { this.tasks.delete(task_id); } } await this.persist(); logger.info('TaskMemoryDB cleanup completed'); } } // Global task memory instance export const taskMemoryDB = new TaskMemoryDB(); //# sourceMappingURL=task-memory-db.js.map