@probelabs/probe 0.6.0-rc108 → 0.6.0-rc112

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.
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { createMessagePreview } from '../tools/common.js';
7
+ import { validate, fixText, extractMermaidBlocks } from '@probelabs/maid';
7
8
 
8
9
  /**
9
10
  * HTML entity decoder map for common entities that might appear in mermaid diagrams
@@ -482,8 +483,9 @@ export function extractMermaidFromMarkdown(response) {
482
483
 
483
484
  while ((match = mermaidBlockRegex.exec(response)) !== null) {
484
485
  const attributes = match[1] ? match[1].trim() : '';
485
- const fullContent = match[2].trim();
486
-
486
+ // Don't trim the content - maid 0.0.6 requires trailing newlines for sequence diagrams
487
+ const fullContent = match[2];
488
+
487
489
  // If attributes exist, they were captured separately, so fullContent is just the diagram
488
490
  // If no attributes, the first line of fullContent might be diagram type or actual content
489
491
  diagrams.push({
@@ -544,135 +546,47 @@ export async function validateMermaidDiagram(diagram) {
544
546
  }
545
547
 
546
548
  try {
547
- const trimmedDiagram = diagram.trim();
548
-
549
+ // Don't trim the diagram - maid 0.0.6 requires trailing newlines for sequence diagrams
550
+ // and handles leading/trailing whitespace correctly
551
+
549
552
  // Check for markdown code block markers
550
- if (trimmedDiagram.includes('```')) {
551
- return {
552
- isValid: false,
553
+ if (diagram.includes('```')) {
554
+ return {
555
+ isValid: false,
553
556
  error: 'Diagram contains markdown code block markers',
554
557
  detailedError: 'Mermaid diagram should not contain ``` markers when extracted from markdown'
555
558
  };
556
559
  }
557
560
 
558
- // Check for common mermaid diagram types (more flexible patterns)
559
- const diagramPatterns = [
560
- { pattern: /^(graph|flowchart)/i, type: 'flowchart' },
561
- { pattern: /^sequenceDiagram/i, type: 'sequence' },
562
- { pattern: /^gantt/i, type: 'gantt' },
563
- { pattern: /^pie/i, type: 'pie' },
564
- { pattern: /^stateDiagram/i, type: 'state' },
565
- { pattern: /^classDiagram/i, type: 'class' },
566
- { pattern: /^erDiagram/i, type: 'er' },
567
- { pattern: /^journey/i, type: 'journey' },
568
- { pattern: /^gitgraph/i, type: 'gitgraph' },
569
- { pattern: /^requirementDiagram/i, type: 'requirement' },
570
- { pattern: /^C4Context/i, type: 'c4' },
571
- ];
561
+ // Use maid to validate the diagram
562
+ const result = validate(diagram);
572
563
 
573
- // Find matching diagram type
574
- let diagramType = null;
575
- for (const { pattern, type } of diagramPatterns) {
576
- if (pattern.test(trimmedDiagram)) {
577
- diagramType = type;
578
- break;
579
- }
580
- }
581
-
582
- if (!diagramType) {
583
- return {
584
- isValid: false,
585
- error: 'Diagram does not match any known Mermaid diagram pattern',
586
- detailedError: 'The diagram must start with a valid Mermaid diagram type (graph, sequenceDiagram, gantt, pie, etc.)'
564
+ // Maid returns { type: string, errors: array }
565
+ // Valid if errors array is empty
566
+ if (result.errors && result.errors.length === 0) {
567
+ return {
568
+ isValid: true,
569
+ diagramType: result.type || 'unknown'
587
570
  };
588
- }
571
+ } else {
572
+ // Format maid errors into a readable error message
573
+ const errorMessages = (result.errors || []).map(err => {
574
+ const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ''}` : '';
575
+ return location ? `${location} - ${err.message}` : err.message;
576
+ });
589
577
 
590
- // GitHub-compatible strict syntax validation
591
- const lines = trimmedDiagram.split('\n');
592
-
593
- for (let i = 0; i < lines.length; i++) {
594
- const line = lines[i].trim();
595
- if (!line) continue;
596
-
597
- // Check for GitHub-incompatible patterns that cause "got 'PS'" errors
598
- if (diagramType === 'flowchart') {
599
- // Check for unbalanced brackets in node labels
600
- const brackets = line.match(/\[[^\]]*$/); // Unclosed bracket
601
- if (brackets) {
602
- return {
603
- isValid: false,
604
- error: `Unclosed bracket on line ${i + 1}`,
605
- detailedError: `Line "${line}" contains an unclosed bracket`
606
- };
607
- }
608
-
609
- // GitHub-strict: Check for parentheses inside node labels (causes PS token error)
610
- // But allow parentheses inside double-quoted strings
611
- const nodeWithParens = line.match(/\[[^"\[\]]*\([^"\[\]]*\]/);
612
- if (nodeWithParens) {
613
- return {
614
- isValid: false,
615
- error: `Parentheses in node label on line ${i + 1} (GitHub incompatible)`,
616
- detailedError: `Line "${line}" contains parentheses inside node label brackets. GitHub mermaid renderer fails with 'got PS' error. Use quotes or escape characters instead.`
617
- };
618
- }
619
-
620
- // GitHub-strict: Check for single quotes and backticks inside node labels (causes PS token error)
621
- const nodeWithQuotes = line.match(/\{[^{}]*['`][^{}]*\}|\[[^[\]]*['`][^[\]]*\]/);
622
- if (nodeWithQuotes) {
623
- const hasBacktick = line.includes('`');
624
- const quoteType = hasBacktick ? 'backticks' : 'single quotes';
625
- return {
626
- isValid: false,
627
- error: `${hasBacktick ? 'Backticks' : 'Single quotes'} in node label on line ${i + 1} (GitHub incompatible)`,
628
- detailedError: `Line "${line}" contains ${quoteType} inside node label. GitHub mermaid renderer fails with 'got PS' error. Use double quotes or escape characters instead.`
629
- };
630
- }
631
-
632
- // GitHub-strict: Check for complex expressions inside diamond nodes
633
- // Allow double-quoted strings in diamond nodes, but catch problematic single quotes and complex expressions
634
- // Allow HTML breaks (<br/>, <br>, etc.) but catch other problematic patterns
635
- const diamondWithComplexContent = line.match(/\{[^"{}]*[()'"<>&][^"{}]*\}/);
636
- const hasHtmlBreak = line.match(/\{[^{}]*<br\s*\/?>.*\}/);
637
- if (diamondWithComplexContent && !line.match(/\{\"[^\"]*\"\}/) && !hasHtmlBreak) {
638
- return {
639
- isValid: false,
640
- error: `Complex expression in diamond node on line ${i + 1} (GitHub incompatible)`,
641
- detailedError: `Line "${line}" contains special characters in diamond node that may cause GitHub parsing errors. Use simpler text or escape characters.`
642
- };
643
- }
644
-
645
- // GitHub-strict: Check for parentheses in subgraph labels (causes PS token error)
646
- if (line.startsWith('subgraph ') && line.match(/subgraph\s+[^"]*\([^"]*\)/)) {
647
- return {
648
- isValid: false,
649
- error: `Parentheses in subgraph label on line ${i + 1} (GitHub incompatible)`,
650
- detailedError: `Line "${line}" contains parentheses in subgraph label. GitHub mermaid renderer fails with 'got PS' error. Use quotes around the label or avoid parentheses.`
651
- };
652
- }
653
- }
654
-
655
- if (diagramType === 'sequence') {
656
- // Check for missing colon in sequence messages
657
- if (line.includes('->>') && !line.includes(':')) {
658
- return {
659
- isValid: false,
660
- error: `Missing colon in sequence message on line ${i + 1}`,
661
- detailedError: `Line "${line}" appears to be a sequence message but is missing a colon`
662
- };
663
- }
664
- }
578
+ return {
579
+ isValid: false,
580
+ diagramType: result.type || 'unknown',
581
+ error: errorMessages[0] || 'Validation failed',
582
+ detailedError: errorMessages.join('\n'),
583
+ errors: result.errors || [] // Include raw maid errors for AI fixing
584
+ };
665
585
  }
666
586
 
667
- // If we get here, basic validation passed
668
- return {
669
- isValid: true,
670
- diagramType
671
- };
672
-
673
587
  } catch (error) {
674
- return {
675
- isValid: false,
588
+ return {
589
+ isValid: false,
676
590
  error: error.message || 'Unknown mermaid parsing error',
677
591
  detailedError: error.stack || error.toString()
678
592
  };
@@ -758,6 +672,61 @@ Ensure all Mermaid diagrams are properly formatted within \`\`\`mermaid code blo
758
672
  return prompt;
759
673
  }
760
674
 
675
+ /**
676
+ * Use maid to attempt auto-fixing of mermaid diagrams
677
+ * @param {string} diagramContent - The diagram content to fix
678
+ * @param {Object} options - Fix options
679
+ * @returns {Object} - {fixed: string, wasFixed: boolean, errors: Array}
680
+ */
681
+ export async function tryMaidAutoFix(diagramContent, options = {}) {
682
+ const { debug = false } = options;
683
+
684
+ try {
685
+ // Always use 'all' level fixes (most aggressive)
686
+ if (debug) {
687
+ console.log(`[DEBUG] Mermaid maid: Trying 'all' level auto-fixes...`);
688
+ }
689
+ const result = fixText(diagramContent, { level: 'all' });
690
+ const validation = validate(result.fixed);
691
+
692
+ // Maid validation returns { type, errors }
693
+ // Valid if errors array is empty
694
+ if (validation.errors && validation.errors.length === 0) {
695
+ if (debug) {
696
+ console.log(`[DEBUG] Mermaid maid: 'All' level fixes succeeded`);
697
+ }
698
+ return {
699
+ fixed: result.fixed,
700
+ wasFixed: result.fixed !== diagramContent,
701
+ errors: [],
702
+ fixLevel: 'all'
703
+ };
704
+ }
705
+
706
+ // Maid couldn't fix it completely, return the best attempt with remaining errors
707
+ if (debug) {
708
+ console.log(`[DEBUG] Mermaid maid: Auto-fixes couldn't resolve all issues, ${validation.errors?.length || 0} errors remain`);
709
+ }
710
+ return {
711
+ fixed: result.fixed,
712
+ wasFixed: result.fixed !== diagramContent,
713
+ errors: validation.errors || [], // Pass maid's structured errors for AI fixing
714
+ fixLevel: 'all'
715
+ };
716
+
717
+ } catch (error) {
718
+ if (debug) {
719
+ console.error(`[DEBUG] Mermaid maid: Auto-fix error: ${error.message}`);
720
+ }
721
+ return {
722
+ fixed: diagramContent,
723
+ wasFixed: false,
724
+ errors: [{ message: error.message }],
725
+ fixLevel: null
726
+ };
727
+ }
728
+ }
729
+
761
730
  /**
762
731
  * Specialized Mermaid diagram fixing agent
763
732
  * Uses a separate ProbeAgent instance optimized for Mermaid syntax correction
@@ -881,17 +850,30 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
881
850
 
882
851
  await this.initializeAgent();
883
852
 
884
- const errorContext = originalErrors.length > 0
885
- ? `\n\nDetected errors: ${originalErrors.join(', ')}`
886
- : '';
853
+ // Format error context - handle both simple strings and maid's structured errors
854
+ let errorContext = '';
855
+ if (originalErrors.length > 0) {
856
+ const formattedErrors = originalErrors.map(err => {
857
+ // Check if this is a maid structured error object
858
+ if (typeof err === 'object' && err.message) {
859
+ const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ''}` : '';
860
+ const hint = err.hint ? `\n Hint: ${err.hint}` : '';
861
+ return location ? `- ${location}: ${err.message}${hint}` : `- ${err.message}${hint}`;
862
+ }
863
+ // Handle simple string errors
864
+ return `- ${err}`;
865
+ }).join('\n');
866
+
867
+ errorContext = `\n\nDetected errors:\n${formattedErrors}`;
868
+ }
887
869
 
888
- const diagramTypeHint = diagramInfo.diagramType
889
- ? `\n\nExpected diagram type: ${diagramInfo.diagramType}`
870
+ const diagramTypeHint = diagramInfo.diagramType
871
+ ? `\n\nExpected diagram type: ${diagramInfo.diagramType}`
890
872
  : '';
891
873
 
892
874
  // Use decoded content for AI fixing to ensure HTML entities are handled
893
875
  const contentToFix = decodedContent !== diagramContent ? decodedContent : diagramContent;
894
-
876
+
895
877
  const prompt = `Analyze and fix the following Mermaid diagram.${errorContext}${diagramTypeHint}
896
878
 
897
879
  Broken Mermaid diagram:
@@ -958,54 +940,35 @@ Provide only the corrected Mermaid diagram within a mermaid code block. Do not a
958
940
  }
959
941
 
960
942
  /**
961
- * Enhanced Mermaid validation with specialized agent fixing
962
- * @param {string} response - Response that may contain mermaid diagrams
963
- * @param {Object} options - Options for validation and fixing
964
- * @returns {Promise<Object>} - Enhanced validation result with fixing capability
943
+ * Validate and fix Mermaid diagrams using maid
944
+ * Replaces old manual fix logic with maid's auto-fix capabilities
945
+ * @param {string} response - Response containing mermaid diagrams
946
+ * @param {Object} options - Validation options
947
+ * @returns {Promise<Object>} - Validation and fixing results
965
948
  */
