@probelabs/probe 0.6.0-rc284 → 0.6.0-rc286

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.
Files changed (40) hide show
  1. package/bin/binaries/probe-v0.6.0-rc286-aarch64-apple-darwin.tar.gz +0 -0
  2. package/bin/binaries/{probe-v0.6.0-rc284-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc286-aarch64-unknown-linux-musl.tar.gz} +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc286-x86_64-apple-darwin.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc286-x86_64-pc-windows-msvc.zip +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc286-x86_64-unknown-linux-musl.tar.gz +0 -0
  6. package/build/agent/ProbeAgent.d.ts +1 -1
  7. package/build/agent/ProbeAgent.js +353 -489
  8. package/build/agent/contextCompactor.js +17 -10
  9. package/build/agent/index.js +325 -719
  10. package/build/agent/schemaUtils.js +10 -11
  11. package/build/agent/shared/prompts.js +2 -2
  12. package/build/agent/tasks/taskTool.js +3 -3
  13. package/build/agent/tools.js +0 -2
  14. package/build/downloader.js +5 -1
  15. package/build/index.js +0 -2
  16. package/build/tools/analyzeAll.js +4 -4
  17. package/build/tools/common.js +55 -55
  18. package/build/tools/index.js +0 -1
  19. package/build/tools/vercel.js +10 -8
  20. package/cjs/agent/ProbeAgent.cjs +316 -775
  21. package/cjs/index.cjs +317 -831
  22. package/package.json +1 -1
  23. package/src/agent/ProbeAgent.d.ts +1 -1
  24. package/src/agent/ProbeAgent.js +353 -489
  25. package/src/agent/contextCompactor.js +17 -10
  26. package/src/agent/index.js +8 -2
  27. package/src/agent/schemaUtils.js +10 -11
  28. package/src/agent/shared/prompts.js +2 -2
  29. package/src/agent/tasks/taskTool.js +3 -3
  30. package/src/agent/tools.js +0 -2
  31. package/src/downloader.js +5 -1
  32. package/src/index.js +0 -2
  33. package/src/tools/analyzeAll.js +4 -4
  34. package/src/tools/common.js +55 -55
  35. package/src/tools/index.js +0 -1
  36. package/src/tools/vercel.js +10 -8
  37. package/bin/binaries/probe-v0.6.0-rc284-aarch64-apple-darwin.tar.gz +0 -0
  38. package/bin/binaries/probe-v0.6.0-rc284-x86_64-apple-darwin.tar.gz +0 -0
  39. package/bin/binaries/probe-v0.6.0-rc284-x86_64-pc-windows-msvc.zip +0 -0
  40. package/bin/binaries/probe-v0.6.0-rc284-x86_64-unknown-linux-musl.tar.gz +0 -0
@@ -2755,7 +2755,11 @@ async function extractBinary(assetPath, outputDir) {
2755
2755
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2756
2756
  console.log(`Extracting zip to ${extractDir}...`);
2757
2757
  }
