const fs = require('fs'); const content = fs.readFileSync('temp/rich-outline-fail_57_1779014018760.txt', 'utf-8'); const marker = '========== AI 原始响应 '; const idx = content.indexOf(marker); const start = content.indexOf('\n', idx) + 1; const end = content.indexOf('\n\n========== 响应结尾'); const aiResponse = content.substring(start, end).trim(); console.log('Length:', aiResponse.length); console.log('First 100:', JSON.stringify(aiResponse.substring(0, 100))); console.log('Last 100:', JSON.stringify(aiResponse.substring(aiResponse.length - 100))); const pos = 798; console.log('\n--- Error at position 798 ---'); console.log('Char at 798:', JSON.stringify(aiResponse[pos])); console.log('Char code at 798:', aiResponse.charCodeAt(798)); // Check if this is inside a string value (unescaped newline inside quotes) let inString = false; let escapeNext = false; let i = 0; const str = aiResponse; while (i < pos) { const ch = str[i]; if (escapeNext) { escapeNext = false; } else if (ch === '"') { inString = !inString; } else if (ch === '\\' && inString) { escapeNext = true; } i++; } console.log('Is inside string at pos 798:', inString); // Check if it's truncation console.log('\n--- Truncation check ---'); console.log('Ends with }:', aiResponse.trim().endsWith('}')); console.log('Ends with ,:', aiResponse.trim().endsWith(',')); // Find the actual last complete property const lastComma = aiResponse.lastIndexOf(','); const lastBrace = aiResponse.lastIndexOf('}'); const lastBracket = aiResponse.lastIndexOf(']'); console.log('Last comma pos:', lastComma, 'char:', JSON.stringify(aiResponse[lastComma])); console.log('Last brace pos:', lastBrace); console.log('Last bracket pos:', lastBracket); // Check the trailing content console.log('\n--- Trailing content ---'); console.log(JSON.stringify(aiResponse.substring(aiResponse.length - 200)));