@probelabs/probe 0.6.0-rc283 → 0.6.0-rc285

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 (39) hide show
  1. package/bin/binaries/probe-v0.6.0-rc285-aarch64-apple-darwin.tar.gz +0 -0
  2. package/bin/binaries/probe-v0.6.0-rc285-aarch64-unknown-linux-musl.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc285-x86_64-apple-darwin.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc285-x86_64-pc-windows-msvc.zip +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc285-x86_64-unknown-linux-musl.tar.gz +0 -0
  6. package/build/agent/ProbeAgent.d.ts +1 -1
  7. package/build/agent/ProbeAgent.js +333 -486
  8. package/build/agent/contextCompactor.js +17 -10
  9. package/build/agent/index.js +301 -702
  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/index.js +0 -2
  15. package/build/tools/analyzeAll.js +4 -4
  16. package/build/tools/common.js +55 -55
  17. package/build/tools/index.js +0 -1
  18. package/build/tools/vercel.js +3 -3
  19. package/cjs/agent/ProbeAgent.cjs +33158 -42860
  20. package/cjs/index.cjs +31325 -41082
  21. package/package.json +8 -8
  22. package/src/agent/ProbeAgent.d.ts +1 -1
  23. package/src/agent/ProbeAgent.js +333 -486
  24. package/src/agent/contextCompactor.js +17 -10
  25. package/src/agent/index.js +8 -2
  26. package/src/agent/schemaUtils.js +10 -11
  27. package/src/agent/shared/prompts.js +2 -2
  28. package/src/agent/tasks/taskTool.js +3 -3
  29. package/src/agent/tools.js +0 -2
  30. package/src/index.js +0 -2
  31. package/src/tools/analyzeAll.js +4 -4
  32. package/src/tools/common.js +55 -55
  33. package/src/tools/index.js +0 -1
  34. package/src/tools/vercel.js +3 -3
  35. package/bin/binaries/probe-v0.6.0-rc283-aarch64-apple-darwin.tar.gz +0 -0
  36. package/bin/binaries/probe-v0.6.0-rc283-aarch64-unknown-linux-musl.tar.gz +0 -0
  37. package/bin/binaries/probe-v0.6.0-rc283-x86_64-apple-darwin.tar.gz +0 -0
  38. package/bin/binaries/probe-v0.6.0-rc283-x86_64-pc-windows-msvc.zip +0 -0
  39. package/bin/binaries/probe-v0.6.0-rc283-x86_64-unknown-linux-musl.tar.gz +0 -0
@@ -4393,7 +4393,7 @@ Instructions:
4393
4393
  - Format as a structured list if multiple items found
4394
4394
  - If nothing relevant is found in this chunk, respond with "No relevant items found in this chunk."
4395
4395
  - Do NOT summarize the code - extract the specific information requested