2758
- await exec(`unzip -q "${assetPath}" -d "${extractDir}"`);
2758
+ if (isWindows) {
2759
+ await exec(`powershell -NoProfile -Command "Expand-Archive -Path '${assetPath}' -DestinationPath '${extractDir}' -Force"`);
2760
+ } else {
2761
+ await exec(`unzip -q "${assetPath}" -d "${extractDir}"`);
2762
+ }
2759
2763
  } else {
2760
2764
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2761
2765
  console.log(`Copying binary directly to ${binaryPath}`);
@@ -4393,7 +4397,7 @@ Instructions:
4393
4397
  - Format as a structured list if multiple items found
4394
4398
  - If nothing relevant is found in this chunk, respond with "No relevant items found in this chunk."
4395
4399
  - Do NOT summarize the code - extract the specific information requested
4396
- - When done, use the attempt_completion tool with your answer as the result.`;
4400
+ - When done, provide your final answer directly.`;
4397
4401
  try {
4398
4402
  const result = await delegate({
4399
4403
  task,
@@ -4455,7 +4459,7 @@ async function aggregateResults(chunkResults2, aggregation, extractionPrompt, op
4455
4459
  ${stripResultTags(r.result)}`).join("\n\n");
4456
4460
  const completionNote = `
4457
4461
 
4458
- When done, use the attempt_completion tool with your answer as the result.`;
4462
+ When done, provide your final answer directly.`;
4459
4463
  const aggregationPrompts = {
4460
4464
  summarize: `Synthesize these analyses into a comprehensive summary. Combine related findings, remove redundancy, and present a coherent overview.
4461
4465
 
@@ -4549,7 +4553,7 @@ For example, if looking for customer data:
4549
4553
  STEP 3: CREATE THE FINAL PLAN
4550
4554
  Based on your experiments, output the BEST search strategy.
4551
4555
 
4552
- Use attempt_completion with this EXACT format:
4556
+ Provide your answer in this EXACT format:
4553
4557
 
4554
4558
  SEARCH_QUERY: <the query that WORKED in your experiments - use OR for multiple terms>
4555
4559
  AGGREGATION: <summarize | list_unique | count | group_by>
@@ -4615,7 +4619,7 @@ Your answer should:
4615
4619
 
4616
4620
  Format your response as a well-structured document that fully answers: "${question}"
4617
4621
 
4618
- When done, use the attempt_completion tool with your answer as the result.`;
4622
+ When done, provide your final answer directly.`;
4619
4623
  try {
4620
4624
  const result = await delegate({
4621
4625
  task: synthesisTask,
@@ -8893,6 +8897,34 @@ function createMessagePreview(message, charsPerSide = 200) {
8893
8897
  const end = message.substring(message.length - charsPerSide);
8894
8898
  return `${start}...${end}`;
8895
8899
  }
8900
+ function detectStuckResponse(response) {
8901
+ if (!response || typeof response !== "string") {
8902
+ return false;
8903
+ }
8904
+ const stuckPatterns = [
8905
+ /\bi\s+cannot\s+proceed\b/i,
8906
+ /\bi\s+can['']t\s+(?:proceed|continue|move\s+forward)\b/i,
8907
+ /\bunable\s+to\s+(?:proceed|continue|complete)\b/i,
8908
+ /\bblocked\b.*\b(?:proceed|continue)\b/i,
8909
+ /\bneed\s+(?:the|an?)\s+\w+(?:\s+\w+)?\s+to\s+(?:proceed|continue)\b/i,
8910
+ /\brequire[sd]?\s+(?:the|an?)\s+\w+\b.*\bto\s+(?:proceed|continue)\b/i,
8911
+ /\bmissing\s+(?:required|necessary|essential)\b/i,
8912
+ /\bdeadlock\b/i,
8913
+ /\bwe\s+are\s+in\s+a\s+loop\b/i,
8914
+ /\bstuck\s+in\s+a\s+loop\b/i,
8915
+ /\bi\s+(?:have|['']ve)\s+(?:explained|stated|mentioned)\s+(?:this|the\s+situation|it)\s+(?:multiple|several)\s+times\b/i,
8916
+ /\bi\s+(?:cannot|can['']t|could\s+not|couldn['']t)\s+(?:find|locate|get|retrieve|obtain)\s+(?:the|this|that|an?)\b/i,
8917
+ /\bno\s+way\s+to\s+(?:find|get|obtain|retrieve)\b/i,
8918
+ /\bi\s+(?:have|['']ve)\s+exhausted\s+(?:all|my)\s+(?:available\s+)?(?:options|methods|approaches)\b/i,
8919
+ /\bneither\s+of\s+these\s+methods\b/i
8920
+ ];
8921
+ for (const pattern of stuckPatterns) {
8922
+ if (pattern.test(response)) {
8923
+ return true;
8924
+ }
8925
+ }
8926
+ return false;
8927
+ }
8896
8928
  function parseTargets(targets) {
8897
8929
  if (!targets || typeof targets !== "string") {
8898
8930
  return [];
@@ -9402,7 +9434,7 @@ var init_vercel = __esm({
9402
9434
  if (debug) {
9403
9435
  console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" (path: "${searchPath}")`);
9404
9436
  }
9405
- return "DUPLICATE SEARCH BLOCKED: You already searched for this exact query. Changing the path does NOT give different results \u2014 probe searches recursively. Do NOT repeat the same search. Try a genuinely different keyword, use extract to examine results you already found, or use attempt_completion if you have enough information.";
9437
+ return "DUPLICATE SEARCH BLOCKED: You already searched for this exact query. Changing the path does NOT give different results \u2014 probe searches recursively. Do NOT repeat the same search. Try a genuinely different keyword, use extract to examine results you already found, or provide your final answer if you have enough information.";
9406
9438
  }
9407
9439
  previousSearches.add(searchKey);
9408
9440
  paginationCounts.set(searchKey, 0);
@@ -9413,7 +9445,7 @@ var init_vercel = __esm({
9413
9445
  if (debug) {
9414
9446
  console.error(`[DEDUP] Blocked excessive pagination (page ${pageCount}/${MAX_PAGES_PER_QUERY}): "${searchQuery}" in "${searchPath}"`);
9415
9447
  }
9416
- return `PAGINATION LIMIT REACHED: You have already retrieved ${MAX_PAGES_PER_QUERY} pages of results for this query. You have enough results \u2014 use extract to examine specific files, or use attempt_completion to return your findings.`;
9448
+ return `PAGINATION LIMIT REACHED: You have already retrieved ${MAX_PAGES_PER_QUERY} pages of results for this query. You have enough results \u2014 use extract to examine specific files, or provide your final answer with your findings.`;
9417
9449
  }
9418
9450
  }
9419
9451
  try {
@@ -9452,7 +9484,7 @@ var init_vercel = __esm({
9452
9484
  bashConfig: null,
9453
9485
  architectureFileName: options.architectureFileName || null,
9454
9486
  promptType: "code-searcher",
9455
- allowedTools: ["search", "extract", "listFiles", "attempt_completion"],
9487
+ allowedTools: ["search", "extract", "listFiles"],
9456
9488
  searchDelegate: false,
9457
9489
  schema: CODE_SEARCH_SCHEMA,
9458
9490
  parentAbortSignal: options.parentAbortSignal || null
@@ -9623,14 +9655,15 @@ var init_vercel = __esm({
9623
9655
  const parsedTargets = parseTargets(targets);
9624
9656
  extractFiles = parsedTargets.map((target) => resolveTargetPath(target, effectiveCwd));
9625
9657
  if (options.allowedFolders && options.allowedFolders.length > 0) {
9658
+ const { join: pathJoin, sep: pathSep } = await import("path");
9626
9659
  extractFiles = extractFiles.map((target) => {
9627
9660
  const { filePart, suffix } = splitTargetSuffix(target);
9628
9661
  if (existsSync(filePart)) return target;
9629
- const cwdPrefix = effectiveCwd.endsWith("/") ? effectiveCwd : effectiveCwd + "/";
9662
+ const cwdPrefix = effectiveCwd.endsWith(pathSep) ? effectiveCwd : effectiveCwd + pathSep;
9630
9663
  const relativePart = filePart.startsWith(cwdPrefix) ? filePart.slice(cwdPrefix.length) : null;
9631
9664
  if (relativePart) {
9632
9665
  for (const folder of options.allowedFolders) {
9633
- const candidate = folder + "/" + relativePart;
9666
+ const candidate = pathJoin(folder, relativePart);
9634
9667
  if (existsSync(candidate)) {
9635
9668
  if (debug) console.error(`[extract] Auto-fixed path: ${filePart} \u2192 ${candidate}`);
9636
9669
  return candidate + suffix;
@@ -9638,11 +9671,12 @@ var init_vercel = __esm({
9638
9671
  }
9639
9672
  }
9640
9673
  for (const folder of options.allowedFolders) {
9641
- const folderPrefix = folder.endsWith("/") ? folder : folder + "/";
9642
- const wsParent = folderPrefix.replace(/[^/]+\/$/, "");
9674
+ const folderPrefix = folder.endsWith(pathSep) ? folder : folder + pathSep;
9675
+ const sepEscaped = pathSep === "\\" ? "\\\\" : pathSep;
9676
+ const wsParent = folderPrefix.replace(new RegExp("[^" + sepEscaped + "]+" + sepEscaped + "$"), "");
9643
9677
  if (filePart.startsWith(wsParent)) {
9644
9678
  const tail = filePart.slice(wsParent.length);
9645
- const candidate = folderPrefix + tail;
9679
+ const candidate = pathJoin(folderPrefix, tail);
9646
9680
  if (candidate !== filePart && existsSync(candidate)) {
9647
9681
  if (debug) console.error(`[extract] Auto-fixed path via workspace: ${filePart} \u2192 ${candidate}`);
9648
9682
  return candidate + suffix;
@@ -31946,7 +31980,7 @@ For each pending/in_progress task, either:
31946
31980
  - Complete it: call task tool with action="complete", id="task-X"
31947
31981
  - Cancel it: call task tool with action="update", id="task-X", status="cancelled"
31948
31982
 
31949
- After all tasks are resolved, call attempt_completion again.`;
31983
+ After all tasks are resolved, provide your final answer.`;
31950
31984
  }
31951
31985
  function createTaskTool(options = {}) {
31952
31986
  const { taskManager, tracer, debug = false } = options;
@@ -32196,13 +32230,13 @@ Tasks = logical units of work, not files or steps.
32196
32230
 
32197
32231
  1. **Plan**: Call task tool with action="create" and a tasks array up front
32198
32232
  2. **Execute**: Update status to "in_progress" / "completed" as you work. Add, split, or cancel tasks as you learn more.
32199
- 3. **Finish**: All tasks must be "completed" or "cancelled" before calling attempt_completion.
32233
+ 3. **Finish**: All tasks must be "completed" or "cancelled" before providing your final answer.
32200
32234
 
32201
32235
  ## Rules
32202
32236
 
32203
32237
  - Dependencies are enforced: a task cannot start until its dependencies are completed
32204
32238
  - Circular dependencies are rejected
32205
- - attempt_completion is blocked while tasks remain unresolved
32239
+ - Completion is blocked while tasks remain unresolved
32206
32240
  `;
32207
32241
  }
32208
32242
  });
@@ -69068,68 +69102,9 @@ var require_ajv = __commonJS({
69068
69102
  });
69069
69103
 
69070
69104
  // src/agent/schemaUtils.js
69071
- var schemaUtils_exports = {};
69072
- __export(schemaUtils_exports, {
69073
- JsonFixingAgent: () => JsonFixingAgent,
69074
- MermaidFixingAgent: () => MermaidFixingAgent,
69075
- cleanSchemaResponse: () => cleanSchemaResponse,
69076
- createJsonCorrectionPrompt: () => createJsonCorrectionPrompt,
69077
- createMermaidCorrectionPrompt: () => createMermaidCorrectionPrompt,
69078
- createSchemaDefinitionCorrectionPrompt: () => createSchemaDefinitionCorrectionPrompt,
69079
- decodeHtmlEntities: () => decodeHtmlEntities2,
69080
- extractMermaidFromJson: () => extractMermaidFromJson,
69081
- extractMermaidFromMarkdown: () => extractMermaidFromMarkdown,
69082
- generateExampleFromSchema: () => generateExampleFromSchema,
69083
- generateSchemaInstructions: () => generateSchemaInstructions,
69084
- isJsonSchema: () => isJsonSchema,
69085
- isJsonSchemaDefinition: () => isJsonSchemaDefinition,
69086
- isMermaidSchema: () => isMermaidSchema,
69087
- isSimpleTextWrapperSchema: () => isSimpleTextWrapperSchema,
69088
- processSchemaResponse: () => processSchemaResponse,
69089
- replaceMermaidDiagramsInJson: () => replaceMermaidDiagramsInJson,
69090
- replaceMermaidDiagramsInMarkdown: () => replaceMermaidDiagramsInMarkdown,
69091
- sanitizeMarkdownEscapesInJson: () => sanitizeMarkdownEscapesInJson,
69092
- tryAutoWrapForSimpleSchema: () => tryAutoWrapForSimpleSchema,
69093
- tryExtractValidJsonPrefix: () => tryExtractValidJsonPrefix,
69094
- tryMaidAutoFix: () => tryMaidAutoFix,
69095
- validateAndFixMermaidResponse: () => validateAndFixMermaidResponse,
69096
- validateJsonResponse: () => validateJsonResponse,
69097
- validateMermaidDiagram: () => validateMermaidDiagram,
69098
- validateMermaidResponse: () => validateMermaidResponse,
69099
- validateXmlResponse: () => validateXmlResponse
69100
- });
69101
- function generateExampleFromSchema(schema, options = {}) {
69102
- const { debug = false } = options;
69103
- try {
69104
- const parsedSchema = typeof schema === "string" ? JSON.parse(schema) : schema;
69105
- if (parsedSchema.type !== "object" || !parsedSchema.properties) {
69106
- return null;
69107
- }
69108
- const exampleObj = {};
69109
- for (const [key, value] of Object.entries(parsedSchema.properties)) {
69110
- if (value.type === "boolean") {
69111
- exampleObj[key] = false;
69112
- } else if (value.type === "number") {
69113
- exampleObj[key] = 0;
69114
- } else if (value.type === "string") {
69115
- exampleObj[key] = value.description || "your answer here";
69116
- } else if (value.type === "array") {
69117
- exampleObj[key] = [];
69118
- } else {
69119
- exampleObj[key] = {};
69120
- }
69121
- }
69122
- return exampleObj;
69123
- } catch (e) {
69124
- if (debug) {
69125
- console.error("[DEBUG] generateExampleFromSchema: Failed to parse schema:", e.message);
69126
- }
69127
- return null;
69128
- }
69129
- }
69130
69105
  function generateSchemaInstructions(schema, options = {}) {
69131
69106
  const { debug = false } = options;
69132
- let instructions = "\n\nIMPORTANT: When you provide your final answer using attempt_completion, you MUST format it as valid JSON matching this schema:\n\n";
69107
+ let instructions = "\n\nIMPORTANT: When you provide your final answer, you MUST format it as valid JSON matching this schema:\n\n";
69133
69108
  try {
69134
69109
  const parsedSchema = typeof schema === "string" ? JSON.parse(schema) : schema;
69135
69110
  instructions += `${JSON.stringify(parsedSchema, null, 2)}
@@ -69143,7 +69118,7 @@ function generateSchemaInstructions(schema, options = {}) {
69143
69118
 
69144
69119
  `;
69145
69120
  }
69146
- instructions += "Your response inside attempt_completion must be ONLY valid JSON - no plain text, no explanations, no markdown.\n\nIMPORTANT: First complete the requested analysis/task thoroughly, then provide your final answer in the JSON format above.";
69121
+ instructions += "Your final response must be ONLY valid JSON - no plain text, no explanations, no markdown.\n\nIMPORTANT: First complete the requested analysis/task thoroughly, then provide your final answer in the JSON format above.";
69147
69122
  return instructions;
69148
69123
  }
69149
69124
  function enforceNoAdditionalProperties(schema) {
@@ -69840,18 +69815,18 @@ function createJsonCorrectionPrompt(invalidResponse, schema, errorOrValidation,
69840
69815
  const strengthLevels = [
69841
69816
  {
69842
69817
  prefix: "CRITICAL JSON ERROR:",
69843
- instruction: "You MUST fix this and respond using attempt_completion with ONLY valid JSON as the result.",
69844
- emphasis: "Use attempt_completion with ONLY the corrected JSON in the result field. No explanatory text, no markdown, no code blocks."
69818
+ instruction: "You MUST fix this and respond with ONLY valid JSON.",
69819
+ emphasis: "Respond with ONLY the corrected JSON. No explanatory text, no markdown, no code blocks."
69845
69820
  },
69846
69821
  {
69847
69822
  prefix: "URGENT - JSON PARSING FAILED:",
69848
- instruction: "This is your second chance. Use attempt_completion with valid JSON that can be parsed by JSON.parse().",
69849
- emphasis: "ABSOLUTELY NO explanatory text or formatting. Use attempt_completion with ONLY raw JSON in the result."
69823
+ instruction: "This is your second chance. Respond with valid JSON that can be parsed by JSON.parse().",
69824
+ emphasis: "ABSOLUTELY NO explanatory text or formatting. Respond with ONLY raw JSON."
69850
69825
  },
69851
69826
  {
69852
69827
  prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
69853
- instruction: "This is the final retry. You MUST use attempt_completion with ONLY raw JSON in the result field.",
69854
- emphasis: 'CORRECT: <attempt_completion><result>{"key": "value"}</result></attempt_completion>\nWRONG: Here is the JSON: {"key": "value"}\nWRONG: ```json{"key": "value"}```'
69828
+ instruction: "This is the final retry. You MUST respond with ONLY raw JSON.",
69829
+ emphasis: 'CORRECT: {"key": "value"}\nWRONG: Here is the JSON: {"key": "value"}\nWRONG: ```json{"key": "value"}```'
69855
69830
  }
69856
69831
  ];
69857
69832
  const level = Math.min(retryCount, strengthLevels.length - 1);
@@ -69905,28 +69880,6 @@ ${currentLevel.example}
69905
69880
  Return ONLY the JSON data object/array that follows the schema structure. NO schema definitions, NO explanations, NO markdown formatting.`;
69906
69881
  return prompt;
69907
69882
  }
69908
- function isMermaidSchema(schema) {
69909
- if (!schema || typeof schema !== "string") {
69910
- return false;
69911
- }
69912
- const trimmedSchema = schema.trim().toLowerCase();
69913
- const mermaidIndicators = [
69914
- trimmedSchema.includes("mermaid"),
69915
- trimmedSchema.includes("diagram"),
69916
- trimmedSchema.includes("flowchart"),
69917
- trimmedSchema.includes("sequence"),
69918
- trimmedSchema.includes("gantt"),
69919
- trimmedSchema.includes("pie chart"),
69920
- trimmedSchema.includes("state diagram"),
69921
- trimmedSchema.includes("class diagram"),
69922
- trimmedSchema.includes("entity relationship"),
69923
- trimmedSchema.includes("user journey"),
69924
- trimmedSchema.includes("git graph"),
69925
- trimmedSchema.includes("requirement diagram"),
69926
- trimmedSchema.includes("c4 context")
69927
- ];
69928
- return mermaidIndicators.some((indicator) => indicator);
69929
- }
69930
69883
  function extractMermaidFromJson(response) {
69931
69884
  if (!response || typeof response !== "string") {
69932
69885
  return { diagrams: [], jsonPaths: [], parsedJson: null };
@@ -70058,28 +70011,6 @@ ${modifiedJsonString}
70058
70011
  }
70059
70012
  return modifiedJsonString;
70060
70013
  }
70061
- function replaceMermaidDiagramsInMarkdown(originalResponse, correctedDiagrams) {
70062
- if (!originalResponse || typeof originalResponse !== "string") {
70063
- return originalResponse;
70064
- }
70065
- if (!correctedDiagrams || correctedDiagrams.length === 0) {
70066
- return originalResponse;
70067
- }
70068
- const hasJsonDiagrams = correctedDiagrams.some((d) => d.isInJson);
70069
- if (hasJsonDiagrams) {
70070
- return replaceMermaidDiagramsInJson(originalResponse, correctedDiagrams);
70071
- }
70072
- let modifiedResponse = originalResponse;
70073
- const sortedDiagrams = [...correctedDiagrams].sort((a, b) => b.startIndex - a.startIndex);
70074
- for (const diagram of sortedDiagrams) {
70075
- const attributesStr = diagram.attributes ? ` ${diagram.attributes}` : "";
70076
- const newCodeBlock = `\`\`\`mermaid${attributesStr}
70077
- ${diagram.content}
70078
- \`\`\``;
70079
- modifiedResponse = modifiedResponse.slice(0, diagram.startIndex) + newCodeBlock + modifiedResponse.slice(diagram.endIndex);
70080
- }
70081
- return modifiedResponse;
70082
- }
70083
70014
  function replaceSingleMermaidDiagramInResponse(response, originalDiagram, newContent) {
70084
70015
  if (!originalDiagram) {
70085
70016
  return response;
@@ -70164,45 +70095,6 @@ async function validateMermaidResponse(response) {
70164
70095
  errors: errors.length > 0 ? errors : void 0
70165
70096
  };
70166
70097
  }
70167
- function createMermaidCorrectionPrompt(invalidResponse, schema, errors, diagrams) {
70168
- let prompt = `Your previous response contains invalid Mermaid diagrams that cannot be parsed. Here's what you returned:
70169
-
70170
- ${invalidResponse}
70171
-
70172
- Validation Errors:`;
70173
- errors.forEach((error, index) => {
70174
- prompt += `
70175
- ${index + 1}. ${error}`;
70176
- });
70177
- if (diagrams && diagrams.length > 0) {
70178
- prompt += `
70179
-
70180
- Diagram Details:`;
70181
- diagrams.forEach((diagramResult, index) => {
70182
- if (!diagramResult.isValid) {
70183
- prompt += `
70184
-
70185
- Diagram ${index + 1}:`;
70186
- const diagramContent = diagramResult.content || diagramResult.diagram || "";
70187
- prompt += `
70188
- - Content: ${diagramContent.substring(0, 100)}${diagramContent.length > 100 ? "..." : ""}`;
70189
- prompt += `
70190
- - Error: ${diagramResult.error}`;
70191
- if (diagramResult.detailedError && diagramResult.detailedError !== diagramResult.error) {
70192
- prompt += `
70193
- - Details: ${diagramResult.detailedError}`;
70194
- }
70195
- }
70196
- });
70197
- }
70198
- prompt += `
70199
-
70200
- Please correct your response to include valid Mermaid diagrams that match this schema:
70201
- ${schema}
70202
-
70203
- Ensure all Mermaid diagrams are properly formatted within \`\`\`mermaid code blocks and follow correct Mermaid syntax.`;
70204
- return prompt;
70205
- }
70206
70098
  async function tryMaidAutoFix(diagramContent, options = {}) {
70207
70099
  const { debug = false } = options;
70208
70100
  try {
@@ -70512,7 +70404,7 @@ async function validateAndFixMermaidResponse(response, options = {}) {
70512
70404
  };
70513
70405
  }
70514
70406
  }
70515
- var import_ajv, HTML_ENTITY_MAP, sessionIdCounter, JsonFixingAgent, MermaidFixingAgent;
70407
+ var import_ajv, HTML_ENTITY_MAP, sessionIdCounter, MermaidFixingAgent;
70516
70408
  var init_schemaUtils = __esm({
70517
70409
  "src/agent/schemaUtils.js"() {
70518
70410
  "use strict";
@@ -70530,172 +70422,6 @@ var init_schemaUtils = __esm({
70530
70422
  "&nbsp;": " "
70531
70423
  };
70532
70424
  sessionIdCounter = 0;
70533
- JsonFixingAgent = class {
70534
- constructor(options = {}) {
70535
- this.ProbeAgent = null;
70536
- this.options = {
70537
- sessionId: options.sessionId || `json-fixer-${Date.now()}-${sessionIdCounter++}`,
70538
- path: options.path || process.cwd(),
70539
- provider: options.provider,
70540
- model: options.model,
70541
- debug: options.debug,
70542
- tracer: options.tracer,
70543
- // Set to false since we're only fixing JSON syntax, not implementing code
70544
- allowEdit: false
70545
- };
70546
- }
70547
- /**
70548
- * Get the specialized prompt for JSON fixing
70549
- */
70550
- getJsonFixingPrompt() {
70551
- 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.
70552
-
70553
- CORE RESPONSIBILITIES:
70554
- - Analyze JSON for syntax errors and structural issues
70555
- - Fix syntax errors while maintaining the original data's semantic meaning
70556
- - Ensure JSON follows proper RFC 8259 specification
70557
- - Handle all JSON structures: objects, arrays, primitives, nested structures
70558
-
70559
- JSON SYNTAX RULES:
70560
- 1. **Property names**: Must be enclosed in double quotes
70561
- 2. **String values**: Must use double quotes (not single quotes)
70562
- 3. **Numbers**: Can be integers or decimals, no quotes needed
70563
- 4. **Booleans**: true or false (lowercase, no quotes)
70564
- 5. **Null**: null (lowercase, no quotes)
70565
- 6. **Arrays**: Comma-separated values in square brackets [...]
70566
- 7. **Objects**: Comma-separated key-value pairs in curly braces {...}
70567
- 8. **No trailing commas**: Last item in array/object must not have a trailing comma
70568
- 9. **Escape sequences**: Special characters must be escaped (\\n, \\t, \\", \\\\, etc.)
70569
-
70570
- COMMON ERRORS TO FIX:
70571
- 1. **Unquoted property names**: {name: "value"} \u2192 {"name": "value"}
70572
- 2. **Single quotes**: {'key': 'value'} \u2192 {"key": "value"}
70573
- 3. **Trailing commas**: {"a": 1,} \u2192 {"a": 1}
70574
- 4. **Unquoted strings**: {key: value} \u2192 {"key": "value"}
70575
- 5. **Missing commas**: {"a": 1 "b": 2} \u2192 {"a": 1, "b": 2}
70576
- 6. **Extra commas**: {"a": 1,, "b": 2} \u2192 {"a": 1, "b": 2}
70577
- 7. **Unclosed brackets/braces**: {"key": "value" \u2192 {"key": "value"}
70578
- 8. **Invalid escape sequences**: Fix or remove
70579
- 9. **Comments**: Remove // or /* */ comments (not allowed in JSON)
70580
- 10. **Undefined values**: Replace undefined with null
70581
-
70582
- FIXING METHODOLOGY:
70583
- 1. **Identify the error location** from the error message
70584
- 2. **Analyze the context** around the error
70585
- 3. **Apply the appropriate fix** based on JSON syntax rules
70586
- 4. **Preserve data intent** - never change the meaning of the data
70587
- 5. **Validate the result** - ensure it's parseable JSON
70588
-
70589
- CRITICAL RULES:
70590
- - ALWAYS output only the corrected JSON
70591
- - NEVER add explanations, comments, or additional text
70592
- - NEVER wrap in markdown code blocks (no \`\`\`json)
70593
- - PRESERVE the original data structure and values
70594
- - FIX only syntax errors, don't modify the data itself
70595
- - ENSURE the output is valid, parseable JSON
70596
-
70597
- When presented with broken JSON, analyze it thoroughly and provide the corrected version that maintains the original intent while fixing all syntax issues.`;
70598
- }
70599
- /**
70600
- * Initialize the ProbeAgent if not already done
70601
- */
70602
- async initializeAgent() {
70603
- if (!this.ProbeAgent) {
70604
- const { ProbeAgent: ProbeAgent2 } = await Promise.resolve().then(() => (init_ProbeAgent(), ProbeAgent_exports));
70605
- this.ProbeAgent = ProbeAgent2;
70606
- }
70607
- if (!this.agent) {
70608
- this.agent = new this.ProbeAgent({
70609
- sessionId: this.options.sessionId,
70610
- customPrompt: this.getJsonFixingPrompt(),
70611
- path: this.options.path,
70612
- provider: this.options.provider,
70613
- model: this.options.model,
70614
- debug: this.options.debug,
70615
- tracer: this.options.tracer,
70616
- allowEdit: this.options.allowEdit,
70617
- maxIterations: 5,
70618
- // Allow multiple iterations for JSON fixing
70619
- disableJsonValidation: true
70620
- // CRITICAL: Disable JSON validation in nested agent to prevent infinite recursion
70621
- });
70622
- }
70623
- return this.agent;
70624
- }
70625
- /**
70626
- * Fix invalid JSON using the specialized agent
70627
- * @param {string} invalidJson - The broken JSON string
70628
- * @param {string} schema - The original schema for context
70629
- * @param {Object} validationResult - Validation result with error details
70630
- * @param {number} attemptNumber - Current attempt number (for logging)
70631
- * @returns {Promise<string>} - The corrected JSON
70632
- */
70633
- async fixJson(invalidJson, schema, validationResult, attemptNumber = 1) {
70634
- await this.initializeAgent();
70635
- let errorContext = validationResult.error;
70636
- if (validationResult.enhancedError) {
70637
- errorContext = validationResult.enhancedError;
70638
- }
70639
- let schemaErrorDetails = "";
70640
- if (validationResult.errorSummary) {
70641
- schemaErrorDetails = `
70642
-
70643
- Schema Validation Errors:
70644
- ${validationResult.errorSummary}`;
70645
- } else if (validationResult.schemaErrors && validationResult.schemaErrors.length > 0) {
70646
- const errors = validationResult.schemaErrors.map((err) => {
70647
- const path9 = err.instancePath || "(root)";
70648
- return ` ${path9}: ${err.message}`;
70649
- }).join("\n");
70650
- schemaErrorDetails = `
70651
-
70652
- Schema Validation Errors:
70653
- ${errors}`;
70654
- }
70655
- const prompt = `Fix the following invalid JSON.
70656
-
70657
- Error: ${errorContext}${schemaErrorDetails}
70658
-
70659
- Invalid JSON:
70660
- ${invalidJson}
70661
-
70662
- Expected schema structure:
70663
- ${schema}
70664
-
70665
- ${schemaErrorDetails ? "CRITICAL: Pay special attention to the schema validation errors above. The JSON may be syntactically valid but does not conform to the required schema. Make sure to:\n- Include all required fields\n- Use correct data types\n- Remove any additional properties not defined in the schema (if additionalProperties is false)\n- Ensure all values match their schema constraints\n\n" : ""}Provide only the corrected JSON without any markdown formatting or explanations.`;
70666
- try {
70667
- if (this.options.debug) {
70668
- console.log(`[DEBUG] JSON fixing: Attempt ${attemptNumber} to fix JSON with separate agent`);
70669
- }
70670
- const result = await this.agent.answer(prompt, []);
70671
- const cleaned = cleanSchemaResponse(result);
70672
- if (this.options.debug) {
70673
- console.log(`[DEBUG] JSON fixing: Agent returned ${cleaned.length} chars`);
70674
- }
70675
- return cleaned;
70676
- } catch (error) {
70677
- if (this.options.debug) {
70678
- console.error(`[DEBUG] JSON fixing failed: ${error.message}`);
70679
- }
70680
- throw new Error(`Failed to fix JSON: ${error.message}`);
70681
- }
70682
- }
70683
- /**
70684
- * Get token usage information from the specialized agent
70685
- * @returns {Object} - Token usage statistics
70686
- */
70687
- getTokenUsage() {
70688
- return this.agent ? this.agent.getTokenUsage() : null;
70689
- }
70690
- /**
70691
- * Cancel any ongoing operations
70692
- */
70693
- cancel() {
70694
- if (this.agent) {
70695
- this.agent.cancel();
70696
- }
70697
- }
70698
- };
70699
70425
  MermaidFixingAgent = class {
70700
70426
  constructor(options = {}) {
70701
70427
  this.ProbeAgent = null;
@@ -70939,7 +70665,7 @@ When reviewing code:
70939
70665
  "code-review-template": `You are going to perform code review according to provided user rules. Ensure to review only code provided in diff and latest commit, if provided. However you still need to fully understand how modified code works, and read dependencies if something is not clear.`,
70940
70666
  "engineer": `You are a senior engineer focused on software architecture and design.
70941
70667
  Before jumping on the task you first analyse the user request in detail, and try to provide an elegant and concise solution.
70942
- If the solution is clear, you can jump to implementation right away. If not, ask the user a clarification question by calling the attempt_completion tool with the required details.
70668
+ If the solution is clear, you can jump to implementation right away. If not, ask the user a clarification question with the required details.
70943
70669
 
70944
70670
  # Tone and Style
70945
70671
  - Be concise and direct. Explain your approach briefly before implementing, then let the code speak for itself.
@@ -70962,7 +70688,7 @@ If the solution is clear, you can jump to implementation right away. If not, ask
70962
70688
  When the request has **multiple distinct goals** (e.g. "Fix bug A AND add feature B"), use the task tool to track them:
70963
70689
  - Call the task tool with action="create" and a tasks array. Each task must have an "id" field.
70964
70690
  - Update task status to "in_progress" when starting and "completed" when done.
70965
- - All tasks must be completed or cancelled before calling attempt_completion.
70691
+ - All tasks must be completed or cancelled before providing your final answer.
70966
70692
  - Stay flexible \u2014 add, remove, or reorganize tasks as your understanding changes.
70967
70693
 
70968
70694
  Do NOT create tasks for single-goal requests, even complex ones. Multiple internal steps for one goal (search, read, analyze, implement) do not need tasks.
@@ -80190,14 +79916,6 @@ var init_FallbackManager = __esm({
80190
79916
  });
80191
79917
 
80192
79918
  // src/agent/contextCompactor.js
80193
- var contextCompactor_exports = {};
80194
- __export(contextCompactor_exports, {
80195
- calculateCompactionStats: () => calculateCompactionStats,
80196
- compactMessages: () => compactMessages,
80197
- handleContextLimitError: () => handleContextLimitError,
80198
- identifyMessageSegments: () => identifyMessageSegments,
80199
- isContextLimitError: () => isContextLimitError
80200
- });
80201
79919
  function isContextLimitError(error) {
80202
79920
  if (!error) return false;
80203
79921
  const errorMessage = (typeof error === "string" ? error : error?.message || "").toLowerCase();
@@ -80219,9 +79937,15 @@ function messageContainsCompletion(msg) {
80219
79937
  }
80220
79938
  if (Array.isArray(msg.content)) {
80221
79939
  if (msg.content.some((p) => p.type === "tool-call" && p.toolName === "attempt_completion")) return true;
79940
+ const hasToolCalls = msg.content.some((p) => p.type === "tool-call");
79941
+ const hasText = msg.content.some((p) => p.type === "text" && p.text?.trim());
79942
+ if (!hasToolCalls && hasText) return true;
80222
79943
  }
80223
79944
  const text = typeof msg.content === "string" ? msg.content : "";
80224
- return text.includes("attempt_completion");
79945
+ if (text.includes("attempt_completion")) return true;
79946
+ const hasNoToolCalls = !Array.isArray(msg.toolInvocations) && !Array.isArray(msg.tool_calls);
79947
+ if (hasNoToolCalls && text.trim().length > 0) return true;
79948
+ return false;
80225
79949
  }
80226
79950
  function identifyMessageSegments(messages) {
80227
79951
  const segments = [];
@@ -81988,7 +81712,7 @@ import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
81988
81712
  import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
81989
81713
  import { createGoogleGenerativeAI as createGoogleGenerativeAI2 } from "@ai-sdk/google";
81990
81714
  import { createAmazonBedrock as createAmazonBedrock2 } from "@ai-sdk/amazon-bedrock";
81991
- import { streamText as streamText2, tool as tool5, stepCountIs, jsonSchema } from "ai";
81715
+ import { streamText as streamText2, tool as tool5, stepCountIs, jsonSchema, Output } from "ai";
81992
81716
  import { randomUUID as randomUUID5 } from "crypto";
81993
81717
  import { EventEmitter as EventEmitter5 } from "events";
81994
81718
  import { existsSync as existsSync7 } from "fs";
@@ -82002,8 +81726,8 @@ function debugTruncate(s, limit = 200) {
82002
81726
  function debugLogToolResults(toolResults) {
82003
81727
  if (!toolResults || toolResults.length === 0) return;
82004
81728
  for (const tr of toolResults) {
82005
- const argsStr = JSON.stringify(tr.args || {});
82006
- const resultStr = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result || "");
81729
+ const argsStr = tr.args != null ? JSON.stringify(tr.args) : "<no args>";
81730
+ const resultStr = tr.result != null ? typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result) : "<no result>";
82007
81731
  console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
82008
81732
  }
82009
81733
  }
@@ -82099,7 +81823,7 @@ var init_ProbeAgent = __esm({
82099
81823
  * @param {Array<Object>} [options.fallback.providers] - List of provider configurations for custom fallback
82100
81824
  * @param {boolean} [options.fallback.stopOnSuccess=true] - Stop on first success
82101
81825
  * @param {number} [options.fallback.maxTotalAttempts=10] - Maximum total attempts across all providers
82102
- * @param {string} [options.completionPrompt] - Custom prompt to run after attempt_completion for validation/review (runs before mermaid/JSON validation)
81826
+ * @param {string} [options.completionPrompt] - Custom prompt to run after completion for validation/review (runs before mermaid/JSON validation)
82103
81827
  * @param {number} [options.maxOutputTokens] - Maximum tokens for tool output before truncation (default: 20000, can also be set via PROBE_MAX_OUTPUT_TOKENS env var)
82104
81828
  * @param {number} [options.requestTimeout] - Timeout in ms for AI requests (default: 120000 or REQUEST_TIMEOUT env var). Used to abort hung requests.
82105
81829
  * @param {number} [options.maxOperationTimeout] - Maximum timeout in ms for the entire operation including all retries and fallbacks (default: 300000 or MAX_OPERATION_TIMEOUT env var). This is the absolute maximum time for streamTextWithRetryAndFallback.
@@ -83251,12 +82975,11 @@ var init_ProbeAgent = __esm({
83251
82975
  * - Delegate tool param injection
83252
82976
  *
83253
82977
  * @param {Object} options - Options from the answer() call
83254
- * @param {Function} onComplete - Callback when attempt_completion is called (receives result string)
83255
82978
  * @param {Object} context - Execution context { maxIterations, currentMessages }
83256
82979
  * @returns {Object} Tools object for streamText()
83257
82980
  * @private
83258
82981
  */
83259
- _buildNativeTools(options, onComplete, context = {}) {
82982
+ _buildNativeTools(options, context = {}) {
83260
82983
  const { maxIterations = 30 } = context;
83261
82984
  const nativeTools = {};
83262
82985
  const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
@@ -83406,16 +83129,6 @@ var init_ProbeAgent = __esm({
83406
83129
  });
83407
83130
  };
83408
83131
  if (options._disableTools) {
83409
- nativeTools.attempt_completion = tool5({
83410
- description: "Signal task completion and provide the final result to the user",
83411
- inputSchema: external_exports.object({
83412
- result: external_exports.string().describe("The final result to present to the user")
83413
- }),
83414
- execute: async ({ result }) => {
83415
- onComplete(result);
83416
- return result;
83417
- }
83418
- });
83419
83132
  return nativeTools;
83420
83133
  }
83421
83134
  for (const [toolName, toolImpl] of Object.entries(this.toolImplementations)) {
@@ -83426,28 +83139,6 @@ var init_ProbeAgent = __esm({
83426
83139
  nativeTools[toolName] = wrapTool(toolName, schema, description, toolImpl.execute);
83427
83140
  }
83428
83141
  }
83429
- nativeTools.attempt_completion = tool5({
83430
- description: "Signal task completion and provide the final result to the user",
83431
- inputSchema: external_exports.object({
83432
- result: external_exports.string().describe("The final result to present to the user")
83433
- }),
83434
- execute: async ({ result }) => {
83435
- if (this.enableTasks && this.taskManager && this.taskManager.hasIncompleteTasks()) {
83436
- const incompleteTasks = this.taskManager.getIncompleteTasks();
83437
- const highIterationCount = (context.currentIteration || 0) > maxIterations * 0.7;
83438
- if (!highIterationCount) {
83439
- const taskSummary = this.taskManager.getTaskSummary();
83440
- const blockedMessage = createTaskCompletionBlockedMessage(taskSummary);
83441
- if (this.debug) {
83442
- console.log("[DEBUG] Task checkpoint: Blocking completion due to incomplete tasks");
83443
- }
83444
- return blockedMessage;
83445
- }
83446
- }
83447
- onComplete(result);
83448
- return result;
83449
- }
83450
- });
83451
83142
  if (this.mcpBridge && !options._disableTools) {
83452
83143
  const mcpTools = this.mcpBridge.getVercelTools(this._filterMcpTools(this.mcpBridge.getToolNames()));
83453
83144
  for (const [name, mcpTool] of Object.entries(mcpTools)) {
@@ -84331,7 +84022,7 @@ Follow these instructions carefully:
84331
84022
  2. Use the available tools step-by-step to fulfill the request.
84332
84023
  3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns extracted code blocks. Use extract only to expand context or read full files." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
84333
84024
  4. Ensure to get really deep and understand the full picture before answering.
84334
- 5. Once the task is fully completed, use the attempt_completion tool to provide the final result.
84025
+ 5. Once the task is fully completed, provide your final answer directly as text.
84335
84026
  6. ${this.searchDelegate ? "Ask clear, specific questions when searching. Each search should target a distinct concept or question." : "Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results."}
84336
84027
  7. NEVER use bash for code exploration (no grep, cat, find, head, tail, awk, sed) \u2014 always use search and extract tools instead. Bash is only for system operations like building, running tests, or git commands.${this.allowEdit ? `
84337
84028
  7. When modifying files, choose the appropriate tool:
@@ -84509,7 +84200,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84509
84200
  });
84510
84201
  const systemMessage = await this.getSystemMessage();
84511
84202
  let userMessage = { role: "user", content: message.trim() };
84512
- if (options.schema && !options._schemaFormatted) {
84203
+ if (options.schema && !options._schemaFormatted && !options._disableTools) {
84513
84204
  const schemaInstructions = generateSchemaInstructions(options.schema, { debug: this.debug });
84514
84205
  userMessage.content = message.trim() + schemaInstructions;
84515
84206
  }
@@ -84541,8 +84232,17 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84541
84232
  userMessage
84542
84233
  ];
84543
84234
  }
84235
+ if (this.history.length > 0) {
84236
+ const compacted = compactMessages(currentMessages, { keepLastSegment: true, minSegmentsToKeep: 1 });
84237
+ if (compacted.length < currentMessages.length) {
84238
+ const stats = calculateCompactionStats(currentMessages, compacted);
84239
+ if (this.debug) {
84240
+ console.log(`[DEBUG] Proactive history compaction: ${currentMessages.length} \u2192 ${compacted.length} messages (${stats.reductionPercent}% reduction, ~${stats.tokensSaved} tokens saved)`);
84241
+ }
84242
+ currentMessages = compacted;
84243
+ }
84244
+ }
84544
84245
  let currentIteration = 0;
84545
- let completionAttempted = false;
84546
84246
  let finalResult = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
84547
84247
  const baseMaxIterations = options._maxIterationsOverride || this.maxIterations || MAX_TOOL_ITERATIONS;
84548
84248
  const maxIterations = options._maxIterationsOverride ? baseMaxIterations : options.schema ? baseMaxIterations + 4 : baseMaxIterations;
@@ -84658,12 +84358,8 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84658
84358
  console.log(`[DEBUG] Schema provided, using extended iteration limit: ${maxIterations} (base: ${baseMaxIterations})`);
84659
84359
  }
84660
84360
  }
84661
- let completionResult = null;
84662
84361
  const toolContext = { maxIterations, currentIteration: 0, currentMessages };
84663
- const tools2 = this._buildNativeTools(options, (result) => {
84664
- completionResult = result;
84665
- completionAttempted = true;
84666
- }, toolContext);
84362
+ const tools2 = this._buildNativeTools(options, toolContext);
84667
84363
  if (this.debug) {
84668
84364
  const toolNames = Object.keys(tools2);
84669
84365
  console.log(`[DEBUG] Agent tools registered (${toolNames.length}): ${toolNames.join(", ")}`);
@@ -84677,6 +84373,8 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84677
84373
  maxResponseTokens = 32e3;
84678
84374
  }
84679
84375
  }
84376
+ let completionPromptInjected = false;
84377
+ let preCompletionResult = null;
84680
84378
  let compactionAttempted = false;
84681
84379
  while (true) {
84682
84380
  try {
@@ -84685,19 +84383,103 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84685
84383
  model: this.provider ? this.provider(this.model) : this.model,
84686
84384
  messages: messagesForAI,
84687
84385
  tools: tools2,
84688
- stopWhen: stepCountIs(maxIterations),
84386
+ stopWhen: ({ steps }) => {
84387
+ if (steps.length >= maxIterations) return true;
84388
+ const lastStep = steps[steps.length - 1];
84389
+ const modelWantsToStop = lastStep?.finishReason === "stop" && (!lastStep?.toolCalls || lastStep.toolCalls.length === 0);
84390
+ if (modelWantsToStop) {
84391
+ if (this.enableTasks && this.taskManager?.hasIncompleteTasks()) {
84392
+ const highIterationCount = steps.length > maxIterations * 0.7;
84393
+ if (!highIterationCount) return false;
84394
+ }
84395
+ if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected) {
84396
+ preCompletionResult = lastStep.text || null;
84397
+ return false;
84398
+ }
84399
+ }
84400
+ let trailingNoTool = 0;
84401
+ for (let i = steps.length - 1; i >= 0; i--) {
84402
+ if (!steps[i].toolCalls?.length) trailingNoTool++;
84403
+ else break;
84404
+ }
84405
+ if (trailingNoTool >= 5) return true;
84406
+ if (trailingNoTool >= 3) {
84407
+ const recentTexts = steps.slice(-3).map((s) => s.text);
84408
+ if (recentTexts.every((t) => t && t === recentTexts[0])) return true;
84409
+ if (recentTexts.every((t) => detectStuckResponse(t))) return true;
84410
+ }
84411
+ return false;
84412
+ },
84413
+ prepareStep: ({ steps, stepNumber }) => {
84414
+ if (stepNumber === maxIterations - 1) {
84415
+ return {
84416
+ toolChoice: "none"
84417
+ };
84418
+ }
84419
+ const lastStep = steps[steps.length - 1];
84420
+ const modelJustStopped = lastStep?.finishReason === "stop" && (!lastStep?.toolCalls || lastStep.toolCalls.length === 0);
84421
+ if (modelJustStopped) {
84422
+ if (this.enableTasks && this.taskManager?.hasIncompleteTasks()) {
84423
+ const taskSummary = this.taskManager.getTaskSummary();
84424
+ const blockedMessage = createTaskCompletionBlockedMessage(taskSummary);
84425
+ return {
84426
+ userMessage: blockedMessage
84427
+ };
84428
+ }
84429
+ if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected) {
84430
+ completionPromptInjected = true;
84431
+ const resultToReview = lastStep.text || preCompletionResult || "";
84432
+ if (this.debug) {
84433
+ console.log("[DEBUG] Injecting completion prompt into main loop via prepareStep...");
84434
+ }
84435
+ if (this.tracer) {
84436
+ this.tracer.recordEvent("completion_prompt.started", {
84437
+ "completion_prompt.original_result_length": resultToReview.length
84438
+ });
84439
+ }
84440
+ const completionPromptMessage = `${this.completionPrompt}
84441
+
84442
+ Here is the result to review:
84443
+ <result>
84444
+ ${resultToReview}
84445
+ </result>
84446
+
84447
+ Double-check your response based on the criteria above. If everything looks good, respond with your previous answer exactly as-is. If something needs to be fixed or is missing, do it now, then respond with the COMPLETE updated answer (everything you did in total, not just the fix).`;
84448
+ return {
84449
+ userMessage: completionPromptMessage
84450
+ };
84451
+ }
84452
+ }
84453
+ return void 0;
84454
+ },
84689
84455
  maxTokens: maxResponseTokens,
84690
84456
  temperature: 0.3,
84691
- onStepFinish: ({ toolResults, text, finishReason, usage }) => {
84457
+ onStepFinish: (stepResult) => {
84458
+ const { toolResults, toolCalls, text, reasoningText, finishReason, usage } = stepResult;
84692
84459
  currentIteration++;
84693
84460
  toolContext.currentIteration = currentIteration;
84694
84461
  if (this.tracer) {
84695
- this.tracer.addEvent("iteration.step", {
84462
+ const stepEvent = {
84696
84463
  "iteration": currentIteration,
84697
84464
  "max_iterations": maxIterations,
84698
84465
  "finish_reason": finishReason,
84699
84466
  "has_tool_calls": !!(toolResults && toolResults.length > 0)
84700
- });
84467
+ };
84468
+ if (text) {
84469
+ stepEvent["ai.text"] = text.substring(0, 1e4);
84470
+ stepEvent["ai.text.length"] = text.length;
84471
+ }
84472
+ if (reasoningText) {
84473
+ stepEvent["ai.reasoning"] = reasoningText.substring(0, 1e4);
84474
+ stepEvent["ai.reasoning.length"] = reasoningText.length;
84475
+ }
84476
+ if (toolCalls && toolCalls.length > 0) {
84477
+ stepEvent["ai.tool_calls"] = toolCalls.map((tc) => ({
84478
+ name: tc.toolName,
84479
+ args: JSON.stringify(tc.args || {}).substring(0, 2e3)
84480
+ }));
84481
+ }
84482
+ this.tracer.addEvent("iteration.step", stepEvent);
84701
84483
  }
84702
84484
  if (usage) {
84703
84485
  this.tokenCounter.recordUsage(usage);
@@ -84707,10 +84489,32 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84707
84489
  }
84708
84490
  if (this.debug) {
84709
84491
  console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
84492
+ if (text) {
84493
+ console.log(`[DEBUG] model text: ${debugTruncate(text)}`);
84494
+ }
84495
+ if (reasoningText) {
84496
+ console.log(`[DEBUG] reasoning: ${debugTruncate(reasoningText)}`);
84497
+ }
84710
84498
  debugLogToolResults(toolResults);
84711
84499
  }
84712
84500
  }
84713
84501
  };
84502
+ const hasActiveTools = Object.keys(tools2).length > 0;
84503
+ if (options.schema && !hasActiveTools) {
84504
+ try {
84505
+ const parsedSchema = typeof options.schema === "string" ? JSON.parse(options.schema) : options.schema;
84506
+ if (isJsonSchema(options.schema)) {
84507
+ streamOptions.output = Output.object({ schema: jsonSchema(parsedSchema) });
84508
+ if (this.debug) {
84509
+ console.log(`[DEBUG] Native JSON schema output enabled (no active tools)`);
84510
+ }
84511
+ }
84512
+ } catch (e) {
84513
+ if (this.debug) {
84514
+ console.log(`[DEBUG] Failed to set native JSON schema output: ${e.message}`);
84515
+ }
84516
+ }
84517
+ }
84714
84518
  const providerOpts = this._buildThinkingProviderOptions(maxResponseTokens);
84715
84519
  if (providerOpts) {
84716
84520
  streamOptions.providerOptions = providerOpts;
@@ -84720,7 +84524,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84720
84524
  const finalText = await result.text;
84721
84525
  if (this.debug) {
84722
84526
  const steps = await result.steps;
84723
- console.log(`[DEBUG] streamText completed: ${steps?.length || 0} steps, finalText=${finalText?.length || 0} chars, completion=${!!completionResult}`);
84527
+ console.log(`[DEBUG] streamText completed: ${steps?.length || 0} steps, finalText=${finalText?.length || 0} chars`);
84724
84528
  }
84725
84529
  const usage = await result.usage;
84726
84530
  if (usage) {
@@ -84744,18 +84548,24 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84744
84548
  } else {
84745
84549
  aiResult = await executeAIRequest();
84746
84550
  }
84747
- if (completionResult) {
84748
- finalResult = completionResult;
84749
- if (options.onStream && finalResult) {
84750
- const chunkSize = 50;
84751
- for (let i = 0; i < finalResult.length; i += chunkSize) {
84752
- const chunk = finalResult.slice(i, Math.min(i + chunkSize, finalResult.length));
84753
- options.onStream(chunk);
84551
+ if (options.schema && streamOptions.output) {
84552
+ try {
84553
+ const outputObject = await aiResult.result.output;
84554
+ if (outputObject) {
84555
+ finalResult = JSON.stringify(outputObject);
84556
+ } else if (aiResult.finalText) {
84557
+ finalResult = aiResult.finalText;
84558
+ }
84559
+ } catch (e) {
84560
+ if (this.debug) {
84561
+ console.log(`[DEBUG] Native JSON output failed, falling back to text: ${e.message}`);
84562
+ }
84563
+ if (aiResult.finalText) {
84564
+ finalResult = aiResult.finalText;
84754
84565
  }
84755
84566
  }
84756
84567
  } else if (aiResult.finalText) {
84757
84568
  finalResult = aiResult.finalText;
84758
- completionAttempted = true;
84759
84569
  }
84760
84570
  const resultMessages = await aiResult.result.response?.messages;
84761
84571
  if (resultMessages) {
@@ -84763,6 +84573,75 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84763
84573
  currentMessages.push(msg);
84764
84574
  }
84765
84575
  }
84576
+ if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected && finalResult) {
84577
+ completionPromptInjected = true;
84578
+ preCompletionResult = finalResult;
84579
+ if (this.debug) {
84580
+ console.log("[DEBUG] Injecting completion prompt as post-streamText follow-up pass...");
84581
+ }
84582
+ if (this.tracer) {
84583
+ this.tracer.recordEvent("completion_prompt.started", {
84584
+ "completion_prompt.original_result_length": finalResult.length
84585
+ });
84586
+ }
84587
+ const completionPromptMessage = `${this.completionPrompt}
84588
+
84589
+ Here is the result to review:
84590
+ <result>
84591
+ ${finalResult}
84592
+ </result>
84593
+
84594
+ Double-check your response based on the criteria above. If everything looks good, respond with your previous answer exactly as-is. If something needs to be fixed or is missing, do it now, then respond with the COMPLETE updated answer (everything you did in total, not just the fix).`;
84595
+ currentMessages.push({ role: "user", content: completionPromptMessage });
84596
+ const completionMaxIterations = 5;
84597
+ const completionStreamOptions = {
84598
+ model: this.provider ? this.provider(this.model) : this.model,
84599
+ messages: this.prepareMessagesWithImages(currentMessages),
84600
+ tools: tools2,
84601
+ stopWhen: stepCountIs(completionMaxIterations),
84602
+ maxTokens: maxResponseTokens,
84603
+ temperature: 0.3,
84604
+ onStepFinish: ({ toolResults, text, finishReason, usage }) => {
84605
+ if (usage) {
84606
+ this.tokenCounter.recordUsage(usage);
84607
+ }
84608
+ if (options.onStream && text) {
84609
+ options.onStream(text);
84610
+ }
84611
+ if (this.debug) {
84612
+ console.log(`[DEBUG] Completion prompt step finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
84613
+ }
84614
+ }
84615
+ };
84616
+ const providerOpts2 = this._buildThinkingProviderOptions(maxResponseTokens);
84617
+ if (providerOpts2) {
84618
+ completionStreamOptions.providerOptions = providerOpts2;
84619
+ }
84620
+ try {
84621
+ const cpResult = await this.streamTextWithRetryAndFallback(completionStreamOptions);
84622
+ const cpFinalText = await cpResult.text;
84623
+ const cpUsage = await cpResult.usage;
84624
+ if (cpUsage) {
84625
+ this.tokenCounter.recordUsage(cpUsage, cpResult.experimental_providerMetadata);
84626
+ }
84627
+ const cpMessages = await cpResult.response?.messages;
84628
+ if (cpMessages) {
84629
+ for (const msg of cpMessages) {
84630
+ currentMessages.push(msg);
84631
+ }
84632
+ }
84633
+ if (cpFinalText && cpFinalText.trim().length > 0) {
84634
+ finalResult = cpFinalText;
84635
+ }
84636
+ if (this.debug) {
84637
+ console.log(`[DEBUG] Completion prompt follow-up produced ${cpFinalText?.length || 0} chars (using ${cpFinalText && cpFinalText.trim().length > 0 ? "updated" : "original"} result)`);
84638
+ }
84639
+ } catch (cpError) {
84640
+ if (this.debug) {
84641
+ console.log(`[DEBUG] Completion prompt follow-up failed: ${cpError.message}, keeping original result`);
84642
+ }
84643
+ }
84644
+ }
84766
84645
  break;
84767
84646
  } catch (error) {
84768
84647
  if (!compactionAttempted && handleContextLimitError) {
@@ -84792,17 +84671,13 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84792
84671
  continue;
84793
84672
  }
84794
84673
  }
84795
- if (completionResult) {
84796
- finalResult = completionResult;
84797
- break;
84798
- }
84799
84674
  console.error(`Error during streamText:`, error);
84800
84675
  finalResult = `Error: Failed to get response from AI model. ${error.message}`;
84801
84676
  throw new Error(finalResult);
84802
84677
  }
84803
84678
  }
84804
- if (currentIteration >= maxIterations && !completionAttempted) {
84805
- console.warn(`[WARN] Max tool iterations (${maxIterations}) reached for session ${this.sessionId}. Returning current error state.`);
84679
+ if (currentIteration >= maxIterations) {
84680
+ console.warn(`[WARN] Max tool iterations (${maxIterations}) reached for session ${this.sessionId}.`);
84806
84681
  }
84807
84682
  this.history = currentMessages.map((msg) => ({ ...msg }));
84808
84683
  if (this.history.length > MAX_HISTORY_MESSAGES) {
@@ -84825,279 +84700,17 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84825
84700
  } catch (error) {
84826
84701
  console.error(`[ERROR] Failed to save messages to storage:`, error);
84827
84702
  }
84828
- if (completionAttempted && this.completionPrompt && !options._completionPromptProcessed) {
84829
- if (this.debug) {
84830
- console.log("[DEBUG] Running completion prompt as continuation of current session...");
84831
- }
84832
- try {
84833
- const originalResult = finalResult;
84834
- if (this.tracer) {
84835
- this.tracer.recordEvent("completion_prompt.started", {
84836
- "completion_prompt.original_result_length": finalResult?.length || 0
84837
- });
84838
- }
84839
- const completionPromptMessage = `${this.completionPrompt}
84840
-
84841
- Here is the result to review:
84842
- <result>
84843
- ${finalResult}
84844
- </result>
84845
-
84846
- Double-check your response based on the criteria above. If everything looks good, respond with your previous answer exactly as-is using attempt_completion. If something needs to be fixed or is missing, do it now, then respond with the COMPLETE updated answer (everything you did in total, not just the fix) using attempt_completion.`;
84847
- currentMessages.push({ role: "user", content: completionPromptMessage });
84848
- completionResult = null;
84849
- completionAttempted = false;
84850
- const completionMaxIterations = 5;
84851
- const completionStreamOptions = {
84852
- model: this.provider ? this.provider(this.model) : this.model,
84853
- messages: this.prepareMessagesWithImages(currentMessages),
84854
- tools: tools2,
84855
- stopWhen: stepCountIs(completionMaxIterations),
84856
- maxTokens: maxResponseTokens,
84857
- temperature: 0.3,
84858
- onStepFinish: ({ toolResults, text, finishReason, usage }) => {
84859
- if (usage) {
84860
- this.tokenCounter.recordUsage(usage);
84861
- }
84862
- if (options.onStream && text) {
84863
- options.onStream(text);
84864
- }
84865
- if (this.debug) {
84866
- console.log(`[DEBUG] Completion prompt step finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
84867
- debugLogToolResults(toolResults);
84868
- }
84869
- }
84870
- };
84871
- const providerOpts = this._buildThinkingProviderOptions(maxResponseTokens);
84872
- if (providerOpts) {
84873
- completionStreamOptions.providerOptions = providerOpts;
84874
- }
84875
- const cpResult = await this.streamTextWithRetryAndFallback(completionStreamOptions);
84876
- const cpFinalText = await cpResult.text;
84877
- const cpUsage = await cpResult.usage;
84878
- if (cpUsage) {
84879
- this.tokenCounter.recordUsage(cpUsage, cpResult.experimental_providerMetadata);
84880
- }
84881
- const cpMessages = await cpResult.response?.messages;
84882
- if (cpMessages) {
84883
- for (const msg of cpMessages) {
84884
- currentMessages.push(msg);
84885
- }
84886
- }
84887
- if (completionResult) {
84888
- finalResult = completionResult;
84889
- completionAttempted = true;
84890
- } else if (cpFinalText && cpFinalText.trim().length > 0) {
84891
- finalResult = cpFinalText;
84892
- completionAttempted = true;
84893
- } else {
84894
- finalResult = originalResult;
84895
- completionAttempted = true;
84896
- if (this.debug) {
84897
- console.log("[DEBUG] Completion prompt returned empty result, keeping original.");
84898
- }
84899
- }
84900
- if (this.debug) {
84901
- console.log(`[DEBUG] Completion prompt finished. Final result length: ${finalResult?.length || 0}`);
84902
- }
84903
- if (this.tracer) {
84904
- this.tracer.recordEvent("completion_prompt.completed", {
84905
- "completion_prompt.final_result_length": finalResult?.length || 0,
84906
- "completion_prompt.used_original": finalResult === originalResult
84907
- });
84908
- }
84909
- } catch (error) {
84910
- console.error("[ERROR] Completion prompt failed:", error);
84911
- if (this.tracer) {
84912
- this.tracer.recordEvent("completion_prompt.error", {
84913
- "completion_prompt.error": error.message
84914
- });
84915
- }
84916
- }
84703
+ if (completionPromptInjected && this.tracer) {
84704
+ this.tracer.recordEvent("completion_prompt.completed", {
84705
+ "completion_prompt.final_result_length": finalResult?.length || 0,
84706
+ "completion_prompt.used_original": preCompletionResult && finalResult === preCompletionResult
84707
+ });
84917
84708
  }
84918
- const reachedMaxIterations = currentIteration >= maxIterations && !completionAttempted;
84919
- if (options.schema && !options._schemaFormatted && !completionAttempted && !reachedMaxIterations) {
84920
- if (this.debug) {
84921
- console.log("[DEBUG] Schema provided, applying automatic formatting...");
84922
- }
84923
- try {
84924
- const schemaPrompt = `CRITICAL: You MUST respond with ONLY valid JSON DATA that conforms to this schema structure. DO NOT return the schema definition itself.
84925
-
84926
- Schema to follow (this is just the structure - provide ACTUAL DATA):
84927
- ${options.schema}
84928
-
84929
- REQUIREMENTS:
84930
- - Return ONLY the JSON object/array with REAL DATA that matches the schema structure
84931
- - DO NOT return the schema definition itself (no "$schema", "$id", "type", "properties", etc.)
84932
- - NO additional text, explanations, or markdown formatting
84933
- - NO code blocks or backticks
84934
- - The JSON must be parseable by JSON.parse()
84935
- - Fill in actual values that make sense based on your previous response content
84936
-
84937
- EXAMPLE:
84938
- If schema defines {type: "object", properties: {name: {type: "string"}, age: {type: "number"}}}
84939
- Return: {"name": "John Doe", "age": 25}
84940
- NOT: {"type": "object", "properties": {"name": {"type": "string"}}}
84941
-
84942
- Convert your previous response content into actual JSON data that follows this schema structure.`;
84943
- finalResult = await this.answer(schemaPrompt, [], {
84944
- ...options,
84945
- _schemaFormatted: true,
84946
- _completionPromptProcessed: true
84947
- // Prevent cascading completion prompts in retry calls
84948
- });
84949
- if (!this.disableMermaidValidation) {
84950
- try {
84951
- if (this.debug) {
84952
- console.log(`[DEBUG] Mermaid validation: Starting enhanced mermaid validation...`);
84953
- }
84954
- if (this.tracer) {
84955
- this.tracer.recordMermaidValidationEvent("schema_processing_started", {
84956
- "mermaid_validation.context": "schema_processing",
84957
- "mermaid_validation.response_length": finalResult.length
84958
- });
84959
- }
84960
- const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
84961
- debug: this.debug,
84962
- path: this.workspaceRoot || this.allowedFolders[0],
84963
- provider: this.clientApiProvider,
84964
- model: this.model,
84965
- tracer: this.tracer
84966
- });
84967
- if (mermaidValidation.wasFixed) {
84968
- finalResult = mermaidValidation.fixedResponse;
84969
- if (this.debug) {
84970
- console.log(`[DEBUG] Mermaid validation: Diagrams successfully fixed`);
84971
- if (mermaidValidation.performanceMetrics) {
84972
- const metrics = mermaidValidation.performanceMetrics;
84973
- console.log(`[DEBUG] Mermaid validation: Performance - total: ${metrics.totalTimeMs}ms, AI fixing: ${metrics.aiFixingTimeMs}ms`);
84974
- console.log(`[DEBUG] Mermaid validation: Results - ${metrics.diagramsFixed}/${metrics.diagramsProcessed} diagrams fixed`);
84975
- }
84976
- if (mermaidValidation.fixingResults) {
84977
- mermaidValidation.fixingResults.forEach((fixResult, index) => {
84978
- if (fixResult.wasFixed) {
84979
- const method = fixResult.fixedWithHtmlDecoding ? "HTML entity decoding" : "AI correction";
84980
- const time = fixResult.aiFixingTimeMs ? ` in ${fixResult.aiFixingTimeMs}ms` : "";
84981
- console.log(`[DEBUG] Mermaid validation: Fixed diagram ${fixResult.diagramIndex + 1} with ${method}${time}`);
84982
- console.log(`[DEBUG] Mermaid validation: Original error: ${fixResult.originalError}`);
84983
- } else {
84984
- console.log(`[DEBUG] Mermaid validation: Failed to fix diagram ${fixResult.diagramIndex + 1}: ${fixResult.fixingError}`);
84985
- }
84986
- });
84987
- }
84988
- }
84989
- } else if (this.debug) {
84990
- console.log(`[DEBUG] Mermaid validation: No fixes needed or fixes unsuccessful`);
84991
- if (mermaidValidation.diagrams?.length > 0) {
84992
- console.log(`[DEBUG] Mermaid validation: Found ${mermaidValidation.diagrams.length} diagrams, all valid: ${mermaidValidation.isValid}`);
84993
- }
84994
- }
84995
- } catch (error) {
84996
- if (this.debug) {
84997
- console.log(`[DEBUG] Mermaid validation: Process failed with error: ${error.message}`);
84998
- console.log(`[DEBUG] Mermaid validation: Stack trace: ${error.stack}`);
84999
- }
85000
- }
85001
- } else if (this.debug) {
85002
- console.log(`[DEBUG] Mermaid validation: Skipped due to disableMermaidValidation option`);
85003
- }
85004
- finalResult = cleanSchemaResponse(finalResult);
85005
- if (isJsonSchema(options.schema)) {
85006
- if (this.debug) {
85007
- console.log(`[DEBUG] JSON validation: Starting validation process for schema response`);
85008
- console.log(`[DEBUG] JSON validation: Cleaned response length: ${finalResult.length} chars`);
85009
- }
85010
- if (this.tracer) {
85011
- this.tracer.recordJsonValidationEvent("started", {
85012
- "json_validation.response_length": finalResult.length,
85013
- "json_validation.schema_type": "JSON"
85014
- });
85015
- }
85016
- let validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85017
- let retryCount = 0;
85018
- const maxRetries = 3;
85019
- if (validation.isValid && isJsonSchemaDefinition(finalResult, { debug: this.debug })) {
85020
- if (this.debug) {
85021
- console.log(`[DEBUG] JSON validation: Response is a JSON schema definition instead of data, needs correction...`);
85022
- }
85023
- validation = {
85024
- isValid: false,
85025
- error: "Response is a JSON schema definition instead of actual data",
85026
- enhancedError: "Response is a JSON schema definition instead of actual data. Please return data that conforms to the schema, not the schema itself."
85027
- };
85028
- }
85029
- if (!validation.isValid) {
85030
- if (this.debug) {
85031
- console.log(`[DEBUG] JSON validation: Starting separate JsonFixingAgent session...`);
85032
- }
85033
- const { JsonFixingAgent: JsonFixingAgent2 } = await Promise.resolve().then(() => (init_schemaUtils(), schemaUtils_exports));
85034
- const jsonFixer = new JsonFixingAgent2({
85035
- path: this.workspaceRoot || this.allowedFolders[0],
85036
- provider: this.clientApiProvider,
85037
- model: this.model,
85038
- debug: this.debug,
85039
- tracer: this.tracer
85040
- });
85041
- let currentResult = finalResult;
85042
- let currentValidation = validation;
85043
- while (!currentValidation.isValid && retryCount < maxRetries) {
85044
- if (this.debug) {
85045
- console.log(`[DEBUG] JSON validation: Validation failed (attempt ${retryCount + 1}/${maxRetries}):`, currentValidation.error);
85046
- console.log(`[DEBUG] JSON validation: Invalid response sample: ${currentResult.substring(0, 300)}${currentResult.length > 300 ? "..." : ""}`);
85047
- }
85048
- try {
85049
- currentResult = await jsonFixer.fixJson(
85050
- currentResult,
85051
- options.schema,
85052
- currentValidation,
85053
- retryCount + 1
85054
- );
85055
- currentValidation = validateJsonResponse(currentResult, { debug: this.debug, schema: options.schema });
85056
- retryCount++;
85057
- if (this.debug) {
85058
- if (!currentValidation.isValid && retryCount < maxRetries) {
85059
- console.log(`[DEBUG] JSON validation: Still invalid after correction ${retryCount}, retrying...`);
85060
- console.log(`[DEBUG] JSON validation: Corrected response sample: ${currentResult.substring(0, 300)}${currentResult.length > 300 ? "..." : ""}`);
85061
- } else if (currentValidation.isValid) {
85062
- console.log(`[DEBUG] JSON validation: Successfully corrected after ${retryCount} attempts with JsonFixingAgent`);
85063
- }
85064
- }
85065
- } catch (error) {
85066
- if (this.debug) {
85067
- console.error(`[DEBUG] JSON validation: JsonFixingAgent error on attempt ${retryCount + 1}:`, error.message);
85068
- }
85069
- break;
85070
- }
85071
- }
85072
- finalResult = currentResult;
85073
- validation = currentValidation;
85074
- if (!validation.isValid && this.debug) {
85075
- console.log(`[DEBUG] JSON validation: Still invalid after ${maxRetries} correction attempts with JsonFixingAgent:`, validation.error);
85076
- console.log(`[DEBUG] JSON validation: Final invalid response: ${finalResult.substring(0, 500)}${finalResult.length > 500 ? "..." : ""}`);
85077
- } else if (validation.isValid && this.debug) {
85078
- console.log(`[DEBUG] JSON validation: Final validation successful`);
85079
- }
85080
- }
85081
- if (this.tracer) {
85082
- this.tracer.recordJsonValidationEvent("completed", {
85083
- "json_validation.success": validation.isValid,
85084
- "json_validation.retry_count": retryCount,
85085
- "json_validation.max_retries": maxRetries,
85086
- "json_validation.final_response_length": finalResult.length,
85087
- "json_validation.error": validation.isValid ? null : validation.error
85088
- });
85089
- }
85090
- }
85091
- } catch (error) {
85092
- console.error("[ERROR] Schema formatting failed:", error);
85093
- }
85094
- } else if (reachedMaxIterations && options.schema && this.debug) {
85095
- console.log("[DEBUG] Skipping schema formatting due to max iterations reached without completion");
85096
- } else if (completionAttempted && options.schema && !options._schemaFormatted && !options._skipValidation) {
84709
+ if (options.schema && !options._schemaFormatted && !options._skipValidation) {
85097
84710
  try {
85098
84711
  if (!this.disableMermaidValidation) {
85099
84712
  if (this.debug) {
85100
- console.log(`[DEBUG] Mermaid validation: Validating attempt_completion result BEFORE schema cleaning...`);
84713
+ console.log(`[DEBUG] Mermaid validation: Validating result BEFORE schema cleaning...`);
85101
84714
  }
85102
84715
  const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
85103
84716
  debug: this.debug,
@@ -85109,55 +84722,51 @@ Convert your previous response content into actual JSON data that follows this s
85109
84722
  if (mermaidValidation.wasFixed) {
85110
84723
  finalResult = mermaidValidation.fixedResponse;
85111
84724
  if (this.debug) {
85112
- console.log(`[DEBUG] Mermaid validation: attempt_completion diagrams fixed`);
84725
+ console.log(`[DEBUG] Mermaid validation: Diagrams fixed`);
85113
84726
  if (mermaidValidation.performanceMetrics) {
85114
84727
  console.log(`[DEBUG] Mermaid validation: Fixed in ${mermaidValidation.performanceMetrics.totalTimeMs}ms`);
85115
84728
  }
85116
84729
  }
85117
84730
  } else if (this.debug) {
85118
- console.log(`[DEBUG] Mermaid validation: attempt_completion result validation completed (no fixes needed)`);
84731
+ console.log(`[DEBUG] Mermaid validation: Completed (no fixes needed)`);
85119
84732
  }
85120
84733
  } else if (this.debug) {
85121
- console.log(`[DEBUG] Mermaid validation: Skipped for attempt_completion result due to disableMermaidValidation option`);
84734
+ console.log(`[DEBUG] Mermaid validation: Skipped due to disableMermaidValidation option`);
85122
84735
  }
85123
84736
  finalResult = cleanSchemaResponse(finalResult);
85124
84737
  if (isJsonSchema(options.schema)) {
85125
84738
  if (this.debug) {
85126
- console.log(`[DEBUG] JSON validation: Starting validation process for attempt_completion result`);
84739
+ console.log(`[DEBUG] JSON validation: Starting validation process`);
85127
84740
  console.log(`[DEBUG] JSON validation: Response length: ${finalResult.length} chars`);
85128
84741
  }
85129
84742
  if (this.tracer) {
85130
- this.tracer.recordJsonValidationEvent("attempt_completion_started", {
84743
+ this.tracer.recordJsonValidationEvent("started", {
85131
84744
  "json_validation.response_length": finalResult.length,
85132
- "json_validation.schema_type": "JSON",
85133
- "json_validation.context": "attempt_completion"
84745
+ "json_validation.schema_type": "JSON"
85134
84746
  });
85135
84747
  }
85136
- let validation = validateJsonResponse(finalResult, { debug: this.debug });
84748
+ let validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85137
84749
  let retryCount = 0;
85138
84750
  const maxRetries = 3;
85139
84751
  if (validation.isValid && isJsonSchemaDefinition(finalResult, { debug: this.debug })) {
85140
84752
  if (this.debug) {
85141
- console.log(`[DEBUG] JSON validation: attempt_completion response is a JSON schema definition instead of data, correcting...`);
84753
+ console.log(`[DEBUG] JSON validation: Response is a JSON schema definition instead of data, correcting...`);
85142
84754
  }
85143
84755
  const schemaDefinitionPrompt = createSchemaDefinitionCorrectionPrompt(
85144
84756
  finalResult,
85145
84757
  options.schema,
85146
84758
  0
85147
84759
  );
85148
- const { schema: _unusedSchema1, ...schemaDefCorrectionOptions } = options;
85149
84760
  finalResult = await this.answer(schemaDefinitionPrompt, [], {
85150
- ...schemaDefCorrectionOptions,
84761
+ ...options,
85151
84762
  _schemaFormatted: true,
85152
84763
  _skipValidation: true,
85153
- // Skip validation in recursive correction calls to prevent loops
84764
+ _disableTools: true,
85154
84765
  _completionPromptProcessed: true,
85155
- // Prevent cascading completion prompts in retry calls
85156
84766
  _maxIterationsOverride: 3
85157
- // Correction should complete in 1-2 iterations (issue #447)
85158
84767
  });
85159
84768
  finalResult = cleanSchemaResponse(finalResult);
85160
- validation = validateJsonResponse(finalResult);
84769
+ validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85161
84770
  retryCount = 1;
85162
84771
  }
85163
84772
  if (!validation.isValid) {
@@ -85167,20 +84776,16 @@ Convert your previous response content into actual JSON data that follows this s
85167
84776
  console.log(`[DEBUG] JSON validation: Auto-wrapped plain text for simple schema`);
85168
84777
  }
85169
84778
  finalResult = autoWrapped;
85170
- validation = validateJsonResponse(finalResult, { debug: this.debug });
84779
+ validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85171
84780
  }
85172
84781
  }
85173
84782
  while (!validation.isValid && retryCount < maxRetries) {
85174
84783
  if (this.debug) {
85175
- console.log(`[DEBUG] JSON validation: attempt_completion validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
85176
- console.log(`[DEBUG] JSON validation: Invalid response sample: ${finalResult.substring(0, 300)}${finalResult.length > 300 ? "..." : ""}`);
84784
+ console.log(`[DEBUG] JSON validation: Validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
85177
84785
  }
85178
84786
  let correctionPrompt;
85179
84787
  try {
85180
84788
  if (isJsonSchemaDefinition(finalResult, { debug: this.debug })) {
85181
- if (this.debug) {
85182
- console.log(`[DEBUG] JSON validation: attempt_completion response is still a schema definition, using specialized correction`);
85183
- }
85184
84789
  correctionPrompt = createSchemaDefinitionCorrectionPrompt(
85185
84790
  finalResult,
85186
84791
  options.schema,
@@ -85202,47 +84807,43 @@ Convert your previous response content into actual JSON data that follows this s
85202
84807
  retryCount
85203
84808
  );
85204
84809
  }
85205
- const { schema: _unusedSchema2, ...correctionOptions } = options;
85206
84810
  finalResult = await this.answer(correctionPrompt, [], {
85207
- ...correctionOptions,
84811
+ ...options,
85208
84812
  _schemaFormatted: true,
85209
84813
  _skipValidation: true,
85210
- // Skip validation in recursive correction calls to prevent loops
85211
84814
  _disableTools: true,
85212
- // Only allow attempt_completion - prevent AI from using search/query tools
85213
84815
  _completionPromptProcessed: true,
85214
- // Prevent cascading completion prompts in retry calls
85215
84816
  _maxIterationsOverride: 3
85216
- // Correction should complete in 1-2 iterations (issue #447)
85217
84817
  });
85218
84818
  finalResult = cleanSchemaResponse(finalResult);
85219
- validation = validateJsonResponse(finalResult, { debug: this.debug });
84819
+ validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85220
84820
  retryCount++;
85221
84821
  if (this.debug) {
85222
84822
  if (validation.isValid) {
85223
- console.log(`[DEBUG] JSON validation: attempt_completion correction successful on attempt ${retryCount}`);
84823
+ console.log(`[DEBUG] JSON validation: Correction successful on attempt ${retryCount}`);
85224
84824
  } else {
85225
- console.log(`[DEBUG] JSON validation: attempt_completion correction failed on attempt ${retryCount}: ${validation.error}`);
84825
+ console.log(`[DEBUG] JSON validation: Correction failed on attempt ${retryCount}: ${validation.error}`);
85226
84826
  }
85227
84827
  }
85228
84828
  }
85229
84829
  if (this.tracer) {
85230
- this.tracer.recordJsonValidationEvent("attempt_completion_completed", {
84830
+ this.tracer.recordJsonValidationEvent("completed", {
85231
84831
  "json_validation.success": validation.isValid,
85232
84832
  "json_validation.retry_count": retryCount,
85233
- "json_validation.final_response_length": finalResult.length
84833
+ "json_validation.max_retries": maxRetries,
84834
+ "json_validation.final_response_length": finalResult.length,
84835
+ "json_validation.error": validation.isValid ? null : validation.error
85234
84836
  });
85235
84837
  }
85236
84838
  if (!validation.isValid && this.debug) {
85237
- console.log(`[DEBUG] JSON validation: attempt_completion result validation failed after ${maxRetries} attempts: ${validation.error}`);
85238
- console.log(`[DEBUG] JSON validation: Final attempt_completion response: ${finalResult.substring(0, 500)}${finalResult.length > 500 ? "..." : ""}`);
84839
+ console.log(`[DEBUG] JSON validation: Failed after ${maxRetries} attempts: ${validation.error}`);
85239
84840
  } else if (validation.isValid && this.debug) {
85240
- console.log(`[DEBUG] JSON validation: attempt_completion result validation successful`);
84841
+ console.log(`[DEBUG] JSON validation: Final validation successful`);
85241
84842
  }
85242
84843
  }
85243
84844
  } catch (error) {
85244
84845
  if (this.debug) {
85245
- console.log(`[DEBUG] attempt_completion result cleanup failed: ${error.message}`);
84846
+ console.log(`[DEBUG] Schema validation/cleanup failed: ${error.message}`);
85246
84847
  }
85247
84848
  }
85248
84849
  }
@@ -85341,7 +84942,6 @@ Convert your previous response content into actual JSON data that follows this s
85341
84942
  * @returns {Object} Compaction statistics
85342
84943
  */
85343
84944
  async compactHistory(options = {}) {
85344
- const { compactMessages: compactMessages2, calculateCompactionStats: calculateCompactionStats2 } = await Promise.resolve().then(() => (init_contextCompactor(), contextCompactor_exports));
85345
84945
  if (this.history.length === 0) {
85346
84946
  if (this.debug) {
85347
84947
  console.log(`[DEBUG] No history to compact for session ${this.sessionId}`);
@@ -85356,8 +84956,8 @@ Convert your previous response content into actual JSON data that follows this s
85356
84956
  tokensSaved: 0
85357
84957
  };
85358
84958
  }
85359
- const compactedMessages = compactMessages2(this.history, options);
85360
- const stats = calculateCompactionStats2(this.history, compactedMessages);
84959
+ const compactedMessages = compactMessages(this.history, options);
84960
+ const stats = calculateCompactionStats(this.history, compactedMessages);
85361
84961
  this.history = compactedMessages;
85362
84962
  try {
85363
84963
  await this.storageAdapter.clearHistory(this.sessionId);
@@ -86418,7 +86018,9 @@ function parseArgs() {
86418
86018
  disableDefaultBashAllow: false,
86419
86019
  disableDefaultBashDeny: false,
86420
86020
  // Native thinking/reasoning effort
86421
- thinkingEffort: null
86021
+ thinkingEffort: null,
86022
+ // Completion prompt for post-completion validation/review
86023
+ completionPrompt: null
86422
86024
  };
86423
86025
  for (let i = 0; i < args.length; i++) {
86424
86026
  const arg = args[i];
@@ -86452,6 +86054,8 @@ function parseArgs() {
86452
86054
  config.architectureFileName = args[++i];
86453
86055
  } else if (arg === "--schema" && i + 1 < args.length) {
86454
86056
  config.schema = args[++i];
86057
+ } else if (arg === "--completion-prompt" && i + 1 < args.length) {
86058
+ config.completionPrompt = args[++i];
86455
86059
  } else if (arg === "--provider" && i + 1 < args.length) {
86456
86060
  config.provider = args[++i];
86457
86061
  } else if (arg === "--model" && i + 1 < args.length) {
@@ -86538,6 +86142,7 @@ Options:
86538
86142
  --system-prompt <text|file> Custom system prompt (text or file path)
86539
86143
  --architecture-file <name> Architecture context filename in repo root (defaults to AGENTS.md with CLAUDE.md fallback; ARCHITECTURE.md is always included when present)
86540
86144
  --schema <schema|file> Output schema (JSON, XML, any format - text or file path)
86145
+ --completion-prompt <text> Post-completion review prompt (validates/enriches the answer)
86541
86146
  --provider <name> Force AI provider: anthropic, openai, google
86542
86147
  --model <name> Override model name
86543
86148
  --allow-edit Enable code modification capabilities (edit + create tools)
@@ -87040,7 +86645,8 @@ async function main() {
87040
86645
  enableTasks: config.enableTasks,
87041
86646
  thinkingEffort: config.thinkingEffort,
87042
86647
  searchDelegateProvider: config.searchDelegateProvider,
87043
- searchDelegateModel: config.searchDelegateModel
86648
+ searchDelegateModel: config.searchDelegateModel,
86649
+ completionPrompt: config.completionPrompt
87044
86650
  };
87045
86651
  const agent = new ProbeAgent(agentConfig2);
87046
86652
  await agent.initialize();