@probelabs/probe 0.6.0-rc67 → 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.
- package/build/agent/ProbeAgent.js +43 -3
- package/build/agent/index.js +186 -46
- package/build/agent/schemaUtils.js +140 -10
- package/build/agent/tools.js +3 -2
- package/build/tools/common.js +62 -40
- package/cjs/agent/ProbeAgent.cjs +189 -49
- package/cjs/index.cjs +189 -49
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +43 -3
- package/src/agent/schemaUtils.js +140 -10
- package/src/agent/tools.js +3 -2
- package/src/tools/common.js +62 -40
|
@@ -580,8 +580,15 @@ When troubleshooting:
|
|
|
580
580
|
throw new Error(finalResult);
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
-
// Parse tool call from response
|
|
584
|
-
const
|
|
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,7 +729,8 @@ When troubleshooting:
|
|
|
722
729
|
this.tokenCounter.updateHistory(this.history);
|
|
723
730
|
|
|
724
731
|
// Schema handling - format response according to provided schema
|
|
725
|
-
|
|
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
|
}
|
|
@@ -822,6 +830,38 @@ Convert your previous response content into this JSON format now. Return nothing
|
|
|
822
830
|
console.error('[ERROR] Schema formatting failed:', error);
|
|
823
831
|
// Return the original result if schema formatting fails
|
|
824
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
|
+
}
|
|
825
865
|
}
|
|
826
866
|
|
|
827
867
|
return finalResult;
|
package/build/agent/index.js
CHANGED
|
@@ -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
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
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
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
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
|
|
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,6 +2297,16 @@ 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;
|
|
@@ -2594,9 +2617,65 @@ async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
2594
2617
|
};
|
|
2595
2618
|
}
|
|
2596
2619
|
if (debug) {
|
|
2597
|
-
console.error("[DEBUG] Invalid Mermaid diagrams detected,
|
|
2620
|
+
console.error("[DEBUG] Invalid Mermaid diagrams detected, trying HTML entity auto-fix first...");
|
|
2598
2621
|
}
|
|
2599
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
|
+
}
|
|
2600
2679
|
const mermaidFixer = new MermaidFixingAgent({
|
|
2601
2680
|
path: path6,
|
|
2602
2681
|
provider,
|
|
@@ -2604,11 +2683,10 @@ async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
2604
2683
|
debug,
|
|
2605
2684
|
tracer
|
|
2606
2685
|
});
|
|
2607
|
-
|
|
2608
|
-
const
|
|
2609
|
-
const
|
|
2610
|
-
const
|
|
2611
|
-
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) {
|
|
2612
2690
|
try {
|
|
2613
2691
|
const fixedContent = await mermaidFixer.fixMermaidDiagram(
|
|
2614
2692
|
invalidDiagram.content,
|
|
@@ -2616,7 +2694,7 @@ async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
2616
2694
|
{ diagramType: invalidDiagram.diagramType }
|
|
2617
2695
|
);
|
|
2618
2696
|
if (fixedContent && fixedContent !== invalidDiagram.content) {
|
|
2619
|
-
const originalDiagram =
|
|
2697
|
+
const originalDiagram = updatedDiagrams[invalidDiagram.originalIndex];
|
|
2620
2698
|
const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : "";
|
|
2621
2699
|
const newCodeBlock = `\`\`\`mermaid${attributesStr}
|
|
2622
2700
|
${fixedContent}
|
|
@@ -2677,10 +2755,18 @@ ${fixedContent}
|
|
|
2677
2755
|
};
|
|
2678
2756
|
}
|
|
2679
2757
|
}
|
|
2680
|
-
var MermaidFixingAgent;
|
|
2758
|
+
var HTML_ENTITY_MAP, MermaidFixingAgent;
|
|
2681
2759
|
var init_schemaUtils = __esm({
|
|
2682
2760
|
"src/agent/schemaUtils.js"() {
|
|
2683
2761
|
"use strict";
|
|
2762
|
+
HTML_ENTITY_MAP = {
|
|
2763
|
+
"<": "<",
|
|
2764
|
+
">": ">",
|
|
2765
|
+
"&": "&",
|
|
2766
|
+
""": '"',
|
|
2767
|
+
"'": "'",
|
|
2768
|
+
" ": " "
|
|
2769
|
+
};
|
|
2684
2770
|
MermaidFixingAgent = class {
|
|
2685
2771
|
constructor(options = {}) {
|
|
2686
2772
|
this.ProbeAgent = null;
|
|
@@ -2766,6 +2852,22 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
2766
2852
|
* @returns {Promise<string>} - The corrected Mermaid diagram
|
|
2767
2853
|
*/
|
|
2768
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
|
+
}
|
|
2769
2871
|
await this.initializeAgent();
|
|
2770
2872
|
const errorContext = originalErrors.length > 0 ? `
|
|
2771
2873
|
|
|
@@ -2773,11 +2875,12 @@ Detected errors: ${originalErrors.join(", ")}` : "";
|
|
|
2773
2875
|
const diagramTypeHint = diagramInfo.diagramType ? `
|
|
2774
2876
|
|
|
2775
2877
|
Expected diagram type: ${diagramInfo.diagramType}` : "";
|
|
2878
|
+
const contentToFix = decodedContent !== diagramContent ? decodedContent : diagramContent;
|
|
2776
2879
|
const prompt = `Analyze and fix the following Mermaid diagram.${errorContext}${diagramTypeHint}
|
|
2777
2880
|
|
|
2778
2881
|
Broken Mermaid diagram:
|
|
2779
2882
|
\`\`\`mermaid
|
|
2780
|
-
${
|
|
2883
|
+
${contentToFix}
|
|
2781
2884
|
\`\`\`
|
|
2782
2885
|
|
|
2783
2886
|
Provide only the corrected Mermaid diagram within a mermaid code block. Do not add any explanations or additional text.`;
|
|
@@ -3304,7 +3407,18 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
3304
3407
|
finalResult = `Error: Failed to get response from AI model during iteration ${currentIteration}. ${error.message}`;
|
|
3305
3408
|
throw new Error(finalResult);
|
|
3306
3409
|
}
|
|
3307
|
-
const
|
|
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);
|
|
3308
3422
|
if (parsedTool) {
|
|
3309
3423
|
const { toolName, params } = parsedTool;
|
|
3310
3424
|
if (this.debug) console.log(`[DEBUG] Parsed tool call: ${toolName} with params:`, params);
|
|
@@ -3423,7 +3537,7 @@ Error: Unknown tool '${toolName}'. Available tools: ${Object.keys(this.toolImple
|
|
|
3423
3537
|
}
|
|
3424
3538
|
}
|
|
3425
3539
|
this.tokenCounter.updateHistory(this.history);
|
|
3426
|
-
if (options.schema && !options._schemaFormatted) {
|
|
3540
|
+
if (options.schema && !options._schemaFormatted && !completionAttempted) {
|
|
3427
3541
|
if (this.debug) {
|
|
3428
3542
|
console.log("[DEBUG] Schema provided, applying automatic formatting...");
|
|
3429
3543
|
}
|
|
@@ -3503,6 +3617,32 @@ Convert your previous response content into this JSON format now. Return nothing
|
|
|
3503
3617
|
} catch (error) {
|
|
3504
3618
|
console.error("[ERROR] Schema formatting failed:", error);
|
|
3505
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
|
+
}
|
|
3506
3646
|
}
|
|
3507
3647
|
return finalResult;
|
|
3508
3648
|
} catch (error) {
|
|
@@ -3,6 +3,37 @@
|
|
|
3
3
|
* Supports JSON and Mermaid diagram validation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* HTML entity decoder map for common entities that might appear in mermaid diagrams
|
|
8
|
+
*/
|
|
9
|
+
const HTML_ENTITY_MAP = {
|
|
10
|
+
'<': '<',
|
|
11
|
+
'>': '>',
|
|
12
|
+
'&': '&',
|
|
13
|
+
'"': '"',
|
|
14
|
+
''': "'",
|
|
15
|
+
' ': ' '
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Decode HTML entities in text without requiring external dependencies
|
|
20
|
+
* @param {string} text - Text that may contain HTML entities
|
|
21
|
+
* @returns {string} - Text with HTML entities decoded
|
|
22
|
+
*/
|
|
23
|
+
export function decodeHtmlEntities(text) {
|
|
24
|
+
if (!text || typeof text !== 'string') {
|
|
25
|
+
return text;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let decoded = text;
|
|
29
|
+
for (const [entity, character] of Object.entries(HTML_ENTITY_MAP)) {
|
|
30
|
+
// Use global replacement to catch all instances
|
|
31
|
+
decoded = decoded.replace(new RegExp(entity, 'g'), character);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return decoded;
|
|
35
|
+
}
|
|
36
|
+
|
|
6
37
|
/**
|
|
7
38
|
* Clean AI response by extracting JSON content when response contains JSON
|
|
8
39
|
* Only processes responses that contain JSON structures { or [
|
|
@@ -644,6 +675,28 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
644
675
|
* @returns {Promise<string>} - The corrected Mermaid diagram
|
|
645
676
|
*/
|
|
646
677
|
async fixMermaidDiagram(diagramContent, originalErrors = [], diagramInfo = {}) {
|
|
678
|
+
// First, try auto-fixing HTML entities without AI
|
|
679
|
+
const decodedContent = decodeHtmlEntities(diagramContent);
|
|
680
|
+
|
|
681
|
+
// If HTML entity decoding changed the content, validate it first
|
|
682
|
+
if (decodedContent !== diagramContent) {
|
|
683
|
+
try {
|
|
684
|
+
const quickValidation = await validateMermaidDiagram(decodedContent);
|
|
685
|
+
if (quickValidation.isValid) {
|
|
686
|
+
// HTML entity decoding fixed the issue, no need for AI
|
|
687
|
+
if (this.options.debug) {
|
|
688
|
+
console.error('[DEBUG] Fixed Mermaid diagram with HTML entity decoding only');
|
|
689
|
+
}
|
|
690
|
+
return decodedContent;
|
|
691
|
+
}
|
|
692
|
+
} catch (error) {
|
|
693
|
+
// If validation fails, continue with AI fixing using decoded content
|
|
694
|
+
if (this.options.debug) {
|
|
695
|
+
console.error('[DEBUG] HTML entity decoding didn\'t fully fix diagram, continuing with AI fixing');
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
647
700
|
await this.initializeAgent();
|
|
648
701
|
|
|
649
702
|
const errorContext = originalErrors.length > 0
|
|
@@ -654,11 +707,14 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
654
707
|
? `\n\nExpected diagram type: ${diagramInfo.diagramType}`
|
|
655
708
|
: '';
|
|
656
709
|
|
|
710
|
+
// Use decoded content for AI fixing to ensure HTML entities are handled
|
|
711
|
+
const contentToFix = decodedContent !== diagramContent ? decodedContent : diagramContent;
|
|
712
|
+
|
|
657
713
|
const prompt = `Analyze and fix the following Mermaid diagram.${errorContext}${diagramTypeHint}
|
|
658
714
|
|
|
659
715
|
Broken Mermaid diagram:
|
|
660
716
|
\`\`\`mermaid
|
|
661
|
-
${
|
|
717
|
+
${contentToFix}
|
|
662
718
|
\`\`\`
|
|
663
719
|
|
|
664
720
|
Provide only the corrected Mermaid diagram within a mermaid code block. Do not add any explanations or additional text.`;
|
|
@@ -751,30 +807,104 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
751
807
|
};
|
|
752
808
|
}
|
|
753
809
|
|
|
754
|
-
// Some diagrams are invalid, try
|
|
810
|
+
// Some diagrams are invalid, first try HTML entity decoding auto-fix
|
|
755
811
|
if (debug) {
|
|
756
|
-
console.error('[DEBUG] Invalid Mermaid diagrams detected,
|
|
812
|
+
console.error('[DEBUG] Invalid Mermaid diagrams detected, trying HTML entity auto-fix first...');
|
|
757
813
|
}
|
|
758
814
|
|
|
759
815
|
try {
|
|
760
|
-
// Create specialized fixing agent
|
|
761
|
-
const mermaidFixer = new MermaidFixingAgent({
|
|
762
|
-
path, provider, model, debug, tracer
|
|
763
|
-
});
|
|
764
|
-
|
|
765
816
|
let fixedResponse = response;
|
|
766
817
|
const fixingResults = [];
|
|
818
|
+
let htmlEntityFixesApplied = false;
|
|
767
819
|
|
|
768
820
|
// Extract diagrams with position information for replacement
|
|
769
821
|
const { diagrams } = extractMermaidFromMarkdown(response);
|
|
770
822
|
|
|
771
|
-
//
|
|
823
|
+
// First pass: Try HTML entity decoding on invalid diagrams
|
|
772
824
|
const invalidDiagrams = validation.diagrams
|
|
773
825
|
.map((result, index) => ({ ...result, originalIndex: index }))
|
|
774
826
|
.filter(result => !result.isValid)
|
|
775
827
|
.reverse();
|
|
776
828
|
|
|
777
829
|
for (const invalidDiagram of invalidDiagrams) {
|
|
830
|
+
const originalContent = invalidDiagram.content;
|
|
831
|
+
const decodedContent = decodeHtmlEntities(originalContent);
|
|
832
|
+
|
|
833
|
+
if (decodedContent !== originalContent) {
|
|
834
|
+
// HTML entities were found and decoded, validate the result
|
|
835
|
+
try {
|
|
836
|
+
const quickValidation = await validateMermaidDiagram(decodedContent);
|
|
837
|
+
if (quickValidation.isValid) {
|
|
838
|
+
// HTML entity decoding fixed this diagram!
|
|
839
|
+
const originalDiagram = diagrams[invalidDiagram.originalIndex];
|
|
840
|
+
const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
|
|
841
|
+
const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${decodedContent}\n\`\`\``;
|
|
842
|
+
|
|
843
|
+
fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
|
|
844
|
+
newCodeBlock +
|
|
845
|
+
fixedResponse.slice(originalDiagram.endIndex);
|
|
846
|
+
|
|
847
|
+
fixingResults.push({
|
|
848
|
+
diagramIndex: invalidDiagram.originalIndex,
|
|
849
|
+
wasFixed: true,
|
|
850
|
+
originalContent: originalContent,
|
|
851
|
+
fixedContent: decodedContent,
|
|
852
|
+
originalError: invalidDiagram.error,
|
|
853
|
+
fixedWithHtmlDecoding: true
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
htmlEntityFixesApplied = true;
|
|
857
|
+
|
|
858
|
+
if (debug) {
|
|
859
|
+
console.error(`[DEBUG] Fixed diagram ${invalidDiagram.originalIndex + 1} with HTML entity decoding: ${invalidDiagram.error}`);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
} catch (error) {
|
|
863
|
+
if (debug) {
|
|
864
|
+
console.error(`[DEBUG] HTML entity decoding didn't fix diagram ${invalidDiagram.originalIndex + 1}: ${error.message}`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// If HTML entity fixes were applied, re-validate the entire response
|
|
871
|
+
if (htmlEntityFixesApplied) {
|
|
872
|
+
const revalidation = await validateMermaidResponse(fixedResponse);
|
|
873
|
+
if (revalidation.isValid) {
|
|
874
|
+
// All diagrams are now valid, return without AI fixing
|
|
875
|
+
if (debug) {
|
|
876
|
+
console.error('[DEBUG] All diagrams fixed with HTML entity decoding, no AI needed');
|
|
877
|
+
}
|
|
878
|
+
return {
|
|
879
|
+
...revalidation,
|
|
880
|
+
wasFixed: true,
|
|
881
|
+
originalResponse: response,
|
|
882
|
+
fixedResponse: fixedResponse,
|
|
883
|
+
fixingResults: fixingResults
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Still have invalid diagrams after HTML entity decoding, proceed with AI fixing
|
|
889
|
+
if (debug) {
|
|
890
|
+
console.error('[DEBUG] Some diagrams still invalid after HTML entity decoding, starting AI fixing...');
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// Create specialized fixing agent for remaining invalid diagrams
|
|
894
|
+
const mermaidFixer = new MermaidFixingAgent({
|
|
895
|
+
path, provider, model, debug, tracer
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
// Re-extract diagrams and re-validate after HTML entity fixes
|
|
899
|
+
const { diagrams: updatedDiagrams } = extractMermaidFromMarkdown(fixedResponse);
|
|
900
|
+
const updatedValidation = await validateMermaidResponse(fixedResponse);
|
|
901
|
+
|
|
902
|
+
const stillInvalidDiagrams = updatedValidation.diagrams
|
|
903
|
+
.map((result, index) => ({ ...result, originalIndex: index }))
|
|
904
|
+
.filter(result => !result.isValid)
|
|
905
|
+
.reverse();
|
|
906
|
+
|
|
907
|
+
for (const invalidDiagram of stillInvalidDiagrams) {
|
|
778
908
|
try {
|
|
779
909
|
const fixedContent = await mermaidFixer.fixMermaidDiagram(
|
|
780
910
|
invalidDiagram.content,
|
|
@@ -784,7 +914,7 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
784
914
|
|
|
785
915
|
if (fixedContent && fixedContent !== invalidDiagram.content) {
|
|
786
916
|
// Replace the diagram in the response
|
|
787
|
-
const originalDiagram =
|
|
917
|
+
const originalDiagram = updatedDiagrams[invalidDiagram.originalIndex];
|
|
788
918
|
const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
|
|
789
919
|
const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
|
|
790
920
|
|
package/build/agent/tools.js
CHANGED
|
@@ -124,9 +124,10 @@ User: Find all markdown files in the docs directory, but only at the top level.
|
|
|
124
124
|
* This function removes any <thinking></thinking> tags from the input string
|
|
125
125
|
* before passing it to the original parseXmlToolCall function
|
|
126
126
|
* @param {string} xmlString - The XML string to parse
|
|
127
|
+
* @param {string[]} [validTools] - List of valid tool names to parse (optional)
|
|
127
128
|
* @returns {Object|null} - The parsed tool call or null if no valid tool call found
|
|
128
129
|
*/
|
|
129
|
-
export function parseXmlToolCallWithThinking(xmlString) {
|
|
130
|
+
export function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
130
131
|
// Extract thinking content if present (for potential logging or analysis)
|
|
131
132
|
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
132
133
|
const thinkingContent = thinkingMatch ? thinkingMatch[1].trim() : null;
|
|
@@ -135,7 +136,7 @@ export function parseXmlToolCallWithThinking(xmlString) {
|
|
|
135
136
|
const cleanedXmlString = xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, '').trim();
|
|
136
137
|
|
|
137
138
|
// Use the original parseXmlToolCall function to parse the cleaned XML string
|
|
138
|
-
const parsedTool = parseXmlToolCall(cleanedXmlString);
|
|
139
|
+
const parsedTool = parseXmlToolCall(cleanedXmlString, validTools);
|
|
139
140
|
|
|
140
141
|
// If debugging is enabled, log the thinking content
|
|
141
142
|
if (process.env.DEBUG === '1' && thinkingContent) {
|