4396
- - When done, use the attempt_completion tool with your answer as the result.`;
4396
+ - When done, provide your final answer directly.`;
4397
4397
  try {
4398
4398
  const result = await delegate({
4399
4399
  task,
@@ -4455,7 +4455,7 @@ async function aggregateResults(chunkResults2, aggregation, extractionPrompt, op
4455
4455
  ${stripResultTags(r.result)}`).join("\n\n");
4456
4456
  const completionNote = `
4457
4457
 
4458
- When done, use the attempt_completion tool with your answer as the result.`;
4458
+ When done, provide your final answer directly.`;
4459
4459
  const aggregationPrompts = {
4460
4460
  summarize: `Synthesize these analyses into a comprehensive summary. Combine related findings, remove redundancy, and present a coherent overview.
4461
4461
 
@@ -4549,7 +4549,7 @@ For example, if looking for customer data:
4549
4549
  STEP 3: CREATE THE FINAL PLAN
4550
4550
  Based on your experiments, output the BEST search strategy.
4551
4551
 
4552
- Use attempt_completion with this EXACT format:
4552
+ Provide your answer in this EXACT format:
4553
4553
 
4554
4554
  SEARCH_QUERY: <the query that WORKED in your experiments - use OR for multiple terms>
4555
4555
  AGGREGATION: <summarize | list_unique | count | group_by>
@@ -4615,7 +4615,7 @@ Your answer should:
4615
4615
 
4616
4616
  Format your response as a well-structured document that fully answers: "${question}"
4617
4617
 
4618
- When done, use the attempt_completion tool with your answer as the result.`;
4618
+ When done, provide your final answer directly.`;
4619
4619
  try {
4620
4620
  const result = await delegate({
4621
4621
  task: synthesisTask,
@@ -8893,6 +8893,34 @@ function createMessagePreview(message, charsPerSide = 200) {
8893
8893
  const end = message.substring(message.length - charsPerSide);
8894
8894
  return `${start}...${end}`;
8895
8895
  }
8896
+ function detectStuckResponse(response) {
8897
+ if (!response || typeof response !== "string") {
8898
+ return false;
8899
+ }
8900
+ const stuckPatterns = [
8901
+ /\bi\s+cannot\s+proceed\b/i,
8902
+ /\bi\s+can['']t\s+(?:proceed|continue|move\s+forward)\b/i,
8903
+ /\bunable\s+to\s+(?:proceed|continue|complete)\b/i,
8904
+ /\bblocked\b.*\b(?:proceed|continue)\b/i,
8905
+ /\bneed\s+(?:the|an?)\s+\w+(?:\s+\w+)?\s+to\s+(?:proceed|continue)\b/i,
8906
+ /\brequire[sd]?\s+(?:the|an?)\s+\w+\b.*\bto\s+(?:proceed|continue)\b/i,
8907
+ /\bmissing\s+(?:required|necessary|essential)\b/i,
8908
+ /\bdeadlock\b/i,
8909
+ /\bwe\s+are\s+in\s+a\s+loop\b/i,
8910
+ /\bstuck\s+in\s+a\s+loop\b/i,
8911
+ /\bi\s+(?:have|['']ve)\s+(?:explained|stated|mentioned)\s+(?:this|the\s+situation|it)\s+(?:multiple|several)\s+times\b/i,
8912
+ /\bi\s+(?:cannot|can['']t|could\s+not|couldn['']t)\s+(?:find|locate|get|retrieve|obtain)\s+(?:the|this|that|an?)\b/i,
8913
+ /\bno\s+way\s+to\s+(?:find|get|obtain|retrieve)\b/i,
8914
+ /\bi\s+(?:have|['']ve)\s+exhausted\s+(?:all|my)\s+(?:available\s+)?(?:options|methods|approaches)\b/i,
8915
+ /\bneither\s+of\s+these\s+methods\b/i
8916
+ ];
8917
+ for (const pattern of stuckPatterns) {
8918
+ if (pattern.test(response)) {
8919
+ return true;
8920
+ }
8921
+ }
8922
+ return false;
8923
+ }
8896
8924
  function parseTargets(targets) {
8897
8925
  if (!targets || typeof targets !== "string") {
8898
8926
  return [];
@@ -9402,7 +9430,7 @@ var init_vercel = __esm({
9402
9430
  if (debug) {
9403
9431
  console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" (path: "${searchPath}")`);
9404
9432
  }
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.";
9433
+ 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
9434
  }
9407
9435
  previousSearches.add(searchKey);
9408
9436
  paginationCounts.set(searchKey, 0);
@@ -9413,7 +9441,7 @@ var init_vercel = __esm({
9413
9441
  if (debug) {
9414
9442
  console.error(`[DEDUP] Blocked excessive pagination (page ${pageCount}/${MAX_PAGES_PER_QUERY}): "${searchQuery}" in "${searchPath}"`);
9415
9443
  }
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.`;
9444
+ 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
9445
  }
9418
9446
  }
9419
9447
  try {
@@ -9452,7 +9480,7 @@ var init_vercel = __esm({
9452
9480
  bashConfig: null,
9453
9481
  architectureFileName: options.architectureFileName || null,
9454
9482
  promptType: "code-searcher",
9455
- allowedTools: ["search", "extract", "listFiles", "attempt_completion"],
9483
+ allowedTools: ["search", "extract", "listFiles"],
9456
9484
  searchDelegate: false,
9457
9485
  schema: CODE_SEARCH_SCHEMA,
9458
9486
  parentAbortSignal: options.parentAbortSignal || null
@@ -31946,7 +31974,7 @@ For each pending/in_progress task, either:
31946
31974
  - Complete it: call task tool with action="complete", id="task-X"
31947
31975
  - Cancel it: call task tool with action="update", id="task-X", status="cancelled"
31948
31976
 
31949
- After all tasks are resolved, call attempt_completion again.`;
31977
+ After all tasks are resolved, provide your final answer.`;
31950
31978
  }
31951
31979
  function createTaskTool(options = {}) {
31952
31980
  const { taskManager, tracer, debug = false } = options;
@@ -32196,13 +32224,13 @@ Tasks = logical units of work, not files or steps.
32196
32224
 
32197
32225
  1. **Plan**: Call task tool with action="create" and a tasks array up front
32198
32226
  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.
32227
+ 3. **Finish**: All tasks must be "completed" or "cancelled" before providing your final answer.
32200
32228
 
32201
32229
  ## Rules
32202
32230
 
32203
32231
  - Dependencies are enforced: a task cannot start until its dependencies are completed
32204
32232
  - Circular dependencies are rejected
32205
- - attempt_completion is blocked while tasks remain unresolved
32233
+ - Completion is blocked while tasks remain unresolved
32206
32234
  `;
32207
32235
  }
32208
32236
  });
@@ -69068,68 +69096,9 @@ var require_ajv = __commonJS({
69068
69096
  });
69069
69097
 
69070
69098
  // 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
69099
  function generateSchemaInstructions(schema, options = {}) {
69131
69100
  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";
69101
+ let instructions = "\n\nIMPORTANT: When you provide your final answer, you MUST format it as valid JSON matching this schema:\n\n";
69133
69102
  try {
69134
69103
  const parsedSchema = typeof schema === "string" ? JSON.parse(schema) : schema;
69135
69104
  instructions += `${JSON.stringify(parsedSchema, null, 2)}
@@ -69143,7 +69112,7 @@ function generateSchemaInstructions(schema, options = {}) {
69143
69112
 
69144
69113
  `;
69145
69114
  }
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.";
69115
+ 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
69116
  return instructions;
69148
69117
  }
69149
69118
  function enforceNoAdditionalProperties(schema) {
@@ -69840,18 +69809,18 @@ function createJsonCorrectionPrompt(invalidResponse, schema, errorOrValidation,
69840
69809
  const strengthLevels = [
69841
69810
  {
69842
69811
  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."
69812
+ instruction: "You MUST fix this and respond with ONLY valid JSON.",
69813
+ emphasis: "Respond with ONLY the corrected JSON. No explanatory text, no markdown, no code blocks."
69845
69814
  },
69846
69815
  {
69847
69816
  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."
69817
+ instruction: "This is your second chance. Respond with valid JSON that can be parsed by JSON.parse().",
69818
+ emphasis: "ABSOLUTELY NO explanatory text or formatting. Respond with ONLY raw JSON."
69850
69819
  },
69851
69820
  {
69852
69821
  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"}```'
69822
+ instruction: "This is the final retry. You MUST respond with ONLY raw JSON.",
69823
+ emphasis: 'CORRECT: {"key": "value"}\nWRONG: Here is the JSON: {"key": "value"}\nWRONG: ```json{"key": "value"}```'
69855
69824
  }
69856
69825
  ];
69857
69826
  const level = Math.min(retryCount, strengthLevels.length - 1);
@@ -69905,28 +69874,6 @@ ${currentLevel.example}
69905
69874
  Return ONLY the JSON data object/array that follows the schema structure. NO schema definitions, NO explanations, NO markdown formatting.`;
69906
69875
  return prompt;
69907
69876
  }
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
69877
  function extractMermaidFromJson(response) {
69931
69878
  if (!response || typeof response !== "string") {
69932
69879
  return { diagrams: [], jsonPaths: [], parsedJson: null };
@@ -70058,28 +70005,6 @@ ${modifiedJsonString}
70058
70005
  }
70059
70006
  return modifiedJsonString;
70060
70007
  }
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
70008
  function replaceSingleMermaidDiagramInResponse(response, originalDiagram, newContent) {
70084
70009
  if (!originalDiagram) {
70085
70010
  return response;
@@ -70164,45 +70089,6 @@ async function validateMermaidResponse(response) {
70164
70089
  errors: errors.length > 0 ? errors : void 0
70165
70090
  };
70166
70091
  }
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
70092
  async function tryMaidAutoFix(diagramContent, options = {}) {
70207
70093
  const { debug = false } = options;
70208
70094
  try {
@@ -70512,7 +70398,7 @@ async function validateAndFixMermaidResponse(response, options = {}) {
70512
70398
  };
70513
70399
  }
70514
70400
  }
70515
- var import_ajv, HTML_ENTITY_MAP, sessionIdCounter, JsonFixingAgent, MermaidFixingAgent;
70401
+ var import_ajv, HTML_ENTITY_MAP, sessionIdCounter, MermaidFixingAgent;
70516
70402
  var init_schemaUtils = __esm({
70517
70403
  "src/agent/schemaUtils.js"() {
70518
70404
  "use strict";
@@ -70530,172 +70416,6 @@ var init_schemaUtils = __esm({
70530
70416
  "&nbsp;": " "
70531
70417
  };
70532
70418
  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
70419
  MermaidFixingAgent = class {
70700
70420
  constructor(options = {}) {
70701
70421
  this.ProbeAgent = null;
@@ -70939,7 +70659,7 @@ When reviewing code:
70939
70659
  "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
70660
  "engineer": `You are a senior engineer focused on software architecture and design.
70941
70661
  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.
70662
+ 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
70663
 
70944
70664
  # Tone and Style
70945
70665
  - Be concise and direct. Explain your approach briefly before implementing, then let the code speak for itself.
@@ -70962,7 +70682,7 @@ If the solution is clear, you can jump to implementation right away. If not, ask
70962
70682
  When the request has **multiple distinct goals** (e.g. "Fix bug A AND add feature B"), use the task tool to track them:
70963
70683
  - Call the task tool with action="create" and a tasks array. Each task must have an "id" field.
70964
70684
  - Update task status to "in_progress" when starting and "completed" when done.
70965
- - All tasks must be completed or cancelled before calling attempt_completion.
70685
+ - All tasks must be completed or cancelled before providing your final answer.
70966
70686
  - Stay flexible \u2014 add, remove, or reorganize tasks as your understanding changes.
70967
70687
 
70968
70688
  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.
@@ -80219,9 +79939,15 @@ function messageContainsCompletion(msg) {
80219
79939
  }
80220
79940
  if (Array.isArray(msg.content)) {
80221
79941
  if (msg.content.some((p) => p.type === "tool-call" && p.toolName === "attempt_completion")) return true;
79942
+ const hasToolCalls = msg.content.some((p) => p.type === "tool-call");
79943
+ const hasText = msg.content.some((p) => p.type === "text" && p.text?.trim());
79944
+ if (!hasToolCalls && hasText) return true;
80222
79945
  }
80223
79946
  const text = typeof msg.content === "string" ? msg.content : "";
80224
- return text.includes("attempt_completion");
79947
+ if (text.includes("attempt_completion")) return true;
79948
+ const hasNoToolCalls = !Array.isArray(msg.toolInvocations) && !Array.isArray(msg.tool_calls);
79949
+ if (hasNoToolCalls && text.trim().length > 0) return true;
79950
+ return false;
80225
79951
  }
80226
79952
  function identifyMessageSegments(messages) {
80227
79953
  const segments = [];
@@ -81988,7 +81714,7 @@ import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
81988
81714
  import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
81989
81715
  import { createGoogleGenerativeAI as createGoogleGenerativeAI2 } from "@ai-sdk/google";
81990
81716
  import { createAmazonBedrock as createAmazonBedrock2 } from "@ai-sdk/amazon-bedrock";
81991
- import { streamText as streamText2, tool as tool5, stepCountIs, jsonSchema } from "ai";
81717
+ import { streamText as streamText2, tool as tool5, stepCountIs, jsonSchema, Output } from "ai";
81992
81718
  import { randomUUID as randomUUID5 } from "crypto";
81993
81719
  import { EventEmitter as EventEmitter5 } from "events";
81994
81720
  import { existsSync as existsSync7 } from "fs";
@@ -82002,8 +81728,8 @@ function debugTruncate(s, limit = 200) {
82002
81728
  function debugLogToolResults(toolResults) {
82003
81729
  if (!toolResults || toolResults.length === 0) return;
82004
81730
  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 || "");
81731
+ const argsStr = tr.args != null ? JSON.stringify(tr.args) : "<no args>";
81732
+ const resultStr = tr.result != null ? typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result) : "<no result>";
82007
81733
  console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
82008
81734
  }
82009
81735
  }
@@ -82099,7 +81825,7 @@ var init_ProbeAgent = __esm({
82099
81825
  * @param {Array<Object>} [options.fallback.providers] - List of provider configurations for custom fallback
82100
81826
  * @param {boolean} [options.fallback.stopOnSuccess=true] - Stop on first success
82101
81827
  * @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)
81828
+ * @param {string} [options.completionPrompt] - Custom prompt to run after completion for validation/review (runs before mermaid/JSON validation)
82103
81829
  * @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
81830
  * @param {number} [options.requestTimeout] - Timeout in ms for AI requests (default: 120000 or REQUEST_TIMEOUT env var). Used to abort hung requests.
82105
81831
  * @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 +82977,11 @@ var init_ProbeAgent = __esm({
83251
82977
  * - Delegate tool param injection
83252
82978
  *
83253
82979
  * @param {Object} options - Options from the answer() call
83254
- * @param {Function} onComplete - Callback when attempt_completion is called (receives result string)
83255
82980
  * @param {Object} context - Execution context { maxIterations, currentMessages }
83256
82981
  * @returns {Object} Tools object for streamText()
83257
82982
  * @private
83258
82983
  */
83259
- _buildNativeTools(options, onComplete, context = {}) {
82984
+ _buildNativeTools(options, context = {}) {
83260
82985
  const { maxIterations = 30 } = context;
83261
82986
  const nativeTools = {};
83262
82987
  const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
@@ -83406,16 +83131,6 @@ var init_ProbeAgent = __esm({
83406
83131
  });
83407
83132
  };
83408
83133
  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
83134
  return nativeTools;
83420
83135
  }
83421
83136
  for (const [toolName, toolImpl] of Object.entries(this.toolImplementations)) {
@@ -83426,28 +83141,6 @@ var init_ProbeAgent = __esm({
83426
83141
  nativeTools[toolName] = wrapTool(toolName, schema, description, toolImpl.execute);
83427
83142
  }
83428
83143
  }
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
83144
  if (this.mcpBridge && !options._disableTools) {
83452
83145
  const mcpTools = this.mcpBridge.getVercelTools(this._filterMcpTools(this.mcpBridge.getToolNames()));
83453
83146
  for (const [name, mcpTool] of Object.entries(mcpTools)) {
@@ -84331,7 +84024,7 @@ Follow these instructions carefully:
84331
84024
  2. Use the available tools step-by-step to fulfill the request.
84332
84025
  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
84026
  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.
84027
+ 5. Once the task is fully completed, provide your final answer directly as text.
84335
84028
  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
84029
  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
84030
  7. When modifying files, choose the appropriate tool:
@@ -84509,7 +84202,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84509
84202
  });
84510
84203
  const systemMessage = await this.getSystemMessage();
84511
84204
  let userMessage = { role: "user", content: message.trim() };
84512
- if (options.schema && !options._schemaFormatted) {
84205
+ if (options.schema && !options._schemaFormatted && !options._disableTools) {
84513
84206
  const schemaInstructions = generateSchemaInstructions(options.schema, { debug: this.debug });
84514
84207
  userMessage.content = message.trim() + schemaInstructions;
84515
84208
  }
@@ -84542,7 +84235,6 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84542
84235
  ];
84543
84236
  }
84544
84237
  let currentIteration = 0;
84545
- let completionAttempted = false;
84546
84238
  let finalResult = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
84547
84239
  const baseMaxIterations = options._maxIterationsOverride || this.maxIterations || MAX_TOOL_ITERATIONS;
84548
84240
  const maxIterations = options._maxIterationsOverride ? baseMaxIterations : options.schema ? baseMaxIterations + 4 : baseMaxIterations;
@@ -84658,12 +84350,8 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84658
84350
  console.log(`[DEBUG] Schema provided, using extended iteration limit: ${maxIterations} (base: ${baseMaxIterations})`);
84659
84351
  }
84660
84352
  }
84661
- let completionResult = null;
84662
84353
  const toolContext = { maxIterations, currentIteration: 0, currentMessages };
84663
- const tools2 = this._buildNativeTools(options, (result) => {
84664
- completionResult = result;
84665
- completionAttempted = true;
84666
- }, toolContext);
84354
+ const tools2 = this._buildNativeTools(options, toolContext);
84667
84355
  if (this.debug) {
84668
84356
  const toolNames = Object.keys(tools2);
84669
84357
  console.log(`[DEBUG] Agent tools registered (${toolNames.length}): ${toolNames.join(", ")}`);
@@ -84677,6 +84365,8 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84677
84365
  maxResponseTokens = 32e3;
84678
84366
  }
84679
84367
  }
84368
+ let completionPromptInjected = false;
84369
+ let preCompletionResult = null;
84680
84370
  let compactionAttempted = false;
84681
84371
  while (true) {
84682
84372
  try {
@@ -84685,19 +84375,103 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84685
84375
  model: this.provider ? this.provider(this.model) : this.model,
84686
84376
  messages: messagesForAI,
84687
84377
  tools: tools2,
84688
- stopWhen: stepCountIs(maxIterations),
84378
+ stopWhen: ({ steps }) => {
84379
+ if (steps.length >= maxIterations) return true;
84380
+ const lastStep = steps[steps.length - 1];
84381
+ const modelWantsToStop = lastStep?.finishReason === "stop" && (!lastStep?.toolCalls || lastStep.toolCalls.length === 0);
84382
+ if (modelWantsToStop) {
84383
+ if (this.enableTasks && this.taskManager?.hasIncompleteTasks()) {
84384
+ const highIterationCount = steps.length > maxIterations * 0.7;
84385
+ if (!highIterationCount) return false;
84386
+ }
84387
+ if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected) {
84388
+ preCompletionResult = lastStep.text || null;
84389
+ return false;
84390
+ }
84391
+ }
84392
+ let trailingNoTool = 0;
84393
+ for (let i = steps.length - 1; i >= 0; i--) {
84394
+ if (!steps[i].toolCalls?.length) trailingNoTool++;
84395
+ else break;
84396
+ }
84397
+ if (trailingNoTool >= 5) return true;
84398
+ if (trailingNoTool >= 3) {
84399
+ const recentTexts = steps.slice(-3).map((s) => s.text);
84400
+ if (recentTexts.every((t) => t && t === recentTexts[0])) return true;
84401
+ if (recentTexts.every((t) => detectStuckResponse(t))) return true;
84402
+ }
84403
+ return false;
84404
+ },
84405
+ prepareStep: ({ steps, stepNumber }) => {
84406
+ if (stepNumber === maxIterations - 1) {
84407
+ return {
84408
+ toolChoice: "none"
84409
+ };
84410
+ }
84411
+ const lastStep = steps[steps.length - 1];
84412
+ const modelJustStopped = lastStep?.finishReason === "stop" && (!lastStep?.toolCalls || lastStep.toolCalls.length === 0);
84413
+ if (modelJustStopped) {
84414
+ if (this.enableTasks && this.taskManager?.hasIncompleteTasks()) {
84415
+ const taskSummary = this.taskManager.getTaskSummary();
84416
+ const blockedMessage = createTaskCompletionBlockedMessage(taskSummary);
84417
+ return {
84418
+ userMessage: blockedMessage
84419
+ };
84420
+ }
84421
+ if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected) {
84422
+ completionPromptInjected = true;
84423
+ const resultToReview = lastStep.text || preCompletionResult || "";
84424
+ if (this.debug) {
84425
+ console.log("[DEBUG] Injecting completion prompt into main loop via prepareStep...");
84426
+ }
84427
+ if (this.tracer) {
84428
+ this.tracer.recordEvent("completion_prompt.started", {
84429
+ "completion_prompt.original_result_length": resultToReview.length
84430
+ });
84431
+ }
84432
+ const completionPromptMessage = `${this.completionPrompt}
84433
+
84434
+ Here is the result to review:
84435
+ <result>
84436
+ ${resultToReview}
84437
+ </result>
84438
+
84439
+ 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).`;
84440
+ return {
84441
+ userMessage: completionPromptMessage
84442
+ };
84443
+ }
84444
+ }
84445
+ return void 0;
84446
+ },
84689
84447
  maxTokens: maxResponseTokens,
84690
84448
  temperature: 0.3,
84691
- onStepFinish: ({ toolResults, text, finishReason, usage }) => {
84449
+ onStepFinish: (stepResult) => {
84450
+ const { toolResults, toolCalls, text, reasoningText, finishReason, usage } = stepResult;
84692
84451
  currentIteration++;
84693
84452
  toolContext.currentIteration = currentIteration;
84694
84453
  if (this.tracer) {
84695
- this.tracer.addEvent("iteration.step", {
84454
+ const stepEvent = {
84696
84455
  "iteration": currentIteration,
84697
84456
  "max_iterations": maxIterations,
84698
84457
  "finish_reason": finishReason,
84699
84458
  "has_tool_calls": !!(toolResults && toolResults.length > 0)
84700
- });
84459
+ };
84460
+ if (text) {
84461
+ stepEvent["ai.text"] = text.substring(0, 1e4);
84462
+ stepEvent["ai.text.length"] = text.length;
84463
+ }
84464
+ if (reasoningText) {
84465
+ stepEvent["ai.reasoning"] = reasoningText.substring(0, 1e4);
84466
+ stepEvent["ai.reasoning.length"] = reasoningText.length;
84467
+ }
84468
+ if (toolCalls && toolCalls.length > 0) {
84469
+ stepEvent["ai.tool_calls"] = toolCalls.map((tc) => ({
84470
+ name: tc.toolName,
84471
+ args: JSON.stringify(tc.args || {}).substring(0, 2e3)
84472
+ }));
84473
+ }
84474
+ this.tracer.addEvent("iteration.step", stepEvent);
84701
84475
  }
84702
84476
  if (usage) {
84703
84477
  this.tokenCounter.recordUsage(usage);
@@ -84707,10 +84481,32 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84707
84481
  }
84708
84482
  if (this.debug) {
84709
84483
  console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
84484
+ if (text) {
84485
+ console.log(`[DEBUG] model text: ${debugTruncate(text)}`);
84486
+ }
84487
+ if (reasoningText) {
84488
+ console.log(`[DEBUG] reasoning: ${debugTruncate(reasoningText)}`);
84489
+ }
84710
84490
  debugLogToolResults(toolResults);
84711
84491
  }
84712
84492
  }
84713
84493
  };
84494
+ const hasActiveTools = Object.keys(tools2).length > 0;
84495
+ if (options.schema && !hasActiveTools) {
84496
+ try {
84497
+ const parsedSchema = typeof options.schema === "string" ? JSON.parse(options.schema) : options.schema;
84498
+ if (isJsonSchema(options.schema)) {
84499
+ streamOptions.output = Output.object({ schema: jsonSchema(parsedSchema) });
84500
+ if (this.debug) {
84501
+ console.log(`[DEBUG] Native JSON schema output enabled (no active tools)`);
84502
+ }
84503
+ }
84504
+ } catch (e) {
84505
+ if (this.debug) {
84506
+ console.log(`[DEBUG] Failed to set native JSON schema output: ${e.message}`);
84507
+ }
84508
+ }
84509
+ }
84714
84510
  const providerOpts = this._buildThinkingProviderOptions(maxResponseTokens);
84715
84511
  if (providerOpts) {
84716
84512
  streamOptions.providerOptions = providerOpts;
@@ -84720,7 +84516,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84720
84516
  const finalText = await result.text;
84721
84517
  if (this.debug) {
84722
84518
  const steps = await result.steps;
84723
- console.log(`[DEBUG] streamText completed: ${steps?.length || 0} steps, finalText=${finalText?.length || 0} chars, completion=${!!completionResult}`);
84519
+ console.log(`[DEBUG] streamText completed: ${steps?.length || 0} steps, finalText=${finalText?.length || 0} chars`);
84724
84520
  }
84725
84521
  const usage = await result.usage;
84726
84522
  if (usage) {
@@ -84744,18 +84540,24 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84744
84540
  } else {
84745
84541
  aiResult = await executeAIRequest();
84746
84542
  }
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);
84543
+ if (options.schema && streamOptions.output) {
84544
+ try {
84545
+ const outputObject = await aiResult.result.output;
84546
+ if (outputObject) {
84547
+ finalResult = JSON.stringify(outputObject);
84548
+ } else if (aiResult.finalText) {
84549
+ finalResult = aiResult.finalText;
84550
+ }
84551
+ } catch (e) {
84552
+ if (this.debug) {
84553
+ console.log(`[DEBUG] Native JSON output failed, falling back to text: ${e.message}`);
84554
+ }
84555
+ if (aiResult.finalText) {
84556
+ finalResult = aiResult.finalText;
84754
84557
  }
84755
84558
  }
84756
84559
  } else if (aiResult.finalText) {
84757
84560
  finalResult = aiResult.finalText;
84758
- completionAttempted = true;
84759
84561
  }
84760
84562
  const resultMessages = await aiResult.result.response?.messages;
84761
84563
  if (resultMessages) {
@@ -84763,6 +84565,75 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84763
84565
  currentMessages.push(msg);
84764
84566
  }
84765
84567
  }
