@@ -160,6 +160,45 @@ function App() {
160160 return selectedModels [ defaultProvider ] || availableModels [ defaultProvider ] ?. [ 0 ] ?. id || "default" ;
161161 } ;
162162
163+ const extractJsonObject = ( raw ) => {
164+ if ( ! raw || typeof raw !== "string" ) return null ;
165+
166+ let text = raw . trim ( ) ;
167+
168+ // Remove markdown fences if the model returns ```json ... ```
169+ text = text . replace ( / ^ ` ` ` (?: j s o n ) ? \s * / i, "" ) . replace ( / ` ` ` $ / i, "" ) . trim ( ) ;
170+
171+ const first = text . indexOf ( "{" ) ;
172+ const last = text . lastIndexOf ( "}" ) ;
173+ if ( first === - 1 || last === - 1 || last <= first ) return null ;
174+
175+ let candidate = text . slice ( first , last + 1 ) ;
176+
177+ const tryParse = ( value ) => {
178+ try {
179+ return JSON . parse ( value ) ;
180+ } catch {
181+ return null ;
182+ }
183+ } ;
184+
185+ let parsed = tryParse ( candidate ) ;
186+ if ( parsed ) return parsed ;
187+
188+ // Common LLM issue on Windows paths: C:\Users\... creates invalid JSON escapes.
189+ // Escape only backslashes that are not valid JSON escape sequences.
190+ const escapedBackslashes = candidate . replace ( / \\ (? ! [ " \\ / b f n r t u ] ) / g, "\\\\" ) ;
191+ parsed = tryParse ( escapedBackslashes ) ;
192+ if ( parsed ) return parsed ;
193+
194+ // Common LLM issue: raw control characters inside strings.
195+ const cleanedControls = escapedBackslashes . replace ( / [ \u0000 - \u001F ] + / g, " " ) ;
196+ parsed = tryParse ( cleanedControls ) ;
197+ if ( parsed ) return parsed ;
198+
199+ return null ;
200+ } ;
201+
163202 // ============================================================
164203 // HANDLERS CON HISTORY
165204 // ============================================================
@@ -179,9 +218,8 @@ function App() {
179218 const model = getCurrentModel ( ) ;
180219 const response = await callAI ( defaultProvider , apiKey , prompt , model ) ;
181220
182- const jsonMatch = response . match ( / \{ [ \s \S ] * \} / ) ;
183- if ( jsonMatch ) {
184- const result = JSON . parse ( jsonMatch [ 0 ] ) ;
221+ const result = extractJsonObject ( response ) ;
222+ if ( result ) {
185223 showToast ( "Analisi completata!" , "success" ) ;
186224 history . addEntry ( {
187225 tool : 'logAnalyzer' ,
@@ -215,9 +253,8 @@ function App() {
215253 const model = getCurrentModel ( ) ;
216254 const response = await callAI ( defaultProvider , apiKey , prompt , model ) ;
217255
218- const jsonMatch = response . match ( / \{ [ \s \S ] * \} / ) ;
219- if ( jsonMatch ) {
220- const result = JSON . parse ( jsonMatch [ 0 ] ) ;
256+ const result = extractJsonObject ( response ) ;
257+ if ( result ) {
221258 showToast ( "Comando generato!" , "success" ) ;
222259 history . addEntry ( {
223260 tool : 'commandCrafter' ,
@@ -251,9 +288,8 @@ function App() {
251288 const model = getCurrentModel ( ) ;
252289 const response = await callAI ( defaultProvider , apiKey , prompt , model ) ;
253290
254- const jsonMatch = response . match ( / \{ [ \s \S ] * \} / ) ;
255- if ( jsonMatch ) {
256- const result = JSON . parse ( jsonMatch [ 0 ] ) ;
291+ const result = extractJsonObject ( response ) ;
292+ if ( result ) {
257293 showToast ( "Spiegazione completata!" , "success" ) ;
258294 history . addEntry ( {
259295 tool : 'explainMode' ,
@@ -287,9 +323,8 @@ function App() {
287323 const model = getCurrentModel ( ) ;
288324 const response = await callAI ( defaultProvider , apiKey , prompt , model ) ;
289325
290- const jsonMatch = response . match ( / \{ [ \s \S ] * \} / ) ;
291- if ( jsonMatch ) {
292- const result = JSON . parse ( jsonMatch [ 0 ] ) ;
326+ const result = extractJsonObject ( response ) ;
327+ if ( result ) {
293328 showToast ( "Configurazione generata!" , "success" ) ;
294329 history . addEntry ( {
295330 tool : 'configGenerator' ,
@@ -323,9 +358,8 @@ function App() {
323358 const model = getCurrentModel ( ) ;
324359 const response = await callAI ( defaultProvider , apiKey , prompt , model ) ;
325360
326- const jsonMatch = response . match ( / \{ [ \s \S ] * \} / ) ;
327- if ( jsonMatch ) {
328- const result = JSON . parse ( jsonMatch [ 0 ] ) ;
361+ const result = extractJsonObject ( response ) ;
362+ if ( result ) {
329363 showToast ( "Diagnosi completata!" , "success" ) ;
330364 history . addEntry ( {
331365 tool : 'troubleshooter' ,
@@ -389,7 +423,7 @@ function App() {
389423 const end = response . lastIndexOf ( '}' ) ;
390424 if ( start !== - 1 && end !== - 1 && end > start ) {
391425 try {
392- const parsed = JSON . parse ( response . substring ( start , end + 1 ) ) ;
426+ const parsed = extractJsonObject ( response . substring ( start , end + 1 ) ) ;
393427 if ( parsed . script ) {
394428 result = { ...result , ...parsed } ;
395429 showToast ( "Script generato!" , "success" ) ;
@@ -438,9 +472,8 @@ function App() {
438472 const model = getCurrentModel ( ) ;
439473 const response = await callAI ( defaultProvider , apiKey , prompt , model ) ;
440474
441- const jsonMatch = response . match ( / \{ [ \s \S ] * \} / ) ;
442- if ( jsonMatch ) {
443- const result = JSON . parse ( jsonMatch [ 0 ] ) ;
475+ const result = extractJsonObject ( response ) ;
476+ if ( result ) {
444477 showToast ( "Analisi sicurezza completata!" , "success" ) ;
445478 history . addEntry ( {
446479 tool : 'securityAuditor' ,
@@ -474,9 +507,8 @@ function App() {
474507 const model = getCurrentModel ( ) ;
475508 const response = await callAI ( defaultProvider , apiKey , prompt , model ) ;
476509
477- const jsonMatch = response . match ( / \{ [ \s \S ] * \} / ) ;
478- if ( jsonMatch ) {
479- const result = JSON . parse ( jsonMatch [ 0 ] ) ;
510+ const result = extractJsonObject ( response ) ;
511+ if ( result ) {
480512 showToast ( "Analisi scan completata!" , "success" ) ;
481513 history . addEntry ( {
482514 tool : 'securityAuditor' ,
0 commit comments