@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
JavaScript
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