966
949
  export async function validateAndFixMermaidResponse(response, options = {}) {
967
950
  const { schema, debug, path, provider, model, tracer } = options;
968
951
  const startTime = Date.now();
969
-
952
+
970
953
  if (debug) {
971
- console.log(`[DEBUG] Mermaid validation: Starting enhanced validation for response (${response.length} chars)`);
972
- console.log(`[DEBUG] Mermaid validation: Options - path: ${path}, provider: ${provider}, model: ${model}`);
954
+ console.log(`[DEBUG] Mermaid validation: Starting maid-based validation for response (${response.length} chars)`);
973
955
  }
974
956
 
975
- /**
976
- * Helper function to determine if node content needs quoting due to problematic characters
977
- * @param {string} content - The node content to check
978
- * @returns {boolean} - True if content needs to be quoted
979
- */
980
- const needsQuoting = (content) => {
981
- return /[()'"<>&`]/.test(content) || // Core problematic characters
982
- content.includes('e.g.') ||
983
- content.includes('i.e.') ||
984
- content.includes('src/') ||
985
- content.includes('defaults/') ||
986
- content.includes('.ts') ||
987
- content.includes('.js') ||
988
- content.includes('.yaml') ||
989
- content.includes('.json') ||
990
- content.includes('.md') ||
991
- content.includes('.html') ||
992
- content.includes('.css');
993
- };
994
-
995
957
  // Record mermaid validation start in telemetry
996
958
  if (tracer) {
997
959
  tracer.recordMermaidValidationEvent('started', {
998
960
  'mermaid_validation.response_length': response.length,
999
961
  'mermaid_validation.provider': provider,
1000
- 'mermaid_validation.model': model
962
+ 'mermaid_validation.model': model,
963
+ 'mermaid_validation.method': 'maid'
1001
964
  });
1002
965
  }
1003
-
966
+
1004
967
  // First, run standard validation
1005
968
  const validationStart = Date.now();
1006
969
  const validation = await validateMermaidResponse(response);
1007
970
  const validationTime = Date.now() - validationStart;
1008
-
971
+
1009
972
  if (debug) {
1010
973
  console.log(`[DEBUG] Mermaid validation: Initial validation completed in ${validationTime}ms`);
1011
974
  console.log(`[DEBUG] Mermaid validation: Found ${validation.diagrams?.length || 0} diagrams, valid: ${validation.isValid}`);
@@ -1018,24 +981,13 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1018
981
  });
1019
982
  }
