@probelabs/probe 0.6.0-rc178 → 0.6.0-rc187

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.
@@ -3513,7 +3513,9 @@ async function delegate({
3513
3513
  parentSessionId = null,
3514
3514
  path: path9 = null,
3515
3515
  provider = null,
3516
- model = null
3516
+ model = null,
3517
+ enableBash = false,
3518
+ bashConfig = null
3517
3519
  }) {
3518
3520
  if (!task || typeof task !== "string") {
3519
3521
  throw new Error("Task parameter is required and must be a string");
@@ -3555,7 +3557,11 @@ async function delegate({
3555
3557
  // Inherit from parent
3556
3558
  provider,
3557
3559
  // Inherit from parent
3558
- model
3560
+ model,
3561
+ // Inherit from parent
3562
+ enableBash,
3563
+ // Inherit from parent
3564
+ bashConfig
3559
3565
  // Inherit from parent
3560
3566
  });
3561
3567
  if (debug) {
@@ -8455,7 +8461,7 @@ var init_vercel = __esm({
8455
8461
  });
8456
8462
  };
8457
8463
  delegateTool = (options = {}) => {
8458
- const { debug = false, timeout = 300, cwd, allowedFolders } = options;
8464
+ const { debug = false, timeout = 300, cwd, allowedFolders, enableBash = false, bashConfig } = options;
8459
8465
  return tool({
8460
8466
  name: "delegate",
8461
8467
  description: delegateDescription,
@@ -8505,7 +8511,9 @@ var init_vercel = __esm({
8505
8511
  path: effectivePath,
8506
8512
  provider,
8507
8513
  model,
8508
- tracer
8514
+ tracer,
8515
+ enableBash,
8516
+ bashConfig
8509
8517
  });
8510
8518
  return result;
8511
8519
  }
@@ -9136,6 +9144,46 @@ function isComplexCommand(command) {
9136
9144
  const result = parseSimpleCommand(command);
9137
9145
  return result.isComplex;
9138
9146
  }
9147
+ function isComplexPattern(pattern) {
9148
+ if (!pattern || typeof pattern !== "string") return false;
9149
+ const operatorPatterns = [
9150
+ /\|/,
9151
+ // Pipes
9152
+ /&&/,
9153
+ // Logical AND
9154
+ /\|\|/,
9155
+ // Logical OR
9156
+ /;/,
9157
+ // Command separator
9158
+ /&$/,
9159
+ // Background execution
9160
+ /\$\(/,
9161
+ // Command substitution $()
9162
+ /`/,
9163
+ // Command substitution ``
9164
+ />/,
9165
+ // Redirection >
9166
+ /</
9167
+ // Redirection <
9168
+ ];
9169
+ return operatorPatterns.some((p) => p.test(pattern));
9170
+ }
9171
+ function globToRegex(pattern) {
9172
+ let escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
9173
+ escaped = escaped.replace(/\*/g, ".*?");
9174
+ return new RegExp("^" + escaped + "$", "i");
9175
+ }
9176
+ function matchesComplexPattern(command, pattern) {
9177
+ if (!command || !pattern) return false;
9178
+ const normalizedCommand = command.trim().replace(/\s+/g, " ");
9179
+ const normalizedPattern = pattern.trim().replace(/\s+/g, " ");
9180
+ try {
9181
+ const regex = globToRegex(normalizedPattern);
9182
+ return regex.test(normalizedCommand);
9183
+ } catch (e) {
9184
+ return normalizedCommand === normalizedPattern;
9185
+ }
9186
+ }
9139
9187
  function parseCommand(command) {
9140
9188
  const result = parseSimpleCommand(command);
9141
9189
  if (!result.success) {
@@ -9250,7 +9298,7 @@ var init_bashPermissions = __esm({
9250
9298
  }
9251
9299
  }
9252
9300
  /**
9253
- * Check if a simple command is allowed (rejects complex commands for security)
9301
+ * Check if a simple command is allowed (complex commands allowed if they match patterns)
9254
9302
  * @param {string} command - Command to check
9255
9303
  * @returns {Object} Permission result
9256
9304
  */
@@ -9262,13 +9310,9 @@ var init_bashPermissions = __esm({
9262
9310
  command
9263
9311
  };
9264
9312
  }
9265
- if (isComplexCommand(command)) {
9266
- return {
9267
- allowed: false,
9268
- reason: "Complex shell commands with pipes, operators, or redirections are not supported for security reasons",
9269
- command,
9270
- isComplex: true
9271
- };
9313
+ const commandIsComplex = isComplexCommand(command);
9314
+ if (commandIsComplex) {
9315
+ return this._checkComplexCommand(command);
9272
9316
  }
9273
9317
  const parsed = parseCommand(command);
9274
9318
  if (parsed.error) {
@@ -9320,6 +9364,59 @@ var init_bashPermissions = __esm({
9320
9364
  }
9321
9365
  return result;
9322
9366
  }
9367
+ /**
9368
+ * Check a complex command against complex patterns in allow/deny lists
9369
+ * @private
9370
+ * @param {string} command - Complex command to check
9371
+ * @returns {Object} Permission result
9372
+ */
9373
+ _checkComplexCommand(command) {
9374
+ if (this.debug) {
9375
+ console.log(`[BashPermissions] Checking complex command: "${command}"`);
9376
+ }
9377
+ const complexAllowPatterns = this.allowPatterns.filter((p) => isComplexPattern(p));
9378
+ const complexDenyPatterns = this.denyPatterns.filter((p) => isComplexPattern(p));
9379
+ if (this.debug) {
9380
+ console.log(`[BashPermissions] Complex allow patterns: ${complexAllowPatterns.length}`);
9381
+ console.log(`[BashPermissions] Complex deny patterns: ${complexDenyPatterns.length}`);
9382
+ }
9383
+ for (const pattern of complexDenyPatterns) {
9384
+ if (matchesComplexPattern(command, pattern)) {
9385
+ if (this.debug) {
9386
+ console.log(`[BashPermissions] DENIED - matches complex deny pattern: ${pattern}`);
9387
+ }
9388
+ return {
9389
+ allowed: false,
9390
+ reason: `Command matches deny pattern: ${pattern}`,
9391
+ command,
9392
+ isComplex: true,
9393
+ matchedPatterns: [pattern]
9394
+ };
9395
+ }
9396
+ }
9397
+ for (const pattern of complexAllowPatterns) {
9398
+ if (matchesComplexPattern(command, pattern)) {
9399
+ if (this.debug) {
9400
+ console.log(`[BashPermissions] ALLOWED - matches complex allow pattern: ${pattern}`);
9401
+ }
9402
+ return {
9403
+ allowed: true,
9404
+ command,
9405
+ isComplex: true,
9406
+ matchedPattern: pattern
9407
+ };
9408
+ }
9409
+ }
9410
+ if (this.debug) {
9411
+ console.log(`[BashPermissions] DENIED - no matching complex pattern found`);
9412
+ }
9413
+ return {
9414
+ allowed: false,
9415
+ reason: 'Complex shell commands require explicit allow patterns (e.g., "cd * && git *")',
9416
+ command,
9417
+ isComplex: true
9418
+ };
9419
+ }
9323
9420
  /**
9324
9421
  * Get configuration summary
9325
9422
  * @returns {Object} Configuration info
@@ -9378,28 +9475,40 @@ async function executeBashCommand(command, options = {}) {
9378
9475
  ...process.env,
9379
9476
  ...env
9380
9477
  };
9381
- const args = parseCommandForExecution(command);
9382
- if (!args || args.length === 0) {
9383
- resolve6({
9384
- success: false,
9385
- error: "Failed to parse command",
9386
- stdout: "",
9387
- stderr: "",
9388
- exitCode: 1,
9389
- command,
9390
- workingDirectory: cwd,
9391
- duration: Date.now() - startTime
9392
- });
9393
- return;
9478
+ const isComplex = isComplexCommand(command);
9479
+ let cmd, cmdArgs, useShell;
9480
+ if (isComplex) {
9481
+ cmd = "sh";
9482
+ cmdArgs = ["-c", command];
9483
+ useShell = false;
9484
+ if (debug) {
9485
+ console.log(`[BashExecutor] Complex command - using sh -c`);
9486
+ }
9487
+ } else {
9488
+ const args = parseCommandForExecution(command);
9489
+ if (!args || args.length === 0) {
9490
+ resolve6({
9491
+ success: false,
9492
+ error: "Failed to parse command",
9493
+ stdout: "",
9494
+ stderr: "",
9495
+ exitCode: 1,
9496
+ command,
9497
+ workingDirectory: cwd,
9498
+ duration: Date.now() - startTime
9499
+ });
9500
+ return;
9501
+ }
9502
+ [cmd, ...cmdArgs] = args;
9503
+ useShell = false;
9394
9504
  }
9395
- const [cmd, ...cmdArgs] = args;
9396
9505
  const child = spawn2(cmd, cmdArgs, {
9397
9506
  cwd,
9398
9507
  env: processEnv,
9399
9508
  stdio: ["ignore", "pipe", "pipe"],
9400
9509
  // stdin ignored, capture stdout/stderr
9401
- shell: false,
9402
- // For security
9510
+ shell: useShell,
9511
+ // false for security
9403
9512
  windowsHide: true
9404
9513
  });
9405
9514
  let stdout = "";
@@ -23000,6 +23109,16 @@ var init_regexp_parser = __esm({
23000
23109
  case "!":
23001
23110
  type = "NegativeLookahead";
23002
23111
  break;
23112
+ case "<": {
23113
+ switch (this.popChar()) {
23114
+ case "=":
23115
+ type = "Lookbehind";
23116
+ break;
23117
+ case "!":
23118
+ type = "NegativeLookbehind";
23119
+ }
23120
+ break;
23121
+ }
23003
23122
  }
23004
23123
  ASSERT_EXISTS(type);
23005
23124
  const disjunction = this.disjunction();
@@ -23491,9 +23610,9 @@ var init_regexp_parser = __esm({
23491
23610
  default:
23492
23611
  return false;
23493
23612
  }
23494
- // '(?=' or '(?!'
23613
+ // '(?=' or '(?!' or `(?<=` or `(?<!`
23495
23614
  case "(":
23496
- return this.peekChar(1) === "?" && (this.peekChar(2) === "=" || this.peekChar(2) === "!");
23615
+ return this.peekChar(1) === "?" && (this.peekChar(2) === "=" || this.peekChar(2) === "!" || this.peekChar(2) === "<" && (this.peekChar(3) === "=" || this.peekChar(3) === "!"));
23497
23616
  default:
23498
23617
  return false;
23499
23618
  }
@@ -23618,6 +23737,12 @@ var init_base_regexp_visitor = __esm({
23618
23737
  case "NegativeLookahead":
23619
23738
  this.visitNegativeLookahead(node);
23620
23739
  break;
23740
+ case "Lookbehind":
23741
+ this.visitLookbehind(node);
23742
+ break;
23743
+ case "NegativeLookbehind":
23744
+ this.visitNegativeLookbehind(node);
23745
+ break;
23621
23746
  case "Character":
23622
23747
  this.visitCharacter(node);
23623
23748
  break;
@@ -23657,6 +23782,10 @@ var init_base_regexp_visitor = __esm({
23657
23782
  }
23658
23783
  visitNegativeLookahead(node) {
23659
23784
  }
23785
+ visitLookbehind(node) {
23786
+ }
23787
+ visitNegativeLookbehind(node) {
23788
+ }
23660
23789
  // atoms
23661
23790
  visitCharacter(node) {
23662
23791
  }
@@ -23750,6 +23879,8 @@ function firstCharOptimizedIndices(ast, result, ignoreCase) {
23750
23879
  // assertions do not affect potential starting codes
23751
23880
  case "Lookahead":
23752
23881
  case "NegativeLookahead":
23882
+ case "Lookbehind":
23883
+ case "NegativeLookbehind":
23753
23884
  case "StartAnchor":
23754
23885
  case "WordBoundary":
23755
23886
  case "NonWordBoundary":
@@ -23894,6 +24025,12 @@ var init_reg_exp = __esm({
23894
24025
  case "NegativeLookahead":
23895
24026
  this.visitNegativeLookahead(node);
23896
24027
  return;
24028
+ case "Lookbehind":
24029
+ this.visitLookbehind(node);
24030
+ return;
24031
+ case "NegativeLookbehind":
24032
+ this.visitNegativeLookbehind(node);
24033
+ return;
23897
24034
  }
23898
24035
  super.visitChildren(node);
23899
24036
  }
@@ -24333,24 +24470,27 @@ function findUnreachablePatterns(tokenTypes) {
24333
24470
  }
24334
24471
  return result;
24335
24472
  }, []);
24336
- forEach_default(tokenTypes, (tokType, testIdx) => {
24337
- forEach_default(canBeTested, ({ str, idx, tokenType }) => {
24338
- if (testIdx < idx && testTokenType(str, tokType.PATTERN)) {
24339
- const msg = `Token: ->${tokenType.name}<- can never be matched.
24340
- Because it appears AFTER the Token Type ->${tokType.name}<-in the lexer's definition.
24473
+ forEach_default(tokenTypes, (aTokType, aIdx) => {
24474
+ forEach_default(canBeTested, ({ str: bStr, idx: bIdx, tokenType: bTokType }) => {
24475
+ if (aIdx < bIdx && tryToMatchStrToPattern(bStr, aTokType.PATTERN)) {
24476
+ const msg = `Token: ->${bTokType.name}<- can never be matched.
24477
+ Because it appears AFTER the Token Type ->${aTokType.name}<-in the lexer's definition.
24341
24478
  See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;
24342
24479
  errors.push({
24343
24480
  message: msg,
24344
24481
  type: LexerDefinitionErrorType.UNREACHABLE_PATTERN,
24345
- tokenTypes: [tokType, tokenType]
24482
+ tokenTypes: [aTokType, bTokType]
24346
24483
  });
24347
24484
  }
24348
24485
  });
24349
24486
  });
24350
24487
  return errors;
24351
24488
  }
24352
- function testTokenType(str, pattern) {
24489
+ function tryToMatchStrToPattern(str, pattern) {
24353
24490
  if (isRegExp_default(pattern)) {
24491
+ if (usesLookAheadOrBehind(pattern)) {
24492
+ return false;
24493
+ }
24354
24494
  const regExpArray = pattern.exec(str);
24355
24495
  return regExpArray !== null && regExpArray.index === 0;
24356
24496
  } else if (isFunction_default(pattern)) {
@@ -24381,6 +24521,9 @@ function noMetaChar(regExp) {
24381
24521
  ];
24382
24522
  return find_default(metaChars, (char) => regExp.source.indexOf(char) !== -1) === void 0;
24383
24523
  }
24524
+ function usesLookAheadOrBehind(regExp) {
24525
+ return /(\(\?=)|(\(\?!)|(\(\?<=)|(\(\?<!)/.test(regExp.source);
24526
+ }
24384
24527
  function addStartOfInput(pattern) {
24385
24528
  const flags = pattern.ignoreCase ? "i" : "";
24386
24529
  return new RegExp(`^(?:${pattern.source})`, flags);
@@ -24728,7 +24871,7 @@ var init_lexer_errors_public = __esm({
24728
24871
  buildUnableToPopLexerModeMessage(token) {
24729
24872
  return `Unable to pop Lexer Mode after encountering Token ->${token.image}<- The Mode Stack is empty`;
24730
24873
  },
24731
- buildUnexpectedCharactersMessage(fullText, startOffset, length, line, column) {
24874
+ buildUnexpectedCharactersMessage(fullText, startOffset, length, line, column, mode) {
24732
24875
  return `unexpected character: ->${fullText.charAt(startOffset)}<- at offset: ${startOffset}, skipped ${length} characters.`;
24733
24876
  }
24734
24877
  };
@@ -25169,7 +25312,7 @@ var init_lexer_public = __esm({
25169
25312
  }
25170
25313
  errLength = offset - errorStartOffset;
25171
25314
  column = this.computeNewColumn(column, errLength);
25172
- msg = this.config.errorMessageProvider.buildUnexpectedCharactersMessage(orgText, errorStartOffset, errLength, errorLine, errorColumn);
25315
+ msg = this.config.errorMessageProvider.buildUnexpectedCharactersMessage(orgText, errorStartOffset, errLength, errorLine, errorColumn, last_default(modeStack));
25173
25316
  errors.push({
25174
25317
  offset: errorStartOffset,
25175
25318
  line: errorLine,
@@ -25282,7 +25425,7 @@ var init_lexer_public = __esm({
25282
25425
  return regExpArray !== null ? regExpArray[0] : null;
25283
25426
  }
25284
25427
  };
25285
- Lexer.SKIPPED = "This marks a skipped Token pattern, this means each token identified by it willbe consumed and then thrown into oblivion, this can be used to for example to completely ignore whitespace.";
25428
+ Lexer.SKIPPED = "This marks a skipped Token pattern, this means each token identified by it will be consumed and then thrown into oblivion, this can be used to for example to completely ignore whitespace.";
25286
25429
  Lexer.NA = /NOT_APPLICABLE/;
25287
25430
  }
25288
25431
  });
@@ -54963,18 +55106,18 @@ function createJsonCorrectionPrompt(invalidResponse, schema, errorOrValidation,
54963
55106
  const strengthLevels = [
54964
55107
  {
54965
55108
  prefix: "CRITICAL JSON ERROR:",
54966
- instruction: "You MUST fix this and return ONLY valid JSON.",
54967
- emphasis: "Return ONLY the corrected JSON, with no additional text or markdown formatting."
55109
+ instruction: "You MUST fix this and respond using attempt_completion with ONLY valid JSON as the result.",
55110
+ emphasis: "Use attempt_completion with ONLY the corrected JSON in the result field. No explanatory text, no markdown, no code blocks."
54968
55111
  },
54969
55112
  {
54970
55113
  prefix: "URGENT - JSON PARSING FAILED:",
54971
- instruction: "This is your second chance. Return ONLY valid JSON that can be parsed by JSON.parse().",
54972
- emphasis: "ABSOLUTELY NO explanatory text, greetings, or formatting. ONLY JSON."
55114
+ instruction: "This is your second chance. Use attempt_completion with valid JSON that can be parsed by JSON.parse().",
55115
+ emphasis: "ABSOLUTELY NO explanatory text or formatting. Use attempt_completion with ONLY raw JSON in the result."
54973
55116
  },
54974
55117
  {
54975
55118
  prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
54976
- instruction: "This is the final retry. You MUST return ONLY raw JSON without any other content.",
54977
- emphasis: 'EXAMPLE: {"key": "value"} NOT: ```json{"key": "value"}``` NOT: Here is the JSON: {"key": "value"}'
55119
+ instruction: "This is the final retry. You MUST use attempt_completion with ONLY raw JSON in the result field.",
55120
+ emphasis: 'CORRECT: <attempt_completion><result>{"key": "value"}</result></attempt_completion>\nWRONG: Here is the JSON: {"key": "value"}\nWRONG: ```json{"key": "value"}```'
54978
55121
  }
54979
55122
  ];
54980
55123
  const level = Math.min(retryCount, strengthLevels.length - 1);
@@ -60988,24 +61131,31 @@ You are working with a repository located at: ${searchDirectory}
60988
61131
  console.log(`[DEBUG] Assistant response (${assistantResponseContent.length} chars): ${assistantPreview}`);
60989
61132
  }
60990
61133
  const validTools = [];
60991
- if (this.allowedTools.isEnabled("search")) validTools.push("search");
60992
- if (this.allowedTools.isEnabled("query")) validTools.push("query");
60993
- if (this.allowedTools.isEnabled("extract")) validTools.push("extract");
60994
- if (this.allowedTools.isEnabled("listFiles")) validTools.push("listFiles");
60995
- if (this.allowedTools.isEnabled("searchFiles")) validTools.push("searchFiles");
60996
- if (this.allowedTools.isEnabled("readImage")) validTools.push("readImage");
60997
- if (this.allowedTools.isEnabled("attempt_completion")) validTools.push("attempt_completion");
60998
- if (this.allowEdit && this.allowedTools.isEnabled("implement")) {
60999
- validTools.push("implement", "edit", "create");
61000
- }
61001
- if (this.enableBash && this.allowedTools.isEnabled("bash")) {
61002
- validTools.push("bash");
61003
- }
61004
- if (this.enableDelegate && this.allowedTools.isEnabled("delegate")) {
61005
- validTools.push("delegate");
61134
+ if (options._disableTools) {
61135
+ validTools.push("attempt_completion");
61136
+ if (this.debug) {
61137
+ console.log(`[DEBUG] Tools disabled for this call - only attempt_completion allowed`);
61138
+ }
61139
+ } else {
61140
+ if (this.allowedTools.isEnabled("search")) validTools.push("search");
61141
+ if (this.allowedTools.isEnabled("query")) validTools.push("query");
61142
+ if (this.allowedTools.isEnabled("extract")) validTools.push("extract");
61143
+ if (this.allowedTools.isEnabled("listFiles")) validTools.push("listFiles");
61144
+ if (this.allowedTools.isEnabled("searchFiles")) validTools.push("searchFiles");
61145
+ if (this.allowedTools.isEnabled("readImage")) validTools.push("readImage");
61146
+ if (this.allowedTools.isEnabled("attempt_completion")) validTools.push("attempt_completion");
61147
+ if (this.allowEdit && this.allowedTools.isEnabled("implement")) {
61148
+ validTools.push("implement", "edit", "create");
61149
+ }
61150
+ if (this.enableBash && this.allowedTools.isEnabled("bash")) {
61151
+ validTools.push("bash");
61152
+ }
61153
+ if (this.enableDelegate && this.allowedTools.isEnabled("delegate")) {
61154
+ validTools.push("delegate");
61155
+ }
61006
61156
  }
61007
61157
  const nativeTools = validTools;
61008
- const parsedTool = this.mcpBridge ? parseHybridXmlToolCall(assistantResponseContent, nativeTools, this.mcpBridge) : parseXmlToolCallWithThinking(assistantResponseContent, validTools);
61158
+ const parsedTool = this.mcpBridge && !options._disableTools ? parseHybridXmlToolCall(assistantResponseContent, nativeTools, this.mcpBridge) : parseXmlToolCallWithThinking(assistantResponseContent, validTools);
61009
61159
  if (parsedTool) {
61010
61160
  const { toolName, params } = parsedTool;
61011
61161
  if (this.debug) console.log(`[DEBUG] Parsed tool call: ${toolName} with params:`, params);
@@ -61086,10 +61236,23 @@ ${toolResultContent}
61086
61236
  }
61087
61237
  } else if (this.toolImplementations[toolName]) {
61088
61238
  try {
61239
+ let resolvedWorkingDirectory = this.allowedFolders && this.allowedFolders[0] || process.cwd();
61240
+ if (params.workingDirectory) {
61241
+ const requestedDir = resolve4(params.workingDirectory);
61242
+ const isWithinAllowed = !this.allowedFolders || this.allowedFolders.length === 0 || this.allowedFolders.some((folder) => {
61243
+ const resolvedFolder = resolve4(folder);
61244
+ return requestedDir === resolvedFolder || requestedDir.startsWith(resolvedFolder + sep3);
61245
+ });
61246
+ if (isWithinAllowed) {
61247
+ resolvedWorkingDirectory = requestedDir;
61248
+ } else if (this.debug) {
61249
+ console.error(`[DEBUG] Rejected workingDirectory "${params.workingDirectory}" - not within allowed folders`);
61250
+ }
61251
+ }
61089
61252
  const toolParams = {
61090
61253
  ...params,
61091
61254
  sessionId: this.sessionId,
61092
- workingDirectory: this.allowedFolders && this.allowedFolders[0] || process.cwd()
61255
+ workingDirectory: resolvedWorkingDirectory
61093
61256
  };
61094
61257
  if (this.debug) {
61095
61258
  console.error(`
@@ -61604,8 +61767,10 @@ Convert your previous response content into actual JSON data that follows this s
61604
61767
  finalResult = await this.answer(correctionPrompt, [], {
61605
61768
  ...options,
61606
61769
  _schemaFormatted: true,
61607
- _skipValidation: true
61770
+ _skipValidation: true,
61608
61771
  // Skip validation in recursive correction calls to prevent loops
61772
+ _disableTools: true
61773
+ // Only allow attempt_completion - prevent AI from using search/query tools
61609
61774
  });
61610
61775
  finalResult = cleanSchemaResponse(finalResult);
61611
61776
  validation = validateJsonResponse(finalResult, { debug: this.debug });
@@ -63084,7 +63249,7 @@ Please reformat your previous response to match this schema exactly. Only return
63084
63249
  if (!validation.isValid) {
63085
63250
  const correctionPrompt = createJsonCorrectionPrompt(result, schema, validation.error);
63086
63251
  try {
63087
- result = await agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true });
63252
+ result = await agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true });
63088
63253
  result = cleanSchemaResponse(result);
63089
63254
  const finalValidation = validateJsonResponse(result);
63090
63255
  if (!finalValidation.isValid && args.debug) {
@@ -63370,11 +63535,11 @@ Please reformat your previous response to match this schema exactly. Only return
63370
63535
  if (appTracer) {
63371
63536
  result = await appTracer.withSpan(
63372
63537
  "agent.json_correction",
63373
- () => agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true }),
63538
+ () => agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true }),
63374
63539
  { "original_error": validation.error }
63375
63540
  );
63376
63541
  } else {
63377
- result = await agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true });
63542
+ result = await agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true });
63378
63543
  }
63379
63544
  result = cleanSchemaResponse(result);
63380
63545
  const finalValidation = validateJsonResponse(result);
@@ -787,21 +787,22 @@ export function createJsonCorrectionPrompt(invalidResponse, schema, errorOrValid
787
787
  }
788
788
 
789
789
  // Create increasingly stronger prompts based on retry attempt
790
+ // These prompts explicitly instruct the AI to use attempt_completion with the JSON result
790
791
  const strengthLevels = [
791
792
  {
792
793
  prefix: "CRITICAL JSON ERROR:",
793
- instruction: "You MUST fix this and return ONLY valid JSON.",
794
- emphasis: "Return ONLY the corrected JSON, with no additional text or markdown formatting."
794
+ instruction: "You MUST fix this and respond using attempt_completion with ONLY valid JSON as the result.",
795
+ emphasis: "Use attempt_completion with ONLY the corrected JSON in the result field. No explanatory text, no markdown, no code blocks."
795
796
  },
796
797
  {
797
798
  prefix: "URGENT - JSON PARSING FAILED:",
798
- instruction: "This is your second chance. Return ONLY valid JSON that can be parsed by JSON.parse().",
799
- emphasis: "ABSOLUTELY NO explanatory text, greetings, or formatting. ONLY JSON."
799
+ instruction: "This is your second chance. Use attempt_completion with valid JSON that can be parsed by JSON.parse().",
800
+ emphasis: "ABSOLUTELY NO explanatory text or formatting. Use attempt_completion with ONLY raw JSON in the result."
800
801
  },
801
802
  {
802
803
  prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
803
- instruction: "This is the final retry. You MUST return ONLY raw JSON without any other content.",
804
- emphasis: "EXAMPLE: {\"key\": \"value\"} NOT: ```json{\"key\": \"value\"}``` NOT: Here is the JSON: {\"key\": \"value\"}"
804
+ instruction: "This is the final retry. You MUST use attempt_completion with ONLY raw JSON in the result field.",
805
+ emphasis: "CORRECT: <attempt_completion><result>{\"key\": \"value\"}</result></attempt_completion>\nWRONG: Here is the JSON: {\"key\": \"value\"}\nWRONG: ```json{\"key\": \"value\"}```"
805
806
  }
806
807
  ];
807
808
 
package/build/delegate.js CHANGED
@@ -176,6 +176,8 @@ const delegationManager = new DelegationManager();
176
176
  * @param {string} [options.provider] - AI provider (inherited from parent)
177
177
  * @param {string} [options.model] - AI model (inherited from parent)
178
178
  * @param {Object} [options.tracer=null] - Telemetry tracer instance
179
+ * @param {boolean} [options.enableBash=false] - Enable bash tool (inherited from parent)
180
+ * @param {Object} [options.bashConfig] - Bash configuration (inherited from parent)
179
181
  * @returns {Promise<string>} The response from the delegate agent
180
182
  */
181
183
  export async function delegate({
@@ -188,7 +190,9 @@ export async function delegate({
188
190
  parentSessionId = null,
189
191
  path = null,
190
192
  provider = null,
191
- model = null
193
+ model = null,
194
+ enableBash = false,
195
+ bashConfig = null
192
196
  }) {
193
197
  if (!task || typeof task !== 'string') {
194
198
  throw new Error('Task parameter is required and must be a string');
@@ -234,9 +238,11 @@ export async function delegate({
234
238
  maxIterations: remainingIterations,
235
239
  debug,
236
240
  tracer,
237
- path, // Inherit from parent
238
- provider, // Inherit from parent
239
- model // Inherit from parent
241
+ path, // Inherit from parent
242
+ provider, // Inherit from parent
243
+ model, // Inherit from parent
244
+ enableBash, // Inherit from parent
245
+ bashConfig // Inherit from parent
240
246
  });
241
247
 
242
248
  if (debug) {
@@ -239,10 +239,12 @@ export const extractTool = (options = {}) => {
239
239
  * @param {number} [options.timeout=300] - Default timeout in seconds
240
240
  * @param {string} [options.cwd] - Working directory to use if not specified in call
241
241
  * @param {string[]} [options.allowedFolders] - Allowed folders for workspace isolation
242
+ * @param {boolean} [options.enableBash=false] - Enable bash tool for sub-agents
243
+ * @param {Object} [options.bashConfig] - Bash configuration (allow/deny patterns)
242
244
  * @returns {Object} Configured delegate tool
243
245
  */
244
246
  export const delegateTool = (options = {}) => {
245
- const { debug = false, timeout = 300, cwd, allowedFolders } = options;
247
+ const { debug = false, timeout = 300, cwd, allowedFolders, enableBash = false, bashConfig } = options;
246
248
 
247
249
  return tool({
248
250
  name: 'delegate',
@@ -309,7 +311,9 @@ export const delegateTool = (options = {}) => {
309
311
  path: effectivePath,
310
312
  provider,
311
313
  model,
312
- tracer
314
+ tracer,
315
+ enableBash,
316
+ bashConfig
313
317
  });
314
318
 
315
319
  return result;