UNPKG

@mrtkrcm/acp-claude-code

Version:

ACP (Agent Client Protocol) bridge for Claude Code

95 lines 4.31 kB
export class ContextMonitor { sessions = new Map(); CONTEXT_LIMIT = 200000; WARNING_THRESHOLD = 0.8; CRITICAL_THRESHOLD = 0.95; cleanupTimer; constructor(_debugMode) { // Auto-cleanup every 10 minutes instead of relying on external calls this.cleanupTimer = setInterval(() => { this.cleanupInactiveSessions(); }, 10 * 60 * 1000); } addMessage(sessionId, content, role) { // Use more accurate token estimation for better memory planning const tokens = this.estimateTokens(content); const stats = this.sessions.get(sessionId) || { usage: 0, estimatedTokens: 0, messages: 0, turnCount: 0, lastActivity: Date.now(), lastUpdate: new Date() }; stats.estimatedTokens += tokens; stats.usage = Math.min(stats.estimatedTokens / this.CONTEXT_LIMIT, 1); stats.messages++; if (role === 'user') stats.turnCount++; stats.lastActivity = Date.now(); stats.lastUpdate = new Date(); this.sessions.set(sessionId, stats); // Proactive cleanup when approaching limits if (this.sessions.size > 100) { this.cleanupInactiveSessions(30 * 60 * 1000); // 30 minutes } if (stats.usage >= this.CRITICAL_THRESHOLD) return { level: 'critical', message: `Context usage critical (${(stats.usage * 100).toFixed(1)}%)`, usage: stats.usage }; if (stats.usage >= this.WARNING_THRESHOLD) return { level: 'warning', message: `High context usage (${(stats.usage * 100).toFixed(1)}%)`, usage: stats.usage }; return null; } estimateTokens(content) { // Simple token estimation - rough approximation for monitoring // Keep this simple and fast for real-time monitoring and test compatibility return Math.ceil(content.length / 4); } getStats(sessionId) { return this.sessions.get(sessionId) || null; } getAllStats() { return this.sessions; } clearSession(sessionId) { this.sessions.delete(sessionId); } getSessionSummary(sessionId) { const stats = this.sessions.get(sessionId); if (!stats) return `Session ${sessionId}: No data`; const usageKB = Math.round(stats.estimatedTokens / 1000); const limitKB = Math.round(this.CONTEXT_LIMIT / 1000); const percent = Math.round(stats.usage * 100); const status = stats.usage >= this.CRITICAL_THRESHOLD ? '[!]' : stats.usage >= this.WARNING_THRESHOLD ? '[WARNING]' : '[✓]'; const usageLabel = stats.usage >= this.CRITICAL_THRESHOLD ? 'CRITICAL' : stats.usage >= this.WARNING_THRESHOLD ? 'HIGH' : 'OK'; const turnsLabel = stats.turnCount === 1 ? '1 turn' : `${stats.turnCount} turns`; return `${status} Context: ${usageKB}K/${limitKB}K (${percent}%) | ${turnsLabel} | Status: ${usageLabel}`; } cleanupInactiveSessions(maxInactiveMs = 3600000) { const now = Date.now(); let removed = 0; for (const [sessionId, stats] of this.sessions.entries()) { if (now - stats.lastActivity > maxInactiveMs) { this.sessions.delete(sessionId); removed++; } } return removed; } resetSession(sessionId) { const stats = this.sessions.get(sessionId); if (stats) { stats.usage = 0; stats.estimatedTokens = 0; stats.messages = 0; stats.turnCount = 0; stats.lastActivity = Date.now(); stats.lastUpdate = new Date(); } } getMemoryStats() { let totalMessages = 0; let totalTokens = 0; for (const stats of this.sessions.values()) { totalMessages += stats.messages; totalTokens += stats.estimatedTokens; } return { activeSessions: this.sessions.size, totalMessages, totalTokens, averageTokensPerSession: this.sessions.size ? totalTokens / this.sessions.size : 0 }; } cleanupOldSessions(maxAgeMs) { return this.cleanupInactiveSessions(maxAgeMs); } destroy() { if (this.cleanupTimer) { clearInterval(this.cleanupTimer); this.cleanupTimer = undefined; } this.sessions.clear(); } } //# sourceMappingURL=context-monitor.js.map