Skip to main content
Glama
by fmangot
lib.ts6.73 kB
/** * Sequential Thinking Library * Core logic for managing thought sequences */ import { StoredThought, ThoughtInput, ThoughtSequence, ThoughtResponse } from './types.js'; import { generateSecureSessionId } from './utils/crypto.js'; import { validateThoughtInput, ValidationError } from './utils/validators.js'; export class SequentialThinkingManager { private sessions: Map<string, ThoughtSequence>; private currentSessionId: string; private cleanupInterval: NodeJS.Timeout | null; // Configuration private readonly SESSION_TTL = 3600000; // 1 hour in milliseconds private readonly CLEANUP_INTERVAL = 600000; // 10 minutes in milliseconds private readonly MAX_SESSIONS = 10000; // Prevent unbounded growth constructor() { this.sessions = new Map(); this.currentSessionId = generateSecureSessionId(); this.initializeSession(this.currentSessionId); this.cleanupInterval = null; // Start cleanup interval this.startCleanup(); } /** * Start periodic cleanup of expired sessions */ private startCleanup(): void { if (this.cleanupInterval) { return; } this.cleanupInterval = setInterval(() => { this.cleanupExpiredSessions(); }, this.CLEANUP_INTERVAL); // Don't prevent process from exiting if (this.cleanupInterval.unref) { this.cleanupInterval.unref(); } } /** * Stop cleanup interval */ private stopCleanup(): void { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } } /** * Clean up expired sessions */ private cleanupExpiredSessions(): void { const now = Date.now(); let cleaned = 0; for (const [id, session] of this.sessions.entries()) { if (now - session.updatedAt > this.SESSION_TTL) { this.sessions.delete(id); cleaned++; } } if (cleaned > 0) { console.log(`Cleaned up ${cleaned} expired sessions. Active sessions: ${this.sessions.size}`); } // Emergency cleanup if we're still over the limit if (this.sessions.size > this.MAX_SESSIONS) { this.emergencyCleanup(); } } /** * Emergency cleanup: remove oldest sessions if limit exceeded */ private emergencyCleanup(): void { const sessions = Array.from(this.sessions.entries()) .sort((a, b) => a[1].updatedAt - b[1].updatedAt); const toRemove = sessions.slice(0, Math.floor(this.MAX_SESSIONS * 0.2)); // Remove oldest 20% for (const [id] of toRemove) { this.sessions.delete(id); } console.warn( `Emergency cleanup: removed ${toRemove.length} oldest sessions. Active: ${this.sessions.size}` ); } private initializeSession(sessionId: string): void { this.sessions.set(sessionId, { sessionId, thoughts: [], branches: new Map(), createdAt: Date.now(), updatedAt: Date.now(), }); } public addThought(input: ThoughtInput, sessionId?: string): ThoughtResponse { // Validate input validateThoughtInput(input); const sid = sessionId || this.currentSessionId; let session = this.sessions.get(sid); if (!session) { this.initializeSession(sid); session = this.sessions.get(sid)!; } const storedThought: StoredThought = { ...input, timestamp: Date.now(), sessionId: sid, }; // Handle branching if (input.branchId && input.branchFromThought !== undefined) { let branchThoughts = session.branches.get(input.branchId); if (!branchThoughts) { branchThoughts = []; session.branches.set(input.branchId, branchThoughts); } branchThoughts.push(storedThought); } else { // Regular thought in main sequence session.thoughts.push(storedThought); } session.updatedAt = Date.now(); // Build response message let message = `Thought ${input.thoughtNumber}`; if (input.isRevision && input.revisesThought !== undefined) { message += ` (revising thought ${input.revisesThought})`; } if (input.branchId) { message += ` [branch: ${input.branchId}]`; } message += `: ${input.thought}`; return { success: true, thoughtNumber: input.thoughtNumber, message, sequence: session.thoughts, totalThoughts: input.totalThoughts, }; } public getSequence(sessionId?: string): StoredThought[] { const sid = sessionId || this.currentSessionId; const session = this.sessions.get(sid); return session ? session.thoughts : []; } public getBranch(branchId: string, sessionId?: string): StoredThought[] { const sid = sessionId || this.currentSessionId; const session = this.sessions.get(sid); return session?.branches.get(branchId) || []; } public getAllBranches(sessionId?: string): Map<string, StoredThought[]> { const sid = sessionId || this.currentSessionId; const session = this.sessions.get(sid); return session?.branches || new Map(); } public resetSession(): string { this.currentSessionId = generateSecureSessionId(); this.initializeSession(this.currentSessionId); return this.currentSessionId; } public getCurrentSessionId(): string { return this.currentSessionId; } public getSessionSummary(sessionId?: string): { sessionId: string; thoughtCount: number; branchCount: number; createdAt: number; updatedAt: number; } | null { const sid = sessionId || this.currentSessionId; const session = this.sessions.get(sid); if (!session) { return null; } return { sessionId: session.sessionId, thoughtCount: session.thoughts.length, branchCount: session.branches.size, createdAt: session.createdAt, updatedAt: session.updatedAt, }; } /** * Get statistics about all sessions */ public getStats(): { totalSessions: number; totalThoughts: number; oldestSession: number | null; newestSession: number | null; } { let totalThoughts = 0; let oldestSession: number | null = null; let newestSession: number | null = null; for (const session of this.sessions.values()) { totalThoughts += session.thoughts.length; if (!oldestSession || session.createdAt < oldestSession) { oldestSession = session.createdAt; } if (!newestSession || session.createdAt > newestSession) { newestSession = session.createdAt; } } return { totalSessions: this.sessions.size, totalThoughts, oldestSession, newestSession, }; } /** * Clean up and destroy this manager */ public destroy(): void { this.stopCleanup(); this.sessions.clear(); } }

Latest Blog Posts

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/fmangot/Mcp'

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