import { createScopedLogger } from '~/utils/logger'; import { StreamingMessageParser, type StreamingMessageParserOptions } from './message-parser'; const logger = createScopedLogger('EnhancedMessageParser'); /** * Enhanced message parser that detects code blocks and file patterns * even when AI models don't wrap them in proper artifact tags. * Fixes issue #1797 where code outputs to chat instead of files. */ export class EnhancedStreamingMessageParser extends StreamingMessageParser { private _processedCodeBlocks = new Map>(); private _artifactCounter = 0; constructor(options: StreamingMessageParserOptions = {}) { super(options); } parse(messageId: string, input: string): string { // First try the normal parsing let output = super.parse(messageId, input); // If no artifacts were detected, check for code blocks that should be files if (!this._hasDetectedArtifacts(input)) { const enhancedInput = this._detectAndWrapCodeBlocks(messageId, input); if (enhancedInput !== input) { // Reset and reparse with enhanced input this.reset(); output = super.parse(messageId, enhancedInput); } } return output; } private _hasDetectedArtifacts(input: string): boolean { return input.includes(''); } private _detectAndWrapCodeBlocks(messageId: string, input: string): string { // Initialize processed blocks for this message if not exists if (!this._processedCodeBlocks.has(messageId)) { this._processedCodeBlocks.set(messageId, new Set()); } const processed = this._processedCodeBlocks.get(messageId)!; // Regex patterns for detecting code blocks with file indicators const patterns = [ // Pattern 1: Explicit file creation/modification mentions /(?:create|update|modify|edit|write|add|generate|here'?s?|file:?)\s+(?:a\s+)?(?:new\s+)?(?:file\s+)?(?:called\s+)?[`'"]*([\/\w\-\.]+\.\w+)[`'"]*:?\s*\n+```(\w*)\n([\s\S]*?)```/gi, // Pattern 2: Code blocks with filename comments /```(\w*)\n(?:\/\/|#|