UNPKG

permamind

Version:

An MCP server that provides an immortal memory layer for AI agents and clients

783 lines (782 loc) 30.7 kB
// Agent templates for different roles const AGENT_TEMPLATES = { analyst: { behaviors: [], defaultCapabilities: [ "requirements_analysis", "process_modeling", "stakeholder_analysis", "gap_analysis", "solution_design", ], defaultPreferences: { communicationStyle: "formal", notifications: { enabled: true, frequency: "daily" }, workingHours: { end: "18:00", start: "08:00", timezone: "UTC" }, }, description: "Requirements analysis and business process optimization", name: "Business Analyst", optionalContext: ["currentProcesses", "stakeholderMap"], requiredContext: ["projectId", "businessRequirements"], role: "analyst", }, architect: { behaviors: [], defaultCapabilities: [ "architecture_design", "technical_guidance", "system_analysis", "technology_selection", "design_review", ], defaultPreferences: { communicationStyle: "technical", notifications: { enabled: true, frequency: "hourly" }, workingHours: { end: "18:00", start: "08:00", timezone: "UTC" }, }, description: "Technical architecture design and guidance", name: "Solution Architect", optionalContext: ["constraints", "existingArchitecture"], requiredContext: ["projectId", "technicalRequirements"], role: "architect", }, "bmad-master": { behaviors: [ { action: "initialize_coordination", condition: "all_agents_ready", priority: 1, trigger: "workflow_start", }, ], defaultCapabilities: [ "methodology_guidance", "workflow_orchestration", "quality_assurance", "team_coordination", "progress_tracking", ], defaultPreferences: { communicationStyle: "formal", notifications: { enabled: true, frequency: "immediate" }, workingHours: { end: "17:00", start: "09:00", timezone: "UTC" }, }, description: "Master coordinator for BMAD methodology implementation", name: "BMAD Master", optionalContext: ["teamMembers", "timeline"], requiredContext: ["projectId", "methodology"], role: "bmad-master", }, developer: { behaviors: [], defaultCapabilities: [ "code_implementation", "testing", "debugging", "code_review", "documentation", ], defaultPreferences: { communicationStyle: "casual", notifications: { enabled: true, frequency: "immediate" }, workingHours: { end: "17:00", start: "09:00", timezone: "UTC" }, }, description: "Code implementation and development tasks", name: "Full Stack Developer", optionalContext: ["testingStrategy", "deploymentInfo"], requiredContext: ["projectId", "codebase"], role: "developer", }, pm: { behaviors: [], defaultCapabilities: [ "project_planning", "resource_management", "risk_management", "stakeholder_communication", "progress_tracking", ], defaultPreferences: { communicationStyle: "collaborative", notifications: { enabled: true, frequency: "daily" }, workingHours: { end: "18:00", start: "08:00", timezone: "UTC" }, }, description: "Project coordination and management", name: "Project Manager", optionalContext: ["budget", "timeline"], requiredContext: ["projectId", "stakeholders"], role: "pm", }, qa: { behaviors: [], defaultCapabilities: [ "test_planning", "quality_review", "defect_tracking", "compliance_verification", "process_improvement", ], defaultPreferences: { communicationStyle: "formal", notifications: { enabled: true, frequency: "immediate" }, workingHours: { end: "17:00", start: "09:00", timezone: "UTC" }, }, description: "Quality assurance and testing coordination", name: "Quality Assurance", optionalContext: ["testingTools", "complianceFramework"], requiredContext: ["projectId", "qualityStandards"], role: "qa", }, sm: { behaviors: [], defaultCapabilities: [ "agile_facilitation", "impediment_removal", "team_coaching", "ceremony_facilitation", "continuous_improvement", ], defaultPreferences: { communicationStyle: "collaborative", notifications: { enabled: true, frequency: "immediate" }, workingHours: { end: "18:00", start: "08:00", timezone: "UTC" }, }, description: "Agile facilitation and process coaching", name: "Scrum Master", optionalContext: ["sprintGoals", "retrospectiveInsights"], requiredContext: ["projectId", "teamDynamics"], role: "sm", }, "ux-expert": { behaviors: [], defaultCapabilities: [ "user_research", "design_thinking", "prototyping", "usability_testing", "interaction_design", ], defaultPreferences: { communicationStyle: "collaborative", notifications: { enabled: true, frequency: "daily" }, workingHours: { end: "17:00", start: "09:00", timezone: "UTC" }, }, description: "User experience design and research", name: "UX Expert", optionalContext: ["designSystem", "accessibilityRequirements"], requiredContext: ["projectId", "userPersonas"], role: "ux-expert", }, }; export class TeamAgentServiceImpl { memoryService; processService; constructor(memoryService, processService) { this.memoryService = memoryService; this.processService = processService; } async addConversationEntry(agentId, entry, hubId) { try { const agent = await this.getAgent(agentId, hubId); if (!agent) { return false; } const updatedHistory = [...agent.state.conversationHistory, entry]; return await this.updateAgentState(agentId, { conversationHistory: updatedHistory }, hubId); } catch (error) { return false; } } async broadcastToAgents(senderAgentId, targetAgentIds, message, messageType, hubId) { const messageId = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const delivered = []; const failed = []; try { const broadcastEntry = { context: { broadcast: true, recipients: targetAgentIds }, id: messageId, message, messageType: messageType, speaker: senderAgentId, timestamp: new Date().toISOString(), }; for (const agentId of targetAgentIds) { try { const success = await this.addConversationEntry(agentId, broadcastEntry, hubId); if (success) { delivered.push(agentId); } else { failed.push({ agentId, reason: "Failed to add conversation entry", retryable: true, }); } } catch (error) { failed.push({ agentId, reason: error instanceof Error ? error.message : "Unknown error", retryable: true, }); } } return { delivered, failed, messageId, success: failed.length === 0, }; } catch (error) { throw new Error(`Failed to broadcast message: ${error instanceof Error ? error.message : "Unknown error"}`); } } async coordinateAgentTasks(coordinatorId, taskAssignments, hubId) { const coordinationId = `coord_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const assignments = []; const errors = []; try { for (const assignment of taskAssignments) { try { const agent = await this.getAgent(assignment.assignedAgentId, hubId); if (!agent) { errors.push({ affectedAgentId: assignment.assignedAgentId, affectedTaskId: assignment.taskId, message: `Agent ${assignment.assignedAgentId} not found`, type: "agent_unavailable", }); assignments.push({ agentId: assignment.assignedAgentId, assigned: false, error: "Agent not found", taskId: assignment.taskId, }); continue; } // Add task to agent's queue const taskItem = { assignedAt: new Date().toISOString(), dependencies: assignment.dependencies, description: `Assigned task from coordination ${coordinationId}`, dueDate: assignment.deadline, id: assignment.taskId, priority: assignment.priority, status: "pending", title: `Task ${assignment.taskId}`, }; const updatedQueue = [...agent.context.taskQueue, taskItem]; await this.updateAgentContext(assignment.assignedAgentId, { taskQueue: updatedQueue }, hubId); assignments.push({ agentId: assignment.assignedAgentId, assigned: true, taskId: assignment.taskId, }); } catch (error) { errors.push({ affectedAgentId: assignment.assignedAgentId, affectedTaskId: assignment.taskId, message: error instanceof Error ? error.message : "Unknown error", type: "invalid_task", }); assignments.push({ agentId: assignment.assignedAgentId, assigned: false, error: error instanceof Error ? error.message : "Unknown error", taskId: assignment.taskId, }); } } return { assignments, coordinationId, errors: errors.length > 0 ? errors : undefined, success: errors.length === 0, }; } catch (error) { throw new Error(`Failed to coordinate tasks: ${error instanceof Error ? error.message : "Unknown error"}`); } } async createAgent(agentConfig, signer, hubId) { // Validate agent configuration if (!agentConfig.name || !agentConfig.role) { throw new Error("Agent name and role are required"); } // Generate unique agent ID const agentId = `agent_${agentConfig.role}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // Create initial agent state const initialState = { conversationHistory: [], lastActivity: new Date().toISOString(), projectContext: { constraints: [], objectives: [], phase: "initialization", projectId: agentConfig.initialContext?.projectId || "", projectName: "", resources: [], stakeholders: [], }, status: "active", }; // Create initial agent context const initialContext = { collaborators: [], environment: "development", projectId: agentConfig.initialContext?.projectId || "", sessionId: `session_${Date.now()}`, taskQueue: [], workflowState: { blockers: [], dependencies: [], nextActions: [], progress: 0, }, ...agentConfig.initialContext, }; // Create initial agent memory const initialMemory = { longTerm: [], preferences: { collaboration: { feedbackStyle: "constructive", meetingPreference: "hybrid", preferredTools: [], }, communicationStyle: "formal", notifications: { enabled: true, frequency: "immediate", types: [] }, workingHours: { end: "17:00", start: "09:00", timezone: "UTC" }, ...agentConfig.preferences, }, shortTerm: [], working: [], }; // Create the team agent const teamAgent = { capabilities: agentConfig.capabilities, context: initialContext, description: agentConfig.description, id: agentId, memory: initialMemory, name: agentConfig.name, role: agentConfig.role, state: initialState, }; // Store agent in memory service const agentMemory = { content: JSON.stringify(teamAgent), context: { domain: "team_agent", sessionId: initialContext.sessionId, topic: `agent_${agentConfig.role}`, }, importance: 0.9, memoryType: "workflow", }; try { await this.memoryService.addEnhanced(signer, hubId, agentMemory); return teamAgent; } catch (error) { throw new Error(`Failed to create agent: ${error instanceof Error ? error.message : "Unknown error"}`); } } customizeAgentFromTemplate(template, customizations) { return { capabilities: [ ...template.defaultCapabilities, ...(customizations.additionalCapabilities || []), ], description: customizations.description || template.description, initialContext: { ...customizations.contextOverrides, }, name: customizations.name || template.name, preferences: { ...template.defaultPreferences, ...customizations.preferenceOverrides, }, role: template.role, }; } async deleteAgent(agentId, hubId) { // Note: This would require memory deletion capability // For now, we'll mark the agent as deleted in its state try { return await this.updateAgentState(agentId, { status: "offline" }, hubId); } catch (error) { return false; } } async executeAgentAction(agentId, action, signer, hubId) { const startTime = Date.now(); try { const agent = await this.getAgent(agentId, hubId); if (!agent) { const endTime = Date.now(); return { duration: Math.max(1, endTime - startTime), // Ensure minimum 1ms duration error: { code: "AGENT_NOT_FOUND", message: `Agent ${agentId} not found`, }, success: false, timestamp: new Date().toISOString(), }; } // Execute action based on type let result; switch (action.type) { case "analyze": result = await this.analyze(agent, action.parameters); break; case "collaborate": result = await this.collaborate(agent, action.parameters, hubId); break; case "create_workflow": result = await this.createWorkflow(agent, action.parameters); break; case "execute_task": result = await this.executeTask(agent, action.parameters); break; case "report": result = await this.generateReport(agent, action.parameters); break; case "send_message": result = await this.sendMessage(agent, action.parameters, hubId); break; case "update_status": result = await this.updateStatus(agent, action.parameters, hubId); break; default: throw new Error(`Unknown action type: ${action.type}`); } // Update agent's last activity await this.updateAgentState(agentId, { conversationHistory: [ ...agent.state.conversationHistory, { id: `action_${action.id}`, message: `Executed action: ${action.type}`, messageType: "command", speaker: agentId, timestamp: new Date().toISOString(), }, ], lastActivity: new Date().toISOString(), }, hubId); const endTime = Date.now(); return { duration: Math.max(1, endTime - startTime), // Ensure minimum 1ms duration result, success: true, timestamp: new Date().toISOString(), }; } catch (error) { const endTime = Date.now(); return { duration: Math.max(1, endTime - startTime), // Ensure minimum 1ms duration error: { code: "ACTION_EXECUTION_FAILED", details: error, message: error instanceof Error ? error.message : "Unknown error", }, success: false, timestamp: new Date().toISOString(), }; } } async getAgent(agentId, hubId) { try { const memories = await this.memoryService.searchAdvanced(hubId, `agent_id:${agentId}`, { memoryType: "workflow" }); if (memories.length === 0) { return null; } try { const agentData = JSON.parse(memories[0].content); return agentData; } catch (parseError) { // Return null for invalid JSON instead of throwing return null; } } catch (error) { throw new Error(`Failed to get agent: ${error instanceof Error ? error.message : "Unknown error"}`); } } getAgentTemplate(role) { return AGENT_TEMPLATES[role]; } async initiateAgentConversation(agentIds, topic, initiatorId, hubId) { const conversationId = `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; try { // Create conversation entry for each agent const conversationEntry = { context: { participants: agentIds, topic }, id: conversationId, message: `Initiated conversation about: ${topic}`, messageType: "command", speaker: initiatorId, timestamp: new Date().toISOString(), }; // Add conversation entry to all participating agents const promises = agentIds.map((agentId) => this.addConversationEntry(agentId, conversationEntry, hubId)); await Promise.all(promises); return conversationId; } catch (error) { throw new Error(`Failed to initiate conversation: ${error instanceof Error ? error.message : "Unknown error"}`); } } async integrateWithMemoryService(agentId, memoryContext, hubId) { try { const agent = await this.getAgent(agentId, hubId); if (!agent) { return false; } // Update agent context with memory integration const updatedContext = { ...agent.context, ...memoryContext, }; return await this.updateAgentContext(agentId, updatedContext, hubId); } catch (error) { return false; } } async listAgents(hubId, role) { try { const searchQuery = role ? `agent_${role}` : "team_agent"; const memories = await this.memoryService.searchAdvanced(hubId, searchQuery, { memoryType: "workflow" }); const agents = []; for (const memory of memories) { try { const agentData = JSON.parse(memory.content); if (agentData.id && (!role || agentData.role === role)) { agents.push(agentData); } } catch (parseError) { // Skip invalid agent data continue; } } return agents; } catch (error) { throw new Error(`Failed to list agents: ${error instanceof Error ? error.message : "Unknown error"}`); } } async loadAgentPreset(presetId, hubId) { try { const memories = await this.memoryService.searchAdvanced(hubId, `preset_${presetId}`, { memoryType: "workflow" }); if (memories.length === 0) { return null; } return JSON.parse(memories[0].content); } catch (error) { return null; } } async retrieveAgentMemories(agentId, filters, hubId) { try { const agent = await this.getAgent(agentId, hubId); if (!agent) { return []; } // Filter agent's memories based on criteria let memories = [ ...agent.memory.longTerm, ...agent.memory.shortTerm, ...agent.memory.working, ]; if (filters.memoryType) { memories = memories.filter((memory) => memory.memoryType === filters.memoryType); } if (filters.importance) { memories = memories.filter((memory) => memory.importance >= filters.importance.min && memory.importance <= filters.importance.max); } if (filters.tags && filters.tags.length > 0) { memories = memories.filter((memory) => filters.tags.some((tag) => memory.tags.includes(tag))); } if (filters.timeRange) { const start = new Date(filters.timeRange.start); const end = new Date(filters.timeRange.end); memories = memories.filter((memory) => { const memoryTime = new Date(memory.timestamp); return memoryTime >= start && memoryTime <= end; }); } if (filters.limit) { memories = memories.slice(0, filters.limit); } return memories; } catch (error) { throw new Error(`Failed to retrieve agent memories: ${error instanceof Error ? error.message : "Unknown error"}`); } } async saveAgentPreset(agentId, presetName, hubId) { try { const agent = await this.getAgent(agentId, hubId); if (!agent) { throw new Error(`Agent ${agentId} not found`); } // Create preset from current agent configuration const preset = { capabilities: agent.capabilities, description: agent.description, initialContext: {}, name: agent.name, preferences: agent.memory.preferences, role: agent.role, }; const presetId = `preset_${presetName}_${Date.now()}`; const presetMemory = { content: JSON.stringify(preset), context: { domain: "agent_preset", topic: presetName, }, importance: 0.8, memoryType: "workflow", }; await this.memoryService.addEnhanced({}, hubId, presetMemory); return presetId; } catch (error) { throw new Error(`Failed to save agent preset: ${error instanceof Error ? error.message : "Unknown error"}`); } } async storeAgentMemory(agentId, memoryEntry, signer, hubId) { try { const agent = await this.getAgent(agentId, hubId); if (!agent) { throw new Error(`Agent ${agentId} not found`); } // Store in AI memory service const aiMemory = { content: memoryEntry.content, context: { domain: "agent_memory", sessionId: agent.context.sessionId, topic: `agent_${agentId}_${memoryEntry.memoryType}`, ...memoryEntry.context, }, importance: memoryEntry.importance, memoryType: "knowledge", }; const memoryId = await this.memoryService.addEnhanced(signer, hubId, aiMemory); // Add to agent's memory const updatedMemory = { ...agent.memory }; updatedMemory.longTerm.push({ ...memoryEntry, id: memoryId }); await this.updateAgentState(agentId, { ...agent.state }, hubId); return memoryId; } catch (error) { throw new Error(`Failed to store agent memory: ${error instanceof Error ? error.message : "Unknown error"}`); } } async updateAgentContext(agentId, context, hubId) { try { const agent = await this.getAgent(agentId, hubId); if (!agent) { return false; } agent.context = { ...agent.context, ...context }; return await this.updateAgentState(agentId, agent.state, hubId); } catch (error) { return false; } } async updateAgentState(agentId, state, hubId) { try { const agent = await this.getAgent(agentId, hubId); if (!agent) { return false; } // Update the agent state agent.state = { ...agent.state, ...state }; agent.state.lastActivity = new Date().toISOString(); // Store updated agent const agentMemory = { content: JSON.stringify(agent), context: { domain: "team_agent", sessionId: agent.context.sessionId, topic: `agent_${agent.role}`, }, importance: 0.9, memoryType: "workflow", }; // Note: In a real implementation, we would update the existing memory // For now, we create a new memory entry await this.memoryService.addEnhanced({}, // This would need proper signer hubId, agentMemory); return true; } catch (error) { return false; } } async analyze(agent, parameters) { // Implementation would perform analysis based on agent capabilities return { analysisCompleted: true, insights: [] }; } async collaborate(agent, parameters, hubId) { // Implementation would facilitate collaboration between agents return { collaborationInitiated: true }; } async createWorkflow(agent, parameters) { // Implementation would create a new workflow return { workflowCreated: true, workflowId: `workflow_${Date.now()}` }; } // Private helper methods for action execution async executeTask(agent, parameters) { // Implementation would depend on task type and parameters return { result: "Task completed successfully", taskExecuted: true }; } async generateReport(agent, parameters) { // Implementation would generate reports based on agent data return { reportGenerated: true, reportId: `report_${Date.now()}` }; } async sendMessage(agent, parameters, hubId) { const { message, messageType, targetAgentId } = parameters; if (typeof targetAgentId !== "string" || typeof message !== "string") { throw new Error("Invalid message parameters"); } const conversationEntry = { id: `msg_${Date.now()}`, message: message, messageType: messageType || "response", speaker: agent.id, timestamp: new Date().toISOString(), }; await this.addConversationEntry(targetAgentId, conversationEntry, hubId); return { messageId: conversationEntry.id, messageSent: true }; } async updateStatus(agent, parameters, hubId) { const { status } = parameters; if (typeof status !== "string") { throw new Error("Invalid status parameter"); } await this.updateAgentState(agent.id, { status: status }, hubId); return { newStatus: status, statusUpdated: true }; } } // Export singleton service factory export const createTeamAgentService = (memoryService, processService) => { return new TeamAgentServiceImpl(memoryService, processService); };