UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

193 lines 6.53 kB
/** * V3 Session Manager * Decomposed from orchestrator.ts - Session handling * ~200 lines (target achieved) */ import { SystemEventTypes } from '../interfaces/event.interface.js'; import { mkdir, writeFile, readFile } from 'fs/promises'; import { join, dirname } from 'path'; import { randomBytes } from 'crypto'; // Secure session ID generation function generateSecureSessionId() { const timestamp = Date.now().toString(36); const random = randomBytes(12).toString('hex'); return `session_${timestamp}_${random}`; } /** * Session manager implementation */ export class SessionManager { eventBus; config; sessions = new Map(); sessionProfiles = new Map(); persistencePath; constructor(eventBus, config) { this.eventBus = eventBus; this.config = config; this.persistencePath = join(config.dataDir || './data', 'sessions.json'); } async createSession(profile, terminalId, memoryBankId) { const session = { id: generateSecureSessionId(), agentId: profile.id, terminalId, startTime: new Date(), status: 'active', lastActivity: new Date(), memoryBankId, }; this.sessions.set(session.id, session); this.sessionProfiles.set(session.id, profile); this.eventBus.emit(SystemEventTypes.SESSION_CREATED, { sessionId: session.id, agentId: profile.id, terminalId, memoryBankId, }); // Persist sessions asynchronously this.persistSessions().catch(() => { // Silently ignore persistence errors }); return session; } getSession(sessionId) { return this.sessions.get(sessionId); } getActiveSessions() { return Array.from(this.sessions.values()).filter(session => session.status === 'active' || session.status === 'idle'); } getSessionsByAgent(agentId) { return Array.from(this.sessions.values()).filter(session => session.agentId === agentId); } async terminateSession(sessionId) { const session = this.sessions.get(sessionId); if (!session) { throw new Error(`Session not found: ${sessionId}`); } session.status = 'terminated'; session.endTime = new Date(); const duration = session.endTime.getTime() - session.startTime.getTime(); this.eventBus.emit(SystemEventTypes.SESSION_TERMINATED, { sessionId, agentId: session.agentId, duration, }); // Clean up profile reference this.sessionProfiles.delete(sessionId); // Persist sessions asynchronously this.persistSessions().catch(() => { // Silently ignore persistence errors }); } async terminateAllSessions() { const sessions = this.getActiveSessions(); const batchSize = 5; for (let i = 0; i < sessions.length; i += batchSize) { const batch = sessions.slice(i, i + batchSize); await Promise.allSettled(batch.map(session => this.terminateSession(session.id))); } } removeSession(sessionId) { this.sessions.delete(sessionId); this.sessionProfiles.delete(sessionId); } updateSessionActivity(sessionId) { const session = this.sessions.get(sessionId); if (session) { session.lastActivity = new Date(); } } async persistSessions() { if (!this.config.persistSessions) { return; } try { const data = { sessions: Array.from(this.sessions.values()) .map(session => ({ ...session, profile: this.sessionProfiles.get(session.id), })) .filter(s => s.profile), metrics: { completedTasks: 0, failedTasks: 0, totalTaskDuration: 0, }, savedAt: new Date(), }; await mkdir(dirname(this.persistencePath), { recursive: true }); await writeFile(this.persistencePath, JSON.stringify(data, null, 2), 'utf8'); this.eventBus.emit(SystemEventTypes.SESSION_PERSISTED, { sessionCount: data.sessions.length, path: this.persistencePath, }); } catch (error) { // Let caller handle persistence errors throw error; } } async restoreSessions() { if (!this.config.persistSessions) { return null; } try { const data = await readFile(this.persistencePath, 'utf8'); const persistence = JSON.parse(data); // Filter to only active/idle sessions const sessionsToRestore = persistence.sessions.filter(s => s.status === 'active' || s.status === 'idle'); this.eventBus.emit(SystemEventTypes.SESSION_RESTORED, { sessionCount: sessionsToRestore.length, path: this.persistencePath, }); return { ...persistence, sessions: sessionsToRestore, }; } catch (error) { if (error.code === 'ENOENT') { return null; } throw error; } } /** * Clean up old terminated sessions */ async cleanupTerminatedSessions(retentionMs) { const cutoffTime = Date.now() - (retentionMs ?? this.config.sessionRetentionMs ?? 3600000); let cleaned = 0; for (const [sessionId, session] of this.sessions.entries()) { if (session.status === 'terminated' && session.endTime) { if (session.endTime.getTime() < cutoffTime) { this.sessions.delete(sessionId); this.sessionProfiles.delete(sessionId); cleaned++; } } } return cleaned; } /** * Get session profile */ getSessionProfile(sessionId) { return this.sessionProfiles.get(sessionId); } /** * Get session count */ getSessionCount() { return this.sessions.size; } /** * Get active session count */ getActiveSessionCount() { return this.getActiveSessions().length; } } //# sourceMappingURL=session-manager.js.map