84568
+ if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected && finalResult) {
84569
+ completionPromptInjected = true;
84570
+ preCompletionResult = finalResult;
84571
+ if (this.debug) {
84572
+ console.log("[DEBUG] Injecting completion prompt as post-streamText follow-up pass...");
84573
+ }
84574
+ if (this.tracer) {
84575
+ this.tracer.recordEvent("completion_prompt.started", {
84576
+ "completion_prompt.original_result_length": finalResult.length
84577
+ });
84578
+ }
84579
+ const completionPromptMessage = `${this.completionPrompt}
84580
+
84581
+ Here is the result to review:
84582
+ <result>
84583
+ ${finalResult}
84584
+ </result>
84585
+
84586
+ 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).`;
84587
+ currentMessages.push({ role: "user", content: completionPromptMessage });
84588
+ const completionMaxIterations = 5;
84589
+ const completionStreamOptions = {
84590
+ model: this.provider ? this.provider(this.model) : this.model,
84591
+ messages: this.prepareMessagesWithImages(currentMessages),
84592
+ tools: tools2,
84593
+ stopWhen: stepCountIs(completionMaxIterations),
84594
+ maxTokens: maxResponseTokens,
84595
+ temperature: 0.3,
84596
+ onStepFinish: ({ toolResults, text, finishReason, usage }) => {
84597
+ if (usage) {
84598
+ this.tokenCounter.recordUsage(usage);
84599
+ }
84600
+ if (options.onStream && text) {
84601
+ options.onStream(text);
84602
+ }
84603
+ if (this.debug) {
84604
+ console.log(`[DEBUG] Completion prompt step finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
84605
+ }
84606
+ }
84607
+ };
84608
+ const providerOpts2 = this._buildThinkingProviderOptions(maxResponseTokens);
84609
+ if (providerOpts2) {
84610
+ completionStreamOptions.providerOptions = providerOpts2;
84611
+ }
84612
+ try {
84613
+ const cpResult = await this.streamTextWithRetryAndFallback(completionStreamOptions);
84614
+ const cpFinalText = await cpResult.text;
84615
+ const cpUsage = await cpResult.usage;
84616
+ if (cpUsage) {
84617
+ this.tokenCounter.recordUsage(cpUsage, cpResult.experimental_providerMetadata);
84618
+ }
84619
+ const cpMessages = await cpResult.response?.messages;
84620
+ if (cpMessages) {
84621
+ for (const msg of cpMessages) {
84622
+ currentMessages.push(msg);
84623
+ }
84624
+ }
84625
+ if (cpFinalText && cpFinalText.trim().length > 0) {
84626
+ finalResult = cpFinalText;
84627
+ }
84628
+ if (this.debug) {
84629
+ console.log(`[DEBUG] Completion prompt follow-up produced ${cpFinalText?.length || 0} chars (using ${cpFinalText && cpFinalText.trim().length > 0 ? "updated" : "original"} result)`);
84630
+ }
84631
+ } catch (cpError) {
84632
+ if (this.debug) {
84633
+ console.log(`[DEBUG] Completion prompt follow-up failed: ${cpError.message}, keeping original result`);
84634
+ }
84635
+ }
84636
+ }
84766
84637
  break;
84767
84638
  } catch (error) {
84768
84639
  if (!compactionAttempted && handleContextLimitError) {
@@ -84792,17 +84663,13 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84792
84663
  continue;
84793
84664
  }
84794
84665
  }
84795
- if (completionResult) {
84796
- finalResult = completionResult;
84797
- break;
84798
- }
84799
84666
  console.error(`Error during streamText:`, error);
84800
84667
  finalResult = `Error: Failed to get response from AI model. ${error.message}`;
84801
84668
  throw new Error(finalResult);
84802
84669
  }
84803
84670
  }
84804
- if (currentIteration >= maxIterations && !completionAttempted) {
84805
- console.warn(`[WARN] Max tool iterations (${maxIterations}) reached for session ${this.sessionId}. Returning current error state.`);
84671
+ if (currentIteration >= maxIterations) {
84672
+ console.warn(`[WARN] Max tool iterations (${maxIterations}) reached for session ${this.sessionId}.`);
84806
84673
  }
84807
84674
  this.history = currentMessages.map((msg) => ({ ...msg }));
84808
84675
  if (this.history.length > MAX_HISTORY_MESSAGES) {
@@ -84825,279 +84692,17 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84825
84692
  } catch (error) {
84826
84693
  console.error(`[ERROR] Failed to save messages to storage:`, error);
84827
84694
  }
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
- }
84695
+ if (completionPromptInjected && this.tracer) {
84696
+ this.tracer.recordEvent("completion_prompt.completed", {
84697
+ "completion_prompt.final_result_length": finalResult?.length || 0,
84698
+ "completion_prompt.used_original": preCompletionResult && finalResult === preCompletionResult
84699
+ });
84917
84700
  }
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) {
84701
+ if (options.schema && !options._schemaFormatted && !options._skipValidation) {
85097
84702
  try {
85098
84703
  if (!this.disableMermaidValidation) {
85099
84704
  if (this.debug) {
85100
- console.log(`[DEBUG] Mermaid validation: Validating attempt_completion result BEFORE schema cleaning...`);
84705
+ console.log(`[DEBUG] Mermaid validation: Validating result BEFORE schema cleaning...`);
85101
84706
  }
85102
84707
  const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
85103
84708
  debug: this.debug,
@@ -85109,55 +84714,51 @@ Convert your previous response content into actual JSON data that follows this s
85109
84714
  if (mermaidValidation.wasFixed) {
85110
84715
  finalResult = mermaidValidation.fixedResponse;
85111
84716
  if (this.debug) {
85112
- console.log(`[DEBUG] Mermaid validation: attempt_completion diagrams fixed`);
84717
+ console.log(`[DEBUG] Mermaid validation: Diagrams fixed`);
85113
84718
  if (mermaidValidation.performanceMetrics) {
85114
84719
  console.log(`[DEBUG] Mermaid validation: Fixed in ${mermaidValidation.performanceMetrics.totalTimeMs}ms`);
85115
84720
  }
85116
84721
  }
85117
84722
  } else if (this.debug) {
85118
- console.log(`[DEBUG] Mermaid validation: attempt_completion result validation completed (no fixes needed)`);
84723
+ console.log(`[DEBUG] Mermaid validation: Completed (no fixes needed)`);
85119
84724
  }
85120
84725
  } else if (this.debug) {
85121
- console.log(`[DEBUG] Mermaid validation: Skipped for attempt_completion result due to disableMermaidValidation option`);
84726
+ console.log(`[DEBUG] Mermaid validation: Skipped due to disableMermaidValidation option`);
85122
84727
  }
85123
84728
  finalResult = cleanSchemaResponse(finalResult);
85124
84729
  if (isJsonSchema(options.schema)) {
85125
84730
  if (this.debug) {
85126
- console.log(`[DEBUG] JSON validation: Starting validation process for attempt_completion result`);
84731
+ console.log(`[DEBUG] JSON validation: Starting validation process`);
85127
84732
  console.log(`[DEBUG] JSON validation: Response length: ${finalResult.length} chars`);
85128
84733
  }
85129
84734
  if (this.tracer) {
85130
- this.tracer.recordJsonValidationEvent("attempt_completion_started", {
84735
+ this.tracer.recordJsonValidationEvent("started", {
85131
84736
  "json_validation.response_length": finalResult.length,
85132
- "json_validation.schema_type": "JSON",
85133
- "json_validation.context": "attempt_completion"
84737
+ "json_validation.schema_type": "JSON"
85134
84738
  });
85135
84739
  }
85136
- let validation = validateJsonResponse(finalResult, { debug: this.debug });
84740
+ let validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85137
84741
  let retryCount = 0;
85138
84742
  const maxRetries = 3;
85139
84743
  if (validation.isValid && isJsonSchemaDefinition(finalResult, { debug: this.debug })) {
85140
84744
  if (this.debug) {
85141
- console.log(`[DEBUG] JSON validation: attempt_completion response is a JSON schema definition instead of data, correcting...`);
84745
+ console.log(`[DEBUG] JSON validation: Response is a JSON schema definition instead of data, correcting...`);
85142
84746
  }
85143
84747
  const schemaDefinitionPrompt = createSchemaDefinitionCorrectionPrompt(
85144
84748
  finalResult,
85145
84749
  options.schema,
85146
84750
  0
85147
84751
  );
85148
- const { schema: _unusedSchema1, ...schemaDefCorrectionOptions } = options;
85149
84752
  finalResult = await this.answer(schemaDefinitionPrompt, [], {
85150
- ...schemaDefCorrectionOptions,
84753
+ ...options,
85151
84754
  _schemaFormatted: true,
85152
84755
  _skipValidation: true,
85153
- // Skip validation in recursive correction calls to prevent loops
84756
+ _disableTools: true,
85154
84757
  _completionPromptProcessed: true,
85155
- // Prevent cascading completion prompts in retry calls
85156
84758
  _maxIterationsOverride: 3
85157
- // Correction should complete in 1-2 iterations (issue #447)
85158
84759
  });
85159
84760
  finalResult = cleanSchemaResponse(finalResult);
85160
- validation = validateJsonResponse(finalResult);
84761
+ validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85161
84762
  retryCount = 1;
85162
84763
  }
85163
84764
  if (!validation.isValid) {
@@ -85167,20 +84768,16 @@ Convert your previous response content into actual JSON data that follows this s
85167
84768
  console.log(`[DEBUG] JSON validation: Auto-wrapped plain text for simple schema`);
85168
84769
  }
85169
84770
  finalResult = autoWrapped;
85170
- validation = validateJsonResponse(finalResult, { debug: this.debug });
84771
+ validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85171
84772
  }
85172
84773
  }
85173
84774
  while (!validation.isValid && retryCount < maxRetries) {
85174
84775
  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 ? "..." : ""}`);
84776
+ console.log(`[DEBUG] JSON validation: Validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
85177
84777
  }
85178
84778
  let correctionPrompt;
85179
84779
  try {
85180
84780
  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
84781
  correctionPrompt = createSchemaDefinitionCorrectionPrompt(
85185
84782
  finalResult,
85186
84783
  options.schema,
@@ -85202,47 +84799,43 @@ Convert your previous response content into actual JSON data that follows this s
85202
84799
  retryCount
85203
84800
  );
85204
84801
  }
85205
- const { schema: _unusedSchema2, ...correctionOptions } = options;
85206
84802
  finalResult = await this.answer(correctionPrompt, [], {
85207
- ...correctionOptions,
84803
+ ...options,
85208
84804
  _schemaFormatted: true,
85209
84805
  _skipValidation: true,
85210
- // Skip validation in recursive correction calls to prevent loops
85211
84806
  _disableTools: true,
85212
- // Only allow attempt_completion - prevent AI from using search/query tools
85213
84807
  _completionPromptProcessed: true,
85214
- // Prevent cascading completion prompts in retry calls
85215
84808
  _maxIterationsOverride: 3
85216
- // Correction should complete in 1-2 iterations (issue #447)
85217
84809
  });
85218
84810
  finalResult = cleanSchemaResponse(finalResult);
85219
- validation = validateJsonResponse(finalResult, { debug: this.debug });
84811
+ validation = validateJsonResponse(finalResult, { debug: this.debug, schema: options.schema });
85220
84812
  retryCount++;
85221
84813
  if (this.debug) {
85222
84814
  if (validation.isValid) {
85223
- console.log(`[DEBUG] JSON validation: attempt_completion correction successful on attempt ${retryCount}`);
84815
+ console.log(`[DEBUG] JSON validation: Correction successful on attempt ${retryCount}`);
85224
84816
  } else {
85225
- console.log(`[DEBUG] JSON validation: attempt_completion correction failed on attempt ${retryCount}: ${validation.error}`);
84817
+ console.log(`[DEBUG] JSON validation: Correction failed on attempt ${retryCount}: ${validation.error}`);
85226
84818
  }
85227
84819
  }
85228
84820
  }
85229
84821
  if (this.tracer) {
85230
- this.tracer.recordJsonValidationEvent("attempt_completion_completed", {
84822
+ this.tracer.recordJsonValidationEvent("completed", {
85231
84823
  "json_validation.success": validation.isValid,
85232
84824
  "json_validation.retry_count": retryCount,
85233
- "json_validation.final_response_length": finalResult.length
84825
+ "json_validation.max_retries": maxRetries,
84826
+ "json_validation.final_response_length": finalResult.length,
84827
+ "json_validation.error": validation.isValid ? null : validation.error
85234
84828
  });
85235
84829
  }
85236
84830
  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 ? "..." : ""}`);
84831
+ console.log(`[DEBUG] JSON validation: Failed after ${maxRetries} attempts: ${validation.error}`);
85239
84832
  } else if (validation.isValid && this.debug) {
85240
- console.log(`[DEBUG] JSON validation: attempt_completion result validation successful`);
84833
+ console.log(`[DEBUG] JSON validation: Final validation successful`);
85241
84834
  }
85242
84835
  }
85243
84836
  } catch (error) {
85244
84837
  if (this.debug) {
85245
- console.log(`[DEBUG] attempt_completion result cleanup failed: ${error.message}`);
84838
+ console.log(`[DEBUG] Schema validation/cleanup failed: ${error.message}`);
85246
84839
  }
85247
84840
  }
85248
84841
  }
