Skip to main content
Glama
SessionManager.ts•8.83 kB
import { SessionState, Phase, FileHistory, WorkflowMetrics, PhaseOutput, WorkflowConfiguration, ValidationState, EscalationContext } from '../types'; import { generateId } from '../utils/helpers'; export class SessionManager { private session: SessionState | null = null; startSession(taskDescription: string, workflowConfig?: WorkflowConfiguration, workflowType?: 'refactor' | 'feature' | 'test' | 'tdd'): SessionState { this.session = { id: generateId(), taskDescription, startedAt: Date.now(), currentPhase: 'PLANNING', completedPhases: [], phaseOutputs: new Map(), fileHistory: new Map(), metrics: this.initializeMetrics(), workflowConfig, iterationCounts: new Map(), validationStates: new Map(), workflowType }; return this.session; } getSession(): SessionState | null { return this.session; } endSession(): void { this.session = null; } updatePhase(phase: Phase): void { if (!this.session) return; // Mark current phase as completed if it's not already if (this.session.currentPhase !== phase && !this.session.completedPhases.includes(this.session.currentPhase)) { this.session.completedPhases.push(this.session.currentPhase); } this.session.currentPhase = phase; this.session.metrics.phasesCompleted = this.session.completedPhases.length; } recordPhaseOutput(phase: Phase, output: any): void { if (!this.session) return; const phaseOutput: PhaseOutput = { phase, completedAt: Date.now(), duration: Date.now() - this.session.startedAt, output }; this.session.phaseOutputs.set(phase, phaseOutput); } recordFileRead(filePath: string): void { if (!this.session) return; const existing = this.session.fileHistory.get(filePath) || { hasBeenRead: false, hasBeenModified: false }; this.session.fileHistory.set(filePath, { ...existing, hasBeenRead: true, firstReadAt: existing.firstReadAt || Date.now() }); this.session.metrics.filesAnalyzed++; } recordFileModification(filePath: string): void { if (!this.session) return; const existing = this.session.fileHistory.get(filePath) || { hasBeenRead: false, hasBeenModified: false }; this.session.fileHistory.set(filePath, { ...existing, hasBeenModified: true, lastModifiedAt: Date.now() }); if (!existing.hasBeenModified) { this.session.metrics.filesModified++; } } getFileHistory(filePath: string): FileHistory { if (!this.session) { return { hasBeenRead: false, hasBeenModified: false }; } return this.session.fileHistory.get(filePath) || { hasBeenRead: false, hasBeenModified: false }; } updateMetrics(updates: Partial<WorkflowMetrics>): void { if (!this.session) return; this.session.metrics = { ...this.session.metrics, ...updates }; } private initializeMetrics(): WorkflowMetrics { return { filesAnalyzed: 0, filesModified: 0, lintIssuesFound: 0, lintIssuesFixed: 0, phasesCompleted: 0 }; } // Workflow Configuration Management setWorkflowConfiguration(config: WorkflowConfiguration): void { if (!this.session) return; this.session.workflowConfig = config; } getWorkflowConfiguration(): WorkflowConfiguration | undefined { return this.session?.workflowConfig; } // Iteration Tracking incrementIterationCount(phase: Phase): number { if (!this.session) return 0; const currentCount = this.session.iterationCounts.get(phase) || 0; const newCount = currentCount + 1; this.session.iterationCounts.set(phase, newCount); return newCount; } getIterationCount(phase: Phase): number { if (!this.session) return 0; return this.session.iterationCounts.get(phase) || 0; } hasReachedIterationLimit(phase: Phase): boolean { if (!this.session?.workflowConfig) return false; const currentCount = this.getIterationCount(phase); const limits = this.session.workflowConfig.iterationLimits; switch (phase) { case 'TEST': return currentCount >= limits.TEST; case 'LINT': return currentCount >= limits.LINT; case 'ITERATE': return currentCount >= limits.ITERATE; default: return false; } } // Validation State Management setValidationState(phase: Phase, state: ValidationState): void { if (!this.session) return; this.session.validationStates.set(phase, state); } getValidationState(phase: Phase): ValidationState | undefined { if (!this.session) return undefined; return this.session.validationStates.get(phase); } isPhaseValidationComplete(phase: Phase): boolean { const state = this.getValidationState(phase); return state?.isComplete || false; } // Escalation Management shouldEscalateToUserInput(phase: Phase, error?: string): EscalationContext | null { if (!this.session?.workflowConfig) return null; const config = this.session.workflowConfig.escalationTriggers; const iterationCount = this.getIterationCount(phase); // Check iteration limit escalation if (config.escalateOnIterationLimit && this.hasReachedIterationLimit(phase)) { return { trigger: 'iteration_limit', failedPhase: phase, attemptCount: iterationCount, lastError: error, options: [ `Continue with ${iterationCount} more ${phase} iterations`, 'Skip to next phase', 'Modify requirements', 'Request human intervention' ], context: { currentLimits: this.session.workflowConfig.iterationLimits, phase, iterationCount } }; } // Check validation failure escalation const validationState = this.getValidationState(phase); if (config.escalateOnErrors && validationState && validationState.attempts >= 3) { return { trigger: 'validation_failure', failedPhase: phase, attemptCount: validationState.attempts, lastError: error, options: [ 'Modify validation criteria', 'Skip validation for this phase', 'Request human review', 'Reset and try again' ], context: { validationState, phase } }; } return null; } // Check if user checkpoint is required requiresUserCheckpoint(phase: Phase): boolean { if (!this.session?.workflowConfig) return false; const checkpoints = this.session.workflowConfig.userCheckpoints; // Check specific checkpoint configurations if (checkpoints.beforeMajorChanges && phase === 'WRITE_OR_REFACTOR') { return true; } if (checkpoints.afterFailedIterations && this.hasReachedIterationLimit(phase)) { return true; } if (checkpoints.beforeFinalPresentation && phase === 'PRESENT') { return true; } if (checkpoints.customCheckpoints?.includes(phase)) { return true; } return false; } // Get workflow progress summary getWorkflowProgress(): any { if (!this.session) return null; const config = this.session.workflowConfig; const selectedPhases = config?.selectedPhases || ['AUDIT', 'INVENTORY', 'WRITE_OR_REFACTOR', 'TEST', 'LINT', 'PRESENT']; return { sessionId: this.session.id, currentPhase: this.session.currentPhase, selectedPhases, completedPhases: this.session.completedPhases, totalPhases: selectedPhases.length, progressPercentage: Math.round((this.session.completedPhases.length / selectedPhases.length) * 100), iterationCounts: Object.fromEntries(this.session.iterationCounts), validationStates: Object.fromEntries( Array.from(this.session.validationStates.entries()).map(([phase, state]) => [ phase, { isComplete: state.isComplete, attempts: state.attempts, lastValidatedAt: new Date(state.lastValidatedAt).toISOString() } ]) ), timeElapsed: Date.now() - this.session.startedAt, nextSuggestedPhase: this.getNextSuggestedPhase() }; } private getNextSuggestedPhase(): Phase | null { if (!this.session?.workflowConfig) return null; const selectedPhases = this.session.workflowConfig.selectedPhases; const currentIndex = selectedPhases.indexOf(this.session.currentPhase); if (currentIndex === -1 || currentIndex === selectedPhases.length - 1) { return null; } return selectedPhases[currentIndex + 1]; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kingdomseed/structured-workflow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server