@probelabs/probe 0.6.0-rc66 → 0.6.0-rc69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -580,8 +580,15 @@ When troubleshooting:
580
580
  throw new Error(finalResult);
581
581
  }
582
582
 
583
- // Parse tool call from response
584
- const parsedTool = parseXmlToolCallWithThinking(assistantResponseContent);
583
+ // Parse tool call from response with valid tools list
584
+ const validTools = [
585
+ 'search', 'query', 'extract', 'listFiles', 'searchFiles', 'attempt_completion'
586
+ ];
587
+ if (this.allowEdit) {
588
+ validTools.push('implement');
589
+ }
590
+
591
+ const parsedTool = parseXmlToolCallWithThinking(assistantResponseContent, validTools);
585
592
  if (parsedTool) {
586
593
  const { toolName, params } = parsedTool;
587
594
  if (this.debug) console.log(`[DEBUG] Parsed tool call: ${toolName} with params:`, params);
@@ -722,14 +729,27 @@ When troubleshooting:
722
729
  this.tokenCounter.updateHistory(this.history);
723
730
 
724
731
  // Schema handling - format response according to provided schema
725
- if (options.schema && !options._schemaFormatted) {
732
+ // Skip schema processing if result came from attempt_completion tool
733
+ if (options.schema && !options._schemaFormatted && !completionAttempted) {
726
734
  if (this.debug) {
727
735
  console.log('[DEBUG] Schema provided, applying automatic formatting...');
728
736
  }
729
737
 
730
738
  try {
731
739
  // Step 1: Make a follow-up call to format according to schema
732
- const schemaPrompt = `Now you need to respond according to this schema:\n\n${options.schema}\n\nPlease reformat your previous response to match this schema exactly. Only return the formatted response, no additional text.`;
740
+ const schemaPrompt = `CRITICAL: You MUST respond with ONLY valid JSON that matches this schema. Ignore any previous response format or instructions.
741
+
742
+ Schema:
743
+ ${options.schema}
744
+
745
+ REQUIREMENTS:
746
+ - Return ONLY the JSON object/array
747
+ - NO additional text, explanations, or markdown
748
+ - NO code blocks or formatting
749
+ - The JSON must be parseable by JSON.parse()
750
+ - DO NOT include any conversational text like "Hello" or explanations
751
+
752
+ Convert your previous response content into this JSON format now. Return nothing but valid JSON.`;
733
753
 
734
754
  // Call answer recursively with _schemaFormatted flag to prevent infinite loop
735
755
  finalResult = await this.answer(schemaPrompt, [], {
@@ -770,18 +790,21 @@ When troubleshooting:
770
790
 
771
791
  // Step 4: Validate and potentially correct JSON responses
772
792
  if (isJsonSchema(options.schema)) {
773
- const validation = validateJsonResponse(finalResult);
793
+ let validation = validateJsonResponse(finalResult);
794
+ let retryCount = 0;
795
+ const maxRetries = 3;
774
796
 
775
- if (!validation.isValid) {
797
+ while (!validation.isValid && retryCount < maxRetries) {
776
798
  if (this.debug) {
777
- console.log('[DEBUG] JSON validation failed:', validation.error);
799
+ console.log(`[DEBUG] JSON validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
778
800
  }
779
801
 
780
- // Attempt correction once
802
+ // Create increasingly stronger correction prompts
781
803
  const correctionPrompt = createJsonCorrectionPrompt(
782
804
  finalResult,
783
805
  options.schema,
784
- validation.error
806
+ validation.error,
807
+ retryCount
785
808
  );
786
809
 
787
810
  finalResult = await this.answer(correctionPrompt, [], {
@@ -790,17 +813,55 @@ When troubleshooting:
790
813
  });
791
814
  finalResult = cleanSchemaResponse(finalResult);
792
815
 
793
- // Final validation
794
- const finalValidation = validateJsonResponse(finalResult);
795
- if (!finalValidation.isValid && this.debug) {
796
- console.log('[DEBUG] JSON still invalid after correction:', finalValidation.error);
816
+ // Validate the corrected response
817
+ validation = validateJsonResponse(finalResult);
818
+ retryCount++;
819
+
820
+ if (this.debug && !validation.isValid && retryCount < maxRetries) {
821
+ console.log(`[DEBUG] JSON still invalid after correction ${retryCount}, retrying...`);
797
822
  }
798
823
  }
824
+
825
+ if (!validation.isValid && this.debug) {
826
+ console.log(`[DEBUG] JSON still invalid after ${maxRetries} correction attempts:`, validation.error);
827
+ }
799
828
  }
800
829
  } catch (error) {
801
830
  console.error('[ERROR] Schema formatting failed:', error);
802
831
  // Return the original result if schema formatting fails
803
832
  }
833
+ } else if (completionAttempted && options.schema) {
834
+ // For attempt_completion results with schema, still clean markdown if needed
835
+ try {
836
+ finalResult = cleanSchemaResponse(finalResult);
837
+
838
+ // Validate and fix Mermaid diagrams if present
839
+ const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
840
+ debug: this.debug,
841
+ path: this.allowedFolders[0],
842
+ provider: this.clientApiProvider,
843
+ model: this.model
844
+ });
845
+
846
+ if (mermaidValidation.wasFixed) {
847
+ finalResult = mermaidValidation.fixedResponse;
848
+ if (this.debug) {
849
+ console.log(`[DEBUG] Mermaid diagrams fixed in attempt_completion result`);
850
+ }
851
+ }
852
+
853
+ // Validate JSON if schema expects JSON
854
+ if (isJsonSchema(options.schema)) {
855
+ const validation = validateJsonResponse(finalResult);
856
+ if (!validation.isValid && this.debug) {
857
+ console.log(`[DEBUG] attempt_completion result JSON validation failed: ${validation.error}`);
858
+ }
859
+ }
860
+ } catch (error) {
861
+ if (this.debug) {
862
+ console.log(`[DEBUG] attempt_completion result cleanup failed: ${error.message}`);
863
+ }
864
+ }
804
865
  }
805
866
 
806
867
  return finalResult;
@@ -1409,44 +1409,48 @@ var init_extract = __esm({
1409
1409
 
1410
1410
  // src/tools/common.js
1411
1411
  import { z } from "zod";
1412
- function parseXmlToolCall(xmlString) {
1413
- const toolMatch = xmlString.match(/<([a-zA-Z0-9_]+)>([\s\S]*?)<\/\1>/);
1414
- if (!toolMatch) {
1415
- return null;
1416
- }
1417
- const toolName = toolMatch[1];
1418
- const innerContent = toolMatch[2];
1419
- const params = {};
1420
- const paramRegex = /<([a-zA-Z0-9_]+)>([\s\S]*?)<\/\1>/g;
1421
- let paramMatch;
1422
- while ((paramMatch = paramRegex.exec(innerContent)) !== null) {
1423
- const paramName = paramMatch[1];
1424
- let paramValue = paramMatch[2].trim();
1425
- if (paramValue.toLowerCase() === "true") {
1426
- paramValue = true;
1427
- } else if (paramValue.toLowerCase() === "false") {
1428
- paramValue = false;
1429
- } else if (!isNaN(paramValue) && paramValue.trim() !== "") {
1430
- const num = Number(paramValue);
1431
- if (Number.isFinite(num)) {
1432
- paramValue = num;
1433
- }
1412
+ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
1413
+ const globalRegex = /<([a-zA-Z0-9_]+)>([\s\S]*?)<\/\1>/g;
1414
+ let match;
1415
+ while ((match = globalRegex.exec(xmlString)) !== null) {
1416
+ const toolName = match[1];
1417
+ if (!validTools.includes(toolName)) {
1418
+ continue;
1434
1419
  }
1435
- params[paramName] = paramValue;
1436
- }
1437
- if (toolName === "attempt_completion") {
1438
- const resultMatch = innerContent.match(/<result>([\s\S]*?)<\/result>/);
1439
- if (resultMatch) {
1440
- params["result"] = resultMatch[1].trim();
1420
+ const innerContent = match[2];
1421
+ const params = {};
1422
+ const paramRegex = /<([a-zA-Z0-9_]+)>([\s\S]*?)<\/\1>/g;
1423
+ let paramMatch;
1424
+ while ((paramMatch = paramRegex.exec(innerContent)) !== null) {
1425
+ const paramName = paramMatch[1];
1426
+ let paramValue = paramMatch[2].trim();
1427
+ if (paramValue.toLowerCase() === "true") {
1428
+ paramValue = true;
1429
+ } else if (paramValue.toLowerCase() === "false") {
1430
+ paramValue = false;
1431
+ } else if (!isNaN(paramValue) && paramValue.trim() !== "") {
1432
+ const num = Number(paramValue);
1433
+ if (Number.isFinite(num)) {
1434
+ paramValue = num;
1435
+ }
1436
+ }
1437
+ params[paramName] = paramValue;
1441
1438
  }
1442
- const commandMatch = innerContent.match(/<command>([\s\S]*?)<\/command>/);
1443
- if (commandMatch) {
1444
- params["command"] = commandMatch[1].trim();
1439
+ if (toolName === "attempt_completion") {
1440
+ const resultMatch = innerContent.match(/<result>([\s\S]*?)<\/result>/);
1441
+ if (resultMatch) {
1442
+ params["result"] = resultMatch[1].trim();
1443
+ }
1444
+ const commandMatch = innerContent.match(/<command>([\s\S]*?)<\/command>/);
1445
+ if (commandMatch) {
1446
+ params["command"] = commandMatch[1].trim();
1447
+ }
1445
1448
  }
1449
+ return { toolName, params };
1446
1450
  }
1447
- return { toolName, params };
1451
+ return null;
1448
1452
  }
1449
- var searchSchema, querySchema, extractSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, attemptCompletionToolDefinition, searchDescription, queryDescription, extractDescription;
1453
+ var searchSchema, querySchema, extractSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, attemptCompletionToolDefinition, searchDescription, queryDescription, extractDescription, DEFAULT_VALID_TOOLS;
1450
1454
  var init_common = __esm({
1451
1455
  "src/tools/common.js"() {
1452
1456
  "use strict";
@@ -1604,6 +1608,15 @@ Usage Example:
1604
1608
  searchDescription = "Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.";
1605
1609
  queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
1606
1610
  extractDescription = "Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.";
1611
+ DEFAULT_VALID_TOOLS = [
1612
+ "search",
1613
+ "query",
1614
+ "extract",
1615
+ "listFiles",
1616
+ "searchFiles",
1617
+ "implement",
1618
+ "attempt_completion"
1619
+ ];
1607
1620
  }
1608
1621
  });
1609
1622
 
@@ -2006,11 +2019,11 @@ function createTools(configOptions) {
2006
2019
  extractTool: extractTool(configOptions)
2007
2020
  };
2008
2021
  }
2009
- function parseXmlToolCallWithThinking(xmlString) {
2022
+ function parseXmlToolCallWithThinking(xmlString, validTools) {
2010
2023
  const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
2011
2024
  const thinkingContent = thinkingMatch ? thinkingMatch[1].trim() : null;
2012
2025
  const cleanedXmlString = xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, "").trim();
2013
- const parsedTool = parseXmlToolCall(cleanedXmlString);
2026
+ const parsedTool = parseXmlToolCall(cleanedXmlString, validTools);
2014
2027
  if (process.env.DEBUG === "1" && thinkingContent) {
2015
2028
  console.log(`[DEBUG] AI Thinking Process:
2016
2029
  ${thinkingContent}`);
@@ -2284,11 +2297,53 @@ var init_probeTool = __esm({
2284
2297
  });
2285
2298
 
2286
2299
  // src/agent/schemaUtils.js
2300
+ function decodeHtmlEntities(text) {
2301
+ if (!text || typeof text !== "string") {
2302
+ return text;
2303
+ }
2304
+ let decoded = text;
2305
+ for (const [entity, character] of Object.entries(HTML_ENTITY_MAP)) {
2306
+ decoded = decoded.replace(new RegExp(entity, "g"), character);
2307
+ }
2308
+ return decoded;
2309
+ }
2287
2310
  function cleanSchemaResponse(response) {
2288
2311
  if (!response || typeof response !== "string") {
2289
2312
  return response;
2290
2313
  }
2291
2314
  const trimmed = response.trim();
2315
+ const codeBlockPatterns = [
2316
+ /```json\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
2317
+ /```\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
2318
+ /`([{\[][\s\S]*?[}\]])`/
2319
+ ];
2320
+ for (const pattern of codeBlockPatterns) {
2321
+ const match = trimmed.match(pattern);
2322
+ if (match) {
2323
+ return match[1].trim();
2324
+ }
2325
+ }
2326
+ const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
2327
+ const codeBlockMatch = trimmed.match(codeBlockStartPattern);
2328
+ if (codeBlockMatch) {
2329
+ const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1;
2330
+ const openChar = codeBlockMatch[1];
2331
+ const closeChar = openChar === "{" ? "}" : "]";
2332
+ let bracketCount = 1;
2333
+ let endIndex = startIndex + 1;
2334
+ while (endIndex < trimmed.length && bracketCount > 0) {
2335
+ const char = trimmed[endIndex];
2336
+ if (char === openChar) {
2337
+ bracketCount++;
2338
+ } else if (char === closeChar) {
2339
+ bracketCount--;
2340
+ }
2341
+ endIndex++;
2342
+ }
2343
+ if (bracketCount === 0) {
2344
+ return trimmed.substring(startIndex, endIndex);
2345
+ }
2346
+ }
2292
2347
  const firstBracket = Math.min(
2293
2348
  trimmed.indexOf("{") >= 0 ? trimmed.indexOf("{") : Infinity,
2294
2349
  trimmed.indexOf("[") >= 0 ? trimmed.indexOf("[") : Infinity
@@ -2299,7 +2354,7 @@ function cleanSchemaResponse(response) {
2299
2354
  );
2300
2355
  if (firstBracket < Infinity && lastBracket >= 0 && firstBracket < lastBracket) {
2301
2356
  const beforeFirstBracket = trimmed.substring(0, firstBracket).trim();
2302
- if (beforeFirstBracket === "" || beforeFirstBracket.match(/^```\w*$/)) {
2357
+ if (beforeFirstBracket === "" || beforeFirstBracket.match(/^```\w*$/) || beforeFirstBracket.split("\n").length <= 2) {
2303
2358
  return trimmed.substring(firstBracket, lastBracket + 1);
2304
2359
  }
2305
2360
  }
@@ -2362,22 +2417,38 @@ function isJsonSchema(schema) {
2362
2417
  ];
2363
2418
  return jsonIndicators.some((indicator) => indicator);
2364
2419
  }
2365
- function createJsonCorrectionPrompt(invalidResponse, schema, error, detailedError = "") {
2366
- let prompt = `Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
2420
+ function createJsonCorrectionPrompt(invalidResponse, schema, error, retryCount = 0) {
2421
+ const strengthLevels = [
2422
+ {
2423
+ prefix: "CRITICAL JSON ERROR:",
2424
+ instruction: "You MUST fix this and return ONLY valid JSON.",
2425
+ emphasis: "Return ONLY the corrected JSON, with no additional text or markdown formatting."
2426
+ },
2427
+ {
2428
+ prefix: "URGENT - JSON PARSING FAILED:",
2429
+ instruction: "This is your second chance. Return ONLY valid JSON that can be parsed by JSON.parse().",
2430
+ emphasis: "ABSOLUTELY NO explanatory text, greetings, or formatting. ONLY JSON."
2431
+ },
2432
+ {
2433
+ prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
2434
+ instruction: "This is the final retry. You MUST return ONLY raw JSON without any other content.",
2435
+ emphasis: 'EXAMPLE: {"key": "value"} NOT: ```json{"key": "value"}``` NOT: Here is the JSON: {"key": "value"}'
2436
+ }
2437
+ ];
2438
+ const level = Math.min(retryCount, strengthLevels.length - 1);
2439
+ const currentLevel = strengthLevels[level];
2440
+ let prompt = `${currentLevel.prefix} Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
2367
2441
 
2368
- ${invalidResponse}
2442
+ ${invalidResponse.substring(0, 500)}${invalidResponse.length > 500 ? "..." : ""}
2369
2443
 
2370
- Error: ${error}`;
2371
- if (detailedError && detailedError !== error) {
2372
- prompt += `
2373
- Detailed Error: ${detailedError}`;
2374
- }
2375
- prompt += `
2444
+ Error: ${error}
2445
+
2446
+ ${currentLevel.instruction}
2376
2447
 
2377
- Please correct your response to be valid JSON that matches this schema:
2448
+ Schema to match:
2378
2449
  ${schema}
2379
2450
 
2380
- Return ONLY the corrected JSON, with no additional text or markdown formatting.`;
2451
+ ${currentLevel.emphasis}`;
2381
2452
  return prompt;
2382
2453
  }
2383
2454
  function extractMermaidFromMarkdown(response) {
@@ -2546,9 +2617,65 @@ async function validateAndFixMermaidResponse(response, options = {}) {
2546
2617
  };
2547
2618
  }
2548
2619
  if (debug) {
2549
- console.error("[DEBUG] Invalid Mermaid diagrams detected, starting specialized fixing agent...");
2620
+ console.error("[DEBUG] Invalid Mermaid diagrams detected, trying HTML entity auto-fix first...");
2550
2621
  }
2551
2622
  try {
2623
+ let fixedResponse = response;
2624
+ const fixingResults = [];
2625
+ let htmlEntityFixesApplied = false;
2626
+ const { diagrams } = extractMermaidFromMarkdown(response);
2627
+ const invalidDiagrams = validation.diagrams.map((result, index) => ({ ...result, originalIndex: index })).filter((result) => !result.isValid).reverse();
2628
+ for (const invalidDiagram of invalidDiagrams) {
2629
+ const originalContent = invalidDiagram.content;
2630
+ const decodedContent = decodeHtmlEntities(originalContent);
2631
+ if (decodedContent !== originalContent) {
2632
+ try {
2633
+ const quickValidation = await validateMermaidDiagram(decodedContent);
2634
+ if (quickValidation.isValid) {
2635
+ const originalDiagram = diagrams[invalidDiagram.originalIndex];
2636
+ const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : "";
2637
+ const newCodeBlock = `\`\`\`mermaid${attributesStr}
2638
+ ${decodedContent}
2639
+ \`\`\``;
2640
+ fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) + newCodeBlock + fixedResponse.slice(originalDiagram.endIndex);
2641
+ fixingResults.push({
2642
+ diagramIndex: invalidDiagram.originalIndex,
2643
+ wasFixed: true,
2644
+ originalContent,
2645
+ fixedContent: decodedContent,
2646
+ originalError: invalidDiagram.error,
2647
+ fixedWithHtmlDecoding: true
2648
+ });
2649
+ htmlEntityFixesApplied = true;
2650
+ if (debug) {
2651
+ console.error(`[DEBUG] Fixed diagram ${invalidDiagram.originalIndex + 1} with HTML entity decoding: ${invalidDiagram.error}`);
2652
+ }
2653
+ }
2654
+ } catch (error) {
2655
+ if (debug) {
2656
+ console.error(`[DEBUG] HTML entity decoding didn't fix diagram ${invalidDiagram.originalIndex + 1}: ${error.message}`);
2657
+ }
2658
+ }
2659
+ }
2660
+ }
2661
+ if (htmlEntityFixesApplied) {
2662
+ const revalidation = await validateMermaidResponse(fixedResponse);
2663
+ if (revalidation.isValid) {
2664
+ if (debug) {
2665
+ console.error("[DEBUG] All diagrams fixed with HTML entity decoding, no AI needed");
2666
+ }
2667
+ return {
2668
+ ...revalidation,
2669
+ wasFixed: true,
2670
+ originalResponse: response,
2671
+ fixedResponse,
2672
+ fixingResults
2673
+ };
2674
+ }
2675
+ }
2676
+ if (debug) {
2677
+ console.error("[DEBUG] Some diagrams still invalid after HTML entity decoding, starting AI fixing...");
2678
+ }
2552
2679
  const mermaidFixer = new MermaidFixingAgent({
2553
2680
  path: path6,
2554
2681
  provider,
@@ -2556,11 +2683,10 @@ async function validateAndFixMermaidResponse(response, options = {}) {
2556
2683
  debug,
2557
2684
  tracer
2558
2685
  });
2559
- let fixedResponse = response;
2560
- const fixingResults = [];
2561
- const { diagrams } = extractMermaidFromMarkdown(response);
2562
- const invalidDiagrams = validation.diagrams.map((result, index) => ({ ...result, originalIndex: index })).filter((result) => !result.isValid).reverse();
2563
- for (const invalidDiagram of invalidDiagrams) {
2686
+ const { diagrams: updatedDiagrams } = extractMermaidFromMarkdown(fixedResponse);
2687
+ const updatedValidation = await validateMermaidResponse(fixedResponse);
2688
+ const stillInvalidDiagrams = updatedValidation.diagrams.map((result, index) => ({ ...result, originalIndex: index })).filter((result) => !result.isValid).reverse();
2689
+ for (const invalidDiagram of stillInvalidDiagrams) {
2564
2690
  try {
2565
2691
  const fixedContent = await mermaidFixer.fixMermaidDiagram(
2566
2692
  invalidDiagram.content,
@@ -2568,7 +2694,7 @@ async function validateAndFixMermaidResponse(response, options = {}) {
2568
2694
  { diagramType: invalidDiagram.diagramType }
2569
2695
  );
2570
2696
  if (fixedContent && fixedContent !== invalidDiagram.content) {
2571
- const originalDiagram = diagrams[invalidDiagram.originalIndex];
2697
+ const originalDiagram = updatedDiagrams[invalidDiagram.originalIndex];
2572
2698
  const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : "";
2573
2699
  const newCodeBlock = `\`\`\`mermaid${attributesStr}
2574
2700
  ${fixedContent}
@@ -2629,10 +2755,18 @@ ${fixedContent}
2629
2755
  };
2630
2756
  }
2631
2757
  }
2632
- var MermaidFixingAgent;
2758
+ var HTML_ENTITY_MAP, MermaidFixingAgent;
2633
2759
  var init_schemaUtils = __esm({
2634
2760
  "src/agent/schemaUtils.js"() {
2635
2761
  "use strict";
2762
+ HTML_ENTITY_MAP = {
2763
+ "&lt;": "<",
2764
+ "&gt;": ">",
2765
+ "&amp;": "&",
2766
+ "&quot;": '"',
2767
+ "&#39;": "'",
2768
+ "&nbsp;": " "
2769
+ };
2636
2770
  MermaidFixingAgent = class {
2637
2771
  constructor(options = {}) {
2638
2772
  this.ProbeAgent = null;
@@ -2718,6 +2852,22 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
2718
2852
  * @returns {Promise<string>} - The corrected Mermaid diagram
2719
2853
  */
2720
2854
  async fixMermaidDiagram(diagramContent, originalErrors = [], diagramInfo = {}) {
2855
+ const decodedContent = decodeHtmlEntities(diagramContent);
2856
+ if (decodedContent !== diagramContent) {
2857
+ try {
2858
+ const quickValidation = await validateMermaidDiagram(decodedContent);
2859
+ if (quickValidation.isValid) {
2860
+ if (this.options.debug) {
2861
+ console.error("[DEBUG] Fixed Mermaid diagram with HTML entity decoding only");
2862
+ }
2863
+ return decodedContent;
2864
+ }
2865
+ } catch (error) {
2866
+ if (this.options.debug) {
2867
+ console.error("[DEBUG] HTML entity decoding didn't fully fix diagram, continuing with AI fixing");
2868
+ }
2869
+ }
2870
+ }
2721
2871
  await this.initializeAgent();
2722
2872
  const errorContext = originalErrors.length > 0 ? `
2723
2873
 
@@ -2725,11 +2875,12 @@ Detected errors: ${originalErrors.join(", ")}` : "";
2725
2875
  const diagramTypeHint = diagramInfo.diagramType ? `
2726
2876
 
2727
2877
  Expected diagram type: ${diagramInfo.diagramType}` : "";
2878
+ const contentToFix = decodedContent !== diagramContent ? decodedContent : diagramContent;
2728
2879
  const prompt = `Analyze and fix the following Mermaid diagram.${errorContext}${diagramTypeHint}
2729
2880
 
2730
2881
  Broken Mermaid diagram:
2731
2882
  \`\`\`mermaid
2732
- ${diagramContent}
2883
+ ${contentToFix}
2733
2884
  \`\`\`
2734
2885
 
2735
2886
  Provide only the corrected Mermaid diagram within a mermaid code block. Do not add any explanations or additional text.`;
@@ -3256,7 +3407,18 @@ You are working with a repository located at: ${searchDirectory}
3256
3407
  finalResult = `Error: Failed to get response from AI model during iteration ${currentIteration}. ${error.message}`;
3257
3408
  throw new Error(finalResult);
3258
3409
  }
3259
- const parsedTool = parseXmlToolCallWithThinking(assistantResponseContent);
3410
+ const validTools = [
3411
+ "search",
3412
+ "query",
3413
+ "extract",
3414
+ "listFiles",
3415
+ "searchFiles",
3416
+ "attempt_completion"
3417
+ ];
3418
+ if (this.allowEdit) {
3419
+ validTools.push("implement");
3420
+ }
3421
+ const parsedTool = parseXmlToolCallWithThinking(assistantResponseContent, validTools);
3260
3422
  if (parsedTool) {
3261
3423
  const { toolName, params } = parsedTool;
3262
3424
  if (this.debug) console.log(`[DEBUG] Parsed tool call: ${toolName} with params:`, params);
@@ -3375,16 +3537,24 @@ Error: Unknown tool '${toolName}'. Available tools: ${Object.keys(this.toolImple
3375
3537
  }
3376
3538
  }
3377
3539
  this.tokenCounter.updateHistory(this.history);
3378
- if (options.schema && !options._schemaFormatted) {
3540
+ if (options.schema && !options._schemaFormatted && !completionAttempted) {
3379
3541
  if (this.debug) {
3380
3542
  console.log("[DEBUG] Schema provided, applying automatic formatting...");
3381
3543
  }
3382
3544
  try {
3383
- const schemaPrompt = `Now you need to respond according to this schema:
3545
+ const schemaPrompt = `CRITICAL: You MUST respond with ONLY valid JSON that matches this schema. Ignore any previous response format or instructions.
3384
3546
 
3547
+ Schema:
3385
3548
  ${options.schema}
3386
3549
 
3387
- Please reformat your previous response to match this schema exactly. Only return the formatted response, no additional text.`;
3550
+ REQUIREMENTS:
3551
+ - Return ONLY the JSON object/array
3552
+ - NO additional text, explanations, or markdown
3553
+ - NO code blocks or formatting
3554
+ - The JSON must be parseable by JSON.parse()
3555
+ - DO NOT include any conversational text like "Hello" or explanations
3556
+
3557
+ Convert your previous response content into this JSON format now. Return nothing but valid JSON.`;
3388
3558
  finalResult = await this.answer(schemaPrompt, [], {
3389
3559
  ...options,
3390
3560
  _schemaFormatted: true
@@ -3416,30 +3586,63 @@ Please reformat your previous response to match this schema exactly. Only return
3416
3586
  }
3417
3587
  }
3418
3588
  if (isJsonSchema(options.schema)) {
3419
- const validation = validateJsonResponse(finalResult);
3420
- if (!validation.isValid) {
3589
+ let validation = validateJsonResponse(finalResult);
3590
+ let retryCount = 0;
3591
+ const maxRetries = 3;
3592
+ while (!validation.isValid && retryCount < maxRetries) {
3421
3593
  if (this.debug) {
3422
- console.log("[DEBUG] JSON validation failed:", validation.error);
3594
+ console.log(`[DEBUG] JSON validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
3423
3595
  }
3424
3596
  const correctionPrompt = createJsonCorrectionPrompt(
3425
3597
  finalResult,
3426
3598
  options.schema,
3427
- validation.error
3599
+ validation.error,
3600
+ retryCount
3428
3601
  );
3429
3602
  finalResult = await this.answer(correctionPrompt, [], {
3430
3603
  ...options,
3431
3604
  _schemaFormatted: true
3432
3605
  });
3433
3606
  finalResult = cleanSchemaResponse(finalResult);
3434
- const finalValidation = validateJsonResponse(finalResult);
3435
- if (!finalValidation.isValid && this.debug) {
3436
- console.log("[DEBUG] JSON still invalid after correction:", finalValidation.error);
3607
+ validation = validateJsonResponse(finalResult);
3608
+ retryCount++;
3609
+ if (this.debug && !validation.isValid && retryCount < maxRetries) {
3610
+ console.log(`[DEBUG] JSON still invalid after correction ${retryCount}, retrying...`);
3437
3611
  }
3438
3612
  }
3613
+ if (!validation.isValid && this.debug) {
3614
+ console.log(`[DEBUG] JSON still invalid after ${maxRetries} correction attempts:`, validation.error);
3615
+ }
3439
3616
  }
3440
3617
  } catch (error) {
3441
3618
  console.error("[ERROR] Schema formatting failed:", error);
3442
3619
  }
3620
+ } else if (completionAttempted && options.schema) {
3621
+ try {
3622
+ finalResult = cleanSchemaResponse(finalResult);
3623
+ const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
3624
+ debug: this.debug,
3625
+ path: this.allowedFolders[0],
3626
+ provider: this.clientApiProvider,
3627
+ model: this.model
3628
+ });
3629
+ if (mermaidValidation.wasFixed) {
3630
+ finalResult = mermaidValidation.fixedResponse;
3631
+ if (this.debug) {
3632
+ console.log(`[DEBUG] Mermaid diagrams fixed in attempt_completion result`);
3633
+ }
3634
+ }
3635
+ if (isJsonSchema(options.schema)) {
3636
+ const validation = validateJsonResponse(finalResult);
3637
+ if (!validation.isValid && this.debug) {
3638
+ console.log(`[DEBUG] attempt_completion result JSON validation failed: ${validation.error}`);
3639
+ }
3640
+ }
3641
+ } catch (error) {
3642
+ if (this.debug) {
3643
+ console.log(`[DEBUG] attempt_completion result cleanup failed: ${error.message}`);
3644
+ }
3645
+ }
3443
3646
  }
3444
3647
  return finalResult;
3445
3648
  } catch (error) {