1020
983
  }
1021
-
1022
- // Always check for HTML entities, even if diagrams are technically valid
1023
- let needsHtmlEntityCheck = false;
1024
- if (validation.diagrams && validation.diagrams.length > 0) {
1025
- for (const diagram of validation.diagrams) {
1026
- if (diagram.content && (diagram.content.includes('&lt;') || diagram.content.includes('&gt;') || diagram.content.includes('&amp;') || diagram.content.includes('&quot;') || diagram.content.includes('&#39;'))) {
1027
- needsHtmlEntityCheck = true;
1028
- break;
1029
- }
1030
- }
1031
- }
1032
984
 
1033
- if (validation.isValid && !needsHtmlEntityCheck) {
985
+ // If all valid, return early
986
+ if (validation.isValid) {
1034
987
  if (debug) {
1035
- console.log(`[DEBUG] Mermaid validation: All diagrams valid and no HTML entities found, no fixing needed`);
988
+ console.log(`[DEBUG] Mermaid validation: All diagrams valid, no fixing needed`);
1036
989
  }
1037
-
1038
- // Record successful validation in telemetry
990
+
1039
991
  if (tracer) {
1040
992
  tracer.recordMermaidValidationEvent('completed', {
1041
993
  'mermaid_validation.success': true,
@@ -1044,8 +996,7 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1044
996
  'mermaid_validation.duration_ms': Date.now() - startTime
1045
997
  });
1046
998
  }
1047
-
1048
- // All diagrams are valid and no HTML entities found, no fixing needed
999
+
1049
1000
  return {
1050
1001
  ...validation,
1051
1002
  wasFixed: false,
@@ -1054,10 +1005,10 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1054
1005
  };
1055
1006
  }
1056
1007
 
1057
- // If no diagrams found at all, return without attempting to fix
1008
+ // If no diagrams found, return without fixing
1058
1009
  if (!validation.diagrams || validation.diagrams.length === 0) {
1059
1010
  if (debug) {
1060
- console.log(`[DEBUG] Mermaid validation: No mermaid diagrams found in response, skipping fixes`);
1011
+ console.log(`[DEBUG] Mermaid validation: No mermaid diagrams found in response`);
1061
1012
  }
1062
1013
  return {
1063
1014
  ...validation,
@@ -1067,596 +1018,147 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1067
1018
  };
1068
1019
  }
1069
1020
 
1070
- // Try HTML entity decoding auto-fix (for both invalid diagrams and valid diagrams with HTML entities)
1021
+ // Try maid auto-fix for invalid diagrams
1071
1022
  const invalidCount = validation.diagrams.filter(d => !d.isValid).length;
1072
1023
  if (debug) {
1073
- if (invalidCount > 0) {
1074
- console.log(`[DEBUG] Mermaid validation: ${invalidCount} invalid diagrams detected, trying HTML entity auto-fix first...`);
1075
- } else {
1076
- console.log(`[DEBUG] Mermaid validation: Diagrams are valid but HTML entities detected, applying HTML entity auto-fix...`);
1077
- }
1024
+ console.log(`[DEBUG] Mermaid validation: ${invalidCount} invalid diagrams, trying maid auto-fix...`);
1078
1025
  }
1079
1026
 
1080
1027
  try {
1081
1028
  let fixedResponse = response;
1082
1029
  const fixingResults = [];
1083
- let htmlEntityFixesApplied = false;
1084
-
1085
- // Extract diagrams with position information for replacement
1030
+ let maidFixesApplied = false;
1031
+
1032
+ // Extract diagrams with position information
1086
1033
  const { diagrams } = extractMermaidFromMarkdown(response);
1087
-
1088
- // First pass: Try HTML entity decoding on ALL diagrams (not just invalid ones)
1089
- // HTML entities in mermaid diagrams are almost always unintended, even if the diagram is technically valid
1090
- const allDiagrams = validation.diagrams
1034
+
1035
+ // Process invalid diagrams in reverse to maintain indices
1036
+ const invalidDiagrams = validation.diagrams
1091
1037
  .map((result, index) => ({ ...result, originalIndex: index }))
1038
+ .filter(result => !result.isValid)
1092
1039
  .reverse();
1093
1040
 
1094
- for (const diagram of allDiagrams) {
1095
- const originalContent = diagram.content;
1096
- const decodedContent = decodeHtmlEntities(originalContent);
1097
-
1098
- if (decodedContent !== originalContent) {
1099
- // HTML entities were found and decoded, validate the result
1100
- try {
1101
- const quickValidation = await validateMermaidDiagram(decodedContent);
1102
- if (quickValidation.isValid) {
1103
- // HTML entity decoding improved this diagram!
1104
- const originalDiagram = diagrams[diagram.originalIndex];
1105
- const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
1106
- const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${decodedContent}\n\`\`\``;
1107
-
1108
- fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
1109
- newCodeBlock +
1110
- fixedResponse.slice(originalDiagram.endIndex);
1111
-
1112
- fixingResults.push({
1113
- diagramIndex: diagram.originalIndex,
1114
- wasFixed: true,
1115
- originalContent: originalContent,
1116
- fixedContent: decodedContent,
1117
- originalError: diagram.error || 'HTML entity cleanup',
1118
- fixedWithHtmlDecoding: true
1119
- });
1120
-
1121
- htmlEntityFixesApplied = true;
1122
-
1123
- if (debug) {
1124
- console.log(`[DEBUG] Mermaid validation: Fixed diagram ${diagram.originalIndex + 1} with HTML entity decoding`);
1125
- console.log(`[DEBUG] Mermaid validation: Original status: ${diagram.isValid ? 'valid' : 'invalid'} - ${diagram.error || 'no error'}`);
1126
- console.log(`[DEBUG] Mermaid validation: Decoded HTML entities`);
1127
- }
1128
- }
1129
- } catch (error) {
1130
- if (debug) {
1131
- console.log(`[DEBUG] Mermaid validation: HTML entity decoding didn't improve diagram ${diagram.originalIndex + 1}: ${error.message}`);
1132
- }
1133
- }
1134
- }
1135
- }
1136
-
1137
- // If HTML entity fixes were applied, re-validate the entire response
1138
- if (htmlEntityFixesApplied) {
1139
- const revalidation = await validateMermaidResponse(fixedResponse);
1140
- if (revalidation.isValid) {
1141
- // All diagrams are now valid, return without AI fixing
1142
- const totalTime = Date.now() - startTime;
1143
- if (debug) {
1144
- console.log(`[DEBUG] Mermaid validation: All diagrams fixed with HTML entity decoding in ${totalTime}ms, no AI needed`);
1145
- console.log(`[DEBUG] Mermaid validation: Applied ${fixingResults.length} HTML entity fixes`);
1146
- }
1147
-
1148
- // Record HTML entity fix success in telemetry
1149
- if (tracer) {
1150
- tracer.recordMermaidValidationEvent('html_fix_completed', {
1151
- 'mermaid_validation.success': true,
1152
- 'mermaid_validation.fix_method': 'html_entity_decoding',
1153
- 'mermaid_validation.diagrams_fixed': fixingResults.length,
1154
- 'mermaid_validation.duration_ms': totalTime
1155
- });
1156
- }
1157
- return {
1158
- ...revalidation,
1159
- wasFixed: true,
1160
- originalResponse: response,
1161
- fixedResponse: fixedResponse,
1162
- fixingResults: fixingResults
1163
- };
1164
- }
1165
- }
1166
-
1167
- // Proactive pass: Fix common node label issues in ALL diagrams (not just invalid ones)
1168
- let proactiveFixesApplied = false;
1169
-
1170
- // Re-extract diagrams after HTML entity fixes
1171
- const { diagrams: currentDiagrams } = extractMermaidFromMarkdown(fixedResponse);
1172
-
1173
- for (let diagramIndex = currentDiagrams.length - 1; diagramIndex >= 0; diagramIndex--) {
1174
- const diagram = currentDiagrams[diagramIndex];
1175
- const originalContent = diagram.content;
1176
- const lines = originalContent.split('\n');
1177
- let wasFixed = false;
1178
-
1179
- // Proactively fix node labels that contain special characters
1180
- const fixedLines = lines.map(line => {
1181
- const trimmedLine = line.trim();
1182
- let modifiedLine = line;
1183
-
1184
- // Enhanced auto-fixing for square bracket nodes [...]
1185
- if (trimmedLine.match(/\[[^\]]*\]/)) {
1186
- modifiedLine = modifiedLine.replace(/\[([^\]]*)\]/g, (match, content) => {
1187
- // Check if already properly quoted with outer quotes
1188
- if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
1189
- // Extract the inner content (between the outer quotes)
1190
- const innerContent = content.trim().slice(1, -1);
1191
- // Check if inner content has unescaped quotes that need escaping
1192
- if (innerContent.includes('"') || innerContent.includes("'")) {
1193
- wasFixed = true;
1194
- // Decode any existing HTML entities first, then re-encode ALL quotes
1195
- const decodedContent = decodeHtmlEntities(innerContent);
1196
- const safeContent = decodedContent
1197
- .replace(/"/g, '&quot;') // Replace ALL double quotes with HTML entity
1198
- .replace(/'/g, '&#39;'); // Replace ALL single quotes with HTML entity
1199
- return `["${safeContent}"]`;
1200
- }
1201
- return match;
1202
- }
1203
-
1204
- // Check if content needs quoting (contains problematic patterns)
1205
- if (needsQuoting(content)) {
1206
- wasFixed = true;
1207
- // Use HTML entities for quotes as per Mermaid best practices:
1208
- // - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
1209
- // - HTML entities are the official way to escape quotes in Mermaid
1210
- // - Always use double quotes with square brackets ["..."] for node labels
1211
- // IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
1212
- const decodedContent = decodeHtmlEntities(content);
1213
- const safeContent = decodedContent
1214
- .replace(/"/g, '&quot;') // Replace double quotes with HTML entity
1215
- .replace(/'/g, '&#39;'); // Replace single quotes with HTML entity
1216
- return `["${safeContent}"]`;
1217
- }
1218
-
1219
- return match;
1220
- });
1221
- }
1222
-
1223
- // Enhanced auto-fixing for diamond nodes {...}
1224
- if (trimmedLine.match(/\{[^{}]*\}/)) {
1225
- modifiedLine = modifiedLine.replace(/\{([^{}]*)\}/g, (match, content) => {
1226
- // Check if already properly quoted with outer quotes
1227
- if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
1228
- // Extract the inner content (between the outer quotes)
1229
- const innerContent = content.trim().slice(1, -1);
1230
- // Check if inner content has unescaped quotes that need escaping
1231
- if (innerContent.includes('"') || innerContent.includes("'")) {
1232
- wasFixed = true;
1233
- // Decode any existing HTML entities first, then re-encode ALL quotes
1234
- const decodedContent = decodeHtmlEntities(innerContent);
1235
- const safeContent = decodedContent
1236
- .replace(/"/g, '&quot;') // Replace ALL double quotes with HTML entity
1237
- .replace(/'/g, '&#39;'); // Replace ALL single quotes with HTML entity
1238
- return `{"${safeContent}"}`;
1239
- }
1240
- return match;
1241
- }
1242
-
1243
- // Check if content needs quoting (contains problematic patterns)
1244
- if (needsQuoting(content)) {
1245
- wasFixed = true;
1246
- // Use HTML entities for quotes as per Mermaid best practices:
1247
- // - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
1248
- // - HTML entities are the official way to escape quotes in Mermaid
1249
- // - Always use double quotes with curly brackets {"..."} for diamond nodes
1250
- // IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
1251
- const decodedContent = decodeHtmlEntities(content);
1252
- const safeContent = decodedContent
1253
- .replace(/"/g, '&quot;') // Replace double quotes with HTML entity
1254
- .replace(/'/g, '&#39;'); // Replace single quotes with HTML entity
1255
- return `{"${safeContent}"}`;
1256
- }
1257
-
1258
- return match;
1259
- });
1260
- }
1261
-
1262
- return modifiedLine;
1263
- });
1264
-
1265
- if (wasFixed) {
1266
- const fixedContent = fixedLines.join('\n');
1267
-
1268
- // Replace the diagram in the response
1269
- const attributesStr = diagram.attributes ? ` ${diagram.attributes}` : '';
1270
- const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
1271
-
1272
- fixedResponse = fixedResponse.slice(0, diagram.startIndex) +
1273
- newCodeBlock +
1274
- fixedResponse.slice(diagram.endIndex);
1275
-
1041
+ for (const invalidDiagram of invalidDiagrams) {
1042
+ const originalContent = invalidDiagram.content;
1043
+
1044
+ // Try maid auto-fix
1045
+ const maidResult = await tryMaidAutoFix(originalContent, { debug });
1046
+
1047
+ if (maidResult.errors.length === 0) {
1048
+ // Maid fixed it completely
1049
+ const originalDiagram = diagrams[invalidDiagram.originalIndex];
1050
+ const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
1051
+ const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${maidResult.fixed}\n\`\`\``;
1052
+
1053
+ fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
1054
+ newCodeBlock +
1055
+ fixedResponse.slice(originalDiagram.endIndex);
1056
+
1276
1057
  fixingResults.push({
1277
- diagramIndex: diagramIndex,
1058
+ diagramIndex: invalidDiagram.originalIndex,
1278
1059
  wasFixed: true,
1279
1060
  originalContent: originalContent,
1280
- fixedContent: fixedContent,
1281
- originalError: 'Proactive node label quoting',
1282
- fixMethod: 'node_label_quote_wrapping',
1283
- fixedWithProactiveQuoting: true
1061
+ fixedContent: maidResult.fixed,
1062
+ originalError: invalidDiagram.error,
1063
+ fixedWithMaid: true,
1064
+ fixLevel: maidResult.fixLevel
1284
1065
  });
1285
-
1286
- proactiveFixesApplied = true;
1287
-
1288
- if (debug) {
1289
- console.log(`[DEBUG] Mermaid validation: Proactively fixed diagram ${diagramIndex + 1} with node label quoting`);
1290
- console.log(`[DEBUG] Mermaid validation: Applied automatic quoting to special characters`);
1291
- }
1292
- }
1293
- }
1294
-
1295
- // If proactive fixes were applied, re-validate the entire response
1296
- if (proactiveFixesApplied) {
1297
- const revalidation = await validateMermaidResponse(fixedResponse);
1298
- if (revalidation.isValid) {
1299
- // All diagrams are now valid, return without AI fixing
1300
- const totalTime = Date.now() - startTime;
1066
+
1067
+ maidFixesApplied = true;
1068
+
1301
1069
  if (debug) {
1302
- console.log(`[DEBUG] Mermaid validation: All diagrams fixed with proactive quoting in ${totalTime}ms, no AI needed`);
1303
- console.log(`[DEBUG] Mermaid validation: Applied ${fixingResults.length} proactive fixes`);
1304
- }
1305
-
1306
- // Record proactive fix success in telemetry
1307
- if (tracer) {
1308
- tracer.recordMermaidValidationEvent('proactive_fix_completed', {
1309
- 'mermaid_validation.success': true,
1310
- 'mermaid_validation.fix_method': 'node_label_quote_wrapping',
1311
- 'mermaid_validation.diagrams_fixed': fixingResults.length,
1312
- 'mermaid_validation.duration_ms': totalTime
1313
- });
1070
+ console.log(`[DEBUG] Mermaid validation: Maid fixed diagram ${invalidDiagram.originalIndex + 1}`);
1314
1071
  }
1315
- return {
1316
- ...revalidation,
1317
- wasFixed: true,
1318
- originalResponse: response,
1319
- fixedResponse: fixedResponse,
1320
- fixingResults: fixingResults,
1321
- performanceMetrics: {
1322
- totalTimeMs: totalTime,
1323
- aiFixingTimeMs: 0,
1324
- finalValidationTimeMs: 0,
1325
- diagramsProcessed: fixingResults.length,
1326
- diagramsFixed: fixingResults.length
1327
- }
1328
- };
1329
- }
1330
- }
1331
-
1332
- // Second pass: Try auto-fixing unquoted subgraph names with parentheses
1333
- let subgraphFixesApplied = false;
1334
-
1335
- // Re-extract diagrams and re-validate after HTML entity fixes
1336
- const { diagrams: postHtmlDiagrams } = extractMermaidFromMarkdown(fixedResponse);
1337
- const postHtmlValidation = await validateMermaidResponse(fixedResponse);
1338
-
1339
- const stillInvalidAfterHtml = postHtmlValidation.diagrams
1340
- .map((result, index) => ({ ...result, originalIndex: index }))
1341
- .filter(result => !result.isValid)
1342
- .reverse();
1072
+ } else if (maidResult.wasFixed) {
1073
+ // Maid improved it but didn't fix everything - update content for AI fixing
1074
+ const originalDiagram = diagrams[invalidDiagram.originalIndex];
1075
+ const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
1076
+ const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${maidResult.fixed}\n\`\`\``;
1343
1077
 
1344
- for (const invalidDiagram of stillInvalidAfterHtml) {
1345
- // Check if this is a subgraph parentheses error that we can auto-fix
1346
- if (invalidDiagram.error && invalidDiagram.error.includes('Parentheses in subgraph label')) {
1347
- const originalContent = invalidDiagram.content;
1348
- const lines = originalContent.split('\n');
1349
- let wasFixed = false;
1350
-
1351
- // Find and fix unquoted subgraph lines with parentheses
1352
- const fixedLines = lines.map(line => {
1353
- const trimmedLine = line.trim();
1354
- if (trimmedLine.startsWith('subgraph ') &&
1355
- !trimmedLine.match(/subgraph\s+"[^"]*"/) && // not already quoted
1356
- trimmedLine.match(/subgraph\s+[^"]*\([^"]*\)/)) { // has unquoted parentheses
1357
-
1358
- // Extract the subgraph name part
1359
- const match = trimmedLine.match(/^(\s*subgraph\s+)(.+)$/);
1360
- if (match) {
1361
- const prefix = match[1];
1362
- const name = match[2];
1363
- const fixedLine = line.replace(trimmedLine, `${prefix.trim()} "${name}"`);
1364
- wasFixed = true;
1365
- return fixedLine;
1366
- }
1367
- }
1368
- return line;
1078
+ fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
1079
+ newCodeBlock +
1080
+ fixedResponse.slice(originalDiagram.endIndex);
1081
+
1082
+ fixingResults.push({
1083
+ diagramIndex: invalidDiagram.originalIndex,
1084
+ wasFixed: false,
1085
+ originalContent: originalContent,
1086
+ partiallyFixedContent: maidResult.fixed,
1087
+ originalError: invalidDiagram.error,
1088
+ remainingErrors: maidResult.errors,
1089
+ fixedWithMaid: 'partial',
1090
+ fixLevel: maidResult.fixLevel
1369
1091
  });
1370
-
1371
- if (wasFixed) {
1372
- const fixedContent = fixedLines.join('\n');
1373
-
1374
- // Validate the fixed content
1375
- try {
1376
- const quickValidation = await validateMermaidDiagram(fixedContent);
1377
- if (quickValidation.isValid) {
1378
- // Subgraph auto-fix worked!
1379
- const originalDiagram = postHtmlDiagrams[invalidDiagram.originalIndex];
1380
- const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
1381
- const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
1382
-
1383
- fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
1384
- newCodeBlock +
1385
- fixedResponse.slice(originalDiagram.endIndex);
1386
-
1387
- fixingResults.push({
1388
- originalIndex: invalidDiagram.originalIndex,
1389
- wasFixed: true,
1390
- originalError: invalidDiagram.error,
1391
- fixMethod: 'subgraph_quote_wrapping',
1392
- fixedWithSubgraphQuoting: true
1393
- });
1394
-
1395
- subgraphFixesApplied = true;
1396
-
1397
- if (debug) {
1398
- console.log(`[DEBUG] Mermaid validation: Fixed diagram ${invalidDiagram.originalIndex + 1} with subgraph quote wrapping`);
1399
- console.log(`[DEBUG] Mermaid validation: Original error: ${invalidDiagram.error}`);
1400
- }
1401
- }
1402
- } catch (error) {
1403
- if (debug) {
1404
- console.log(`[DEBUG] Mermaid validation: Subgraph auto-fix didn't work for diagram ${invalidDiagram.originalIndex + 1}: ${error.message}`);
1405
- }
1406
- }
1407
- }
1408
- }
1409
- }
1410
-
1411
- // If subgraph fixes were applied, re-validate the entire response
1412
- if (subgraphFixesApplied) {
1413
- const revalidation = await validateMermaidResponse(fixedResponse);
1414
- if (revalidation.isValid) {
1415
- // All diagrams are now valid, return without AI fixing
1416
- const totalTime = Date.now() - startTime;
1092
+
1093
+ maidFixesApplied = true;
1094
+
1417
1095
  if (debug) {
1418
- console.log(`[DEBUG] Mermaid validation: All diagrams fixed with auto-fixes in ${totalTime}ms, no AI needed`);
1419
- console.log(`[DEBUG] Mermaid validation: Applied ${fixingResults.length} auto-fixes (HTML entities + subgraph quotes)`);
1420
- }
1421
-
1422
- // Record auto-fix success in telemetry
1423
- if (tracer) {
1424
- tracer.recordMermaidValidationEvent('auto_fix_completed', {
1425
- 'mermaid_validation.success': true,
1426
- 'mermaid_validation.fix_method': 'auto_fixes',
1427
- 'mermaid_validation.diagrams_fixed': fixingResults.length,
1428
- 'mermaid_validation.duration_ms': totalTime
1429
- });
1096
+ console.log(`[DEBUG] Mermaid validation: Maid partially fixed diagram ${invalidDiagram.originalIndex + 1}, ${maidResult.errors.length} errors remain`);
1430
1097
  }
1431
- return {
1432
- ...revalidation,
1433
- wasFixed: true,
1434
- originalResponse: response,
1435
- fixedResponse: fixedResponse,
1436
- fixingResults: fixingResults,
1437
- performanceMetrics: {
1438
- totalTimeMs: totalTime,
1439
- aiFixingTimeMs: 0,
1440
- finalValidationTimeMs: 0,
1441
- diagramsProcessed: fixingResults.length,
1442
- diagramsFixed: fixingResults.length
1443
- }
1444
- };
1098
+ } else {
1099
+ // Maid couldn't fix it, keep track for AI fixing
1100
+ fixingResults.push({
1101
+ diagramIndex: invalidDiagram.originalIndex,
1102
+ wasFixed: false,
1103
+ originalContent: originalContent,
1104
+ originalError: invalidDiagram.error,
1105
+ maidErrors: maidResult.errors,
1106
+ fixedWithMaid: false
1107
+ });
1445
1108
  }
1446
1109
  }
1447
-
1448
- // Third pass: Try auto-fixing node labels with parentheses or single quotes
1449
- let nodeLabelFixesApplied = false;
1450
-
1451
- // Re-extract diagrams and re-validate after previous fixes
1452
- const { diagrams: postSubgraphDiagrams } = extractMermaidFromMarkdown(fixedResponse);
1453
- const postSubgraphValidation = await validateMermaidResponse(fixedResponse);
1454
-
1455
- const stillInvalidAfterSubgraph = postSubgraphValidation.diagrams
1456
- .map((result, index) => ({ ...result, originalIndex: index }))
1457
- .filter(result => !result.isValid)
1458
- .reverse();
1459
1110
 
1460
- for (const invalidDiagram of stillInvalidAfterSubgraph) {
1461
- // Check if this is a node label error that we can auto-fix
1462
- if (invalidDiagram.error &&
1463
- (invalidDiagram.error.includes('Parentheses in node label') ||
1464
- invalidDiagram.error.includes('Complex expression in diamond node') ||
1465
- invalidDiagram.error.includes('Single quotes in node label') ||
1466
- invalidDiagram.error.includes('Backticks in node label'))) {
1467
- const originalContent = invalidDiagram.content;
1468
- const lines = originalContent.split('\n');
1469
- let wasFixed = false;
1470
-
1471
- // Find and fix node labels with special characters that need quoting
1472
- const fixedLines = lines.map(line => {
1473
- const trimmedLine = line.trim();
1474
- let modifiedLine = line;
1475
-
1476
- // Enhanced auto-fixing for square bracket nodes [...]
1477
- // Look for any node labels that contain special characters and aren't already quoted
1478
- if (trimmedLine.match(/\[[^\]]*\]/)) {
1479
- modifiedLine = modifiedLine.replace(/\[([^\]]*)\]/g, (match, content) => {
1480
- // Check if already properly quoted with outer quotes
1481
- if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
1482
- // Extract the inner content (between the outer quotes)
1483
- const innerContent = content.trim().slice(1, -1);
1484
- // Check if inner content has unescaped quotes that need escaping
1485
- if (innerContent.includes('"') || innerContent.includes("'")) {
1486
- wasFixed = true;
1487
- // Decode any existing HTML entities first, then re-encode ALL quotes
1488
- const decodedContent = decodeHtmlEntities(innerContent);
1489
- const safeContent = decodedContent
1490
- .replace(/"/g, '&quot;') // Replace ALL double quotes with HTML entity
1491
- .replace(/'/g, '&#39;'); // Replace ALL single quotes with HTML entity
1492
- return `["${safeContent}"]`;
1493
- }
1494
- return match;
1495
- }
1496
-
1497
- // Check if content needs quoting (contains problematic patterns)
1498
- if (needsQuoting(content)) {
1499
- wasFixed = true;
1500
- // Use HTML entities for quotes as per Mermaid best practices:
1501
- // - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
1502
- // - HTML entities are the official way to escape quotes in Mermaid
1503
- // - Always use double quotes with square brackets ["..."] for node labels
1504
- // IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
1505
- const decodedContent = decodeHtmlEntities(content);
1506
- const safeContent = decodedContent
1507
- .replace(/"/g, '&quot;') // Replace double quotes with HTML entity
1508
- .replace(/'/g, '&#39;'); // Replace single quotes with HTML entity
1509
- return `["${safeContent}"]`;
1510
- }
1511
-
1512
- return match;
1513
- });
1514
- }
1515
-
1516
- // Enhanced auto-fixing for diamond nodes {...}
1517
- if (trimmedLine.match(/\{[^{}]*\}/)) {
1518
- modifiedLine = modifiedLine.replace(/\{([^{}]*)\}/g, (match, content) => {
1519
- // Check if already properly quoted with outer quotes
1520
- if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
1521
- // Extract the inner content (between the outer quotes)
1522
- const innerContent = content.trim().slice(1, -1);
1523
- // Check if inner content has unescaped quotes that need escaping
1524
- if (innerContent.includes('"') || innerContent.includes("'")) {
1525
- wasFixed = true;
1526
- // Decode any existing HTML entities first, then re-encode ALL quotes
1527
- const decodedContent = decodeHtmlEntities(innerContent);
1528
- const safeContent = decodedContent
1529
- .replace(/"/g, '&quot;') // Replace ALL double quotes with HTML entity
1530
- .replace(/'/g, '&#39;'); // Replace ALL single quotes with HTML entity
1531
- return `{"${safeContent}"}`;
1532
- }
1533
- return match;
1534
- }
1535
-
1536
- // Check if content needs quoting (contains problematic patterns)
1537
- if (needsQuoting(content)) {
1538
- wasFixed = true;
1539
- // Use HTML entities for quotes as per Mermaid best practices:
1540
- // - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
1541
- // - HTML entities are the official way to escape quotes in Mermaid
1542
- // - Always use double quotes with curly brackets {"..."} for diamond nodes
1543
- // IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
1544
- const decodedContent = decodeHtmlEntities(content);
1545
- const safeContent = decodedContent
1546
- .replace(/"/g, '&quot;') // Replace double quotes with HTML entity
1547
- .replace(/'/g, '&#39;'); // Replace single quotes with HTML entity
1548
- return `{"${safeContent}"}`;
1549
- }
1550
-
1551
- return match;
1552
- });
1553
- }
1554
-
1555
- return modifiedLine;
1111
+ // Re-validate after maid fixes
1112
+ const revalidation = await validateMermaidResponse(fixedResponse);
1113
+ if (revalidation.isValid) {
1114
+ // All diagrams fixed with maid
1115
+ const totalTime = Date.now() - startTime;
1116
+ if (debug) {
1117
+ console.log(`[DEBUG] Mermaid validation: All diagrams fixed with maid in ${totalTime}ms, no AI needed`);
1118
+ }
1119
+
1120
+ if (tracer) {
1121
+ tracer.recordMermaidValidationEvent('maid_fix_completed', {
1122
+ 'mermaid_validation.success': true,
1123
+ 'mermaid_validation.fix_method': 'maid',
1124
+ 'mermaid_validation.diagrams_fixed': fixingResults.filter(r => r.wasFixed).length,
1125
+ 'mermaid_validation.duration_ms': totalTime
1556
1126
  });
1557
-
1558
- if (wasFixed) {
1559
- const fixedContent = fixedLines.join('\n');
1560
-
1561
- // Validate the fixed content
1562
- try {
1563
- const quickValidation = await validateMermaidDiagram(fixedContent);
1564
- if (quickValidation.isValid) {
1565
- // Node label auto-fix worked!
1566
- const originalDiagram = postSubgraphDiagrams[invalidDiagram.originalIndex];
1567
- const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
1568
- const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
1569
-
1570
- fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
1571
- newCodeBlock +
1572
- fixedResponse.slice(originalDiagram.endIndex);
1573
-
1574
- fixingResults.push({
1575
- originalIndex: invalidDiagram.originalIndex,
1576
- wasFixed: true,
1577
- originalError: invalidDiagram.error,
1578
- fixMethod: 'node_label_quote_wrapping',
1579
- fixedWithNodeLabelQuoting: true
1580
- });
1581
-
1582
- nodeLabelFixesApplied = true;
1583
-
1584
- if (debug) {
1585
- console.log(`[DEBUG] Mermaid validation: Fixed diagram ${invalidDiagram.originalIndex + 1} with node label quote wrapping`);
1586
- console.log(`[DEBUG] Mermaid validation: Original error: ${invalidDiagram.error}`);
1587
- }
1588
- }
1589
- } catch (error) {
1590
- if (debug) {
1591
- console.log(`[DEBUG] Mermaid validation: Node label auto-fix didn't work for diagram ${invalidDiagram.originalIndex + 1}: ${error.message}`);
1592
- }
1593
- }
1594
- }
1595
1127
  }
1596
- }
1597
-
1598
- // If node label fixes were applied, re-validate the entire response
1599
- if (nodeLabelFixesApplied) {
1600
- const revalidation = await validateMermaidResponse(fixedResponse);
1601
- if (revalidation.isValid) {
1602
- // All diagrams are now valid, return without AI fixing
1603
- const totalTime = Date.now() - startTime;
1604
- if (debug) {
1605
- console.log(`[DEBUG] Mermaid validation: All diagrams fixed with auto-fixes in ${totalTime}ms, no AI needed`);
1606
- console.log(`[DEBUG] Mermaid validation: Applied ${fixingResults.length} auto-fixes (HTML entities + subgraph quotes + node label quotes)`);
1607
- }
1608
-
1609
- // Record auto-fix success in telemetry
1610
- if (tracer) {
1611
- tracer.recordMermaidValidationEvent('auto_fix_completed', {
1612
- 'mermaid_validation.success': true,
1613
- 'mermaid_validation.fix_method': 'auto_fixes',
1614
- 'mermaid_validation.diagrams_fixed': fixingResults.length,
1615
- 'mermaid_validation.duration_ms': totalTime
1616
- });
1128
+
1129
+ return {
1130
+ ...revalidation,
1131
+ wasFixed: true,
1132
+ originalResponse: response,
1133
+ fixedResponse: fixedResponse,
1134
+ fixingResults: fixingResults,
1135
+ performanceMetrics: {
1136
+ totalTimeMs: totalTime,
1137
+ aiFixingTimeMs: 0,
1138
+ diagramsProcessed: fixingResults.length,
1139
+ diagramsFixed: fixingResults.filter(r => r.wasFixed).length
1617
1140
  }
1618
- return {
1619
- ...revalidation,
1620
- wasFixed: true,
1621
- originalResponse: response,
1622
- fixedResponse: fixedResponse,
1623
- fixingResults: fixingResults,
1624
- performanceMetrics: {
1625
- totalTimeMs: totalTime,
1626
- aiFixingTimeMs: 0,
1627
- finalValidationTimeMs: 0,
1628
- diagramsProcessed: fixingResults.length,
1629
- diagramsFixed: fixingResults.length
1630
- }
1631
- };
1632
- }
1633
- }
1634
-
1635
- // Re-extract diagrams and re-validate after HTML entity fixes
1636
- const { diagrams: updatedDiagrams } = extractMermaidFromMarkdown(fixedResponse);
1637
- const updatedValidation = await validateMermaidResponse(fixedResponse);
1638
-
1639
- // Still have invalid diagrams after all auto-fixes, proceed with AI fixing
1640
- if (debug) {
1641
- const stillInvalidAfterHtml = updatedValidation?.diagrams?.filter(d => !d.isValid)?.length || invalidCount;
1642
- console.log(`[DEBUG] Mermaid validation: ${stillInvalidAfterHtml} diagrams still invalid after HTML entity decoding, starting AI fixing...`);
1643
- console.log(`[DEBUG] Mermaid validation: HTML entity fixes applied: ${fixingResults.length}`);
1141
+ };
1644
1142
  }
1645
-
1646
- // Create specialized fixing agent for remaining invalid diagrams
1143
+
1144
+ // Still have invalid diagrams, proceed with AI fixing
1647
1145
  if (debug) {
1648
- console.log(`[DEBUG] Mermaid validation: Creating specialized AI fixing agent...`);
1146
+ const stillInvalid = revalidation.diagrams.filter(d => !d.isValid).length;
1147
+ console.log(`[DEBUG] Mermaid validation: ${stillInvalid} diagrams still invalid after maid, starting AI fixing...`);
1649
1148
  }
1149
+
1650
1150
  const aiFixingStart = Date.now();
1651
1151
  const mermaidFixer = new MermaidFixingAgent({
1652
1152
  path, provider, model, debug, tracer
1653
1153
  });
1654
-
1655
- const stillInvalidDiagrams = updatedValidation.diagrams
1154
+
1155
+ // Extract updated diagrams and validation
1156
+ const { diagrams: updatedDiagrams } = extractMermaidFromMarkdown(fixedResponse);
1157
+ const stillInvalidDiagrams = revalidation.diagrams
1656
1158
  .map((result, index) => ({ ...result, originalIndex: index }))
1657
1159
  .filter(result => !result.isValid)
1658
1160
  .reverse();
1659
-
1161
+
1660
1162
  if (debug) {
1661
1163
  console.log(`[DEBUG] Mermaid validation: Found ${stillInvalidDiagrams.length} diagrams requiring AI fixing`);
1662
1164
  }
@@ -1664,15 +1166,18 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1664
1166
  for (const invalidDiagram of stillInvalidDiagrams) {
1665
1167
  if (debug) {
1666
1168
  console.log(`[DEBUG] Mermaid validation: Attempting AI fix for diagram ${invalidDiagram.originalIndex + 1}`);
1667
- console.log(`[DEBUG] Mermaid validation: Diagram type: ${invalidDiagram.diagramType || 'unknown'}`);
1668
- console.log(`[DEBUG] Mermaid validation: Error to fix: ${invalidDiagram.error}`);
1669
1169
  }
1670
-
1170
+
1671
1171
  const diagramFixStart = Date.now();
1672
1172
  try {
1173
+ // Pass maid's structured errors if available
1174
+ const errorsToPass = invalidDiagram.errors && invalidDiagram.errors.length > 0
1175
+ ? invalidDiagram.errors
1176
+ : [invalidDiagram.error];
1177
+
1673
1178
  const fixedContent = await mermaidFixer.fixMermaidDiagram(
1674
1179
  invalidDiagram.content,
1675
- [invalidDiagram.error],
1180
+ errorsToPass,
1676
1181
  { diagramType: invalidDiagram.diagramType }
1677
1182
  );
1678
1183
  const diagramFixTime = Date.now() - diagramFixStart;
@@ -1682,91 +1187,68 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1682
1187
  const originalDiagram = updatedDiagrams[invalidDiagram.originalIndex];
1683
1188
  const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
1684
1189
  const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
1685
-
1686
- fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
1687
- newCodeBlock +
1190
+
1191
+ fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
1192
+ newCodeBlock +
1688
1193
  fixedResponse.slice(originalDiagram.endIndex);
1689
-
1690
- fixingResults.push({
1691
- diagramIndex: invalidDiagram.originalIndex,
1692
- wasFixed: true,
1693
- originalContent: invalidDiagram.content,
1694
- fixedContent: fixedContent,
1695
- originalError: invalidDiagram.error,
1696
- aiFixingTimeMs: diagramFixTime
1697
- });
1194
+
1195
+ // Find existing result or create new one
1196
+ const existingResultIndex = fixingResults.findIndex(r => r.diagramIndex === invalidDiagram.originalIndex);
1197
+ if (existingResultIndex >= 0) {
1198
+ fixingResults[existingResultIndex] = {
1199
+ ...fixingResults[existingResultIndex],
1200
+ wasFixed: true,
1201
+ fixedContent: fixedContent,
1202
+ aiFixingTimeMs: diagramFixTime
1203
+ };
1204
+ } else {
1205
+ fixingResults.push({
1206
+ diagramIndex: invalidDiagram.originalIndex,
1207
+ wasFixed: true,
1208
+ originalContent: invalidDiagram.content,
1209
+ fixedContent: fixedContent,
1210
+ originalError: invalidDiagram.error,
1211
+ aiFixingTimeMs: diagramFixTime
1212
+ });
1213
+ }
1698
1214
 
1699
1215
  if (debug) {
1700
- console.log(`[DEBUG] Mermaid validation: Successfully fixed diagram ${invalidDiagram.originalIndex + 1} in ${diagramFixTime}ms`);
1701
- console.log(`[DEBUG] Mermaid validation: Original error: ${invalidDiagram.error}`);
1702
- console.log(`[DEBUG] Mermaid validation: Content changes: ${invalidDiagram.content.length} -> ${fixedContent.length} chars`);
1216
+ console.log(`[DEBUG] Mermaid validation: Successfully fixed diagram ${invalidDiagram.originalIndex + 1} with AI in ${diagramFixTime}ms`);
1703
1217
  }
1704
1218
  } else {
1705
- fixingResults.push({
1706
- diagramIndex: invalidDiagram.originalIndex,
1707
- wasFixed: false,
1708
- originalContent: invalidDiagram.content,
1709
- originalError: invalidDiagram.error,
1710
- fixingError: 'No valid fix generated',
1711
- aiFixingTimeMs: diagramFixTime
1712
- });
1713
-
1714
1219
  if (debug) {
1715
1220
  console.log(`[DEBUG] Mermaid validation: AI fix failed for diagram ${invalidDiagram.originalIndex + 1} - no valid fix generated`);
1716
1221
  }
1717
1222
  }
1718
1223
  } catch (error) {
1719
- const diagramFixTime = Date.now() - diagramFixStart;
1720
- fixingResults.push({
1721
- diagramIndex: invalidDiagram.originalIndex,
1722
- wasFixed: false,
1723
- originalContent: invalidDiagram.content,
1724
- originalError: invalidDiagram.error,
1725
- fixingError: error.message,
1726
- aiFixingTimeMs: diagramFixTime
1727
- });
1728
-
1729
1224
  if (debug) {
1730
- console.log(`[DEBUG] Mermaid validation: AI fix failed for diagram ${invalidDiagram.originalIndex + 1} after ${diagramFixTime}ms: ${error.message}`);
1225
+ console.log(`[DEBUG] Mermaid validation: AI fix failed for diagram ${invalidDiagram.originalIndex + 1}: ${error.message}`);
1731
1226
  }
1732
1227
  }
1733
1228
  }
1734
1229
 
1735
- // Re-validate the fixed response
1736
- const finalValidationStart = Date.now();
1230
+ // Final validation
1737
1231
  const finalValidation = await validateMermaidResponse(fixedResponse);
1738
- const finalValidationTime = Date.now() - finalValidationStart;
1739
1232
  const totalTime = Date.now() - startTime;
1740
1233
  const aiFixingTime = Date.now() - aiFixingStart;
1741
1234
 
1742
- // Check if any diagrams were actually fixed
1743
1235
  const wasActuallyFixed = fixingResults.some(result => result.wasFixed);
1744
1236
  const fixedCount = fixingResults.filter(result => result.wasFixed).length;
1745
- const totalAttempts = fixingResults.length;
1746
1237
 
1747
1238
  if (debug) {
1748
- console.log(`[DEBUG] Mermaid validation: Final validation completed in ${finalValidationTime}ms`);
1749
1239
  console.log(`[DEBUG] Mermaid validation: Total process time: ${totalTime}ms (AI fixing: ${aiFixingTime}ms)`);
1750
- console.log(`[DEBUG] Mermaid validation: Fixed ${fixedCount}/${totalAttempts} diagrams with AI`);
1240
+ console.log(`[DEBUG] Mermaid validation: Fixed ${fixedCount}/${fixingResults.length} diagrams`);
1751
1241
  console.log(`[DEBUG] Mermaid validation: Final result - all valid: ${finalValidation.isValid}`);
1752
-
1753
- if (mermaidFixer.getTokenUsage) {
1754
- const tokenUsage = mermaidFixer.getTokenUsage();
1755
- console.log(`[DEBUG] Mermaid validation: AI token usage - prompt: ${tokenUsage?.promptTokens || 0}, completion: ${tokenUsage?.completionTokens || 0}`);
1756
- }
1757
1242
  }
1758
-
1759
- // Record final completion in telemetry
1243
+
1760
1244
  if (tracer) {
1761
1245
  tracer.recordMermaidValidationEvent('completed', {
1762
1246
  'mermaid_validation.success': finalValidation.isValid,
1763
1247
  'mermaid_validation.was_fixed': wasActuallyFixed,
1764
- 'mermaid_validation.diagrams_processed': totalAttempts,
1248
+ 'mermaid_validation.diagrams_processed': fixingResults.length,
1765
1249
  'mermaid_validation.diagrams_fixed': fixedCount,
1766
1250
  'mermaid_validation.total_duration_ms': totalTime,
1767
- 'mermaid_validation.ai_fixing_duration_ms': aiFixingTime,
1768
- 'mermaid_validation.final_validation_duration_ms': finalValidationTime,
1769
- 'mermaid_validation.token_usage': mermaidFixer.getTokenUsage ? mermaidFixer.getTokenUsage() : null
1251
+ 'mermaid_validation.ai_fixing_duration_ms': aiFixingTime
1770
1252
  });
1771
1253
  }
1772
1254
 
@@ -1779,8 +1261,7 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1779
1261
  performanceMetrics: {
1780
1262
  totalTimeMs: totalTime,
1781
1263
  aiFixingTimeMs: aiFixingTime,
1782
- finalValidationTimeMs: finalValidationTime,
1783
- diagramsProcessed: totalAttempts,
1264
+ diagramsProcessed: fixingResults.length,
1784
1265
  diagramsFixed: fixedCount
1785
1266
  },
1786
1267
  tokenUsage: mermaidFixer.getTokenUsage()
@@ -1788,10 +1269,9 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1788
1269
 
1789
1270
  } catch (error) {
1790
1271
  if (debug) {
1791
- console.error(`[DEBUG] Mermaid fixing agent failed: ${error.message}`);
1272
+ console.error(`[DEBUG] Mermaid fixing failed: ${error.message}`);
1792
1273
  }
1793
1274
 
1794
- // Return original validation with fixing error
1795
1275
  return {
1796
1276
  ...validation,
1797
1277
  wasFixed: false,
@@ -1800,4 +1280,4 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
1800
1280
  fixingError: error.message
1801
1281
  };
1802
1282
  }
1803
- }
1283
+ }