export interface ConsoleBufferMessage { id: string; type: "log" | "warn" | "error" | "info"; message: string; timestamp: number; context?: string; } export class ConsoleBuffer { private static instance: ConsoleBuffer | null = null; private messages: ConsoleBufferMessage[] = []; private maxMessages = 200; private lastReadTimestamp = 0; private executionContext: string | null = null; private messagesSinceLastTool: ConsoleBufferMessage[] = []; private constructor() {} static getInstance(): ConsoleBuffer { if (!ConsoleBuffer.instance) { ConsoleBuffer.instance = new ConsoleBuffer(); } return ConsoleBuffer.instance; } addMessage(message: ConsoleBufferMessage): void { if (this.executionContext) { message.context = this.executionContext; } this.messages.push(message); this.messagesSinceLastTool.push(message); if (this.messages.length > this.maxMessages) { this.messages = this.messages.slice(-this.maxMessages); } if (this.messagesSinceLastTool.length > 50) { this.messagesSinceLastTool = this.messagesSinceLastTool.slice(-50); } } getRecentMessages(since?: number, limit?: number): ConsoleBufferMessage[] { const sinceTimestamp = since || this.lastReadTimestamp; const filtered = this.messages.filter( (msg) => msg.timestamp > sinceTimestamp, ); if (limit && limit > 0 && filtered.length > limit) { return filtered.slice(-limit); } return filtered; } getAllMessages(limit?: number): ConsoleBufferMessage[] { if (limit && limit > 0 && this.messages.length > limit) { return this.messages.slice(-limit); } return [...this.messages]; } markAsRead(): void { if (this.messages.length > 0) { this.lastReadTimestamp = this.messages[this.messages.length - 1].timestamp; } } clear(): void { this.lastReadTimestamp = Date.now(); this.messagesSinceLastTool = []; } onGameReloadStart(): void { this.messagesSinceLastTool = []; this.lastReadTimestamp = Date.now(); this.addMessage({ id: `reload-${Date.now()}`, type: "info", message: "🔄 Game reloading...", timestamp: Date.now(), }); } onGameReloadComplete(): void { this.addMessage({ id: `reload-complete-${Date.now()}`, type: "info", message: "✅ Game reload complete", timestamp: Date.now(), }); } setExecutionContext(context: string | null): void { this.executionContext = context; } getMessagesSinceLastTool(): ConsoleBufferMessage[] { return [...this.messagesSinceLastTool]; } clearToolMessages(): void { this.messagesSinceLastTool = []; } getGameStateFromMessages(): { isLoading: boolean; hasError: boolean; lastError?: string; isReady: boolean; isReloading: boolean; messageCount: number; } { const recentMessages = this.messagesSinceLastTool.length > 0 ? this.messagesSinceLastTool : this.getRecentMessages(Date.now() - 5000); const hasReloadStartMessage = recentMessages.some((msg) => msg.message.includes("🔄 Game reloading"), ); const hasReloadCompleteMessage = recentMessages.some((msg) => msg.message.includes("✅ Game reload complete"), ); const hasStartMessage = recentMessages.some( (msg) => msg.message.includes("🎮 Starting game") || msg.message.includes("Starting game") || msg.message.includes("loading") || msg.message.includes("Loading"), ); const hasSuccessMessage = recentMessages.some( (msg) => msg.message.includes("✅ Game started") || msg.message.includes("Game started!") || msg.message.includes("Game script loaded!") || msg.message.includes("✅ Game reload complete") || msg.message.includes("successfully"), ); const errorMessages = recentMessages.filter( (msg) => msg.type === "error" || msg.message.includes("❌ Error") || msg.message.includes("Error:") || msg.message.includes("Failed") || msg.message.includes("failed"), ); const lastError = errorMessages.length > 0 ? errorMessages[errorMessages.length - 1].message : undefined; const isReloading = hasReloadStartMessage && !hasReloadCompleteMessage; return { isLoading: (hasStartMessage || isReloading) && !hasSuccessMessage && errorMessages.length === 0, hasError: errorMessages.length > 0, lastError, isReady: hasSuccessMessage && errorMessages.length === 0 && !isReloading, isReloading, messageCount: recentMessages.length, }; } } export const consoleBuffer = ConsoleBuffer.getInstance();