@@ -86418,7 +86011,9 @@ function parseArgs() {
86418
86011
  disableDefaultBashAllow: false,
86419
86012
  disableDefaultBashDeny: false,
86420
86013
  // Native thinking/reasoning effort
86421
- thinkingEffort: null
86014
+ thinkingEffort: null,
86015
+ // Completion prompt for post-completion validation/review
86016
+ completionPrompt: null
86422
86017
  };
86423
86018
  for (let i = 0; i < args.length; i++) {
86424
86019
  const arg = args[i];
@@ -86452,6 +86047,8 @@ function parseArgs() {
86452
86047
  config.architectureFileName = args[++i];
86453
86048
  } else if (arg === "--schema" && i + 1 < args.length) {
86454
86049
  config.schema = args[++i];
86050
+ } else if (arg === "--completion-prompt" && i + 1 < args.length) {
86051
+ config.completionPrompt = args[++i];
86455
86052
  } else if (arg === "--provider" && i + 1 < args.length) {
86456
86053
  config.provider = args[++i];
86457
86054
  } else if (arg === "--model" && i + 1 < args.length) {
@@ -86538,6 +86135,7 @@ Options:
86538
86135
  --system-prompt <text|file> Custom system prompt (text or file path)
86539
86136
  --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
86137
  --schema <schema|file> Output schema (JSON, XML, any format - text or file path)
86138
+ --completion-prompt <text> Post-completion review prompt (validates/enriches the answer)
86541
86139
  --provider <name> Force AI provider: anthropic, openai, google
86542
86140
  --model <name> Override model name
86543
86141
  --allow-edit Enable code modification capabilities (edit + create tools)
@@ -87040,7 +86638,8 @@ async function main() {
87040
86638
  enableTasks: config.enableTasks,
87041
86639
  thinkingEffort: config.thinkingEffort,
87042
86640
  searchDelegateProvider: config.searchDelegateProvider,
87043
- searchDelegateModel: config.searchDelegateModel
86641
+ searchDelegateModel: config.searchDelegateModel,
86642
+ completionPrompt: config.completionPrompt
87044
86643
  };
87045
86644
  const agent = new ProbeAgent(agentConfig2);
87046
86645
  await agent.initialize();