UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

361 lines (357 loc) 11.5 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { v4 as uuidv4 } from "uuid"; import { logger } from "../../core/monitoring/logger.js"; class ClaudeCodeTaskCoordinator { activeTasks = /* @__PURE__ */ new Map(); completedTasks = []; metrics; constructor() { this.metrics = { totalTasks: 0, completedTasks: 0, failedTasks: 0, averageExecutionTime: 0, totalCost: 0, successRate: 0, agentUtilization: {} }; } /** * Execute task with Claude Code agent */ async executeTask(agentName, agentConfig, prompt, options = {}) { const taskId = uuidv4(); const { maxRetries = 2, timeout = 3e5, priority = "medium" } = options; const task = { id: taskId, agentName, agentType: agentConfig.type, prompt, startTime: Date.now(), status: "pending", retryCount: 0, estimatedCost: this.estimateTaskCost(prompt, agentConfig) }; this.activeTasks.set(taskId, task); this.metrics.totalTasks++; logger.info("Starting Claude Code task execution", { taskId, agentName, agentType: agentConfig.type, promptLength: prompt.length, estimatedCost: task.estimatedCost, priority }); try { let lastError = null; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { task.retryCount = attempt; task.status = "running"; const result = await this.executeWithTimeout( () => this.invokeClaudeCodeAgent(agentName, prompt, agentConfig), timeout ); task.status = "completed"; task.result = result; task.endTime = Date.now(); task.actualTokens = this.estimateTokenUsage(prompt, result); this.completeTask(task); return result; } catch (error) { lastError = error; task.status = "failed"; logger.warn(`Claude Code task attempt ${attempt + 1} failed`, { taskId, agentName, error: lastError.message, attempt: attempt + 1, maxRetries: maxRetries + 1 }); if (attempt === maxRetries) { break; } const backoffMs = Math.min(1e3 * Math.pow(2, attempt), 1e4); await new Promise((resolve) => setTimeout(resolve, backoffMs)); } } task.error = lastError?.message || "Unknown error"; task.endTime = Date.now(); this.failTask(task, lastError); throw lastError; } finally { this.activeTasks.delete(taskId); } } /** * Execute multiple tasks in parallel with coordination */ async executeParallelTasks(tasks) { logger.info("Executing parallel Claude Code tasks", { taskCount: tasks.length, agents: tasks.map((t) => t.agentName) }); const priorityGroups = { high: tasks.filter((t) => t.priority === "high"), medium: tasks.filter((t) => t.priority === "medium"), low: tasks.filter((t) => t.priority === "low") }; const results = []; const failures = []; for (const priorityLevel of ["high", "medium", "low"]) { const priorityTasks = priorityGroups[priorityLevel]; if (priorityTasks.length === 0) continue; logger.info(`Executing ${priorityLevel} priority tasks`, { count: priorityTasks.length }); const promises = priorityTasks.map(async (task) => { try { const result = await this.executeTask( task.agentName, task.agentConfig, task.prompt, { priority: task.priority } ); return { success: true, result }; } catch (error) { return { success: false, error }; } }); const outcomes = await Promise.allSettled(promises); for (const outcome of outcomes) { if (outcome.status === "fulfilled") { if (outcome.value.success) { results.push(outcome.value.result); } else { failures.push(outcome.value.error); } } else { failures.push(new Error(outcome.reason)); } } } logger.info("Parallel task execution completed", { totalTasks: tasks.length, successful: results.length, failed: failures.length, successRate: (results.length / tasks.length * 100).toFixed(1) }); return { results, failures }; } /** * Get coordination metrics and health status */ getCoordinationMetrics() { const recentTasks = this.completedTasks.slice(-10); const recentErrorRate = recentTasks.length > 0 ? recentTasks.filter((t) => t.status === "failed").length / recentTasks.length : 0; const performanceTrend = recentErrorRate < 0.1 ? "improving" : recentErrorRate < 0.3 ? "stable" : "degrading"; const recentErrors = this.completedTasks.slice(-5).filter((t) => t.status === "failed").map((t) => t.error || "Unknown error"); return { ...this.metrics, activeTasks: this.activeTasks.size, recentErrors, performanceTrend }; } /** * Clean up resources and reset metrics */ async cleanup() { logger.info("Cleaning up Claude Code Task Coordinator", { activeTasks: this.activeTasks.size, completedTasks: this.completedTasks.length }); if (this.activeTasks.size > 0) { const timeoutPromise = new Promise( (resolve) => setTimeout(resolve, 3e4) ); const completionPromise = this.waitForTaskCompletion(); await Promise.race([completionPromise, timeoutPromise]); if (this.activeTasks.size > 0) { logger.warn("Force terminating active tasks", { remainingTasks: this.activeTasks.size }); } } this.activeTasks.clear(); this.completedTasks = []; this.resetMetrics(); } /** * Execute with timeout wrapper */ async executeWithTimeout(fn, timeoutMs) { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new Error(`Task execution timeout after ${timeoutMs}ms`)); }, timeoutMs); }); return Promise.race([fn(), timeoutPromise]); } /** * Invoke Claude Code agent (integration point) */ async invokeClaudeCodeAgent(agentName, prompt, agentConfig) { logger.debug("Invoking Claude Code agent", { agentName, agentType: agentConfig.type, promptTokens: this.estimateTokenUsage(prompt, "") }); return this.simulateClaudeCodeExecution(agentName, prompt, agentConfig); } /** * Simulate Claude Code execution (temporary until real integration) */ simulateClaudeCodeExecution(agentName, prompt, agentConfig) { return new Promise((resolve) => { const executionTime = agentConfig.type === "oracle" ? 2e3 + Math.random() * 3e3 : 1e3 + Math.random() * 2e3; setTimeout(() => { const result = `Claude Code agent '${agentName}' completed task successfully. Agent Capabilities Used: ${agentConfig.capabilities.slice(0, 3).join(", ")} Task Type: ${agentConfig.type} Specializations: ${agentConfig.specializations.join(", ")} Simulated output based on prompt context: ${prompt.substring(0, 100)}... This simulation will be replaced with actual Claude Code Task tool integration.`; resolve(result); }, executionTime); }); } /** * Complete a successful task */ completeTask(task) { this.completedTasks.push({ ...task }); this.metrics.completedTasks++; this.updateExecutionMetrics(task); this.updateAgentUtilization(task.agentName); this.updateSuccessRate(); logger.info("Claude Code task completed", { taskId: task.id, agentName: task.agentName, executionTime: task.endTime - task.startTime, retries: task.retryCount, cost: this.calculateActualCost(task) }); } /** * Handle a failed task */ failTask(task, error) { this.completedTasks.push({ ...task }); this.metrics.failedTasks++; this.updateExecutionMetrics(task); this.updateSuccessRate(); logger.error("Claude Code task failed", { taskId: task.id, agentName: task.agentName, error: error.message, retries: task.retryCount, executionTime: task.endTime - task.startTime }); } /** * Update execution time metrics */ updateExecutionMetrics(task) { if (!task.endTime) return; const executionTime = task.endTime - task.startTime; const totalTasks = this.metrics.completedTasks + this.metrics.failedTasks; if (totalTasks === 1) { this.metrics.averageExecutionTime = executionTime; } else { this.metrics.averageExecutionTime = (this.metrics.averageExecutionTime * (totalTasks - 1) + executionTime) / totalTasks; } this.metrics.totalCost += this.calculateActualCost(task); } /** * Update agent utilization metrics */ updateAgentUtilization(agentName) { this.metrics.agentUtilization[agentName] = (this.metrics.agentUtilization[agentName] || 0) + 1; } /** * Update success rate */ updateSuccessRate() { const total = this.metrics.completedTasks + this.metrics.failedTasks; this.metrics.successRate = total > 0 ? this.metrics.completedTasks / total : 0; } /** * Estimate task cost based on prompt and agent */ estimateTaskCost(prompt, agentConfig) { const estimatedTokens = this.estimateTokenUsage(prompt, ""); const baseCost = agentConfig.type === "oracle" ? 0.015 : 25e-5; return estimatedTokens / 1e3 * baseCost * agentConfig.costMultiplier; } /** * Estimate token usage */ estimateTokenUsage(prompt, response) { return Math.ceil((prompt.length + response.length) / 4); } /** * Calculate actual task cost */ calculateActualCost(task) { if (!task.actualTokens) return task.estimatedCost; const baseCost = task.agentType === "oracle" ? 0.015 : 25e-5; return task.actualTokens / 1e3 * baseCost; } /** * Wait for all active tasks to complete */ async waitForTaskCompletion() { while (this.activeTasks.size > 0) { await new Promise((resolve) => setTimeout(resolve, 1e3)); } } /** * Reset metrics */ resetMetrics() { this.metrics = { totalTasks: 0, completedTasks: 0, failedTasks: 0, averageExecutionTime: 0, totalCost: 0, successRate: 0, agentUtilization: {} }; } /** * Get active task status */ getActiveTaskStatus() { return Array.from(this.activeTasks.values()).map((task) => ({ taskId: task.id, agentName: task.agentName, status: task.status, runtime: Date.now() - task.startTime })); } /** * Cancel active task */ async cancelTask(taskId) { const task = this.activeTasks.get(taskId); if (!task) return false; task.status = "failed"; task.error = "Task cancelled by user"; task.endTime = Date.now(); this.failTask(task, new Error("Task cancelled")); this.activeTasks.delete(taskId); logger.info("Task cancelled", { taskId, agentName: task.agentName }); return true; } } var task_coordinator_default = ClaudeCodeTaskCoordinator; export { ClaudeCodeTaskCoordinator, task_coordinator_default as default }; //# sourceMappingURL=task-coordinator.js.map