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

334 lines 11 kB
/** * V3 Session Management Hooks * * Provides session-end and session-restore hooks for state persistence. * Enables cross-session memory and state recovery. * * @module v3/shared/hooks/session-hooks */ import { HookEvent, HookPriority, } from './types.js'; /** * In-memory session storage (for testing and fallback) */ export class InMemorySessionStorage { sessions = new Map(); async save(sessionId, state) { this.sessions.set(sessionId, state); } async load(sessionId) { return this.sessions.get(sessionId) || null; } async list() { return Array.from(this.sessions.entries()).map(([id, state]) => ({ id, startTime: state.startTime, })); } async delete(sessionId) { return this.sessions.delete(sessionId); } async getLatest() { let latest = null; for (const [id, state] of this.sessions) { const time = state.startTime.getTime(); if (!latest || time > latest.time) { latest = { id, time }; } } return latest?.id || null; } } /** * Session Hooks Manager * * Manages session lifecycle hooks with state persistence. */ export class SessionHooksManager { registry; storage; currentSessionId = null; sessionStartTime = null; activity = { tasksExecuted: 0, tasksSucceeded: 0, tasksFailed: 0, commandsExecuted: 0, filesModified: new Set(), agentsSpawned: new Set(), }; constructor(registry, storage) { this.registry = registry; this.storage = storage || new InMemorySessionStorage(); this.registerDefaultHooks(); } /** * Register default session hooks */ registerDefaultHooks() { // Session start hook this.registry.register(HookEvent.SessionStart, this.handleSessionStart.bind(this), HookPriority.High, { name: 'session-hooks:start' }); // Session end hook this.registry.register(HookEvent.SessionEnd, this.handleSessionEnd.bind(this), HookPriority.High, { name: 'session-hooks:end' }); // Session resume hook (for restoration) this.registry.register(HookEvent.SessionResume, this.handleSessionResume.bind(this), HookPriority.High, { name: 'session-hooks:resume' }); // Track tasks this.registry.register(HookEvent.PostTaskExecute, this.trackTaskExecution.bind(this), HookPriority.Low, { name: 'session-hooks:track-task' }); // Track commands this.registry.register(HookEvent.PostCommand, this.trackCommandExecution.bind(this), HookPriority.Low, { name: 'session-hooks:track-command' }); // Track file modifications this.registry.register(HookEvent.PostEdit, this.trackFileModification.bind(this), HookPriority.Low, { name: 'session-hooks:track-file' }); // Track agent spawns this.registry.register(HookEvent.PostAgentSpawn, this.trackAgentSpawn.bind(this), HookPriority.Low, { name: 'session-hooks:track-agent' }); } /** * Handle session start */ async handleSessionStart(context) { this.currentSessionId = context.session?.id || `session-${Date.now()}`; this.sessionStartTime = new Date(); this.resetActivity(); return { success: true, data: { session: { id: this.currentSessionId, startTime: this.sessionStartTime, }, }, }; } /** * Handle session end */ async handleSessionEnd(context) { if (!this.currentSessionId || !this.sessionStartTime) { return { success: true }; // No active session to end } const endTime = new Date(); const duration = endTime.getTime() - this.sessionStartTime.getTime(); // Build session summary const summary = { tasksExecuted: this.activity.tasksExecuted, tasksSucceeded: this.activity.tasksSucceeded, tasksFailed: this.activity.tasksFailed, commandsExecuted: this.activity.commandsExecuted, filesModified: this.activity.filesModified.size, agentsSpawned: this.activity.agentsSpawned.size, duration, }; // Build session state const state = { sessionId: this.currentSessionId, startTime: this.sessionStartTime, endTime, workingDirectory: context.metadata?.workingDirectory, activeTasks: context.metadata?.activeTasks, spawnedAgents: context.metadata?.spawnedAgents, memoryEntries: context.metadata?.memoryEntries, gitState: context.metadata?.gitState, learningMetrics: context.metadata?.learningMetrics, metadata: { summary, ...(context.metadata || {}), }, }; // Persist state await this.storage.save(this.currentSessionId, state); // Reset session tracking const sessionId = this.currentSessionId; this.currentSessionId = null; this.sessionStartTime = null; this.resetActivity(); return { success: true, persistedState: state, statePath: `sessions/${sessionId}.json`, duration, summary, }; } /** * Handle session resume (restoration) */ async handleSessionResume(context) { let sessionId = context.session?.id; // If 'latest' is requested, get the most recent session if (sessionId === 'latest' || !sessionId) { sessionId = await this.storage.getLatest() || undefined; } if (!sessionId) { return { success: false, error: new Error('No session ID provided and no previous sessions found'), warnings: ['No sessions available for restoration'], }; } // Load session state const state = await this.storage.load(sessionId); if (!state) { return { success: false, error: new Error(`Session ${sessionId} not found`), warnings: [`Session ${sessionId} does not exist or has been deleted`], }; } const warnings = []; // Validate state age const stateAge = Date.now() - state.startTime.getTime(); const maxAge = 7 * 24 * 60 * 60 * 1000; // 7 days if (stateAge > maxAge) { warnings.push(`Session is ${Math.floor(stateAge / (24 * 60 * 60 * 1000))} days old, some state may be stale`); } // Count restorable items const tasksRestored = state.activeTasks?.length || 0; const agentsRestored = state.spawnedAgents?.length || 0; const memoryRestored = state.memoryEntries?.length || 0; // Check for incomplete tasks const incompleteTasks = state.activeTasks?.filter(t => t.status === 'pending' || t.status === 'in_progress'); if (incompleteTasks && incompleteTasks.length > 0) { warnings.push(`${incompleteTasks.length} tasks were incomplete when session ended`); } // Update current session tracking this.currentSessionId = `session-${Date.now()}-restored`; this.sessionStartTime = new Date(); this.resetActivity(); return { success: true, restoredState: state, tasksRestored, agentsRestored, memoryRestored, warnings: warnings.length > 0 ? warnings : undefined, data: { session: { id: this.currentSessionId, startTime: this.sessionStartTime, metadata: { restoredFrom: sessionId, originalStartTime: state.startTime, }, }, }, }; } /** * Track task execution */ async trackTaskExecution(context) { this.activity.tasksExecuted++; if (context.metadata?.success !== false) { this.activity.tasksSucceeded++; } else { this.activity.tasksFailed++; } return { success: true }; } /** * Track command execution */ async trackCommandExecution(context) { this.activity.commandsExecuted++; return { success: true }; } /** * Track file modification */ async trackFileModification(context) { if (context.file?.path) { this.activity.filesModified.add(context.file.path); } return { success: true }; } /** * Track agent spawn */ async trackAgentSpawn(context) { if (context.agent?.id) { this.activity.agentsSpawned.add(context.agent.id); } return { success: true }; } /** * Reset activity tracking */ resetActivity() { this.activity = { tasksExecuted: 0, tasksSucceeded: 0, tasksFailed: 0, commandsExecuted: 0, filesModified: new Set(), agentsSpawned: new Set(), }; } /** * Execute session-end hook manually */ async executeSessionEnd(metadata) { const context = { event: HookEvent.SessionEnd, timestamp: new Date(), session: this.currentSessionId ? { id: this.currentSessionId, startTime: this.sessionStartTime, } : undefined, metadata, }; return this.handleSessionEnd(context); } /** * Execute session-restore hook manually */ async executeSessionRestore(sessionId, metadata) { const context = { event: HookEvent.SessionResume, timestamp: new Date(), session: { id: sessionId || 'latest', startTime: new Date(), }, metadata, }; return this.handleSessionResume(context); } /** * List available sessions */ async listSessions() { return this.storage.list(); } /** * Delete a session */ async deleteSession(sessionId) { return this.storage.delete(sessionId); } /** * Get current session ID */ getCurrentSessionId() { return this.currentSessionId; } /** * Get current session activity */ getCurrentActivity() { return { ...this.activity }; } /** * Set storage backend */ setStorage(storage) { this.storage = storage; } } /** * Create session hooks manager */ export function createSessionHooksManager(registry, storage) { return new SessionHooksManager(registry, storage); } //# sourceMappingURL=session-hooks.js.map