@probelabs/probe 0.6.0-rc252 → 0.6.0-rc254

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.
@@ -3571,7 +3571,12 @@ Follow these instructions carefully:
3571
3571
  } else {
3572
3572
  // Content was mostly/entirely inside thinking tags.
3573
3573
  // Extract thinking content and use it as the actual answer.
3574
- const thinkingContent = extractThinkingContent(prevContent);
3574
+ // extractThinkingContent now handles nested thinking tags (issue #439)
3575
+ let thinkingContent = extractThinkingContent(prevContent);
3576
+ // Also apply removeThinkingTags as extra safety to catch any edge cases
3577
+ if (thinkingContent) {
3578
+ thinkingContent = removeThinkingTags(thinkingContent) || thinkingContent.replace(/<\/?thinking>/g, '');
3579
+ }
3575
3580
  if (thinkingContent && thinkingContent.length > 50) {
3576
3581
  finalResult = thinkingContent;
3577
3582
  if (this.debug) console.log(`[DEBUG] Previous response was mostly in thinking tags — using thinking content as completion: ${finalResult.substring(0, 100)}...`);
@@ -4000,6 +4005,33 @@ Follow these instructions carefully:
4000
4005
  break;
4001
4006
  }
4002
4007
 
4008
+ // Issue #443: Check if response contains valid schema-matching JSON
4009
+ // Before triggering error.no_tool_call, strip markdown fences and validate
4010
+ // This handles cases where AI returns valid JSON without using attempt_completion
4011
+ if (options.schema) {
4012
+ // Remove thinking tags first
4013
+ let contentToCheck = assistantResponseContent;
4014
+ contentToCheck = contentToCheck.replace(/<thinking>[\s\S]*?<\/thinking>/gi, '').trim();
4015
+ contentToCheck = contentToCheck.replace(/<thinking>[\s\S]*$/gi, '').trim();
4016
+
4017
+ // Try to extract and validate JSON
4018
+ const cleanedJson = cleanSchemaResponse(contentToCheck);
4019
+ try {
4020
+ JSON.parse(cleanedJson);
4021
+ const validation = validateJsonResponse(cleanedJson, { debug: this.debug, schema: options.schema });
4022
+ if (validation.isValid) {
4023
+ if (this.debug) {
4024
+ console.log(`[DEBUG] Issue #443: Accepting valid JSON response without attempt_completion (${cleanedJson.length} chars)`);
4025
+ }
4026
+ finalResult = cleanedJson;
4027
+ completionAttempted = true;
4028
+ break;
4029
+ }
4030
+ } catch {
4031
+ // Not valid JSON - continue to standard no_tool_call handling
4032
+ }
4033
+ }
4034
+
4003
4035
  // Increment consecutive no-tool counter (catches alternating stuck responses)
4004
4036
  consecutiveNoToolCount++;
4005
4037
 
@@ -4795,10 +4827,25 @@ Convert your previous response content into actual JSON data that follows this s
4795
4827
  }
4796
4828
 
4797
4829
  // Remove thinking tags from final result before returning to user
4830
+ // Skip for valid JSON to avoid destroying JSON structure when <thinking> appears
4831
+ // inside string values (e.g., after tryAutoWrapForSimpleSchema embeds content with
4832
+ // residual thinking tag fragments — issue #439)
4798
4833
  if (!options._schemaFormatted) {
4799
- finalResult = removeThinkingTags(finalResult);
4800
- if (this.debug) {
4801
- console.log(`[DEBUG] Removed thinking tags from final result`);
4834
+ let isValidJson = false;
4835
+ try {
4836
+ JSON.parse(finalResult);
4837
+ isValidJson = true;
4838
+ } catch {
4839
+ // Not valid JSON, proceed with thinking tag removal
4840
+ }
4841
+
4842
+ if (!isValidJson) {
4843
+ finalResult = removeThinkingTags(finalResult);
4844
+ if (this.debug) {
4845
+ console.log(`[DEBUG] Removed thinking tags from final result`);
4846
+ }
4847
+ } else if (this.debug) {
4848
+ console.log(`[DEBUG] Skipped thinking tag removal for valid JSON result (issue #439)`);
4802
4849
  }
4803
4850
  }
4804
4851
 
@@ -183,6 +183,16 @@ export function generateSandboxGlobals(options) {
183
183
  });
184
184
  }
185
185
 
186
+ // Issue #444: Auto-coerce object paths to strings for search()
187
+ // AI-generated DSL sometimes passes field objects instead of field.file_path strings
188
+ if (params.path && typeof params.path === 'object') {
189
+ const coercedPath = params.path.file_path || params.path.path || params.path.directory || params.path.filename;
190
+ if (coercedPath && typeof coercedPath === 'string') {
191
+ logFn?.(`[${name}] Warning: Coerced object path to string "${coercedPath}" (issue #444)`);
192
+ params.path = coercedPath;
193
+ }
194
+ }
195
+
186
196
  const validated = schema.safeParse(params);
187
197
  if (!validated.success) {
188
198
  throw new Error(`Invalid parameters for ${name}: ${validated.error.message}`);
@@ -232,6 +242,15 @@ export function generateSandboxGlobals(options) {
232
242
  // When schema is provided, auto-parse the JSON result for easier downstream processing
233
243
  if (llmCall) {
234
244
  const rawLLM = async (instruction, data, opts = {}) => {
245
+ // Issue #444: Guard against error strings being passed as data
246
+ // When previous tool calls fail, they return "ERROR: ..." strings
247
+ // Passing these to LLM() spawns useless delegates that can't help
248
+ const dataStr = typeof data === 'string' ? data : JSON.stringify(data);
249
+ if (dataStr && dataStr.startsWith('ERROR:')) {
250
+ logFn?.('[LLM] Blocked: data contains error from previous tool call');
251
+ return 'ERROR: Previous tool call failed - ' + dataStr.substring(0, 200);
252
+ }
253
+
235
254
  const result = await llmCall(instruction, data, opts);
236
255
  // Auto-parse JSON when schema is provided and result is a string
237
256
  if (opts.schema && typeof result === 'string') {