@probelabs/probe 0.6.0-rc206 → 0.6.0-rc207
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/binaries/probe-v0.6.0-rc207-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/{probe-v0.6.0-rc206-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc207-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/probe-v0.6.0-rc207-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/{probe-v0.6.0-rc206-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc207-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/probe-v0.6.0-rc207-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +144 -2
- package/build/agent/index.js +116 -2
- package/build/tools/common.js +31 -0
- package/cjs/agent/ProbeAgent.cjs +116 -2
- package/cjs/index.cjs +116 -2
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +144 -2
- package/src/tools/common.js +31 -0
- package/bin/binaries/probe-v0.6.0-rc206-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc206-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc206-x86_64-unknown-linux-musl.tar.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -91,6 +91,45 @@ const MAX_TOOL_ITERATIONS = (() => {
|
|
|
91
91
|
})();
|
|
92
92
|
const MAX_HISTORY_MESSAGES = 100;
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Extract tool name from wrapped_tool:toolName format
|
|
96
|
+
* @param {string} wrappedToolError - Error string in format 'wrapped_tool:toolName'
|
|
97
|
+
* @returns {string} The extracted tool name or 'unknown' if format is invalid
|
|
98
|
+
*/
|
|
99
|
+
function extractWrappedToolName(wrappedToolError) {
|
|
100
|
+
if (!wrappedToolError || typeof wrappedToolError !== 'string') {
|
|
101
|
+
return 'unknown';
|
|
102
|
+
}
|
|
103
|
+
const colonIndex = wrappedToolError.indexOf(':');
|
|
104
|
+
return colonIndex !== -1 ? wrappedToolError.slice(colonIndex + 1) : 'unknown';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check if an error indicates a wrapped tool format error
|
|
109
|
+
* @param {string|null} error - Error from detectUnrecognizedToolCall
|
|
110
|
+
* @returns {boolean} True if it's a wrapped tool error
|
|
111
|
+
*/
|
|
112
|
+
function isWrappedToolError(error) {
|
|
113
|
+
return error && typeof error === 'string' && error.startsWith('wrapped_tool:');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create error message for wrapped tool format issues
|
|
118
|
+
* @param {string} wrappedToolName - The tool name that was incorrectly wrapped
|
|
119
|
+
* @returns {string} User-friendly error message with correct format instructions
|
|
120
|
+
*/
|
|
121
|
+
function createWrappedToolErrorMessage(wrappedToolName) {
|
|
122
|
+
return `Your response contained an incorrectly formatted tool call (${wrappedToolName} wrapped in XML tags). This cannot be used.
|
|
123
|
+
|
|
124
|
+
Please use the CORRECT format:
|
|
125
|
+
|
|
126
|
+
<${wrappedToolName}>
|
|
127
|
+
Your content here
|
|
128
|
+
</${wrappedToolName}>
|
|
129
|
+
|
|
130
|
+
Do NOT wrap in other tags like <api_call>, <tool_name>, <function>, etc.`;
|
|
131
|
+
}
|
|
132
|
+
|
|
94
133
|
// Supported image file extensions (imported from shared config)
|
|
95
134
|
|
|
96
135
|
// Maximum image file size (20MB) to prevent OOM attacks
|
|
@@ -2542,6 +2581,11 @@ Follow these instructions carefully:
|
|
|
2542
2581
|
}
|
|
2543
2582
|
}
|
|
2544
2583
|
|
|
2584
|
+
// Circuit breaker for repeated format errors
|
|
2585
|
+
let lastFormatErrorType = null;
|
|
2586
|
+
let sameFormatErrorCount = 0;
|
|
2587
|
+
const MAX_REPEATED_FORMAT_ERRORS = 3;
|
|
2588
|
+
|
|
2545
2589
|
// Tool iteration loop (only for non-CLI engines like Vercel/Anthropic/OpenAI)
|
|
2546
2590
|
while (currentIteration < maxIterations && !completionAttempted) {
|
|
2547
2591
|
currentIteration++;
|
|
@@ -2835,7 +2879,28 @@ Follow these instructions carefully:
|
|
|
2835
2879
|
);
|
|
2836
2880
|
|
|
2837
2881
|
if (lastAssistantMessage) {
|
|
2838
|
-
|
|
2882
|
+
const prevContent = lastAssistantMessage.content;
|
|
2883
|
+
|
|
2884
|
+
// Check for patterns indicating a failed/wrapped tool call attempt
|
|
2885
|
+
// Use detectUnrecognizedToolCall for consistent detection logic
|
|
2886
|
+
const wrappedToolError = detectUnrecognizedToolCall(prevContent, validTools);
|
|
2887
|
+
|
|
2888
|
+
if (isWrappedToolError(wrappedToolError)) {
|
|
2889
|
+
// Previous response was a broken tool call attempt - don't reuse it
|
|
2890
|
+
const wrappedToolName = extractWrappedToolName(wrappedToolError);
|
|
2891
|
+
if (this.debug) {
|
|
2892
|
+
console.log(`[DEBUG] Previous response contains wrapped tool '${wrappedToolName}' - rejecting for __PREVIOUS_RESPONSE__`);
|
|
2893
|
+
}
|
|
2894
|
+
currentMessages.push({ role: 'assistant', content: assistantResponseContent });
|
|
2895
|
+
currentMessages.push({
|
|
2896
|
+
role: 'user',
|
|
2897
|
+
content: createWrappedToolErrorMessage(wrappedToolName)
|
|
2898
|
+
});
|
|
2899
|
+
completionAttempted = false;
|
|
2900
|
+
continue; // Don't use broken response, continue the loop
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
finalResult = prevContent;
|
|
2839
2904
|
if (this.debug) console.log(`[DEBUG] Using previous response as completion: ${finalResult.substring(0, 100)}...`);
|
|
2840
2905
|
} else {
|
|
2841
2906
|
finalResult = 'Error: No previous response found to use as completion.';
|
|
@@ -3165,7 +3230,32 @@ Follow these instructions carefully:
|
|
|
3165
3230
|
const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
|
|
3166
3231
|
|
|
3167
3232
|
let reminderContent;
|
|
3168
|
-
if (unrecognizedTool) {
|
|
3233
|
+
if (isWrappedToolError(unrecognizedTool)) {
|
|
3234
|
+
// AI wrapped a valid tool name in arbitrary XML tags - provide clear format error
|
|
3235
|
+
const wrappedToolName = extractWrappedToolName(unrecognizedTool);
|
|
3236
|
+
if (this.debug) {
|
|
3237
|
+
console.log(`[DEBUG] Detected wrapped tool '${wrappedToolName}' in assistant response - wrong XML format.`);
|
|
3238
|
+
}
|
|
3239
|
+
const toolError = new ParameterError(
|
|
3240
|
+
`Tool '${wrappedToolName}' found but in WRONG FORMAT - do not wrap tools in other XML tags.`,
|
|
3241
|
+
{
|
|
3242
|
+
suggestion: `Use the tool tag DIRECTLY without any wrapper:
|
|
3243
|
+
|
|
3244
|
+
CORRECT FORMAT:
|
|
3245
|
+
<${wrappedToolName}>
|
|
3246
|
+
<param>value</param>
|
|
3247
|
+
</${wrappedToolName}>
|
|
3248
|
+
|
|
3249
|
+
WRONG (what you did - do not wrap in other tags):
|
|
3250
|
+
<api_call><tool_name>${wrappedToolName}</tool_name>...</api_call>
|
|
3251
|
+
<function>${wrappedToolName}</function>
|
|
3252
|
+
<call name="${wrappedToolName}">...</call>
|
|
3253
|
+
|
|
3254
|
+
Remove ALL wrapper tags and use <${wrappedToolName}> directly as the outermost tag.`
|
|
3255
|
+
}
|
|
3256
|
+
);
|
|
3257
|
+
reminderContent = `<tool_result>\n${formatErrorForAI(toolError)}\n</tool_result>`;
|
|
3258
|
+
} else if (unrecognizedTool) {
|
|
3169
3259
|
// AI tried to use a tool that's not available - provide clear error
|
|
3170
3260
|
if (this.debug) {
|
|
3171
3261
|
console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
|
|
@@ -3175,6 +3265,33 @@ Follow these instructions carefully:
|
|
|
3175
3265
|
});
|
|
3176
3266
|
reminderContent = `<tool_result>\n${formatErrorForAI(toolError)}\n</tool_result>`;
|
|
3177
3267
|
} else {
|
|
3268
|
+
// No tool call detected at all - check if this is the last iteration
|
|
3269
|
+
// On the last iteration, if the AI gave a substantive response without using
|
|
3270
|
+
// attempt_completion, accept it as the final answer rather than losing the content
|
|
3271
|
+
if (currentIteration >= maxIterations) {
|
|
3272
|
+
// Clean up the response - remove thinking tags
|
|
3273
|
+
let cleanedResponse = assistantResponseContent;
|
|
3274
|
+
// Remove <thinking>...</thinking> blocks
|
|
3275
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, '').trim();
|
|
3276
|
+
// Also remove unclosed thinking tags
|
|
3277
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*$/gi, '').trim();
|
|
3278
|
+
|
|
3279
|
+
// Only use if there's substantial content (not just a failed tool call attempt)
|
|
3280
|
+
const hasSubstantialContent = cleanedResponse.length > 50 &&
|
|
3281
|
+
!cleanedResponse.includes('<api_call>') &&
|
|
3282
|
+
!cleanedResponse.includes('<tool_name>') &&
|
|
3283
|
+
!cleanedResponse.includes('<function>');
|
|
3284
|
+
|
|
3285
|
+
if (hasSubstantialContent) {
|
|
3286
|
+
if (this.debug) {
|
|
3287
|
+
console.log(`[DEBUG] Max iterations reached - accepting AI response as final answer (${cleanedResponse.length} chars)`);
|
|
3288
|
+
}
|
|
3289
|
+
finalResult = cleanedResponse;
|
|
3290
|
+
completionAttempted = true;
|
|
3291
|
+
break;
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3178
3295
|
// Standard reminder - no tool call detected at all
|
|
3179
3296
|
reminderContent = `Please use one of the available tools to help answer the question, or use attempt_completion if you have enough information to provide a final answer.
|
|
3180
3297
|
|
|
@@ -3206,6 +3323,31 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
|
|
|
3206
3323
|
console.log(`[DEBUG] No tool call detected in assistant response. Prompting for tool use.`);
|
|
3207
3324
|
}
|
|
3208
3325
|
}
|
|
3326
|
+
|
|
3327
|
+
// Circuit breaker: track repeated format errors and break early
|
|
3328
|
+
// For wrapped_tool errors, track them as a category (any wrapped_tool counts)
|
|
3329
|
+
// For other errors, track the exact error type
|
|
3330
|
+
if (unrecognizedTool) {
|
|
3331
|
+
const isWrapped = isWrappedToolError(unrecognizedTool);
|
|
3332
|
+
const errorCategory = isWrapped ? 'wrapped_tool' : unrecognizedTool;
|
|
3333
|
+
|
|
3334
|
+
if (errorCategory === lastFormatErrorType) {
|
|
3335
|
+
sameFormatErrorCount++;
|
|
3336
|
+
if (sameFormatErrorCount >= MAX_REPEATED_FORMAT_ERRORS) {
|
|
3337
|
+
const errorDesc = isWrapped ? 'wrapped tool format' : unrecognizedTool;
|
|
3338
|
+
console.error(`[ERROR] Format error category '${errorCategory}' repeated ${sameFormatErrorCount} times. Breaking loop early to prevent infinite iteration.`);
|
|
3339
|
+
finalResult = `Error: Unable to complete request. The AI model repeatedly used incorrect tool call format (${errorDesc}). Please try rephrasing your question or using a different model.`;
|
|
3340
|
+
break;
|
|
3341
|
+
}
|
|
3342
|
+
} else {
|
|
3343
|
+
lastFormatErrorType = errorCategory;
|
|
3344
|
+
sameFormatErrorCount = 1;
|
|
3345
|
+
}
|
|
3346
|
+
} else {
|
|
3347
|
+
// Reset counter if it's a different kind of "no tool call" situation
|
|
3348
|
+
lastFormatErrorType = null;
|
|
3349
|
+
sameFormatErrorCount = 0;
|
|
3350
|
+
}
|
|
3209
3351
|
}
|
|
3210
3352
|
|
|
3211
3353
|
// Keep message history manageable
|
package/build/agent/index.js
CHANGED
|
@@ -9059,6 +9059,22 @@ function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
9059
9059
|
return toolName;
|
|
9060
9060
|
}
|
|
9061
9061
|
}
|
|
9062
|
+
const allToolNames = [.../* @__PURE__ */ new Set([...knownToolNames, ...validTools])];
|
|
9063
|
+
for (const toolName of allToolNames) {
|
|
9064
|
+
const escapedToolName = toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9065
|
+
const wrapperPatterns = [
|
|
9066
|
+
new RegExp(`<tool_name>\\s*${escapedToolName}\\s*</tool_name>`, "i"),
|
|
9067
|
+
new RegExp(`<function>\\s*${escapedToolName}\\s*</function>`, "i"),
|
|
9068
|
+
new RegExp(`<name>\\s*${escapedToolName}\\s*</name>`, "i"),
|
|
9069
|
+
// Also check for tool name immediately after api_call or call opening tag
|
|
9070
|
+
new RegExp(`<(?:api_call|call)[^>]*>[\\s\\S]*?<tool_name>\\s*${escapedToolName}`, "i")
|
|
9071
|
+
];
|
|
9072
|
+
for (const pattern of wrapperPatterns) {
|
|
9073
|
+
if (pattern.test(xmlString)) {
|
|
9074
|
+
return `wrapped_tool:${toolName}`;
|
|
9075
|
+
}
|
|
9076
|
+
}
|
|
9077
|
+
}
|
|
9062
9078
|
return null;
|
|
9063
9079
|
}
|
|
9064
9080
|
function parseTargets(targets) {
|
|
@@ -68676,6 +68692,27 @@ import { EventEmitter as EventEmitter5 } from "events";
|
|
|
68676
68692
|
import { existsSync as existsSync6 } from "fs";
|
|
68677
68693
|
import { readFile as readFile3, stat, readdir as readdir3 } from "fs/promises";
|
|
68678
68694
|
import { resolve as resolve6, isAbsolute as isAbsolute5, dirname as dirname5, basename, normalize as normalize2, sep as sep5 } from "path";
|
|
68695
|
+
function extractWrappedToolName(wrappedToolError) {
|
|
68696
|
+
if (!wrappedToolError || typeof wrappedToolError !== "string") {
|
|
68697
|
+
return "unknown";
|
|
68698
|
+
}
|
|
68699
|
+
const colonIndex = wrappedToolError.indexOf(":");
|
|
68700
|
+
return colonIndex !== -1 ? wrappedToolError.slice(colonIndex + 1) : "unknown";
|
|
68701
|
+
}
|
|
68702
|
+
function isWrappedToolError(error) {
|
|
68703
|
+
return error && typeof error === "string" && error.startsWith("wrapped_tool:");
|
|
68704
|
+
}
|
|
68705
|
+
function createWrappedToolErrorMessage(wrappedToolName) {
|
|
68706
|
+
return `Your response contained an incorrectly formatted tool call (${wrappedToolName} wrapped in XML tags). This cannot be used.
|
|
68707
|
+
|
|
68708
|
+
Please use the CORRECT format:
|
|
68709
|
+
|
|
68710
|
+
<${wrappedToolName}>
|
|
68711
|
+
Your content here
|
|
68712
|
+
</${wrappedToolName}>
|
|
68713
|
+
|
|
68714
|
+
Do NOT wrap in other tags like <api_call>, <tool_name>, <function>, etc.`;
|
|
68715
|
+
}
|
|
68679
68716
|
var MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
68680
68717
|
var init_ProbeAgent = __esm({
|
|
68681
68718
|
"src/agent/ProbeAgent.js"() {
|
|
@@ -70675,6 +70712,9 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
70675
70712
|
console.log(`[DEBUG] Schema provided, using extended iteration limit: ${maxIterations} (base: ${baseMaxIterations})`);
|
|
70676
70713
|
}
|
|
70677
70714
|
}
|
|
70715
|
+
let lastFormatErrorType = null;
|
|
70716
|
+
let sameFormatErrorCount = 0;
|
|
70717
|
+
const MAX_REPEATED_FORMAT_ERRORS = 3;
|
|
70678
70718
|
while (currentIteration < maxIterations && !completionAttempted) {
|
|
70679
70719
|
currentIteration++;
|
|
70680
70720
|
if (this.cancelled) throw new Error("Request was cancelled by the user");
|
|
@@ -70873,7 +70913,22 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
70873
70913
|
(msg) => msg.role === "assistant" && msg.content && !(this.mcpBridge ? parseHybridXmlToolCall(msg.content, validTools, this.mcpBridge) : parseXmlToolCallWithThinking(msg.content, validTools))
|
|
70874
70914
|
);
|
|
70875
70915
|
if (lastAssistantMessage) {
|
|
70876
|
-
|
|
70916
|
+
const prevContent = lastAssistantMessage.content;
|
|
70917
|
+
const wrappedToolError = detectUnrecognizedToolCall(prevContent, validTools);
|
|
70918
|
+
if (isWrappedToolError(wrappedToolError)) {
|
|
70919
|
+
const wrappedToolName = extractWrappedToolName(wrappedToolError);
|
|
70920
|
+
if (this.debug) {
|
|
70921
|
+
console.log(`[DEBUG] Previous response contains wrapped tool '${wrappedToolName}' - rejecting for __PREVIOUS_RESPONSE__`);
|
|
70922
|
+
}
|
|
70923
|
+
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
70924
|
+
currentMessages.push({
|
|
70925
|
+
role: "user",
|
|
70926
|
+
content: createWrappedToolErrorMessage(wrappedToolName)
|
|
70927
|
+
});
|
|
70928
|
+
completionAttempted = false;
|
|
70929
|
+
continue;
|
|
70930
|
+
}
|
|
70931
|
+
finalResult = prevContent;
|
|
70877
70932
|
if (this.debug) console.log(`[DEBUG] Using previous response as completion: ${finalResult.substring(0, 100)}...`);
|
|
70878
70933
|
} else {
|
|
70879
70934
|
finalResult = "Error: No previous response found to use as completion.";
|
|
@@ -71144,7 +71199,33 @@ ${errorXml}
|
|
|
71144
71199
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
71145
71200
|
const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
|
|
71146
71201
|
let reminderContent;
|
|
71147
|
-
if (unrecognizedTool) {
|
|
71202
|
+
if (isWrappedToolError(unrecognizedTool)) {
|
|
71203
|
+
const wrappedToolName = extractWrappedToolName(unrecognizedTool);
|
|
71204
|
+
if (this.debug) {
|
|
71205
|
+
console.log(`[DEBUG] Detected wrapped tool '${wrappedToolName}' in assistant response - wrong XML format.`);
|
|
71206
|
+
}
|
|
71207
|
+
const toolError = new ParameterError(
|
|
71208
|
+
`Tool '${wrappedToolName}' found but in WRONG FORMAT - do not wrap tools in other XML tags.`,
|
|
71209
|
+
{
|
|
71210
|
+
suggestion: `Use the tool tag DIRECTLY without any wrapper:
|
|
71211
|
+
|
|
71212
|
+
CORRECT FORMAT:
|
|
71213
|
+
<${wrappedToolName}>
|
|
71214
|
+
<param>value</param>
|
|
71215
|
+
</${wrappedToolName}>
|
|
71216
|
+
|
|
71217
|
+
WRONG (what you did - do not wrap in other tags):
|
|
71218
|
+
<api_call><tool_name>${wrappedToolName}</tool_name>...</api_call>
|
|
71219
|
+
<function>${wrappedToolName}</function>
|
|
71220
|
+
<call name="${wrappedToolName}">...</call>
|
|
71221
|
+
|
|
71222
|
+
Remove ALL wrapper tags and use <${wrappedToolName}> directly as the outermost tag.`
|
|
71223
|
+
}
|
|
71224
|
+
);
|
|
71225
|
+
reminderContent = `<tool_result>
|
|
71226
|
+
${formatErrorForAI(toolError)}
|
|
71227
|
+
</tool_result>`;
|
|
71228
|
+
} else if (unrecognizedTool) {
|
|
71148
71229
|
if (this.debug) {
|
|
71149
71230
|
console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
|
|
71150
71231
|
}
|
|
@@ -71155,6 +71236,20 @@ ${errorXml}
|
|
|
71155
71236
|
${formatErrorForAI(toolError)}
|
|
71156
71237
|
</tool_result>`;
|
|
71157
71238
|
} else {
|
|
71239
|
+
if (currentIteration >= maxIterations) {
|
|
71240
|
+
let cleanedResponse = assistantResponseContent;
|
|
71241
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").trim();
|
|
71242
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*$/gi, "").trim();
|
|
71243
|
+
const hasSubstantialContent = cleanedResponse.length > 50 && !cleanedResponse.includes("<api_call>") && !cleanedResponse.includes("<tool_name>") && !cleanedResponse.includes("<function>");
|
|
71244
|
+
if (hasSubstantialContent) {
|
|
71245
|
+
if (this.debug) {
|
|
71246
|
+
console.log(`[DEBUG] Max iterations reached - accepting AI response as final answer (${cleanedResponse.length} chars)`);
|
|
71247
|
+
}
|
|
71248
|
+
finalResult = cleanedResponse;
|
|
71249
|
+
completionAttempted = true;
|
|
71250
|
+
break;
|
|
71251
|
+
}
|
|
71252
|
+
}
|
|
71158
71253
|
reminderContent = `Please use one of the available tools to help answer the question, or use attempt_completion if you have enough information to provide a final answer.
|
|
71159
71254
|
|
|
71160
71255
|
Remember: Use proper XML format with BOTH opening and closing tags:
|
|
@@ -71184,6 +71279,25 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
|
|
|
71184
71279
|
console.log(`[DEBUG] No tool call detected in assistant response. Prompting for tool use.`);
|
|
71185
71280
|
}
|
|
71186
71281
|
}
|
|
71282
|
+
if (unrecognizedTool) {
|
|
71283
|
+
const isWrapped = isWrappedToolError(unrecognizedTool);
|
|
71284
|
+
const errorCategory = isWrapped ? "wrapped_tool" : unrecognizedTool;
|
|
71285
|
+
if (errorCategory === lastFormatErrorType) {
|
|
71286
|
+
sameFormatErrorCount++;
|
|
71287
|
+
if (sameFormatErrorCount >= MAX_REPEATED_FORMAT_ERRORS) {
|
|
71288
|
+
const errorDesc = isWrapped ? "wrapped tool format" : unrecognizedTool;
|
|
71289
|
+
console.error(`[ERROR] Format error category '${errorCategory}' repeated ${sameFormatErrorCount} times. Breaking loop early to prevent infinite iteration.`);
|
|
71290
|
+
finalResult = `Error: Unable to complete request. The AI model repeatedly used incorrect tool call format (${errorDesc}). Please try rephrasing your question or using a different model.`;
|
|
71291
|
+
break;
|
|
71292
|
+
}
|
|
71293
|
+
} else {
|
|
71294
|
+
lastFormatErrorType = errorCategory;
|
|
71295
|
+
sameFormatErrorCount = 1;
|
|
71296
|
+
}
|
|
71297
|
+
} else {
|
|
71298
|
+
lastFormatErrorType = null;
|
|
71299
|
+
sameFormatErrorCount = 0;
|
|
71300
|
+
}
|
|
71187
71301
|
}
|
|
71188
71302
|
if (currentMessages.length > MAX_HISTORY_MESSAGES) {
|
|
71189
71303
|
const messagesBefore = currentMessages.length;
|
package/build/tools/common.js
CHANGED
|
@@ -617,6 +617,37 @@ export function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
617
617
|
}
|
|
618
618
|
}
|
|
619
619
|
|
|
620
|
+
// Check if any valid tool name appears inside specific wrapper patterns
|
|
621
|
+
// This catches cases where AI wraps tools in arbitrary tags like:
|
|
622
|
+
// <api_call><tool_name>attempt_completion</tool_name>...</api_call>
|
|
623
|
+
// <function>search</function>
|
|
624
|
+
// <call name="extract">...</call>
|
|
625
|
+
// Only match specific wrapper patterns to avoid false positives with normal text
|
|
626
|
+
const allToolNames = [...new Set([...knownToolNames, ...validTools])];
|
|
627
|
+
for (const toolName of allToolNames) {
|
|
628
|
+
// Escape regex metacharacters in tool name to prevent regex errors
|
|
629
|
+
const escapedToolName = toolName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
630
|
+
|
|
631
|
+
// Match specific wrapper patterns that indicate a tool call attempt:
|
|
632
|
+
// 1. <tool_name>toolName</tool_name> - common Claude API-style wrapper
|
|
633
|
+
// 2. <function>toolName</function> - function call style
|
|
634
|
+
// 3. <name>toolName</name> - generic name wrapper
|
|
635
|
+
// 4. <call><name>toolName - partial wrapper patterns
|
|
636
|
+
const wrapperPatterns = [
|
|
637
|
+
new RegExp(`<tool_name>\\s*${escapedToolName}\\s*</tool_name>`, 'i'),
|
|
638
|
+
new RegExp(`<function>\\s*${escapedToolName}\\s*</function>`, 'i'),
|
|
639
|
+
new RegExp(`<name>\\s*${escapedToolName}\\s*</name>`, 'i'),
|
|
640
|
+
// Also check for tool name immediately after api_call or call opening tag
|
|
641
|
+
new RegExp(`<(?:api_call|call)[^>]*>[\\s\\S]*?<tool_name>\\s*${escapedToolName}`, 'i')
|
|
642
|
+
];
|
|
643
|
+
|
|
644
|
+
for (const pattern of wrapperPatterns) {
|
|
645
|
+
if (pattern.test(xmlString)) {
|
|
646
|
+
return `wrapped_tool:${toolName}`;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
620
651
|
return null;
|
|
621
652
|
}
|
|
622
653
|
|
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -36006,6 +36006,22 @@ function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
36006
36006
|
return toolName;
|
|
36007
36007
|
}
|
|
36008
36008
|
}
|
|
36009
|
+
const allToolNames = [.../* @__PURE__ */ new Set([...knownToolNames, ...validTools])];
|
|
36010
|
+
for (const toolName of allToolNames) {
|
|
36011
|
+
const escapedToolName = toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
36012
|
+
const wrapperPatterns = [
|
|
36013
|
+
new RegExp(`<tool_name>\\s*${escapedToolName}\\s*</tool_name>`, "i"),
|
|
36014
|
+
new RegExp(`<function>\\s*${escapedToolName}\\s*</function>`, "i"),
|
|
36015
|
+
new RegExp(`<name>\\s*${escapedToolName}\\s*</name>`, "i"),
|
|
36016
|
+
// Also check for tool name immediately after api_call or call opening tag
|
|
36017
|
+
new RegExp(`<(?:api_call|call)[^>]*>[\\s\\S]*?<tool_name>\\s*${escapedToolName}`, "i")
|
|
36018
|
+
];
|
|
36019
|
+
for (const pattern of wrapperPatterns) {
|
|
36020
|
+
if (pattern.test(xmlString)) {
|
|
36021
|
+
return `wrapped_tool:${toolName}`;
|
|
36022
|
+
}
|
|
36023
|
+
}
|
|
36024
|
+
}
|
|
36009
36025
|
return null;
|
|
36010
36026
|
}
|
|
36011
36027
|
function parseTargets(targets) {
|
|
@@ -95379,6 +95395,27 @@ __export(ProbeAgent_exports, {
|
|
|
95379
95395
|
ProbeAgent: () => ProbeAgent
|
|
95380
95396
|
});
|
|
95381
95397
|
module.exports = __toCommonJS(ProbeAgent_exports);
|
|
95398
|
+
function extractWrappedToolName(wrappedToolError) {
|
|
95399
|
+
if (!wrappedToolError || typeof wrappedToolError !== "string") {
|
|
95400
|
+
return "unknown";
|
|
95401
|
+
}
|
|
95402
|
+
const colonIndex = wrappedToolError.indexOf(":");
|
|
95403
|
+
return colonIndex !== -1 ? wrappedToolError.slice(colonIndex + 1) : "unknown";
|
|
95404
|
+
}
|
|
95405
|
+
function isWrappedToolError(error2) {
|
|
95406
|
+
return error2 && typeof error2 === "string" && error2.startsWith("wrapped_tool:");
|
|
95407
|
+
}
|
|
95408
|
+
function createWrappedToolErrorMessage(wrappedToolName) {
|
|
95409
|
+
return `Your response contained an incorrectly formatted tool call (${wrappedToolName} wrapped in XML tags). This cannot be used.
|
|
95410
|
+
|
|
95411
|
+
Please use the CORRECT format:
|
|
95412
|
+
|
|
95413
|
+
<${wrappedToolName}>
|
|
95414
|
+
Your content here
|
|
95415
|
+
</${wrappedToolName}>
|
|
95416
|
+
|
|
95417
|
+
Do NOT wrap in other tags like <api_call>, <tool_name>, <function>, etc.`;
|
|
95418
|
+
}
|
|
95382
95419
|
var import_dotenv2, import_anthropic2, import_openai2, import_google2, import_ai5, import_crypto9, import_events4, import_fs14, import_promises6, import_path17, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
95383
95420
|
var init_ProbeAgent = __esm({
|
|
95384
95421
|
"src/agent/ProbeAgent.js"() {
|
|
@@ -97388,6 +97425,9 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
97388
97425
|
console.log(`[DEBUG] Schema provided, using extended iteration limit: ${maxIterations} (base: ${baseMaxIterations})`);
|
|
97389
97426
|
}
|
|
97390
97427
|
}
|
|
97428
|
+
let lastFormatErrorType = null;
|
|
97429
|
+
let sameFormatErrorCount = 0;
|
|
97430
|
+
const MAX_REPEATED_FORMAT_ERRORS = 3;
|
|
97391
97431
|
while (currentIteration < maxIterations && !completionAttempted) {
|
|
97392
97432
|
currentIteration++;
|
|
97393
97433
|
if (this.cancelled) throw new Error("Request was cancelled by the user");
|
|
@@ -97586,7 +97626,22 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
97586
97626
|
(msg) => msg.role === "assistant" && msg.content && !(this.mcpBridge ? parseHybridXmlToolCall(msg.content, validTools, this.mcpBridge) : parseXmlToolCallWithThinking(msg.content, validTools))
|
|
97587
97627
|
);
|
|
97588
97628
|
if (lastAssistantMessage) {
|
|
97589
|
-
|
|
97629
|
+
const prevContent = lastAssistantMessage.content;
|
|
97630
|
+
const wrappedToolError = detectUnrecognizedToolCall(prevContent, validTools);
|
|
97631
|
+
if (isWrappedToolError(wrappedToolError)) {
|
|
97632
|
+
const wrappedToolName = extractWrappedToolName(wrappedToolError);
|
|
97633
|
+
if (this.debug) {
|
|
97634
|
+
console.log(`[DEBUG] Previous response contains wrapped tool '${wrappedToolName}' - rejecting for __PREVIOUS_RESPONSE__`);
|
|
97635
|
+
}
|
|
97636
|
+
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
97637
|
+
currentMessages.push({
|
|
97638
|
+
role: "user",
|
|
97639
|
+
content: createWrappedToolErrorMessage(wrappedToolName)
|
|
97640
|
+
});
|
|
97641
|
+
completionAttempted = false;
|
|
97642
|
+
continue;
|
|
97643
|
+
}
|
|
97644
|
+
finalResult = prevContent;
|
|
97590
97645
|
if (this.debug) console.log(`[DEBUG] Using previous response as completion: ${finalResult.substring(0, 100)}...`);
|
|
97591
97646
|
} else {
|
|
97592
97647
|
finalResult = "Error: No previous response found to use as completion.";
|
|
@@ -97857,7 +97912,33 @@ ${errorXml}
|
|
|
97857
97912
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
97858
97913
|
const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
|
|
97859
97914
|
let reminderContent;
|
|
97860
|
-
if (unrecognizedTool) {
|
|
97915
|
+
if (isWrappedToolError(unrecognizedTool)) {
|
|
97916
|
+
const wrappedToolName = extractWrappedToolName(unrecognizedTool);
|
|
97917
|
+
if (this.debug) {
|
|
97918
|
+
console.log(`[DEBUG] Detected wrapped tool '${wrappedToolName}' in assistant response - wrong XML format.`);
|
|
97919
|
+
}
|
|
97920
|
+
const toolError = new ParameterError(
|
|
97921
|
+
`Tool '${wrappedToolName}' found but in WRONG FORMAT - do not wrap tools in other XML tags.`,
|
|
97922
|
+
{
|
|
97923
|
+
suggestion: `Use the tool tag DIRECTLY without any wrapper:
|
|
97924
|
+
|
|
97925
|
+
CORRECT FORMAT:
|
|
97926
|
+
<${wrappedToolName}>
|
|
97927
|
+
<param>value</param>
|
|
97928
|
+
</${wrappedToolName}>
|
|
97929
|
+
|
|
97930
|
+
WRONG (what you did - do not wrap in other tags):
|
|
97931
|
+
<api_call><tool_name>${wrappedToolName}</tool_name>...</api_call>
|
|
97932
|
+
<function>${wrappedToolName}</function>
|
|
97933
|
+
<call name="${wrappedToolName}">...</call>
|
|
97934
|
+
|
|
97935
|
+
Remove ALL wrapper tags and use <${wrappedToolName}> directly as the outermost tag.`
|
|
97936
|
+
}
|
|
97937
|
+
);
|
|
97938
|
+
reminderContent = `<tool_result>
|
|
97939
|
+
${formatErrorForAI(toolError)}
|
|
97940
|
+
</tool_result>`;
|
|
97941
|
+
} else if (unrecognizedTool) {
|
|
97861
97942
|
if (this.debug) {
|
|
97862
97943
|
console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
|
|
97863
97944
|
}
|
|
@@ -97868,6 +97949,20 @@ ${errorXml}
|
|
|
97868
97949
|
${formatErrorForAI(toolError)}
|
|
97869
97950
|
</tool_result>`;
|
|
97870
97951
|
} else {
|
|
97952
|
+
if (currentIteration >= maxIterations) {
|
|
97953
|
+
let cleanedResponse = assistantResponseContent;
|
|
97954
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").trim();
|
|
97955
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*$/gi, "").trim();
|
|
97956
|
+
const hasSubstantialContent = cleanedResponse.length > 50 && !cleanedResponse.includes("<api_call>") && !cleanedResponse.includes("<tool_name>") && !cleanedResponse.includes("<function>");
|
|
97957
|
+
if (hasSubstantialContent) {
|
|
97958
|
+
if (this.debug) {
|
|
97959
|
+
console.log(`[DEBUG] Max iterations reached - accepting AI response as final answer (${cleanedResponse.length} chars)`);
|
|
97960
|
+
}
|
|
97961
|
+
finalResult = cleanedResponse;
|
|
97962
|
+
completionAttempted = true;
|
|
97963
|
+
break;
|
|
97964
|
+
}
|
|
97965
|
+
}
|
|
97871
97966
|
reminderContent = `Please use one of the available tools to help answer the question, or use attempt_completion if you have enough information to provide a final answer.
|
|
97872
97967
|
|
|
97873
97968
|
Remember: Use proper XML format with BOTH opening and closing tags:
|
|
@@ -97897,6 +97992,25 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
|
|
|
97897
97992
|
console.log(`[DEBUG] No tool call detected in assistant response. Prompting for tool use.`);
|
|
97898
97993
|
}
|
|
97899
97994
|
}
|
|
97995
|
+
if (unrecognizedTool) {
|
|
97996
|
+
const isWrapped = isWrappedToolError(unrecognizedTool);
|
|
97997
|
+
const errorCategory = isWrapped ? "wrapped_tool" : unrecognizedTool;
|
|
97998
|
+
if (errorCategory === lastFormatErrorType) {
|
|
97999
|
+
sameFormatErrorCount++;
|
|
98000
|
+
if (sameFormatErrorCount >= MAX_REPEATED_FORMAT_ERRORS) {
|
|
98001
|
+
const errorDesc = isWrapped ? "wrapped tool format" : unrecognizedTool;
|
|
98002
|
+
console.error(`[ERROR] Format error category '${errorCategory}' repeated ${sameFormatErrorCount} times. Breaking loop early to prevent infinite iteration.`);
|
|
98003
|
+
finalResult = `Error: Unable to complete request. The AI model repeatedly used incorrect tool call format (${errorDesc}). Please try rephrasing your question or using a different model.`;
|
|
98004
|
+
break;
|
|
98005
|
+
}
|
|
98006
|
+
} else {
|
|
98007
|
+
lastFormatErrorType = errorCategory;
|
|
98008
|
+
sameFormatErrorCount = 1;
|
|
98009
|
+
}
|
|
98010
|
+
} else {
|
|
98011
|
+
lastFormatErrorType = null;
|
|
98012
|
+
sameFormatErrorCount = 0;
|
|
98013
|
+
}
|
|
97900
98014
|
}
|
|
97901
98015
|
if (currentMessages.length > MAX_HISTORY_MESSAGES) {
|
|
97902
98016
|
const messagesBefore = currentMessages.length;
|
package/cjs/index.cjs
CHANGED
|
@@ -35787,6 +35787,22 @@ function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
35787
35787
|
return toolName;
|
|
35788
35788
|
}
|
|
35789
35789
|
}
|
|
35790
|
+
const allToolNames = [.../* @__PURE__ */ new Set([...knownToolNames, ...validTools])];
|
|
35791
|
+
for (const toolName of allToolNames) {
|
|
35792
|
+
const escapedToolName = toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
35793
|
+
const wrapperPatterns = [
|
|
35794
|
+
new RegExp(`<tool_name>\\s*${escapedToolName}\\s*</tool_name>`, "i"),
|
|
35795
|
+
new RegExp(`<function>\\s*${escapedToolName}\\s*</function>`, "i"),
|
|
35796
|
+
new RegExp(`<name>\\s*${escapedToolName}\\s*</name>`, "i"),
|
|
35797
|
+
// Also check for tool name immediately after api_call or call opening tag
|
|
35798
|
+
new RegExp(`<(?:api_call|call)[^>]*>[\\s\\S]*?<tool_name>\\s*${escapedToolName}`, "i")
|
|
35799
|
+
];
|
|
35800
|
+
for (const pattern of wrapperPatterns) {
|
|
35801
|
+
if (pattern.test(xmlString)) {
|
|
35802
|
+
return `wrapped_tool:${toolName}`;
|
|
35803
|
+
}
|
|
35804
|
+
}
|
|
35805
|
+
}
|
|
35790
35806
|
return null;
|
|
35791
35807
|
}
|
|
35792
35808
|
function parseTargets(targets) {
|
|
@@ -93061,6 +93077,27 @@ var ProbeAgent_exports = {};
|
|
|
93061
93077
|
__export(ProbeAgent_exports, {
|
|
93062
93078
|
ProbeAgent: () => ProbeAgent
|
|
93063
93079
|
});
|
|
93080
|
+
function extractWrappedToolName(wrappedToolError) {
|
|
93081
|
+
if (!wrappedToolError || typeof wrappedToolError !== "string") {
|
|
93082
|
+
return "unknown";
|
|
93083
|
+
}
|
|
93084
|
+
const colonIndex = wrappedToolError.indexOf(":");
|
|
93085
|
+
return colonIndex !== -1 ? wrappedToolError.slice(colonIndex + 1) : "unknown";
|
|
93086
|
+
}
|
|
93087
|
+
function isWrappedToolError(error2) {
|
|
93088
|
+
return error2 && typeof error2 === "string" && error2.startsWith("wrapped_tool:");
|
|
93089
|
+
}
|
|
93090
|
+
function createWrappedToolErrorMessage(wrappedToolName) {
|
|
93091
|
+
return `Your response contained an incorrectly formatted tool call (${wrappedToolName} wrapped in XML tags). This cannot be used.
|
|
93092
|
+
|
|
93093
|
+
Please use the CORRECT format:
|
|
93094
|
+
|
|
93095
|
+
<${wrappedToolName}>
|
|
93096
|
+
Your content here
|
|
93097
|
+
</${wrappedToolName}>
|
|
93098
|
+
|
|
93099
|
+
Do NOT wrap in other tags like <api_call>, <tool_name>, <function>, etc.`;
|
|
93100
|
+
}
|
|
93064
93101
|
var import_dotenv, import_anthropic2, import_openai2, import_google2, import_ai3, import_crypto8, import_events4, import_fs11, import_promises6, import_path13, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
93065
93102
|
var init_ProbeAgent = __esm({
|
|
93066
93103
|
"src/agent/ProbeAgent.js"() {
|
|
@@ -95071,6 +95108,9 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
95071
95108
|
console.log(`[DEBUG] Schema provided, using extended iteration limit: ${maxIterations} (base: ${baseMaxIterations})`);
|
|
95072
95109
|
}
|
|
95073
95110
|
}
|
|
95111
|
+
let lastFormatErrorType = null;
|
|
95112
|
+
let sameFormatErrorCount = 0;
|
|
95113
|
+
const MAX_REPEATED_FORMAT_ERRORS = 3;
|
|
95074
95114
|
while (currentIteration < maxIterations && !completionAttempted) {
|
|
95075
95115
|
currentIteration++;
|
|
95076
95116
|
if (this.cancelled) throw new Error("Request was cancelled by the user");
|
|
@@ -95269,7 +95309,22 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
95269
95309
|
(msg) => msg.role === "assistant" && msg.content && !(this.mcpBridge ? parseHybridXmlToolCall(msg.content, validTools, this.mcpBridge) : parseXmlToolCallWithThinking(msg.content, validTools))
|
|
95270
95310
|
);
|
|
95271
95311
|
if (lastAssistantMessage) {
|
|
95272
|
-
|
|
95312
|
+
const prevContent = lastAssistantMessage.content;
|
|
95313
|
+
const wrappedToolError = detectUnrecognizedToolCall(prevContent, validTools);
|
|
95314
|
+
if (isWrappedToolError(wrappedToolError)) {
|
|
95315
|
+
const wrappedToolName = extractWrappedToolName(wrappedToolError);
|
|
95316
|
+
if (this.debug) {
|
|
95317
|
+
console.log(`[DEBUG] Previous response contains wrapped tool '${wrappedToolName}' - rejecting for __PREVIOUS_RESPONSE__`);
|
|
95318
|
+
}
|
|
95319
|
+
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
95320
|
+
currentMessages.push({
|
|
95321
|
+
role: "user",
|
|
95322
|
+
content: createWrappedToolErrorMessage(wrappedToolName)
|
|
95323
|
+
});
|
|
95324
|
+
completionAttempted = false;
|
|
95325
|
+
continue;
|
|
95326
|
+
}
|
|
95327
|
+
finalResult = prevContent;
|
|
95273
95328
|
if (this.debug) console.log(`[DEBUG] Using previous response as completion: ${finalResult.substring(0, 100)}...`);
|
|
95274
95329
|
} else {
|
|
95275
95330
|
finalResult = "Error: No previous response found to use as completion.";
|
|
@@ -95540,7 +95595,33 @@ ${errorXml}
|
|
|
95540
95595
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
95541
95596
|
const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
|
|
95542
95597
|
let reminderContent;
|
|
95543
|
-
if (unrecognizedTool) {
|
|
95598
|
+
if (isWrappedToolError(unrecognizedTool)) {
|
|
95599
|
+
const wrappedToolName = extractWrappedToolName(unrecognizedTool);
|
|
95600
|
+
if (this.debug) {
|
|
95601
|
+
console.log(`[DEBUG] Detected wrapped tool '${wrappedToolName}' in assistant response - wrong XML format.`);
|
|
95602
|
+
}
|
|
95603
|
+
const toolError = new ParameterError(
|
|
95604
|
+
`Tool '${wrappedToolName}' found but in WRONG FORMAT - do not wrap tools in other XML tags.`,
|
|
95605
|
+
{
|
|
95606
|
+
suggestion: `Use the tool tag DIRECTLY without any wrapper:
|
|
95607
|
+
|
|
95608
|
+
CORRECT FORMAT:
|
|
95609
|
+
<${wrappedToolName}>
|
|
95610
|
+
<param>value</param>
|
|
95611
|
+
</${wrappedToolName}>
|
|
95612
|
+
|
|
95613
|
+
WRONG (what you did - do not wrap in other tags):
|
|
95614
|
+
<api_call><tool_name>${wrappedToolName}</tool_name>...</api_call>
|
|
95615
|
+
<function>${wrappedToolName}</function>
|
|
95616
|
+
<call name="${wrappedToolName}">...</call>
|
|
95617
|
+
|
|
95618
|
+
Remove ALL wrapper tags and use <${wrappedToolName}> directly as the outermost tag.`
|
|
95619
|
+
}
|
|
95620
|
+
);
|
|
95621
|
+
reminderContent = `<tool_result>
|
|
95622
|
+
${formatErrorForAI(toolError)}
|
|
95623
|
+
</tool_result>`;
|
|
95624
|
+
} else if (unrecognizedTool) {
|
|
95544
95625
|
if (this.debug) {
|
|
95545
95626
|
console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
|
|
95546
95627
|
}
|
|
@@ -95551,6 +95632,20 @@ ${errorXml}
|
|
|
95551
95632
|
${formatErrorForAI(toolError)}
|
|
95552
95633
|
</tool_result>`;
|
|
95553
95634
|
} else {
|
|
95635
|
+
if (currentIteration >= maxIterations) {
|
|
95636
|
+
let cleanedResponse = assistantResponseContent;
|
|
95637
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").trim();
|
|
95638
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*$/gi, "").trim();
|
|
95639
|
+
const hasSubstantialContent = cleanedResponse.length > 50 && !cleanedResponse.includes("<api_call>") && !cleanedResponse.includes("<tool_name>") && !cleanedResponse.includes("<function>");
|
|
95640
|
+
if (hasSubstantialContent) {
|
|
95641
|
+
if (this.debug) {
|
|
95642
|
+
console.log(`[DEBUG] Max iterations reached - accepting AI response as final answer (${cleanedResponse.length} chars)`);
|
|
95643
|
+
}
|
|
95644
|
+
finalResult = cleanedResponse;
|
|
95645
|
+
completionAttempted = true;
|
|
95646
|
+
break;
|
|
95647
|
+
}
|
|
95648
|
+
}
|
|
95554
95649
|
reminderContent = `Please use one of the available tools to help answer the question, or use attempt_completion if you have enough information to provide a final answer.
|
|
95555
95650
|
|
|
95556
95651
|
Remember: Use proper XML format with BOTH opening and closing tags:
|
|
@@ -95580,6 +95675,25 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
|
|
|
95580
95675
|
console.log(`[DEBUG] No tool call detected in assistant response. Prompting for tool use.`);
|
|
95581
95676
|
}
|
|
95582
95677
|
}
|
|
95678
|
+
if (unrecognizedTool) {
|
|
95679
|
+
const isWrapped = isWrappedToolError(unrecognizedTool);
|
|
95680
|
+
const errorCategory = isWrapped ? "wrapped_tool" : unrecognizedTool;
|
|
95681
|
+
if (errorCategory === lastFormatErrorType) {
|
|
95682
|
+
sameFormatErrorCount++;
|
|
95683
|
+
if (sameFormatErrorCount >= MAX_REPEATED_FORMAT_ERRORS) {
|
|
95684
|
+
const errorDesc = isWrapped ? "wrapped tool format" : unrecognizedTool;
|
|
95685
|
+
console.error(`[ERROR] Format error category '${errorCategory}' repeated ${sameFormatErrorCount} times. Breaking loop early to prevent infinite iteration.`);
|
|
95686
|
+
finalResult = `Error: Unable to complete request. The AI model repeatedly used incorrect tool call format (${errorDesc}). Please try rephrasing your question or using a different model.`;
|
|
95687
|
+
break;
|
|
95688
|
+
}
|
|
95689
|
+
} else {
|
|
95690
|
+
lastFormatErrorType = errorCategory;
|
|
95691
|
+
sameFormatErrorCount = 1;
|
|
95692
|
+
}
|
|
95693
|
+
} else {
|
|
95694
|
+
lastFormatErrorType = null;
|
|
95695
|
+
sameFormatErrorCount = 0;
|
|
95696
|
+
}
|
|
95583
95697
|
}
|
|
95584
95698
|
if (currentMessages.length > MAX_HISTORY_MESSAGES) {
|
|
95585
95699
|
const messagesBefore = currentMessages.length;
|
package/package.json
CHANGED
package/src/agent/ProbeAgent.js
CHANGED
|
@@ -91,6 +91,45 @@ const MAX_TOOL_ITERATIONS = (() => {
|
|
|
91
91
|
})();
|
|
92
92
|
const MAX_HISTORY_MESSAGES = 100;
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Extract tool name from wrapped_tool:toolName format
|
|
96
|
+
* @param {string} wrappedToolError - Error string in format 'wrapped_tool:toolName'
|
|
97
|
+
* @returns {string} The extracted tool name or 'unknown' if format is invalid
|
|
98
|
+
*/
|
|
99
|
+
function extractWrappedToolName(wrappedToolError) {
|
|
100
|
+
if (!wrappedToolError || typeof wrappedToolError !== 'string') {
|
|
101
|
+
return 'unknown';
|
|
102
|
+
}
|
|
103
|
+
const colonIndex = wrappedToolError.indexOf(':');
|
|
104
|
+
return colonIndex !== -1 ? wrappedToolError.slice(colonIndex + 1) : 'unknown';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check if an error indicates a wrapped tool format error
|
|
109
|
+
* @param {string|null} error - Error from detectUnrecognizedToolCall
|
|
110
|
+
* @returns {boolean} True if it's a wrapped tool error
|
|
111
|
+
*/
|
|
112
|
+
function isWrappedToolError(error) {
|
|
113
|
+
return error && typeof error === 'string' && error.startsWith('wrapped_tool:');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create error message for wrapped tool format issues
|
|
118
|
+
* @param {string} wrappedToolName - The tool name that was incorrectly wrapped
|
|
119
|
+
* @returns {string} User-friendly error message with correct format instructions
|
|
120
|
+
*/
|
|
121
|
+
function createWrappedToolErrorMessage(wrappedToolName) {
|
|
122
|
+
return `Your response contained an incorrectly formatted tool call (${wrappedToolName} wrapped in XML tags). This cannot be used.
|
|
123
|
+
|
|
124
|
+
Please use the CORRECT format:
|
|
125
|
+
|
|
126
|
+
<${wrappedToolName}>
|
|
127
|
+
Your content here
|
|
128
|
+
</${wrappedToolName}>
|
|
129
|
+
|
|
130
|
+
Do NOT wrap in other tags like <api_call>, <tool_name>, <function>, etc.`;
|
|
131
|
+
}
|
|
132
|
+
|
|
94
133
|
// Supported image file extensions (imported from shared config)
|
|
95
134
|
|
|
96
135
|
// Maximum image file size (20MB) to prevent OOM attacks
|
|
@@ -2542,6 +2581,11 @@ Follow these instructions carefully:
|
|
|
2542
2581
|
}
|
|
2543
2582
|
}
|
|
2544
2583
|
|
|
2584
|
+
// Circuit breaker for repeated format errors
|
|
2585
|
+
let lastFormatErrorType = null;
|
|
2586
|
+
let sameFormatErrorCount = 0;
|
|
2587
|
+
const MAX_REPEATED_FORMAT_ERRORS = 3;
|
|
2588
|
+
|
|
2545
2589
|
// Tool iteration loop (only for non-CLI engines like Vercel/Anthropic/OpenAI)
|
|
2546
2590
|
while (currentIteration < maxIterations && !completionAttempted) {
|
|
2547
2591
|
currentIteration++;
|
|
@@ -2835,7 +2879,28 @@ Follow these instructions carefully:
|
|
|
2835
2879
|
);
|
|
2836
2880
|
|
|
2837
2881
|
if (lastAssistantMessage) {
|
|
2838
|
-
|
|
2882
|
+
const prevContent = lastAssistantMessage.content;
|
|
2883
|
+
|
|
2884
|
+
// Check for patterns indicating a failed/wrapped tool call attempt
|
|
2885
|
+
// Use detectUnrecognizedToolCall for consistent detection logic
|
|
2886
|
+
const wrappedToolError = detectUnrecognizedToolCall(prevContent, validTools);
|
|
2887
|
+
|
|
2888
|
+
if (isWrappedToolError(wrappedToolError)) {
|
|
2889
|
+
// Previous response was a broken tool call attempt - don't reuse it
|
|
2890
|
+
const wrappedToolName = extractWrappedToolName(wrappedToolError);
|
|
2891
|
+
if (this.debug) {
|
|
2892
|
+
console.log(`[DEBUG] Previous response contains wrapped tool '${wrappedToolName}' - rejecting for __PREVIOUS_RESPONSE__`);
|
|
2893
|
+
}
|
|
2894
|
+
currentMessages.push({ role: 'assistant', content: assistantResponseContent });
|
|
2895
|
+
currentMessages.push({
|
|
2896
|
+
role: 'user',
|
|
2897
|
+
content: createWrappedToolErrorMessage(wrappedToolName)
|
|
2898
|
+
});
|
|
2899
|
+
completionAttempted = false;
|
|
2900
|
+
continue; // Don't use broken response, continue the loop
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
finalResult = prevContent;
|
|
2839
2904
|
if (this.debug) console.log(`[DEBUG] Using previous response as completion: ${finalResult.substring(0, 100)}...`);
|
|
2840
2905
|
} else {
|
|
2841
2906
|
finalResult = 'Error: No previous response found to use as completion.';
|
|
@@ -3165,7 +3230,32 @@ Follow these instructions carefully:
|
|
|
3165
3230
|
const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
|
|
3166
3231
|
|
|
3167
3232
|
let reminderContent;
|
|
3168
|
-
if (unrecognizedTool) {
|
|
3233
|
+
if (isWrappedToolError(unrecognizedTool)) {
|
|
3234
|
+
// AI wrapped a valid tool name in arbitrary XML tags - provide clear format error
|
|
3235
|
+
const wrappedToolName = extractWrappedToolName(unrecognizedTool);
|
|
3236
|
+
if (this.debug) {
|
|
3237
|
+
console.log(`[DEBUG] Detected wrapped tool '${wrappedToolName}' in assistant response - wrong XML format.`);
|
|
3238
|
+
}
|
|
3239
|
+
const toolError = new ParameterError(
|
|
3240
|
+
`Tool '${wrappedToolName}' found but in WRONG FORMAT - do not wrap tools in other XML tags.`,
|
|
3241
|
+
{
|
|
3242
|
+
suggestion: `Use the tool tag DIRECTLY without any wrapper:
|
|
3243
|
+
|
|
3244
|
+
CORRECT FORMAT:
|
|
3245
|
+
<${wrappedToolName}>
|
|
3246
|
+
<param>value</param>
|
|
3247
|
+
</${wrappedToolName}>
|
|
3248
|
+
|
|
3249
|
+
WRONG (what you did - do not wrap in other tags):
|
|
3250
|
+
<api_call><tool_name>${wrappedToolName}</tool_name>...</api_call>
|
|
3251
|
+
<function>${wrappedToolName}</function>
|
|
3252
|
+
<call name="${wrappedToolName}">...</call>
|
|
3253
|
+
|
|
3254
|
+
Remove ALL wrapper tags and use <${wrappedToolName}> directly as the outermost tag.`
|
|
3255
|
+
}
|
|
3256
|
+
);
|
|
3257
|
+
reminderContent = `<tool_result>\n${formatErrorForAI(toolError)}\n</tool_result>`;
|
|
3258
|
+
} else if (unrecognizedTool) {
|
|
3169
3259
|
// AI tried to use a tool that's not available - provide clear error
|
|
3170
3260
|
if (this.debug) {
|
|
3171
3261
|
console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
|
|
@@ -3175,6 +3265,33 @@ Follow these instructions carefully:
|
|
|
3175
3265
|
});
|
|
3176
3266
|
reminderContent = `<tool_result>\n${formatErrorForAI(toolError)}\n</tool_result>`;
|
|
3177
3267
|
} else {
|
|
3268
|
+
// No tool call detected at all - check if this is the last iteration
|
|
3269
|
+
// On the last iteration, if the AI gave a substantive response without using
|
|
3270
|
+
// attempt_completion, accept it as the final answer rather than losing the content
|
|
3271
|
+
if (currentIteration >= maxIterations) {
|
|
3272
|
+
// Clean up the response - remove thinking tags
|
|
3273
|
+
let cleanedResponse = assistantResponseContent;
|
|
3274
|
+
// Remove <thinking>...</thinking> blocks
|
|
3275
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, '').trim();
|
|
3276
|
+
// Also remove unclosed thinking tags
|
|
3277
|
+
cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*$/gi, '').trim();
|
|
3278
|
+
|
|
3279
|
+
// Only use if there's substantial content (not just a failed tool call attempt)
|
|
3280
|
+
const hasSubstantialContent = cleanedResponse.length > 50 &&
|
|
3281
|
+
!cleanedResponse.includes('<api_call>') &&
|
|
3282
|
+
!cleanedResponse.includes('<tool_name>') &&
|
|
3283
|
+
!cleanedResponse.includes('<function>');
|
|
3284
|
+
|
|
3285
|
+
if (hasSubstantialContent) {
|
|
3286
|
+
if (this.debug) {
|
|
3287
|
+
console.log(`[DEBUG] Max iterations reached - accepting AI response as final answer (${cleanedResponse.length} chars)`);
|
|
3288
|
+
}
|
|
3289
|
+
finalResult = cleanedResponse;
|
|
3290
|
+
completionAttempted = true;
|
|
3291
|
+
break;
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3178
3295
|
// Standard reminder - no tool call detected at all
|
|
3179
3296
|
reminderContent = `Please use one of the available tools to help answer the question, or use attempt_completion if you have enough information to provide a final answer.
|
|
3180
3297
|
|
|
@@ -3206,6 +3323,31 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
|
|
|
3206
3323
|
console.log(`[DEBUG] No tool call detected in assistant response. Prompting for tool use.`);
|
|
3207
3324
|
}
|
|
3208
3325
|
}
|
|
3326
|
+
|
|
3327
|
+
// Circuit breaker: track repeated format errors and break early
|
|
3328
|
+
// For wrapped_tool errors, track them as a category (any wrapped_tool counts)
|
|
3329
|
+
// For other errors, track the exact error type
|
|
3330
|
+
if (unrecognizedTool) {
|
|
3331
|
+
const isWrapped = isWrappedToolError(unrecognizedTool);
|
|
3332
|
+
const errorCategory = isWrapped ? 'wrapped_tool' : unrecognizedTool;
|
|
3333
|
+
|
|
3334
|
+
if (errorCategory === lastFormatErrorType) {
|
|
3335
|
+
sameFormatErrorCount++;
|
|
3336
|
+
if (sameFormatErrorCount >= MAX_REPEATED_FORMAT_ERRORS) {
|
|
3337
|
+
const errorDesc = isWrapped ? 'wrapped tool format' : unrecognizedTool;
|
|
3338
|
+
console.error(`[ERROR] Format error category '${errorCategory}' repeated ${sameFormatErrorCount} times. Breaking loop early to prevent infinite iteration.`);
|
|
3339
|
+
finalResult = `Error: Unable to complete request. The AI model repeatedly used incorrect tool call format (${errorDesc}). Please try rephrasing your question or using a different model.`;
|
|
3340
|
+
break;
|
|
3341
|
+
}
|
|
3342
|
+
} else {
|
|
3343
|
+
lastFormatErrorType = errorCategory;
|
|
3344
|
+
sameFormatErrorCount = 1;
|
|
3345
|
+
}
|
|
3346
|
+
} else {
|
|
3347
|
+
// Reset counter if it's a different kind of "no tool call" situation
|
|
3348
|
+
lastFormatErrorType = null;
|
|
3349
|
+
sameFormatErrorCount = 0;
|
|
3350
|
+
}
|
|
3209
3351
|
}
|
|
3210
3352
|
|
|
3211
3353
|
// Keep message history manageable
|
package/src/tools/common.js
CHANGED
|
@@ -617,6 +617,37 @@ export function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
617
617
|
}
|
|
618
618
|
}
|
|
619
619
|
|
|
620
|
+
// Check if any valid tool name appears inside specific wrapper patterns
|
|
621
|
+
// This catches cases where AI wraps tools in arbitrary tags like:
|
|
622
|
+
// <api_call><tool_name>attempt_completion</tool_name>...</api_call>
|
|
623
|
+
// <function>search</function>
|
|
624
|
+
// <call name="extract">...</call>
|
|
625
|
+
// Only match specific wrapper patterns to avoid false positives with normal text
|
|
626
|
+
const allToolNames = [...new Set([...knownToolNames, ...validTools])];
|
|
627
|
+
for (const toolName of allToolNames) {
|
|
628
|
+
// Escape regex metacharacters in tool name to prevent regex errors
|
|
629
|
+
const escapedToolName = toolName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
630
|
+
|
|
631
|
+
// Match specific wrapper patterns that indicate a tool call attempt:
|
|
632
|
+
// 1. <tool_name>toolName</tool_name> - common Claude API-style wrapper
|
|
633
|
+
// 2. <function>toolName</function> - function call style
|
|
634
|
+
// 3. <name>toolName</name> - generic name wrapper
|
|
635
|
+
// 4. <call><name>toolName - partial wrapper patterns
|
|
636
|
+
const wrapperPatterns = [
|
|
637
|
+
new RegExp(`<tool_name>\\s*${escapedToolName}\\s*</tool_name>`, 'i'),
|
|
638
|
+
new RegExp(`<function>\\s*${escapedToolName}\\s*</function>`, 'i'),
|
|
639
|
+
new RegExp(`<name>\\s*${escapedToolName}\\s*</name>`, 'i'),
|
|
640
|
+
// Also check for tool name immediately after api_call or call opening tag
|
|
641
|
+
new RegExp(`<(?:api_call|call)[^>]*>[\\s\\S]*?<tool_name>\\s*${escapedToolName}`, 'i')
|
|
642
|
+
];
|
|
643
|
+
|
|
644
|
+
for (const pattern of wrapperPatterns) {
|
|
645
|
+
if (pattern.test(xmlString)) {
|
|
646
|
+
return `wrapped_tool:${toolName}`;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
620
651
|
return null;
|
|
621
652
|
}
|
|
622
653
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|