@probelabs/probe 0.6.0-rc255 → 0.6.0-rc256

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cjs/index.cjs CHANGED
@@ -3701,6 +3701,7 @@ var require_ChecksumStream = __commonJS({
3701
3701
  checksum;
3702
3702
  source;
3703
3703
  base64Encoder;
3704
+ pendingCallback = null;
3704
3705
  constructor({ expectedChecksum, checksum, source, checksumSourceLocation, base64Encoder }) {
3705
3706
  super();
3706
3707
  if (typeof source.pipe === "function") {
@@ -3715,11 +3716,20 @@ var require_ChecksumStream = __commonJS({
3715
3716
  this.source.pipe(this);
3716
3717
  }
3717
3718
  _read(size) {
3719
+ if (this.pendingCallback) {
3720
+ const callback = this.pendingCallback;
3721
+ this.pendingCallback = null;
3722
+ callback();
3723
+ }
3718
3724
  }
3719
3725
  _write(chunk, encoding, callback) {
3720
3726
  try {
3721
3727
  this.checksum.update(chunk);
3722
- this.push(chunk);
3728
+ const canPushMore = this.push(chunk);
3729
+ if (!canPushMore) {
3730
+ this.pendingCallback = callback;
3731
+ return;
3732
+ }
3723
3733
  } catch (e5) {
3724
3734
  return callback(e5);
3725
3735
  }
@@ -39015,6 +39025,10 @@ function getValidParamsForTool(toolName) {
39015
39025
  }
39016
39026
  return [];
39017
39027
  }
39028
+ function unescapeXmlEntities(str) {
39029
+ if (typeof str !== "string") return str;
39030
+ return str.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
39031
+ }
39018
39032
  function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
39019
39033
  let earliestToolName = null;
39020
39034
  let earliestOpenIndex = Infinity;
@@ -39071,10 +39085,10 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
39071
39085
  }
39072
39086
  paramCloseIndex = nextTagIndex;
39073
39087
  }
39074
- let paramValue = innerContent.substring(
39088
+ let paramValue = unescapeXmlEntities(innerContent.substring(
39075
39089
  paramOpenIndex + paramOpenTag.length,
39076
39090
  paramCloseIndex
39077
- ).trim();
39091
+ ).trim());
39078
39092
  if (paramValue.toLowerCase() === "true") {
39079
39093
  paramValue = true;
39080
39094
  } else if (paramValue.toLowerCase() === "false") {
@@ -39088,7 +39102,7 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
39088
39102
  params[paramName] = paramValue;
39089
39103
  }
39090
39104
  if (toolName === "attempt_completion") {
39091
- params["result"] = innerContent.trim();
39105
+ params["result"] = unescapeXmlEntities(innerContent.trim());
39092
39106
  if (params.command) {
39093
39107
  delete params.command;
39094
39108
  }
@@ -84467,6 +84481,7 @@ __export(schemaUtils_exports, {
84467
84481
  replaceMermaidDiagramsInMarkdown: () => replaceMermaidDiagramsInMarkdown,
84468
84482
  sanitizeMarkdownEscapesInJson: () => sanitizeMarkdownEscapesInJson,
84469
84483
  tryAutoWrapForSimpleSchema: () => tryAutoWrapForSimpleSchema,
84484
+ tryExtractValidJsonPrefix: () => tryExtractValidJsonPrefix,
84470
84485
  tryMaidAutoFix: () => tryMaidAutoFix,
84471
84486
  validateAndFixMermaidResponse: () => validateAndFixMermaidResponse,
84472
84487
  validateJsonResponse: () => validateJsonResponse,
@@ -84853,6 +84868,13 @@ function validateJsonResponse(response, options = {}) {
84853
84868
  errorPosition = response.indexOf(problematicToken);
84854
84869
  }
84855
84870
  }
84871
+ const prefixResult = tryExtractValidJsonPrefix(responseToValidate, { schema, debug });
84872
+ if (prefixResult && prefixResult.isValid) {
84873
+ if (debug) {
84874
+ console.log(`[DEBUG] JSON validation: Recovered valid JSON prefix (${prefixResult.extracted.length} chars) from response with trailing content`);
84875
+ }
84876
+ return { isValid: true, parsed: prefixResult.parsed };
84877
+ }
84856
84878
  let enhancedError = error2.message;
84857
84879
  let errorContext = null;
84858
84880
  if (errorPosition !== null && errorPosition >= 0 && response && response.length > 0) {
@@ -84903,6 +84925,84 @@ ${errorContext.pointer}`);
84903
84925
  };
84904
84926
  }
84905
84927
  }
84928
+ function tryExtractValidJsonPrefix(response, options = {}) {
84929
+ const { schema = null, debug = false } = options;
84930
+ if (!response || typeof response !== "string") {
84931
+ return null;
84932
+ }
84933
+ const trimmed = response.trim();
84934
+ if (trimmed.length === 0) {
84935
+ return null;
84936
+ }
84937
+ const firstChar = trimmed[0];
84938
+ if (firstChar !== "{" && firstChar !== "[") {
84939
+ return null;
84940
+ }
84941
+ try {
84942
+ JSON.parse(trimmed);
84943
+ return null;
84944
+ } catch {
84945
+ }
84946
+ const openChar = firstChar;
84947
+ const closeChar = openChar === "{" ? "}" : "]";
84948
+ let depth = 0;
84949
+ let inString = false;
84950
+ let escapeNext = false;
84951
+ let endPos = -1;
84952
+ for (let i5 = 0; i5 < trimmed.length; i5++) {
84953
+ const char = trimmed[i5];
84954
+ if (escapeNext) {
84955
+ escapeNext = false;
84956
+ continue;
84957
+ }
84958
+ if (char === "\\" && inString) {
84959
+ escapeNext = true;
84960
+ continue;
84961
+ }
84962
+ if (char === '"') {
84963
+ inString = !inString;
84964
+ continue;
84965
+ }
84966
+ if (inString) {
84967
+ continue;
84968
+ }
84969
+ if (char === openChar) {
84970
+ depth++;
84971
+ } else if (char === closeChar) {
84972
+ depth--;
84973
+ if (depth === 0) {
84974
+ endPos = i5 + 1;
84975
+ break;
84976
+ }
84977
+ }
84978
+ }
84979
+ if (endPos <= 0 || endPos >= trimmed.length) {
84980
+ return null;
84981
+ }
84982
+ const remainder = trimmed.substring(endPos).trim();
84983
+ if (remainder.length === 0) {
84984
+ return null;
84985
+ }
84986
+ const prefix = trimmed.substring(0, endPos);
84987
+ try {
84988
+ const parsed = JSON.parse(prefix);
84989
+ if (debug) {
84990
+ console.log(`[DEBUG] tryExtractValidJsonPrefix: Extracted valid JSON prefix (${prefix.length} chars), stripped trailing content (${remainder.length} chars)`);
84991
+ }
84992
+ if (schema) {
84993
+ const schemaValidation = validateJsonResponse(prefix, { debug, schema });
84994
+ if (!schemaValidation.isValid) {
84995
+ if (debug) {
84996
+ console.log(`[DEBUG] tryExtractValidJsonPrefix: Prefix is valid JSON but fails schema validation: ${schemaValidation.error}`);
84997
+ }
84998
+ return null;
84999
+ }
85000
+ }
85001
+ return { isValid: true, parsed, extracted: prefix };
85002
+ } catch {
85003
+ return null;
85004
+ }
85005
+ }
84906
85006
  function validateXmlResponse(response) {
84907
85007
  const xmlPattern = /<\/?[\w\s="'.-]+>/g;
84908
85008
  const tags = response.match(xmlPattern);
@@ -87004,7 +87104,7 @@ function parseXmlMcpToolCall(xmlString, mcpToolNames = []) {
87004
87104
  let match2;
87005
87105
  while ((match2 = paramPattern.exec(content)) !== null) {
87006
87106
  const [, paramName, paramValue] = match2;
87007
- params[paramName] = paramValue.trim();
87107
+ params[paramName] = unescapeXmlEntities(paramValue.trim());
87008
87108
  }
87009
87109
  }
87010
87110
  return { toolName, params };
@@ -87054,7 +87154,7 @@ function parseNativeXmlTool(xmlString, toolName) {
87054
87154
  while ((match2 = paramPattern.exec(content)) !== null) {
87055
87155
  const [, paramName, paramValue] = match2;
87056
87156
  if (paramName !== "params") {
87057
- params[paramName] = paramValue.trim();
87157
+ params[paramName] = unescapeXmlEntities(paramValue.trim());
87058
87158
  }
87059
87159
  }
87060
87160
  if (Object.keys(params).length > 0) {
@@ -87069,6 +87169,7 @@ var init_xmlBridge = __esm({
87069
87169
  init_client2();
87070
87170
  init_config();
87071
87171
  init_xmlParsingUtils();
87172
+ init_common2();
87072
87173
  MCPXmlBridge = class {
87073
87174
  constructor(options = {}) {
87074
87175
  this.debug = options.debug || false;
@@ -105765,6 +105866,7 @@ var init_bashDefaults = __esm({
105765
105866
  "tree:*",
105766
105867
  // Git read-only operations
105767
105868
  "git:status",
105869
+ "git:status:*",
105768
105870
  "git:log",
105769
105871
  "git:log:*",
105770
105872
  "git:diff",
@@ -105783,14 +105885,109 @@ var init_bashDefaults = __esm({
105783
105885
  "git:blame",
105784
105886
  "git:blame:*",
105785
105887
  "git:shortlog",
105888
+ "git:shortlog:*",
105786
105889
  "git:reflog",
105890
+ "git:reflog:*",
105787
105891
  "git:ls-files",
105892
+ "git:ls-files:*",
105788
105893
  "git:ls-tree",
105894
+ "git:ls-tree:*",
105895
+ "git:ls-remote",
105896
+ "git:ls-remote:*",
105789
105897
  "git:rev-parse",
105898
+ "git:rev-parse:*",
105790
105899
  "git:rev-list",
105900
+ "git:rev-list:*",
105901
+ "git:cat-file",
105902
+ "git:cat-file:*",
105903
+ "git:diff-tree",
105904
+ "git:diff-tree:*",
105905
+ "git:diff-files",
105906
+ "git:diff-files:*",
105907
+ "git:diff-index",
105908
+ "git:diff-index:*",
105909
+ "git:for-each-ref",
105910
+ "git:for-each-ref:*",
105911
+ "git:merge-base",
105912
+ "git:merge-base:*",
105913
+ "git:name-rev",
105914
+ "git:name-rev:*",
105915
+ "git:count-objects",
105916
+ "git:count-objects:*",
105917
+ "git:verify-commit",
105918
+ "git:verify-commit:*",
105919
+ "git:verify-tag",
105920
+ "git:verify-tag:*",
105921
+ "git:check-ignore",
105922
+ "git:check-ignore:*",
105923
+ "git:check-attr",
105924
+ "git:check-attr:*",
105925
+ "git:stash:list",
105926
+ "git:stash:show",
105927
+ "git:stash:show:*",
105928
+ "git:worktree:list",
105929
+ "git:worktree:list:*",
105930
+ "git:notes:list",
105931
+ "git:notes:show",
105932
+ "git:notes:show:*",
105791
105933
  "git:--version",
105792
105934
  "git:help",
105793
105935
  "git:help:*",
105936
+ // GitHub CLI (gh) read-only operations
105937
+ "gh:--version",
105938
+ "gh:help",
105939
+ "gh:help:*",
105940
+ "gh:status",
105941
+ "gh:auth:status",
105942
+ "gh:auth:status:*",
105943
+ "gh:issue:list",
105944
+ "gh:issue:list:*",
105945
+ "gh:issue:view",
105946
+ "gh:issue:view:*",
105947
+ "gh:issue:status",
105948
+ "gh:issue:status:*",
105949
+ "gh:pr:list",
105950
+ "gh:pr:list:*",
105951
+ "gh:pr:view",
105952
+ "gh:pr:view:*",
105953
+ "gh:pr:status",
105954
+ "gh:pr:status:*",
105955
+ "gh:pr:diff",
105956
+ "gh:pr:diff:*",
105957
+ "gh:pr:checks",
105958
+ "gh:pr:checks:*",
105959
+ "gh:repo:list",
105960
+ "gh:repo:list:*",
105961
+ "gh:repo:view",
105962
+ "gh:repo:view:*",
105963
+ "gh:release:list",
105964
+ "gh:release:list:*",
105965
+ "gh:release:view",
105966
+ "gh:release:view:*",
105967
+ "gh:run:list",
105968
+ "gh:run:list:*",
105969
+ "gh:run:view",
105970
+ "gh:run:view:*",
105971
+ "gh:workflow:list",
105972
+ "gh:workflow:list:*",
105973
+ "gh:workflow:view",
105974
+ "gh:workflow:view:*",
105975
+ "gh:gist:list",
105976
+ "gh:gist:list:*",
105977
+ "gh:gist:view",
105978
+ "gh:gist:view:*",
105979
+ "gh:search:issues",
105980
+ "gh:search:issues:*",
105981
+ "gh:search:prs",
105982
+ "gh:search:prs:*",
105983
+ "gh:search:repos",
105984
+ "gh:search:repos:*",
105985
+ "gh:search:code",
105986
+ "gh:search:code:*",
105987
+ "gh:search:commits",
105988
+ "gh:search:commits:*",
105989
+ "gh:api",
105990
+ "gh:api:*",
105794
105991
  // Package managers (information only)
105795
105992
  "npm:list",
105796
105993
  "npm:ls",
@@ -106101,14 +106298,132 @@ var init_bashDefaults = __esm({
106101
106298
  "git:push",
106102
106299
  "git:push:*",
106103
106300
  "git:force",
106104
- "git:reset:--hard:*",
106105
- "git:clean:-fd",
106301
+ "git:reset",
106302
+ "git:reset:*",
106303
+ "git:clean",
106304
+ "git:clean:*",
106305
+ "git:rm",
106106
106306
  "git:rm:*",
106107
106307
  "git:commit",
106308
+ "git:commit:*",
106108
106309
  "git:merge",
106310
+ "git:merge:*",
106109
106311
  "git:rebase",
106312
+ "git:rebase:*",
106110
106313
  "git:cherry-pick",
106314
+ "git:cherry-pick:*",
106111
106315
  "git:stash:drop",
106316
+ "git:stash:drop:*",
106317
+ "git:stash:pop",
106318
+ "git:stash:pop:*",
106319
+ "git:stash:push",
106320
+ "git:stash:push:*",
106321
+ "git:stash:clear",
106322
+ "git:branch:-d",
106323
+ "git:branch:-d:*",
106324
+ "git:branch:-D",
106325
+ "git:branch:-D:*",
106326
+ "git:branch:--delete",
106327
+ "git:branch:--delete:*",
106328
+ "git:tag:-d",
106329
+ "git:tag:-d:*",
106330
+ "git:tag:--delete",
106331
+ "git:tag:--delete:*",
106332
+ "git:remote:remove",
106333
+ "git:remote:remove:*",
106334
+ "git:remote:rm",
106335
+ "git:remote:rm:*",
106336
+ "git:checkout:--force",
106337
+ "git:checkout:--force:*",
106338
+ "git:checkout:-f",
106339
+ "git:checkout:-f:*",
106340
+ "git:submodule:deinit",
106341
+ "git:submodule:deinit:*",
106342
+ "git:notes:add",
106343
+ "git:notes:add:*",
106344
+ "git:notes:remove",
106345
+ "git:notes:remove:*",
106346
+ "git:worktree:add",
106347
+ "git:worktree:add:*",
106348
+ "git:worktree:remove",
106349
+ "git:worktree:remove:*",
106350
+ // Dangerous GitHub CLI (gh) write operations
106351
+ "gh:issue:create",
106352
+ "gh:issue:create:*",
106353
+ "gh:issue:close",
106354
+ "gh:issue:close:*",
106355
+ "gh:issue:delete",
106356
+ "gh:issue:delete:*",
106357
+ "gh:issue:edit",
106358
+ "gh:issue:edit:*",
106359
+ "gh:issue:reopen",
106360
+ "gh:issue:reopen:*",
106361
+ "gh:issue:comment",
106362
+ "gh:issue:comment:*",
106363
+ "gh:pr:create",
106364
+ "gh:pr:create:*",
106365
+ "gh:pr:close",
106366
+ "gh:pr:close:*",
106367
+ "gh:pr:merge",
106368
+ "gh:pr:merge:*",
106369
+ "gh:pr:edit",
106370
+ "gh:pr:edit:*",
106371
+ "gh:pr:reopen",
106372
+ "gh:pr:reopen:*",
106373
+ "gh:pr:review",
106374
+ "gh:pr:review:*",
106375
+ "gh:pr:comment",
106376
+ "gh:pr:comment:*",
106377
+ "gh:repo:create",
106378
+ "gh:repo:create:*",
106379
+ "gh:repo:delete",
106380
+ "gh:repo:delete:*",
106381
+ "gh:repo:fork",
106382
+ "gh:repo:fork:*",
106383
+ "gh:repo:rename",
106384
+ "gh:repo:rename:*",
106385
+ "gh:repo:archive",
106386
+ "gh:repo:archive:*",
106387
+ "gh:repo:clone",
106388
+ "gh:repo:clone:*",
106389
+ "gh:release:create",
106390
+ "gh:release:create:*",
106391
+ "gh:release:delete",
106392
+ "gh:release:delete:*",
106393
+ "gh:release:edit",
106394
+ "gh:release:edit:*",
106395
+ "gh:run:cancel",
106396
+ "gh:run:cancel:*",
106397
+ "gh:run:rerun",
106398
+ "gh:run:rerun:*",
106399
+ "gh:workflow:run",
106400
+ "gh:workflow:run:*",
106401
+ "gh:workflow:enable",
106402
+ "gh:workflow:enable:*",
106403
+ "gh:workflow:disable",
106404
+ "gh:workflow:disable:*",
106405
+ "gh:gist:create",
106406
+ "gh:gist:create:*",
106407
+ "gh:gist:delete",
106408
+ "gh:gist:delete:*",
106409
+ "gh:gist:edit",
106410
+ "gh:gist:edit:*",
106411
+ "gh:secret:set",
106412
+ "gh:secret:set:*",
106413
+ "gh:secret:delete",
106414
+ "gh:secret:delete:*",
106415
+ "gh:variable:set",
106416
+ "gh:variable:set:*",
106417
+ "gh:variable:delete",
106418
+ "gh:variable:delete:*",
106419
+ "gh:label:create",
106420
+ "gh:label:create:*",
106421
+ "gh:label:delete",
106422
+ "gh:label:delete:*",
106423
+ "gh:ssh-key:add",
106424
+ "gh:ssh-key:add:*",
106425
+ "gh:ssh-key:delete",
106426
+ "gh:ssh-key:delete:*",
106112
106427
  // File system mounting and partitioning
106113
106428
  "mount",
106114
106429
  "mount:*",
@@ -112166,8 +112481,8 @@ You are working with a workspace. Available paths: ${workspaceDesc}
112166
112481
  let currentIteration = 0;
112167
112482
  let completionAttempted = false;
112168
112483
  let finalResult = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
112169
- const baseMaxIterations = this.maxIterations || MAX_TOOL_ITERATIONS;
112170
- const maxIterations = options.schema ? baseMaxIterations + 4 : baseMaxIterations;
112484
+ const baseMaxIterations = options._maxIterationsOverride || this.maxIterations || MAX_TOOL_ITERATIONS;
112485
+ const maxIterations = options._maxIterationsOverride ? baseMaxIterations : options.schema ? baseMaxIterations + 4 : baseMaxIterations;
112171
112486
  const isClaudeCode = this.clientApiProvider === "claude-code" || process.env.USE_CLAUDE_CODE === "true";
112172
112487
  const isCodex = this.clientApiProvider === "codex" || process.env.USE_CODEX === "true";
112173
112488
  if (isClaudeCode) {
@@ -113404,13 +113719,16 @@ Convert your previous response content into actual JSON data that follows this s
113404
113719
  options.schema,
113405
113720
  0
113406
113721
  );
113722
+ const { schema: _unusedSchema1, ...schemaDefCorrectionOptions } = options;
113407
113723
  finalResult = await this.answer(schemaDefinitionPrompt, [], {
113408
- ...options,
113724
+ ...schemaDefCorrectionOptions,
113409
113725
  _schemaFormatted: true,
113410
113726
  _skipValidation: true,
113411
113727
  // Skip validation in recursive correction calls to prevent loops
113412
- _completionPromptProcessed: true
113728
+ _completionPromptProcessed: true,
113413
113729
  // Prevent cascading completion prompts in retry calls
113730
+ _maxIterationsOverride: 3
113731
+ // Correction should complete in 1-2 iterations (issue #447)
113414
113732
  });
113415
113733
  finalResult = cleanSchemaResponse(finalResult);
113416
113734
  validation = validateJsonResponse(finalResult);
@@ -113458,15 +113776,18 @@ Convert your previous response content into actual JSON data that follows this s
113458
113776
  retryCount
113459
113777
  );
113460
113778
  }
113779
+ const { schema: _unusedSchema2, ...correctionOptions } = options;
113461
113780
  finalResult = await this.answer(correctionPrompt, [], {
113462
- ...options,
113781
+ ...correctionOptions,
113463
113782
  _schemaFormatted: true,
113464
113783
  _skipValidation: true,
113465
113784
  // Skip validation in recursive correction calls to prevent loops
113466
113785
  _disableTools: true,
113467
113786
  // Only allow attempt_completion - prevent AI from using search/query tools
113468
- _completionPromptProcessed: true
113787
+ _completionPromptProcessed: true,
113469
113788
  // Prevent cascading completion prompts in retry calls
113789
+ _maxIterationsOverride: 3
113790
+ // Correction should complete in 1-2 iterations (issue #447)
113470
113791
  });
113471
113792
  finalResult = cleanSchemaResponse(finalResult);
113472
113793
  validation = validateJsonResponse(finalResult, { debug: this.debug });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc255",
3
+ "version": "0.6.0-rc256",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -83,7 +83,8 @@ import {
83
83
  isJsonSchemaDefinition,
84
84
  createSchemaDefinitionCorrectionPrompt,
85
85
  validateAndFixMermaidResponse,
86
- tryAutoWrapForSimpleSchema
86
+ tryAutoWrapForSimpleSchema,
87
+ tryExtractValidJsonPrefix
87
88
  } from './schemaUtils.js';
88
89
  import { removeThinkingTags, extractThinkingContent } from './xmlParsingUtils.js';
89
90
  import { predefinedPrompts } from './shared/prompts.js';
@@ -3027,8 +3028,9 @@ Follow these instructions carefully:
3027
3028
  // +1 for schema formatting
3028
3029
  // +2 for potential Mermaid validation retries (can be multiple diagrams)
3029
3030
  // +1 for potential JSON correction
3030
- const baseMaxIterations = this.maxIterations || MAX_TOOL_ITERATIONS;
3031
- const maxIterations = options.schema ? baseMaxIterations + 4 : baseMaxIterations;
3031
+ // _maxIterationsOverride: used by correction calls to cap iterations (issue #447)
3032
+ const baseMaxIterations = options._maxIterationsOverride || this.maxIterations || MAX_TOOL_ITERATIONS;
3033
+ const maxIterations = (options._maxIterationsOverride) ? baseMaxIterations : (options.schema ? baseMaxIterations + 4 : baseMaxIterations);
3032
3034
 
3033
3035
  // Check if we're using CLI-based engines which handle their own agentic loop
3034
3036
  const isClaudeCode = this.clientApiProvider === 'claude-code' || process.env.USE_CLAUDE_CODE === 'true';
@@ -4693,11 +4695,14 @@ Convert your previous response content into actual JSON data that follows this s
4693
4695
  0
4694
4696
  );
4695
4697
 
4698
+ // Strip schema from correction options to prevent inflated iteration budget (issue #447)
4699
+ const { schema: _unusedSchema1, ...schemaDefCorrectionOptions } = options;
4696
4700
  finalResult = await this.answer(schemaDefinitionPrompt, [], {
4697
- ...options,
4701
+ ...schemaDefCorrectionOptions,
4698
4702
  _schemaFormatted: true,
4699
4703
  _skipValidation: true, // Skip validation in recursive correction calls to prevent loops
4700
- _completionPromptProcessed: true // Prevent cascading completion prompts in retry calls
4704
+ _completionPromptProcessed: true, // Prevent cascading completion prompts in retry calls
4705
+ _maxIterationsOverride: 3 // Correction should complete in 1-2 iterations (issue #447)
4701
4706
  });
4702
4707
  finalResult = cleanSchemaResponse(finalResult);
4703
4708
  validation = validateJsonResponse(finalResult);
@@ -4753,12 +4758,15 @@ Convert your previous response content into actual JSON data that follows this s
4753
4758
  );
4754
4759
  }
4755
4760
 
4761
+ // Strip schema from correction options to prevent inflated iteration budget (issue #447)
4762
+ const { schema: _unusedSchema2, ...correctionOptions } = options;
4756
4763
  finalResult = await this.answer(correctionPrompt, [], {
4757
- ...options,
4764
+ ...correctionOptions,
4758
4765
  _schemaFormatted: true,
4759
4766
  _skipValidation: true, // Skip validation in recursive correction calls to prevent loops
4760
4767
  _disableTools: true, // Only allow attempt_completion - prevent AI from using search/query tools
4761
- _completionPromptProcessed: true // Prevent cascading completion prompts in retry calls
4768
+ _completionPromptProcessed: true, // Prevent cascading completion prompts in retry calls
4769
+ _maxIterationsOverride: 3 // Correction should complete in 1-2 iterations (issue #447)
4762
4770
  });
4763
4771
  finalResult = cleanSchemaResponse(finalResult);
4764
4772
 
@@ -30,13 +30,45 @@ export const DEFAULT_ALLOW_PATTERNS = [
30
30
  'tree', 'tree:*',
31
31
 
32
32
  // Git read-only operations
33
- 'git:status', 'git:log', 'git:log:*', 'git:diff', 'git:diff:*',
33
+ 'git:status', 'git:status:*', 'git:log', 'git:log:*', 'git:diff', 'git:diff:*',
34
34
  'git:show', 'git:show:*', 'git:branch', 'git:branch:*',
35
35
  'git:tag', 'git:tag:*', 'git:describe', 'git:describe:*',
36
36
  'git:remote', 'git:remote:*', 'git:config:*',
37
- 'git:blame', 'git:blame:*', 'git:shortlog', 'git:reflog',
38
- 'git:ls-files', 'git:ls-tree', 'git:rev-parse', 'git:rev-list',
37
+ 'git:blame', 'git:blame:*', 'git:shortlog', 'git:shortlog:*', 'git:reflog', 'git:reflog:*',
38
+ 'git:ls-files', 'git:ls-files:*', 'git:ls-tree', 'git:ls-tree:*',
39
+ 'git:ls-remote', 'git:ls-remote:*',
40
+ 'git:rev-parse', 'git:rev-parse:*', 'git:rev-list', 'git:rev-list:*',
41
+ 'git:cat-file', 'git:cat-file:*',
42
+ 'git:diff-tree', 'git:diff-tree:*', 'git:diff-files', 'git:diff-files:*',
43
+ 'git:diff-index', 'git:diff-index:*',
44
+ 'git:for-each-ref', 'git:for-each-ref:*',
45
+ 'git:merge-base', 'git:merge-base:*',
46
+ 'git:name-rev', 'git:name-rev:*',
47
+ 'git:count-objects', 'git:count-objects:*',
48
+ 'git:verify-commit', 'git:verify-commit:*', 'git:verify-tag', 'git:verify-tag:*',
49
+ 'git:check-ignore', 'git:check-ignore:*', 'git:check-attr', 'git:check-attr:*',
50
+ 'git:stash:list', 'git:stash:show', 'git:stash:show:*',
51
+ 'git:worktree:list', 'git:worktree:list:*',
52
+ 'git:notes:list', 'git:notes:show', 'git:notes:show:*',
39
53
  'git:--version', 'git:help', 'git:help:*',
54
+
55
+ // GitHub CLI (gh) read-only operations
56
+ 'gh:--version', 'gh:help', 'gh:help:*', 'gh:status',
57
+ 'gh:auth:status', 'gh:auth:status:*',
58
+ 'gh:issue:list', 'gh:issue:list:*', 'gh:issue:view', 'gh:issue:view:*',
59
+ 'gh:issue:status', 'gh:issue:status:*',
60
+ 'gh:pr:list', 'gh:pr:list:*', 'gh:pr:view', 'gh:pr:view:*',
61
+ 'gh:pr:status', 'gh:pr:status:*', 'gh:pr:diff', 'gh:pr:diff:*',
62
+ 'gh:pr:checks', 'gh:pr:checks:*',
63
+ 'gh:repo:list', 'gh:repo:list:*', 'gh:repo:view', 'gh:repo:view:*',
64
+ 'gh:release:list', 'gh:release:list:*', 'gh:release:view', 'gh:release:view:*',
65
+ 'gh:run:list', 'gh:run:list:*', 'gh:run:view', 'gh:run:view:*',
66
+ 'gh:workflow:list', 'gh:workflow:list:*', 'gh:workflow:view', 'gh:workflow:view:*',
67
+ 'gh:gist:list', 'gh:gist:list:*', 'gh:gist:view', 'gh:gist:view:*',
68
+ 'gh:search:issues', 'gh:search:issues:*', 'gh:search:prs', 'gh:search:prs:*',
69
+ 'gh:search:repos', 'gh:search:repos:*', 'gh:search:code', 'gh:search:code:*',
70
+ 'gh:search:commits', 'gh:search:commits:*',
71
+ 'gh:api', 'gh:api:*',
40
72
 
41
73
  // Package managers (information only)
42
74
  'npm:list', 'npm:ls', 'npm:view', 'npm:info', 'npm:show',
@@ -165,9 +197,46 @@ export const DEFAULT_DENY_PATTERNS = [
165
197
  'sysctl:-w:*',
166
198
 
167
199
  // Dangerous git operations
168
- 'git:push', 'git:push:*', 'git:force', 'git:reset:--hard:*',
169
- 'git:clean:-fd', 'git:rm:*', 'git:commit', 'git:merge',
170
- 'git:rebase', 'git:cherry-pick', 'git:stash:drop',
200
+ 'git:push', 'git:push:*', 'git:force', 'git:reset', 'git:reset:*',
201
+ 'git:clean', 'git:clean:*', 'git:rm', 'git:rm:*',
202
+ 'git:commit', 'git:commit:*', 'git:merge', 'git:merge:*',
203
+ 'git:rebase', 'git:rebase:*', 'git:cherry-pick', 'git:cherry-pick:*',
204
+ 'git:stash:drop', 'git:stash:drop:*', 'git:stash:pop', 'git:stash:pop:*',
205
+ 'git:stash:push', 'git:stash:push:*', 'git:stash:clear',
206
+ 'git:branch:-d', 'git:branch:-d:*', 'git:branch:-D', 'git:branch:-D:*',
207
+ 'git:branch:--delete', 'git:branch:--delete:*',
208
+ 'git:tag:-d', 'git:tag:-d:*', 'git:tag:--delete', 'git:tag:--delete:*',
209
+ 'git:remote:remove', 'git:remote:remove:*', 'git:remote:rm', 'git:remote:rm:*',
210
+ 'git:checkout:--force', 'git:checkout:--force:*',
211
+ 'git:checkout:-f', 'git:checkout:-f:*',
212
+ 'git:submodule:deinit', 'git:submodule:deinit:*',
213
+ 'git:notes:add', 'git:notes:add:*', 'git:notes:remove', 'git:notes:remove:*',
214
+ 'git:worktree:add', 'git:worktree:add:*',
215
+ 'git:worktree:remove', 'git:worktree:remove:*',
216
+
217
+ // Dangerous GitHub CLI (gh) write operations
218
+ 'gh:issue:create', 'gh:issue:create:*', 'gh:issue:close', 'gh:issue:close:*',
219
+ 'gh:issue:delete', 'gh:issue:delete:*', 'gh:issue:edit', 'gh:issue:edit:*',
220
+ 'gh:issue:reopen', 'gh:issue:reopen:*',
221
+ 'gh:issue:comment', 'gh:issue:comment:*',
222
+ 'gh:pr:create', 'gh:pr:create:*', 'gh:pr:close', 'gh:pr:close:*',
223
+ 'gh:pr:merge', 'gh:pr:merge:*', 'gh:pr:edit', 'gh:pr:edit:*',
224
+ 'gh:pr:reopen', 'gh:pr:reopen:*', 'gh:pr:review', 'gh:pr:review:*',
225
+ 'gh:pr:comment', 'gh:pr:comment:*',
226
+ 'gh:repo:create', 'gh:repo:create:*', 'gh:repo:delete', 'gh:repo:delete:*',
227
+ 'gh:repo:fork', 'gh:repo:fork:*', 'gh:repo:rename', 'gh:repo:rename:*',
228
+ 'gh:repo:archive', 'gh:repo:archive:*', 'gh:repo:clone', 'gh:repo:clone:*',
229
+ 'gh:release:create', 'gh:release:create:*', 'gh:release:delete', 'gh:release:delete:*',
230
+ 'gh:release:edit', 'gh:release:edit:*',
231
+ 'gh:run:cancel', 'gh:run:cancel:*', 'gh:run:rerun', 'gh:run:rerun:*',
232
+ 'gh:workflow:run', 'gh:workflow:run:*',
233
+ 'gh:workflow:enable', 'gh:workflow:enable:*', 'gh:workflow:disable', 'gh:workflow:disable:*',
234
+ 'gh:gist:create', 'gh:gist:create:*', 'gh:gist:delete', 'gh:gist:delete:*',
235
+ 'gh:gist:edit', 'gh:gist:edit:*',
236
+ 'gh:secret:set', 'gh:secret:set:*', 'gh:secret:delete', 'gh:secret:delete:*',
237
+ 'gh:variable:set', 'gh:variable:set:*', 'gh:variable:delete', 'gh:variable:delete:*',
238
+ 'gh:label:create', 'gh:label:create:*', 'gh:label:delete', 'gh:label:delete:*',
239
+ 'gh:ssh-key:add', 'gh:ssh-key:add:*', 'gh:ssh-key:delete', 'gh:ssh-key:delete:*',
171
240
 
172
241
  // File system mounting and partitioning
173
242
  'mount', 'mount:*', 'umount', 'umount:*', 'fdisk', 'fdisk:*',