@probelabs/probe 0.6.0-rc130 → 0.6.0-rc132

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.
@@ -142,29 +142,87 @@ export function cleanSchemaResponse(response) {
142
142
  */
143
143
  export function validateJsonResponse(response, options = {}) {
144
144
  const { debug = false } = options;
145
-
145
+
146
146
  if (debug) {
147
147
  console.log(`[DEBUG] JSON validation: Starting validation for response (${response.length} chars)`);
148
148
  const preview = createMessagePreview(response);
149
149
  console.log(`[DEBUG] JSON validation: Preview: ${preview}`);
150
150
  }
151
-
151
+
152
152
  try {
153
153
  const parseStart = Date.now();
154
154
  const parsed = JSON.parse(response);
155
155
  const parseTime = Date.now() - parseStart;
156
-
156
+
157
157
  if (debug) {
158
158
  console.log(`[DEBUG] JSON validation: Successfully parsed in ${parseTime}ms`);
159
159
  console.log(`[DEBUG] JSON validation: Object type: ${typeof parsed}, keys: ${Object.keys(parsed || {}).length}`);
160
160
  }
161
-
161
+
162
162
  return { isValid: true, parsed };
163
163
  } catch (error) {
164
+ // Extract error position from error message if available
165
+ // Old format: "Unexpected token < in JSON at position 0"
166
+ // New format: "Unexpected token '<', \"...\" is not valid JSON"
167
+ const positionMatch = error.message.match(/position (\d+)/);
168
+ let errorPosition = positionMatch ? parseInt(positionMatch[1], 10) : null;
169
+
170
+ // If position not found in old format, try to extract from new format
171
+ if (errorPosition === null) {
172
+ // Try to find the problematic token in the new error format
173
+ const tokenMatch = error.message.match(/Unexpected token '(.)', /);
174
+ if (tokenMatch && tokenMatch[1]) {
175
+ const problematicToken = tokenMatch[1];
176
+ // Find first occurrence of this token in the response
177
+ errorPosition = response.indexOf(problematicToken);
178
+ }
179
+ }
180
+
181
+ // Create enhanced error message with context snippet
182
+ let enhancedError = error.message;
183
+ let errorContext = null;
184
+
185
+ if (errorPosition !== null && errorPosition >= 0 && response && response.length > 0) {
186
+ // Calculate context window (50 chars before and after)
187
+ const contextRadius = 50;
188
+ const startPos = Math.max(0, errorPosition - contextRadius);
189
+ const endPos = Math.min(response.length, errorPosition + contextRadius);
190
+
191
+ // Extract context snippet
192
+ const beforeError = response.substring(startPos, errorPosition);
193
+ const atError = response[errorPosition] || '';
194
+ const afterError = response.substring(errorPosition + 1, endPos);
195
+
196
+ // Build error context with visual pointer
197
+ const snippet = beforeError + atError + afterError;
198
+ const pointerOffset = beforeError.length;
199
+ const pointer = ' '.repeat(pointerOffset) + '^';
200
+
201
+ errorContext = {
202
+ position: errorPosition,
203
+ snippet: snippet,
204
+ pointer: pointer,
205
+ beforeError: beforeError,
206
+ atError: atError,
207
+ afterError: afterError
208
+ };
209
+
210
+ // Create human-readable error context for display
211
+ enhancedError = `${error.message}
212
+
213
+ Error location (position ${errorPosition}):
214
+ ${snippet}
215
+ ${pointer} here`;
216
+ }
217
+
164
218
  if (debug) {
165
219
  console.log(`[DEBUG] JSON validation: Parse failed with error: ${error.message}`);
166
- console.log(`[DEBUG] JSON validation: Error at position: ${error.message.match(/position (\d+)/) ? error.message.match(/position (\d+)/)[1] : 'unknown'}`);
167
-
220
+ console.log(`[DEBUG] JSON validation: Error at position: ${errorPosition !== null ? errorPosition : 'unknown'}`);
221
+
222
+ if (errorContext) {
223
+ console.log(`[DEBUG] JSON validation: Error context:\n${errorContext.snippet}\n${errorContext.pointer}`);
224
+ }
225
+
168
226
  // Try to identify common JSON issues
169
227
  if (error.message.includes('Unexpected token')) {
170
228
  console.log(`[DEBUG] JSON validation: Likely syntax error - unexpected character`);
@@ -174,8 +232,13 @@ export function validateJsonResponse(response, options = {}) {
174
232
  console.log(`[DEBUG] JSON validation: Likely unquoted property names`);
175
233
  }
176
234
  }
177
-
178
- return { isValid: false, error: error.message };
235
+
236
+ return {
237
+ isValid: false,
238
+ error: error.message,
239
+ enhancedError: enhancedError,
240
+ errorContext: errorContext
241
+ };
179
242
  }
180
243
  }
181
244
 
@@ -357,11 +420,25 @@ export function isJsonSchemaDefinition(jsonString, options = {}) {
357
420
  * Create a correction prompt for invalid JSON
358
421
  * @param {string} invalidResponse - The invalid JSON response
359
422
  * @param {string} schema - The original schema
360
- * @param {string} error - The JSON parsing error
423
+ * @param {string|Object} errorOrValidation - The JSON parsing error string or validation result object
361
424
  * @param {number} [retryCount=0] - The current retry attempt (0-based)
362
425
  * @returns {string} - Correction prompt for the AI
363
426
  */
364
- export function createJsonCorrectionPrompt(invalidResponse, schema, error, retryCount = 0) {
427
+ export function createJsonCorrectionPrompt(invalidResponse, schema, errorOrValidation, retryCount = 0) {
428
+ // Extract error information from validation result or string
429
+ let errorMessage;
430
+ let enhancedError;
431
+
432
+ if (typeof errorOrValidation === 'object' && errorOrValidation !== null) {
433
+ // It's a validation result object
434
+ errorMessage = errorOrValidation.error;
435
+ enhancedError = errorOrValidation.enhancedError || errorMessage;
436
+ } else {
437
+ // It's a plain error string (backwards compatibility)
438
+ errorMessage = errorOrValidation;
439
+ enhancedError = errorMessage;
440
+ }
441
+
365
442
  // Create increasingly stronger prompts based on retry attempt
366
443
  const strengthLevels = [
367
444
  {
@@ -388,7 +465,7 @@ export function createJsonCorrectionPrompt(invalidResponse, schema, error, retry
388
465
 
389
466
  ${invalidResponse.substring(0, 500)}${invalidResponse.length > 500 ? '...' : ''}
390
467
 
391
- Error: ${error}
468
+ Error: ${enhancedError}
392
469
 
393
470
  ${currentLevel.instruction}
394
471
 
@@ -688,6 +765,178 @@ Ensure all Mermaid diagrams are properly formatted within \`\`\`mermaid code blo
688
765
  return prompt;
689
766
  }
690
767
 
768
+ /**
769
+ * Specialized JSON fixing agent
770
+ * Uses a separate ProbeAgent instance optimized for JSON syntax correction
771
+ */
772
+ export class JsonFixingAgent {
773
+ constructor(options = {}) {
774
+ // Import ProbeAgent dynamically to avoid circular dependencies
775
+ this.ProbeAgent = null;
776
+ this.options = {
777
+ sessionId: options.sessionId || `json-fixer-${Date.now()}`,
778
+ path: options.path || process.cwd(),
779
+ provider: options.provider,
780
+ model: options.model,
781
+ debug: options.debug,
782
+ tracer: options.tracer,
783
+ // Set to false since we're only fixing JSON syntax, not implementing code
784
+ allowEdit: false
785
+ };
786
+ }
787
+
788
+ /**
789
+ * Get the specialized prompt for JSON fixing
790
+ */
791
+ getJsonFixingPrompt() {
792
+ return `You are a world-class JSON syntax correction specialist. Your expertise lies in analyzing and fixing JSON syntax errors while preserving the original data structure and intent.
793
+
794
+ CORE RESPONSIBILITIES:
795
+ - Analyze JSON for syntax errors and structural issues
796
+ - Fix syntax errors while maintaining the original data's semantic meaning
797
+ - Ensure JSON follows proper RFC 8259 specification
798
+ - Handle all JSON structures: objects, arrays, primitives, nested structures
799
+
800
+ JSON SYNTAX RULES:
801
+ 1. **Property names**: Must be enclosed in double quotes
802
+ 2. **String values**: Must use double quotes (not single quotes)
803
+ 3. **Numbers**: Can be integers or decimals, no quotes needed
804
+ 4. **Booleans**: true or false (lowercase, no quotes)
805
+ 5. **Null**: null (lowercase, no quotes)
806
+ 6. **Arrays**: Comma-separated values in square brackets [...]
807
+ 7. **Objects**: Comma-separated key-value pairs in curly braces {...}
808
+ 8. **No trailing commas**: Last item in array/object must not have a trailing comma
809
+ 9. **Escape sequences**: Special characters must be escaped (\\n, \\t, \\", \\\\, etc.)
810
+
811
+ COMMON ERRORS TO FIX:
812
+ 1. **Unquoted property names**: {name: "value"} → {"name": "value"}
813
+ 2. **Single quotes**: {'key': 'value'} → {"key": "value"}
814
+ 3. **Trailing commas**: {"a": 1,} → {"a": 1}
815
+ 4. **Unquoted strings**: {key: value} → {"key": "value"}
816
+ 5. **Missing commas**: {"a": 1 "b": 2} → {"a": 1, "b": 2}
817
+ 6. **Extra commas**: {"a": 1,, "b": 2} → {"a": 1, "b": 2}
818
+ 7. **Unclosed brackets/braces**: {"key": "value" → {"key": "value"}
819
+ 8. **Invalid escape sequences**: Fix or remove
820
+ 9. **Comments**: Remove // or /* */ comments (not allowed in JSON)
821
+ 10. **Undefined values**: Replace undefined with null
822
+
823
+ FIXING METHODOLOGY:
824
+ 1. **Identify the error location** from the error message
825
+ 2. **Analyze the context** around the error
826
+ 3. **Apply the appropriate fix** based on JSON syntax rules
827
+ 4. **Preserve data intent** - never change the meaning of the data
828
+ 5. **Validate the result** - ensure it's parseable JSON
829
+
830
+ CRITICAL RULES:
831
+ - ALWAYS output only the corrected JSON
832
+ - NEVER add explanations, comments, or additional text
833
+ - NEVER wrap in markdown code blocks (no \`\`\`json)
834
+ - PRESERVE the original data structure and values
835
+ - FIX only syntax errors, don't modify the data itself
836
+ - ENSURE the output is valid, parseable JSON
837
+
838
+ When presented with broken JSON, analyze it thoroughly and provide the corrected version that maintains the original intent while fixing all syntax issues.`;
839
+ }
840
+
841
+ /**
842
+ * Initialize the ProbeAgent if not already done
843
+ */
844
+ async initializeAgent() {
845
+ if (!this.ProbeAgent) {
846
+ // Dynamic import to avoid circular dependency
847
+ const { ProbeAgent } = await import('./ProbeAgent.js');
848
+ this.ProbeAgent = ProbeAgent;
849
+ }
850
+
851
+ if (!this.agent) {
852
+ this.agent = new this.ProbeAgent({
853
+ sessionId: this.options.sessionId,
854
+ customPrompt: this.getJsonFixingPrompt(),
855
+ path: this.options.path,
856
+ provider: this.options.provider,
857
+ model: this.options.model,
858
+ debug: this.options.debug,
859
+ tracer: this.options.tracer,
860
+ allowEdit: this.options.allowEdit,
861
+ maxIterations: 5, // Allow multiple iterations for JSON fixing
862
+ disableJsonValidation: true // CRITICAL: Disable JSON validation in nested agent to prevent infinite recursion
863
+ });
864
+ }
865
+
866
+ return this.agent;
867
+ }
868
+
869
+ /**
870
+ * Fix invalid JSON using the specialized agent
871
+ * @param {string} invalidJson - The broken JSON string
872
+ * @param {string} schema - The original schema for context
873
+ * @param {Object} validationResult - Validation result with error details
874
+ * @param {number} attemptNumber - Current attempt number (for logging)
875
+ * @returns {Promise<string>} - The corrected JSON
876
+ */
877
+ async fixJson(invalidJson, schema, validationResult, attemptNumber = 1) {
878
+ await this.initializeAgent();
879
+
880
+ // Build error context from validation result
881
+ let errorContext = validationResult.error;
882
+ if (validationResult.enhancedError) {
883
+ errorContext = validationResult.enhancedError;
884
+ }
885
+
886
+ const prompt = `Fix the following invalid JSON.
887
+
888
+ Error: ${errorContext}
889
+
890
+ Invalid JSON:
891
+ ${invalidJson}
892
+
893
+ Expected schema structure:
894
+ ${schema}
895
+
896
+ Provide only the corrected JSON without any markdown formatting or explanations.`;
897
+
898
+ try {
899
+ if (this.options.debug) {
900
+ console.log(`[DEBUG] JSON fixing: Attempt ${attemptNumber} to fix JSON with separate agent`);
901
+ }
902
+
903
+ // Call the specialized JSON fixing agent
904
+ const result = await this.agent.answer(prompt, []);
905
+
906
+ // Clean the result (in case AI added markdown despite instructions)
907
+ const cleaned = cleanSchemaResponse(result);
908
+
909
+ if (this.options.debug) {
910
+ console.log(`[DEBUG] JSON fixing: Agent returned ${cleaned.length} chars`);
911
+ }
912
+
913
+ return cleaned;
914
+ } catch (error) {
915
+ if (this.options.debug) {
916
+ console.error(`[DEBUG] JSON fixing failed: ${error.message}`);
917
+ }
918
+ throw new Error(`Failed to fix JSON: ${error.message}`);
919
+ }
920
+ }
921
+
922
+ /**
923
+ * Get token usage information from the specialized agent
924
+ * @returns {Object} - Token usage statistics
925
+ */
926
+ getTokenUsage() {
927
+ return this.agent ? this.agent.getTokenUsage() : null;
928
+ }
929
+
930
+ /**
931
+ * Cancel any ongoing operations
932
+ */
933
+ cancel() {
934
+ if (this.agent) {
935
+ this.agent.cancel();
936
+ }
937
+ }
938
+ }
939
+
691
940
  /**
692
941
  * Use maid to attempt auto-fixing of mermaid diagrams
693
942
  * @param {string} diagramContent - The diagram content to fix