export function parseJsonCandidate(candidate: string | null): unknown { if (candidate === null) return undefined; for (const text of candidateJsonTexts(candidate)) { try { return JSON.parse(text) as unknown; } catch { // Try the next lossless candidate shape. } } return undefined; } export function candidateJsonTexts(candidate: string): readonly string[] { const trimmed = candidate.trim(); const texts = [trimmed]; const fenced = trimmed.match(/^```(?:json)?\s*\n(?[\s\S]*?)\n```$/u)?.groups?.["body"]?.trim(); if (fenced) texts.push(fenced); const objectStart = trimmed.indexOf("{"); const objectEnd = trimmed.lastIndexOf("}"); if (objectStart !== -1 && objectEnd > objectStart) { texts.push(trimmed.slice(objectStart, objectEnd + 1)); } const arrayStart = trimmed.indexOf("["); const arrayEnd = trimmed.lastIndexOf("]"); if (arrayStart !== -1 && arrayEnd > arrayStart) { texts.push(trimmed.slice(arrayStart, arrayEnd + 1)); } return [...new Set(texts)]; }